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

Side by Side Diff: runtime/bin/vmstats_impl.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, 9 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/include/dart_debugger_api.h » ('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 <sstream>
8
9 #include "bin/fdutils.h"
10 #include "bin/file.h"
11 #include "bin/log.h"
12 #include "bin/platform.h"
13 #include "bin/socket.h"
14 #include "bin/thread.h"
15 #include "bin/utils.h"
16 #include "include/dart_debugger_api.h"
17 #include "platform/json.h"
18
19 #define BUFSIZE 8192
20 #define RETRY_PAUSE 100 // milliseconds
21
22 static const char* INDEX_HTML = "index.html";
23 static const char* VMSTATS_HTML = "vmstats.html";
24 static const char* DEFAULT_HOST = "localhost";
25
26 // Global static pointer used to ensure a single instance of the class.
27 VmStats* VmStats::instance_ = NULL;
28 dart::Monitor VmStats::instance_monitor_;
29 dart::Mutex VmStatusService::mutex_;
30
31
32 void VmStats::Start(int port, const char* root_dir) {
33 if (instance_ != NULL) {
34 FATAL("VmStats already started.");
35 }
36 MonitorLocker ml(&instance_monitor_);
37 instance_ = new VmStats();
38 VmStatusService::InitOnce();
39 Socket::Initialize();
40
41 if (root_dir != NULL) {
42 instance_->root_directory_ = root_dir;
43 }
44
45 // TODO(tball): allow host to be specified.
46 char* host = const_cast<char*>(DEFAULT_HOST);
47 OSError* os_error;
48 const char* host_ip = Socket::LookupIPv4Address(host, &os_error);
49 if (host_ip == NULL) {
50 Log::PrintErr("Failed IP lookup of VmStats host %s: %s\n",
51 host, os_error->message());
52 return;
53 }
54
55 const intptr_t BACKLOG = 128; // Default value from HttpServer.dart
56 int64_t address = ServerSocket::CreateBindListen(host_ip, port, BACKLOG);
57 if (address < 0) {
58 Log::PrintErr("Failed binding VmStats socket: %s:%d\n", host, port);
59 return;
60 }
61 instance_->bind_address_ = address;
62 Log::Print(
63 #if defined(TARGET_ARCH_X64)
64 "VmStats URL: http://%s:%ld/\n",
65 #else
66 "VmStats URL: http://%s:%d/\n",
67 #endif
68 host,
69 Socket::GetPort(address));
70
71 instance_->running_ = true;
72 int errno = dart::Thread::Start(WebServer, address);
73 if (errno != 0) {
74 Log::PrintErr("Failed starting VmStats thread: %d\n", errno);
75 Shutdown();
76 }
77 }
78
79
80 void VmStats::Stop() {
81 MonitorLocker ml(&instance_monitor_);
82 if (instance_ != NULL) {
83 instance_->running_ = false;
84 }
85 }
86
87
88 void VmStats::Shutdown() {
89 MonitorLocker ml(&instance_monitor_);
90 Socket::Close(instance_->bind_address_);
91 delete instance_;
92 instance_ = NULL;
93 }
94
95
96 void VmStats::AddIsolate(IsolateData* isolate_data,
97 Dart_Isolate isolate) {
98 MonitorLocker ml(&instance_monitor_);
99 if (instance_ != NULL) {
100 instance_->isolate_table_[isolate_data] = isolate;
101 }
102 }
103
104
105 void VmStats::RemoveIsolate(IsolateData* isolate_data) {
106 MonitorLocker ml(&instance_monitor_);
107 if (instance_ != NULL) {
108 instance_->isolate_table_.erase(isolate_data);
109 }
110 }
111
112
113 static const char* ContentType(const char* url) {
114 const char* suffix = strrchr(url, '.');
115 if (suffix != NULL) {
116 if (!strcmp(suffix, ".html")) {
117 return "text/html; charset=UTF-8";
118 }
119 if (!strcmp(suffix, ".dart")) {
120 return "application/dart; charset=UTF-8";
121 }
122 if (!strcmp(suffix, ".js")) {
123 return "application/javascript; charset=UTF-8";
124 }
125 if (!strcmp(suffix, ".css")) {
126 return "text/css; charset=UTF-8";
127 }
128 if (!strcmp(suffix, ".gif")) {
129 return "image/gif";
130 }
131 if (!strcmp(suffix, ".png")) {
132 return "image/png";
133 }
134 if (!strcmp(suffix, ".jpg") || !strcmp(suffix, ".jpeg")) {
135 return "image/jpeg";
136 }
137 }
138 return "text/plain";
139 }
140
141
142 void VmStats::WebServer(uword bind_address) {
143 while (true) {
144 intptr_t socket = ServerSocket::Accept(bind_address);
145 if (socket == ServerSocket::kTemporaryFailure) {
146 // Not a real failure, woke up but no connection available.
147
148 // Use MonitorLocker.Wait(), since it has finer granularity than sleep().
149 dart::Monitor m;
150 MonitorLocker ml(&m);
151 ml.Wait(RETRY_PAUSE);
152
153 continue;
154 }
155 if (socket < 0) {
156 // Stop() closed the socket.
157 return;
158 }
159 FDUtils::SetBlocking(socket);
160
161 // TODO(tball): rewrite this to use STL, so as to eliminate the static
162 // buffer and support resource URLs that are longer than BUFSIZE.
163
164 // Read request.
165 // TODO(tball): support partial reads, possibly needed for POST uploads.
166 char buffer[BUFSIZE + 1];
167 int len = read(socket, buffer, BUFSIZE);
168 if (len <= 0) {
169 // Invalid HTTP request, ignore.
170 continue;
171 }
172 buffer[len] = '\0';
173
174 // Verify it's a GET request.
175 // TODO(tball): support POST requests.
176 if (strncmp("GET ", buffer, 4) != 0 && strncmp("get ", buffer, 4) != 0) {
177 Log::PrintErr("Unsupported HTTP request type");
178 const char* response = "HTTP/1.1 403 Forbidden\n"
179 "Content-Length: 120\n"
180 "Connection: close\n"
181 "Content-Type: text/html\n\n"
182 "<html><head>\n<title>403 Forbidden</title>\n</head>"
183 "<body>\n<h1>Forbidden</h1>\nUnsupported HTTP request type\n</body>"
184 "</html>\n";
185 Socket::Write(socket, response, strlen(response));
186 Socket::Close(socket);
187 continue;
188 }
189
190 // Extract GET URL, and null-terminate URL in case request line has
191 // HTTP version.
192 for (int i = 4; i < len; i++) {
193 if (buffer[i] == ' ') {
194 buffer[i] = '\0';
195 }
196 }
197 char* url = &buffer[4];
198
199 Log::Print("vmstats: %s requested\n", url);
200 char* content = NULL;
201
202 // Check for VmStats-specific URLs.
203 if (strcmp(url, "/isolates") == 0) {
204 content = instance_->IsolatesStatus();
205 } else {
206 // Check plug-ins.
207 content = VmStatusService::GetVmStatus(url);
208 }
209
210 if (content != NULL) {
211 size_t content_len = strlen(content);
212 len = snprintf(buffer, BUFSIZE,
213 #if defined(TARGET_ARCH_X64)
214 "HTTP/1.1 200 OK\nContent-Type: application/json; charset=UTF-8\n"
215 "Content-Length: %lx\n\n",
216 #else
217 "HTTP/1.1 200 OK\nContent-Type: application/json; charset=UTF-8\n"
218 "Content-Length: %d\n\n",
219 #endif
220 content_len);
221 Socket::Write(socket, buffer, strlen(buffer));
222 Socket::Write(socket, content, content_len);
223 Socket::Write(socket, "\n", 1);
224 Socket::Write(socket, buffer, strlen(buffer));
225 free(content);
226 } else {
227 // No status content with this URL, return file or resource content.
228 std::string path(instance_->root_directory_);
229 path.append(url);
230
231 // Expand directory URLs.
232 if (strcmp(url, "/") == 0) {
233 path.append(VMSTATS_HTML);
234 } else if (url[strlen(url) - 1] == '/') {
235 path.append(INDEX_HTML);
236 }
237
238 bool success = false;
239 if (File::Exists(path.c_str())) {
240 File* f = File::Open(path.c_str(), File::kRead);
241 if (f != NULL) {
242 intptr_t len = f->Length();
243 char* text_buffer = reinterpret_cast<char*>(malloc(len));
244 if (f->ReadFully(text_buffer, len)) {
245 const char* content_type = ContentType(path.c_str());
246 snprintf(buffer, BUFSIZE,
247 #if defined(TARGET_ARCH_X64)
248 "HTTP/1.1 200 OK\nContent-Type: %s\n"
249 "Content-Length: %ld\n\n",
250 #else
251 "HTTP/1.1 200 OK\nContent-Type: %s\n"
252 "Content-Length: %d\n\n",
253 #endif
254 content_type, len);
255 Socket::Write(socket, buffer, strlen(buffer));
256 Socket::Write(socket, text_buffer, len);
257 Socket::Write(socket, "\n", 1);
258 success = true;
259 }
260 free(text_buffer);
261 delete f;
262 }
263 } else {
264 // TODO(tball): look up linked in resource.
265 }
266 if (!success) {
267 const char* response = "HTTP/1.1 404 Not Found\n\n";
268 Socket::Write(socket, response, strlen(response));
269 }
270 }
271 Socket::Close(socket);
272 }
273
274 Shutdown();
275 }
276
277
278 char* VmStats::IsolatesStatus() {
279 std::ostringstream stream;
280 stream << '{' << std::endl;
281 stream << "\"isolates\": [" << std::endl;
282 IsolateTable::iterator itr;
283 bool first = true;
284 for (itr = isolate_table_.begin(); itr != isolate_table_.end(); ++itr) {
285 Dart_Isolate isolate = itr->second;
286 static char request[512];
287 snprintf(request, sizeof(request),
288 #if defined(TARGET_ARCH_X64)
289 "/isolate/0x%lx",
290 #else
291 "/isolate/0x%llx",
292 #endif
293 reinterpret_cast<int64_t>(isolate));
294 char* status = VmStatusService::GetVmStatus(request);
295 if (status != NULL) {
296 stream << status;
297 if (!first) {
298 stream << "," << std::endl;
299 }
300 first = false;
301 }
302 free(status);
303 }
304 stream << std::endl << "]";
305 stream << std::endl << '}' << std::endl;
306 return strdup(stream.str().c_str());
307 }
308
309
310 // Global static pointer used to ensure a single instance of the class.
311 VmStatusService* VmStatusService::instance_ = NULL;
312
313
314 void VmStatusService::InitOnce() {
315 ASSERT(VmStatusService::instance_ == NULL);
316 VmStatusService::instance_ = new VmStatusService();
317
318 // Register built-in status plug-ins. RegisterPlugin is not used because
319 // this isn't called within an isolate, and because parameter checking
320 // isn't necessary.
321 instance_->RegisterPlugin(&Dart_GetVmStatus);
322
323 // TODO(tball): dynamically load any additional plug-ins.
324 }
325
326
327 int VmStatusService::RegisterPlugin(Dart_VmStatusCallback callback) {
328 MutexLocker ml(&mutex_);
329 if (callback == NULL) {
330 return -1;
331 }
332 VmStatusPlugin* plugin = new VmStatusPlugin(callback);
333 VmStatusPlugin* list = instance_->registered_plugin_list_;
334 if (list == NULL) {
335 instance_->registered_plugin_list_ = plugin;
336 } else {
337 list->Append(plugin);
338 }
339 return 0;
340 }
341
342
343 char* VmStatusService::GetVmStatus(const char* request) {
344 VmStatusPlugin* plugin = instance_->registered_plugin_list_;
345 while (plugin != NULL) {
346 char* result = (plugin->callback())(request);
347 if (result != NULL) {
348 return result;
349 }
350 plugin = plugin->next();
351 }
352 return NULL;
353 }
OLDNEW
« no previous file with comments | « runtime/bin/vmstats_impl.h ('k') | runtime/include/dart_debugger_api.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698