Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 #include "bin/vmstats_impl.h" | |
| 6 | |
| 7 #include "bin/fdutils.h" | |
| 8 #include "bin/file.h" | |
| 9 #include "bin/platform.h" | |
| 10 #include "bin/socket.h" | |
| 11 #include "bin/thread.h" | |
| 12 #include "include/dart_debugger_api.h" | |
| 13 #include "platform/json.h" | |
| 14 | |
| 15 namespace dart { | |
| 16 | |
| 17 #define BUFSIZE 8192 | |
| 18 | |
| 19 // Global static pointer used to ensure a single instance of the class. | |
| 20 VmStats* VmStats::instance_ = NULL; | |
| 21 Mutex VmStats::instance_lock_; | |
| 22 | |
| 23 void VmStats::Start(int port, const char* root_dir) { | |
| 24 if (instance_ != NULL) { | |
|
siva
2013/02/15 05:59:10
Where is instance_ set? I don't see it being set t
Tom Ball
2013/02/16 00:57:19
It was the last statement in Start(), but I change
| |
| 25 FATAL("VmStats already started."); | |
| 26 } | |
| 27 MutexLocker ml(&instance_lock_); | |
| 28 VmStats* vmstats = new VmStats; | |
|
siva
2013/02/15 05:59:10
probably you meant instance_ = new VmStats(); here
Tom Ball
2013/02/16 00:57:19
Done.
| |
| 29 VmStatusService::InitOnce(); | |
| 30 Socket::Initialize(); | |
| 31 | |
| 32 if (root_dir != NULL) { | |
| 33 vmstats->root_directory_ = root_dir; | |
| 34 } | |
| 35 | |
| 36 const intptr_t BACKLOG = 128; // Default value from HttpServer.dart | |
| 37 intptr_t bind_address = | |
| 38 ServerSocket::CreateBindListen("127.0.0.1", port, BACKLOG); | |
| 39 if (bind_address < 0) { | |
| 40 fprintf(stderr, "Failed binding VmStats socket: 127.0.0.1:%d\n", port); | |
| 41 return; | |
| 42 } | |
|
siva
2013/02/15 05:59:10
If port is -1 we probably need code here to get th
Tom Ball
2013/02/16 00:57:19
Done.
| |
| 43 int errno = Thread::Start(WebServer, bind_address); | |
| 44 if (errno != 0) { | |
| 45 fprintf(stderr, "Failed starting VmStats thread: %d\n", errno); | |
| 46 return; | |
| 47 } | |
| 48 instance_ = vmstats; | |
| 49 } | |
| 50 | |
| 51 | |
| 52 void VmStats::Stop() { | |
| 53 MutexLocker ml(&instance_lock_); | |
| 54 if (instance_ != NULL) { | |
| 55 delete instance_; | |
|
siva
2013/02/15 05:59:10
The WebServer thread is still running and it acces
Tom Ball
2013/02/16 00:57:19
Reworked stop so it only sets a running_ instance
| |
| 56 instance_ = NULL; | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 | |
| 61 void VmStats::AddIsolate(IsolateData* isolate_data, | |
| 62 Dart_Isolate isolate) { | |
| 63 MutexLocker ml(&instance_lock_); | |
| 64 if (instance_ != NULL) { | |
| 65 instance_->isolate_table_[isolate_data] = isolate; | |
| 66 } | |
| 67 } | |
| 68 | |
| 69 | |
| 70 void VmStats::RemoveIsolate(IsolateData* isolate_data) { | |
| 71 MutexLocker ml(&instance_lock_); | |
| 72 if (instance_ != NULL) { | |
| 73 instance_->isolate_table_.erase(isolate_data); | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 | |
| 78 static bool endsWith(const std::string s, const char* ending) { | |
| 79 size_t ending_len = strlen(ending); | |
| 80 if (s.length() > ending_len) { | |
| 81 return s.compare(s.length() - ending_len, ending_len, ending) == 0; | |
| 82 } else { | |
| 83 return false; | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 | |
| 88 static const char* ContentType(const char* url) { | |
| 89 // TODO(tball): replace with static map? | |
| 90 std::string path(url); | |
|
siva
2013/02/15 05:59:10
might be more efficient to just do a
const char* s
Tom Ball
2013/02/16 00:57:19
Done.
| |
| 91 if (endsWith(path, ".html")) { | |
| 92 return "text/html; charset=UTF-8"; | |
| 93 } | |
| 94 if (endsWith(path, ".dart")) { | |
| 95 return "application/dart; charset=UTF-8"; | |
| 96 } | |
| 97 if (endsWith(path, ".js")) { | |
| 98 return "application/javascript; charset=UTF-8"; | |
| 99 } | |
| 100 if (endsWith(path, ".css")) { | |
| 101 return "text/css; charset=UTF-8"; | |
| 102 } | |
| 103 if (endsWith(path, ".gif")) { | |
| 104 return "image/gif"; | |
| 105 } | |
| 106 if (endsWith(path, ".png")) { | |
| 107 return "image/png"; | |
| 108 } | |
| 109 if (endsWith(path, ".jpg") || endsWith(path, ".jpeg")) { | |
| 110 return "image/jpeg"; | |
| 111 } | |
| 112 return "text/plain"; | |
| 113 } | |
| 114 | |
| 115 | |
| 116 void VmStats::WebServer(uword bind_address) { | |
| 117 while (true) { | |
| 118 intptr_t socket = ServerSocket::Accept(bind_address); | |
| 119 if (socket == ServerSocket::kTemporaryFailure) { | |
| 120 // Not a real failure, woke up but no connection available. | |
| 121 sleep(1); | |
| 122 continue; | |
| 123 } | |
| 124 if (socket < 0) { | |
| 125 FATAL("Accepting new web server connection failed.\n"); | |
| 126 } | |
| 127 FDUtils::SetBlocking(socket); | |
| 128 | |
| 129 // TODO(tball): rewrite this to use STL, so as to eliminate the static | |
| 130 // buffer and support resources that are bigger than BUFSIZE. | |
| 131 | |
| 132 // Read request. | |
| 133 static char buffer[BUFSIZE + 1]; | |
| 134 int len = read(socket, buffer, BUFSIZE); | |
|
siva
2013/02/15 05:59:10
We may need to handle partial reads here but let u
Tom Ball
2013/02/16 00:57:19
This buffer works as long as the server only suppo
| |
| 135 if (len <= 0) { | |
| 136 // Invalid HTTP request, ignore. | |
| 137 continue; | |
| 138 } | |
| 139 buffer[len] = '\0'; | |
| 140 | |
| 141 // Verify it's a GET request. | |
| 142 if (strncmp("GET ", buffer, 4) != 0 && strncmp("get ", buffer, 4) != 0) { | |
| 143 fprintf(stderr, "Unsupported HTTP request type"); | |
| 144 const char* response = "HTTP/1.1 403 Forbidden\n" | |
| 145 "Content-Length: 120\n" | |
| 146 "Connection: close\n" | |
| 147 "Content-Type: text/html\n\n" | |
| 148 "<html><head>\n<title>403 Forbidden</title>\n</head>" | |
| 149 "<body>\n<h1>Forbidden</h1>\nUnsupported HTTP request type\n</body>" | |
| 150 "</html>\n"; | |
| 151 Socket::Write(socket, response, strlen(response)); | |
|
siva
2013/02/15 05:59:10
Does Socket::Write assert that the entire buffer p
Tom Ball
2013/02/16 00:57:19
It does because I previously set the socket to blo
| |
| 152 close(socket); | |
|
siva
2013/02/15 05:59:10
Socket::Close(socket); ?
Tom Ball
2013/02/16 00:57:19
Done.
| |
| 153 continue; | |
| 154 } | |
| 155 | |
| 156 // Extract GET URL, and null-terminate URL in case request line has | |
| 157 // HTTP version. | |
| 158 for (int i = 4; i < len; i++) { | |
| 159 if (buffer[i] == ' ') { | |
| 160 buffer[i] = '\0'; | |
| 161 } | |
| 162 } | |
| 163 char* url = &buffer[4]; | |
| 164 printf("vmstats: %s requested\n", url); | |
| 165 char* content = NULL; | |
| 166 | |
| 167 // Check for VmStats-specific URLs. | |
| 168 if (strcmp(url, "/isolates") == 0) { | |
| 169 content = instance_->IsolatesStatus(); | |
| 170 } else { | |
| 171 // Check plug-ins. | |
| 172 content = VmStatusService::GetVmStatus(url); | |
| 173 } | |
| 174 | |
| 175 if (content != NULL) { | |
| 176 size_t content_len = strlen(content); | |
| 177 len = snprintf(buffer, BUFSIZE-1, | |
| 178 "HTTP/1.1 200 OK\nContent-Type: application/json; charset=UTF-8\n" | |
| 179 "Content-Length: %d\n\n", content_len); | |
| 180 Socket::Write(socket, buffer, strlen(buffer)); | |
| 181 Socket::Write(socket, content, content_len); | |
| 182 Socket::Write(socket, "\n", 1); | |
| 183 Socket::Write(socket, buffer, strlen(buffer)); | |
| 184 free(content); | |
| 185 } else { | |
| 186 // No status content with this URL, return file or resource content. | |
| 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\nContent-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 } else { | |
| 211 // TODO(tball): look up linked in resource. | |
| 212 } | |
| 213 if (!success) { | |
| 214 const char* response = "HTTP/1.1 404 Not Found\n\n"; | |
| 215 Socket::Write(socket, response, strlen(response)); | |
| 216 } | |
| 217 } | |
| 218 Socket::Close(socket); | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 | |
| 223 char* VmStats::IsolatesStatus() { | |
| 224 std::ostringstream stream; | |
| 225 stream << '{' << std::endl; | |
| 226 stream << "\"isolates\": [" << std::endl; | |
| 227 IsolateTable::iterator itr; | |
| 228 bool first = true; | |
| 229 for (itr = isolate_table_.begin(); itr != isolate_table_.end(); ++itr) { | |
| 230 Dart_Isolate isolate = itr->second; | |
| 231 static char request[512]; | |
| 232 snprintf(request, sizeof(request), "/isolate/0x%llx", | |
| 233 reinterpret_cast<int64_t>(isolate)); | |
| 234 char* status = VmStatusService::GetVmStatus(request); | |
| 235 if (status != NULL) { | |
| 236 stream << status; | |
| 237 if (!first) { | |
| 238 stream << "," << std::endl; | |
| 239 } | |
| 240 first = false; | |
| 241 } | |
| 242 free(status); | |
| 243 } | |
| 244 stream << std::endl << "]"; | |
| 245 stream << std::endl << '}' << std::endl; | |
| 246 return strdup(stream.str().c_str()); | |
| 247 } | |
| 248 | |
| 249 | |
| 250 // Global static pointer used to ensure a single instance of the class. | |
| 251 VmStatusService* VmStatusService::instance_ = NULL; | |
| 252 | |
| 253 | |
| 254 void VmStatusService::InitOnce() { | |
|
siva
2013/02/15 05:59:10
ASSERT(VmStatusService::Instance_ == NULL);
Tom Ball
2013/02/16 00:57:19
Done.
| |
| 255 VmStatusService::instance_ = new VmStatusService(); | |
| 256 | |
| 257 // Register built-in status plug-ins. RegisterPlugin is not used because | |
| 258 // this isn't called within an isolate, and because parameter checking | |
| 259 // isn't necessary. | |
| 260 instance_->RegisterPlugin(&Dart_GetVmStatus); | |
| 261 | |
| 262 // TODO(tball): dynamically load any additional plug-ins. | |
| 263 } | |
| 264 | |
| 265 | |
| 266 int VmStatusService::RegisterPlugin(Dart_VmStatusCallback callback) { | |
|
siva
2013/02/15 05:59:10
We should probably protect this call also under a
Tom Ball
2013/02/16 00:57:19
Done.
| |
| 267 if (callback == NULL) { | |
| 268 return -1; | |
| 269 } | |
| 270 VmStatusPlugin* plugin = new VmStatusPlugin(callback); | |
| 271 VmStatusPlugin* list = instance_->registered_plugin_list_; | |
| 272 if (list == NULL) { | |
| 273 instance_->registered_plugin_list_ = plugin; | |
| 274 } else { | |
| 275 list->Append(plugin); | |
| 276 } | |
| 277 return 0; | |
| 278 } | |
| 279 | |
| 280 | |
| 281 char* VmStatusService::GetVmStatus(const char* request) { | |
| 282 VmStatusPlugin* plugin = instance_->registered_plugin_list_; | |
| 283 while (plugin != NULL) { | |
| 284 char* result = (plugin->callback())(request); | |
| 285 if (result != NULL) { | |
| 286 return result; | |
| 287 } | |
| 288 plugin = plugin->next(); | |
| 289 } | |
| 290 return NULL; | |
| 291 } | |
| 292 | |
| 293 } // namespace dart | |
| OLD | NEW |