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:convert"; | 10 import "dart:convert"; |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 void matchResponse(Debugger debugger) { | 173 void matchResponse(Debugger debugger) { |
174 Map response = debugger.currentMessage; | 174 Map response = debugger.currentMessage; |
175 var id = template["id"]; | 175 var id = template["id"]; |
176 assert(id != null && id >= 0); | 176 assert(id != null && id >= 0); |
177 if (response["id"] != id) { | 177 if (response["id"] != id) { |
178 debugger.error("Error: expected messaged id $id but got ${response["id"]}.
"); | 178 debugger.error("Error: expected messaged id $id but got ${response["id"]}.
"); |
179 } | 179 } |
180 } | 180 } |
181 } | 181 } |
182 | 182 |
| 183 class GetLineTableCmd extends Command { |
| 184 GetLineTableCmd() { |
| 185 template = {"id": 0, |
| 186 "command": "getLineNumberTable", |
| 187 "params": {"isolateId": 0, "libraryId": 0, "url": ""}}; |
| 188 } |
| 189 |
| 190 void send(Debugger debugger) { |
| 191 assert(debugger.scriptUrl != null); |
| 192 template["params"]["url"] = debugger.scriptUrl; |
| 193 template["params"]["libraryId"] = debugger.libraryId; |
| 194 debugger.sendMessage(template); |
| 195 } |
| 196 |
| 197 void matchResponse(Debugger debugger) { |
| 198 super.matchResponse(debugger); |
| 199 List<List<int>> table = getJsonValue(debugger.currentMessage, "result:lines"
); |
| 200 debugger.tokenToLine = {}; |
| 201 for (var line in table) { |
| 202 // Each entry begins with a line number... |
| 203 var lineNumber = line[0]; |
| 204 for (var pos = 1; pos < line.length; pos += 2) { |
| 205 // ...and is followed by (token offset, col number) pairs. |
| 206 var tokenOffset = line[pos]; |
| 207 debugger.tokenToLine[tokenOffset] = lineNumber; |
| 208 } |
| 209 } |
| 210 } |
| 211 } |
| 212 |
| 213 |
| 214 class LineMatcher extends Command { |
| 215 int expectedLineNumber; |
| 216 |
| 217 LineMatcher(this.expectedLineNumber) { |
| 218 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}}
; |
| 219 } |
| 220 |
| 221 void matchResponse(Debugger debugger) { |
| 222 assert(debugger.tokenToLine != null); |
| 223 super.matchResponse(debugger); |
| 224 var msg = debugger.currentMessage; |
| 225 List frames = getJsonValue(msg, "result:callFrames"); |
| 226 assert(frames != null); |
| 227 var tokenOffset = frames[0]["location"]["tokenOffset"]; |
| 228 assert(tokenOffset != null); |
| 229 var lineNumber = debugger.tokenToLine[tokenOffset]; |
| 230 assert(lineNumber != null); |
| 231 if (expectedLineNumber != lineNumber) { |
| 232 debugger.error("Error: expected pause at line $expectedLineNumber " |
| 233 "but reported line is $lineNumber."); |
| 234 return; |
| 235 } |
| 236 print("Matched line number $lineNumber"); |
| 237 } |
| 238 } |
| 239 |
| 240 MatchLine(lineNumber) { |
| 241 return new LineMatcher(lineNumber); |
| 242 } |
| 243 |
183 | 244 |
184 class FrameMatcher extends Command { | 245 class FrameMatcher extends Command { |
185 int frameIndex; | 246 int frameIndex; |
186 List<String> functionNames; | 247 List<String> functionNames; |
187 bool exactMatch; | 248 bool exactMatch; |
188 | 249 |
189 FrameMatcher(this.frameIndex, this.functionNames, this.exactMatch) { | 250 FrameMatcher(this.frameIndex, this.functionNames, this.exactMatch) { |
190 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}}
; | 251 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}}
; |
191 } | 252 } |
192 | 253 |
193 void matchResponse(Debugger debugger) { | 254 void matchResponse(Debugger debugger) { |
194 super.matchResponse(debugger); | 255 super.matchResponse(debugger); |
195 var msg = debugger.currentMessage; | 256 var msg = debugger.currentMessage; |
196 List frames = getJsonValue(msg, "result:callFrames"); | 257 List frames = getJsonValue(msg, "result:callFrames"); |
197 assert(frames != null); | 258 assert(frames != null); |
198 if (debugger.scriptUrl == null) { | 259 if (debugger.scriptUrl == null) { |
199 var name = frames[0]["functionName"]; | 260 var name = frames[0]["functionName"]; |
200 if (name == "main") { | 261 if (name == "main") { |
201 // Extract script url of debugged script. | 262 // Extract script url of debugged script. |
202 debugger.scriptUrl = frames[0]["location"]["url"]; | 263 debugger.scriptUrl = frames[0]["location"]["url"]; |
203 assert(debugger.scriptUrl != null); | 264 assert(debugger.scriptUrl != null); |
| 265 debugger.libraryId = frames[0]["location"]["libraryId"]; |
| 266 assert(debugger.libraryId != null); |
204 } | 267 } |
205 } | 268 } |
206 if (frames.length < functionNames.length) { | 269 if (frames.length < functionNames.length) { |
207 debugger.error("Error: stack trace not long enough " | 270 debugger.error("Error: stack trace not long enough " |
208 "to match ${functionNames.length} frames"); | 271 "to match ${functionNames.length} frames"); |
209 return; | 272 return; |
210 } | 273 } |
211 for (int i = 0; i < functionNames.length; i++) { | 274 for (int i = 0; i < functionNames.length; i++) { |
212 var idx = i + frameIndex; | 275 var idx = i + frameIndex; |
213 var name = frames[idx]["functionName"]; | 276 var name = frames[idx]["functionName"]; |
214 assert(name != null); | 277 assert(name != null); |
215 bool isMatch = exactMatch ? name == functionNames[i] | 278 bool isMatch = exactMatch ? name == functionNames[i] |
216 : name.contains(functionNames[i]); | 279 : name.contains(functionNames[i]); |
217 if (!isMatch) { | 280 if (!isMatch) { |
218 debugger.error("Error: call frame $idx: " | 281 debugger.error("Error: call frame $idx: " |
219 "expected function name '${functionNames[i]}' but found '$name'"); | 282 "expected function name '${functionNames[i]}' but found '$name'"); |
220 return; | 283 return; |
221 } | 284 } |
222 } | 285 } |
| 286 print("Matched frames: $functionNames"); |
223 } | 287 } |
224 } | 288 } |
225 | 289 |
226 | 290 |
227 MatchFrame(int frameIndex, String functionName, {exactMatch: false}) { | 291 MatchFrame(int frameIndex, String functionName, {exactMatch: false}) { |
228 return new FrameMatcher(frameIndex, [functionName], exactMatch); | 292 return new FrameMatcher(frameIndex, [functionName], exactMatch); |
229 } | 293 } |
230 | 294 |
231 MatchFrames(List<String> functionNames, {exactMatch: false}) { | 295 MatchFrames(List<String> functionNames, {exactMatch: false}) { |
232 return new FrameMatcher(0, functionNames, exactMatch); | 296 return new FrameMatcher(0, functionNames, exactMatch); |
(...skipping 24 matching lines...) Expand all Loading... |
257 return; | 321 return; |
258 } | 322 } |
259 String expected = locals[key]; | 323 String expected = locals[key]; |
260 String actual = reportedLocals[key]['text']; | 324 String actual = reportedLocals[key]['text']; |
261 if (expected != actual) { | 325 if (expected != actual) { |
262 debugger.error("Error in $functionName(): got '$actual' for local " | 326 debugger.error("Error in $functionName(): got '$actual' for local " |
263 "variable $key, but expected '$expected'"); | 327 "variable $key, but expected '$expected'"); |
264 return; | 328 return; |
265 } | 329 } |
266 } | 330 } |
| 331 print("Matched locals ${locals.keys}"); |
267 } | 332 } |
268 } | 333 } |
269 | 334 |
270 | 335 |
271 MatchLocals(Map localValues) { | 336 MatchLocals(Map localValues) { |
272 return new LocalsMatcher(localValues); | 337 return new LocalsMatcher(localValues); |
273 } | 338 } |
274 | 339 |
275 | 340 |
276 class EventMatcher { | 341 class EventMatcher { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 RunCommand.stepInto() { | 376 RunCommand.stepInto() { |
312 template = {"id": 0, "command": "stepInto", "params": {"isolateId": 0}}; | 377 template = {"id": 0, "command": "stepInto", "params": {"isolateId": 0}}; |
313 } | 378 } |
314 RunCommand.stepOut() { | 379 RunCommand.stepOut() { |
315 template = {"id": 0, "command": "stepOut", "params": {"isolateId": 0}}; | 380 template = {"id": 0, "command": "stepOut", "params": {"isolateId": 0}}; |
316 } | 381 } |
317 void send(Debugger debugger) { | 382 void send(Debugger debugger) { |
318 debugger.sendMessage(template); | 383 debugger.sendMessage(template); |
319 debugger.isPaused = false; | 384 debugger.isPaused = false; |
320 } | 385 } |
| 386 void matchResponse(Debugger debugger) { |
| 387 super.matchResponse(debugger); |
| 388 print("Command: ${template['command']}"); |
| 389 } |
321 } | 390 } |
322 | 391 |
323 | 392 |
324 Resume() => new RunCommand.resume(); | 393 Resume() => new RunCommand.resume(); |
325 Step() => new RunCommand.step(); | 394 Step() => new RunCommand.step(); |
326 StepInto() => new RunCommand.stepInto(); | 395 StepInto() => new RunCommand.stepInto(); |
327 StepOut() => new RunCommand.stepOut(); | 396 StepOut() => new RunCommand.stepOut(); |
328 | 397 |
329 class SetBreakpointCommand extends Command { | 398 class SetBreakpointCommand extends Command { |
330 int line; | 399 int line; |
331 SetBreakpointCommand(int this.line) { | 400 SetBreakpointCommand(int this.line) { |
332 template = {"id": 0, | 401 template = {"id": 0, |
333 "command": "setBreakpoint", | 402 "command": "setBreakpoint", |
334 "params": { "isolateId": 0, | 403 "params": { "isolateId": 0, |
335 "url": null, | 404 "url": null, |
336 "line": null }}; | 405 "line": null }}; |
337 } | 406 } |
338 | 407 |
339 void send(Debugger debugger) { | 408 void send(Debugger debugger) { |
340 assert(debugger.scriptUrl != null); | 409 assert(debugger.scriptUrl != null); |
341 template["params"]["url"] = debugger.scriptUrl; | 410 template["params"]["url"] = debugger.scriptUrl; |
342 template["params"]["line"] = line; | 411 template["params"]["line"] = line; |
343 debugger.sendMessage(template); | 412 debugger.sendMessage(template); |
344 } | 413 } |
| 414 |
| 415 void matchResponse(Debugger debugger) { |
| 416 super.matchResponse(debugger); |
| 417 print("Set breakpoint at line $line"); |
| 418 } |
345 } | 419 } |
346 | 420 |
347 SetBreakpoint(int line) => new SetBreakpointCommand(line); | 421 SetBreakpoint(int line) => new SetBreakpointCommand(line); |
348 | 422 |
349 class Event { | 423 class Event { |
350 String name; | 424 String name; |
351 Map params; | 425 Map params; |
352 | 426 |
353 Event(Map json) { | 427 Event(Map json) { |
354 name = json['event']; | 428 name = json['event']; |
355 params = json['params']; | 429 params = json['params']; |
356 } | 430 } |
357 } | 431 } |
358 | 432 |
359 | 433 |
360 // A debug script is a list of Command objects. | 434 // A debug script is a list of Command objects. |
361 class DebugScript { | 435 class DebugScript { |
362 List entries; | 436 List entries; |
363 DebugScript(List scriptEntries) { | 437 DebugScript(List scriptEntries) { |
364 entries = new List.from(scriptEntries.reversed); | 438 entries = new List.from(scriptEntries.reversed); |
| 439 entries.add(new GetLineTableCmd()); |
365 entries.add(MatchFrame(0, "main")); | 440 entries.add(MatchFrame(0, "main")); |
366 } | 441 } |
367 bool get isEmpty => entries.isEmpty; | 442 bool get isEmpty => entries.isEmpty; |
368 bool get isNextEventMatcher => !isEmpty && currentEntry is EventMatcher; | 443 bool get isNextEventMatcher => !isEmpty && currentEntry is EventMatcher; |
369 get currentEntry => entries.last; | 444 get currentEntry => entries.last; |
370 advance() => entries.removeLast(); | 445 advance() => entries.removeLast(); |
371 add(entry) => entries.add(entry); | 446 add(entry) => entries.add(entry); |
372 } | 447 } |
373 | 448 |
374 | 449 |
375 class Debugger { | 450 class Debugger { |
376 // Debug target process properties. | 451 // Debug target process properties. |
377 Process targetProcess; | 452 Process targetProcess; |
378 Socket socket; | 453 Socket socket; |
379 JsonBuffer responses = new JsonBuffer(); | 454 JsonBuffer responses = new JsonBuffer(); |
380 | 455 |
381 DebugScript script; | 456 DebugScript script; |
382 int seqNr = 0; // Sequence number of next debugger command message. | 457 int seqNr = 0; // Sequence number of next debugger command message. |
383 Command lastCommand = null; // Most recent command sent to target. | 458 Command lastCommand = null; // Most recent command sent to target. |
384 List<String> errors = new List(); | 459 List<String> errors = new List(); |
385 List<Event> events = new List(); | 460 List<Event> events = new List(); |
386 bool cleanupDone = false; | 461 bool cleanupDone = false; |
387 | 462 |
388 // Data collected from debug target. | 463 // Data collected from debug target. |
389 Map currentMessage = null; // Currently handled message sent by target. | 464 Map currentMessage = null; // Currently handled message sent by target. |
390 String scriptUrl = null; | 465 String scriptUrl = null; |
| 466 int libraryId = null; |
| 467 Map<int,int> tokenToLine = null; |
391 bool shutdownEventSeen = false; | 468 bool shutdownEventSeen = false; |
392 int isolateId = 0; | 469 int isolateId = 0; |
393 bool isPaused = false; | 470 bool isPaused = false; |
394 | 471 |
395 Debugger(this.targetProcess, this.script) { | 472 Debugger(this.targetProcess, this.script) { |
396 var stdoutStringStream = targetProcess.stdout | 473 var stdoutStringStream = targetProcess.stdout |
397 .transform(UTF8.decoder) | 474 .transform(UTF8.decoder) |
398 .transform(new LineSplitter()); | 475 .transform(new LineSplitter()); |
399 stdoutStringStream.listen((line) { | 476 stdoutStringStream.listen((line) { |
400 print("TARG: $line"); | 477 print("TARG: $line"); |
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
617 targetOpts.add("--debuggee"); | 694 targetOpts.add("--debuggee"); |
618 print('args: ${targetOpts.join(" ")}'); | 695 print('args: ${targetOpts.join(" ")}'); |
619 | 696 |
620 Process.start(Platform.executable, targetOpts).then((Process process) { | 697 Process.start(Platform.executable, targetOpts).then((Process process) { |
621 print("Debug target process started, pid ${process.pid}."); | 698 print("Debug target process started, pid ${process.pid}."); |
622 process.stdin.close(); | 699 process.stdin.close(); |
623 var debugger = new Debugger(process, new DebugScript(script)); | 700 var debugger = new Debugger(process, new DebugScript(script)); |
624 }); | 701 }); |
625 return true; | 702 return true; |
626 } | 703 } |
OLD | NEW |