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(const Debug::EventDetails& event_details) {
     54   // TODO(svenpanne) There should be a way to retrieve this in the callback.
     55   Isolate* isolate = Isolate::GetCurrent();
     56   HandleScope scope(isolate);
     57 
     58   DebugEvent event = event_details.GetEvent();
     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   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::New("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 = String::New("debugCommandProcessor");
     93   Handle<Object> exec_state = event_details.GetExecutionState();
     94   Local<Function> fun = Local<Function>::Cast(exec_state->Get(fun_name));
     95   Local<Object> cmd_processor =
     96       Local<Object>::Cast(fun->Call(exec_state, 0, NULL));
     97   if (try_catch.HasCaught()) {
     98     Shell::ReportException(isolate, &try_catch);
     99     return;
    100   }
    101 
    102   static const int kBufferSize = 256;
    103   bool running = false;
    104   while (!running) {
    105     char command[kBufferSize];
    106     PrintPrompt(running);
    107     char* str = fgets(command, kBufferSize, stdin);
    108     if (str == NULL) break;
    109 
    110     // Ignore empty commands.
    111     if (strlen(command) == 0) continue;
    112 
    113     TryCatch try_catch;
    114 
    115     // Convert the debugger command to a JSON debugger request.
    116     Handle<Value> request =
    117         Shell::DebugCommandToJSONRequest(isolate, String::New(command));
    118     if (try_catch.HasCaught()) {
    119       Shell::ReportException(isolate, &try_catch);
    120       continue;
    121     }
    122 
    123     // If undefined is returned the command was handled internally and there is
    124     // no JSON to send.
    125     if (request->IsUndefined()) {
    126       continue;
    127     }
    128 
    129     Handle<String> fun_name;
    130     Handle<Function> fun;
    131     // All the functions used below take one argument.
    132     static const int kArgc = 1;
    133     Handle<Value> args[kArgc];
    134 
    135     // Invoke the JavaScript to convert the debug command line to a JSON
    136     // request, invoke the JSON request and convert the JSON respose to a text
    137     // representation.
    138     fun_name = String::New("processDebugRequest");
    139     fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
    140     args[0] = request;
    141     Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
    142     if (try_catch.HasCaught()) {
    143       Shell::ReportException(isolate, &try_catch);
    144       continue;
    145     }
    146     Handle<String> response = Handle<String>::Cast(response_val);
    147 
    148     // Convert the debugger response into text details and the running state.
    149     Handle<Object> response_details =
    150         Shell::DebugMessageDetails(isolate, response);
    151     if (try_catch.HasCaught()) {
    152       Shell::ReportException(isolate, &try_catch);
    153       continue;
    154     }
    155     String::Utf8Value text_str(response_details->Get(String::New("text")));
    156     if (text_str.length() > 0) {
    157       printf("%s\n", *text_str);
    158     }
    159     running =
    160         response_details->Get(String::New("running"))->ToBoolean()->Value();
    161   }
    162 }
    163 
    164 
    165 void RunRemoteDebugger(Isolate* isolate, int port) {
    166   RemoteDebugger debugger(isolate, port);
    167   debugger.Run();
    168 }
    169 
    170 
    171 void RemoteDebugger::Run() {
    172   bool ok;
    173 
    174   // Make sure that socket support is initialized.
    175   ok = i::Socket::SetUp();
    176   if (!ok) {
    177     printf("Unable to initialize socket support %d\n", i::Socket::LastError());
    178     return;
    179   }
    180 
    181   // Connect to the debugger agent.
    182   conn_ = i::OS::CreateSocket();
    183   static const int kPortStrSize = 6;
    184   char port_str[kPortStrSize];
    185   i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
    186   ok = conn_->Connect("localhost", port_str);
    187   if (!ok) {
    188     printf("Unable to connect to debug agent %d\n", i::Socket::LastError());
    189     return;
    190   }
    191 
    192   // Start the receiver thread.
    193   ReceiverThread receiver(this);
    194   receiver.Start();
    195 
    196   // Start the keyboard thread.
    197   KeyboardThread keyboard(this);
    198   keyboard.Start();
    199   PrintPrompt();
    200 
    201   // Process events received from debugged VM and from the keyboard.
    202   bool terminate = false;
    203   while (!terminate) {
    204     event_available_->Wait();
    205     RemoteDebuggerEvent* event = GetEvent();
    206     switch (event->type()) {
    207       case RemoteDebuggerEvent::kMessage:
    208         HandleMessageReceived(event->data());
    209         break;
    210       case RemoteDebuggerEvent::kKeyboard:
    211         HandleKeyboardCommand(event->data());
    212         break;
    213       case RemoteDebuggerEvent::kDisconnect:
    214         terminate = true;
    215         break;
    216 
    217       default:
    218         UNREACHABLE();
    219     }
    220     delete event;
    221   }
    222 
    223   // Wait for the receiver thread to end.
    224   receiver.Join();
    225 }
    226 
    227 
    228 void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
    229   RemoteDebuggerEvent* event =
    230       new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
    231   AddEvent(event);
    232 }
    233 
    234 
    235 void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
    236   RemoteDebuggerEvent* event =
    237       new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
    238   AddEvent(event);
    239 }
    240 
    241 
    242 void RemoteDebugger::ConnectionClosed() {
    243   RemoteDebuggerEvent* event =
    244       new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
    245                               i::SmartArrayPointer<char>());
    246   AddEvent(event);
    247 }
    248 
    249 
    250 void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
    251   i::ScopedLock lock(event_access_);
    252   if (head_ == NULL) {
    253     ASSERT(tail_ == NULL);
    254     head_ = event;
    255     tail_ = event;
    256   } else {
    257     ASSERT(tail_ != NULL);
    258     tail_->set_next(event);
    259     tail_ = event;
    260   }
    261   event_available_->Signal();
    262 }
    263 
    264 
    265 RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
    266   i::ScopedLock lock(event_access_);
    267   ASSERT(head_ != NULL);
    268   RemoteDebuggerEvent* result = head_;
    269   head_ = head_->next();
    270   if (head_ == NULL) {
    271     ASSERT(tail_ == result);
    272     tail_ = NULL;
    273   }
    274   return result;
    275 }
    276 
    277 
    278 void RemoteDebugger::HandleMessageReceived(char* message) {
    279   Locker lock(isolate_);
    280   HandleScope scope(isolate_);
    281 
    282   // Print the event details.
    283   TryCatch try_catch;
    284   Handle<Object> details =
    285       Shell::DebugMessageDetails(isolate_,
    286                                  Handle<String>::Cast(String::New(message)));
    287   if (try_catch.HasCaught()) {
    288     Shell::ReportException(isolate_, &try_catch);
    289     PrintPrompt();
    290     return;
    291   }
    292   String::Utf8Value str(details->Get(String::New("text")));
    293   if (str.length() == 0) {
    294     // Empty string is used to signal not to process this event.
    295     return;
    296   }
    297   if (*str != NULL) {
    298     printf("%s\n", *str);
    299   } else {
    300     printf("???\n");
    301   }
    302 
    303   bool is_running = details->Get(String::New("running"))->ToBoolean()->Value();
    304   PrintPrompt(is_running);
    305 }
    306 
    307 
    308 void RemoteDebugger::HandleKeyboardCommand(char* command) {
    309   Locker lock(isolate_);
    310   HandleScope scope(isolate_);
    311 
    312   // Convert the debugger command to a JSON debugger request.
    313   TryCatch try_catch;
    314   Handle<Value> request =
    315       Shell::DebugCommandToJSONRequest(isolate_, String::New(command));
    316   if (try_catch.HasCaught()) {
    317     Shell::ReportException(isolate_, &try_catch);
    318     PrintPrompt();
    319     return;
    320   }
    321 
    322   // If undefined is returned the command was handled internally and there is
    323   // no JSON to send.
    324   if (request->IsUndefined()) {
    325     PrintPrompt();
    326     return;
    327   }
    328 
    329   // Send the JSON debugger request.
    330   i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
    331 }
    332 
    333 
    334 void ReceiverThread::Run() {
    335   // Receive the connect message (with empty body).
    336   i::SmartArrayPointer<char> message =
    337       i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
    338   ASSERT(*message == NULL);
    339 
    340   while (true) {
    341     // Receive a message.
    342     i::SmartArrayPointer<char> message =
    343         i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
    344     if (*message == NULL) {
    345       remote_debugger_->ConnectionClosed();
    346       return;
    347     }
    348 
    349     // Pass the message to the main thread.
    350     remote_debugger_->MessageReceived(message);
    351   }
    352 }
    353 
    354 
    355 void KeyboardThread::Run() {
    356   static const int kBufferSize = 256;
    357   while (true) {
    358     // read keyboard input.
    359     char command[kBufferSize];
    360     char* str = fgets(command, kBufferSize, stdin);
    361     if (str == NULL) {
    362       break;
    363     }
    364 
    365     // Pass the keyboard command to the main thread.
    366     remote_debugger_->KeyboardCommand(
    367         i::SmartArrayPointer<char>(i::StrDup(command)));
    368   }
    369 }
    370 
    371 
    372 }  // namespace v8
    373 
    374 #endif  // ENABLE_DEBUGGER_SUPPORT
    375