blob: 5f3ed766ab845c77f558bffa4e4dfdc1d15b541f [file] [log] [blame]
ager@chromium.org32912102009-01-16 10:38:43 +00001// Copyright 2008 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
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
28
29#include "d8.h"
30#include "d8-debug.h"
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000031#include "platform.h"
32#include "debug-agent.h"
ager@chromium.org32912102009-01-16 10:38:43 +000033
34
35namespace v8 {
36
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000037void PrintPrompt() {
38 printf("dbg> ");
39 fflush(stdout);
40}
41
ager@chromium.org32912102009-01-16 10:38:43 +000042
43void HandleDebugEvent(DebugEvent event,
44 Handle<Object> exec_state,
45 Handle<Object> event_data,
46 Handle<Value> data) {
47 HandleScope scope;
48
iposva@chromium.org245aa852009-02-10 00:49:54 +000049 // Check for handled event.
50 if (event != Break && event != Exception && event != AfterCompile) {
51 return;
52 }
ager@chromium.org32912102009-01-16 10:38:43 +000053
54 TryCatch try_catch;
55
kasperl@chromium.org061ef742009-02-27 12:16:20 +000056 // Get the toJSONProtocol function on the event and get the JSON format.
57 Local<String> to_json_fun_name = String::New("toJSONProtocol");
58 Local<Function> to_json_fun =
59 Function::Cast(*event_data->Get(to_json_fun_name));
60 Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
61 if (try_catch.HasCaught()) {
62 Shell::ReportException(&try_catch);
63 return;
64 }
65
ager@chromium.org32912102009-01-16 10:38:43 +000066 // Print the event details.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000067 Handle<Object> details =
68 Shell::DebugMessageDetails(Handle<String>::Cast(event_json));
69 if (try_catch.HasCaught()) {
70 Shell::ReportException(&try_catch);
71 return;
72 }
73 String::Utf8Value str(details->Get(String::New("text")));
74 if (str.length() == 0) {
iposva@chromium.org245aa852009-02-10 00:49:54 +000075 // Empty string is used to signal not to process this event.
76 return;
77 }
ager@chromium.org32912102009-01-16 10:38:43 +000078 printf("%s\n", *str);
79
80 // Get the debug command processor.
81 Local<String> fun_name = String::New("debugCommandProcessor");
82 Local<Function> fun = Function::Cast(*exec_state->Get(fun_name));
83 Local<Object> cmd_processor =
84 Object::Cast(*fun->Call(exec_state, 0, NULL));
85 if (try_catch.HasCaught()) {
86 Shell::ReportException(&try_catch);
87 return;
88 }
89
90 static const int kBufferSize = 256;
91 bool running = false;
92 while (!running) {
93 char command[kBufferSize];
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000094 PrintPrompt();
ager@chromium.org32912102009-01-16 10:38:43 +000095 char* str = fgets(command, kBufferSize, stdin);
96 if (str == NULL) break;
97
98 // Ignore empty commands.
99 if (strlen(command) == 0) continue;
100
101 TryCatch try_catch;
102
103 // Convert the debugger command to a JSON debugger request.
104 Handle<Value> request =
105 Shell::DebugCommandToJSONRequest(String::New(command));
106 if (try_catch.HasCaught()) {
107 Shell::ReportException(&try_catch);
108 continue;
109 }
110
111 // If undefined is returned the command was handled internally and there is
112 // no JSON to send.
113 if (request->IsUndefined()) {
114 continue;
115 }
116
117 Handle<String> fun_name;
118 Handle<Function> fun;
119 // All the functions used below take one argument.
120 static const int kArgc = 1;
121 Handle<Value> args[kArgc];
122
123 // Invoke the JavaScript to convert the debug command line to a JSON
124 // request, invoke the JSON request and convert the JSON respose to a text
125 // representation.
126 fun_name = String::New("processDebugRequest");
127 fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
128 args[0] = request;
129 Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
130 if (try_catch.HasCaught()) {
131 Shell::ReportException(&try_catch);
132 continue;
133 }
134 Handle<String> response = Handle<String>::Cast(response_val);
135
136 // Convert the debugger response into text details and the running state.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000137 Handle<Object> response_details = Shell::DebugMessageDetails(response);
ager@chromium.org32912102009-01-16 10:38:43 +0000138 if (try_catch.HasCaught()) {
139 Shell::ReportException(&try_catch);
140 continue;
141 }
142 String::Utf8Value text_str(response_details->Get(String::New("text")));
143 if (text_str.length() > 0) {
144 printf("%s\n", *text_str);
145 }
146 running =
147 response_details->Get(String::New("running"))->ToBoolean()->Value();
148 }
149}
150
151
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000152void RunRemoteDebugger(int port) {
153 RemoteDebugger debugger(port);
154 debugger.Run();
155}
156
157
158void RemoteDebugger::Run() {
159 bool ok;
160
161 // Make sure that socket support is initialized.
162 ok = i::Socket::Setup();
163 if (!ok) {
164 printf("Unable to initialize socket support %d\n", i::Socket::LastError());
165 return;
166 }
167
168 // Connect to the debugger agent.
169 conn_ = i::OS::CreateSocket();
170 static const int kPortStrSize = 6;
171 char port_str[kPortStrSize];
172 i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
173 ok = conn_->Connect("localhost", port_str);
174 if (!ok) {
175 printf("Unable to connect to debug agent %d\n", i::Socket::LastError());
176 return;
177 }
178
179 // Start the receiver thread.
180 ReceiverThread receiver(this);
181 receiver.Start();
182
183 // Start the keyboard thread.
184 KeyboardThread keyboard(this);
185 keyboard.Start();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000186 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000187
188 // Process events received from debugged VM and from the keyboard.
189 bool terminate = false;
190 while (!terminate) {
191 event_available_->Wait();
192 RemoteDebuggerEvent* event = GetEvent();
193 switch (event->type()) {
194 case RemoteDebuggerEvent::kMessage:
195 HandleMessageReceived(event->data());
196 break;
197 case RemoteDebuggerEvent::kKeyboard:
198 HandleKeyboardCommand(event->data());
199 break;
200 case RemoteDebuggerEvent::kDisconnect:
201 terminate = true;
202 break;
203
204 default:
205 UNREACHABLE();
206 }
207 delete event;
208 }
209
210 // Wait for the receiver thread to end.
211 receiver.Join();
212}
213
214
215void RemoteDebugger::MessageReceived(i::SmartPointer<char> message) {
216 RemoteDebuggerEvent* event =
217 new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
218 AddEvent(event);
219}
220
221
222void RemoteDebugger::KeyboardCommand(i::SmartPointer<char> command) {
223 RemoteDebuggerEvent* event =
224 new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
225 AddEvent(event);
226}
227
228
229void RemoteDebugger::ConnectionClosed() {
230 RemoteDebuggerEvent* event =
231 new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
232 i::SmartPointer<char>());
233 AddEvent(event);
234}
235
236
237void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
238 i::ScopedLock lock(event_access_);
239 if (head_ == NULL) {
240 ASSERT(tail_ == NULL);
241 head_ = event;
242 tail_ = event;
243 } else {
244 ASSERT(tail_ != NULL);
245 tail_->set_next(event);
246 tail_ = event;
247 }
248 event_available_->Signal();
249}
250
251
252RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
253 i::ScopedLock lock(event_access_);
254 ASSERT(head_ != NULL);
255 RemoteDebuggerEvent* result = head_;
256 head_ = head_->next();
257 if (head_ == NULL) {
258 ASSERT(tail_ == result);
259 tail_ = NULL;
260 }
261 return result;
262}
263
264
265void RemoteDebugger::HandleMessageReceived(char* message) {
266 HandleScope scope;
267
268 // Print the event details.
269 TryCatch try_catch;
270 Handle<Object> details =
271 Shell::DebugMessageDetails(Handle<String>::Cast(String::New(message)));
272 if (try_catch.HasCaught()) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000273 Shell::ReportException(&try_catch);
274 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000275 return;
276 }
277 String::Utf8Value str(details->Get(String::New("text")));
278 if (str.length() == 0) {
279 // Empty string is used to signal not to process this event.
280 return;
281 }
282 if (*str != NULL) {
283 printf("%s\n", *str);
284 } else {
285 printf("???\n");
286 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000287 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000288}
289
290
291void RemoteDebugger::HandleKeyboardCommand(char* command) {
292 HandleScope scope;
293
294 // Convert the debugger command to a JSON debugger request.
295 TryCatch try_catch;
296 Handle<Value> request =
297 Shell::DebugCommandToJSONRequest(String::New(command));
298 if (try_catch.HasCaught()) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000299 v8::String::Utf8Value exception(try_catch.Exception());
300 const char* exception_string = Shell::ToCString(exception);
301 printf("%s\n", exception_string);
302 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000303 return;
304 }
305
306 // If undefined is returned the command was handled internally and there is
307 // no JSON to send.
308 if (request->IsUndefined()) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000309 PrintPrompt();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000310 return;
311 }
312
313 // Send the JSON debugger request.
314 i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
315}
316
317
318void ReceiverThread::Run() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000319 // Receive the connect message (with empty body).
320 i::SmartPointer<char> message =
321 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
322 ASSERT(*message == NULL);
323
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000324 while (true) {
325 // Receive a message.
326 i::SmartPointer<char> message =
327 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
328 if (*message == NULL) {
329 remote_debugger_->ConnectionClosed();
330 return;
331 }
332
333 // Pass the message to the main thread.
334 remote_debugger_->MessageReceived(message);
335 }
336}
337
338
339void KeyboardThread::Run() {
340 static const int kBufferSize = 256;
341 while (true) {
342 // read keyboard input.
343 char command[kBufferSize];
344 char* str = fgets(command, kBufferSize, stdin);
345 if (str == NULL) {
346 break;
347 }
348
349 // Pass the keyboard command to the main thread.
350 remote_debugger_->KeyboardCommand(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000351 i::SmartPointer<char>(i::StrDup(command)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000352 }
353}
354
355
ager@chromium.org32912102009-01-16 10:38:43 +0000356} // namespace v8