Home | History | Annotate | Download | only in src
      1 // 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 
     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(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];
    103     PrintPrompt(running);
    104     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 
    161 void RunRemoteDebugger(int port) {
    162   RemoteDebugger debugger(i::Isolate::Current(), port);
    163   debugger.Run();
    164 }
    165 
    166 
    167 void 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.
    189   ReceiverThread receiver(isolate_, this);
    190   receiver.Start();
    191 
    192   // Start the keyboard thread.
    193   KeyboardThread keyboard(isolate_, 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::SmartPointer<char> message) {
    225   RemoteDebuggerEvent* event =
    226       new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
    227   AddEvent(event);
    228 }
    229 
    230 
    231 void RemoteDebugger::KeyboardCommand(i::SmartPointer<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::SmartPointer<char>());
    242   AddEvent(event);
    243 }
    244 
    245 
    246 void 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 
    261 RemoteDebuggerEvent* 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 
    274 void RemoteDebugger::HandleMessageReceived(char* message) {
    275   HandleScope scope;
    276 
    277   // Print the event details.
    278   TryCatch try_catch;
    279   Handle<Object> details =
    280       Shell::DebugMessageDetails(Handle<String>::Cast(String::New(message)));
    281   if (try_catch.HasCaught()) {
    282     Shell::ReportException(&try_catch);
    283     PrintPrompt();
    284     return;
    285   }
    286   String::Utf8Value str(details->Get(String::New("text")));
    287   if (str.length() == 0) {
    288     // Empty string is used to signal not to process this event.
    289     return;
    290   }
    291   if (*str != NULL) {
    292     printf("%s\n", *str);
    293   } else {
    294     printf("???\n");
    295   }
    296 
    297   bool is_running = details->Get(String::New("running"))->ToBoolean()->Value();
    298   PrintPrompt(is_running);
    299 }
    300 
    301 
    302 void RemoteDebugger::HandleKeyboardCommand(char* command) {
    303   HandleScope scope;
    304 
    305   // Convert the debugger command to a JSON debugger request.
    306   TryCatch try_catch;
    307   Handle<Value> request =
    308       Shell::DebugCommandToJSONRequest(String::New(command));
    309   if (try_catch.HasCaught()) {
    310     v8::String::Utf8Value exception(try_catch.Exception());
    311     const char* exception_string = Shell::ToCString(exception);
    312     printf("%s\n", exception_string);
    313     PrintPrompt();
    314     return;
    315   }
    316 
    317   // If undefined is returned the command was handled internally and there is
    318   // no JSON to send.
    319   if (request->IsUndefined()) {
    320     PrintPrompt();
    321     return;
    322   }
    323 
    324   // Send the JSON debugger request.
    325   i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
    326 }
    327 
    328 
    329 void ReceiverThread::Run() {
    330   // Receive the connect message (with empty body).
    331   i::SmartPointer<char> message =
    332     i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
    333   ASSERT(*message == NULL);
    334 
    335   while (true) {
    336     // Receive a message.
    337     i::SmartPointer<char> message =
    338       i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
    339     if (*message == NULL) {
    340       remote_debugger_->ConnectionClosed();
    341       return;
    342     }
    343 
    344     // Pass the message to the main thread.
    345     remote_debugger_->MessageReceived(message);
    346   }
    347 }
    348 
    349 
    350 void KeyboardThread::Run() {
    351   static const int kBufferSize = 256;
    352   while (true) {
    353     // read keyboard input.
    354     char command[kBufferSize];
    355     char* str = fgets(command, kBufferSize, stdin);
    356     if (str == NULL) {
    357       break;
    358     }
    359 
    360     // Pass the keyboard command to the main thread.
    361     remote_debugger_->KeyboardCommand(
    362         i::SmartPointer<char>(i::StrDup(command)));
    363   }
    364 }
    365 
    366 
    367 }  // namespace v8
    368