1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #ifdef ENABLE_DEBUGGER_SUPPORT
32 #include "debug-agent.h"
33 #include "platform/socket.h"
38 static bool was_running = true;
40 void PrintPrompt(bool is_running) {
41 const char* prompt = is_running? "> " : "dbg> ";
42 was_running = is_running;
49 PrintPrompt(was_running);
53 void HandleDebugEvent(const Debug::EventDetails& event_details) {
54 // TODO(svenpanne) There should be a way to retrieve this in the callback.
55 Isolate* isolate = Isolate::GetCurrent();
56 HandleScope scope(isolate);
58 DebugEvent event = event_details.GetEvent();
59 // Check for handled event.
60 if (event != Break && event != Exception && event != AfterCompile) {
66 // Get the toJSONProtocol function on the event and get the JSON format.
67 Local<String> to_json_fun_name =
68 String::NewFromUtf8(isolate, "toJSONProtocol");
69 Handle<Object> event_data = event_details.GetEventData();
70 Local<Function> to_json_fun =
71 Local<Function>::Cast(event_data->Get(to_json_fun_name));
72 Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
73 if (try_catch.HasCaught()) {
74 Shell::ReportException(isolate, &try_catch);
78 // Print the event details.
79 Handle<Object> details =
80 Shell::DebugMessageDetails(isolate, Handle<String>::Cast(event_json));
81 if (try_catch.HasCaught()) {
82 Shell::ReportException(isolate, &try_catch);
85 String::Utf8Value str(details->Get(String::NewFromUtf8(isolate, "text")));
86 if (str.length() == 0) {
87 // Empty string is used to signal not to process this event.
92 // Get the debug command processor.
93 Local<String> fun_name =
94 String::NewFromUtf8(isolate, "debugCommandProcessor");
95 Handle<Object> exec_state = event_details.GetExecutionState();
96 Local<Function> fun = Local<Function>::Cast(exec_state->Get(fun_name));
97 Local<Object> cmd_processor =
98 Local<Object>::Cast(fun->Call(exec_state, 0, NULL));
99 if (try_catch.HasCaught()) {
100 Shell::ReportException(isolate, &try_catch);
104 static const int kBufferSize = 256;
105 bool running = false;
107 char command[kBufferSize];
108 PrintPrompt(running);
109 char* str = fgets(command, kBufferSize, stdin);
110 if (str == NULL) break;
112 // Ignore empty commands.
113 if (strlen(command) == 0) continue;
117 // Convert the debugger command to a JSON debugger request.
118 Handle<Value> request = Shell::DebugCommandToJSONRequest(
119 isolate, String::NewFromUtf8(isolate, command));
120 if (try_catch.HasCaught()) {
121 Shell::ReportException(isolate, &try_catch);
125 // If undefined is returned the command was handled internally and there is
127 if (request->IsUndefined()) {
131 Handle<String> fun_name;
132 Handle<Function> fun;
133 // All the functions used below take one argument.
134 static const int kArgc = 1;
135 Handle<Value> args[kArgc];
137 // Invoke the JavaScript to convert the debug command line to a JSON
138 // request, invoke the JSON request and convert the JSON respose to a text
140 fun_name = String::NewFromUtf8(isolate, "processDebugRequest");
141 fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
143 Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
144 if (try_catch.HasCaught()) {
145 Shell::ReportException(isolate, &try_catch);
148 Handle<String> response = Handle<String>::Cast(response_val);
150 // Convert the debugger response into text details and the running state.
151 Handle<Object> response_details =
152 Shell::DebugMessageDetails(isolate, response);
153 if (try_catch.HasCaught()) {
154 Shell::ReportException(isolate, &try_catch);
157 String::Utf8Value text_str(
158 response_details->Get(String::NewFromUtf8(isolate, "text")));
159 if (text_str.length() > 0) {
160 printf("%s\n", *text_str);
162 running = response_details->Get(String::NewFromUtf8(isolate, "running"))
169 void RunRemoteDebugger(Isolate* isolate, int port) {
170 RemoteDebugger debugger(isolate, port);
175 void RemoteDebugger::Run() {
178 // Connect to the debugger agent.
179 conn_ = new i::Socket;
180 static const int kPortStrSize = 6;
181 char port_str[kPortStrSize];
182 i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
183 ok = conn_->Connect("localhost", port_str);
185 printf("Unable to connect to debug agent %d\n", i::Socket::GetLastError());
189 // Start the receiver thread.
190 ReceiverThread receiver(this);
193 // Start the keyboard thread.
194 KeyboardThread keyboard(this);
198 // Process events received from debugged VM and from the keyboard.
199 bool terminate = false;
201 event_available_.Wait();
202 RemoteDebuggerEvent* event = GetEvent();
203 switch (event->type()) {
204 case RemoteDebuggerEvent::kMessage:
205 HandleMessageReceived(event->data());
207 case RemoteDebuggerEvent::kKeyboard:
208 HandleKeyboardCommand(event->data());
210 case RemoteDebuggerEvent::kDisconnect:
220 // Wait for the receiver thread to end.
225 void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
226 RemoteDebuggerEvent* event =
227 new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
232 void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
233 RemoteDebuggerEvent* event =
234 new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
239 void RemoteDebugger::ConnectionClosed() {
240 RemoteDebuggerEvent* event =
241 new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
242 i::SmartArrayPointer<char>());
247 void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
248 i::LockGuard<i::Mutex> lock_guard(&event_access_);
250 ASSERT(tail_ == NULL);
254 ASSERT(tail_ != NULL);
255 tail_->set_next(event);
258 event_available_.Signal();
262 RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
263 i::LockGuard<i::Mutex> lock_guard(&event_access_);
264 ASSERT(head_ != NULL);
265 RemoteDebuggerEvent* result = head_;
266 head_ = head_->next();
268 ASSERT(tail_ == result);
275 void RemoteDebugger::HandleMessageReceived(char* message) {
276 Locker lock(isolate_);
277 HandleScope scope(isolate_);
279 // Print the event details.
281 Handle<Object> details = Shell::DebugMessageDetails(
282 isolate_, Handle<String>::Cast(String::NewFromUtf8(isolate_, message)));
283 if (try_catch.HasCaught()) {
284 Shell::ReportException(isolate_, &try_catch);
288 String::Utf8Value str(details->Get(String::NewFromUtf8(isolate_, "text")));
289 if (str.length() == 0) {
290 // Empty string is used to signal not to process this event.
294 printf("%s\n", *str);
299 bool is_running = details->Get(String::NewFromUtf8(isolate_, "running"))
302 PrintPrompt(is_running);
306 void RemoteDebugger::HandleKeyboardCommand(char* command) {
307 Locker lock(isolate_);
308 HandleScope scope(isolate_);
310 // Convert the debugger command to a JSON debugger request.
312 Handle<Value> request = Shell::DebugCommandToJSONRequest(
313 isolate_, String::NewFromUtf8(isolate_, command));
314 if (try_catch.HasCaught()) {
315 Shell::ReportException(isolate_, &try_catch);
320 // If undefined is returned the command was handled internally and there is
322 if (request->IsUndefined()) {
327 // Send the JSON debugger request.
328 i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
332 void ReceiverThread::Run() {
333 // Receive the connect message (with empty body).
334 i::SmartArrayPointer<char> message =
335 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
336 ASSERT(message.get() == NULL);
339 // Receive a message.
340 i::SmartArrayPointer<char> message =
341 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
342 if (message.get() == NULL) {
343 remote_debugger_->ConnectionClosed();
347 // Pass the message to the main thread.
348 remote_debugger_->MessageReceived(message);
353 void KeyboardThread::Run() {
354 static const int kBufferSize = 256;
356 // read keyboard input.
357 char command[kBufferSize];
358 char* str = fgets(command, kBufferSize, stdin);
363 // Pass the keyboard command to the main thread.
364 remote_debugger_->KeyboardCommand(
365 i::SmartArrayPointer<char>(i::StrDup(command)));
372 #endif // ENABLE_DEBUGGER_SUPPORT