| Index: runtime/bin/vmstats_impl.cc
|
| diff --git a/runtime/bin/vmstats_impl.cc b/runtime/bin/vmstats_impl.cc
|
| deleted file mode 100644
|
| index b3eebff2ba390a0f1f9523ff7fb6e4fea63a5894..0000000000000000000000000000000000000000
|
| --- a/runtime/bin/vmstats_impl.cc
|
| +++ /dev/null
|
| @@ -1,483 +0,0 @@
|
| -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -#include "bin/vmstats_impl.h"
|
| -
|
| -#include "bin/file.h"
|
| -#include "bin/log.h"
|
| -#include "bin/platform.h"
|
| -#include "bin/resources.h"
|
| -#include "bin/socket.h"
|
| -#include "bin/thread.h"
|
| -#include "bin/utils.h"
|
| -#include "include/dart_debugger_api.h"
|
| -#include "platform/json.h"
|
| -
|
| -
|
| -namespace dart {
|
| -namespace bin {
|
| -
|
| -#define BUFSIZE 8192
|
| -#define RETRY_PAUSE 100 // milliseconds
|
| -
|
| -static const char* INDEX_HTML = "index.html";
|
| -static const char* VMSTATS_HTML = "vmstats.html";
|
| -static const char* DEFAULT_HOST = "localhost";
|
| -
|
| -// Global static pointer used to ensure a single instance of the class.
|
| -VmStats* VmStats::instance_ = NULL;
|
| -dart::Monitor* VmStats::instance_monitor_;
|
| -dart::Mutex* VmStatusService::mutex_;
|
| -
|
| -
|
| -void VmStats::Start(int port, const char* root_dir, bool verbose) {
|
| - if (instance_ != NULL) {
|
| - FATAL("VmStats already started.");
|
| - }
|
| - instance_ = new VmStats(verbose);
|
| - instance_monitor_ = new dart::Monitor();
|
| - Initialize();
|
| - VmStatusService::InitOnce();
|
| -
|
| - if (port >= 0) {
|
| - StartServer(port, root_dir);
|
| - }
|
| -}
|
| -
|
| -
|
| -void VmStats::StartServer(int port, const char* root_dir) {
|
| - ASSERT(port >= 0);
|
| - ASSERT(instance_ != NULL);
|
| - ASSERT(instance_monitor_ != NULL);
|
| - Socket::Initialize();
|
| -
|
| - if (root_dir != NULL) {
|
| - instance_->root_directory_ = root_dir;
|
| - }
|
| -
|
| - // TODO(tball): allow host to be specified.
|
| - char* host = const_cast<char*>(DEFAULT_HOST);
|
| - OSError* os_error;
|
| - AddressList<SocketAddress>* addresses =
|
| - Socket::LookupAddress(host, -1, &os_error);
|
| - if (addresses == NULL) {
|
| - Log::PrintErr("Failed IP lookup of VmStats host %s: %s\n",
|
| - host, os_error->message());
|
| - return;
|
| - }
|
| - const intptr_t BACKLOG = 128; // Default value from HttpServer.dart
|
| - int64_t address = ServerSocket::CreateBindListen(
|
| - addresses->GetAt(0)->addr(), port, BACKLOG);
|
| - delete addresses;
|
| - if (address < 0) {
|
| - Log::PrintErr("Failed binding VmStats socket: %s:%d\n", host, port);
|
| - return;
|
| - }
|
| - instance_->bind_address_ = address;
|
| - Log::Print("VmStats URL: http://%s:%"Pd"/\n", host, Socket::GetPort(address));
|
| -
|
| - MonitorLocker ml(instance_monitor_);
|
| - instance_->running_ = true;
|
| - int err = dart::Thread::Start(WebServer, address);
|
| - if (err != 0) {
|
| - Log::PrintErr("Failed starting VmStats thread: %d\n", err);
|
| - Shutdown();
|
| - }
|
| -}
|
| -
|
| -void VmStats::Stop() {
|
| - ASSERT(instance_ != NULL);
|
| - MonitorLocker ml(instance_monitor_);
|
| - instance_->running_ = false;
|
| -}
|
| -
|
| -
|
| -void VmStats::Shutdown() {
|
| - ASSERT(instance_ != NULL);
|
| - MonitorLocker ml(instance_monitor_);
|
| - Socket::Close(instance_->bind_address_);
|
| - delete instance_;
|
| - instance_ = NULL;
|
| -}
|
| -
|
| -
|
| -void VmStats::AddIsolate(IsolateData* isolate_data,
|
| - Dart_Isolate isolate) {
|
| - MonitorLocker ml(instance_monitor_);
|
| - instance_->isolate_table_[isolate_data] = isolate;
|
| -}
|
| -
|
| -
|
| -void VmStats::RemoveIsolate(IsolateData* isolate_data) {
|
| - MonitorLocker ml(instance_monitor_);
|
| - instance_->isolate_table_.erase(isolate_data);
|
| -}
|
| -
|
| -
|
| -static const char* ContentType(const char* url) {
|
| - const char* suffix = strrchr(url, '.');
|
| - if (suffix != NULL) {
|
| - if (!strcmp(suffix, ".html")) {
|
| - return "text/html; charset=UTF-8";
|
| - }
|
| - if (!strcmp(suffix, ".dart")) {
|
| - return "application/dart; charset=UTF-8";
|
| - }
|
| - if (!strcmp(suffix, ".js")) {
|
| - return "application/javascript; charset=UTF-8";
|
| - }
|
| - if (!strcmp(suffix, ".css")) {
|
| - return "text/css; charset=UTF-8";
|
| - }
|
| - if (!strcmp(suffix, ".gif")) {
|
| - return "image/gif";
|
| - }
|
| - if (!strcmp(suffix, ".png")) {
|
| - return "image/png";
|
| - }
|
| - if (!strcmp(suffix, ".jpg") || !strcmp(suffix, ".jpeg")) {
|
| - return "image/jpeg";
|
| - }
|
| - }
|
| - return "text/plain";
|
| -}
|
| -
|
| -
|
| -// Return a malloc'd string from a format string and arguments.
|
| -intptr_t alloc_printf(char** result, const char* format, ...) {
|
| - va_list args;
|
| - va_start(args, format);
|
| - intptr_t len = vsnprintf(NULL, 0, format, args) + 1;
|
| - *result = reinterpret_cast<char*>(malloc(len));
|
| - return vsnprintf(*result, len, format, args);
|
| -}
|
| -
|
| -
|
| -void writeResponse(intptr_t socket, const char* content_type,
|
| - const char* data, size_t length) {
|
| - char* header;
|
| - intptr_t len = alloc_printf(&header,
|
| - "HTTP/1.1 200 OK\nContent-Type: %s\nContent-Length: %"Pu"\n\n",
|
| - content_type, length);
|
| - Socket::Write(socket, header, len);
|
| - Socket::Write(socket, data, length);
|
| - Socket::Write(socket, "\n", 1);
|
| - free(header);
|
| -}
|
| -
|
| -
|
| -void writeErrorResponse(intptr_t socket, intptr_t error_num, const char* error,
|
| - const char* description) {
|
| - if (description != NULL) {
|
| - // Create body first, so its length is known when creating the header.
|
| - char* body;
|
| - intptr_t body_len = alloc_printf(&body,
|
| - "<html><head><title>%d %s</title></head>\n"
|
| - "<body>\n<h1>%s</h1>\n%s\n</body></html>\n",
|
| - error_num, error, error, description);
|
| - char* header;
|
| - intptr_t header_len = alloc_printf(&header,
|
| - "HTTP/1.1 %d %s\n"
|
| - "Content-Length: %d\n"
|
| - "Connection: close\n"
|
| - "Content-Type: text/html\n\n",
|
| - error_num, error, body_len);
|
| - Socket::Write(socket, header, header_len);
|
| - Socket::Write(socket, body, body_len);
|
| - free(header);
|
| - free(body);
|
| - } else {
|
| - char* response;
|
| - intptr_t len =
|
| - alloc_printf(&response, "HTTP/1.1 %d %s\n\n", error_num, error);
|
| - Socket::Write(socket, response, len);
|
| - free(response);
|
| - }
|
| -}
|
| -
|
| -
|
| -void VmStats::WebServer(uword bind_address) {
|
| - while (true) {
|
| - intptr_t socket = ServerSocket::Accept(bind_address);
|
| - if (socket == ServerSocket::kTemporaryFailure) {
|
| - // Not a real failure, woke up but no connection available.
|
| -
|
| - // Use MonitorLocker.Wait(), since it has finer granularity than sleep().
|
| - dart::Monitor m;
|
| - MonitorLocker ml(&m);
|
| - ml.Wait(RETRY_PAUSE);
|
| -
|
| - continue;
|
| - }
|
| - if (socket < 0) {
|
| - // Stop() closed the socket.
|
| - return;
|
| - }
|
| - Socket::SetBlocking(socket);
|
| -
|
| - // TODO(tball): rewrite this to use STL, so as to eliminate the static
|
| - // buffer and support resource URLs that are longer than BUFSIZE.
|
| -
|
| - // Read request.
|
| - char buffer[BUFSIZE + 1];
|
| - intptr_t len = Socket::Read(socket, buffer, BUFSIZE);
|
| - if (len <= 0) {
|
| - // Invalid HTTP request, ignore.
|
| - continue;
|
| - }
|
| - buffer[len] = '\0';
|
| -
|
| - // Verify it's a GET request.
|
| - // TODO(tball): support POST requests.
|
| - if (strncmp("GET ", buffer, 4) != 0 && strncmp("get ", buffer, 4) != 0) {
|
| - Log::PrintErr("Unsupported HTTP request type");
|
| - writeErrorResponse(socket, 403, "Forbidden",
|
| - "Unsupported HTTP request type");
|
| - Socket::Close(socket);
|
| - continue;
|
| - }
|
| -
|
| - // Extract GET URL, and null-terminate URL in case request line has
|
| - // HTTP version.
|
| - for (int i = 4; i < len; i++) {
|
| - if (buffer[i] == ' ') {
|
| - buffer[i] = '\0';
|
| - }
|
| - }
|
| - char* url = strdup(&buffer[4]);
|
| -
|
| - if (instance_->verbose_) {
|
| - Log::Print("vmstats: %s requested\n", url);
|
| - }
|
| - char* content = NULL;
|
| -
|
| - // Check for VmStats-specific URLs.
|
| - if (strcmp(url, "/isolates") == 0) {
|
| - content = instance_->IsolatesStatus();
|
| - } else {
|
| - // Check plug-ins.
|
| - content = VmStatusService::GetVmStatus(url);
|
| - }
|
| -
|
| - if (content != NULL) {
|
| - writeResponse(socket, "application/json", content, strlen(content));
|
| - free(content);
|
| - } else {
|
| - // No status content with this URL, return file or resource content.
|
| - dart::TextBuffer path(strlen(instance_->root_directory_) + strlen(url));
|
| - path.AddString(instance_->root_directory_);
|
| - path.AddString(url);
|
| -
|
| - // Expand directory URLs.
|
| - if (strcmp(url, "/") == 0) {
|
| - path.AddString(VMSTATS_HTML);
|
| - } else if (url[strlen(url) - 1] == '/') {
|
| - path.AddString(INDEX_HTML);
|
| - }
|
| -
|
| - bool success = false;
|
| - char* text_buffer = NULL;
|
| - const char* content_type = ContentType(path.buf());
|
| - if (File::Exists(path.buf())) {
|
| - File* f = File::Open(path.buf(), File::kRead);
|
| - if (f != NULL) {
|
| - intptr_t len = f->Length();
|
| - text_buffer = reinterpret_cast<char*>(malloc(len));
|
| - if (f->ReadFully(text_buffer, len)) {
|
| - writeResponse(socket, content_type, text_buffer, len);
|
| - success = true;
|
| - }
|
| - free(text_buffer);
|
| - delete f;
|
| - }
|
| - } else {
|
| - const char* resource;
|
| - intptr_t len = Resources::ResourceLookup(path.buf(), &resource);
|
| - if (len != Resources::kNoSuchInstance) {
|
| - ASSERT(len >= 0);
|
| - writeResponse(socket, content_type, resource, len);
|
| - success = true;
|
| - }
|
| - }
|
| - if (!success) {
|
| - char* description;
|
| - alloc_printf(
|
| - &description, "URL <a href=\"%s\">%s</a> not found.", url, url);
|
| - writeErrorResponse(socket, 404, "Not Found", description);
|
| - free(description);
|
| - }
|
| - }
|
| - Socket::Close(socket);
|
| - free(url);
|
| - }
|
| -
|
| - Shutdown();
|
| -}
|
| -
|
| -
|
| -char* VmStats::IsolatesStatus() {
|
| - dart::TextBuffer text(64);
|
| - text.Printf("{\n\"isolates\": [\n");
|
| - IsolateTable::iterator itr;
|
| - bool first = true;
|
| - for (itr = isolate_table_.begin(); itr != isolate_table_.end(); ++itr) {
|
| - Dart_Isolate isolate = itr->second;
|
| - static char request[512];
|
| - snprintf(request, sizeof(request),
|
| - "/isolate/0x%"Px,
|
| - reinterpret_cast<intptr_t>(isolate));
|
| - char* status = VmStatusService::GetVmStatus(request);
|
| - if (status != NULL) {
|
| - if (!first) {
|
| - text.AddString(",\n");
|
| - }
|
| - text.AddString(status);
|
| - first = false;
|
| - free(status);
|
| - }
|
| - }
|
| - text.AddString("\n]\n}\n");
|
| - return strdup(text.buf());
|
| -}
|
| -
|
| -
|
| -// Advance the scanner to the value token of a specified name-value pair.
|
| -void SeekNamedValue(const char* name, dart::JSONScanner* scanner) {
|
| - while (!scanner->EOM()) {
|
| - scanner->Scan();
|
| - if (scanner->IsStringLiteral(name)) {
|
| - scanner->Scan();
|
| - ASSERT(scanner->CurrentToken() == dart::JSONScanner::TokenColon);
|
| - scanner->Scan();
|
| - return;
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| -// Windows doesn't have strndup(), so this is a simple, private version.
|
| -static char* StrNDup(const char* s, uword len) {
|
| - if (strlen(s) < len) {
|
| - len = strlen(s);
|
| - }
|
| - char* result = reinterpret_cast<char*>(malloc(len + 1));
|
| - memmove(result, s, len);
|
| - result[len + 1] = '\0';
|
| - return result;
|
| -}
|
| -
|
| -
|
| -void VmStats::DumpStackThread(uword unused) {
|
| - Log::Print("Isolate dump:\n");
|
| - IsolateTable::iterator itr;
|
| - MonitorLocker ml(instance_monitor_);
|
| - for (itr = instance_->isolate_table_.begin();
|
| - itr != instance_->isolate_table_.end(); ++itr) {
|
| - Dart_Isolate isolate = itr->second;
|
| -
|
| - // Print isolate name and details.
|
| - static char buffer[512];
|
| - snprintf(buffer, sizeof(buffer),
|
| - "/isolate/0x%"Px, reinterpret_cast<intptr_t>(isolate));
|
| - char* isolate_details = VmStatusService::GetVmStatus(buffer);
|
| - if (isolate_details != NULL) {
|
| - dart::JSONScanner scanner(isolate_details);
|
| - SeekNamedValue("name", &scanner);
|
| - char* name = StrNDup(scanner.TokenChars(), scanner.TokenLen());
|
| - SeekNamedValue("port", &scanner);
|
| - char* port = StrNDup(scanner.TokenChars(), scanner.TokenLen());
|
| - Log::Print("\"%s\" port=%s\n", name, port);
|
| - free(isolate_details);
|
| - free(port);
|
| - free(name);
|
| - }
|
| -
|
| - // Print stack trace.
|
| - snprintf(buffer, sizeof(buffer),
|
| - "/isolate/0x%"Px"/stacktrace",
|
| - reinterpret_cast<intptr_t>(isolate));
|
| - char* trace = VmStatusService::GetVmStatus(buffer);
|
| - if (trace != NULL) {
|
| - dart::JSONScanner scanner(trace);
|
| - while (true) {
|
| - SeekNamedValue("url", &scanner);
|
| - if (scanner.CurrentToken() == dart::JSONScanner::TokenEOM) {
|
| - break;
|
| - }
|
| - char* url = StrNDup(scanner.TokenChars(), scanner.TokenLen());
|
| - SeekNamedValue("line", &scanner);
|
| - char* line = StrNDup(scanner.TokenChars(), scanner.TokenLen());
|
| - SeekNamedValue("function", &scanner);
|
| - char* function = StrNDup(scanner.TokenChars(), scanner.TokenLen());
|
| - Log::Print(" at %s(%s:%s)\n", function, url, line);
|
| - free(url);
|
| - free(line);
|
| - free(function);
|
| - }
|
| - free(trace);
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| -void VmStats::DumpStack() {
|
| - int err = dart::Thread::Start(DumpStackThread, 0);
|
| - if (err != 0) {
|
| - Log::PrintErr("Failed starting VmStats stackdump thread: %d\n", err);
|
| - Shutdown();
|
| - }
|
| -}
|
| -
|
| -
|
| -// Global static pointer used to ensure a single instance of the class.
|
| -VmStatusService* VmStatusService::instance_ = NULL;
|
| -
|
| -
|
| -void VmStatusService::InitOnce() {
|
| - ASSERT(VmStatusService::instance_ == NULL);
|
| - VmStatusService::instance_ = new VmStatusService();
|
| - VmStatusService::mutex_ = new dart::Mutex();
|
| -
|
| - // Register built-in status plug-ins. RegisterPlugin is not used because
|
| - // this isn't called within an isolate, and because parameter checking
|
| - // isn't necessary.
|
| - instance_->RegisterPlugin(&Dart_GetVmStatus);
|
| -
|
| - // TODO(tball): dynamically load any additional plug-ins.
|
| -}
|
| -
|
| -
|
| -int VmStatusService::RegisterPlugin(Dart_VmStatusCallback callback) {
|
| - ASSERT(VmStatusService::instance_ != NULL);
|
| - ASSERT(VmStatusService::mutex_ != NULL);
|
| - MutexLocker ml(mutex_);
|
| - if (callback == NULL) {
|
| - return -1;
|
| - }
|
| - VmStatusPlugin* plugin = new VmStatusPlugin(callback);
|
| - VmStatusPlugin* list = instance_->registered_plugin_list_;
|
| - if (list == NULL) {
|
| - instance_->registered_plugin_list_ = plugin;
|
| - } else {
|
| - list->Append(plugin);
|
| - }
|
| - return 0;
|
| -}
|
| -
|
| -
|
| -char* VmStatusService::GetVmStatus(const char* request) {
|
| - ASSERT(VmStatusService::instance_ != NULL);
|
| - VmStatusPlugin* plugin = instance_->registered_plugin_list_;
|
| - while (plugin != NULL) {
|
| - char* result = (plugin->callback())(request);
|
| - if (result != NULL) {
|
| - return result;
|
| - }
|
| - plugin = plugin->next();
|
| - }
|
| - return NULL;
|
| -}
|
| -
|
| -} // namespace bin
|
| -} // namespace dart
|
|
|