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 |