Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // 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.
| |
| 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. | |
| 4 | |
| 5 #include "bin/vmstats.h" | |
| 6 | |
| 7 #include "bin/fdutils.h" | |
| 8 #include "bin/file.h" | |
| 9 #include "bin/platform.h" | |
| 10 #include "bin/socket.h" | |
| 11 #include "platform/json.h" | |
| 12 #include "platform/thread.h" | |
| 13 | |
| 14 namespace dart { | |
| 15 | |
| 16 #define BUFSIZE 8192 | |
| 17 | |
| 18 // Global static pointer used to ensure a single instance of the class. | |
| 19 VmStats* VmStats::instance_ = NULL; | |
| 20 | |
| 21 | |
| 22 void VmStats::Start(int port) { | |
| 23 if (instance_ != NULL) { | |
| 24 FATAL("VmStats already started."); | |
| 25 } | |
| 26 VmStats* vmstats = new VmStats; | |
| 27 Socket::Initialize(); | |
| 28 | |
| 29 // TODO(tball): replace $HOME with SDK-specific path to web server's | |
| 30 // root directory. | |
| 31 intptr_t num_vars; | |
| 32 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
| |
| 33 for (int i = 0; i < num_vars; i++) { | |
| 34 char* var = env[i]; | |
| 35 if (strncmp(var, "HOME=", 5) == 0) { | |
| 36 char* home = var + 5; | |
| 37 ASSERT(strlen(home) > 0); | |
| 38 vmstats->root_directory_ = home; | |
| 39 vmstats->root_directory_.append("/vmstats"); | |
| 40 break; | |
| 41 } | |
| 42 } | |
| 43 Platform::FreeEnvironment(env, num_vars); | |
| 44 ASSERT(vmstats->root_directory_.length() > 0); | |
| 45 | |
| 46 const intptr_t BACKLOG = 128; // Default value from HttpServer.dart | |
| 47 intptr_t bind_address = | |
| 48 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
| |
| 49 if (bind_address < 0) { | |
| 50 fprintf(stderr, "Failed binding VmStats socket: 127.0.0.1:%d\n", port); | |
| 51 return; | |
| 52 } | |
| 53 int errno = Thread::Start(WebServer, bind_address); | |
| 54 if (errno != 0) { | |
| 55 fprintf(stderr, "Failed starting VmStats thread: %d\n", errno); | |
| 56 return; | |
| 57 } | |
| 58 instance_ = vmstats; | |
| 59 } | |
| 60 | |
| 61 | |
| 62 void VmStats::Stop() { | |
| 63 if (instance_ != NULL) { | |
| 64 delete instance_; | |
| 65 instance_ = NULL; | |
| 66 } | |
| 67 } | |
| 68 | |
| 69 | |
| 70 | |
| 71 void VmStats::AddIsolate(IsolateData* isolate_data, | |
| 72 Dart_Isolate isolate) { | |
|
siva
2013/02/09 01:00:57
.... indentation.
Tom Ball
2013/02/14 23:45:16
Done.
| |
| 73 if (instance_ != NULL) { | |
| 74 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.
| |
| 75 } | |
| 76 } | |
| 77 | |
| 78 | |
| 79 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.
| |
| 80 if (instance_ != NULL) { | |
| 81 instance_->isolate_table_.erase(isolate_data); | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 | |
| 86 static bool endsWith(const std::string s, const char* ending) { | |
| 87 size_t ending_len = strlen(ending); | |
| 88 if (s.length() > ending_len) { | |
| 89 return s.compare(s.length() - ending_len, ending_len, ending) == 0; | |
| 90 } else { | |
| 91 return false; | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 | |
| 96 static const char* ContentType(const char* url) { | |
| 97 // TODO(tball): replace with static map? | |
| 98 std::string path(url); | |
| 99 if (endsWith(path, ".html")) { | |
| 100 return "text/html; charset=UTF-8"; | |
| 101 } | |
| 102 if (endsWith(path, ".dart")) { | |
| 103 return "application/dart; charset=UTF-8"; | |
| 104 } | |
| 105 if (endsWith(path, ".js")) { | |
| 106 return "application/javascript; charset=UTF-8"; | |
| 107 } | |
| 108 if (endsWith(path, ".css")) { | |
| 109 return "text/css; charset=UTF-8"; | |
| 110 } | |
| 111 if (endsWith(path, ".gif")) { | |
| 112 return "image/gif"; | |
| 113 } | |
| 114 if (endsWith(path, ".png")) { | |
| 115 return "image/png"; | |
| 116 } | |
| 117 if (endsWith(path, ".jpg") || endsWith(path, ".jpeg")) { | |
| 118 return "image/jpeg"; | |
| 119 } | |
| 120 return "text/plain"; | |
| 121 } | |
| 122 | |
| 123 | |
| 124 void VmStats::WebServer(uword bind_address) { | |
| 125 while (true) { | |
| 126 intptr_t socket = ServerSocket::Accept(bind_address); | |
| 127 if (socket == ServerSocket::kTemporaryFailure) { | |
| 128 // Not a real failure, woke up but no connection available. | |
| 129 sleep(1); | |
| 130 continue; | |
| 131 } | |
| 132 if (socket < 0) { | |
| 133 FATAL("Accepting new web server connection failed.\n"); | |
| 134 } | |
| 135 FDUtils::SetBlocking(socket); | |
| 136 | |
| 137 // Read request. | |
| 138 static char buffer[BUFSIZE + 1]; | |
| 139 int len = read(socket, buffer, BUFSIZE); | |
|
siva
2013/02/09 01:00:57
How do you deal with partial reads here
(e.g: the
| |
| 140 if (len <= 0) { | |
| 141 // Invalid HTTP request, ignore. | |
| 142 continue; | |
| 143 } | |
| 144 buffer[len] = '\0'; | |
| 145 | |
| 146 // Verify it's a GET request. | |
| 147 if (strncmp("GET ", buffer, 4) != 0 && strncmp("get ", buffer, 4) != 0) { | |
| 148 fprintf(stderr, "Unsupported HTTP request type"); | |
| 149 const char* response = "HTTP/1.1 403 Forbidden\n" | |
| 150 "Content-Length: 120\n" | |
| 151 "Connection: close\n" | |
| 152 "Content-Type: text/html\n\n" | |
| 153 "<html><head>\n<title>403 Forbidden</title>\n</head>" | |
| 154 "<body>\n<h1>Forbidden</h1>\nUnsupported HTTP request type\n</body>" | |
| 155 "</html>\n"; | |
| 156 Socket::Write(socket, response, strlen(response)); | |
|
siva
2013/02/09 01:00:57
In all these writes I would assert that the return
| |
| 157 close(socket); | |
| 158 continue; | |
| 159 } | |
| 160 | |
| 161 // Extract GET URL. | |
| 162 for (int i = 4; i < len; i++) { | |
| 163 if (buffer[i] == ' ') { | |
| 164 buffer[i] = '\0'; | |
| 165 break; | |
| 166 } | |
| 167 } | |
| 168 char* url = &buffer[4]; | |
| 169 printf("vmstats: %s requested\n", url); | |
| 170 // TODO(tball): parse URL to determine data requested. | |
| 171 if (strcmp(url, "/isolates") == 0) { | |
| 172 std::ostringstream stream; | |
| 173 stream << '{' << std::endl; | |
| 174 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.
| |
| 175 // TODO(tball): add other metrics, separated by commas. | |
| 176 stream << std::endl << '}' << std::endl; | |
| 177 std::string content = stream.str(); | |
| 178 | |
| 179 len = snprintf(buffer, BUFSIZE-1, | |
| 180 "HTTP/1.1 200 OK\n:Content-Type: application/json; charset=UTF-8\n" | |
| 181 "Content-Length: %d\n\n", static_cast<int>(content.length())); | |
| 182 Socket::Write(socket, buffer, strlen(buffer)); | |
| 183 Socket::Write(socket, content.c_str(), content.length()); | |
| 184 Socket::Write(socket, "\n", 1); | |
| 185 Socket::Write(socket, buffer, strlen(buffer)); | |
| 186 } 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.
| |
| 187 std::string path(instance_->root_directory_); | |
| 188 path.append(url); | |
| 189 bool success = false; | |
| 190 if (File::Exists(path.c_str())) { | |
| 191 File* f = File::Open(path.c_str(), File::kRead); | |
| 192 if (f != NULL) { | |
| 193 intptr_t len = f->Length(); | |
| 194 char* text_buffer = reinterpret_cast<char*>(malloc(len)); | |
| 195 if (f->ReadFully(text_buffer, len)) { | |
| 196 std::string content(text_buffer, len); | |
| 197 const char* content_type = ContentType(url); | |
| 198 len = snprintf(buffer, BUFSIZE-1, | |
| 199 "HTTP/1.1 200 OK\n:Content-Type: %s\n" | |
| 200 "Content-Length: %d\n\n", | |
| 201 content_type, static_cast<int>(content.length())); | |
| 202 Socket::Write(socket, buffer, strlen(buffer)); | |
| 203 Socket::Write(socket, content.c_str(), content.length()); | |
| 204 Socket::Write(socket, "\n", 1); | |
| 205 success = true; | |
| 206 } | |
| 207 free(text_buffer); | |
| 208 delete f; | |
| 209 } | |
| 210 } | |
| 211 if (!success) { | |
| 212 const char* response = "HTTP/1.1 404 Not Found\n\n"; | |
| 213 Socket::Write(socket, response, strlen(response)); | |
| 214 } | |
| 215 } | |
| 216 close(socket); | |
| 217 } | |
| 218 } | |
| 219 | |
| 220 | |
| 221 void VmStats::MemoryUsed(std::ostringstream* stream) { | |
| 222 *stream << "\"isolates\": [" << std::endl; | |
| 223 IsolateTable::iterator itr; | |
| 224 bool first = true; | |
| 225 for (itr = isolate_table_.begin(); itr != isolate_table_.end(); ++itr) { | |
| 226 if (!first) { | |
| 227 *stream << "," << std::endl; | |
| 228 } | |
| 229 first = false; | |
| 230 | |
| 231 Dart_Isolate isolate = itr->second; | |
| 232 static char request[512]; | |
| 233 snprintf(request, sizeof(request), "/isolate/0x%llx", | |
| 234 reinterpret_cast<int64_t>(isolate)); | |
| 235 char* status = Dart_GetVmStatus(request); | |
| 236 *stream << status; | |
| 237 free(status); | |
| 238 } | |
| 239 *stream << std::endl << "]"; | |
| 240 } | |
| 241 | |
| 242 | |
| 243 } // namespace dart | |
| OLD | NEW |