d8-debug.cc 10.5 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

28
#ifdef ENABLE_DEBUGGER_SUPPORT
29 30 31

#include "d8.h"
#include "d8-debug.h"
32
#include "debug-agent.h"
33 34 35 36


namespace v8 {

37 38 39 40 41 42
static bool was_running = true;

void PrintPrompt(bool is_running) {
  const char* prompt = is_running? "> " : "dbg> ";
  was_running = is_running;
  printf("%s", prompt);
43 44 45
  fflush(stdout);
}

46

47 48 49 50 51
void PrintPrompt() {
  PrintPrompt(was_running);
}


52
void HandleDebugEvent(const Debug::EventDetails& event_details) {
53 54 55
  // TODO(svenpanne) There should be a way to retrieve this in the callback.
  Isolate* isolate = Isolate::GetCurrent();
  HandleScope scope(isolate);
56

57
  DebugEvent event = event_details.GetEvent();
58 59 60 61
  // Check for handled event.
  if (event != Break && event != Exception && event != AfterCompile) {
    return;
  }
62 63 64

  TryCatch try_catch;

65 66
  // Get the toJSONProtocol function on the event and get the JSON format.
  Local<String> to_json_fun_name = String::New("toJSONProtocol");
67
  Handle<Object> event_data = event_details.GetEventData();
68
  Local<Function> to_json_fun =
69
      Local<Function>::Cast(event_data->Get(to_json_fun_name));
70 71
  Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
  if (try_catch.HasCaught()) {
72
    Shell::ReportException(isolate, &try_catch);
73 74 75
    return;
  }

76
  // Print the event details.
77
  Handle<Object> details =
78
      Shell::DebugMessageDetails(isolate, Handle<String>::Cast(event_json));
79
  if (try_catch.HasCaught()) {
80
    Shell::ReportException(isolate, &try_catch);
81 82 83 84
    return;
  }
  String::Utf8Value str(details->Get(String::New("text")));
  if (str.length() == 0) {
85 86 87
    // Empty string is used to signal not to process this event.
    return;
  }
88 89 90 91
  printf("%s\n", *str);

  // Get the debug command processor.
  Local<String> fun_name = String::New("debugCommandProcessor");
92
  Handle<Object> exec_state = event_details.GetExecutionState();
93
  Local<Function> fun = Local<Function>::Cast(exec_state->Get(fun_name));
94
  Local<Object> cmd_processor =
95
      Local<Object>::Cast(fun->Call(exec_state, 0, NULL));
96
  if (try_catch.HasCaught()) {
97
    Shell::ReportException(isolate, &try_catch);
98 99 100 101 102 103 104
    return;
  }

  static const int kBufferSize = 256;
  bool running = false;
  while (!running) {
    char command[kBufferSize];
105
    PrintPrompt(running);
106 107 108 109 110 111 112 113 114 115
    char* str = fgets(command, kBufferSize, stdin);
    if (str == NULL) break;

    // Ignore empty commands.
    if (strlen(command) == 0) continue;

    TryCatch try_catch;

    // Convert the debugger command to a JSON debugger request.
    Handle<Value> request =
116
        Shell::DebugCommandToJSONRequest(isolate, String::New(command));
117
    if (try_catch.HasCaught()) {
118
      Shell::ReportException(isolate, &try_catch);
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
      continue;
    }

    // If undefined is returned the command was handled internally and there is
    // no JSON to send.
    if (request->IsUndefined()) {
      continue;
    }

    Handle<String> fun_name;
    Handle<Function> fun;
    // All the functions used below take one argument.
    static const int kArgc = 1;
    Handle<Value> args[kArgc];

    // Invoke the JavaScript to convert the debug command line to a JSON
    // request, invoke the JSON request and convert the JSON respose to a text
    // representation.
    fun_name = String::New("processDebugRequest");
    fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
    args[0] = request;
    Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
    if (try_catch.HasCaught()) {
142
      Shell::ReportException(isolate, &try_catch);
143 144 145 146 147
      continue;
    }
    Handle<String> response = Handle<String>::Cast(response_val);

    // Convert the debugger response into text details and the running state.
148 149
    Handle<Object> response_details =
        Shell::DebugMessageDetails(isolate, response);
150
    if (try_catch.HasCaught()) {
151
      Shell::ReportException(isolate, &try_catch);
152 153 154 155 156 157 158 159 160 161 162 163
      continue;
    }
    String::Utf8Value text_str(response_details->Get(String::New("text")));
    if (text_str.length() > 0) {
      printf("%s\n", *text_str);
    }
    running =
        response_details->Get(String::New("running"))->ToBoolean()->Value();
  }
}


164 165
void RunRemoteDebugger(Isolate* isolate, int port) {
  RemoteDebugger debugger(isolate, port);
166 167 168 169 170 171 172 173
  debugger.Run();
}


void RemoteDebugger::Run() {
  bool ok;

  // Connect to the debugger agent.
174
  conn_ = new i::Socket;
175 176 177 178 179
  static const int kPortStrSize = 6;
  char port_str[kPortStrSize];
  i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
  ok = conn_->Connect("localhost", port_str);
  if (!ok) {
180
    printf("Unable to connect to debug agent %d\n", i::Socket::GetLastError());
181 182 183 184
    return;
  }

  // Start the receiver thread.
185
  ReceiverThread receiver(this);
186 187 188
  receiver.Start();

  // Start the keyboard thread.
189
  KeyboardThread keyboard(this);
190
  keyboard.Start();
191
  PrintPrompt();
192 193 194 195

  // Process events received from debugged VM and from the keyboard.
  bool terminate = false;
  while (!terminate) {
196
    event_available_.Wait();
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    RemoteDebuggerEvent* event = GetEvent();
    switch (event->type()) {
      case RemoteDebuggerEvent::kMessage:
        HandleMessageReceived(event->data());
        break;
      case RemoteDebuggerEvent::kKeyboard:
        HandleKeyboardCommand(event->data());
        break;
      case RemoteDebuggerEvent::kDisconnect:
        terminate = true;
        break;

      default:
        UNREACHABLE();
    }
    delete event;
  }

  // Wait for the receiver thread to end.
  receiver.Join();
}


220
void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
221 222 223 224 225 226
  RemoteDebuggerEvent* event =
      new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
  AddEvent(event);
}


227
void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
228 229 230 231 232 233 234 235 236
  RemoteDebuggerEvent* event =
      new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
  AddEvent(event);
}


void RemoteDebugger::ConnectionClosed() {
  RemoteDebuggerEvent* event =
      new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
237
                              i::SmartArrayPointer<char>());
238 239 240 241 242
  AddEvent(event);
}


void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
243
  i::LockGuard<i::Mutex> lock_guard(&event_access_);
244 245 246 247 248 249 250 251 252
  if (head_ == NULL) {
    ASSERT(tail_ == NULL);
    head_ = event;
    tail_ = event;
  } else {
    ASSERT(tail_ != NULL);
    tail_->set_next(event);
    tail_ = event;
  }
253
  event_available_.Signal();
254 255 256 257
}


RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
258
  i::LockGuard<i::Mutex> lock_guard(&event_access_);
259 260 261 262 263 264 265 266 267 268 269 270
  ASSERT(head_ != NULL);
  RemoteDebuggerEvent* result = head_;
  head_ = head_->next();
  if (head_ == NULL) {
    ASSERT(tail_ == result);
    tail_ = NULL;
  }
  return result;
}


void RemoteDebugger::HandleMessageReceived(char* message) {
271 272
  Locker lock(isolate_);
  HandleScope scope(isolate_);
273 274 275 276

  // Print the event details.
  TryCatch try_catch;
  Handle<Object> details =
277 278
      Shell::DebugMessageDetails(isolate_,
                                 Handle<String>::Cast(String::New(message)));
279
  if (try_catch.HasCaught()) {
280
    Shell::ReportException(isolate_, &try_catch);
281
    PrintPrompt();
282 283 284 285 286 287 288 289 290 291 292 293
    return;
  }
  String::Utf8Value str(details->Get(String::New("text")));
  if (str.length() == 0) {
    // Empty string is used to signal not to process this event.
    return;
  }
  if (*str != NULL) {
    printf("%s\n", *str);
  } else {
    printf("???\n");
  }
294 295 296

  bool is_running = details->Get(String::New("running"))->ToBoolean()->Value();
  PrintPrompt(is_running);
297 298 299 300
}


void RemoteDebugger::HandleKeyboardCommand(char* command) {
301 302
  Locker lock(isolate_);
  HandleScope scope(isolate_);
303 304 305 306

  // Convert the debugger command to a JSON debugger request.
  TryCatch try_catch;
  Handle<Value> request =
307
      Shell::DebugCommandToJSONRequest(isolate_, String::New(command));
308
  if (try_catch.HasCaught()) {
309
    Shell::ReportException(isolate_, &try_catch);
310
    PrintPrompt();
311 312 313 314 315 316
    return;
  }

  // If undefined is returned the command was handled internally and there is
  // no JSON to send.
  if (request->IsUndefined()) {
317
    PrintPrompt();
318 319 320 321 322 323 324 325 326
    return;
  }

  // Send the JSON debugger request.
  i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
}


void ReceiverThread::Run() {
327
  // Receive the connect message (with empty body).
328 329
  i::SmartArrayPointer<char> message =
      i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
330 331
  ASSERT(*message == NULL);

332 333
  while (true) {
    // Receive a message.
334 335
    i::SmartArrayPointer<char> message =
        i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
    if (*message == NULL) {
      remote_debugger_->ConnectionClosed();
      return;
    }

    // Pass the message to the main thread.
    remote_debugger_->MessageReceived(message);
  }
}


void KeyboardThread::Run() {
  static const int kBufferSize = 256;
  while (true) {
    // read keyboard input.
    char command[kBufferSize];
    char* str = fgets(command, kBufferSize, stdin);
    if (str == NULL) {
      break;
    }

    // Pass the keyboard command to the main thread.
    remote_debugger_->KeyboardCommand(
359
        i::SmartArrayPointer<char>(i::StrDup(command)));
360 361 362 363
  }
}


364
}  // namespace v8
365 366

#endif  // ENABLE_DEBUGGER_SUPPORT