Home | History | Annotate | Download | only in src
      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 "debug-agent.h"
     33 
     34 
     35 namespace v8 {
     36 
     37 static bool was_running = true;
     38 
     39 void PrintPrompt(bool is_running) {
     40   const char* prompt = is_running? "> " : "dbg> ";
     41   was_running = is_running;
     42   printf("%s", prompt);
     43   fflush(stdout);
     44 }
     45 
     46 
     47 void PrintPrompt() {
     48   PrintPrompt(was_running);
     49 }
     50 
     51 
     52 void HandleDebugEvent(const Debug::EventDetails& event_details) {
     53   // TODO(svenpanne) There should be a way to retrieve this in the callback.
     54   Isolate* isolate = Isolate::GetCurrent();
     55   HandleScope scope(isolate);
     56 
     57   DebugEvent event = event_details.GetEvent();
     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 =
     67       String::NewFromUtf8(isolate, "toJSONProtocol");
     68   Handle<Object> event_data = event_details.GetEventData();
     69   Local<Function> to_json_fun =
     70       Local<Function>::Cast(event_data->Get(to_json_fun_name));
     71   Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
     72   if (try_catch.HasCaught()) {
     73     Shell::ReportException(isolate, &try_catch);
     74     return;
     75   }
     76 
     77   // Print the event details.
     78   Handle<Object> details =
     79       Shell::DebugMessageDetails(isolate, Handle<String>::Cast(event_json));
     80   if (try_catch.HasCaught()) {
     81     Shell::ReportException(isolate, &try_catch);
     82     return;
     83   }
     84   String::Utf8Value str(details->Get(String::NewFromUtf8(isolate, "text")));
     85   if (str.length() == 0) {
     86     // Empty string is used to signal not to process this event.
     87     return;
     88   }
     89   printf("%s\n", *str);
     90 
     91   // Get the debug command processor.
     92   Local<String> fun_name =
     93       String::NewFromUtf8(isolate, "debugCommandProcessor");
     94   Handle<Object> exec_state = event_details.GetExecutionState();
     95   Local<Function> fun = Local<Function>::Cast(exec_state->Get(fun_name));
     96   Local<Object> cmd_processor =
     97       Local<Object>::Cast(fun->Call(exec_state, 0, NULL));
     98   if (try_catch.HasCaught()) {
     99     Shell::ReportException(isolate, &try_catch);
    100     return;
    101   }
    102 
    103   static const int kBufferSize = 256;
    104   bool running = false;
    105   while (!running) {
    106     char command[kBufferSize];
    107     PrintPrompt(running);
    108     char* str = fgets(command, kBufferSize, stdin);
    109     if (str == NULL) break;
    110 
    111     // Ignore empty commands.
    112     if (strlen(command) == 0) continue;
    113 
    114     TryCatch try_catch;
    115 
    116     // Convert the debugger command to a JSON debugger request.
    117     Handle<Value> request = Shell::DebugCommandToJSONRequest(
    118         isolate, String::NewFromUtf8(isolate, command));
    119     if (try_catch.HasCaught()) {
    120       Shell::ReportException(isolate, &try_catch);
    121       continue;
    122     }
    123 
    124     // If undefined is returned the command was handled internally and there is
    125     // no JSON to send.
    126     if (request->IsUndefined()) {
    127       continue;
    128     }
    129 
    130     Handle<String> fun_name;
    131     Handle<Function> fun;
    132     // All the functions used below take one argument.
    133     static const int kArgc = 1;
    134     Handle<Value> args[kArgc];
    135 
    136     // Invoke the JavaScript to convert the debug command line to a JSON
    137     // request, invoke the JSON request and convert the JSON respose to a text
    138     // representation.
    139     fun_name = String::NewFromUtf8(isolate, "processDebugRequest");
    140     fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
    141     args[0] = request;
    142     Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
    143     if (try_catch.HasCaught()) {
    144       Shell::ReportException(isolate, &try_catch);
    145       continue;
    146     }
    147     Handle<String> response = Handle<String>::Cast(response_val);
    148 
    149     // Convert the debugger response into text details and the running state.
    150     Handle<Object> response_details =
    151         Shell::DebugMessageDetails(isolate, response);
    152     if (try_catch.HasCaught()) {
    153       Shell::ReportException(isolate, &try_catch);
    154       continue;
    155     }
    156     String::Utf8Value text_str(
    157         response_details->Get(String::NewFromUtf8(isolate, "text")));
    158     if (text_str.length() > 0) {
    159       printf("%s\n", *text_str);
    160     }
    161     running = response_details->Get(String::NewFromUtf8(isolate, "running"))
    162                   ->ToBoolean()
    163                   ->Value();
    164   }
    165 }
    166 
    167 
    168 void RunRemoteDebugger(Isolate* isolate, int port) {
    169   RemoteDebugger debugger(isolate, port);
    170   debugger.Run();
    171 }
    172 
    173 
    174 void RemoteDebugger::Run() {
    175   bool ok;
    176 
    177   // Connect to the debugger agent.
    178   conn_ = new i::Socket;
    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::GetLastError());
    185     return;
    186   }
    187 
    188   // Start the receiver thread.
    189   ReceiverThread receiver(this);
    190   receiver.Start();
    191 
    192   // Start the keyboard thread.
    193   KeyboardThread keyboard(this);
    194   keyboard.Start();
    195   PrintPrompt();
    196 
    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 
    224 void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
    225   RemoteDebuggerEvent* event =
    226       new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
    227   AddEvent(event);
    228 }
    229 
    230 
    231 void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
    232   RemoteDebuggerEvent* event =
    233       new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
    234   AddEvent(event);
    235 }
    236 
    237 
    238 void RemoteDebugger::ConnectionClosed() {
    239   RemoteDebuggerEvent* event =
    240       new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
    241                               i::SmartArrayPointer<char>());
    242   AddEvent(event);
    243 }
    244 
    245 
    246 void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
    247   i::LockGuard<i::Mutex> lock_guard(&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 
    261 RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
    262   i::LockGuard<i::Mutex> lock_guard(&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 
    274 void RemoteDebugger::HandleMessageReceived(char* message) {
    275   Locker lock(isolate_);
    276   HandleScope scope(isolate_);
    277 
    278   // Print the event details.
    279   TryCatch try_catch;
    280   Handle<Object> details = Shell::DebugMessageDetails(
    281       isolate_, Handle<String>::Cast(String::NewFromUtf8(isolate_, message)));
    282   if (try_catch.HasCaught()) {
    283     Shell::ReportException(isolate_, &try_catch);
    284     PrintPrompt();
    285     return;
    286   }
    287   String::Utf8Value str(details->Get(String::NewFromUtf8(isolate_, "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   }
    297 
    298   bool is_running = details->Get(String::NewFromUtf8(isolate_, "running"))
    299                         ->ToBoolean()
    300                         ->Value();
    301   PrintPrompt(is_running);
    302 }
    303 
    304 
    305 void RemoteDebugger::HandleKeyboardCommand(char* command) {
    306   Locker lock(isolate_);
    307   HandleScope scope(isolate_);
    308 
    309   // Convert the debugger command to a JSON debugger request.
    310   TryCatch try_catch;
    311   Handle<Value> request = Shell::DebugCommandToJSONRequest(
    312       isolate_, String::NewFromUtf8(isolate_, command));
    313   if (try_catch.HasCaught()) {
    314     Shell::ReportException(isolate_, &try_catch);
    315     PrintPrompt();
    316     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()) {
    322     PrintPrompt();
    323     return;
    324   }
    325 
    326   // Send the JSON debugger request.
    327   i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
    328 }
    329 
    330 
    331 void ReceiverThread::Run() {
    332   // Receive the connect message (with empty body).
    333   i::SmartArrayPointer<char> message =
    334       i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
    335   ASSERT(*message == NULL);
    336 
    337   while (true) {
    338     // Receive a message.
    339     i::SmartArrayPointer<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 
    352 void 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::SmartArrayPointer<char>(i::StrDup(command)));
    365   }
    366 }
    367 
    368 
    369 }  // namespace v8
    370 
    371 #endif  // ENABLE_DEBUGGER_SUPPORT
    372