Remove DebuggerAgent.
R=svenpanne@chromium.org Review URL: https://codereview.chromium.org/279423004 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21315 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
c0ec7f20c8
commit
33fba3bfa1
@ -201,20 +201,6 @@ class V8_EXPORT Debug {
|
||||
const uint16_t* command, int length,
|
||||
ClientData* client_data = NULL);
|
||||
|
||||
/**
|
||||
* Register a callback function to be called when a debug message has been
|
||||
* received and is ready to be processed. For the debug messages to be
|
||||
* processed V8 needs to be entered, and in certain embedding scenarios this
|
||||
* callback can be used to make sure V8 is entered for the debug message to
|
||||
* be processed. Note that debug messages will only be processed if there is
|
||||
* a V8 break. This can happen automatically by using the option
|
||||
* --debugger-auto-break.
|
||||
* \param provide_locker requires that V8 acquires v8::Locker for you before
|
||||
* calling handler
|
||||
*/
|
||||
static void SetDebugMessageDispatchHandler(
|
||||
DebugMessageDispatchHandler handler, bool provide_locker = false);
|
||||
|
||||
/**
|
||||
* Run a JavaScript function in the debugger.
|
||||
* \param fun the function to call
|
||||
@ -241,22 +227,6 @@ class V8_EXPORT Debug {
|
||||
*/
|
||||
static Local<Value> GetMirror(v8::Handle<v8::Value> obj);
|
||||
|
||||
/**
|
||||
* Enable the V8 builtin debug agent. The debugger agent will listen on the
|
||||
* supplied TCP/IP port for remote debugger connection.
|
||||
* \param name the name of the embedding application
|
||||
* \param port the TCP/IP port to listen on
|
||||
* \param wait_for_connection whether V8 should pause on a first statement
|
||||
* allowing remote debugger to connect before anything interesting happened
|
||||
*/
|
||||
static bool EnableAgent(const char* name, int port,
|
||||
bool wait_for_connection = false);
|
||||
|
||||
/**
|
||||
* Disable the V8 builtin debug agent. The TCP/IP connection will be closed.
|
||||
*/
|
||||
static void DisableAgent();
|
||||
|
||||
/**
|
||||
* Makes V8 process all pending debug messages.
|
||||
*
|
||||
@ -275,10 +245,6 @@ class V8_EXPORT Debug {
|
||||
* until V8 gets control again; however, embedding application may improve
|
||||
* this by manually calling this method.
|
||||
*
|
||||
* It makes sense to call this method whenever a new debug message arrived and
|
||||
* V8 is not already running. Method v8::Debug::SetDebugMessageDispatchHandler
|
||||
* should help with the former condition.
|
||||
*
|
||||
* Technically this method in many senses is equivalent to executing empty
|
||||
* script:
|
||||
* 1. It does nothing except for processing all pending debug messages.
|
||||
|
@ -109,28 +109,6 @@ bool RunCppCycle(v8::Handle<v8::Script> script,
|
||||
|
||||
v8::Persistent<v8::Context> debug_message_context;
|
||||
|
||||
void DispatchDebugMessages() {
|
||||
// We are in some random thread. We should already have v8::Locker acquired
|
||||
// (we requested this when registered this callback). We was called
|
||||
// because new debug messages arrived; they may have already been processed,
|
||||
// but we shouldn't worry about this.
|
||||
//
|
||||
// All we have to do is to set context and call ProcessDebugMessages.
|
||||
//
|
||||
// We should decide which V8 context to use here. This is important for
|
||||
// "evaluate" command, because it must be executed some context.
|
||||
// In our sample we have only one context, so there is nothing really to
|
||||
// think about.
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> context =
|
||||
v8::Local<v8::Context>::New(isolate, debug_message_context);
|
||||
v8::Context::Scope scope(context);
|
||||
|
||||
v8::Debug::ProcessDebugMessages();
|
||||
}
|
||||
|
||||
|
||||
int RunMain(int argc, char* argv[]) {
|
||||
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
|
||||
v8::Isolate* isolate = v8::Isolate::New();
|
||||
@ -142,10 +120,6 @@ int RunMain(int argc, char* argv[]) {
|
||||
v8::Handle<v8::Value> script_name;
|
||||
int script_param_counter = 0;
|
||||
|
||||
int port_number = -1;
|
||||
bool wait_for_connection = false;
|
||||
bool support_callback = false;
|
||||
|
||||
MainCycleType cycle_type = CycleInCpp;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
@ -158,13 +132,6 @@ int RunMain(int argc, char* argv[]) {
|
||||
cycle_type = CycleInCpp;
|
||||
} else if (strcmp(str, "--main-cycle-in-js") == 0) {
|
||||
cycle_type = CycleInJs;
|
||||
} else if (strcmp(str, "--callback") == 0) {
|
||||
support_callback = true;
|
||||
} else if (strcmp(str, "--wait-for-connection") == 0) {
|
||||
wait_for_connection = true;
|
||||
} else if (strcmp(str, "-p") == 0 && i + 1 < argc) {
|
||||
port_number = atoi(argv[i + 1]); // NOLINT
|
||||
i++;
|
||||
} else if (strncmp(str, "--", 2) == 0) {
|
||||
printf("Warning: unknown flag %s.\nTry --help for options\n", str);
|
||||
} else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
|
||||
@ -214,14 +181,6 @@ int RunMain(int argc, char* argv[]) {
|
||||
|
||||
debug_message_context.Reset(isolate, context);
|
||||
|
||||
if (support_callback) {
|
||||
v8::Debug::SetDebugMessageDispatchHandler(DispatchDebugMessages, true);
|
||||
}
|
||||
|
||||
if (port_number != -1) {
|
||||
v8::Debug::EnableAgent("lineprocessor", port_number, wait_for_connection);
|
||||
}
|
||||
|
||||
bool report_exceptions = true;
|
||||
|
||||
v8::Handle<v8::Script> script;
|
||||
|
24
src/api.cc
24
src/api.cc
@ -6824,22 +6824,11 @@ void Debug::SendCommand(Isolate* isolate,
|
||||
int length,
|
||||
ClientData* client_data) {
|
||||
i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
internal_isolate->debugger()->ProcessCommand(
|
||||
internal_isolate->debugger()->EnqueueCommandMessage(
|
||||
i::Vector<const uint16_t>(command, length), client_data);
|
||||
}
|
||||
|
||||
|
||||
void Debug::SetDebugMessageDispatchHandler(
|
||||
DebugMessageDispatchHandler handler, bool provide_locker) {
|
||||
i::Isolate* isolate = i::Isolate::Current();
|
||||
EnsureInitializedForIsolate(isolate,
|
||||
"v8::Debug::SetDebugMessageDispatchHandler");
|
||||
ENTER_V8(isolate);
|
||||
isolate->debugger()->SetDebugMessageDispatchHandler(
|
||||
handler, provide_locker);
|
||||
}
|
||||
|
||||
|
||||
Local<Value> Debug::Call(v8::Handle<v8::Function> fun,
|
||||
v8::Handle<v8::Value> data) {
|
||||
i::Isolate* isolate = i::Isolate::Current();
|
||||
@ -6891,17 +6880,6 @@ Local<Value> Debug::GetMirror(v8::Handle<v8::Value> obj) {
|
||||
}
|
||||
|
||||
|
||||
bool Debug::EnableAgent(const char* name, int port, bool wait_for_connection) {
|
||||
return i::Isolate::Current()->debugger()->StartAgent(name, port,
|
||||
wait_for_connection);
|
||||
}
|
||||
|
||||
|
||||
void Debug::DisableAgent() {
|
||||
return i::Isolate::Current()->debugger()->StopAgent();
|
||||
}
|
||||
|
||||
|
||||
void Debug::ProcessDebugMessages() {
|
||||
i::Execution::ProcessDebugMessages(i::Isolate::Current(), true);
|
||||
}
|
||||
|
215
src/d8-debug.cc
215
src/d8-debug.cc
@ -4,27 +4,16 @@
|
||||
|
||||
#include "d8.h"
|
||||
#include "d8-debug.h"
|
||||
#include "debug-agent.h"
|
||||
#include "platform/socket.h"
|
||||
|
||||
|
||||
namespace v8 {
|
||||
|
||||
static bool was_running = true;
|
||||
|
||||
void PrintPrompt(bool is_running) {
|
||||
const char* prompt = is_running? "> " : "dbg> ";
|
||||
was_running = is_running;
|
||||
printf("%s", prompt);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
void PrintPrompt() {
|
||||
PrintPrompt(was_running);
|
||||
}
|
||||
|
||||
|
||||
void HandleDebugEvent(const Debug::EventDetails& event_details) {
|
||||
// TODO(svenpanne) There should be a way to retrieve this in the callback.
|
||||
Isolate* isolate = Isolate::GetCurrent();
|
||||
@ -140,208 +129,4 @@ void HandleDebugEvent(const Debug::EventDetails& event_details) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RunRemoteDebugger(Isolate* isolate, int port) {
|
||||
RemoteDebugger debugger(isolate, port);
|
||||
debugger.Run();
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::Run() {
|
||||
bool ok;
|
||||
|
||||
// Connect to the debugger agent.
|
||||
conn_ = new i::Socket;
|
||||
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::GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the receiver thread.
|
||||
ReceiverThread receiver(this);
|
||||
receiver.Start();
|
||||
|
||||
// Start the keyboard thread.
|
||||
KeyboardThread keyboard(this);
|
||||
keyboard.Start();
|
||||
PrintPrompt();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
delete conn_;
|
||||
conn_ = NULL;
|
||||
// Wait for the receiver thread to end.
|
||||
receiver.Join();
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
|
||||
RemoteDebuggerEvent* event =
|
||||
new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
|
||||
AddEvent(event);
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
|
||||
RemoteDebuggerEvent* event =
|
||||
new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
|
||||
AddEvent(event);
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::ConnectionClosed() {
|
||||
RemoteDebuggerEvent* event =
|
||||
new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
|
||||
i::SmartArrayPointer<char>());
|
||||
AddEvent(event);
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
|
||||
i::LockGuard<i::Mutex> lock_guard(&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::LockGuard<i::Mutex> lock_guard(&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) {
|
||||
Locker lock(isolate_);
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
// Print the event details.
|
||||
TryCatch try_catch;
|
||||
Handle<Object> details = Shell::DebugMessageDetails(
|
||||
isolate_, Handle<String>::Cast(String::NewFromUtf8(isolate_, message)));
|
||||
if (try_catch.HasCaught()) {
|
||||
Shell::ReportException(isolate_, &try_catch);
|
||||
PrintPrompt();
|
||||
return;
|
||||
}
|
||||
String::Utf8Value str(details->Get(String::NewFromUtf8(isolate_, "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");
|
||||
}
|
||||
|
||||
bool is_running = details->Get(String::NewFromUtf8(isolate_, "running"))
|
||||
->ToBoolean()
|
||||
->Value();
|
||||
PrintPrompt(is_running);
|
||||
}
|
||||
|
||||
|
||||
void RemoteDebugger::HandleKeyboardCommand(char* command) {
|
||||
Locker lock(isolate_);
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
// Convert the debugger command to a JSON debugger request.
|
||||
TryCatch try_catch;
|
||||
Handle<Value> request = Shell::DebugCommandToJSONRequest(
|
||||
isolate_, String::NewFromUtf8(isolate_, command));
|
||||
if (try_catch.HasCaught()) {
|
||||
Shell::ReportException(isolate_, &try_catch);
|
||||
PrintPrompt();
|
||||
return;
|
||||
}
|
||||
|
||||
// If undefined is returned the command was handled internally and there is
|
||||
// no JSON to send.
|
||||
if (request->IsUndefined()) {
|
||||
PrintPrompt();
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the JSON debugger request.
|
||||
i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
|
||||
}
|
||||
|
||||
|
||||
void ReceiverThread::Run() {
|
||||
// Receive the connect message (with empty body).
|
||||
i::SmartArrayPointer<char> message =
|
||||
i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
|
||||
ASSERT(message.get() == NULL);
|
||||
|
||||
while (true) {
|
||||
// Receive a message.
|
||||
i::SmartArrayPointer<char> message =
|
||||
i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
|
||||
if (message.get() == 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::SmartArrayPointer<char>(i::StrDup(command)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace v8
|
||||
|
113
src/d8-debug.h
113
src/d8-debug.h
@ -8,125 +8,12 @@
|
||||
|
||||
#include "d8.h"
|
||||
#include "debug.h"
|
||||
#include "platform/socket.h"
|
||||
|
||||
|
||||
namespace v8 {
|
||||
|
||||
|
||||
void HandleDebugEvent(const Debug::EventDetails& event_details);
|
||||
|
||||
// Start the remove debugger connecting to a V8 debugger agent on the specified
|
||||
// port.
|
||||
void RunRemoteDebugger(Isolate* isolate, int port);
|
||||
|
||||
// Forward declerations.
|
||||
class RemoteDebuggerEvent;
|
||||
class ReceiverThread;
|
||||
|
||||
|
||||
// Remote debugging class.
|
||||
class RemoteDebugger {
|
||||
public:
|
||||
explicit RemoteDebugger(Isolate* isolate, int port)
|
||||
: isolate_(isolate),
|
||||
port_(port),
|
||||
event_available_(0),
|
||||
head_(NULL), tail_(NULL) {}
|
||||
void Run();
|
||||
|
||||
// Handle events from the subordinate threads.
|
||||
void MessageReceived(i::SmartArrayPointer<char> message);
|
||||
void KeyboardCommand(i::SmartArrayPointer<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_; }
|
||||
|
||||
Isolate* isolate_;
|
||||
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)
|
||||
: Thread("d8:ReceiverThrd"),
|
||||
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)
|
||||
: Thread("d8:KeyboardThrd"),
|
||||
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::SmartArrayPointer<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_.get(); }
|
||||
|
||||
private:
|
||||
void set_next(RemoteDebuggerEvent* event) { next_ = event; }
|
||||
RemoteDebuggerEvent* next() { return next_; }
|
||||
|
||||
int type_;
|
||||
i::SmartArrayPointer<char> data_;
|
||||
RemoteDebuggerEvent* next_;
|
||||
|
||||
friend class RemoteDebugger;
|
||||
};
|
||||
|
||||
|
||||
} // namespace v8
|
||||
|
||||
|
||||
|
@ -83,7 +83,7 @@ bool ReadLineEditor::Close() {
|
||||
Handle<String> ReadLineEditor::Prompt(const char* prompt) {
|
||||
char* result = NULL;
|
||||
{ // Release lock for blocking input.
|
||||
Unlocker unlock(Isolate::GetCurrent());
|
||||
Unlocker unlock(isolate_);
|
||||
result = readline(prompt);
|
||||
}
|
||||
if (result == NULL) return Handle<String>();
|
||||
|
31
src/d8.cc
31
src/d8.cc
@ -638,16 +638,6 @@ Local<Value> Shell::DebugCommandToJSONRequest(Isolate* isolate,
|
||||
}
|
||||
|
||||
|
||||
void Shell::DispatchDebugMessages() {
|
||||
Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> context =
|
||||
v8::Local<v8::Context>::New(isolate, Shell::evaluation_context_);
|
||||
v8::Context::Scope context_scope(context);
|
||||
v8::Debug::ProcessDebugMessages();
|
||||
}
|
||||
|
||||
|
||||
int32_t* Counter::Bind(const char* name, bool is_histogram) {
|
||||
int i;
|
||||
for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
|
||||
@ -796,9 +786,7 @@ void Shell::InstallUtilityScript(Isolate* isolate) {
|
||||
script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE));
|
||||
|
||||
// Start the in-process debugger if requested.
|
||||
if (i::FLAG_debugger && !i::FLAG_debugger_agent) {
|
||||
v8::Debug::SetDebugEventListener(HandleDebugEvent);
|
||||
}
|
||||
if (i::FLAG_debugger) v8::Debug::SetDebugEventListener(HandleDebugEvent);
|
||||
}
|
||||
#endif // !V8_SHARED
|
||||
|
||||
@ -920,12 +908,6 @@ void Shell::InitializeDebugger(Isolate* isolate) {
|
||||
Handle<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
|
||||
utility_context_.Reset(isolate,
|
||||
Context::New(isolate, NULL, global_template));
|
||||
|
||||
// Start the debugger agent if requested.
|
||||
if (i::FLAG_debugger_agent) {
|
||||
v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port, true);
|
||||
v8::Debug::SetDebugMessageDispatchHandler(DispatchDebugMessages, true);
|
||||
}
|
||||
#endif // !V8_SHARED
|
||||
}
|
||||
|
||||
@ -1552,19 +1534,8 @@ int Shell::Main(int argc, char* argv[]) {
|
||||
result = RunMain(isolate, argc, argv);
|
||||
}
|
||||
|
||||
|
||||
#ifndef V8_SHARED
|
||||
// Run remote debugger if requested, but never on --test
|
||||
if (i::FLAG_remote_debugger && !options.test_shell) {
|
||||
InstallUtilityScript(isolate);
|
||||
RunRemoteDebugger(isolate, i::FLAG_debugger_port);
|
||||
return 0;
|
||||
}
|
||||
#endif // !V8_SHARED
|
||||
|
||||
// Run interactive shell if explicitly requested or if no script has been
|
||||
// executed, but never on --test
|
||||
|
||||
if (( options.interactive_shell || !options.script_executed )
|
||||
&& !options.test_shell ) {
|
||||
#ifndef V8_SHARED
|
||||
|
1
src/d8.h
1
src/d8.h
@ -265,7 +265,6 @@ class Shell : public i::AllStatic {
|
||||
Handle<String> message);
|
||||
static Local<Value> DebugCommandToJSONRequest(Isolate* isolate,
|
||||
Handle<String> command);
|
||||
static void DispatchDebugMessages();
|
||||
|
||||
static void PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
#endif // !V8_SHARED
|
||||
|
@ -1,481 +0,0 @@
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "v8.h"
|
||||
#include "debug.h"
|
||||
#include "debug-agent.h"
|
||||
#include "platform/socket.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// Public V8 debugger API message handler function. This function just delegates
|
||||
// to the debugger agent through it's data parameter.
|
||||
void DebuggerAgentMessageHandler(const v8::Debug::Message& message) {
|
||||
Isolate* isolate = reinterpret_cast<Isolate*>(message.GetIsolate());
|
||||
DebuggerAgent* agent = isolate->debugger_agent_instance();
|
||||
ASSERT(agent != NULL);
|
||||
agent->DebuggerMessage(message);
|
||||
}
|
||||
|
||||
|
||||
DebuggerAgent::DebuggerAgent(Isolate* isolate, const char* name, int port)
|
||||
: Thread(name),
|
||||
isolate_(isolate),
|
||||
name_(StrDup(name)),
|
||||
port_(port),
|
||||
server_(new Socket),
|
||||
terminate_(false),
|
||||
session_(NULL),
|
||||
terminate_now_(0),
|
||||
listening_(0) {
|
||||
ASSERT(isolate_->debugger_agent_instance() == NULL);
|
||||
isolate_->set_debugger_agent_instance(this);
|
||||
}
|
||||
|
||||
|
||||
DebuggerAgent::~DebuggerAgent() {
|
||||
isolate_->set_debugger_agent_instance(NULL);
|
||||
delete server_;
|
||||
}
|
||||
|
||||
|
||||
// Debugger agent main thread.
|
||||
void DebuggerAgent::Run() {
|
||||
// Allow this socket to reuse port even if still in TIME_WAIT.
|
||||
server_->SetReuseAddress(true);
|
||||
|
||||
// First bind the socket to the requested port.
|
||||
bool bound = false;
|
||||
while (!bound && !terminate_) {
|
||||
bound = server_->Bind(port_);
|
||||
|
||||
// If an error occurred wait a bit before retrying. The most common error
|
||||
// would be that the port is already in use so this avoids a busy loop and
|
||||
// make the agent take over the port when it becomes free.
|
||||
if (!bound) {
|
||||
const TimeDelta kTimeout = TimeDelta::FromSeconds(1);
|
||||
PrintF("Failed to open socket on port %d, "
|
||||
"waiting %d ms before retrying\n", port_,
|
||||
static_cast<int>(kTimeout.InMilliseconds()));
|
||||
if (!terminate_now_.WaitFor(kTimeout)) {
|
||||
if (terminate_) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Accept connections on the bound port.
|
||||
while (!terminate_) {
|
||||
bool ok = server_->Listen(1);
|
||||
listening_.Signal();
|
||||
if (ok) {
|
||||
// Accept the new connection.
|
||||
Socket* client = server_->Accept();
|
||||
ok = client != NULL;
|
||||
if (ok) {
|
||||
// Create and start a new session.
|
||||
CreateSession(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DebuggerAgent::Shutdown() {
|
||||
// Set the termination flag.
|
||||
terminate_ = true;
|
||||
|
||||
// Signal termination and make the server exit either its listen call or its
|
||||
// binding loop. This makes sure that no new sessions can be established.
|
||||
terminate_now_.Signal();
|
||||
server_->Shutdown();
|
||||
Join();
|
||||
|
||||
// Close existing session if any.
|
||||
CloseSession();
|
||||
}
|
||||
|
||||
|
||||
void DebuggerAgent::WaitUntilListening() {
|
||||
listening_.Wait();
|
||||
}
|
||||
|
||||
static const char* kCreateSessionMessage =
|
||||
"Remote debugging session already active\r\n";
|
||||
|
||||
void DebuggerAgent::CreateSession(Socket* client) {
|
||||
LockGuard<RecursiveMutex> session_access_guard(&session_access_);
|
||||
|
||||
// If another session is already established terminate this one.
|
||||
if (session_ != NULL) {
|
||||
int len = StrLength(kCreateSessionMessage);
|
||||
int res = client->Send(kCreateSessionMessage, len);
|
||||
delete client;
|
||||
USE(res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new session and hook up the debug message handler.
|
||||
session_ = new DebuggerAgentSession(this, client);
|
||||
isolate_->debugger()->SetMessageHandler(DebuggerAgentMessageHandler);
|
||||
session_->Start();
|
||||
}
|
||||
|
||||
|
||||
void DebuggerAgent::CloseSession() {
|
||||
LockGuard<RecursiveMutex> session_access_guard(&session_access_);
|
||||
|
||||
// Terminate the session.
|
||||
if (session_ != NULL) {
|
||||
session_->Shutdown();
|
||||
session_->Join();
|
||||
delete session_;
|
||||
session_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DebuggerAgent::DebuggerMessage(const v8::Debug::Message& message) {
|
||||
LockGuard<RecursiveMutex> session_access_guard(&session_access_);
|
||||
|
||||
// Forward the message handling to the session.
|
||||
if (session_ != NULL) {
|
||||
v8::String::Value val(message.GetJSON());
|
||||
session_->DebuggerMessage(Vector<uint16_t>(const_cast<uint16_t*>(*val),
|
||||
val.length()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DebuggerAgentSession::~DebuggerAgentSession() {
|
||||
delete client_;
|
||||
}
|
||||
|
||||
|
||||
void DebuggerAgent::OnSessionClosed(DebuggerAgentSession* session) {
|
||||
// Don't do anything during termination.
|
||||
if (terminate_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Terminate the session.
|
||||
LockGuard<RecursiveMutex> session_access_guard(&session_access_);
|
||||
ASSERT(session == session_);
|
||||
if (session == session_) {
|
||||
session_->Shutdown();
|
||||
delete session_;
|
||||
session_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DebuggerAgentSession::Run() {
|
||||
// Send the hello message.
|
||||
bool ok = DebuggerAgentUtil::SendConnectMessage(client_, agent_->name_.get());
|
||||
if (!ok) return;
|
||||
|
||||
while (true) {
|
||||
// Read data from the debugger front end.
|
||||
SmartArrayPointer<char> message =
|
||||
DebuggerAgentUtil::ReceiveMessage(client_);
|
||||
|
||||
const char* msg = message.get();
|
||||
bool is_closing_session = (msg == NULL);
|
||||
|
||||
if (msg == NULL) {
|
||||
// If we lost the connection, then simulate a disconnect msg:
|
||||
msg = "{\"seq\":1,\"type\":\"request\",\"command\":\"disconnect\"}";
|
||||
|
||||
} else {
|
||||
// Check if we're getting a disconnect request:
|
||||
const char* disconnectRequestStr =
|
||||
"\"type\":\"request\",\"command\":\"disconnect\"}";
|
||||
const char* result = strstr(msg, disconnectRequestStr);
|
||||
if (result != NULL) {
|
||||
is_closing_session = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert UTF-8 to UTF-16.
|
||||
unibrow::Utf8Decoder<128> decoder(msg, StrLength(msg));
|
||||
int utf16_length = decoder.Utf16Length();
|
||||
ScopedVector<uint16_t> temp(utf16_length + 1);
|
||||
decoder.WriteUtf16(temp.start(), utf16_length);
|
||||
|
||||
// Send the request received to the debugger.
|
||||
v8::Debug::SendCommand(reinterpret_cast<v8::Isolate*>(agent_->isolate()),
|
||||
temp.start(),
|
||||
utf16_length,
|
||||
NULL);
|
||||
|
||||
if (is_closing_session) {
|
||||
// Session is closed.
|
||||
agent_->OnSessionClosed(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DebuggerAgentSession::DebuggerMessage(Vector<uint16_t> message) {
|
||||
DebuggerAgentUtil::SendMessage(client_, message);
|
||||
}
|
||||
|
||||
|
||||
void DebuggerAgentSession::Shutdown() {
|
||||
// Shutdown the socket to end the blocking receive.
|
||||
client_->Shutdown();
|
||||
}
|
||||
|
||||
|
||||
const char* const DebuggerAgentUtil::kContentLength = "Content-Length";
|
||||
|
||||
|
||||
SmartArrayPointer<char> DebuggerAgentUtil::ReceiveMessage(Socket* conn) {
|
||||
int received;
|
||||
|
||||
// Read header.
|
||||
int content_length = 0;
|
||||
while (true) {
|
||||
const int kHeaderBufferSize = 80;
|
||||
char header_buffer[kHeaderBufferSize];
|
||||
int header_buffer_position = 0;
|
||||
char c = '\0'; // One character receive buffer.
|
||||
char prev_c = '\0'; // Previous character.
|
||||
|
||||
// Read until CRLF.
|
||||
while (!(c == '\n' && prev_c == '\r')) {
|
||||
prev_c = c;
|
||||
received = conn->Receive(&c, 1);
|
||||
if (received == 0) {
|
||||
PrintF("Error %d\n", Socket::GetLastError());
|
||||
return SmartArrayPointer<char>();
|
||||
}
|
||||
|
||||
// Add character to header buffer.
|
||||
if (header_buffer_position < kHeaderBufferSize) {
|
||||
header_buffer[header_buffer_position++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for end of header (empty header line).
|
||||
if (header_buffer_position == 2) { // Receive buffer contains CRLF.
|
||||
break;
|
||||
}
|
||||
|
||||
// Terminate header.
|
||||
ASSERT(header_buffer_position > 1); // At least CRLF is received.
|
||||
ASSERT(header_buffer_position <= kHeaderBufferSize);
|
||||
header_buffer[header_buffer_position - 2] = '\0';
|
||||
|
||||
// Split header.
|
||||
char* key = header_buffer;
|
||||
char* value = NULL;
|
||||
for (int i = 0; header_buffer[i] != '\0'; i++) {
|
||||
if (header_buffer[i] == ':') {
|
||||
header_buffer[i] = '\0';
|
||||
value = header_buffer + i + 1;
|
||||
while (*value == ' ') {
|
||||
value++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that key is Content-Length.
|
||||
if (strcmp(key, kContentLength) == 0) {
|
||||
// Get the content length value if present and within a sensible range.
|
||||
if (value == NULL || strlen(value) > 7) {
|
||||
return SmartArrayPointer<char>();
|
||||
}
|
||||
for (int i = 0; value[i] != '\0'; i++) {
|
||||
// Bail out if illegal data.
|
||||
if (value[i] < '0' || value[i] > '9') {
|
||||
return SmartArrayPointer<char>();
|
||||
}
|
||||
content_length = 10 * content_length + (value[i] - '0');
|
||||
}
|
||||
} else {
|
||||
// For now just print all other headers than Content-Length.
|
||||
PrintF("%s: %s\n", key, value != NULL ? value : "(no value)");
|
||||
}
|
||||
}
|
||||
|
||||
// Return now if no body.
|
||||
if (content_length == 0) {
|
||||
return SmartArrayPointer<char>();
|
||||
}
|
||||
|
||||
// Read body.
|
||||
char* buffer = NewArray<char>(content_length + 1);
|
||||
received = ReceiveAll(conn, buffer, content_length);
|
||||
if (received < content_length) {
|
||||
PrintF("Error %d\n", Socket::GetLastError());
|
||||
return SmartArrayPointer<char>();
|
||||
}
|
||||
buffer[content_length] = '\0';
|
||||
|
||||
return SmartArrayPointer<char>(buffer);
|
||||
}
|
||||
|
||||
|
||||
bool DebuggerAgentUtil::SendConnectMessage(Socket* conn,
|
||||
const char* embedding_host) {
|
||||
static const int kBufferSize = 80;
|
||||
char buffer[kBufferSize]; // Sending buffer.
|
||||
bool ok;
|
||||
int len;
|
||||
|
||||
// Send the header.
|
||||
len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
|
||||
"Type: connect\r\n");
|
||||
ok = conn->Send(buffer, len);
|
||||
if (!ok) return false;
|
||||
|
||||
len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
|
||||
"V8-Version: %s\r\n", v8::V8::GetVersion());
|
||||
ok = conn->Send(buffer, len);
|
||||
if (!ok) return false;
|
||||
|
||||
len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
|
||||
"Protocol-Version: 1\r\n");
|
||||
ok = conn->Send(buffer, len);
|
||||
if (!ok) return false;
|
||||
|
||||
if (embedding_host != NULL) {
|
||||
len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
|
||||
"Embedding-Host: %s\r\n", embedding_host);
|
||||
ok = conn->Send(buffer, len);
|
||||
if (!ok) return false;
|
||||
}
|
||||
|
||||
len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
|
||||
"%s: 0\r\n", kContentLength);
|
||||
ok = conn->Send(buffer, len);
|
||||
if (!ok) return false;
|
||||
|
||||
// Terminate header with empty line.
|
||||
len = OS::SNPrintF(Vector<char>(buffer, kBufferSize), "\r\n");
|
||||
ok = conn->Send(buffer, len);
|
||||
if (!ok) return false;
|
||||
|
||||
// No body for connect message.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DebuggerAgentUtil::SendMessage(Socket* conn,
|
||||
const Vector<uint16_t> message) {
|
||||
static const int kBufferSize = 80;
|
||||
char buffer[kBufferSize]; // Sending buffer both for header and body.
|
||||
|
||||
// Calculate the message size in UTF-8 encoding.
|
||||
int utf8_len = 0;
|
||||
int previous = unibrow::Utf16::kNoPreviousCharacter;
|
||||
for (int i = 0; i < message.length(); i++) {
|
||||
uint16_t character = message[i];
|
||||
utf8_len += unibrow::Utf8::Length(character, previous);
|
||||
previous = character;
|
||||
}
|
||||
|
||||
// Send the header.
|
||||
int len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
|
||||
"%s: %d\r\n", kContentLength, utf8_len);
|
||||
if (conn->Send(buffer, len) < len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Terminate header with empty line.
|
||||
len = OS::SNPrintF(Vector<char>(buffer, kBufferSize), "\r\n");
|
||||
if (conn->Send(buffer, len) < len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send message body as UTF-8.
|
||||
int buffer_position = 0; // Current buffer position.
|
||||
previous = unibrow::Utf16::kNoPreviousCharacter;
|
||||
for (int i = 0; i < message.length(); i++) {
|
||||
// Write next UTF-8 encoded character to buffer.
|
||||
uint16_t character = message[i];
|
||||
buffer_position +=
|
||||
unibrow::Utf8::Encode(buffer + buffer_position, character, previous);
|
||||
ASSERT(buffer_position <= kBufferSize);
|
||||
|
||||
// Send buffer if full or last character is encoded.
|
||||
if (kBufferSize - buffer_position <
|
||||
unibrow::Utf16::kMaxExtraUtf8BytesForOneUtf16CodeUnit ||
|
||||
i == message.length() - 1) {
|
||||
if (unibrow::Utf16::IsLeadSurrogate(character)) {
|
||||
const int kEncodedSurrogateLength =
|
||||
unibrow::Utf16::kUtf8BytesToCodeASurrogate;
|
||||
ASSERT(buffer_position >= kEncodedSurrogateLength);
|
||||
len = buffer_position - kEncodedSurrogateLength;
|
||||
if (conn->Send(buffer, len) < len) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < kEncodedSurrogateLength; i++) {
|
||||
buffer[i] = buffer[buffer_position + i];
|
||||
}
|
||||
buffer_position = kEncodedSurrogateLength;
|
||||
} else {
|
||||
len = buffer_position;
|
||||
if (conn->Send(buffer, len) < len) {
|
||||
return false;
|
||||
}
|
||||
buffer_position = 0;
|
||||
}
|
||||
}
|
||||
previous = character;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DebuggerAgentUtil::SendMessage(Socket* conn,
|
||||
const v8::Handle<v8::String> request) {
|
||||
static const int kBufferSize = 80;
|
||||
char buffer[kBufferSize]; // Sending buffer both for header and body.
|
||||
|
||||
// Convert the request to UTF-8 encoding.
|
||||
v8::String::Utf8Value utf8_request(request);
|
||||
|
||||
// Send the header.
|
||||
int len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
|
||||
"Content-Length: %d\r\n", utf8_request.length());
|
||||
if (conn->Send(buffer, len) < len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Terminate header with empty line.
|
||||
len = OS::SNPrintF(Vector<char>(buffer, kBufferSize), "\r\n");
|
||||
if (conn->Send(buffer, len) < len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send message body as UTF-8.
|
||||
len = utf8_request.length();
|
||||
if (conn->Send(*utf8_request, len) < len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Receive the full buffer before returning unless an error occours.
|
||||
int DebuggerAgentUtil::ReceiveAll(Socket* conn, char* data, int len) {
|
||||
int total_received = 0;
|
||||
while (total_received < len) {
|
||||
int received = conn->Receive(data + total_received, len - total_received);
|
||||
if (received == 0) {
|
||||
return total_received;
|
||||
}
|
||||
total_received += received;
|
||||
}
|
||||
return total_received;
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,93 +0,0 @@
|
||||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_DEBUG_AGENT_H_
|
||||
#define V8_DEBUG_AGENT_H_
|
||||
|
||||
#include "../include/v8-debug.h"
|
||||
#include "platform.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// Forward decelrations.
|
||||
class DebuggerAgentSession;
|
||||
class Socket;
|
||||
|
||||
|
||||
// Debugger agent which starts a socket listener on the debugger port and
|
||||
// handles connection from a remote debugger.
|
||||
class DebuggerAgent: public Thread {
|
||||
public:
|
||||
DebuggerAgent(Isolate* isolate, const char* name, int port);
|
||||
~DebuggerAgent();
|
||||
|
||||
void Shutdown();
|
||||
void WaitUntilListening();
|
||||
|
||||
Isolate* isolate() { return isolate_; }
|
||||
|
||||
private:
|
||||
void Run();
|
||||
void CreateSession(Socket* socket);
|
||||
void DebuggerMessage(const v8::Debug::Message& message);
|
||||
void CloseSession();
|
||||
void OnSessionClosed(DebuggerAgentSession* session);
|
||||
|
||||
Isolate* isolate_;
|
||||
SmartArrayPointer<const char> name_; // Name of the embedding application.
|
||||
int port_; // Port to use for the agent.
|
||||
Socket* server_; // Server socket for listen/accept.
|
||||
bool terminate_; // Termination flag.
|
||||
RecursiveMutex session_access_; // Mutex guarding access to session_.
|
||||
DebuggerAgentSession* session_; // Current active session if any.
|
||||
Semaphore terminate_now_; // Semaphore to signal termination.
|
||||
Semaphore listening_;
|
||||
|
||||
friend class DebuggerAgentSession;
|
||||
friend void DebuggerAgentMessageHandler(const v8::Debug::Message& message);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DebuggerAgent);
|
||||
};
|
||||
|
||||
|
||||
// Debugger agent session. The session receives requests from the remote
|
||||
// debugger and sends debugger events/responses to the remote debugger.
|
||||
class DebuggerAgentSession: public Thread {
|
||||
public:
|
||||
DebuggerAgentSession(DebuggerAgent* agent, Socket* client)
|
||||
: Thread("v8:DbgAgntSessn"),
|
||||
agent_(agent), client_(client) {}
|
||||
~DebuggerAgentSession();
|
||||
|
||||
void DebuggerMessage(Vector<uint16_t> message);
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
void Run();
|
||||
|
||||
void DebuggerMessage(Vector<char> message);
|
||||
|
||||
DebuggerAgent* agent_;
|
||||
Socket* client_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DebuggerAgentSession);
|
||||
};
|
||||
|
||||
|
||||
// Utility methods factored out to be used by the D8 shell as well.
|
||||
class DebuggerAgentUtil {
|
||||
public:
|
||||
static const char* const kContentLength;
|
||||
|
||||
static SmartArrayPointer<char> ReceiveMessage(Socket* conn);
|
||||
static bool SendConnectMessage(Socket* conn, const char* embedding_host);
|
||||
static bool SendMessage(Socket* conn, const Vector<uint16_t> message);
|
||||
static bool SendMessage(Socket* conn, const v8::Handle<v8::String> message);
|
||||
static int ReceiveAll(Socket* conn, char* data, int len);
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_DEBUG_AGENT_H_
|
121
src/debug.cc
121
src/debug.cc
@ -2632,9 +2632,6 @@ Debugger::Debugger(Isolate* isolate)
|
||||
never_unload_debugger_(false),
|
||||
message_handler_(NULL),
|
||||
debugger_unload_pending_(false),
|
||||
debug_message_dispatch_handler_(NULL),
|
||||
message_dispatch_helper_thread_(NULL),
|
||||
agent_(NULL),
|
||||
command_queue_(isolate->logger(), kQueueInitialSize),
|
||||
command_received_(0),
|
||||
event_command_queue_(isolate->logger(), kQueueInitialSize),
|
||||
@ -3189,7 +3186,7 @@ void Debugger::SetMessageHandler(v8::Debug::MessageHandler handler) {
|
||||
// Send an empty command to the debugger if in a break to make JavaScript
|
||||
// run again if the debugger is closed.
|
||||
if (isolate_->debug()->InDebugger()) {
|
||||
ProcessCommand(Vector<const uint16_t>::empty());
|
||||
EnqueueCommandMessage(Vector<const uint16_t>::empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3211,18 +3208,6 @@ void Debugger::ListenersChanged() {
|
||||
}
|
||||
|
||||
|
||||
void Debugger::SetDebugMessageDispatchHandler(
|
||||
v8::Debug::DebugMessageDispatchHandler handler, bool provide_locker) {
|
||||
LockGuard<Mutex> lock_guard(&dispatch_handler_access_);
|
||||
debug_message_dispatch_handler_ = handler;
|
||||
|
||||
if (provide_locker && message_dispatch_helper_thread_ == NULL) {
|
||||
message_dispatch_helper_thread_ = new MessageDispatchHelperThread(isolate_);
|
||||
message_dispatch_helper_thread_->Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Calls the registered debug message handler. This callback is part of the
|
||||
// public API.
|
||||
void Debugger::InvokeMessageHandler(MessageImpl message) {
|
||||
@ -3238,8 +3223,8 @@ void Debugger::InvokeMessageHandler(MessageImpl message) {
|
||||
// a copy of the command string managed by the debugger. Up to this
|
||||
// point, the command data was managed by the API client. Called
|
||||
// by the API client thread.
|
||||
void Debugger::ProcessCommand(Vector<const uint16_t> command,
|
||||
v8::Debug::ClientData* client_data) {
|
||||
void Debugger::EnqueueCommandMessage(Vector<const uint16_t> command,
|
||||
v8::Debug::ClientData* client_data) {
|
||||
// Need to cast away const.
|
||||
CommandMessage message = CommandMessage::New(
|
||||
Vector<uint16_t>(const_cast<uint16_t*>(command.start()),
|
||||
@ -3253,18 +3238,6 @@ void Debugger::ProcessCommand(Vector<const uint16_t> command,
|
||||
if (!isolate_->debug()->InDebugger()) {
|
||||
isolate_->stack_guard()->RequestDebugCommand();
|
||||
}
|
||||
|
||||
MessageDispatchHelperThread* dispatch_thread;
|
||||
{
|
||||
LockGuard<Mutex> lock_guard(&dispatch_handler_access_);
|
||||
dispatch_thread = message_dispatch_helper_thread_;
|
||||
}
|
||||
|
||||
if (dispatch_thread == NULL) {
|
||||
CallMessageDispatchHandler();
|
||||
} else {
|
||||
dispatch_thread->Schedule();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3312,60 +3285,6 @@ MaybeHandle<Object> Debugger::Call(Handle<JSFunction> fun,
|
||||
}
|
||||
|
||||
|
||||
static void StubMessageHandler2(const v8::Debug::Message& message) {
|
||||
// Simply ignore message.
|
||||
}
|
||||
|
||||
|
||||
bool Debugger::StartAgent(const char* name, int port,
|
||||
bool wait_for_connection) {
|
||||
if (wait_for_connection) {
|
||||
// Suspend V8 if it is already running or set V8 to suspend whenever
|
||||
// it starts.
|
||||
// Provide stub message handler; V8 auto-continues each suspend
|
||||
// when there is no message handler; we doesn't need it.
|
||||
// Once become suspended, V8 will stay so indefinitely long, until remote
|
||||
// debugger connects and issues "continue" command.
|
||||
Debugger::message_handler_ = StubMessageHandler2;
|
||||
v8::Debug::DebugBreak(reinterpret_cast<v8::Isolate*>(isolate_));
|
||||
}
|
||||
|
||||
if (agent_ == NULL) {
|
||||
agent_ = new DebuggerAgent(isolate_, name, port);
|
||||
agent_->Start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Debugger::StopAgent() {
|
||||
if (agent_ != NULL) {
|
||||
agent_->Shutdown();
|
||||
agent_->Join();
|
||||
delete agent_;
|
||||
agent_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Debugger::WaitForAgent() {
|
||||
if (agent_ != NULL)
|
||||
agent_->WaitUntilListening();
|
||||
}
|
||||
|
||||
|
||||
void Debugger::CallMessageDispatchHandler() {
|
||||
v8::Debug::DebugMessageDispatchHandler handler;
|
||||
{
|
||||
LockGuard<Mutex> lock_guard(&dispatch_handler_access_);
|
||||
handler = Debugger::debug_message_dispatch_handler_;
|
||||
}
|
||||
if (handler != NULL) {
|
||||
handler();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EnterDebugger::EnterDebugger(Isolate* isolate)
|
||||
: isolate_(isolate),
|
||||
prev_(isolate_->debug()->debugger_entry()),
|
||||
@ -3703,38 +3622,4 @@ void LockingCommandMessageQueue::Clear() {
|
||||
queue_.Clear();
|
||||
}
|
||||
|
||||
|
||||
MessageDispatchHelperThread::MessageDispatchHelperThread(Isolate* isolate)
|
||||
: Thread("v8:MsgDispHelpr"),
|
||||
isolate_(isolate), sem_(0),
|
||||
already_signalled_(false) {
|
||||
}
|
||||
|
||||
|
||||
void MessageDispatchHelperThread::Schedule() {
|
||||
{
|
||||
LockGuard<Mutex> lock_guard(&mutex_);
|
||||
if (already_signalled_) {
|
||||
return;
|
||||
}
|
||||
already_signalled_ = true;
|
||||
}
|
||||
sem_.Signal();
|
||||
}
|
||||
|
||||
|
||||
void MessageDispatchHelperThread::Run() {
|
||||
while (true) {
|
||||
sem_.Wait();
|
||||
{
|
||||
LockGuard<Mutex> lock_guard(&mutex_);
|
||||
already_signalled_ = false;
|
||||
}
|
||||
{
|
||||
Locker locker(reinterpret_cast<v8::Isolate*>(isolate_));
|
||||
isolate_->debugger()->CallMessageDispatchHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
55
src/debug.h
55
src/debug.h
@ -8,7 +8,6 @@
|
||||
#include "allocation.h"
|
||||
#include "arguments.h"
|
||||
#include "assembler.h"
|
||||
#include "debug-agent.h"
|
||||
#include "execution.h"
|
||||
#include "factory.h"
|
||||
#include "flags.h"
|
||||
@ -783,16 +782,10 @@ class Debugger {
|
||||
bool auto_continue);
|
||||
void SetEventListener(Handle<Object> callback, Handle<Object> data);
|
||||
void SetMessageHandler(v8::Debug::MessageHandler handler);
|
||||
void SetDebugMessageDispatchHandler(
|
||||
v8::Debug::DebugMessageDispatchHandler handler,
|
||||
bool provide_locker);
|
||||
|
||||
// Invoke the message handler function.
|
||||
void InvokeMessageHandler(MessageImpl message);
|
||||
|
||||
// Add a debugger command to the command queue.
|
||||
void ProcessCommand(Vector<const uint16_t> command,
|
||||
v8::Debug::ClientData* client_data = NULL);
|
||||
void EnqueueCommandMessage(Vector<const uint16_t> command,
|
||||
v8::Debug::ClientData* client_data = NULL);
|
||||
|
||||
// Check whether there are commands in the command queue.
|
||||
bool HasCommands();
|
||||
@ -803,18 +796,6 @@ class Debugger {
|
||||
MUST_USE_RESULT MaybeHandle<Object> Call(Handle<JSFunction> fun,
|
||||
Handle<Object> data);
|
||||
|
||||
// Start the debugger agent listening on the provided port.
|
||||
bool StartAgent(const char* name, int port,
|
||||
bool wait_for_connection = false);
|
||||
|
||||
// Stop the debugger agent.
|
||||
void StopAgent();
|
||||
|
||||
// Blocks until the agent has started listening for connections
|
||||
void WaitForAgent();
|
||||
|
||||
void CallMessageDispatchHandler();
|
||||
|
||||
Handle<Context> GetDebugContext();
|
||||
|
||||
// Unload the debugger if possible. Only called when no debugger is currently
|
||||
@ -882,6 +863,9 @@ class Debugger {
|
||||
Handle<Object> event_data);
|
||||
void ListenersChanged();
|
||||
|
||||
// Invoke the message handler function.
|
||||
void InvokeMessageHandler(MessageImpl message);
|
||||
|
||||
RecursiveMutex debugger_access_; // Mutex guarding debugger variables.
|
||||
Handle<Object> event_listener_; // Global handle to listener.
|
||||
Handle<Object> event_listener_data_;
|
||||
@ -892,12 +876,6 @@ class Debugger {
|
||||
v8::Debug::MessageHandler message_handler_;
|
||||
bool debugger_unload_pending_; // Was message handler cleared?
|
||||
|
||||
Mutex dispatch_handler_access_; // Mutex guarding dispatch handler.
|
||||
v8::Debug::DebugMessageDispatchHandler debug_message_dispatch_handler_;
|
||||
MessageDispatchHelperThread* message_dispatch_helper_thread_;
|
||||
|
||||
DebuggerAgent* agent_;
|
||||
|
||||
static const int kQueueInitialSize = 4;
|
||||
LockingCommandMessageQueue command_queue_;
|
||||
Semaphore command_received_; // Signaled for each command received.
|
||||
@ -994,29 +972,6 @@ class Debug_Address {
|
||||
Debug::AddressId id_;
|
||||
};
|
||||
|
||||
// The optional thread that Debug Agent may use to temporary call V8 to process
|
||||
// pending debug requests if debuggee is not running V8 at the moment.
|
||||
// Techincally it does not call V8 itself, rather it asks embedding program
|
||||
// to do this via v8::Debug::HostDispatchHandler
|
||||
class MessageDispatchHelperThread: public Thread {
|
||||
public:
|
||||
explicit MessageDispatchHelperThread(Isolate* isolate);
|
||||
~MessageDispatchHelperThread() {}
|
||||
|
||||
void Schedule();
|
||||
|
||||
private:
|
||||
void Run();
|
||||
|
||||
Isolate* isolate_;
|
||||
Semaphore sem_;
|
||||
Mutex mutex_;
|
||||
bool already_signalled_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessageDispatchHelperThread);
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_DEBUG_H_
|
||||
|
@ -714,6 +714,9 @@ void Execution::DebugBreakHelper(Isolate* isolate) {
|
||||
|
||||
void Execution::ProcessDebugMessages(Isolate* isolate,
|
||||
bool debug_command_only) {
|
||||
// Assert that we are on the main thread of the isolate.
|
||||
ASSERT(ThreadId::Current().Equals(isolate->thread_id()));
|
||||
|
||||
isolate->stack_guard()->ClearDebugCommand();
|
||||
|
||||
StackLimitCheck check(isolate);
|
||||
|
@ -660,10 +660,6 @@ DEFINE_bool(help, false, "Print usage message, including flags, on console")
|
||||
DEFINE_bool(dump_counters, false, "Dump counters on exit")
|
||||
|
||||
DEFINE_bool(debugger, false, "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, "", "Map counters to a file")
|
||||
DEFINE_args(js_arguments,
|
||||
|
@ -75,7 +75,6 @@ typedef void* ExternalReferenceRedirectorPointer();
|
||||
|
||||
class Debug;
|
||||
class Debugger;
|
||||
class DebuggerAgent;
|
||||
|
||||
#if !defined(__arm__) && V8_TARGET_ARCH_ARM || \
|
||||
!defined(__aarch64__) && V8_TARGET_ARCH_ARM64 || \
|
||||
@ -358,7 +357,6 @@ typedef List<HeapObject*> DebugObjectCache;
|
||||
V(bool, fp_stubs_generated, false) \
|
||||
V(int, max_available_threads, 0) \
|
||||
V(uint32_t, per_isolate_assert_data, 0xFFFFFFFFu) \
|
||||
V(DebuggerAgent*, debugger_agent_instance, NULL) \
|
||||
V(InterruptCallback, api_interrupt_callback, NULL) \
|
||||
V(void*, api_interrupt_callback_data, NULL) \
|
||||
ISOLATE_INIT_SIMULATOR_LIST(V)
|
||||
|
@ -5788,242 +5788,6 @@ TEST(DebuggerClearMessageHandlerWhileActive) {
|
||||
}
|
||||
|
||||
|
||||
/* Test DebugMessageDispatch */
|
||||
/* In this test, the V8 thread waits for a message from the debug thread.
|
||||
* The DebugMessageDispatchHandler is executed from the debugger thread
|
||||
* which signals the V8 thread to wake up.
|
||||
*/
|
||||
|
||||
class DebugMessageDispatchV8Thread : public v8::internal::Thread {
|
||||
public:
|
||||
DebugMessageDispatchV8Thread() : Thread("DebugMessageDispatchV8Thread") { }
|
||||
void Run();
|
||||
};
|
||||
|
||||
class DebugMessageDispatchDebuggerThread : public v8::internal::Thread {
|
||||
public:
|
||||
DebugMessageDispatchDebuggerThread()
|
||||
: Thread("DebugMessageDispatchDebuggerThread") { }
|
||||
void Run();
|
||||
};
|
||||
|
||||
Barriers* debug_message_dispatch_barriers;
|
||||
|
||||
|
||||
static void DebugMessageHandler() {
|
||||
debug_message_dispatch_barriers->semaphore_1.Signal();
|
||||
}
|
||||
|
||||
|
||||
void DebugMessageDispatchV8Thread::Run() {
|
||||
v8::Isolate::Scope isolate_scope(CcTest::isolate());
|
||||
DebugLocalContext env;
|
||||
v8::HandleScope scope(env->GetIsolate());
|
||||
|
||||
// Set up debug message dispatch handler.
|
||||
v8::Debug::SetDebugMessageDispatchHandler(DebugMessageHandler);
|
||||
|
||||
CompileRun("var y = 1 + 2;\n");
|
||||
debug_message_dispatch_barriers->barrier_1.Wait();
|
||||
debug_message_dispatch_barriers->semaphore_1.Wait();
|
||||
debug_message_dispatch_barriers->barrier_2.Wait();
|
||||
}
|
||||
|
||||
|
||||
void DebugMessageDispatchDebuggerThread::Run() {
|
||||
debug_message_dispatch_barriers->barrier_1.Wait();
|
||||
SendContinueCommand();
|
||||
debug_message_dispatch_barriers->barrier_2.Wait();
|
||||
}
|
||||
|
||||
|
||||
TEST(DebuggerDebugMessageDispatch) {
|
||||
DebugMessageDispatchDebuggerThread debug_message_dispatch_debugger_thread;
|
||||
DebugMessageDispatchV8Thread debug_message_dispatch_v8_thread;
|
||||
|
||||
// Create a V8 environment
|
||||
Barriers stack_allocated_debug_message_dispatch_barriers;
|
||||
debug_message_dispatch_barriers =
|
||||
&stack_allocated_debug_message_dispatch_barriers;
|
||||
|
||||
debug_message_dispatch_v8_thread.Start();
|
||||
debug_message_dispatch_debugger_thread.Start();
|
||||
|
||||
debug_message_dispatch_v8_thread.Join();
|
||||
debug_message_dispatch_debugger_thread.Join();
|
||||
}
|
||||
|
||||
|
||||
TEST(DebuggerAgent) {
|
||||
v8::V8::Initialize();
|
||||
i::Debugger* debugger = CcTest::i_isolate()->debugger();
|
||||
// Make sure these ports is not used by other tests to allow tests to run in
|
||||
// parallel.
|
||||
const int kPort1 = 5858 + FlagDependentPortOffset();
|
||||
const int kPort2 = 5857 + FlagDependentPortOffset();
|
||||
const int kPort3 = 5856 + FlagDependentPortOffset();
|
||||
|
||||
// Make a string with the port2 number.
|
||||
const int kPortBufferLen = 6;
|
||||
char port2_str[kPortBufferLen];
|
||||
OS::SNPrintF(i::Vector<char>(port2_str, kPortBufferLen), "%d", kPort2);
|
||||
|
||||
bool ok;
|
||||
|
||||
// Test starting and stopping the agent without any client connection.
|
||||
debugger->StartAgent("test", kPort1);
|
||||
debugger->StopAgent();
|
||||
// Test starting the agent, connecting a client and shutting down the agent
|
||||
// with the client connected.
|
||||
ok = debugger->StartAgent("test", kPort2);
|
||||
CHECK(ok);
|
||||
debugger->WaitForAgent();
|
||||
i::Socket* client = new i::Socket;
|
||||
ok = client->Connect("localhost", port2_str);
|
||||
CHECK(ok);
|
||||
// It is important to wait for a message from the agent. Otherwise,
|
||||
// we can close the server socket during "accept" syscall, making it failing
|
||||
// (at least on Linux), and the test will work incorrectly.
|
||||
char buf;
|
||||
ok = client->Receive(&buf, 1) == 1;
|
||||
CHECK(ok);
|
||||
debugger->StopAgent();
|
||||
delete client;
|
||||
|
||||
// Test starting and stopping the agent with the required port already
|
||||
// occoupied.
|
||||
i::Socket* server = new i::Socket;
|
||||
ok = server->Bind(kPort3);
|
||||
CHECK(ok);
|
||||
|
||||
debugger->StartAgent("test", kPort3);
|
||||
debugger->StopAgent();
|
||||
|
||||
delete server;
|
||||
}
|
||||
|
||||
|
||||
class DebuggerAgentProtocolServerThread : public i::Thread {
|
||||
public:
|
||||
explicit DebuggerAgentProtocolServerThread(int port)
|
||||
: Thread("DebuggerAgentProtocolServerThread"),
|
||||
port_(port),
|
||||
server_(NULL),
|
||||
client_(NULL),
|
||||
listening_(0) {
|
||||
}
|
||||
~DebuggerAgentProtocolServerThread() {
|
||||
// Close both sockets.
|
||||
delete client_;
|
||||
delete server_;
|
||||
}
|
||||
|
||||
void Run();
|
||||
void WaitForListening() { listening_.Wait(); }
|
||||
char* body() { return body_.get(); }
|
||||
|
||||
private:
|
||||
int port_;
|
||||
i::SmartArrayPointer<char> body_;
|
||||
i::Socket* server_; // Server socket used for bind/accept.
|
||||
i::Socket* client_; // Single client connection used by the test.
|
||||
i::Semaphore listening_; // Signalled when the server is in listen mode.
|
||||
};
|
||||
|
||||
|
||||
void DebuggerAgentProtocolServerThread::Run() {
|
||||
bool ok;
|
||||
|
||||
// Create the server socket and bind it to the requested port.
|
||||
server_ = new i::Socket;
|
||||
CHECK(server_ != NULL);
|
||||
ok = server_->Bind(port_);
|
||||
CHECK(ok);
|
||||
|
||||
// Listen for new connections.
|
||||
ok = server_->Listen(1);
|
||||
CHECK(ok);
|
||||
listening_.Signal();
|
||||
|
||||
// Accept a connection.
|
||||
client_ = server_->Accept();
|
||||
CHECK(client_ != NULL);
|
||||
|
||||
// Receive a debugger agent protocol message.
|
||||
i::DebuggerAgentUtil::ReceiveMessage(client_);
|
||||
}
|
||||
|
||||
|
||||
TEST(DebuggerAgentProtocolOverflowHeader) {
|
||||
// Make sure this port is not used by other tests to allow tests to run in
|
||||
// parallel.
|
||||
const int kPort = 5860 + FlagDependentPortOffset();
|
||||
static const char* kLocalhost = "localhost";
|
||||
|
||||
// Make a string with the port number.
|
||||
const int kPortBufferLen = 6;
|
||||
char port_str[kPortBufferLen];
|
||||
OS::SNPrintF(i::Vector<char>(port_str, kPortBufferLen), "%d", kPort);
|
||||
|
||||
// Create a socket server to receive a debugger agent message.
|
||||
DebuggerAgentProtocolServerThread* server =
|
||||
new DebuggerAgentProtocolServerThread(kPort);
|
||||
server->Start();
|
||||
server->WaitForListening();
|
||||
|
||||
// Connect.
|
||||
i::Socket* client = new i::Socket;
|
||||
CHECK(client != NULL);
|
||||
bool ok = client->Connect(kLocalhost, port_str);
|
||||
CHECK(ok);
|
||||
|
||||
// Send headers which overflow the receive buffer.
|
||||
static const int kBufferSize = 1000;
|
||||
char buffer[kBufferSize];
|
||||
|
||||
// Long key and short value: XXXX....XXXX:0\r\n.
|
||||
for (int i = 0; i < kBufferSize - 4; i++) {
|
||||
buffer[i] = 'X';
|
||||
}
|
||||
buffer[kBufferSize - 4] = ':';
|
||||
buffer[kBufferSize - 3] = '0';
|
||||
buffer[kBufferSize - 2] = '\r';
|
||||
buffer[kBufferSize - 1] = '\n';
|
||||
int result = client->Send(buffer, kBufferSize);
|
||||
CHECK_EQ(kBufferSize, result);
|
||||
|
||||
// Short key and long value: X:XXXX....XXXX\r\n.
|
||||
buffer[0] = 'X';
|
||||
buffer[1] = ':';
|
||||
for (int i = 2; i < kBufferSize - 2; i++) {
|
||||
buffer[i] = 'X';
|
||||
}
|
||||
buffer[kBufferSize - 2] = '\r';
|
||||
buffer[kBufferSize - 1] = '\n';
|
||||
result = client->Send(buffer, kBufferSize);
|
||||
CHECK_EQ(kBufferSize, result);
|
||||
|
||||
// Add empty body to request.
|
||||
const char* content_length_zero_header = "Content-Length:0\r\n";
|
||||
int length = StrLength(content_length_zero_header);
|
||||
result = client->Send(content_length_zero_header, length);
|
||||
CHECK_EQ(length, result);
|
||||
result = client->Send("\r\n", 2);
|
||||
CHECK_EQ(2, result);
|
||||
|
||||
// Wait until data is received.
|
||||
server->Join();
|
||||
|
||||
// Check for empty body.
|
||||
CHECK(server->body() == NULL);
|
||||
|
||||
// Close the client before the server to avoid TIME_WAIT issues.
|
||||
client->Shutdown();
|
||||
delete client;
|
||||
delete server;
|
||||
}
|
||||
|
||||
|
||||
// Test for issue http://code.google.com/p/v8/issues/detail?id=289.
|
||||
// Make sure that DebugGetLoadedScripts doesn't return scripts
|
||||
// with disposed external source.
|
||||
@ -6783,7 +6547,7 @@ TEST(NoDebugBreakInAfterCompileMessageHandler) {
|
||||
static int counting_message_handler_counter;
|
||||
|
||||
static void CountingMessageHandler(const v8::Debug::Message& message) {
|
||||
counting_message_handler_counter++;
|
||||
if (message.IsResponse()) counting_message_handler_counter++;
|
||||
}
|
||||
|
||||
|
||||
@ -6830,6 +6594,83 @@ TEST(ProcessDebugMessages) {
|
||||
}
|
||||
|
||||
|
||||
class SendCommandThread : public v8::internal::Thread {
|
||||
public:
|
||||
explicit SendCommandThread(v8::Isolate* isolate)
|
||||
: Thread("SendCommandThread"),
|
||||
semaphore_(0),
|
||||
isolate_(isolate) { }
|
||||
|
||||
static void ProcessDebugMessages(v8::Isolate* isolate, void* data) {
|
||||
v8::Debug::ProcessDebugMessages();
|
||||
reinterpret_cast<v8::internal::Semaphore*>(data)->Signal();
|
||||
}
|
||||
|
||||
virtual void Run() {
|
||||
semaphore_.Wait();
|
||||
const int kBufferSize = 1000;
|
||||
uint16_t buffer[kBufferSize];
|
||||
const char* scripts_command =
|
||||
"{\"seq\":0,"
|
||||
"\"type\":\"request\","
|
||||
"\"command\":\"scripts\"}";
|
||||
int length = AsciiToUtf16(scripts_command, buffer);
|
||||
// Send scripts command.
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
CHECK_EQ(i, counting_message_handler_counter);
|
||||
// Queue debug message.
|
||||
v8::Debug::SendCommand(isolate_, buffer, length);
|
||||
// Synchronize with the main thread to force message processing.
|
||||
isolate_->RequestInterrupt(ProcessDebugMessages, &semaphore_);
|
||||
semaphore_.Wait();
|
||||
}
|
||||
|
||||
v8::V8::TerminateExecution(isolate_);
|
||||
}
|
||||
|
||||
void StartSending() {
|
||||
semaphore_.Signal();
|
||||
}
|
||||
|
||||
private:
|
||||
v8::internal::Semaphore semaphore_;
|
||||
v8::Isolate* isolate_;
|
||||
};
|
||||
|
||||
|
||||
static SendCommandThread* send_command_thread_ = NULL;
|
||||
|
||||
static void StartSendingCommands(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
send_command_thread_->StartSending();
|
||||
}
|
||||
|
||||
|
||||
TEST(ProcessDebugMessagesThreaded) {
|
||||
DebugLocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
|
||||
counting_message_handler_counter = 0;
|
||||
|
||||
v8::Debug::SetMessageHandler2(CountingMessageHandler);
|
||||
send_command_thread_ = new SendCommandThread(isolate);
|
||||
send_command_thread_->Start();
|
||||
|
||||
v8::Handle<v8::FunctionTemplate> start =
|
||||
v8::FunctionTemplate::New(isolate, StartSendingCommands);
|
||||
env->Global()->Set(v8_str("start"), start->GetFunction());
|
||||
|
||||
CompileRun("start(); while (true) { }");
|
||||
|
||||
CHECK_EQ(100, counting_message_handler_counter);
|
||||
|
||||
v8::Debug::SetMessageHandler2(NULL);
|
||||
CheckDebuggerUnloaded();
|
||||
}
|
||||
|
||||
|
||||
struct BacktraceData {
|
||||
static int frame_counter;
|
||||
static void MessageHandler(const v8::Debug::Message& message) {
|
||||
|
@ -313,8 +313,6 @@
|
||||
'../../src/dateparser-inl.h',
|
||||
'../../src/dateparser.cc',
|
||||
'../../src/dateparser.h',
|
||||
'../../src/debug-agent.cc',
|
||||
'../../src/debug-agent.h',
|
||||
'../../src/debug.cc',
|
||||
'../../src/debug.h',
|
||||
'../../src/deoptimizer.cc',
|
||||
|
Loading…
Reference in New Issue
Block a user