| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library vmservice; | 5 library vmservice; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:convert'; | 8 import 'dart:convert'; |
| 9 import 'dart:isolate'; | 9 import 'dart:isolate'; |
| 10 import 'dart:typed_data'; | 10 import 'dart:typed_data'; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 | 29 |
| 30 /// Collection of currently running isolates. | 30 /// Collection of currently running isolates. |
| 31 RunningIsolates runningIsolates = new RunningIsolates(); | 31 RunningIsolates runningIsolates = new RunningIsolates(); |
| 32 | 32 |
| 33 /// A port used to receive events from the VM. | 33 /// A port used to receive events from the VM. |
| 34 final RawReceivePort eventPort; | 34 final RawReceivePort eventPort; |
| 35 | 35 |
| 36 ShutdownCallback onShutdown; | 36 ShutdownCallback onShutdown; |
| 37 | 37 |
| 38 void _addClient(Client client) { | 38 void _addClient(Client client) { |
| 39 assert(client.streams.isEmpty); |
| 39 clients.add(client); | 40 clients.add(client); |
| 40 } | 41 } |
| 41 | 42 |
| 42 void _removeClient(Client client) { | 43 void _removeClient(Client client) { |
| 43 clients.remove(client); | 44 clients.remove(client); |
| 44 } | 45 for (var streamId in client.streams) { |
| 45 | 46 if (!_isAnyClientSubscribed(streamId)) { |
| 46 void _eventMessageHandler(dynamic eventMessage) { | 47 _vmCancelStream(streamId); |
| 47 for (var client in clients) { | |
| 48 if (client.sendEvents) { | |
| 49 client.post(eventMessage); | |
| 50 } | 48 } |
| 51 } | 49 } |
| 52 } | 50 } |
| 51 |
| 52 void _eventMessageHandler(List eventMessage) { |
| 53 var streamId = eventMessage[0]; |
| 54 var event = eventMessage[1]; |
| 55 for (var client in clients) { |
| 56 if (client.sendEvents && client.streams.contains(streamId)) { |
| 57 client.post(event); |
| 58 } |
| 59 } |
| 60 } |
| 53 | 61 |
| 54 void _controlMessageHandler(int code, | 62 void _controlMessageHandler(int code, |
| 55 int portId, | 63 int portId, |
| 56 SendPort sp, | 64 SendPort sp, |
| 57 String name) { | 65 String name) { |
| 58 switch (code) { | 66 switch (code) { |
| 59 case Constants.ISOLATE_STARTUP_MESSAGE_ID: | 67 case Constants.ISOLATE_STARTUP_MESSAGE_ID: |
| 60 runningIsolates.isolateStartup(portId, sp, name); | 68 runningIsolates.isolateStartup(portId, sp, name); |
| 61 break; | 69 break; |
| 62 case Constants.ISOLATE_SHUTDOWN_MESSAGE_ID: | 70 case Constants.ISOLATE_SHUTDOWN_MESSAGE_ID: |
| (...skipping 11 matching lines...) Expand all Loading... |
| 74 client.close(); | 82 client.close(); |
| 75 } | 83 } |
| 76 // Call embedder shutdown hook after the internal shutdown. | 84 // Call embedder shutdown hook after the internal shutdown. |
| 77 if (onShutdown != null) { | 85 if (onShutdown != null) { |
| 78 onShutdown(); | 86 onShutdown(); |
| 79 } | 87 } |
| 80 _onExit(); | 88 _onExit(); |
| 81 } | 89 } |
| 82 | 90 |
| 83 void messageHandler(message) { | 91 void messageHandler(message) { |
| 84 if (message is String) { | |
| 85 // This is an event intended for all clients. | |
| 86 _eventMessageHandler(message); | |
| 87 return; | |
| 88 } | |
| 89 if (message is Uint8List) { | |
| 90 // This is "raw" data intended for a specific client. | |
| 91 // | |
| 92 // TODO(turnidge): Do not broadcast this data to all clients. | |
| 93 _eventMessageHandler(message); | |
| 94 return; | |
| 95 } | |
| 96 if (message is List) { | 92 if (message is List) { |
| 97 // This is an internal vm service event. | 93 if (message.length == 2) { |
| 94 // This is an event. |
| 95 assert(message[0] is String); |
| 96 assert(message[1] is String || message[1] is Uint8List); |
| 97 _eventMessageHandler(message); |
| 98 return; |
| 99 } |
| 98 if (message.length == 1) { | 100 if (message.length == 1) { |
| 99 // This is a control message directing the vm service to exit. | 101 // This is a control message directing the vm service to exit. |
| 100 assert(message[0] == Constants.SERVICE_EXIT_MESSAGE_ID); | 102 assert(message[0] == Constants.SERVICE_EXIT_MESSAGE_ID); |
| 101 _exit(); | 103 _exit(); |
| 102 return; | 104 return; |
| 103 } | 105 } |
| 104 if (message.length == 4) { | 106 if (message.length == 4) { |
| 105 // This is a message informing us of the birth or death of an | 107 // This is a message informing us of the birth or death of an |
| 106 // isolate. | 108 // isolate. |
| 107 _controlMessageHandler(message[0], message[1], message[2], message[3]); | 109 _controlMessageHandler(message[0], message[1], message[2], message[3]); |
| 108 return; | 110 return; |
| 109 } | 111 } |
| 110 } | 112 } |
| 111 | |
| 112 Logger.root.severe( | 113 Logger.root.severe( |
| 113 'Internal vm-service error: ignoring illegal message: $message'); | 114 'Internal vm-service error: ignoring illegal message: $message'); |
| 114 } | 115 } |
| 115 | 116 |
| 116 void _notSupported(_) { | 117 void _notSupported(_) { |
| 117 throw new UnimplementedError('Service script loading not supported.'); | 118 throw new UnimplementedError('Service script loading not supported.'); |
| 118 } | 119 } |
| 119 | 120 |
| 120 VMService._internal() | 121 VMService._internal() |
| 121 : eventPort = isolateLifecyclePort { | 122 : eventPort = isolateLifecyclePort { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 135 var members = []; | 136 var members = []; |
| 136 var result = {}; | 137 var result = {}; |
| 137 clients.forEach((client) { | 138 clients.forEach((client) { |
| 138 members.add(client.toJson()); | 139 members.add(client.toJson()); |
| 139 }); | 140 }); |
| 140 result['type'] = 'ClientList'; | 141 result['type'] = 'ClientList'; |
| 141 result['members'] = members; | 142 result['members'] = members; |
| 142 message.setResponse(JSON.encode(result)); | 143 message.setResponse(JSON.encode(result)); |
| 143 } | 144 } |
| 144 | 145 |
| 146 // These must be kept in sync with the declarations in vm/json_stream.h. |
| 147 static const _kInvalidStream = 100; |
| 148 static const _kStreamAlreadySubscribed = 101; |
| 149 static const _kStreamNotSubscribed = 102; |
| 150 |
| 151 var _errorMessages = { |
| 152 _kInvalidStream: 'Invalid stream', |
| 153 _kStreamAlreadySubscribed: 'Stream already subscribed', |
| 154 _kStreamNotSubscribed: 'Stream not subscribed', |
| 155 }; |
| 156 |
| 157 String _encodeError(Message message, int code) { |
| 158 var response = { |
| 159 'id' : message.serial, |
| 160 'error' : { |
| 161 'code': code, |
| 162 'message': _errorMessages[code], |
| 163 }, |
| 164 }; |
| 165 return JSON.encode(response); |
| 166 } |
| 167 |
| 168 String _encodeResult(Message message, Map result) { |
| 169 var response = { |
| 170 'id' : message.serial, |
| 171 'result' : result, |
| 172 }; |
| 173 return JSON.encode(response); |
| 174 } |
| 175 |
| 176 bool _isValidStream(String streamId) { |
| 177 var validStreams = [ 'Isolate', 'Debug', 'GC', '_Echo', '_Graph' ]; |
| 178 return validStreams.contains(streamId); |
| 179 } |
| 180 |
| 181 bool _isAnyClientSubscribed(String streamId) { |
| 182 for (var client in clients) { |
| 183 if (client.streams.contains(streamId)) { |
| 184 return true; |
| 185 } |
| 186 } |
| 187 return false; |
| 188 } |
| 189 |
| 190 Future<String> _streamListen(Message message) async { |
| 191 var client = message.client; |
| 192 var streamId = message.params['streamId']; |
| 193 |
| 194 if (!_isValidStream(streamId)) { |
| 195 return _encodeError(message, _kInvalidStream); |
| 196 } |
| 197 if (client.streams.contains(streamId)) { |
| 198 return _encodeError(message, _kStreamAlreadySubscribed); |
| 199 } |
| 200 if (!_isAnyClientSubscribed(streamId)) { |
| 201 _vmListenStream(streamId); |
| 202 } |
| 203 client.streams.add(streamId); |
| 204 |
| 205 var result = { 'type' : 'Success' }; |
| 206 return _encodeResult(message, result); |
| 207 } |
| 208 |
| 209 Future<String> _streamCancel(Message message) async { |
| 210 var client = message.client; |
| 211 var streamId = message.params['streamId']; |
| 212 |
| 213 if (!_isValidStream(streamId)) { |
| 214 return _encodeError(message, _kInvalidStream); |
| 215 } |
| 216 if (!client.streams.contains(streamId)) { |
| 217 return _encodeError(message, _kStreamNotSubscribed); |
| 218 } |
| 219 client.streams.remove(streamId); |
| 220 if (!_isAnyClientSubscribed(streamId)) { |
| 221 _vmCancelStream(streamId); |
| 222 } |
| 223 |
| 224 var result = { 'type' : 'Success' }; |
| 225 return _encodeResult(message, result); |
| 226 } |
| 227 |
| 145 // TODO(johnmccutchan): Turn this into a command line tool that uses the | 228 // TODO(johnmccutchan): Turn this into a command line tool that uses the |
| 146 // service library. | 229 // service library. |
| 147 Future<String> _getCrashDump() async { | 230 Future<String> _getCrashDump(Message message) async { |
| 231 var client = message.client; |
| 148 final perIsolateRequests = [ | 232 final perIsolateRequests = [ |
| 149 // ?isolateId=<isolate id> will be appended to each of these requests. | 233 // ?isolateId=<isolate id> will be appended to each of these requests. |
| 150 // Isolate information. | 234 // Isolate information. |
| 151 Uri.parse('getIsolate'), | 235 Uri.parse('getIsolate'), |
| 152 // State of heap. | 236 // State of heap. |
| 153 Uri.parse('_getAllocationProfile'), | 237 Uri.parse('_getAllocationProfile'), |
| 154 // Call stack + local variables. | 238 // Call stack + local variables. |
| 155 Uri.parse('getStack?_full=true'), | 239 Uri.parse('getStack?_full=true'), |
| 156 ]; | 240 ]; |
| 157 | 241 |
| 158 // Snapshot of running isolates. | 242 // Snapshot of running isolates. |
| 159 var isolates = runningIsolates.isolates.values.toList(); | 243 var isolates = runningIsolates.isolates.values.toList(); |
| 160 | 244 |
| 161 // Collect the mapping from request uris to responses. | 245 // Collect the mapping from request uris to responses. |
| 162 var responses = { | 246 var responses = { |
| 163 }; | 247 }; |
| 164 | 248 |
| 165 // Request VM. | 249 // Request VM. |
| 166 var getVM = Uri.parse('getVM'); | 250 var getVM = Uri.parse('getVM'); |
| 167 var getVmResponse = JSON.decode( | 251 var getVmResponse = JSON.decode( |
| 168 await new Message.fromUri(getVM).sendToVM()); | 252 await new Message.fromUri(client, getVM).sendToVM()); |
| 169 responses[getVM.toString()] = getVmResponse['result']; | 253 responses[getVM.toString()] = getVmResponse['result']; |
| 170 | 254 |
| 171 // Request command line flags. | 255 // Request command line flags. |
| 172 var getFlagList = Uri.parse('getFlagList'); | 256 var getFlagList = Uri.parse('getFlagList'); |
| 173 var getFlagListResponse = JSON.decode( | 257 var getFlagListResponse = JSON.decode( |
| 174 await new Message.fromUri(getFlagList).sendToVM()); | 258 await new Message.fromUri(client, getFlagList).sendToVM()); |
| 175 responses[getFlagList.toString()] = getFlagListResponse['result']; | 259 responses[getFlagList.toString()] = getFlagListResponse['result']; |
| 176 | 260 |
| 177 // Make requests to each isolate. | 261 // Make requests to each isolate. |
| 178 for (var isolate in isolates) { | 262 for (var isolate in isolates) { |
| 179 for (var request in perIsolateRequests) { | 263 for (var request in perIsolateRequests) { |
| 180 var message = new Message.forIsolate(request, isolate); | 264 var message = new Message.forIsolate(request, isolate); |
| 181 // Decode the JSON and and insert it into the map. The map key | 265 // Decode the JSON and and insert it into the map. The map key |
| 182 // is the request Uri. | 266 // is the request Uri. |
| 183 var response = JSON.decode(await isolate.route(message)); | 267 var response = JSON.decode(await isolate.route(message)); |
| 184 responses[message.toUri().toString()] = response['result']; | 268 responses[message.toUri().toString()] = response['result']; |
| 185 } | 269 } |
| 186 // Dump the object id ring requests. | 270 // Dump the object id ring requests. |
| 187 var message = | 271 var message = |
| 188 new Message.forIsolate(Uri.parse('_dumpIdZone'), isolate); | 272 new Message.forIsolate(client, Uri.parse('_dumpIdZone'), isolate); |
| 189 var response = JSON.decode(await isolate.route(message)); | 273 var response = JSON.decode(await isolate.route(message)); |
| 190 // Insert getObject requests into responses map. | 274 // Insert getObject requests into responses map. |
| 191 for (var object in response['result']['objects']) { | 275 for (var object in response['result']['objects']) { |
| 192 final requestUri = | 276 final requestUri = |
| 193 'getObject&isolateId=${isolate.serviceId}?objectId=${object["id"]}'; | 277 'getObject&isolateId=${isolate.serviceId}?objectId=${object["id"]}'; |
| 194 responses[requestUri] = object; | 278 responses[requestUri] = object; |
| 195 } | 279 } |
| 196 } | 280 } |
| 197 | 281 |
| 198 // Encode the entire crash dump. | 282 // Encode the entire crash dump. |
| 199 return JSON.encode({ | 283 return _encodeResult(message, responses); |
| 200 'id' : null, | |
| 201 'result' : responses, | |
| 202 }); | |
| 203 } | 284 } |
| 204 | 285 |
| 205 Future<String> route(Message message) { | 286 Future<String> route(Message message) { |
| 206 if (message.completed) { | 287 if (message.completed) { |
| 207 return message.response; | 288 return message.response; |
| 208 } | 289 } |
| 209 // TODO(turnidge): Update to json rpc. BEFORE SUBMIT. | 290 // TODO(turnidge): Update to json rpc. BEFORE SUBMIT. |
| 210 if ((message.path.length == 1) && (message.path[0] == 'clients')) { | 291 if ((message.path.length == 1) && (message.path[0] == 'clients')) { |
| 211 _clientCollection(message); | 292 _clientCollection(message); |
| 212 return message.response; | 293 return message.response; |
| 213 } | 294 } |
| 214 if (message.method == '_getCrashDump') { | 295 if (message.method == '_getCrashDump') { |
| 215 return _getCrashDump(); | 296 return _getCrashDump(message); |
| 297 } |
| 298 if (message.method == 'streamListen') { |
| 299 return _streamListen(message); |
| 300 } |
| 301 if (message.method == 'streamCancel') { |
| 302 return _streamCancel(message); |
| 216 } | 303 } |
| 217 if (message.params['isolateId'] != null) { | 304 if (message.params['isolateId'] != null) { |
| 218 return runningIsolates.route(message); | 305 return runningIsolates.route(message); |
| 219 } | 306 } |
| 220 return message.sendToVM(); | 307 return message.sendToVM(); |
| 221 } | 308 } |
| 222 } | 309 } |
| 223 | 310 |
| 224 RawReceivePort boot() { | 311 RawReceivePort boot() { |
| 225 // Return the port we expect isolate startup and shutdown messages on. | 312 // Return the port we expect isolate startup and shutdown messages on. |
| 226 return isolateLifecyclePort; | 313 return isolateLifecyclePort; |
| 227 } | 314 } |
| 228 | 315 |
| 229 void _registerIsolate(int port_id, SendPort sp, String name) { | 316 void _registerIsolate(int port_id, SendPort sp, String name) { |
| 230 var service = new VMService(); | 317 var service = new VMService(); |
| 231 service.runningIsolates.isolateStartup(port_id, sp, name); | 318 service.runningIsolates.isolateStartup(port_id, sp, name); |
| 232 } | 319 } |
| 233 | 320 |
| 234 void _onStart() native "VMService_OnStart"; | 321 void _onStart() native "VMService_OnStart"; |
| 235 | 322 |
| 236 void _onExit() native "VMService_OnExit"; | 323 void _onExit() native "VMService_OnExit"; |
| 324 |
| 325 void _vmListenStream(String streamId) native "VMService_ListenStream"; |
| 326 |
| 327 void _vmCancelStream(String streamId) native "VMService_CancelStream"; |
| OLD | NEW |