Add remote debugging front end to developer shell.
D8 now supports demote debuggign of a V8 instance with the debugger agent enabled. Running D8 with the option --remote-debugger will try to connect to a V8 debugger agent and process the debugging protocol. The command line UI is the same as for the D8 in-process debugger as the same code is used for processing the debugger JSON. Review URL: http://codereview.chromium.org/40011 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1411 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
fa5bc7ea0b
commit
6ef7ef657e
195
src/d8-debug.cc
195
src/d8-debug.cc
@ -28,6 +28,8 @@
|
||||
|
||||
#include "d8.h"
|
||||
#include "d8-debug.h"
|
||||
#include "platform.h"
|
||||
#include "debug-agent.h"
|
||||
|
||||
|
||||
namespace v8 {
|
||||
@ -142,4 +144,197 @@ void HandleDebugEvent(DebugEvent event,
|
||||
}
|
||||
|
||||
|
||||
void RunRemoteDebugger(int port) {
|
||||
RemoteDebugger debugger(port);
|
||||
debugger.Run();
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::Run() {
|
||||
bool ok;
|
||||
|
||||
// Make sure that socket support is initialized.
|
||||
ok = i::Socket::Setup();
|
||||
if (!ok) {
|
||||
printf("Unable to initialize socket support %d\n", i::Socket::LastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Connect to the debugger agent.
|
||||
conn_ = i::OS::CreateSocket();
|
||||
static const int kPortStrSize = 6;
|
||||
char port_str[kPortStrSize];
|
||||
i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
|
||||
ok = conn_->Connect("localhost", port_str);
|
||||
if (!ok) {
|
||||
printf("Unable to connect to debug agent %d\n", i::Socket::LastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the receiver thread.
|
||||
ReceiverThread receiver(this);
|
||||
receiver.Start();
|
||||
|
||||
// Start the keyboard thread.
|
||||
KeyboardThread keyboard(this);
|
||||
keyboard.Start();
|
||||
|
||||
// Process events received from debugged VM and from the keyboard.
|
||||
bool terminate = false;
|
||||
while (!terminate) {
|
||||
event_available_->Wait();
|
||||
RemoteDebuggerEvent* event = GetEvent();
|
||||
switch (event->type()) {
|
||||
case RemoteDebuggerEvent::kMessage:
|
||||
HandleMessageReceived(event->data());
|
||||
break;
|
||||
case RemoteDebuggerEvent::kKeyboard:
|
||||
HandleKeyboardCommand(event->data());
|
||||
break;
|
||||
case RemoteDebuggerEvent::kDisconnect:
|
||||
terminate = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
delete event;
|
||||
}
|
||||
|
||||
// Wait for the receiver thread to end.
|
||||
receiver.Join();
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::MessageReceived(i::SmartPointer<char> message) {
|
||||
RemoteDebuggerEvent* event =
|
||||
new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
|
||||
AddEvent(event);
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::KeyboardCommand(i::SmartPointer<char> command) {
|
||||
RemoteDebuggerEvent* event =
|
||||
new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
|
||||
AddEvent(event);
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::ConnectionClosed() {
|
||||
RemoteDebuggerEvent* event =
|
||||
new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
|
||||
i::SmartPointer<char>());
|
||||
AddEvent(event);
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
|
||||
i::ScopedLock lock(event_access_);
|
||||
if (head_ == NULL) {
|
||||
ASSERT(tail_ == NULL);
|
||||
head_ = event;
|
||||
tail_ = event;
|
||||
} else {
|
||||
ASSERT(tail_ != NULL);
|
||||
tail_->set_next(event);
|
||||
tail_ = event;
|
||||
}
|
||||
event_available_->Signal();
|
||||
}
|
||||
|
||||
|
||||
RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
|
||||
i::ScopedLock lock(event_access_);
|
||||
ASSERT(head_ != NULL);
|
||||
RemoteDebuggerEvent* result = head_;
|
||||
head_ = head_->next();
|
||||
if (head_ == NULL) {
|
||||
ASSERT(tail_ == result);
|
||||
tail_ = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::HandleMessageReceived(char* message) {
|
||||
HandleScope scope;
|
||||
|
||||
// Print the event details.
|
||||
TryCatch try_catch;
|
||||
Handle<Object> details =
|
||||
Shell::DebugMessageDetails(Handle<String>::Cast(String::New(message)));
|
||||
if (try_catch.HasCaught()) {
|
||||
Shell::ReportException(&try_catch);
|
||||
return;
|
||||
}
|
||||
String::Utf8Value str(details->Get(String::New("text")));
|
||||
if (str.length() == 0) {
|
||||
// Empty string is used to signal not to process this event.
|
||||
return;
|
||||
}
|
||||
if (*str != NULL) {
|
||||
printf("%s\n", *str);
|
||||
} else {
|
||||
printf("???\n");
|
||||
}
|
||||
printf("dbg> ");
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::HandleKeyboardCommand(char* command) {
|
||||
HandleScope scope;
|
||||
|
||||
// Convert the debugger command to a JSON debugger request.
|
||||
TryCatch try_catch;
|
||||
Handle<Value> request =
|
||||
Shell::DebugCommandToJSONRequest(String::New(command));
|
||||
if (try_catch.HasCaught()) {
|
||||
Shell::ReportException(&try_catch);
|
||||
return;
|
||||
}
|
||||
|
||||
// If undefined is returned the command was handled internally and there is
|
||||
// no JSON to send.
|
||||
if (request->IsUndefined()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the JSON debugger request.
|
||||
i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
|
||||
}
|
||||
|
||||
|
||||
void ReceiverThread::Run() {
|
||||
while (true) {
|
||||
// Receive a message.
|
||||
i::SmartPointer<char> message =
|
||||
i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
|
||||
if (*message == NULL) {
|
||||
remote_debugger_->ConnectionClosed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass the message to the main thread.
|
||||
remote_debugger_->MessageReceived(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void KeyboardThread::Run() {
|
||||
static const int kBufferSize = 256;
|
||||
while (true) {
|
||||
// read keyboard input.
|
||||
char command[kBufferSize];
|
||||
char* str = fgets(command, kBufferSize, stdin);
|
||||
if (str == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Pass the keyboard command to the main thread.
|
||||
remote_debugger_->KeyboardCommand(
|
||||
i::SmartPointer<char>(i::OS::StrDup(command)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace v8
|
||||
|
107
src/d8-debug.h
107
src/d8-debug.h
@ -41,6 +41,113 @@ void HandleDebugEvent(DebugEvent event,
|
||||
Handle<Object> event_data,
|
||||
Handle<Value> data);
|
||||
|
||||
// Start the remove debugger connecting to a V8 debugger agent on the specified
|
||||
// port.
|
||||
void RunRemoteDebugger(int port);
|
||||
|
||||
// Forward declerations.
|
||||
class RemoteDebuggerEvent;
|
||||
class ReceiverThread;
|
||||
|
||||
|
||||
// Remote debugging class.
|
||||
class RemoteDebugger {
|
||||
public:
|
||||
explicit RemoteDebugger(int port)
|
||||
: port_(port),
|
||||
event_access_(i::OS::CreateMutex()),
|
||||
event_available_(i::OS::CreateSemaphore(0)),
|
||||
head_(NULL), tail_(NULL) {}
|
||||
void Run();
|
||||
|
||||
// Handle events from the subordinate threads.
|
||||
void MessageReceived(i::SmartPointer<char> message);
|
||||
void KeyboardCommand(i::SmartPointer<char> command);
|
||||
void ConnectionClosed();
|
||||
|
||||
private:
|
||||
// Add new debugger event to the list.
|
||||
void AddEvent(RemoteDebuggerEvent* event);
|
||||
// Read next debugger event from the list.
|
||||
RemoteDebuggerEvent* GetEvent();
|
||||
|
||||
// Handle a message from the debugged V8.
|
||||
void HandleMessageReceived(char* message);
|
||||
// Handle a keyboard command.
|
||||
void HandleKeyboardCommand(char* command);
|
||||
|
||||
// Get connection to agent in debugged V8.
|
||||
i::Socket* conn() { return conn_; }
|
||||
|
||||
int port_; // Port used to connect to debugger V8.
|
||||
i::Socket* conn_; // Connection to debugger agent in debugged V8.
|
||||
|
||||
// Linked list of events from debugged V8 and from keyboard input. Access to
|
||||
// the list is guarded by a mutex and a semaphore signals new items in the
|
||||
// list.
|
||||
i::Mutex* event_access_;
|
||||
i::Semaphore* event_available_;
|
||||
RemoteDebuggerEvent* head_;
|
||||
RemoteDebuggerEvent* tail_;
|
||||
|
||||
friend class ReceiverThread;
|
||||
};
|
||||
|
||||
|
||||
// Thread reading from debugged V8 instance.
|
||||
class ReceiverThread: public i::Thread {
|
||||
public:
|
||||
explicit ReceiverThread(RemoteDebugger* remote_debugger)
|
||||
: remote_debugger_(remote_debugger) {}
|
||||
~ReceiverThread() {}
|
||||
|
||||
void Run();
|
||||
|
||||
private:
|
||||
RemoteDebugger* remote_debugger_;
|
||||
};
|
||||
|
||||
|
||||
// Thread reading keyboard input.
|
||||
class KeyboardThread: public i::Thread {
|
||||
public:
|
||||
explicit KeyboardThread(RemoteDebugger* remote_debugger)
|
||||
: remote_debugger_(remote_debugger) {}
|
||||
~KeyboardThread() {}
|
||||
|
||||
void Run();
|
||||
|
||||
private:
|
||||
RemoteDebugger* remote_debugger_;
|
||||
};
|
||||
|
||||
|
||||
// Events processed by the main deubgger thread.
|
||||
class RemoteDebuggerEvent {
|
||||
public:
|
||||
RemoteDebuggerEvent(int type, i::SmartPointer<char> data)
|
||||
: type_(type), data_(data), next_(NULL) {
|
||||
ASSERT(type == kMessage || type == kKeyboard || type == kDisconnect);
|
||||
}
|
||||
|
||||
static const int kMessage = 1;
|
||||
static const int kKeyboard = 2;
|
||||
static const int kDisconnect = 3;
|
||||
|
||||
int type() { return type_; }
|
||||
char* data() { return *data_; }
|
||||
|
||||
private:
|
||||
void set_next(RemoteDebuggerEvent* event) { next_ = event; }
|
||||
RemoteDebuggerEvent* next() { return next_; }
|
||||
|
||||
int type_;
|
||||
i::SmartPointer<char> data_;
|
||||
RemoteDebuggerEvent* next_;
|
||||
|
||||
friend class RemoteDebugger;
|
||||
};
|
||||
|
||||
|
||||
} // namespace v8
|
||||
|
||||
|
10
src/d8.cc
10
src/d8.cc
@ -574,16 +574,22 @@ int Shell::Main(int argc, char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
// Run the remote debugger if requested.
|
||||
if (i::FLAG_remote_debugger) {
|
||||
RunRemoteDebugger(i::FLAG_debugger_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Start the debugger agent if requested.
|
||||
if (i::FLAG_debugger_agent) {
|
||||
v8::Debug::EnableAgent(i::FLAG_debugger_port);
|
||||
}
|
||||
|
||||
// Start the in-process debugger if requested.
|
||||
if (i::FLAG_debugger && !i::FLAG_debugger_agent)
|
||||
if (i::FLAG_debugger && !i::FLAG_debugger_agent) {
|
||||
v8::Debug::SetDebugEventListener(HandleDebugEvent);
|
||||
}
|
||||
}
|
||||
|
||||
if (run_shell)
|
||||
RunShell();
|
||||
for (int i = 0; i < threads.length(); i++) {
|
||||
|
@ -226,6 +226,8 @@ DEFINE_string(testing_serialization_file, "/tmp/serdes",
|
||||
DEFINE_bool(help, false, "Print usage message, including flags, on console")
|
||||
DEFINE_bool(dump_counters, false, "Dump counters on exit")
|
||||
DEFINE_bool(debugger, true, "Enable JavaScript debugger")
|
||||
DEFINE_bool(remote_debugger, false, "Connect JavaScript debugger to the "
|
||||
"debugger agent in another process")
|
||||
DEFINE_bool(debugger_agent, false, "Enable debugger agent")
|
||||
DEFINE_int(debugger_port, 5858, "Port to use for remote debugging")
|
||||
DEFINE_string(map_counters, false, "Map counters to a file")
|
||||
|
Loading…
Reference in New Issue
Block a user