Chromium Code Reviews| 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 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 179 debugger.error("Error: expected messaged id $id but got ${response["id"]}. "); | 179 debugger.error("Error: expected messaged id $id but got ${response["id"]}. "); |
| 180 } | 180 } |
| 181 } | 181 } |
| 182 } | 182 } |
| 183 | 183 |
| 184 | 184 |
| 185 class FrameMatcher extends Command { | 185 class FrameMatcher extends Command { |
| 186 int frameIndex; | 186 int frameIndex; |
| 187 List<String> functionNames; | 187 List<String> functionNames; |
| 188 | 188 |
| 189 Map locals = {}; | |
| 190 | |
| 189 FrameMatcher(this.frameIndex, this.functionNames) { | 191 FrameMatcher(this.frameIndex, this.functionNames) { |
| 190 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}} ; | 192 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}} ; |
| 191 } | 193 } |
| 192 | 194 |
| 195 void matchLocal(String localName, String localValue) { | |
| 196 locals[localName] = localValue; | |
| 197 } | |
| 198 | |
| 193 void matchResponse(Debugger debugger) { | 199 void matchResponse(Debugger debugger) { |
| 194 super.matchResponse(debugger); | 200 super.matchResponse(debugger); |
| 195 var msg = debugger.currentMessage; | 201 var msg = debugger.currentMessage; |
| 196 List frames = getJsonValue(msg, "result:callFrames"); | 202 List frames = getJsonValue(msg, "result:callFrames"); |
| 197 assert(frames != null); | 203 assert(frames != null); |
| 198 if (debugger.scriptUrl == null) { | 204 if (debugger.scriptUrl == null) { |
| 199 var name = frames[0]["functionName"]; | 205 var name = frames[0]["functionName"]; |
| 200 if (name == "main") { | 206 if (name == "main") { |
| 201 // Extract script url of debugged script. | 207 // Extract script url of debugged script. |
| 202 debugger.scriptUrl = frames[0]["location"]["url"]; | 208 debugger.scriptUrl = frames[0]["location"]["url"]; |
| 203 assert(debugger.scriptUrl != null); | 209 assert(debugger.scriptUrl != null); |
| 204 } | 210 } |
| 205 } | 211 } |
| 206 if (frames.length < functionNames.length) { | 212 if (frames.length < functionNames.length) { |
| 207 debugger.error("Error: stack trace not long enough " | 213 debugger.error("Error: stack trace not long enough " |
| 208 "to match ${functionNames.length} frames"); | 214 "to match ${functionNames.length} frames"); |
| 209 return; | 215 return; |
| 210 } | 216 } |
| 211 for (int i = 0; i < functionNames.length; i++) { | 217 for (int i = 0; i < functionNames.length; i++) { |
| 212 var idx = i + frameIndex; | 218 var idx = i + frameIndex; |
| 213 var name = frames[idx]["functionName"]; | 219 var name = frames[idx]["functionName"]; |
| 214 assert(name != null); | 220 assert(name != null); |
| 215 if (name != functionNames[i]) { | 221 if (name != functionNames[i]) { |
| 216 debugger.error("Error: call frame $idx: " | 222 debugger.error("Error: call frame $idx: " |
| 217 "expected function name '${functionNames[i]}' but found '$name'"); | 223 "expected function name '${functionNames[i]}' but found '$name'"); |
| 218 return; | 224 return; |
| 219 } | 225 } |
| 220 } | 226 } |
| 227 | |
| 228 // verify locals | |
| 229 String functionName = frames[frameIndex]['functionName']; | |
| 230 List localsList = frames[frameIndex]['locals']; | |
| 231 Map reportedLocals = {}; | |
| 232 localsList.forEach((local) => reportedLocals[local['name']] = local['value'] ); | |
| 233 for (String key in locals.keys) { | |
| 234 if (reportedLocals[key] == null) { | |
| 235 debugger.error("Error in $functionName(): no value reported for local " | |
| 236 "variable $key"); | |
| 237 return; | |
| 238 } | |
| 239 String expected = locals[key]; | |
| 240 String actual = reportedLocals[key]['text']; | |
| 241 if (expected != actual) { | |
| 242 debugger.error("Error in $functionName(): got '$actual' for local " | |
| 243 "variable $key, but expected '$expected'"); | |
| 244 return; | |
| 245 } | |
| 246 } | |
| 221 } | 247 } |
| 222 } | 248 } |
| 223 | 249 |
| 224 | 250 |
| 225 MatchFrame(int frameIndex, String functionName) { | 251 MatchFrame(int frameIndex, String functionName) { |
| 226 return new FrameMatcher(frameIndex, [ functionName ]); | 252 return new FrameMatcher(frameIndex, [ functionName ]); |
| 227 } | 253 } |
| 228 | 254 |
| 229 MatchFrames(List<String> functionNames) { | 255 MatchFrames(List<String> functionNames) { |
|
hausner
2013/06/18 17:30:25
How about an optional parameter here that takes a
| |
| 230 return new FrameMatcher(0, functionNames); | 256 return new FrameMatcher(0, functionNames); |
| 231 } | 257 } |
| 232 | 258 |
| 233 | 259 |
| 260 class EventMatcher { | |
| 261 String eventName; | |
| 262 | |
| 263 EventMatcher(this.eventName); | |
| 264 | |
| 265 void matchEvent(Debugger debugger, Map event) { | |
| 266 String actualName = event['event']; | |
| 267 | |
| 268 if (eventName != actualName) { | |
| 269 debugger.error("Error: got event $actualName but expected event $eventName "); | |
| 270 return; | |
| 271 } | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 | |
| 276 ExpectEvent(String eventName) { | |
| 277 return new EventMatcher(eventName); | |
| 278 } | |
| 279 | |
| 280 | |
| 234 class RunCommand extends Command { | 281 class RunCommand extends Command { |
| 235 RunCommand.resume() { | 282 RunCommand.resume() { |
| 236 template = {"id": 0, "command": "resume", "params": {"isolateId": 0}}; | 283 template = {"id": 0, "command": "resume", "params": {"isolateId": 0}}; |
| 237 } | 284 } |
| 238 RunCommand.step() { | 285 RunCommand.step() { |
| 239 template = {"id": 0, "command": "stepOver", "params": {"isolateId": 0}}; | 286 template = {"id": 0, "command": "stepOver", "params": {"isolateId": 0}}; |
| 240 } | 287 } |
| 241 RunCommand.stepInto() { | 288 RunCommand.stepInto() { |
| 242 template = {"id": 0, "command": "stepInto", "params": {"isolateId": 0}}; | 289 template = {"id": 0, "command": "stepInto", "params": {"isolateId": 0}}; |
| 243 } | 290 } |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 278 | 325 |
| 279 | 326 |
| 280 // A debug script is a list of Command objects. | 327 // A debug script is a list of Command objects. |
| 281 class DebugScript { | 328 class DebugScript { |
| 282 List entries; | 329 List entries; |
| 283 DebugScript(List scriptEntries) { | 330 DebugScript(List scriptEntries) { |
| 284 entries = new List.from(scriptEntries.reversed); | 331 entries = new List.from(scriptEntries.reversed); |
| 285 entries.add(MatchFrame(0, "main")); | 332 entries.add(MatchFrame(0, "main")); |
| 286 } | 333 } |
| 287 bool get isEmpty => entries.isEmpty; | 334 bool get isEmpty => entries.isEmpty; |
| 335 bool get isNextEventMatcher => !isEmpty && currentEntry is EventMatcher; | |
| 288 get currentEntry => entries.last; | 336 get currentEntry => entries.last; |
| 289 advance() => entries.removeLast(); | 337 advance() => entries.removeLast(); |
| 290 add(entry) => entries.add(entry); | 338 add(entry) => entries.add(entry); |
| 291 } | 339 } |
| 292 | 340 |
| 293 | 341 |
| 294 class Debugger { | 342 class Debugger { |
| 295 // Debug target process properties. | 343 // Debug target process properties. |
| 296 Process targetProcess; | 344 Process targetProcess; |
| 297 Socket socket; | 345 Socket socket; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 354 var location = msg["params"]["location"]; | 402 var location = msg["params"]["location"]; |
| 355 assert(location != null); | 403 assert(location != null); |
| 356 print("Isolate $isolateId: breakpoint $bpId resolved" | 404 print("Isolate $isolateId: breakpoint $bpId resolved" |
| 357 " at location $location"); | 405 " at location $location"); |
| 358 // We may want to maintain a table of breakpoints in the future. | 406 // We may want to maintain a table of breakpoints in the future. |
| 359 } else if (msg["event"] == "paused") { | 407 } else if (msg["event"] == "paused") { |
| 360 isPaused = true; | 408 isPaused = true; |
| 361 } else { | 409 } else { |
| 362 error("Error: unknown debugger event received"); | 410 error("Error: unknown debugger event received"); |
| 363 } | 411 } |
| 412 | |
| 413 if (script.isNextEventMatcher) { | |
| 414 EventMatcher matcher = script.currentEntry; | |
| 415 script.advance(); | |
| 416 matcher.matchEvent(this, msg); | |
| 417 } | |
| 364 } | 418 } |
| 365 | 419 |
| 366 // Handle one JSON message object and match it to the | 420 // Handle one JSON message object and match it to the |
| 367 // expected events and responses in the debugging script. | 421 // expected events and responses in the debugging script. |
| 368 void handleMessage(Map<String,dynamic> receivedMsg) { | 422 void handleMessage(Map<String,dynamic> receivedMsg) { |
| 369 currentMessage = receivedMsg; | 423 currentMessage = receivedMsg; |
| 370 if (receivedMsg["event"] != null) { | 424 if (receivedMsg["event"] != null) { |
| 371 handleEvent(receivedMsg); | 425 handleEvent(receivedMsg); |
| 372 if (errorsDetected) { | 426 if (errorsDetected) { |
| 373 error("Error while handling debugger event"); | 427 error("Error while handling debugger event"); |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 525 targetOpts.add("--debuggee"); | 579 targetOpts.add("--debuggee"); |
| 526 print('args: ${targetOpts.join(" ")}'); | 580 print('args: ${targetOpts.join(" ")}'); |
| 527 | 581 |
| 528 Process.start(options.executable, targetOpts).then((Process process) { | 582 Process.start(options.executable, targetOpts).then((Process process) { |
| 529 print("Debug target process started, pid ${process.pid}."); | 583 print("Debug target process started, pid ${process.pid}."); |
| 530 process.stdin.close(); | 584 process.stdin.close(); |
| 531 var debugger = new Debugger(process, new DebugScript(script)); | 585 var debugger = new Debugger(process, new DebugScript(script)); |
| 532 }); | 586 }); |
| 533 return true; | 587 return true; |
| 534 } | 588 } |
| OLD | NEW |