1// Copyright 2012 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#ifdef ENABLE_DEBUGGER_SUPPORT
29
30#include "d8.h"
31#include "d8-debug.h"
32#include "platform.h"
33#include "debug-agent.h"
34
35
36namespace v8 {
37
38static 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);
44  fflush(stdout);
45}
46
47
48void PrintPrompt() {
49  PrintPrompt(was_running);
50}
51
52
53void HandleDebugEvent(DebugEvent event,
54                      Handle<Object> exec_state,
55                      Handle<Object> event_data,
56                      Handle<Value> data) {
57  HandleScope scope;
58
59  // Check for handled event.
60  if (event != Break && event != Exception && event != AfterCompile) {
61    return;
62  }
63
64  TryCatch try_catch;
65
66  // Get the toJSONProtocol function on the event and get the JSON format.
67  Local<String> to_json_fun_name = String::New("toJSONProtocol");
68  Local<Function> to_json_fun =
69      Function::Cast(*event_data->Get(to_json_fun_name));
70  Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
71  if (try_catch.HasCaught()) {
72    Shell::ReportException(&try_catch);
73    return;
74  }
75
76  // Print the event details.
77  Handle<Object> details =
78      Shell::DebugMessageDetails(Handle<String>::Cast(event_json));
79  if (try_catch.HasCaught()) {
80    Shell::ReportException(&try_catch);
81    return;
82  }
83  String::Utf8Value str(details->Get(String::New("text")));
84  if (str.length() == 0) {
85    // Empty string is used to signal not to process this event.
86    return;
87  }
88  printf("%s\n", *str);
89
90  // Get the debug command processor.
91  Local<String> fun_name = String::New("debugCommandProcessor");
92  Local<Function> fun = Function::Cast(*exec_state->Get(fun_name));
93  Local<Object> cmd_processor =
94      Object::Cast(*fun->Call(exec_state, 0, NULL));
95  if (try_catch.HasCaught()) {
96    Shell::ReportException(&try_catch);
97    return;
98  }
99
100  static const int kBufferSize = 256;
101  bool running = false;
102  while (!running) {
103    char command[kBufferSize];
104    PrintPrompt(running);
105    char* str = fgets(command, kBufferSize, stdin);
106    if (str == NULL) break;
107
108    // Ignore empty commands.
109    if (strlen(command) == 0) continue;
110
111    TryCatch try_catch;
112
113    // Convert the debugger command to a JSON debugger request.
114    Handle<Value> request =
115        Shell::DebugCommandToJSONRequest(String::New(command));
116    if (try_catch.HasCaught()) {
117      Shell::ReportException(&try_catch);
118      continue;
119    }
120
121    // If undefined is returned the command was handled internally and there is
122    // no JSON to send.
123    if (request->IsUndefined()) {
124      continue;
125    }
126
127    Handle<String> fun_name;
128    Handle<Function> fun;
129    // All the functions used below take one argument.
130    static const int kArgc = 1;
131    Handle<Value> args[kArgc];
132
133    // Invoke the JavaScript to convert the debug command line to a JSON
134    // request, invoke the JSON request and convert the JSON respose to a text
135    // representation.
136    fun_name = String::New("processDebugRequest");
137    fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
138    args[0] = request;
139    Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
140    if (try_catch.HasCaught()) {
141      Shell::ReportException(&try_catch);
142      continue;
143    }
144    Handle<String> response = Handle<String>::Cast(response_val);
145
146    // Convert the debugger response into text details and the running state.
147    Handle<Object> response_details = Shell::DebugMessageDetails(response);
148    if (try_catch.HasCaught()) {
149      Shell::ReportException(&try_catch);
150      continue;
151    }
152    String::Utf8Value text_str(response_details->Get(String::New("text")));
153    if (text_str.length() > 0) {
154      printf("%s\n", *text_str);
155    }
156    running =
157        response_details->Get(String::New("running"))->ToBoolean()->Value();
158  }
159}
160
161
162void RunRemoteDebugger(int port) {
163  RemoteDebugger debugger(port);
164  debugger.Run();
165}
166
167
168void RemoteDebugger::Run() {
169  bool ok;
170
171  // Make sure that socket support is initialized.
172  ok = i::Socket::SetUp();
173  if (!ok) {
174    printf("Unable to initialize socket support %d\n", i::Socket::LastError());
175    return;
176  }
177
178  // Connect to the debugger agent.
179  conn_ = i::OS::CreateSocket();
180  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) {
185    printf("Unable to connect to debug agent %d\n", i::Socket::LastError());
186    return;
187  }
188
189  // Start the receiver thread.
190  ReceiverThread receiver(this);
191  receiver.Start();
192
193  // Start the keyboard thread.
194  KeyboardThread keyboard(this);
195  keyboard.Start();
196  PrintPrompt();
197
198  // Process events received from debugged VM and from the keyboard.
199  bool terminate = false;
200  while (!terminate) {
201    event_available_->Wait();
202    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
225void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
226  RemoteDebuggerEvent* event =
227      new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
228  AddEvent(event);
229}
230
231
232void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
233  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,
242                              i::SmartArrayPointer<char>());
243  AddEvent(event);
244}
245
246
247void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
248  i::ScopedLock lock(event_access_);
249  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  }
258  event_available_->Signal();
259}
260
261
262RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
263  i::ScopedLock lock(event_access_);
264  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) {
276  Locker lock;
277  HandleScope scope;
278
279  // Print the event details.
280  TryCatch try_catch;
281  Handle<Object> details =
282      Shell::DebugMessageDetails(Handle<String>::Cast(String::New(message)));
283  if (try_catch.HasCaught()) {
284    Shell::ReportException(&try_catch);
285    PrintPrompt();
286    return;
287  }
288  String::Utf8Value str(details->Get(String::New("text")));
289  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  }
298
299  bool is_running = details->Get(String::New("running"))->ToBoolean()->Value();
300  PrintPrompt(is_running);
301}
302
303
304void RemoteDebugger::HandleKeyboardCommand(char* command) {
305  Locker lock;
306  HandleScope scope;
307
308  // Convert the debugger command to a JSON debugger request.
309  TryCatch try_catch;
310  Handle<Value> request =
311      Shell::DebugCommandToJSONRequest(String::New(command));
312  if (try_catch.HasCaught()) {
313    Shell::ReportException(&try_catch);
314    PrintPrompt();
315    return;
316  }
317
318  // If undefined is returned the command was handled internally and there is
319  // no JSON to send.
320  if (request->IsUndefined()) {
321    PrintPrompt();
322    return;
323  }
324
325  // Send the JSON debugger request.
326  i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
327}
328
329
330void ReceiverThread::Run() {
331  // Receive the connect message (with empty body).
332  i::SmartArrayPointer<char> message =
333      i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
334  ASSERT(*message == NULL);
335
336  while (true) {
337    // Receive a message.
338    i::SmartArrayPointer<char> message =
339        i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
340    if (*message == NULL) {
341      remote_debugger_->ConnectionClosed();
342      return;
343    }
344
345    // Pass the message to the main thread.
346    remote_debugger_->MessageReceived(message);
347  }
348}
349
350
351void KeyboardThread::Run() {
352  static const int kBufferSize = 256;
353  while (true) {
354    // read keyboard input.
355    char command[kBufferSize];
356    char* str = fgets(command, kBufferSize, stdin);
357    if (str == NULL) {
358      break;
359    }
360
361    // Pass the keyboard command to the main thread.
362    remote_debugger_->KeyboardCommand(
363        i::SmartArrayPointer<char>(i::StrDup(command)));
364  }
365}
366
367
368}  // namespace v8
369
370#endif  // ENABLE_DEBUGGER_SUPPORT
371