blob: e1d29d98efb812b1d861fb115754c3e084b7c724 [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 "platform.h"
33#include "debug-agent.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
ager@chromium.org32912102009-01-16 10:38:43 +000053void HandleDebugEvent(DebugEvent event,
54 Handle<Object> exec_state,
55 Handle<Object> event_data,
56 Handle<Value> data) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000057 // TODO(svenpanne) There should be a way to retrieve this in the callback.
58 Isolate* isolate = Isolate::GetCurrent();
59 HandleScope scope(isolate);
ager@chromium.org32912102009-01-16 10:38:43 +000060
iposva@chromium.org245aa852009-02-10 00:49:54 +000061 // Check for handled event.
62 if (event != Break && event != Exception && event != AfterCompile) {
63 return;
64 }
ager@chromium.org32912102009-01-16 10:38:43 +000065
66 TryCatch try_catch;
67
kasperl@chromium.org061ef742009-02-27 12:16:20 +000068 // Get the toJSONProtocol function on the event and get the JSON format.
69 Local<String> to_json_fun_name = String::New("toJSONProtocol");
70 Local<Function> to_json_fun =
71 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()) {
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 }
85 String::Utf8Value str(details->Get(String::New("text")));
86 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.
93 Local<String> fun_name = String::New("debugCommandProcessor");
94 Local<Function> fun = Function::Cast(*exec_state->Get(fun_name));
95 Local<Object> cmd_processor =
96 Object::Cast(*fun->Call(exec_state, 0, NULL));
97 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000098 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +000099 return;
100 }
101
102 static const int kBufferSize = 256;
103 bool running = false;
104 while (!running) {
105 char command[kBufferSize];
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +0000106 PrintPrompt(running);
ager@chromium.org32912102009-01-16 10:38:43 +0000107 char* str = fgets(command, kBufferSize, stdin);
108 if (str == NULL) break;
109
110 // Ignore empty commands.
111 if (strlen(command) == 0) continue;
112
113 TryCatch try_catch;
114
115 // Convert the debugger command to a JSON debugger request.
116 Handle<Value> request =
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000117 Shell::DebugCommandToJSONRequest(isolate, String::New(command));
ager@chromium.org32912102009-01-16 10:38:43 +0000118 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000119 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +0000120 continue;
121 }
122
123 // If undefined is returned the command was handled internally and there is
124 // no JSON to send.
125 if (request->IsUndefined()) {
126 continue;
127 }
128
129 Handle<String> fun_name;
130 Handle<Function> fun;
131 // All the functions used below take one argument.
132 static const int kArgc = 1;
133 Handle<Value> args[kArgc];
134
135 // Invoke the JavaScript to convert the debug command line to a JSON
136 // request, invoke the JSON request and convert the JSON respose to a text
137 // representation.
138 fun_name = String::New("processDebugRequest");
139 fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
140 args[0] = request;
141 Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
142 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000143 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +0000144 continue;
145 }
146 Handle<String> response = Handle<String>::Cast(response_val);
147
148 // Convert the debugger response into text details and the running state.
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000149 Handle<Object> response_details =
150 Shell::DebugMessageDetails(isolate, response);
ager@chromium.org32912102009-01-16 10:38:43 +0000151 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000152 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +0000153 continue;
154 }
155 String::Utf8Value text_str(response_details->Get(String::New("text")));
156 if (text_str.length() > 0) {
157 printf("%s\n", *text_str);
158 }
159 running =
160 response_details->Get(String::New("running"))->ToBoolean()->Value();
161 }
162}
163
164
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000165void RunRemoteDebugger(Isolate* isolate, int port) {
166 RemoteDebugger debugger(isolate, port);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000167 debugger.Run();
168}
169
170
171void RemoteDebugger::Run() {
172 bool ok;
173
174 // Make sure that socket support is initialized.
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000175 ok = i::Socket::SetUp();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000176 if (!ok) {
177 printf("Unable to initialize socket support %d\n", i::Socket::LastError());
178 return;
179 }
180
181 // Connect to the debugger agent.
182 conn_ = i::OS::CreateSocket();
183 static const int kPortStrSize = 6;
184 char port_str[kPortStrSize];
185 i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
186 ok = conn_->Connect("localhost", port_str);
187 if (!ok) {
188 printf("Unable to connect to debug agent %d\n", i::Socket::LastError());
189 return;
190 }
191
192 // Start the receiver thread.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000193 ReceiverThread receiver(this);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000194 receiver.Start();
195
196 // Start the keyboard thread.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000197 KeyboardThread keyboard(this);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000198 keyboard.Start();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000199 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000200
201 // Process events received from debugged VM and from the keyboard.
202 bool terminate = false;
203 while (!terminate) {
204 event_available_->Wait();
205 RemoteDebuggerEvent* event = GetEvent();
206 switch (event->type()) {
207 case RemoteDebuggerEvent::kMessage:
208 HandleMessageReceived(event->data());
209 break;
210 case RemoteDebuggerEvent::kKeyboard:
211 HandleKeyboardCommand(event->data());
212 break;
213 case RemoteDebuggerEvent::kDisconnect:
214 terminate = true;
215 break;
216
217 default:
218 UNREACHABLE();
219 }
220 delete event;
221 }
222
223 // Wait for the receiver thread to end.
224 receiver.Join();
225}
226
227
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000228void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000229 RemoteDebuggerEvent* event =
230 new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
231 AddEvent(event);
232}
233
234
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000235void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000236 RemoteDebuggerEvent* event =
237 new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
238 AddEvent(event);
239}
240
241
242void RemoteDebugger::ConnectionClosed() {
243 RemoteDebuggerEvent* event =
244 new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000245 i::SmartArrayPointer<char>());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000246 AddEvent(event);
247}
248
249
250void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
251 i::ScopedLock lock(event_access_);
252 if (head_ == NULL) {
253 ASSERT(tail_ == NULL);
254 head_ = event;
255 tail_ = event;
256 } else {
257 ASSERT(tail_ != NULL);
258 tail_->set_next(event);
259 tail_ = event;
260 }
261 event_available_->Signal();
262}
263
264
265RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
266 i::ScopedLock lock(event_access_);
267 ASSERT(head_ != NULL);
268 RemoteDebuggerEvent* result = head_;
269 head_ = head_->next();
270 if (head_ == NULL) {
271 ASSERT(tail_ == result);
272 tail_ = NULL;
273 }
274 return result;
275}
276
277
278void RemoteDebugger::HandleMessageReceived(char* message) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000279 Locker lock(isolate_);
280 HandleScope scope(isolate_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000281
282 // Print the event details.
283 TryCatch try_catch;
284 Handle<Object> details =
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000285 Shell::DebugMessageDetails(isolate_,
286 Handle<String>::Cast(String::New(message)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000287 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000288 Shell::ReportException(isolate_, &try_catch);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000289 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000290 return;
291 }
292 String::Utf8Value str(details->Get(String::New("text")));
293 if (str.length() == 0) {
294 // Empty string is used to signal not to process this event.
295 return;
296 }
297 if (*str != NULL) {
298 printf("%s\n", *str);
299 } else {
300 printf("???\n");
301 }
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +0000302
303 bool is_running = details->Get(String::New("running"))->ToBoolean()->Value();
304 PrintPrompt(is_running);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000305}
306
307
308void RemoteDebugger::HandleKeyboardCommand(char* command) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000309 Locker lock(isolate_);
310 HandleScope scope(isolate_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000311
312 // Convert the debugger command to a JSON debugger request.
313 TryCatch try_catch;
314 Handle<Value> request =
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000315 Shell::DebugCommandToJSONRequest(isolate_, String::New(command));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000317 Shell::ReportException(isolate_, &try_catch);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000318 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000319 return;
320 }
321
322 // If undefined is returned the command was handled internally and there is
323 // no JSON to send.
324 if (request->IsUndefined()) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000325 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000326 return;
327 }
328
329 // Send the JSON debugger request.
330 i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
331}
332
333
334void ReceiverThread::Run() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000335 // Receive the connect message (with empty body).
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000336 i::SmartArrayPointer<char> message =
337 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000338 ASSERT(*message == NULL);
339
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000340 while (true) {
341 // Receive a message.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000342 i::SmartArrayPointer<char> message =
343 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000344 if (*message == NULL) {
345 remote_debugger_->ConnectionClosed();
346 return;
347 }
348
349 // Pass the message to the main thread.
350 remote_debugger_->MessageReceived(message);
351 }
352}
353
354
355void KeyboardThread::Run() {
356 static const int kBufferSize = 256;
357 while (true) {
358 // read keyboard input.
359 char command[kBufferSize];
360 char* str = fgets(command, kBufferSize, stdin);
361 if (str == NULL) {
362 break;
363 }
364
365 // Pass the keyboard command to the main thread.
366 remote_debugger_->KeyboardCommand(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000367 i::SmartArrayPointer<char>(i::StrDup(command)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000368 }
369}
370
371
ager@chromium.org32912102009-01-16 10:38:43 +0000372} // namespace v8
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000373
374#endif // ENABLE_DEBUGGER_SUPPORT