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 "platform.h"
     33 #include "debug-agent.h"
     34 
     35 
     36 namespace v8 {
     37 
     38 static bool was_running = true;
     39 
     40 void 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 
     48 void PrintPrompt() {
     49   PrintPrompt(was_running);
     50 }
     51 
     52 
     53 void 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 
    162 void RunRemoteDebugger(int port) {
    163   RemoteDebugger debugger(port);
    164   debugger.Run();
    165 }
    166 
    167 
    168 void 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 
    225 void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
    226   RemoteDebuggerEvent* event =
    227       new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
    228   AddEvent(event);
    229 }
    230 
    231 
    232 void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
    233   RemoteDebuggerEvent* event =
    234       new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
    235   AddEvent(event);
    236 }
    237 
    238 
    239 void RemoteDebugger::ConnectionClosed() {
    240   RemoteDebuggerEvent* event =
    241       new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
    242                               i::SmartArrayPointer<char>());
    243   AddEvent(event);
    244 }
    245 
    246 
    247 void 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 
    262 RemoteDebuggerEvent* 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 
    275 void 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 
    304 void 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 
    330 void 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 
    351 void 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