blob: 2c909fa7622703421f0960c9b82eb2fbe26ca07e [file] [log] [blame]
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001// Copyright 2012 the V8 project authors. All rights reserved.
ager@chromium.org32912102009-01-16 10:38:43 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
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.
15//
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.
27
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +000028#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org32912102009-01-16 10:38:43 +000029
30#include "d8.h"
31#include "d8-debug.h"
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000032#include "debug-agent.h"
rossberg@chromium.org9ed27462014-01-07 14:16:41 +000033#include "platform/socket.h"
ager@chromium.org32912102009-01-16 10:38:43 +000034
35
36namespace v8 {
37
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +000038static bool was_running = true;
39
40void PrintPrompt(bool is_running) {
41 const char* prompt = is_running? "> " : "dbg> ";
42 was_running = is_running;
43 printf("%s", prompt);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000044 fflush(stdout);
45}
46
ager@chromium.org32912102009-01-16 10:38:43 +000047
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +000048void PrintPrompt() {
49 PrintPrompt(was_running);
50}
51
52
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +000053void HandleDebugEvent(const Debug::EventDetails& event_details) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000054 // TODO(svenpanne) There should be a way to retrieve this in the callback.
55 Isolate* isolate = Isolate::GetCurrent();
56 HandleScope scope(isolate);
ager@chromium.org32912102009-01-16 10:38:43 +000057
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +000058 DebugEvent event = event_details.GetEvent();
iposva@chromium.org245aa852009-02-10 00:49:54 +000059 // Check for handled event.
60 if (event != Break && event != Exception && event != AfterCompile) {
61 return;
62 }
ager@chromium.org32912102009-01-16 10:38:43 +000063
64 TryCatch try_catch;
65
kasperl@chromium.org061ef742009-02-27 12:16:20 +000066 // Get the toJSONProtocol function on the event and get the JSON format.
machenbach@chromium.orgf9841892013-11-25 12:01:13 +000067 Local<String> to_json_fun_name =
68 String::NewFromUtf8(isolate, "toJSONProtocol");
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +000069 Handle<Object> event_data = event_details.GetEventData();
kasperl@chromium.org061ef742009-02-27 12:16:20 +000070 Local<Function> to_json_fun =
danno@chromium.orgf95d4b92013-06-13 14:40:17 +000071 Local<Function>::Cast(event_data->Get(to_json_fun_name));
kasperl@chromium.org061ef742009-02-27 12:16:20 +000072 Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
73 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000074 Shell::ReportException(isolate, &try_catch);
kasperl@chromium.org061ef742009-02-27 12:16:20 +000075 return;
76 }
77
ager@chromium.org32912102009-01-16 10:38:43 +000078 // Print the event details.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000079 Handle<Object> details =
ulan@chromium.org57ff8812013-05-10 08:16:55 +000080 Shell::DebugMessageDetails(isolate, Handle<String>::Cast(event_json));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000081 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000082 Shell::ReportException(isolate, &try_catch);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000083 return;
84 }
machenbach@chromium.orgf9841892013-11-25 12:01:13 +000085 String::Utf8Value str(details->Get(String::NewFromUtf8(isolate, "text")));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000086 if (str.length() == 0) {
iposva@chromium.org245aa852009-02-10 00:49:54 +000087 // Empty string is used to signal not to process this event.
88 return;
89 }
ager@chromium.org32912102009-01-16 10:38:43 +000090 printf("%s\n", *str);
91
92 // Get the debug command processor.
machenbach@chromium.orgf9841892013-11-25 12:01:13 +000093 Local<String> fun_name =
94 String::NewFromUtf8(isolate, "debugCommandProcessor");
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +000095 Handle<Object> exec_state = event_details.GetExecutionState();
danno@chromium.orgf95d4b92013-06-13 14:40:17 +000096 Local<Function> fun = Local<Function>::Cast(exec_state->Get(fun_name));
ager@chromium.org32912102009-01-16 10:38:43 +000097 Local<Object> cmd_processor =
danno@chromium.orgf95d4b92013-06-13 14:40:17 +000098 Local<Object>::Cast(fun->Call(exec_state, 0, NULL));
ager@chromium.org32912102009-01-16 10:38:43 +000099 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000100 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +0000101 return;
102 }
103
104 static const int kBufferSize = 256;
105 bool running = false;
106 while (!running) {
107 char command[kBufferSize];
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +0000108 PrintPrompt(running);
ager@chromium.org32912102009-01-16 10:38:43 +0000109 char* str = fgets(command, kBufferSize, stdin);
110 if (str == NULL) break;
111
112 // Ignore empty commands.
113 if (strlen(command) == 0) continue;
114
115 TryCatch try_catch;
116
117 // Convert the debugger command to a JSON debugger request.
machenbach@chromium.orgf9841892013-11-25 12:01:13 +0000118 Handle<Value> request = Shell::DebugCommandToJSONRequest(
119 isolate, String::NewFromUtf8(isolate, command));
ager@chromium.org32912102009-01-16 10:38:43 +0000120 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000121 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +0000122 continue;
123 }
124
125 // If undefined is returned the command was handled internally and there is
126 // no JSON to send.
127 if (request->IsUndefined()) {
128 continue;
129 }
130
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];
136
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
139 // representation.
machenbach@chromium.orgf9841892013-11-25 12:01:13 +0000140 fun_name = String::NewFromUtf8(isolate, "processDebugRequest");
ager@chromium.org32912102009-01-16 10:38:43 +0000141 fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
142 args[0] = request;
143 Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
144 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000145 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +0000146 continue;
147 }
148 Handle<String> response = Handle<String>::Cast(response_val);
149
150 // Convert the debugger response into text details and the running state.
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000151 Handle<Object> response_details =
152 Shell::DebugMessageDetails(isolate, response);
ager@chromium.org32912102009-01-16 10:38:43 +0000153 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000154 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +0000155 continue;
156 }
machenbach@chromium.orgf9841892013-11-25 12:01:13 +0000157 String::Utf8Value text_str(
158 response_details->Get(String::NewFromUtf8(isolate, "text")));
ager@chromium.org32912102009-01-16 10:38:43 +0000159 if (text_str.length() > 0) {
160 printf("%s\n", *text_str);
161 }
machenbach@chromium.orgf9841892013-11-25 12:01:13 +0000162 running = response_details->Get(String::NewFromUtf8(isolate, "running"))
163 ->ToBoolean()
164 ->Value();
ager@chromium.org32912102009-01-16 10:38:43 +0000165 }
166}
167
168
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000169void RunRemoteDebugger(Isolate* isolate, int port) {
170 RemoteDebugger debugger(isolate, port);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000171 debugger.Run();
172}
173
174
175void RemoteDebugger::Run() {
176 bool ok;
177
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000178 // Connect to the debugger agent.
jkummerow@chromium.org3d00d0a2013-09-04 13:57:32 +0000179 conn_ = new i::Socket;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000180 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);
184 if (!ok) {
jkummerow@chromium.org3d00d0a2013-09-04 13:57:32 +0000185 printf("Unable to connect to debug agent %d\n", i::Socket::GetLastError());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000186 return;
187 }
188
189 // Start the receiver thread.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000190 ReceiverThread receiver(this);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000191 receiver.Start();
192
193 // Start the keyboard thread.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000194 KeyboardThread keyboard(this);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000195 keyboard.Start();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000196 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000197
198 // Process events received from debugged VM and from the keyboard.
199 bool terminate = false;
200 while (!terminate) {
mstarzinger@chromium.orge9000182013-09-03 11:25:39 +0000201 event_available_.Wait();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000202 RemoteDebuggerEvent* event = GetEvent();
203 switch (event->type()) {
204 case RemoteDebuggerEvent::kMessage:
205 HandleMessageReceived(event->data());
206 break;
207 case RemoteDebuggerEvent::kKeyboard:
208 HandleKeyboardCommand(event->data());
209 break;
210 case RemoteDebuggerEvent::kDisconnect:
211 terminate = true;
212 break;
213
214 default:
215 UNREACHABLE();
216 }
217 delete event;
218 }
219
220 // Wait for the receiver thread to end.
221 receiver.Join();
222}
223
224
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000225void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000226 RemoteDebuggerEvent* event =
227 new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
228 AddEvent(event);
229}
230
231
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000232void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000233 RemoteDebuggerEvent* event =
234 new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
235 AddEvent(event);
236}
237
238
239void RemoteDebugger::ConnectionClosed() {
240 RemoteDebuggerEvent* event =
241 new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000242 i::SmartArrayPointer<char>());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000243 AddEvent(event);
244}
245
246
247void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
jkummerow@chromium.orgdc94e192013-08-30 11:35:42 +0000248 i::LockGuard<i::Mutex> lock_guard(&event_access_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000249 if (head_ == NULL) {
250 ASSERT(tail_ == NULL);
251 head_ = event;
252 tail_ = event;
253 } else {
254 ASSERT(tail_ != NULL);
255 tail_->set_next(event);
256 tail_ = event;
257 }
mstarzinger@chromium.orge9000182013-09-03 11:25:39 +0000258 event_available_.Signal();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000259}
260
261
262RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
jkummerow@chromium.orgdc94e192013-08-30 11:35:42 +0000263 i::LockGuard<i::Mutex> lock_guard(&event_access_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000264 ASSERT(head_ != NULL);
265 RemoteDebuggerEvent* result = head_;
266 head_ = head_->next();
267 if (head_ == NULL) {
268 ASSERT(tail_ == result);
269 tail_ = NULL;
270 }
271 return result;
272}
273
274
275void RemoteDebugger::HandleMessageReceived(char* message) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000276 Locker lock(isolate_);
277 HandleScope scope(isolate_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000278
279 // Print the event details.
280 TryCatch try_catch;
machenbach@chromium.orgf9841892013-11-25 12:01:13 +0000281 Handle<Object> details = Shell::DebugMessageDetails(
282 isolate_, Handle<String>::Cast(String::NewFromUtf8(isolate_, message)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000283 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000284 Shell::ReportException(isolate_, &try_catch);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000285 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000286 return;
287 }
machenbach@chromium.orgf9841892013-11-25 12:01:13 +0000288 String::Utf8Value str(details->Get(String::NewFromUtf8(isolate_, "text")));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000289 if (str.length() == 0) {
290 // Empty string is used to signal not to process this event.
291 return;
292 }
293 if (*str != NULL) {
294 printf("%s\n", *str);
295 } else {
296 printf("???\n");
297 }
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +0000298
machenbach@chromium.orgf9841892013-11-25 12:01:13 +0000299 bool is_running = details->Get(String::NewFromUtf8(isolate_, "running"))
300 ->ToBoolean()
301 ->Value();
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +0000302 PrintPrompt(is_running);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000303}
304
305
306void RemoteDebugger::HandleKeyboardCommand(char* command) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000307 Locker lock(isolate_);
308 HandleScope scope(isolate_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000309
310 // Convert the debugger command to a JSON debugger request.
311 TryCatch try_catch;
machenbach@chromium.orgf9841892013-11-25 12:01:13 +0000312 Handle<Value> request = Shell::DebugCommandToJSONRequest(
313 isolate_, String::NewFromUtf8(isolate_, command));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000314 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000315 Shell::ReportException(isolate_, &try_catch);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000316 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000317 return;
318 }
319
320 // If undefined is returned the command was handled internally and there is
321 // no JSON to send.
322 if (request->IsUndefined()) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000323 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000324 return;
325 }
326
327 // Send the JSON debugger request.
328 i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
329}
330
331
332void ReceiverThread::Run() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000333 // Receive the connect message (with empty body).
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000334 i::SmartArrayPointer<char> message =
335 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
machenbach@chromium.orgafbdadc2013-12-09 16:12:18 +0000336 ASSERT(message.get() == NULL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000337
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000338 while (true) {
339 // Receive a message.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000340 i::SmartArrayPointer<char> message =
341 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
machenbach@chromium.orgafbdadc2013-12-09 16:12:18 +0000342 if (message.get() == NULL) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000343 remote_debugger_->ConnectionClosed();
344 return;
345 }
346
347 // Pass the message to the main thread.
348 remote_debugger_->MessageReceived(message);
349 }
350}
351
352
353void KeyboardThread::Run() {
354 static const int kBufferSize = 256;
355 while (true) {
356 // read keyboard input.
357 char command[kBufferSize];
358 char* str = fgets(command, kBufferSize, stdin);
359 if (str == NULL) {
360 break;
361 }
362
363 // Pass the keyboard command to the main thread.
364 remote_debugger_->KeyboardCommand(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000365 i::SmartArrayPointer<char>(i::StrDup(command)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000366 }
367}
368
369
ager@chromium.org32912102009-01-16 10:38:43 +0000370} // namespace v8
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000371
372#endif // ENABLE_DEBUGGER_SUPPORT