blob: 379631cb7c5a5bd2eee80870db93a5bf1884cb6f [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"
ager@chromium.org32912102009-01-16 10:38:43 +000033
34
35namespace v8 {
36
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +000037static bool was_running = true;
38
39void PrintPrompt(bool is_running) {
40 const char* prompt = is_running? "> " : "dbg> ";
41 was_running = is_running;
42 printf("%s", prompt);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000043 fflush(stdout);
44}
45
ager@chromium.org32912102009-01-16 10:38:43 +000046
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +000047void PrintPrompt() {
48 PrintPrompt(was_running);
49}
50
51
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +000052void HandleDebugEvent(const Debug::EventDetails& event_details) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000053 // TODO(svenpanne) There should be a way to retrieve this in the callback.
54 Isolate* isolate = Isolate::GetCurrent();
55 HandleScope scope(isolate);
ager@chromium.org32912102009-01-16 10:38:43 +000056
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +000057 DebugEvent event = event_details.GetEvent();
iposva@chromium.org245aa852009-02-10 00:49:54 +000058 // Check for handled event.
59 if (event != Break && event != Exception && event != AfterCompile) {
60 return;
61 }
ager@chromium.org32912102009-01-16 10:38:43 +000062
63 TryCatch try_catch;
64
kasperl@chromium.org061ef742009-02-27 12:16:20 +000065 // Get the toJSONProtocol function on the event and get the JSON format.
66 Local<String> to_json_fun_name = String::New("toJSONProtocol");
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +000067 Handle<Object> event_data = event_details.GetEventData();
kasperl@chromium.org061ef742009-02-27 12:16:20 +000068 Local<Function> to_json_fun =
danno@chromium.orgf95d4b92013-06-13 14:40:17 +000069 Local<Function>::Cast(event_data->Get(to_json_fun_name));
kasperl@chromium.org061ef742009-02-27 12:16:20 +000070 Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
71 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000072 Shell::ReportException(isolate, &try_catch);
kasperl@chromium.org061ef742009-02-27 12:16:20 +000073 return;
74 }
75
ager@chromium.org32912102009-01-16 10:38:43 +000076 // Print the event details.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000077 Handle<Object> details =
ulan@chromium.org57ff8812013-05-10 08:16:55 +000078 Shell::DebugMessageDetails(isolate, Handle<String>::Cast(event_json));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000079 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000080 Shell::ReportException(isolate, &try_catch);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000081 return;
82 }
83 String::Utf8Value str(details->Get(String::New("text")));
84 if (str.length() == 0) {
iposva@chromium.org245aa852009-02-10 00:49:54 +000085 // Empty string is used to signal not to process this event.
86 return;
87 }
ager@chromium.org32912102009-01-16 10:38:43 +000088 printf("%s\n", *str);
89
90 // Get the debug command processor.
91 Local<String> fun_name = String::New("debugCommandProcessor");
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +000092 Handle<Object> exec_state = event_details.GetExecutionState();
danno@chromium.orgf95d4b92013-06-13 14:40:17 +000093 Local<Function> fun = Local<Function>::Cast(exec_state->Get(fun_name));
ager@chromium.org32912102009-01-16 10:38:43 +000094 Local<Object> cmd_processor =
danno@chromium.orgf95d4b92013-06-13 14:40:17 +000095 Local<Object>::Cast(fun->Call(exec_state, 0, NULL));
ager@chromium.org32912102009-01-16 10:38:43 +000096 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000097 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +000098 return;
99 }
100
101 static const int kBufferSize = 256;
102 bool running = false;
103 while (!running) {
104 char command[kBufferSize];
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +0000105 PrintPrompt(running);
ager@chromium.org32912102009-01-16 10:38:43 +0000106 char* str = fgets(command, kBufferSize, stdin);
107 if (str == NULL) break;
108
109 // Ignore empty commands.
110 if (strlen(command) == 0) continue;
111
112 TryCatch try_catch;
113
114 // Convert the debugger command to a JSON debugger request.
115 Handle<Value> request =
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000116 Shell::DebugCommandToJSONRequest(isolate, String::New(command));
ager@chromium.org32912102009-01-16 10:38:43 +0000117 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000118 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +0000119 continue;
120 }
121
122 // If undefined is returned the command was handled internally and there is
123 // no JSON to send.
124 if (request->IsUndefined()) {
125 continue;
126 }
127
128 Handle<String> fun_name;
129 Handle<Function> fun;
130 // All the functions used below take one argument.
131 static const int kArgc = 1;
132 Handle<Value> args[kArgc];
133
134 // Invoke the JavaScript to convert the debug command line to a JSON
135 // request, invoke the JSON request and convert the JSON respose to a text
136 // representation.
137 fun_name = String::New("processDebugRequest");
138 fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
139 args[0] = request;
140 Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
141 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000142 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +0000143 continue;
144 }
145 Handle<String> response = Handle<String>::Cast(response_val);
146
147 // Convert the debugger response into text details and the running state.
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000148 Handle<Object> response_details =
149 Shell::DebugMessageDetails(isolate, response);
ager@chromium.org32912102009-01-16 10:38:43 +0000150 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000151 Shell::ReportException(isolate, &try_catch);
ager@chromium.org32912102009-01-16 10:38:43 +0000152 continue;
153 }
154 String::Utf8Value text_str(response_details->Get(String::New("text")));
155 if (text_str.length() > 0) {
156 printf("%s\n", *text_str);
157 }
158 running =
159 response_details->Get(String::New("running"))->ToBoolean()->Value();
160 }
161}
162
163
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000164void RunRemoteDebugger(Isolate* isolate, int port) {
165 RemoteDebugger debugger(isolate, port);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000166 debugger.Run();
167}
168
169
170void RemoteDebugger::Run() {
171 bool ok;
172
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000173 // Connect to the debugger agent.
jkummerow@chromium.org3d00d0a2013-09-04 13:57:32 +0000174 conn_ = new i::Socket;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000175 static const int kPortStrSize = 6;
176 char port_str[kPortStrSize];
177 i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
178 ok = conn_->Connect("localhost", port_str);
179 if (!ok) {
jkummerow@chromium.org3d00d0a2013-09-04 13:57:32 +0000180 printf("Unable to connect to debug agent %d\n", i::Socket::GetLastError());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000181 return;
182 }
183
184 // Start the receiver thread.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000185 ReceiverThread receiver(this);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000186 receiver.Start();
187
188 // Start the keyboard thread.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000189 KeyboardThread keyboard(this);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000190 keyboard.Start();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000191 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000192
193 // Process events received from debugged VM and from the keyboard.
194 bool terminate = false;
195 while (!terminate) {
mstarzinger@chromium.orge9000182013-09-03 11:25:39 +0000196 event_available_.Wait();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000197 RemoteDebuggerEvent* event = GetEvent();
198 switch (event->type()) {
199 case RemoteDebuggerEvent::kMessage:
200 HandleMessageReceived(event->data());
201 break;
202 case RemoteDebuggerEvent::kKeyboard:
203 HandleKeyboardCommand(event->data());
204 break;
205 case RemoteDebuggerEvent::kDisconnect:
206 terminate = true;
207 break;
208
209 default:
210 UNREACHABLE();
211 }
212 delete event;
213 }
214
215 // Wait for the receiver thread to end.
216 receiver.Join();
217}
218
219
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000220void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000221 RemoteDebuggerEvent* event =
222 new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
223 AddEvent(event);
224}
225
226
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000227void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000228 RemoteDebuggerEvent* event =
229 new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
230 AddEvent(event);
231}
232
233
234void RemoteDebugger::ConnectionClosed() {
235 RemoteDebuggerEvent* event =
236 new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000237 i::SmartArrayPointer<char>());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000238 AddEvent(event);
239}
240
241
242void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
jkummerow@chromium.orgdc94e192013-08-30 11:35:42 +0000243 i::LockGuard<i::Mutex> lock_guard(&event_access_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000244 if (head_ == NULL) {
245 ASSERT(tail_ == NULL);
246 head_ = event;
247 tail_ = event;
248 } else {
249 ASSERT(tail_ != NULL);
250 tail_->set_next(event);
251 tail_ = event;
252 }
mstarzinger@chromium.orge9000182013-09-03 11:25:39 +0000253 event_available_.Signal();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000254}
255
256
257RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
jkummerow@chromium.orgdc94e192013-08-30 11:35:42 +0000258 i::LockGuard<i::Mutex> lock_guard(&event_access_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000259 ASSERT(head_ != NULL);
260 RemoteDebuggerEvent* result = head_;
261 head_ = head_->next();
262 if (head_ == NULL) {
263 ASSERT(tail_ == result);
264 tail_ = NULL;
265 }
266 return result;
267}
268
269
270void RemoteDebugger::HandleMessageReceived(char* message) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000271 Locker lock(isolate_);
272 HandleScope scope(isolate_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000273
274 // Print the event details.
275 TryCatch try_catch;
276 Handle<Object> details =
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000277 Shell::DebugMessageDetails(isolate_,
278 Handle<String>::Cast(String::New(message)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000279 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000280 Shell::ReportException(isolate_, &try_catch);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000281 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000282 return;
283 }
284 String::Utf8Value str(details->Get(String::New("text")));
285 if (str.length() == 0) {
286 // Empty string is used to signal not to process this event.
287 return;
288 }
289 if (*str != NULL) {
290 printf("%s\n", *str);
291 } else {
292 printf("???\n");
293 }
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +0000294
295 bool is_running = details->Get(String::New("running"))->ToBoolean()->Value();
296 PrintPrompt(is_running);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000297}
298
299
300void RemoteDebugger::HandleKeyboardCommand(char* command) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000301 Locker lock(isolate_);
302 HandleScope scope(isolate_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000303
304 // Convert the debugger command to a JSON debugger request.
305 TryCatch try_catch;
306 Handle<Value> request =
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000307 Shell::DebugCommandToJSONRequest(isolate_, String::New(command));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000308 if (try_catch.HasCaught()) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000309 Shell::ReportException(isolate_, &try_catch);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000310 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000311 return;
312 }
313
314 // If undefined is returned the command was handled internally and there is
315 // no JSON to send.
316 if (request->IsUndefined()) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000317 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000318 return;
319 }
320
321 // Send the JSON debugger request.
322 i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
323}
324
325
326void ReceiverThread::Run() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000327 // Receive the connect message (with empty body).
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000328 i::SmartArrayPointer<char> message =
329 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000330 ASSERT(*message == NULL);
331
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000332 while (true) {
333 // Receive a message.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000334 i::SmartArrayPointer<char> message =
335 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000336 if (*message == NULL) {
337 remote_debugger_->ConnectionClosed();
338 return;
339 }
340
341 // Pass the message to the main thread.
342 remote_debugger_->MessageReceived(message);
343 }
344}
345
346
347void KeyboardThread::Run() {
348 static const int kBufferSize = 256;
349 while (true) {
350 // read keyboard input.
351 char command[kBufferSize];
352 char* str = fgets(command, kBufferSize, stdin);
353 if (str == NULL) {
354 break;
355 }
356
357 // Pass the keyboard command to the main thread.
358 remote_debugger_->KeyboardCommand(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000359 i::SmartArrayPointer<char>(i::StrDup(command)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000360 }
361}
362
363
ager@chromium.org32912102009-01-16 10:38:43 +0000364} // namespace v8
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000365
366#endif // ENABLE_DEBUGGER_SUPPORT