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

Side by Side Diff: runtime/bin/vmstats_impl.cc

Issue 18611003: Remove VM Stats (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 5 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « runtime/bin/vmstats_impl.h ('k') | runtime/bin/vmstats_impl_android.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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/file.h"
8 #include "bin/log.h"
9 #include "bin/platform.h"
10 #include "bin/resources.h"
11 #include "bin/socket.h"
12 #include "bin/thread.h"
13 #include "bin/utils.h"
14 #include "include/dart_debugger_api.h"
15 #include "platform/json.h"
16
17
18 namespace dart {
19 namespace bin {
20
21 #define BUFSIZE 8192
22 #define RETRY_PAUSE 100 // milliseconds
23
24 static const char* INDEX_HTML = "index.html";
25 static const char* VMSTATS_HTML = "vmstats.html";
26 static const char* DEFAULT_HOST = "localhost";
27
28 // Global static pointer used to ensure a single instance of the class.
29 VmStats* VmStats::instance_ = NULL;
30 dart::Monitor* VmStats::instance_monitor_;
31 dart::Mutex* VmStatusService::mutex_;
32
33
34 void VmStats::Start(int port, const char* root_dir, bool verbose) {
35 if (instance_ != NULL) {
36 FATAL("VmStats already started.");
37 }
38 instance_ = new VmStats(verbose);
39 instance_monitor_ = new dart::Monitor();
40 Initialize();
41 VmStatusService::InitOnce();
42
43 if (port >= 0) {
44 StartServer(port, root_dir);
45 }
46 }
47
48
49 void VmStats::StartServer(int port, const char* root_dir) {
50 ASSERT(port >= 0);
51 ASSERT(instance_ != NULL);
52 ASSERT(instance_monitor_ != NULL);
53 Socket::Initialize();
54
55 if (root_dir != NULL) {
56 instance_->root_directory_ = root_dir;
57 }
58
59 // TODO(tball): allow host to be specified.
60 char* host = const_cast<char*>(DEFAULT_HOST);
61 OSError* os_error;
62 AddressList<SocketAddress>* addresses =
63 Socket::LookupAddress(host, -1, &os_error);
64 if (addresses == NULL) {
65 Log::PrintErr("Failed IP lookup of VmStats host %s: %s\n",
66 host, os_error->message());
67 return;
68 }
69 const intptr_t BACKLOG = 128; // Default value from HttpServer.dart
70 int64_t address = ServerSocket::CreateBindListen(
71 addresses->GetAt(0)->addr(), port, BACKLOG);
72 delete addresses;
73 if (address < 0) {
74 Log::PrintErr("Failed binding VmStats socket: %s:%d\n", host, port);
75 return;
76 }
77 instance_->bind_address_ = address;
78 Log::Print("VmStats URL: http://%s:%"Pd"/\n", host, Socket::GetPort(address));
79
80 MonitorLocker ml(instance_monitor_);
81 instance_->running_ = true;
82 int err = dart::Thread::Start(WebServer, address);
83 if (err != 0) {
84 Log::PrintErr("Failed starting VmStats thread: %d\n", err);
85 Shutdown();
86 }
87 }
88
89 void VmStats::Stop() {
90 ASSERT(instance_ != NULL);
91 MonitorLocker ml(instance_monitor_);
92 instance_->running_ = false;
93 }
94
95
96 void VmStats::Shutdown() {
97 ASSERT(instance_ != NULL);
98 MonitorLocker ml(instance_monitor_);
99 Socket::Close(instance_->bind_address_);
100 delete instance_;
101 instance_ = NULL;
102 }
103
104
105 void VmStats::AddIsolate(IsolateData* isolate_data,
106 Dart_Isolate isolate) {
107 MonitorLocker ml(instance_monitor_);
108 instance_->isolate_table_[isolate_data] = isolate;
109 }
110
111
112 void VmStats::RemoveIsolate(IsolateData* isolate_data) {
113 MonitorLocker ml(instance_monitor_);
114 instance_->isolate_table_.erase(isolate_data);
115 }
116
117
118 static const char* ContentType(const char* url) {
119 const char* suffix = strrchr(url, '.');
120 if (suffix != NULL) {
121 if (!strcmp(suffix, ".html")) {
122 return "text/html; charset=UTF-8";
123 }
124 if (!strcmp(suffix, ".dart")) {
125 return "application/dart; charset=UTF-8";
126 }
127 if (!strcmp(suffix, ".js")) {
128 return "application/javascript; charset=UTF-8";
129 }
130 if (!strcmp(suffix, ".css")) {
131 return "text/css; charset=UTF-8";
132 }
133 if (!strcmp(suffix, ".gif")) {
134 return "image/gif";
135 }
136 if (!strcmp(suffix, ".png")) {
137 return "image/png";
138 }
139 if (!strcmp(suffix, ".jpg") || !strcmp(suffix, ".jpeg")) {
140 return "image/jpeg";
141 }
142 }
143 return "text/plain";
144 }
145
146
147 // Return a malloc'd string from a format string and arguments.
148 intptr_t alloc_printf(char** result, const char* format, ...) {
149 va_list args;
150 va_start(args, format);
151 intptr_t len = vsnprintf(NULL, 0, format, args) + 1;
152 *result = reinterpret_cast<char*>(malloc(len));
153 return vsnprintf(*result, len, format, args);
154 }
155
156
157 void writeResponse(intptr_t socket, const char* content_type,
158 const char* data, size_t length) {
159 char* header;
160 intptr_t len = alloc_printf(&header,
161 "HTTP/1.1 200 OK\nContent-Type: %s\nContent-Length: %"Pu"\n\n",
162 content_type, length);
163 Socket::Write(socket, header, len);
164 Socket::Write(socket, data, length);
165 Socket::Write(socket, "\n", 1);
166 free(header);
167 }
168
169
170 void writeErrorResponse(intptr_t socket, intptr_t error_num, const char* error,
171 const char* description) {
172 if (description != NULL) {
173 // Create body first, so its length is known when creating the header.
174 char* body;
175 intptr_t body_len = alloc_printf(&body,
176 "<html><head><title>%d %s</title></head>\n"
177 "<body>\n<h1>%s</h1>\n%s\n</body></html>\n",
178 error_num, error, error, description);
179 char* header;
180 intptr_t header_len = alloc_printf(&header,
181 "HTTP/1.1 %d %s\n"
182 "Content-Length: %d\n"
183 "Connection: close\n"
184 "Content-Type: text/html\n\n",
185 error_num, error, body_len);
186 Socket::Write(socket, header, header_len);
187 Socket::Write(socket, body, body_len);
188 free(header);
189 free(body);
190 } else {
191 char* response;
192 intptr_t len =
193 alloc_printf(&response, "HTTP/1.1 %d %s\n\n", error_num, error);
194 Socket::Write(socket, response, len);
195 free(response);
196 }
197 }
198
199
200 void VmStats::WebServer(uword bind_address) {
201 while (true) {
202 intptr_t socket = ServerSocket::Accept(bind_address);
203 if (socket == ServerSocket::kTemporaryFailure) {
204 // Not a real failure, woke up but no connection available.
205
206 // Use MonitorLocker.Wait(), since it has finer granularity than sleep().
207 dart::Monitor m;
208 MonitorLocker ml(&m);
209 ml.Wait(RETRY_PAUSE);
210
211 continue;
212 }
213 if (socket < 0) {
214 // Stop() closed the socket.
215 return;
216 }
217 Socket::SetBlocking(socket);
218
219 // TODO(tball): rewrite this to use STL, so as to eliminate the static
220 // buffer and support resource URLs that are longer than BUFSIZE.
221
222 // Read request.
223 char buffer[BUFSIZE + 1];
224 intptr_t len = Socket::Read(socket, buffer, BUFSIZE);
225 if (len <= 0) {
226 // Invalid HTTP request, ignore.
227 continue;
228 }
229 buffer[len] = '\0';
230
231 // Verify it's a GET request.
232 // TODO(tball): support POST requests.
233 if (strncmp("GET ", buffer, 4) != 0 && strncmp("get ", buffer, 4) != 0) {
234 Log::PrintErr("Unsupported HTTP request type");
235 writeErrorResponse(socket, 403, "Forbidden",
236 "Unsupported HTTP request type");
237 Socket::Close(socket);
238 continue;
239 }
240
241 // Extract GET URL, and null-terminate URL in case request line has
242 // HTTP version.
243 for (int i = 4; i < len; i++) {
244 if (buffer[i] == ' ') {
245 buffer[i] = '\0';
246 }
247 }
248 char* url = strdup(&buffer[4]);
249
250 if (instance_->verbose_) {
251 Log::Print("vmstats: %s requested\n", url);
252 }
253 char* content = NULL;
254
255 // Check for VmStats-specific URLs.
256 if (strcmp(url, "/isolates") == 0) {
257 content = instance_->IsolatesStatus();
258 } else {
259 // Check plug-ins.
260 content = VmStatusService::GetVmStatus(url);
261 }
262
263 if (content != NULL) {
264 writeResponse(socket, "application/json", content, strlen(content));
265 free(content);
266 } else {
267 // No status content with this URL, return file or resource content.
268 dart::TextBuffer path(strlen(instance_->root_directory_) + strlen(url));
269 path.AddString(instance_->root_directory_);
270 path.AddString(url);
271
272 // Expand directory URLs.
273 if (strcmp(url, "/") == 0) {
274 path.AddString(VMSTATS_HTML);
275 } else if (url[strlen(url) - 1] == '/') {
276 path.AddString(INDEX_HTML);
277 }
278
279 bool success = false;
280 char* text_buffer = NULL;
281 const char* content_type = ContentType(path.buf());
282 if (File::Exists(path.buf())) {
283 File* f = File::Open(path.buf(), File::kRead);
284 if (f != NULL) {
285 intptr_t len = f->Length();
286 text_buffer = reinterpret_cast<char*>(malloc(len));
287 if (f->ReadFully(text_buffer, len)) {
288 writeResponse(socket, content_type, text_buffer, len);
289 success = true;
290 }
291 free(text_buffer);
292 delete f;
293 }
294 } else {
295 const char* resource;
296 intptr_t len = Resources::ResourceLookup(path.buf(), &resource);
297 if (len != Resources::kNoSuchInstance) {
298 ASSERT(len >= 0);
299 writeResponse(socket, content_type, resource, len);
300 success = true;
301 }
302 }
303 if (!success) {
304 char* description;
305 alloc_printf(
306 &description, "URL <a href=\"%s\">%s</a> not found.", url, url);
307 writeErrorResponse(socket, 404, "Not Found", description);
308 free(description);
309 }
310 }
311 Socket::Close(socket);
312 free(url);
313 }
314
315 Shutdown();
316 }
317
318
319 char* VmStats::IsolatesStatus() {
320 dart::TextBuffer text(64);
321 text.Printf("{\n\"isolates\": [\n");
322 IsolateTable::iterator itr;
323 bool first = true;
324 for (itr = isolate_table_.begin(); itr != isolate_table_.end(); ++itr) {
325 Dart_Isolate isolate = itr->second;
326 static char request[512];
327 snprintf(request, sizeof(request),
328 "/isolate/0x%"Px,
329 reinterpret_cast<intptr_t>(isolate));
330 char* status = VmStatusService::GetVmStatus(request);
331 if (status != NULL) {
332 if (!first) {
333 text.AddString(",\n");
334 }
335 text.AddString(status);
336 first = false;
337 free(status);
338 }
339 }
340 text.AddString("\n]\n}\n");
341 return strdup(text.buf());
342 }
343
344
345 // Advance the scanner to the value token of a specified name-value pair.
346 void SeekNamedValue(const char* name, dart::JSONScanner* scanner) {
347 while (!scanner->EOM()) {
348 scanner->Scan();
349 if (scanner->IsStringLiteral(name)) {
350 scanner->Scan();
351 ASSERT(scanner->CurrentToken() == dart::JSONScanner::TokenColon);
352 scanner->Scan();
353 return;
354 }
355 }
356 }
357
358
359 // Windows doesn't have strndup(), so this is a simple, private version.
360 static char* StrNDup(const char* s, uword len) {
361 if (strlen(s) < len) {
362 len = strlen(s);
363 }
364 char* result = reinterpret_cast<char*>(malloc(len + 1));
365 memmove(result, s, len);
366 result[len + 1] = '\0';
367 return result;
368 }
369
370
371 void VmStats::DumpStackThread(uword unused) {
372 Log::Print("Isolate dump:\n");
373 IsolateTable::iterator itr;
374 MonitorLocker ml(instance_monitor_);
375 for (itr = instance_->isolate_table_.begin();
376 itr != instance_->isolate_table_.end(); ++itr) {
377 Dart_Isolate isolate = itr->second;
378
379 // Print isolate name and details.
380 static char buffer[512];
381 snprintf(buffer, sizeof(buffer),
382 "/isolate/0x%"Px, reinterpret_cast<intptr_t>(isolate));
383 char* isolate_details = VmStatusService::GetVmStatus(buffer);
384 if (isolate_details != NULL) {
385 dart::JSONScanner scanner(isolate_details);
386 SeekNamedValue("name", &scanner);
387 char* name = StrNDup(scanner.TokenChars(), scanner.TokenLen());
388 SeekNamedValue("port", &scanner);
389 char* port = StrNDup(scanner.TokenChars(), scanner.TokenLen());
390 Log::Print("\"%s\" port=%s\n", name, port);
391 free(isolate_details);
392 free(port);
393 free(name);
394 }
395
396 // Print stack trace.
397 snprintf(buffer, sizeof(buffer),
398 "/isolate/0x%"Px"/stacktrace",
399 reinterpret_cast<intptr_t>(isolate));
400 char* trace = VmStatusService::GetVmStatus(buffer);
401 if (trace != NULL) {
402 dart::JSONScanner scanner(trace);
403 while (true) {
404 SeekNamedValue("url", &scanner);
405 if (scanner.CurrentToken() == dart::JSONScanner::TokenEOM) {
406 break;
407 }
408 char* url = StrNDup(scanner.TokenChars(), scanner.TokenLen());
409 SeekNamedValue("line", &scanner);
410 char* line = StrNDup(scanner.TokenChars(), scanner.TokenLen());
411 SeekNamedValue("function", &scanner);
412 char* function = StrNDup(scanner.TokenChars(), scanner.TokenLen());
413 Log::Print(" at %s(%s:%s)\n", function, url, line);
414 free(url);
415 free(line);
416 free(function);
417 }
418 free(trace);
419 }
420 }
421 }
422
423
424 void VmStats::DumpStack() {
425 int err = dart::Thread::Start(DumpStackThread, 0);
426 if (err != 0) {
427 Log::PrintErr("Failed starting VmStats stackdump thread: %d\n", err);
428 Shutdown();
429 }
430 }
431
432
433 // Global static pointer used to ensure a single instance of the class.
434 VmStatusService* VmStatusService::instance_ = NULL;
435
436
437 void VmStatusService::InitOnce() {
438 ASSERT(VmStatusService::instance_ == NULL);
439 VmStatusService::instance_ = new VmStatusService();
440 VmStatusService::mutex_ = new dart::Mutex();
441
442 // Register built-in status plug-ins. RegisterPlugin is not used because
443 // this isn't called within an isolate, and because parameter checking
444 // isn't necessary.
445 instance_->RegisterPlugin(&Dart_GetVmStatus);
446
447 // TODO(tball): dynamically load any additional plug-ins.
448 }
449
450
451 int VmStatusService::RegisterPlugin(Dart_VmStatusCallback callback) {
452 ASSERT(VmStatusService::instance_ != NULL);
453 ASSERT(VmStatusService::mutex_ != NULL);
454 MutexLocker ml(mutex_);
455 if (callback == NULL) {
456 return -1;
457 }
458 VmStatusPlugin* plugin = new VmStatusPlugin(callback);
459 VmStatusPlugin* list = instance_->registered_plugin_list_;
460 if (list == NULL) {
461 instance_->registered_plugin_list_ = plugin;
462 } else {
463 list->Append(plugin);
464 }
465 return 0;
466 }
467
468
469 char* VmStatusService::GetVmStatus(const char* request) {
470 ASSERT(VmStatusService::instance_ != NULL);
471 VmStatusPlugin* plugin = instance_->registered_plugin_list_;
472 while (plugin != NULL) {
473 char* result = (plugin->callback())(request);
474 if (result != NULL) {
475 return result;
476 }
477 plugin = plugin->next();
478 }
479 return NULL;
480 }
481
482 } // namespace bin
483 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/bin/vmstats_impl.h ('k') | runtime/bin/vmstats_impl_android.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698