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 |