| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "bin/vmstats_impl.h" | 5 #include "bin/vmstats_impl.h" |
| 6 | 6 |
| 7 #include "bin/file.h" | 7 #include "bin/file.h" |
| 8 #include "bin/log.h" | 8 #include "bin/log.h" |
| 9 #include "bin/platform.h" | 9 #include "bin/platform.h" |
| 10 #include "bin/resources.h" | 10 #include "bin/resources.h" |
| 11 #include "bin/socket.h" | 11 #include "bin/socket.h" |
| 12 #include "bin/thread.h" | 12 #include "bin/thread.h" |
| 13 #include "bin/utils.h" | 13 #include "bin/utils.h" |
| 14 #include "include/dart_debugger_api.h" | 14 #include "include/dart_debugger_api.h" |
| 15 #include "platform/json.h" | 15 #include "platform/json.h" |
| 16 | 16 |
| 17 #define BUFSIZE 8192 | 17 #define BUFSIZE 8192 |
| 18 #define RETRY_PAUSE 100 // milliseconds | 18 #define RETRY_PAUSE 100 // milliseconds |
| 19 | 19 |
| 20 static const char* INDEX_HTML = "index.html"; | 20 static const char* INDEX_HTML = "index.html"; |
| 21 static const char* VMSTATS_HTML = "vmstats.html"; | 21 static const char* VMSTATS_HTML = "vmstats.html"; |
| 22 static const char* DEFAULT_HOST = "localhost"; | 22 static const char* DEFAULT_HOST = "localhost"; |
| 23 | 23 |
| 24 // Global static pointer used to ensure a single instance of the class. | 24 // Global static pointer used to ensure a single instance of the class. |
| 25 VmStats* VmStats::instance_ = NULL; | 25 VmStats* VmStats::instance_ = NULL; |
| 26 dart::Monitor* VmStats::instance_monitor_; | 26 dart::Monitor* VmStats::instance_monitor_; |
| 27 dart::Mutex* VmStatusService::mutex_; | 27 dart::Mutex* VmStatusService::mutex_; |
| 28 | 28 |
| 29 | 29 |
| 30 void VmStats::Start(int port, const char* root_dir) { | 30 void VmStats::Start(int port, const char* root_dir, bool verbose) { |
| 31 if (instance_ != NULL) { | 31 if (instance_ != NULL) { |
| 32 FATAL("VmStats already started."); | 32 FATAL("VmStats already started."); |
| 33 } | 33 } |
| 34 instance_ = new VmStats(); | 34 instance_ = new VmStats(verbose); |
| 35 instance_monitor_ = new dart::Monitor(); | 35 instance_monitor_ = new dart::Monitor(); |
| 36 Initialize(); |
| 36 VmStatusService::InitOnce(); | 37 VmStatusService::InitOnce(); |
| 38 |
| 39 if (port >= 0) { |
| 40 StartServer(port, root_dir); |
| 41 } |
| 42 } |
| 43 |
| 44 |
| 45 void VmStats::StartServer(int port, const char* root_dir) { |
| 46 ASSERT(port >= 0); |
| 47 ASSERT(instance_ != NULL); |
| 48 ASSERT(instance_monitor_ != NULL); |
| 37 Socket::Initialize(); | 49 Socket::Initialize(); |
| 38 | 50 |
| 39 if (root_dir != NULL) { | 51 if (root_dir != NULL) { |
| 40 instance_->root_directory_ = root_dir; | 52 instance_->root_directory_ = root_dir; |
| 41 } | 53 } |
| 42 | 54 |
| 43 // TODO(tball): allow host to be specified. | 55 // TODO(tball): allow host to be specified. |
| 44 char* host = const_cast<char*>(DEFAULT_HOST); | 56 char* host = const_cast<char*>(DEFAULT_HOST); |
| 45 OSError* os_error; | 57 OSError* os_error; |
| 46 const char* host_ip = Socket::LookupIPv4Address(host, &os_error); | 58 const char* host_ip = Socket::LookupIPv4Address(host, &os_error); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 61 | 73 |
| 62 MonitorLocker ml(instance_monitor_); | 74 MonitorLocker ml(instance_monitor_); |
| 63 instance_->running_ = true; | 75 instance_->running_ = true; |
| 64 int err = dart::Thread::Start(WebServer, address); | 76 int err = dart::Thread::Start(WebServer, address); |
| 65 if (err != 0) { | 77 if (err != 0) { |
| 66 Log::PrintErr("Failed starting VmStats thread: %d\n", err); | 78 Log::PrintErr("Failed starting VmStats thread: %d\n", err); |
| 67 Shutdown(); | 79 Shutdown(); |
| 68 } | 80 } |
| 69 } | 81 } |
| 70 | 82 |
| 71 | |
| 72 void VmStats::Stop() { | 83 void VmStats::Stop() { |
| 73 ASSERT(instance_ != NULL); | 84 ASSERT(instance_ != NULL); |
| 74 MonitorLocker ml(instance_monitor_); | 85 MonitorLocker ml(instance_monitor_); |
| 75 instance_->running_ = false; | 86 instance_->running_ = false; |
| 76 } | 87 } |
| 77 | 88 |
| 78 | 89 |
| 79 void VmStats::Shutdown() { | 90 void VmStats::Shutdown() { |
| 80 ASSERT(instance_ != NULL); | 91 ASSERT(instance_ != NULL); |
| 81 MonitorLocker ml(instance_monitor_); | 92 MonitorLocker ml(instance_monitor_); |
| 82 Socket::Close(instance_->bind_address_); | 93 Socket::Close(instance_->bind_address_); |
| 83 delete instance_; | 94 delete instance_; |
| 84 instance_ = NULL; | 95 instance_ = NULL; |
| 85 } | 96 } |
| 86 | 97 |
| 87 | 98 |
| 88 void VmStats::AddIsolate(IsolateData* isolate_data, | 99 void VmStats::AddIsolate(IsolateData* isolate_data, |
| 89 Dart_Isolate isolate) { | 100 Dart_Isolate isolate) { |
| 90 if (instance_ != NULL) { | 101 MonitorLocker ml(instance_monitor_); |
| 91 MonitorLocker ml(instance_monitor_); | 102 instance_->isolate_table_[isolate_data] = isolate; |
| 92 instance_->isolate_table_[isolate_data] = isolate; | |
| 93 } | |
| 94 } | 103 } |
| 95 | 104 |
| 96 | 105 |
| 97 void VmStats::RemoveIsolate(IsolateData* isolate_data) { | 106 void VmStats::RemoveIsolate(IsolateData* isolate_data) { |
| 98 if (instance_ != NULL) { | 107 MonitorLocker ml(instance_monitor_); |
| 99 MonitorLocker ml(instance_monitor_); | 108 instance_->isolate_table_.erase(isolate_data); |
| 100 instance_->isolate_table_.erase(isolate_data); | |
| 101 } | |
| 102 } | 109 } |
| 103 | 110 |
| 104 | 111 |
| 105 static const char* ContentType(const char* url) { | 112 static const char* ContentType(const char* url) { |
| 106 const char* suffix = strrchr(url, '.'); | 113 const char* suffix = strrchr(url, '.'); |
| 107 if (suffix != NULL) { | 114 if (suffix != NULL) { |
| 108 if (!strcmp(suffix, ".html")) { | 115 if (!strcmp(suffix, ".html")) { |
| 109 return "text/html; charset=UTF-8"; | 116 return "text/html; charset=UTF-8"; |
| 110 } | 117 } |
| 111 if (!strcmp(suffix, ".dart")) { | 118 if (!strcmp(suffix, ".dart")) { |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 227 | 234 |
| 228 // Extract GET URL, and null-terminate URL in case request line has | 235 // Extract GET URL, and null-terminate URL in case request line has |
| 229 // HTTP version. | 236 // HTTP version. |
| 230 for (int i = 4; i < len; i++) { | 237 for (int i = 4; i < len; i++) { |
| 231 if (buffer[i] == ' ') { | 238 if (buffer[i] == ' ') { |
| 232 buffer[i] = '\0'; | 239 buffer[i] = '\0'; |
| 233 } | 240 } |
| 234 } | 241 } |
| 235 char* url = strdup(&buffer[4]); | 242 char* url = strdup(&buffer[4]); |
| 236 | 243 |
| 237 Log::Print("vmstats: %s requested\n", url); | 244 if (instance_->verbose_) { |
| 245 Log::Print("vmstats: %s requested\n", url); |
| 246 } |
| 238 char* content = NULL; | 247 char* content = NULL; |
| 239 | 248 |
| 240 // Check for VmStats-specific URLs. | 249 // Check for VmStats-specific URLs. |
| 241 if (strcmp(url, "/isolates") == 0) { | 250 if (strcmp(url, "/isolates") == 0) { |
| 242 content = instance_->IsolatesStatus(); | 251 content = instance_->IsolatesStatus(); |
| 243 } else { | 252 } else { |
| 244 // Check plug-ins. | 253 // Check plug-ins. |
| 245 content = VmStatusService::GetVmStatus(url); | 254 content = VmStatusService::GetVmStatus(url); |
| 246 } | 255 } |
| 247 | 256 |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 307 IsolateTable::iterator itr; | 316 IsolateTable::iterator itr; |
| 308 bool first = true; | 317 bool first = true; |
| 309 for (itr = isolate_table_.begin(); itr != isolate_table_.end(); ++itr) { | 318 for (itr = isolate_table_.begin(); itr != isolate_table_.end(); ++itr) { |
| 310 Dart_Isolate isolate = itr->second; | 319 Dart_Isolate isolate = itr->second; |
| 311 static char request[512]; | 320 static char request[512]; |
| 312 snprintf(request, sizeof(request), | 321 snprintf(request, sizeof(request), |
| 313 "/isolate/0x%"Px, | 322 "/isolate/0x%"Px, |
| 314 reinterpret_cast<intptr_t>(isolate)); | 323 reinterpret_cast<intptr_t>(isolate)); |
| 315 char* status = VmStatusService::GetVmStatus(request); | 324 char* status = VmStatusService::GetVmStatus(request); |
| 316 if (status != NULL) { | 325 if (status != NULL) { |
| 317 text.AddString(status); | |
| 318 if (!first) { | 326 if (!first) { |
| 319 text.AddString(",\n"); | 327 text.AddString(",\n"); |
| 320 } | 328 } |
| 329 text.AddString(status); |
| 321 first = false; | 330 first = false; |
| 322 free(status); | 331 free(status); |
| 323 } | 332 } |
| 324 } | 333 } |
| 325 text.AddString("\n]\n}\n"); | 334 text.AddString("\n]\n}\n"); |
| 326 return strdup(text.buf()); | 335 return strdup(text.buf()); |
| 327 } | 336 } |
| 328 | 337 |
| 329 | 338 |
| 339 // Advance the scanner to the value token of a specified name-value pair. |
| 340 void SeekNamedValue(const char* name, dart::JSONScanner* scanner) { |
| 341 while (!scanner->EOM()) { |
| 342 scanner->Scan(); |
| 343 if (scanner->IsStringLiteral(name)) { |
| 344 scanner->Scan(); |
| 345 ASSERT(scanner->CurrentToken() == dart::JSONScanner::TokenColon); |
| 346 scanner->Scan(); |
| 347 return; |
| 348 } |
| 349 } |
| 350 } |
| 351 |
| 352 |
| 353 // Windows doesn't have strndup(), so this is a simple, private version. |
| 354 static char* StrNDup(const char* s, uword len) { |
| 355 if (strlen(s) < len) { |
| 356 len = strlen(s); |
| 357 } |
| 358 char* result = reinterpret_cast<char*>(malloc(len + 1)); |
| 359 memmove(result, s, len); |
| 360 result[len + 1] = '\0'; |
| 361 return result; |
| 362 } |
| 363 |
| 364 |
| 365 void VmStats::DumpStackThread(uword unused) { |
| 366 Log::Print("Isolate dump:\n"); |
| 367 IsolateTable::iterator itr; |
| 368 MonitorLocker ml(instance_monitor_); |
| 369 for (itr = instance_->isolate_table_.begin(); |
| 370 itr != instance_->isolate_table_.end(); ++itr) { |
| 371 Dart_Isolate isolate = itr->second; |
| 372 |
| 373 // Print isolate name and details. |
| 374 static char buffer[512]; |
| 375 snprintf(buffer, sizeof(buffer), |
| 376 "/isolate/0x%"Px, reinterpret_cast<intptr_t>(isolate)); |
| 377 char* isolate_details = VmStatusService::GetVmStatus(buffer); |
| 378 if (isolate_details != NULL) { |
| 379 dart::JSONScanner scanner(isolate_details); |
| 380 SeekNamedValue("name", &scanner); |
| 381 char* name = StrNDup(scanner.TokenChars(), scanner.TokenLen()); |
| 382 SeekNamedValue("port", &scanner); |
| 383 char* port = StrNDup(scanner.TokenChars(), scanner.TokenLen()); |
| 384 Log::Print("\"%s\" port=%s\n", name, port); |
| 385 free(isolate_details); |
| 386 free(port); |
| 387 free(name); |
| 388 } |
| 389 |
| 390 // Print stack trace. |
| 391 snprintf(buffer, sizeof(buffer), |
| 392 "/isolate/0x%"Px"/stacktrace", |
| 393 reinterpret_cast<intptr_t>(isolate)); |
| 394 char* trace = VmStatusService::GetVmStatus(buffer); |
| 395 if (trace != NULL) { |
| 396 dart::JSONScanner scanner(trace); |
| 397 while (true) { |
| 398 SeekNamedValue("url", &scanner); |
| 399 if (scanner.CurrentToken() == dart::JSONScanner::TokenEOM) { |
| 400 break; |
| 401 } |
| 402 char* url = StrNDup(scanner.TokenChars(), scanner.TokenLen()); |
| 403 SeekNamedValue("line", &scanner); |
| 404 char* line = StrNDup(scanner.TokenChars(), scanner.TokenLen()); |
| 405 SeekNamedValue("function", &scanner); |
| 406 char* function = StrNDup(scanner.TokenChars(), scanner.TokenLen()); |
| 407 Log::Print(" at %s(%s:%s)\n", function, url, line); |
| 408 free(url); |
| 409 free(line); |
| 410 free(function); |
| 411 } |
| 412 free(trace); |
| 413 } |
| 414 } |
| 415 } |
| 416 |
| 417 |
| 418 void VmStats::DumpStack() { |
| 419 int err = dart::Thread::Start(DumpStackThread, 0); |
| 420 if (err != 0) { |
| 421 Log::PrintErr("Failed starting VmStats stackdump thread: %d\n", err); |
| 422 Shutdown(); |
| 423 } |
| 424 } |
| 425 |
| 426 |
| 330 // Global static pointer used to ensure a single instance of the class. | 427 // Global static pointer used to ensure a single instance of the class. |
| 331 VmStatusService* VmStatusService::instance_ = NULL; | 428 VmStatusService* VmStatusService::instance_ = NULL; |
| 332 | 429 |
| 333 | 430 |
| 334 void VmStatusService::InitOnce() { | 431 void VmStatusService::InitOnce() { |
| 335 ASSERT(VmStatusService::instance_ == NULL); | 432 ASSERT(VmStatusService::instance_ == NULL); |
| 336 VmStatusService::instance_ = new VmStatusService(); | 433 VmStatusService::instance_ = new VmStatusService(); |
| 337 VmStatusService::mutex_ = new dart::Mutex(); | 434 VmStatusService::mutex_ = new dart::Mutex(); |
| 338 | 435 |
| 339 // Register built-in status plug-ins. RegisterPlugin is not used because | 436 // Register built-in status plug-ins. RegisterPlugin is not used because |
| 340 // this isn't called within an isolate, and because parameter checking | 437 // this isn't called within an isolate, and because parameter checking |
| 341 // isn't necessary. | 438 // isn't necessary. |
| 342 instance_->RegisterPlugin(&Dart_GetVmStatus); | 439 instance_->RegisterPlugin(&Dart_GetVmStatus); |
| 343 | 440 |
| 344 // TODO(tball): dynamically load any additional plug-ins. | 441 // TODO(tball): dynamically load any additional plug-ins. |
| 345 } | 442 } |
| 346 | 443 |
| 347 | 444 |
| 348 int VmStatusService::RegisterPlugin(Dart_VmStatusCallback callback) { | 445 int VmStatusService::RegisterPlugin(Dart_VmStatusCallback callback) { |
| 446 ASSERT(VmStatusService::instance_ != NULL); |
| 447 ASSERT(VmStatusService::mutex_ != NULL); |
| 349 MutexLocker ml(mutex_); | 448 MutexLocker ml(mutex_); |
| 350 if (callback == NULL) { | 449 if (callback == NULL) { |
| 351 return -1; | 450 return -1; |
| 352 } | 451 } |
| 353 VmStatusPlugin* plugin = new VmStatusPlugin(callback); | 452 VmStatusPlugin* plugin = new VmStatusPlugin(callback); |
| 354 VmStatusPlugin* list = instance_->registered_plugin_list_; | 453 VmStatusPlugin* list = instance_->registered_plugin_list_; |
| 355 if (list == NULL) { | 454 if (list == NULL) { |
| 356 instance_->registered_plugin_list_ = plugin; | 455 instance_->registered_plugin_list_ = plugin; |
| 357 } else { | 456 } else { |
| 358 list->Append(plugin); | 457 list->Append(plugin); |
| 359 } | 458 } |
| 360 return 0; | 459 return 0; |
| 361 } | 460 } |
| 362 | 461 |
| 363 | 462 |
| 364 char* VmStatusService::GetVmStatus(const char* request) { | 463 char* VmStatusService::GetVmStatus(const char* request) { |
| 464 ASSERT(VmStatusService::instance_ != NULL); |
| 365 VmStatusPlugin* plugin = instance_->registered_plugin_list_; | 465 VmStatusPlugin* plugin = instance_->registered_plugin_list_; |
| 366 while (plugin != NULL) { | 466 while (plugin != NULL) { |
| 367 char* result = (plugin->callback())(request); | 467 char* result = (plugin->callback())(request); |
| 368 if (result != NULL) { | 468 if (result != NULL) { |
| 369 return result; | 469 return result; |
| 370 } | 470 } |
| 371 plugin = plugin->next(); | 471 plugin = plugin->next(); |
| 372 } | 472 } |
| 373 return NULL; | 473 return NULL; |
| 374 } | 474 } |
| OLD | NEW |