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