blob: 06622057b4a487364c305754256cf9f9353fc076 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +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"
31#include "platform.h"
32#include "debug-agent.h"
33
34
35namespace v8 {
36
Steve Block9fac8402011-05-12 15:51:54 +010037static 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);
Steve Block6ded16b2010-05-10 14:33:55 +010043 fflush(stdout);
44}
45
Steve Blocka7e24c12009-10-30 11:49:00 +000046
Steve Block9fac8402011-05-12 15:51:54 +010047void PrintPrompt() {
48 PrintPrompt(was_running);
49}
50
51
Steve Blocka7e24c12009-10-30 11:49:00 +000052void HandleDebugEvent(DebugEvent event,
53 Handle<Object> exec_state,
54 Handle<Object> event_data,
55 Handle<Value> data) {
56 HandleScope scope;
57
58 // Check for handled event.
59 if (event != Break && event != Exception && event != AfterCompile) {
60 return;
61 }
62
63 TryCatch try_catch;
64
65 // Get the toJSONProtocol function on the event and get the JSON format.
66 Local<String> to_json_fun_name = String::New("toJSONProtocol");
67 Local<Function> to_json_fun =
68 Function::Cast(*event_data->Get(to_json_fun_name));
69 Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
70 if (try_catch.HasCaught()) {
71 Shell::ReportException(&try_catch);
72 return;
73 }
74
75 // Print the event details.
76 Handle<Object> details =
77 Shell::DebugMessageDetails(Handle<String>::Cast(event_json));
78 if (try_catch.HasCaught()) {
79 Shell::ReportException(&try_catch);
80 return;
81 }
82 String::Utf8Value str(details->Get(String::New("text")));
83 if (str.length() == 0) {
84 // Empty string is used to signal not to process this event.
85 return;
86 }
87 printf("%s\n", *str);
88
89 // Get the debug command processor.
90 Local<String> fun_name = String::New("debugCommandProcessor");
91 Local<Function> fun = Function::Cast(*exec_state->Get(fun_name));
92 Local<Object> cmd_processor =
93 Object::Cast(*fun->Call(exec_state, 0, NULL));
94 if (try_catch.HasCaught()) {
95 Shell::ReportException(&try_catch);
96 return;
97 }
98
99 static const int kBufferSize = 256;
100 bool running = false;
101 while (!running) {
102 char command[kBufferSize];
Steve Block9fac8402011-05-12 15:51:54 +0100103 PrintPrompt(running);
Steve Blocka7e24c12009-10-30 11:49:00 +0000104 char* str = fgets(command, kBufferSize, stdin);
105 if (str == NULL) break;
106
107 // Ignore empty commands.
108 if (strlen(command) == 0) continue;
109
110 TryCatch try_catch;
111
112 // Convert the debugger command to a JSON debugger request.
113 Handle<Value> request =
114 Shell::DebugCommandToJSONRequest(String::New(command));
115 if (try_catch.HasCaught()) {
116 Shell::ReportException(&try_catch);
117 continue;
118 }
119
120 // If undefined is returned the command was handled internally and there is
121 // no JSON to send.
122 if (request->IsUndefined()) {
123 continue;
124 }
125
126 Handle<String> fun_name;
127 Handle<Function> fun;
128 // All the functions used below take one argument.
129 static const int kArgc = 1;
130 Handle<Value> args[kArgc];
131
132 // Invoke the JavaScript to convert the debug command line to a JSON
133 // request, invoke the JSON request and convert the JSON respose to a text
134 // representation.
135 fun_name = String::New("processDebugRequest");
136 fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
137 args[0] = request;
138 Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
139 if (try_catch.HasCaught()) {
140 Shell::ReportException(&try_catch);
141 continue;
142 }
143 Handle<String> response = Handle<String>::Cast(response_val);
144
145 // Convert the debugger response into text details and the running state.
146 Handle<Object> response_details = Shell::DebugMessageDetails(response);
147 if (try_catch.HasCaught()) {
148 Shell::ReportException(&try_catch);
149 continue;
150 }
151 String::Utf8Value text_str(response_details->Get(String::New("text")));
152 if (text_str.length() > 0) {
153 printf("%s\n", *text_str);
154 }
155 running =
156 response_details->Get(String::New("running"))->ToBoolean()->Value();
157 }
158}
159
160
161void RunRemoteDebugger(int port) {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000162 RemoteDebugger debugger(port);
Steve Blocka7e24c12009-10-30 11:49:00 +0000163 debugger.Run();
164}
165
166
167void RemoteDebugger::Run() {
168 bool ok;
169
170 // Make sure that socket support is initialized.
171 ok = i::Socket::Setup();
172 if (!ok) {
173 printf("Unable to initialize socket support %d\n", i::Socket::LastError());
174 return;
175 }
176
177 // Connect to the debugger agent.
178 conn_ = i::OS::CreateSocket();
179 static const int kPortStrSize = 6;
180 char port_str[kPortStrSize];
181 i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
182 ok = conn_->Connect("localhost", port_str);
183 if (!ok) {
184 printf("Unable to connect to debug agent %d\n", i::Socket::LastError());
185 return;
186 }
187
188 // Start the receiver thread.
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000189 ReceiverThread receiver(this);
Steve Blocka7e24c12009-10-30 11:49:00 +0000190 receiver.Start();
191
192 // Start the keyboard thread.
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000193 KeyboardThread keyboard(this);
Steve Blocka7e24c12009-10-30 11:49:00 +0000194 keyboard.Start();
Steve Block6ded16b2010-05-10 14:33:55 +0100195 PrintPrompt();
Steve Blocka7e24c12009-10-30 11:49:00 +0000196
197 // Process events received from debugged VM and from the keyboard.
198 bool terminate = false;
199 while (!terminate) {
200 event_available_->Wait();
201 RemoteDebuggerEvent* event = GetEvent();
202 switch (event->type()) {
203 case RemoteDebuggerEvent::kMessage:
204 HandleMessageReceived(event->data());
205 break;
206 case RemoteDebuggerEvent::kKeyboard:
207 HandleKeyboardCommand(event->data());
208 break;
209 case RemoteDebuggerEvent::kDisconnect:
210 terminate = true;
211 break;
212
213 default:
214 UNREACHABLE();
215 }
216 delete event;
217 }
218
219 // Wait for the receiver thread to end.
220 receiver.Join();
221}
222
223
224void RemoteDebugger::MessageReceived(i::SmartPointer<char> message) {
225 RemoteDebuggerEvent* event =
226 new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
227 AddEvent(event);
228}
229
230
231void RemoteDebugger::KeyboardCommand(i::SmartPointer<char> command) {
232 RemoteDebuggerEvent* event =
233 new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
234 AddEvent(event);
235}
236
237
238void RemoteDebugger::ConnectionClosed() {
239 RemoteDebuggerEvent* event =
240 new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
241 i::SmartPointer<char>());
242 AddEvent(event);
243}
244
245
246void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
247 i::ScopedLock lock(event_access_);
248 if (head_ == NULL) {
249 ASSERT(tail_ == NULL);
250 head_ = event;
251 tail_ = event;
252 } else {
253 ASSERT(tail_ != NULL);
254 tail_->set_next(event);
255 tail_ = event;
256 }
257 event_available_->Signal();
258}
259
260
261RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
262 i::ScopedLock lock(event_access_);
263 ASSERT(head_ != NULL);
264 RemoteDebuggerEvent* result = head_;
265 head_ = head_->next();
266 if (head_ == NULL) {
267 ASSERT(tail_ == result);
268 tail_ = NULL;
269 }
270 return result;
271}
272
273
274void RemoteDebugger::HandleMessageReceived(char* message) {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000275 Locker lock;
Steve Blocka7e24c12009-10-30 11:49:00 +0000276 HandleScope scope;
277
278 // Print the event details.
279 TryCatch try_catch;
280 Handle<Object> details =
281 Shell::DebugMessageDetails(Handle<String>::Cast(String::New(message)));
282 if (try_catch.HasCaught()) {
Steve Block6ded16b2010-05-10 14:33:55 +0100283 Shell::ReportException(&try_catch);
284 PrintPrompt();
Steve Blocka7e24c12009-10-30 11:49:00 +0000285 return;
286 }
287 String::Utf8Value str(details->Get(String::New("text")));
288 if (str.length() == 0) {
289 // Empty string is used to signal not to process this event.
290 return;
291 }
292 if (*str != NULL) {
293 printf("%s\n", *str);
294 } else {
295 printf("???\n");
296 }
Steve Block9fac8402011-05-12 15:51:54 +0100297
298 bool is_running = details->Get(String::New("running"))->ToBoolean()->Value();
299 PrintPrompt(is_running);
Steve Blocka7e24c12009-10-30 11:49:00 +0000300}
301
302
303void RemoteDebugger::HandleKeyboardCommand(char* command) {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000304 Locker lock;
Steve Blocka7e24c12009-10-30 11:49:00 +0000305 HandleScope scope;
306
307 // Convert the debugger command to a JSON debugger request.
308 TryCatch try_catch;
309 Handle<Value> request =
310 Shell::DebugCommandToJSONRequest(String::New(command));
311 if (try_catch.HasCaught()) {
Steve Block6ded16b2010-05-10 14:33:55 +0100312 v8::String::Utf8Value exception(try_catch.Exception());
313 const char* exception_string = Shell::ToCString(exception);
314 printf("%s\n", exception_string);
315 PrintPrompt();
Steve Blocka7e24c12009-10-30 11:49:00 +0000316 return;
317 }
318
319 // If undefined is returned the command was handled internally and there is
320 // no JSON to send.
321 if (request->IsUndefined()) {
Steve Block6ded16b2010-05-10 14:33:55 +0100322 PrintPrompt();
Steve Blocka7e24c12009-10-30 11:49:00 +0000323 return;
324 }
325
326 // Send the JSON debugger request.
327 i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
328}
329
330
331void ReceiverThread::Run() {
332 // Receive the connect message (with empty body).
333 i::SmartPointer<char> message =
334 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
335 ASSERT(*message == NULL);
336
337 while (true) {
338 // Receive a message.
339 i::SmartPointer<char> message =
340 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
341 if (*message == NULL) {
342 remote_debugger_->ConnectionClosed();
343 return;
344 }
345
346 // Pass the message to the main thread.
347 remote_debugger_->MessageReceived(message);
348 }
349}
350
351
352void KeyboardThread::Run() {
353 static const int kBufferSize = 256;
354 while (true) {
355 // read keyboard input.
356 char command[kBufferSize];
357 char* str = fgets(command, kBufferSize, stdin);
358 if (str == NULL) {
359 break;
360 }
361
362 // Pass the keyboard command to the main thread.
363 remote_debugger_->KeyboardCommand(
364 i::SmartPointer<char>(i::StrDup(command)));
365 }
366}
367
368
369} // namespace v8