Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(607)

Unified Diff: runtime/bin/vmstats.cc

Issue 12221022: Initial prototype of vmstats support, based on Dart VM Stats draft design doc. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: runtime/bin/vmstats.cc
===================================================================
--- runtime/bin/vmstats.cc (revision 0)
+++ runtime/bin/vmstats.cc (revision 0)
@@ -0,0 +1,243 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
siva 2013/02/09 01:00:57 2013
Tom Ball 2013/02/14 23:45:16 Done.
+// 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.h"
+
+#include "bin/fdutils.h"
+#include "bin/file.h"
+#include "bin/platform.h"
+#include "bin/socket.h"
+#include "platform/json.h"
+#include "platform/thread.h"
+
+namespace dart {
+
+#define BUFSIZE 8192
+
+// Global static pointer used to ensure a single instance of the class.
+VmStats* VmStats::instance_ = NULL;
+
+
+void VmStats::Start(int port) {
+ if (instance_ != NULL) {
+ FATAL("VmStats already started.");
+ }
+ VmStats* vmstats = new VmStats;
+ Socket::Initialize();
+
+ // TODO(tball): replace $HOME with SDK-specific path to web server's
+ // root directory.
+ intptr_t num_vars;
+ char** env = Platform::Environment(&num_vars);
siva 2013/02/09 01:00:57 Are you going to switch this to be a command line
Tom Ball 2013/02/14 23:45:16 Done. I added a flag that optionally sets root dir
+ for (int i = 0; i < num_vars; i++) {
+ char* var = env[i];
+ if (strncmp(var, "HOME=", 5) == 0) {
+ char* home = var + 5;
+ ASSERT(strlen(home) > 0);
+ vmstats->root_directory_ = home;
+ vmstats->root_directory_.append("/vmstats");
+ break;
+ }
+ }
+ Platform::FreeEnvironment(env, num_vars);
+ ASSERT(vmstats->root_directory_.length() > 0);
+
+ const intptr_t BACKLOG = 128; // Default value from HttpServer.dart
+ intptr_t bind_address =
+ ServerSocket::CreateBindListen("127.0.0.1", port, BACKLOG);
siva 2013/02/09 01:00:57 fine for now but we may want to make 127.0.0.1 a p
Tom Ball 2013/02/14 23:45:16 Sounds great -- any security issues with making th
+ if (bind_address < 0) {
+ fprintf(stderr, "Failed binding VmStats socket: 127.0.0.1:%d\n", port);
+ return;
+ }
+ int errno = Thread::Start(WebServer, bind_address);
+ if (errno != 0) {
+ fprintf(stderr, "Failed starting VmStats thread: %d\n", errno);
+ return;
+ }
+ instance_ = vmstats;
+}
+
+
+void VmStats::Stop() {
+ if (instance_ != NULL) {
+ delete instance_;
+ instance_ = NULL;
+ }
+}
+
+
+
+void VmStats::AddIsolate(IsolateData* isolate_data,
+ Dart_Isolate isolate) {
siva 2013/02/09 01:00:57 .... indentation.
Tom Ball 2013/02/14 23:45:16 Done.
+ if (instance_ != NULL) {
+ instance_->isolate_table_[isolate_data] = isolate;
siva 2013/02/11 19:14:55 This method needs to be thread safe right as we ca
Tom Ball 2013/02/14 23:45:16 Done.
+ }
+}
+
+
+void VmStats::RemoveIsolate(IsolateData* isolate_data) {
siva 2013/02/11 19:14:55 Ditto comment about this method being thread safe.
Tom Ball 2013/02/14 23:45:16 Done.
+ if (instance_ != NULL) {
+ instance_->isolate_table_.erase(isolate_data);
+ }
+}
+
+
+static bool endsWith(const std::string s, const char* ending) {
+ size_t ending_len = strlen(ending);
+ if (s.length() > ending_len) {
+ return s.compare(s.length() - ending_len, ending_len, ending) == 0;
+ } else {
+ return false;
+ }
+}
+
+
+static const char* ContentType(const char* url) {
+ // TODO(tball): replace with static map?
+ std::string path(url);
+ if (endsWith(path, ".html")) {
+ return "text/html; charset=UTF-8";
+ }
+ if (endsWith(path, ".dart")) {
+ return "application/dart; charset=UTF-8";
+ }
+ if (endsWith(path, ".js")) {
+ return "application/javascript; charset=UTF-8";
+ }
+ if (endsWith(path, ".css")) {
+ return "text/css; charset=UTF-8";
+ }
+ if (endsWith(path, ".gif")) {
+ return "image/gif";
+ }
+ if (endsWith(path, ".png")) {
+ return "image/png";
+ }
+ if (endsWith(path, ".jpg") || endsWith(path, ".jpeg")) {
+ return "image/jpeg";
+ }
+ return "text/plain";
+}
+
+
+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.
+ sleep(1);
+ continue;
+ }
+ if (socket < 0) {
+ FATAL("Accepting new web server connection failed.\n");
+ }
+ FDUtils::SetBlocking(socket);
+
+ // Read request.
+ static char buffer[BUFSIZE + 1];
+ int len = read(socket, buffer, BUFSIZE);
siva 2013/02/09 01:00:57 How do you deal with partial reads here (e.g: the
+ if (len <= 0) {
+ // Invalid HTTP request, ignore.
+ continue;
+ }
+ buffer[len] = '\0';
+
+ // Verify it's a GET request.
+ if (strncmp("GET ", buffer, 4) != 0 && strncmp("get ", buffer, 4) != 0) {
+ fprintf(stderr, "Unsupported HTTP request type");
+ const char* response = "HTTP/1.1 403 Forbidden\n"
+ "Content-Length: 120\n"
+ "Connection: close\n"
+ "Content-Type: text/html\n\n"
+ "<html><head>\n<title>403 Forbidden</title>\n</head>"
+ "<body>\n<h1>Forbidden</h1>\nUnsupported HTTP request type\n</body>"
+ "</html>\n";
+ Socket::Write(socket, response, strlen(response));
siva 2013/02/09 01:00:57 In all these writes I would assert that the return
+ close(socket);
+ continue;
+ }
+
+ // Extract GET URL.
+ for (int i = 4; i < len; i++) {
+ if (buffer[i] == ' ') {
+ buffer[i] = '\0';
+ break;
+ }
+ }
+ char* url = &buffer[4];
+ printf("vmstats: %s requested\n", url);
+ // TODO(tball): parse URL to determine data requested.
+ if (strcmp(url, "/isolates") == 0) {
+ std::ostringstream stream;
+ stream << '{' << std::endl;
+ instance_->MemoryUsed(&stream);
siva 2013/02/09 01:00:57 Instead of MemoryUsed you could probably call this
Tom Ball 2013/02/14 23:45:16 Done.
+ // TODO(tball): add other metrics, separated by commas.
+ stream << std::endl << '}' << std::endl;
+ std::string content = stream.str();
+
+ len = snprintf(buffer, BUFSIZE-1,
+ "HTTP/1.1 200 OK\n:Content-Type: application/json; charset=UTF-8\n"
+ "Content-Length: %d\n\n", static_cast<int>(content.length()));
+ Socket::Write(socket, buffer, strlen(buffer));
+ Socket::Write(socket, content.c_str(), content.length());
+ Socket::Write(socket, "\n", 1);
+ Socket::Write(socket, buffer, strlen(buffer));
+ } else {
siva 2013/02/09 01:00:57 I assume this else section is present for serving
Tom Ball 2013/02/14 23:45:16 Done.
+ std::string path(instance_->root_directory_);
+ path.append(url);
+ bool success = false;
+ if (File::Exists(path.c_str())) {
+ File* f = File::Open(path.c_str(), File::kRead);
+ if (f != NULL) {
+ intptr_t len = f->Length();
+ char* text_buffer = reinterpret_cast<char*>(malloc(len));
+ if (f->ReadFully(text_buffer, len)) {
+ std::string content(text_buffer, len);
+ const char* content_type = ContentType(url);
+ len = snprintf(buffer, BUFSIZE-1,
+ "HTTP/1.1 200 OK\n:Content-Type: %s\n"
+ "Content-Length: %d\n\n",
+ content_type, static_cast<int>(content.length()));
+ Socket::Write(socket, buffer, strlen(buffer));
+ Socket::Write(socket, content.c_str(), content.length());
+ Socket::Write(socket, "\n", 1);
+ success = true;
+ }
+ free(text_buffer);
+ delete f;
+ }
+ }
+ if (!success) {
+ const char* response = "HTTP/1.1 404 Not Found\n\n";
+ Socket::Write(socket, response, strlen(response));
+ }
+ }
+ close(socket);
+ }
+}
+
+
+void VmStats::MemoryUsed(std::ostringstream* stream) {
+ *stream << "\"isolates\": [" << std::endl;
+ IsolateTable::iterator itr;
+ bool first = true;
+ for (itr = isolate_table_.begin(); itr != isolate_table_.end(); ++itr) {
+ if (!first) {
+ *stream << "," << std::endl;
+ }
+ first = false;
+
+ Dart_Isolate isolate = itr->second;
+ static char request[512];
+ snprintf(request, sizeof(request), "/isolate/0x%llx",
+ reinterpret_cast<int64_t>(isolate));
+ char* status = Dart_GetVmStatus(request);
+ *stream << status;
+ free(status);
+ }
+ *stream << std::endl << "]";
+}
+
+
+} // namespace dart

Powered by Google App Engine
This is Rietveld 408576698