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 used by debugger wire protocol tests (standalone VM debugging). | 5 // Library used by debugger wire protocol tests (standalone VM debugging). |
6 | 6 |
7 library DartDebugger; | 7 library DartDebugger; |
8 | 8 |
9 import "dart:async"; | 9 import "dart:async"; |
10 import "dart:io"; | 10 import "dart:io"; |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
224 | 224 |
225 MatchFrame(int frameIndex, String functionName) { | 225 MatchFrame(int frameIndex, String functionName) { |
226 return new FrameMatcher(frameIndex, [ functionName ]); | 226 return new FrameMatcher(frameIndex, [ functionName ]); |
227 } | 227 } |
228 | 228 |
229 MatchFrames(List<String> functionNames) { | 229 MatchFrames(List<String> functionNames) { |
230 return new FrameMatcher(0, functionNames); | 230 return new FrameMatcher(0, functionNames); |
231 } | 231 } |
232 | 232 |
233 | 233 |
234 class LocalsMatcher extends Command { | |
235 Map locals = {}; | |
236 | |
237 LocalsMatcher(this.locals) { | |
238 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}} ; | |
239 } | |
240 | |
241 void matchResponse(Debugger debugger) { | |
242 super.matchResponse(debugger); | |
243 | |
244 List frames = getJsonValue(debugger.currentMessage, "result:callFrames"); | |
245 assert(frames != null); | |
246 | |
247 String functionName = frames[0]['functionName']; | |
248 List localsList = frames[0]['locals']; | |
249 Map reportedLocals = {}; | |
250 localsList.forEach((local) => reportedLocals[local['name']] = local['value'] ); | |
251 for (String key in locals.keys) { | |
252 if (reportedLocals[key] == null) { | |
253 debugger.error("Error in $functionName(): no value reported for local " | |
254 "variable $key"); | |
255 return; | |
256 } | |
257 String expected = locals[key]; | |
258 String actual = reportedLocals[key]['text']; | |
259 if (expected != actual) { | |
260 debugger.error("Error in $functionName(): got '$actual' for local " | |
261 "variable $key, but expected '$expected'"); | |
262 return; | |
263 } | |
264 } | |
265 } | |
266 } | |
267 | |
268 | |
269 MatchLocals(Map localValues) { | |
270 return new LocalsMatcher(localValues); | |
271 } | |
272 | |
273 | |
274 class EventMatcher { | |
275 String eventName; | |
276 Map params; | |
277 | |
278 EventMatcher(this.eventName, this.params); | |
279 | |
280 void matchEvent(Debugger debugger) { | |
hausner
2013/06/18 21:05:09
If an event was matched, it might make sense to ta
| |
281 for (Event event in debugger.events) { | |
282 if (event.name == eventName) { | |
283 if (params == null) { | |
284 return; | |
285 } else if (matchMaps(params, event.params)) { | |
286 return; | |
287 } | |
288 } | |
289 } | |
290 | |
291 String msg = params == null ? '' : params.toString(); | |
292 debugger.error("Error: could not match event $eventName $msg"); | |
293 } | |
294 } | |
295 | |
296 | |
297 ExpectEvent(String eventName, [Map params]) { | |
298 return new EventMatcher(eventName, params); | |
299 } | |
300 | |
301 | |
234 class RunCommand extends Command { | 302 class RunCommand extends Command { |
235 RunCommand.resume() { | 303 RunCommand.resume() { |
236 template = {"id": 0, "command": "resume", "params": {"isolateId": 0}}; | 304 template = {"id": 0, "command": "resume", "params": {"isolateId": 0}}; |
237 } | 305 } |
238 RunCommand.step() { | 306 RunCommand.step() { |
239 template = {"id": 0, "command": "stepOver", "params": {"isolateId": 0}}; | 307 template = {"id": 0, "command": "stepOver", "params": {"isolateId": 0}}; |
240 } | 308 } |
241 RunCommand.stepInto() { | 309 RunCommand.stepInto() { |
242 template = {"id": 0, "command": "stepInto", "params": {"isolateId": 0}}; | 310 template = {"id": 0, "command": "stepInto", "params": {"isolateId": 0}}; |
243 } | 311 } |
(...skipping 25 matching lines...) Expand all Loading... | |
269 void send(Debugger debugger) { | 337 void send(Debugger debugger) { |
270 assert(debugger.scriptUrl != null); | 338 assert(debugger.scriptUrl != null); |
271 template["params"]["url"] = debugger.scriptUrl; | 339 template["params"]["url"] = debugger.scriptUrl; |
272 template["params"]["line"] = line; | 340 template["params"]["line"] = line; |
273 debugger.sendMessage(template); | 341 debugger.sendMessage(template); |
274 } | 342 } |
275 } | 343 } |
276 | 344 |
277 SetBreakpoint(int line) => new SetBreakpointCommand(line); | 345 SetBreakpoint(int line) => new SetBreakpointCommand(line); |
278 | 346 |
347 class Event { | |
348 String name; | |
349 Map params; | |
350 | |
351 Event(Map json) { | |
352 name = json['event']; | |
353 params = json['params']; | |
354 } | |
355 } | |
356 | |
279 | 357 |
280 // A debug script is a list of Command objects. | 358 // A debug script is a list of Command objects. |
281 class DebugScript { | 359 class DebugScript { |
282 List entries; | 360 List entries; |
283 DebugScript(List scriptEntries) { | 361 DebugScript(List scriptEntries) { |
284 entries = new List.from(scriptEntries.reversed); | 362 entries = new List.from(scriptEntries.reversed); |
285 entries.add(MatchFrame(0, "main")); | 363 entries.add(MatchFrame(0, "main")); |
286 } | 364 } |
287 bool get isEmpty => entries.isEmpty; | 365 bool get isEmpty => entries.isEmpty; |
366 bool get isNextEventMatcher => !isEmpty && currentEntry is EventMatcher; | |
288 get currentEntry => entries.last; | 367 get currentEntry => entries.last; |
289 advance() => entries.removeLast(); | 368 advance() => entries.removeLast(); |
290 add(entry) => entries.add(entry); | 369 add(entry) => entries.add(entry); |
291 } | 370 } |
292 | 371 |
293 | 372 |
294 class Debugger { | 373 class Debugger { |
295 // Debug target process properties. | 374 // Debug target process properties. |
296 Process targetProcess; | 375 Process targetProcess; |
297 Socket socket; | 376 Socket socket; |
298 JsonBuffer responses = new JsonBuffer(); | 377 JsonBuffer responses = new JsonBuffer(); |
299 | 378 |
300 DebugScript script; | 379 DebugScript script; |
301 int seqNr = 0; // Sequence number of next debugger command message. | 380 int seqNr = 0; // Sequence number of next debugger command message. |
302 Command lastCommand = null; // Most recent command sent to target. | 381 Command lastCommand = null; // Most recent command sent to target. |
303 List<String> errors = new List(); | 382 List<String> errors = new List(); |
383 List<Event> events = new List(); | |
304 bool cleanupDone = false; | 384 bool cleanupDone = false; |
305 | 385 |
306 // Data collected from debug target. | 386 // Data collected from debug target. |
307 Map currentMessage = null; // Currently handled message sent by target. | 387 Map currentMessage = null; // Currently handled message sent by target. |
308 String scriptUrl = null; | 388 String scriptUrl = null; |
309 bool shutdownEventSeen = false; | 389 bool shutdownEventSeen = false; |
310 int isolateId = 0; | 390 int isolateId = 0; |
311 bool isPaused = false; | 391 bool isPaused = false; |
312 | 392 |
313 Debugger(this.targetProcess, this.script) { | 393 Debugger(this.targetProcess, this.script) { |
(...skipping 13 matching lines...) Expand all Loading... | |
327 var stderrStringStream = targetProcess.stderr | 407 var stderrStringStream = targetProcess.stderr |
328 .transform(new StringDecoder()) | 408 .transform(new StringDecoder()) |
329 .transform(new LineTransformer()); | 409 .transform(new LineTransformer()); |
330 stderrStringStream.listen((line) { | 410 stderrStringStream.listen((line) { |
331 print("TARG: $line"); | 411 print("TARG: $line"); |
332 }); | 412 }); |
333 } | 413 } |
334 | 414 |
335 // Handle debugger events, updating the debugger state. | 415 // Handle debugger events, updating the debugger state. |
336 void handleEvent(Map<String,dynamic> msg) { | 416 void handleEvent(Map<String,dynamic> msg) { |
417 events.add(new Event(msg)); | |
418 | |
337 if (msg["event"] == "isolate") { | 419 if (msg["event"] == "isolate") { |
338 if (msg["params"]["reason"] == "created") { | 420 if (msg["params"]["reason"] == "created") { |
339 isolateId = msg["params"]["id"]; | 421 isolateId = msg["params"]["id"]; |
340 assert(isolateId != null); | 422 assert(isolateId != null); |
341 print("Debuggee isolate id $isolateId created."); | 423 print("Debuggee isolate id $isolateId created."); |
342 } else if (msg["params"]["reason"] == "shutdown") { | 424 } else if (msg["params"]["reason"] == "shutdown") { |
343 print("Debuggee isolate id ${msg["params"]["id"]} shut down."); | 425 print("Debuggee isolate id ${msg["params"]["id"]} shut down."); |
344 shutdownEventSeen = true; | 426 shutdownEventSeen = true; |
345 if (!script.isEmpty) { | 427 if (!script.isEmpty) { |
346 error("Error: premature isolate shutdown event seen."); | 428 error("Error: premature isolate shutdown event seen."); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
381 if (errorsDetected) { | 463 if (errorsDetected) { |
382 error("Error while matching response to debugger command"); | 464 error("Error while matching response to debugger command"); |
383 error("Response received from debug target: $receivedMsg"); | 465 error("Response received from debug target: $receivedMsg"); |
384 } | 466 } |
385 } | 467 } |
386 } | 468 } |
387 | 469 |
388 // Send next debugger command in the script, if a response | 470 // Send next debugger command in the script, if a response |
389 // form the last command has been received and processed. | 471 // form the last command has been received and processed. |
390 void sendNextCommand() { | 472 void sendNextCommand() { |
473 while (script.isNextEventMatcher) { | |
474 EventMatcher matcher = script.currentEntry; | |
475 script.advance(); | |
476 matcher.matchEvent(this); | |
477 } | |
478 | |
391 if (lastCommand == null) { | 479 if (lastCommand == null) { |
392 if (script.currentEntry is Command) { | 480 if (script.currentEntry is Command) { |
393 script.currentEntry.send(this); | 481 script.currentEntry.send(this); |
394 lastCommand = script.currentEntry; | 482 lastCommand = script.currentEntry; |
395 seqNr++; | 483 seqNr++; |
396 script.advance(); | 484 script.advance(); |
397 } | 485 } |
398 } | 486 } |
399 } | 487 } |
400 | 488 |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
525 targetOpts.add("--debuggee"); | 613 targetOpts.add("--debuggee"); |
526 print('args: ${targetOpts.join(" ")}'); | 614 print('args: ${targetOpts.join(" ")}'); |
527 | 615 |
528 Process.start(options.executable, targetOpts).then((Process process) { | 616 Process.start(options.executable, targetOpts).then((Process process) { |
529 print("Debug target process started, pid ${process.pid}."); | 617 print("Debug target process started, pid ${process.pid}."); |
530 process.stdin.close(); | 618 process.stdin.close(); |
531 var debugger = new Debugger(process, new DebugScript(script)); | 619 var debugger = new Debugger(process, new DebugScript(script)); |
532 }); | 620 }); |
533 return true; | 621 return true; |
534 } | 622 } |
OLD | NEW |