OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, 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 #include "vm/debugger.h" | 5 #include "vm/debugger.h" |
6 | 6 |
7 #include "vm/code_index_table.h" | 7 #include "vm/code_index_table.h" |
8 #include "vm/code_generator.h" | 8 #include "vm/code_generator.h" |
9 #include "vm/code_patcher.h" | 9 #include "vm/code_patcher.h" |
10 #include "vm/compiler.h" | 10 #include "vm/compiler.h" |
11 #include "vm/dart_entry.h" | 11 #include "vm/dart_entry.h" |
12 #include "vm/flags.h" | 12 #include "vm/flags.h" |
13 #include "vm/globals.h" | 13 #include "vm/globals.h" |
14 #include "vm/longjump.h" | 14 #include "vm/longjump.h" |
15 #include "vm/object.h" | 15 #include "vm/object.h" |
16 #include "vm/object_store.h" | 16 #include "vm/object_store.h" |
17 #include "vm/os.h" | 17 #include "vm/os.h" |
18 #include "vm/stack_frame.h" | 18 #include "vm/stack_frame.h" |
19 #include "vm/stub_code.h" | 19 #include "vm/stub_code.h" |
20 #include "vm/visitor.h" | 20 #include "vm/visitor.h" |
21 | 21 |
22 | 22 |
23 namespace dart { | 23 namespace dart { |
24 | 24 |
25 static const bool verbose = false; | 25 static const bool verbose = false; |
26 | 26 |
27 | 27 |
28 Breakpoint::Breakpoint(const Function& func, intptr_t pc_desc_index) | 28 SourceBreakpoint::SourceBreakpoint(const Function& func, intptr_t token_index) |
29 : function_(func.raw()), | 29 : function_(func.raw()), |
30 pc_desc_index_(pc_desc_index), | 30 token_index_(token_index), |
31 pc_(0), | |
32 line_number_(-1), | 31 line_number_(-1), |
33 is_temporary_(false), | 32 is_enabled_(false), |
34 is_patched_(false), | |
35 next_(NULL) { | 33 next_(NULL) { |
36 ASSERT(!func.HasOptimizedCode()); | 34 ASSERT(!func.IsNull()); |
37 Code& code = Code::Handle(func.unoptimized_code()); | 35 ASSERT((func.token_index() <= token_index_) && |
38 ASSERT(!code.IsNull()); // Function must be compiled. | 36 (token_index_ < func.end_token_index())); |
39 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); | |
40 ASSERT(pc_desc_index < desc.Length()); | |
41 this->token_index_ = desc.TokenIndex(pc_desc_index); | |
42 ASSERT(this->token_index_ > 0); | |
43 this->pc_ = desc.PC(pc_desc_index); | |
44 ASSERT(this->pc_ != 0); | |
45 this->breakpoint_kind_ = desc.DescriptorKind(pc_desc_index); | |
46 } | 37 } |
47 | 38 |
48 | 39 |
49 RawScript* Breakpoint::SourceCode() { | 40 void SourceBreakpoint::Enable() { |
50 const Function& func = Function::Handle(this->function_); | 41 is_enabled_ = true; |
| 42 Isolate::Current()->debugger()->SyncBreakpoint(this); |
| 43 } |
| 44 |
| 45 |
| 46 void SourceBreakpoint::Disable() { |
| 47 is_enabled_ = false; |
| 48 Isolate::Current()->debugger()->SyncBreakpoint(this); |
| 49 } |
| 50 |
| 51 |
| 52 RawScript* SourceBreakpoint::SourceCode() { |
| 53 const Function& func = Function::Handle(function_); |
51 const Class& cls = Class::Handle(func.owner()); | 54 const Class& cls = Class::Handle(func.owner()); |
52 return cls.script(); | 55 return cls.script(); |
53 } | 56 } |
54 | 57 |
55 | 58 |
56 RawString* Breakpoint::SourceUrl() { | 59 RawString* SourceBreakpoint::SourceUrl() { |
57 const Script& script = Script::Handle(this->SourceCode()); | 60 const Script& script = Script::Handle(SourceCode()); |
58 return script.url(); | 61 return script.url(); |
59 } | 62 } |
60 | 63 |
61 | 64 |
62 intptr_t Breakpoint::LineNumber() { | 65 intptr_t SourceBreakpoint::LineNumber() { |
63 // Compute line number lazily since it causes scanning of the script. | 66 // Compute line number lazily since it causes scanning of the script. |
64 if (this->line_number_ < 0) { | 67 if (line_number_ < 0) { |
65 const Script& script = Script::Handle(this->SourceCode()); | 68 const Script& script = Script::Handle(SourceCode()); |
66 intptr_t ignore_column; | 69 intptr_t ignore_column; |
67 script.GetTokenLocation(this->token_index_, | 70 script.GetTokenLocation(token_index_, &line_number_, &ignore_column); |
68 &this->line_number_, &ignore_column); | |
69 } | 71 } |
70 return this->line_number_; | 72 return line_number_; |
71 } | 73 } |
72 | 74 |
73 | 75 |
74 void Breakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { | 76 void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
| 77 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); |
| 78 } |
| 79 |
| 80 |
| 81 |
| 82 void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
75 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); | 83 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); |
76 } | 84 } |
77 | 85 |
78 | 86 |
79 ActivationFrame::ActivationFrame(uword pc, uword fp, uword sp) | 87 ActivationFrame::ActivationFrame(uword pc, uword fp, uword sp) |
80 : pc_(pc), fp_(fp), sp_(sp), | 88 : pc_(pc), fp_(fp), sp_(sp), |
81 function_(Function::ZoneHandle()), | 89 function_(Function::ZoneHandle()), |
82 token_index_(-1), | 90 token_index_(-1), |
83 line_number_(-1), | 91 line_number_(-1), |
84 var_descriptors_(NULL), | 92 var_descriptors_(NULL), |
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
274 OS::SNPrint(NULL, 0, kFormat, func_name, url.ToCString(), line); | 282 OS::SNPrint(NULL, 0, kFormat, func_name, url.ToCString(), line); |
275 len++; // String terminator. | 283 len++; // String terminator. |
276 char* chars = reinterpret_cast<char*>( | 284 char* chars = reinterpret_cast<char*>( |
277 Isolate::Current()->current_zone()->Allocate(len)); | 285 Isolate::Current()->current_zone()->Allocate(len)); |
278 OS::SNPrint(chars, len, kFormat, func_name, url.ToCString(), line); | 286 OS::SNPrint(chars, len, kFormat, func_name, url.ToCString(), line); |
279 return chars; | 287 return chars; |
280 } | 288 } |
281 | 289 |
282 | 290 |
283 void StackTrace::AddActivation(ActivationFrame* frame) { | 291 void StackTrace::AddActivation(ActivationFrame* frame) { |
284 this->trace_.Add(frame); | 292 trace_.Add(frame); |
285 } | 293 } |
286 | 294 |
287 | 295 |
288 void Breakpoint::PatchCode() { | 296 CodeBreakpoint::CodeBreakpoint(const Function& func, intptr_t pc_desc_index) |
289 ASSERT(!is_patched_); | 297 : function_(func.raw()), |
| 298 pc_desc_index_(pc_desc_index), |
| 299 pc_(0), |
| 300 line_number_(-1), |
| 301 is_enabled_(false), |
| 302 src_bpt_(NULL), |
| 303 next_(NULL) { |
| 304 ASSERT(!func.HasOptimizedCode()); |
| 305 Code& code = Code::Handle(func.unoptimized_code()); |
| 306 ASSERT(!code.IsNull()); // Function must be compiled. |
| 307 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
| 308 ASSERT(pc_desc_index < desc.Length()); |
| 309 token_index_ = desc.TokenIndex(pc_desc_index); |
| 310 ASSERT(token_index_ > 0); |
| 311 pc_ = desc.PC(pc_desc_index); |
| 312 ASSERT(pc_ != 0); |
| 313 breakpoint_kind_ = desc.DescriptorKind(pc_desc_index); |
| 314 } |
| 315 |
| 316 |
| 317 CodeBreakpoint::~CodeBreakpoint() { |
| 318 // Make sure we don't leave patched code behind. |
| 319 ASSERT(!IsEnabled()); |
| 320 } |
| 321 |
| 322 |
| 323 RawScript* CodeBreakpoint::SourceCode() { |
| 324 const Function& func = Function::Handle(function_); |
| 325 const Class& cls = Class::Handle(func.owner()); |
| 326 return cls.script(); |
| 327 } |
| 328 |
| 329 |
| 330 RawString* CodeBreakpoint::SourceUrl() { |
| 331 const Script& script = Script::Handle(SourceCode()); |
| 332 return script.url(); |
| 333 } |
| 334 |
| 335 |
| 336 intptr_t CodeBreakpoint::LineNumber() { |
| 337 // Compute line number lazily since it causes scanning of the script. |
| 338 if (line_number_ < 0) { |
| 339 const Script& script = Script::Handle(SourceCode()); |
| 340 intptr_t ignore_column; |
| 341 script.GetTokenLocation(token_index_, &line_number_, &ignore_column); |
| 342 } |
| 343 return line_number_; |
| 344 } |
| 345 |
| 346 |
| 347 void CodeBreakpoint::PatchCode() { |
| 348 ASSERT(!is_enabled_); |
290 switch (breakpoint_kind_) { | 349 switch (breakpoint_kind_) { |
291 case PcDescriptors::kIcCall: { | 350 case PcDescriptors::kIcCall: { |
292 int num_args, num_named_args; | 351 int num_args, num_named_args; |
293 CodePatcher::GetInstanceCallAt(pc_, | 352 CodePatcher::GetInstanceCallAt(pc_, |
294 NULL, &num_args, &num_named_args, | 353 NULL, &num_args, &num_named_args, |
295 &saved_bytes_.target_address_); | 354 &saved_bytes_.target_address_); |
296 CodePatcher::PatchInstanceCallAt( | 355 CodePatcher::PatchInstanceCallAt( |
297 pc_, StubCode::BreakpointDynamicEntryPoint()); | 356 pc_, StubCode::BreakpointDynamicEntryPoint()); |
298 break; | 357 break; |
299 } | 358 } |
300 case PcDescriptors::kFuncCall: { | 359 case PcDescriptors::kFuncCall: { |
301 Function& func = Function::Handle(); | 360 Function& func = Function::Handle(); |
302 CodePatcher::GetStaticCallAt(pc_, &func, &saved_bytes_.target_address_); | 361 CodePatcher::GetStaticCallAt(pc_, &func, &saved_bytes_.target_address_); |
303 CodePatcher::PatchStaticCallAt(pc_, | 362 CodePatcher::PatchStaticCallAt(pc_, |
304 StubCode::BreakpointStaticEntryPoint()); | 363 StubCode::BreakpointStaticEntryPoint()); |
305 break; | 364 break; |
306 } | 365 } |
307 case PcDescriptors::kReturn: | 366 case PcDescriptors::kReturn: |
308 PatchFunctionReturn(); | 367 PatchFunctionReturn(); |
309 break; | 368 break; |
310 default: | 369 default: |
311 UNREACHABLE(); | 370 UNREACHABLE(); |
312 } | 371 } |
313 is_patched_ = true; | 372 is_enabled_ = true; |
314 } | 373 } |
315 | 374 |
316 | 375 |
317 void Breakpoint::RestoreCode() { | 376 void CodeBreakpoint::RestoreCode() { |
318 ASSERT(is_patched_); | 377 ASSERT(is_enabled_); |
319 switch (breakpoint_kind_) { | 378 switch (breakpoint_kind_) { |
320 case PcDescriptors::kIcCall: | 379 case PcDescriptors::kIcCall: |
321 CodePatcher::PatchInstanceCallAt(pc_, saved_bytes_.target_address_); | 380 CodePatcher::PatchInstanceCallAt(pc_, saved_bytes_.target_address_); |
322 break; | 381 break; |
323 case PcDescriptors::kFuncCall: | 382 case PcDescriptors::kFuncCall: |
324 CodePatcher::PatchStaticCallAt(pc_, saved_bytes_.target_address_); | 383 CodePatcher::PatchStaticCallAt(pc_, saved_bytes_.target_address_); |
325 break; | 384 break; |
326 case PcDescriptors::kReturn: | 385 case PcDescriptors::kReturn: |
327 RestoreFunctionReturn(); | 386 RestoreFunctionReturn(); |
328 break; | 387 break; |
329 default: | 388 default: |
330 UNREACHABLE(); | 389 UNREACHABLE(); |
331 } | 390 } |
332 is_patched_ = false; | 391 is_enabled_ = false; |
333 } | |
334 | |
335 void Breakpoint::SetActive(bool value) { | |
336 if (value && !is_patched_) { | |
337 PatchCode(); | |
338 return; | |
339 } | |
340 if (!value && is_patched_) { | |
341 RestoreCode(); | |
342 } | |
343 } | 392 } |
344 | 393 |
345 | 394 |
346 bool Breakpoint::IsActive() { | 395 void CodeBreakpoint::Enable() { |
347 return is_patched_; | 396 if (!is_enabled_) { |
| 397 PatchCode(); |
| 398 } |
| 399 ASSERT(is_enabled_); |
348 } | 400 } |
349 | 401 |
350 | 402 |
| 403 void CodeBreakpoint::Disable() { |
| 404 if (is_enabled_) { |
| 405 RestoreCode(); |
| 406 } |
| 407 ASSERT(!is_enabled_); |
| 408 } |
| 409 |
| 410 |
351 Debugger::Debugger() | 411 Debugger::Debugger() |
352 : isolate_(NULL), | 412 : isolate_(NULL), |
353 initialized_(false), | 413 initialized_(false), |
354 bp_handler_(NULL), | 414 bp_handler_(NULL), |
355 breakpoints_(NULL), | 415 src_breakpoints_(NULL), |
| 416 code_breakpoints_(NULL), |
356 resume_action_(kContinue) { | 417 resume_action_(kContinue) { |
357 } | 418 } |
358 | 419 |
359 | 420 |
360 Debugger::~Debugger() { | 421 Debugger::~Debugger() { |
361 ASSERT(breakpoints_ == NULL); | 422 ASSERT(src_breakpoints_ == NULL); |
| 423 ASSERT(code_breakpoints_ == NULL); |
362 } | 424 } |
363 | 425 |
364 | 426 |
365 void Debugger::Shutdown() { | 427 void Debugger::Shutdown() { |
366 while (breakpoints_ != NULL) { | 428 while (src_breakpoints_ != NULL) { |
367 Breakpoint* bpt = breakpoints_; | 429 SourceBreakpoint* bpt = src_breakpoints_; |
368 breakpoints_ = breakpoints_->next(); | 430 src_breakpoints_ = src_breakpoints_->next(); |
369 UnsetBreakpoint(bpt); | 431 delete bpt; |
| 432 } |
| 433 while (code_breakpoints_ != NULL) { |
| 434 CodeBreakpoint* bpt = code_breakpoints_; |
| 435 code_breakpoints_ = code_breakpoints_->next(); |
| 436 bpt->Disable(); |
370 delete bpt; | 437 delete bpt; |
371 } | 438 } |
372 } | 439 } |
373 | 440 |
374 | 441 |
375 bool Debugger::IsActive() { | 442 bool Debugger::IsActive() { |
376 // TODO(hausner): The code generator uses this function to prevent | 443 // TODO(hausner): The code generator uses this function to prevent |
377 // generation of optimized code when Dart code is being debugged. | 444 // generation of optimized code when Dart code is being debugged. |
378 // This is probably not conservative enough (we could set the first | 445 // This is probably not conservative enough (we could set the first |
379 // breakpoint after optimized code has already been produced). | 446 // breakpoint after optimized code has already been produced). |
380 // Long-term, we need to be able to de-optimize code. | 447 // Long-term, we need to be able to de-optimize code. |
381 return breakpoints_ != NULL; | 448 return (src_breakpoints_ != NULL) || (code_breakpoints_ != NULL); |
382 } | 449 } |
383 | 450 |
384 | 451 |
385 static RawFunction* ResolveLibraryFunction( | 452 static RawFunction* ResolveLibraryFunction( |
386 const Library& library, | 453 const Library& library, |
387 const String& fname) { | 454 const String& fname) { |
388 ASSERT(!library.IsNull()); | 455 ASSERT(!library.IsNull()); |
389 Function& function = Function::Handle(); | 456 Function& function = Function::Handle(); |
390 const Object& object = Object::Handle(library.LookupObject(fname)); | 457 const Object& object = Object::Handle(library.LookupObject(fname)); |
391 if (!object.IsNull() && object.IsFunction()) { | 458 if (!object.IsNull() && object.IsFunction()) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
423 // adding breakpoints to target. | 490 // adding breakpoints to target. |
424 if (!target_function.HasCode()) { | 491 if (!target_function.HasCode()) { |
425 return; | 492 return; |
426 } | 493 } |
427 } | 494 } |
428 ASSERT(!target_function.HasOptimizedCode()); | 495 ASSERT(!target_function.HasOptimizedCode()); |
429 Code& code = Code::Handle(target_function.unoptimized_code()); | 496 Code& code = Code::Handle(target_function.unoptimized_code()); |
430 ASSERT(!code.IsNull()); | 497 ASSERT(!code.IsNull()); |
431 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); | 498 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
432 for (int i = 0; i < desc.Length(); i++) { | 499 for (int i = 0; i < desc.Length(); i++) { |
433 Breakpoint* bpt = GetBreakpoint(desc.PC(i)); | 500 CodeBreakpoint* bpt = GetCodeBreakpoint(desc.PC(i)); |
434 if (bpt != NULL) { | 501 if (bpt != NULL) { |
435 // There is already a breakpoint for this address. Leave it alone. | 502 // There is already a breakpoint for this address. Leave it alone. |
436 continue; | 503 continue; |
437 } | 504 } |
438 PcDescriptors::Kind kind = desc.DescriptorKind(i); | 505 PcDescriptors::Kind kind = desc.DescriptorKind(i); |
439 if ((kind == PcDescriptors::kIcCall) || | 506 if ((kind == PcDescriptors::kIcCall) || |
440 (kind == PcDescriptors::kFuncCall) || | 507 (kind == PcDescriptors::kFuncCall) || |
441 (kind == PcDescriptors::kReturn)) { | 508 (kind == PcDescriptors::kReturn)) { |
442 bpt = new Breakpoint(target_function, i); | 509 bpt = new CodeBreakpoint(target_function, i); |
443 bpt->set_temporary(true); | 510 RegisterCodeBreakpoint(bpt); |
444 bpt->PatchCode(); | 511 bpt->Enable(); |
445 RegisterBreakpoint(bpt); | |
446 } | 512 } |
447 } | 513 } |
448 } | 514 } |
449 | 515 |
450 | 516 |
451 // TODO(hausner): Distinguish between newly created breakpoints and | 517 CodeBreakpoint* Debugger::MakeCodeBreakpoint(SourceBreakpoint* src_bpt) { |
452 // returning a breakpoint that already exists? | 518 Function& func = Function::Handle(src_bpt->function()); |
453 Breakpoint* Debugger::SetBreakpoint(const Function& target_function, | 519 ASSERT(func.HasCode()); |
454 intptr_t token_index, | 520 ASSERT(!func.HasOptimizedCode()); |
455 Error* error) { | 521 Code& code = Code::Handle(func.unoptimized_code()); |
456 if ((token_index < target_function.token_index()) || | |
457 (target_function.end_token_index() <= token_index)) { | |
458 // The given token position is not within the target function. | |
459 return NULL; | |
460 } | |
461 if (!target_function.HasCode()) { | |
462 *error = Compiler::CompileFunction(target_function); | |
463 if (!error->IsNull()) { | |
464 return NULL; | |
465 } | |
466 } | |
467 ASSERT(!target_function.HasOptimizedCode()); | |
468 Code& code = Code::Handle(target_function.unoptimized_code()); | |
469 ASSERT(!code.IsNull()); | 522 ASSERT(!code.IsNull()); |
470 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); | 523 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
| 524 intptr_t requested_token_index = src_bpt->token_index(); |
471 for (int i = 0; i < desc.Length(); i++) { | 525 for (int i = 0; i < desc.Length(); i++) { |
472 if (desc.TokenIndex(i) < token_index) { | 526 if (desc.TokenIndex(i) < requested_token_index) { |
473 continue; | 527 continue; |
474 } | 528 } |
475 Breakpoint* bpt = GetBreakpoint(desc.PC(i)); | 529 CodeBreakpoint* bpt = GetCodeBreakpoint(desc.PC(i)); |
| 530 // We should only ever have one code breakpoint at the same address. |
| 531 // If we find an existing breakpoint, it must be an internal one which |
| 532 // is used for stepping. |
476 if (bpt != NULL) { | 533 if (bpt != NULL) { |
477 // Found existing breakpoint. | 534 ASSERT(bpt->src_bpt() == NULL); |
| 535 bpt->set_src_bpt(src_bpt); |
478 return bpt; | 536 return bpt; |
479 } | 537 } |
| 538 |
480 PcDescriptors::Kind kind = desc.DescriptorKind(i); | 539 PcDescriptors::Kind kind = desc.DescriptorKind(i); |
481 if ((kind == PcDescriptors::kIcCall) || | 540 if ((kind == PcDescriptors::kIcCall) || |
482 (kind == PcDescriptors::kFuncCall) || | 541 (kind == PcDescriptors::kFuncCall) || |
483 (kind == PcDescriptors::kReturn)) { | 542 (kind == PcDescriptors::kReturn)) { |
484 bpt = new Breakpoint(target_function, i); | 543 bpt = new CodeBreakpoint(func, i); |
485 bpt->PatchCode(); | 544 bpt->set_src_bpt(src_bpt); |
486 RegisterBreakpoint(bpt); | |
487 if (verbose) { | 545 if (verbose) { |
488 OS::Print("Setting breakpoint at '%s' line %d (PC %p)\n", | 546 OS::Print("Setting breakpoint in function '%s' (%s:%d) (PC %p)\n", |
| 547 String::Handle(func.name()).ToCString(), |
489 String::Handle(bpt->SourceUrl()).ToCString(), | 548 String::Handle(bpt->SourceUrl()).ToCString(), |
490 bpt->LineNumber(), | 549 bpt->LineNumber(), |
491 bpt->pc()); | 550 bpt->pc()); |
492 } | 551 } |
| 552 RegisterCodeBreakpoint(bpt); |
493 return bpt; | 553 return bpt; |
494 } | 554 } |
495 } | 555 } |
496 return NULL; | 556 return NULL; |
497 } | 557 } |
498 | 558 |
499 | 559 |
500 void Debugger::UnsetBreakpoint(Breakpoint* bpt) { | 560 SourceBreakpoint* Debugger::SetBreakpoint(const Function& target_function, |
501 bpt->SetActive(false); | 561 intptr_t token_index) { |
| 562 if ((token_index < target_function.token_index()) || |
| 563 (target_function.end_token_index() <= token_index)) { |
| 564 // The given token position is not within the target function. |
| 565 return NULL; |
| 566 } |
| 567 SourceBreakpoint* bpt = GetSourceBreakpoint(target_function, token_index); |
| 568 if (bpt != NULL) { |
| 569 // A breakpoint for this location already exists, return it. |
| 570 return bpt; |
| 571 } |
| 572 bpt = new SourceBreakpoint(target_function, token_index); |
| 573 RegisterSourceBreakpoint(bpt); |
| 574 if (verbose && !target_function.HasCode()) { |
| 575 OS::Print("Registering breakpoint for uncompiled function '%s'" |
| 576 " (%s:%d)\n", |
| 577 String::Handle(target_function.name()).ToCString(), |
| 578 String::Handle(bpt->SourceUrl()).ToCString(), |
| 579 bpt->LineNumber()); |
| 580 } |
| 581 |
| 582 if (target_function.HasCode()) { |
| 583 CodeBreakpoint* cbpt = MakeCodeBreakpoint(bpt); |
| 584 if (cbpt == NULL) { |
| 585 if (verbose) { |
| 586 OS::Print("Failed to set breakpoint at '%s' line %d\n", |
| 587 String::Handle(bpt->SourceUrl()).ToCString(), |
| 588 bpt->LineNumber()); |
| 589 } |
| 590 } |
| 591 } |
| 592 bpt->Enable(); |
| 593 return bpt; |
502 } | 594 } |
503 | 595 |
504 | 596 |
505 Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function, | 597 void Debugger::SyncBreakpoint(SourceBreakpoint* bpt) { |
506 Error* error) { | 598 CodeBreakpoint* cbpt = code_breakpoints_; |
507 ASSERT(!target_function.IsNull()); | 599 while (cbpt != NULL) { |
508 return SetBreakpoint(target_function, target_function.token_index(), error); | 600 if (bpt == cbpt->src_bpt()) { |
| 601 if (bpt->IsEnabled()) { |
| 602 cbpt->Enable(); |
| 603 } else { |
| 604 cbpt->Disable(); |
| 605 } |
| 606 } |
| 607 cbpt = cbpt->next(); |
| 608 } |
509 } | 609 } |
510 | 610 |
511 | 611 |
512 Breakpoint* Debugger::SetBreakpointAtLine(const String& script_url, | 612 SourceBreakpoint* Debugger::SetBreakpointAtEntry( |
513 intptr_t line_number, | 613 const Function& target_function) { |
514 Error* error) { | 614 ASSERT(!target_function.IsNull()); |
| 615 return SetBreakpoint(target_function, target_function.token_index()); |
| 616 } |
| 617 |
| 618 |
| 619 SourceBreakpoint* Debugger::SetBreakpointAtLine(const String& script_url, |
| 620 intptr_t line_number) { |
515 Library& lib = Library::Handle(); | 621 Library& lib = Library::Handle(); |
516 Script& script = Script::Handle(); | 622 Script& script = Script::Handle(); |
517 lib = isolate_->object_store()->registered_libraries(); | 623 lib = isolate_->object_store()->registered_libraries(); |
518 while (!lib.IsNull()) { | 624 while (!lib.IsNull()) { |
519 script = lib.LookupScript(script_url); | 625 script = lib.LookupScript(script_url); |
520 if (!script.IsNull()) { | 626 if (!script.IsNull()) { |
521 break; | 627 break; |
522 } | 628 } |
523 lib = lib.next_registered(); | 629 lib = lib.next_registered(); |
524 } | 630 } |
525 if (script.IsNull()) { | 631 if (script.IsNull()) { |
| 632 if (verbose) { |
| 633 OS::Print("Failed to find script with url '%s'\n", |
| 634 script_url.ToCString()); |
| 635 } |
526 return NULL; | 636 return NULL; |
527 } | 637 } |
528 intptr_t token_index_at_line = script.TokenIndexAtLine(line_number); | 638 intptr_t token_index_at_line = script.TokenIndexAtLine(line_number); |
529 if (token_index_at_line < 0) { | 639 if (token_index_at_line < 0) { |
530 // Script does not contain the given line number. | 640 // Script does not contain the given line number. |
| 641 if (verbose) { |
| 642 OS::Print("Script '%s' does not contain line number %d\n", |
| 643 script_url.ToCString(), line_number); |
| 644 } |
531 return NULL; | 645 return NULL; |
532 } | 646 } |
533 const Function& func = | 647 const Function& func = |
534 Function::Handle(lib.LookupFunctionInScript(script, token_index_at_line)); | 648 Function::Handle(lib.LookupFunctionInScript(script, token_index_at_line)); |
535 if (func.IsNull()) { | 649 if (func.IsNull()) { |
| 650 if (verbose) { |
| 651 OS::Print("No executable code at line %d in '%s'\n", |
| 652 line_number, script_url.ToCString()); |
| 653 } |
536 return NULL; | 654 return NULL; |
537 } | 655 } |
538 return SetBreakpoint(func, token_index_at_line, error); | 656 return SetBreakpoint(func, token_index_at_line); |
539 } | 657 } |
540 | 658 |
541 | 659 |
542 static RawArray* MakeNameValueList(const GrowableArray<Object*>& pairs) { | 660 static RawArray* MakeNameValueList(const GrowableArray<Object*>& pairs) { |
543 int pairs_len = pairs.length(); | 661 int pairs_len = pairs.length(); |
544 ASSERT(pairs_len % 2 == 0); | 662 ASSERT(pairs_len % 2 == 0); |
545 const Array& list = Array::Handle(Array::New(pairs_len)); | 663 const Array& list = Array::Handle(Array::New(pairs_len)); |
546 for (int i = 0; i < pairs_len; i++) { | 664 for (int i = 0; i < pairs_len; i++) { |
547 list.SetAt(i, *pairs[i]); | 665 list.SetAt(i, *pairs[i]); |
548 } | 666 } |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
634 field_list.Add(&field_name); | 752 field_list.Add(&field_name); |
635 field_list.Add(&field_value); | 753 field_list.Add(&field_value); |
636 } | 754 } |
637 } | 755 } |
638 return MakeNameValueList(field_list); | 756 return MakeNameValueList(field_list); |
639 } | 757 } |
640 | 758 |
641 | 759 |
642 void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) { | 760 void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
643 ASSERT(visitor != NULL); | 761 ASSERT(visitor != NULL); |
644 Breakpoint* bpt = this->breakpoints_; | 762 SourceBreakpoint* bpt = src_breakpoints_; |
645 while (bpt != NULL) { | 763 while (bpt != NULL) { |
646 bpt->VisitObjectPointers(visitor); | 764 bpt->VisitObjectPointers(visitor); |
647 bpt = bpt->next(); | 765 bpt = bpt->next(); |
648 } | 766 } |
| 767 CodeBreakpoint* cbpt = code_breakpoints_; |
| 768 while (cbpt != NULL) { |
| 769 cbpt->VisitObjectPointers(visitor); |
| 770 cbpt = cbpt->next(); |
| 771 } |
649 } | 772 } |
650 | 773 |
651 | 774 |
652 static void DefaultBreakpointHandler(Breakpoint* bpt, StackTrace* stack) { | 775 static void DefaultBreakpointHandler(SourceBreakpoint* bpt, StackTrace* stack) { |
653 String& var_name = String::Handle(); | 776 String& var_name = String::Handle(); |
654 Instance& value = Instance::Handle(); | 777 Instance& value = Instance::Handle(); |
655 for (intptr_t i = 0; i < stack->Length(); i++) { | 778 for (intptr_t i = 0; i < stack->Length(); i++) { |
656 ActivationFrame* frame = stack->ActivationFrameAt(i); | 779 ActivationFrame* frame = stack->ActivationFrameAt(i); |
657 OS::Print(" %d. %s\n", | 780 OS::Print(" %d. %s\n", |
658 i + 1, frame->ToCString()); | 781 i + 1, frame->ToCString()); |
659 intptr_t num_locals = frame->NumLocalVariables(); | 782 intptr_t num_locals = frame->NumLocalVariables(); |
660 for (intptr_t i = 0; i < num_locals; i++) { | 783 for (intptr_t i = 0; i < num_locals; i++) { |
661 intptr_t token_pos, end_pos; | 784 intptr_t token_pos, end_pos; |
662 frame->VariableAt(i, &var_name, &token_pos, &end_pos, &value); | 785 frame->VariableAt(i, &var_name, &token_pos, &end_pos, &value); |
(...skipping 10 matching lines...) Expand all Loading... |
673 bp_handler_ = &DefaultBreakpointHandler; | 796 bp_handler_ = &DefaultBreakpointHandler; |
674 } | 797 } |
675 } | 798 } |
676 | 799 |
677 | 800 |
678 void Debugger::BreakpointCallback() { | 801 void Debugger::BreakpointCallback() { |
679 ASSERT(initialized_); | 802 ASSERT(initialized_); |
680 DartFrameIterator iterator; | 803 DartFrameIterator iterator; |
681 DartFrame* frame = iterator.NextFrame(); | 804 DartFrame* frame = iterator.NextFrame(); |
682 ASSERT(frame != NULL); | 805 ASSERT(frame != NULL); |
683 Breakpoint* bpt = GetBreakpoint(frame->pc()); | 806 CodeBreakpoint* bpt = GetCodeBreakpoint(frame->pc()); |
684 ASSERT(bpt != NULL); | 807 ASSERT(bpt != NULL); |
685 if (verbose) { | 808 if (verbose) { |
686 OS::Print(">>> %s breakpoint at %s:%d (Address %p)\n", | 809 OS::Print(">>> %s breakpoint at %s:%d (Address %p)\n", |
687 bpt->is_temporary() ? "hit temp" : "hit user", | 810 bpt->IsInternal() ? "hit internal" : "hit user", |
688 bpt ? String::Handle(bpt->SourceUrl()).ToCString() : "?", | 811 bpt ? String::Handle(bpt->SourceUrl()).ToCString() : "?", |
689 bpt ? bpt->LineNumber() : 0, | 812 bpt ? bpt->LineNumber() : 0, |
690 frame->pc()); | 813 frame->pc()); |
691 } | 814 } |
692 StackTrace* stack_trace = new StackTrace(8); | 815 StackTrace* stack_trace = new StackTrace(8); |
693 while (frame != NULL) { | 816 while (frame != NULL) { |
694 ASSERT(frame->IsValid()); | 817 ASSERT(frame->IsValid()); |
695 ASSERT(frame->IsDartFrame()); | 818 ASSERT(frame->IsDartFrame()); |
696 ActivationFrame* activation = | 819 ActivationFrame* activation = |
697 new ActivationFrame(frame->pc(), frame->fp(), frame->sp()); | 820 new ActivationFrame(frame->pc(), frame->fp(), frame->sp()); |
698 stack_trace->AddActivation(activation); | 821 stack_trace->AddActivation(activation); |
699 frame = iterator.NextFrame(); | 822 frame = iterator.NextFrame(); |
700 } | 823 } |
701 | 824 |
702 resume_action_ = kContinue; | 825 resume_action_ = kContinue; |
703 if (bp_handler_ != NULL) { | 826 if (bp_handler_ != NULL) { |
704 (*bp_handler_)(bpt, stack_trace); | 827 SourceBreakpoint* src_bpt = bpt->src_bpt(); |
| 828 (*bp_handler_)(src_bpt, stack_trace); |
705 } | 829 } |
706 | 830 |
707 if (resume_action_ == kContinue) { | 831 if (resume_action_ == kContinue) { |
708 RemoveTemporaryBreakpoints(); | 832 RemoveInternalBreakpoints(); |
709 } else if (resume_action_ == kStepOver) { | 833 } else if (resume_action_ == kStepOver) { |
710 Function& func = Function::Handle(bpt->function()); | 834 Function& func = Function::Handle(bpt->function()); |
711 if (bpt->breakpoint_kind_ == PcDescriptors::kReturn) { | 835 if (bpt->breakpoint_kind_ == PcDescriptors::kReturn) { |
712 // If we are at the function return, do a StepOut action. | 836 // If we are at the function return, do a StepOut action. |
713 if (stack_trace->Length() > 1) { | 837 if (stack_trace->Length() > 1) { |
714 ActivationFrame* caller = stack_trace->ActivationFrameAt(1); | 838 ActivationFrame* caller = stack_trace->ActivationFrameAt(1); |
715 func = caller->DartFunction().raw(); | 839 func = caller->DartFunction().raw(); |
716 RemoveTemporaryBreakpoints(); | 840 RemoveInternalBreakpoints(); |
717 } | 841 } |
718 } | 842 } |
719 InstrumentForStepping(func); | 843 InstrumentForStepping(func); |
720 } else if (resume_action_ == kStepInto) { | 844 } else if (resume_action_ == kStepInto) { |
721 RemoveTemporaryBreakpoints(); | 845 RemoveInternalBreakpoints(); |
722 if (bpt->breakpoint_kind_ == PcDescriptors::kIcCall) { | 846 if (bpt->breakpoint_kind_ == PcDescriptors::kIcCall) { |
723 int num_args, num_named_args; | 847 int num_args, num_named_args; |
724 uword target; | 848 uword target; |
725 CodePatcher::GetInstanceCallAt(bpt->pc_, NULL, | 849 CodePatcher::GetInstanceCallAt(bpt->pc_, NULL, |
726 &num_args, &num_named_args, &target); | 850 &num_args, &num_named_args, &target); |
727 ActivationFrame* top_frame = stack_trace->ActivationFrameAt(0); | 851 ActivationFrame* top_frame = stack_trace->ActivationFrameAt(0); |
728 Instance& receiver = Instance::Handle( | 852 Instance& receiver = Instance::Handle( |
729 top_frame->GetInstanceCallReceiver(num_args)); | 853 top_frame->GetInstanceCallReceiver(num_args)); |
730 Code& code = Code::Handle( | 854 Code& code = Code::Handle( |
731 ResolveCompileInstanceCallTarget(isolate_, receiver)); | 855 ResolveCompileInstanceCallTarget(isolate_, receiver)); |
732 if (!code.IsNull()) { | 856 if (!code.IsNull()) { |
733 Function& callee = Function::Handle(code.function()); | 857 Function& callee = Function::Handle(code.function()); |
734 InstrumentForStepping(callee); | 858 InstrumentForStepping(callee); |
735 } | 859 } |
736 } else if (bpt->breakpoint_kind_ == PcDescriptors::kFuncCall) { | 860 } else if (bpt->breakpoint_kind_ == PcDescriptors::kFuncCall) { |
737 Function& callee = Function::Handle(); | 861 Function& callee = Function::Handle(); |
738 uword target; | 862 uword target; |
739 CodePatcher::GetStaticCallAt(bpt->pc_, &callee, &target); | 863 CodePatcher::GetStaticCallAt(bpt->pc_, &callee, &target); |
740 InstrumentForStepping(callee); | 864 InstrumentForStepping(callee); |
741 } else { | 865 } else { |
742 ASSERT(bpt->breakpoint_kind_ == PcDescriptors::kReturn); | 866 ASSERT(bpt->breakpoint_kind_ == PcDescriptors::kReturn); |
743 // Treat like stepping out to caller. | 867 // Treat like stepping out to caller. |
744 if (stack_trace->Length() > 1) { | 868 if (stack_trace->Length() > 1) { |
745 ActivationFrame* caller = stack_trace->ActivationFrameAt(1); | 869 ActivationFrame* caller = stack_trace->ActivationFrameAt(1); |
746 InstrumentForStepping(caller->DartFunction()); | 870 InstrumentForStepping(caller->DartFunction()); |
747 } | 871 } |
748 } | 872 } |
749 } else { | 873 } else { |
750 ASSERT(resume_action_ == kStepOut); | 874 ASSERT(resume_action_ == kStepOut); |
751 // Set temporary breakpoints in the caller. | 875 // Set stepping breakpoints in the caller. |
752 RemoveTemporaryBreakpoints(); | 876 RemoveInternalBreakpoints(); |
753 if (stack_trace->Length() > 1) { | 877 if (stack_trace->Length() > 1) { |
754 ActivationFrame* caller = stack_trace->ActivationFrameAt(1); | 878 ActivationFrame* caller = stack_trace->ActivationFrameAt(1); |
755 InstrumentForStepping(caller->DartFunction()); | 879 InstrumentForStepping(caller->DartFunction()); |
756 } | 880 } |
757 } | 881 } |
758 } | 882 } |
759 | 883 |
760 | 884 |
761 void Debugger::Initialize(Isolate* isolate) { | 885 void Debugger::Initialize(Isolate* isolate) { |
762 if (initialized_) { | 886 if (initialized_) { |
763 return; | 887 return; |
764 } | 888 } |
765 isolate_ = isolate; | 889 isolate_ = isolate; |
766 initialized_ = true; | 890 initialized_ = true; |
767 SetBreakpointHandler(DefaultBreakpointHandler); | 891 SetBreakpointHandler(DefaultBreakpointHandler); |
768 } | 892 } |
769 | 893 |
770 | 894 |
771 Breakpoint* Debugger::GetBreakpoint(uword breakpoint_address) { | 895 // TODO(hausner): handle closure functions. |
772 Breakpoint* bpt = this->breakpoints_; | 896 void Debugger::NotifyCompilation(const Function& func) { |
| 897 SourceBreakpoint* bpt = src_breakpoints_; |
| 898 while (bpt != NULL) { |
| 899 if (func.raw() == bpt->function()) { |
| 900 if (verbose) { |
| 901 OS::Print("Enable latent breakpoint for function '%s'\n", |
| 902 String::Handle(func.name()).ToCString()); |
| 903 } |
| 904 CodeBreakpoint* cbpt = MakeCodeBreakpoint(bpt); |
| 905 bpt->Enable(); |
| 906 } |
| 907 bpt = bpt->next(); |
| 908 } |
| 909 } |
| 910 |
| 911 |
| 912 CodeBreakpoint* Debugger::GetCodeBreakpoint(uword breakpoint_address) { |
| 913 CodeBreakpoint* bpt = code_breakpoints_; |
773 while (bpt != NULL) { | 914 while (bpt != NULL) { |
774 if (bpt->pc() == breakpoint_address) { | 915 if (bpt->pc() == breakpoint_address) { |
775 return bpt; | 916 return bpt; |
776 } | 917 } |
777 bpt = bpt->next(); | 918 bpt = bpt->next(); |
778 } | 919 } |
779 return NULL; | 920 return NULL; |
780 } | 921 } |
781 | 922 |
782 | 923 |
783 void Debugger::RemoveBreakpoint(Breakpoint* bpt) { | 924 // Remove and delete the source breakpoint bpt and its associated |
784 ASSERT(breakpoints_ != NULL); | 925 // code breakpoints. |
785 Breakpoint* prev_bpt = NULL; | 926 void Debugger::RemoveBreakpoint(SourceBreakpoint* bpt) { |
786 Breakpoint* curr_bpt = breakpoints_; | 927 ASSERT(src_breakpoints_ != NULL); |
| 928 SourceBreakpoint* prev_bpt = NULL; |
| 929 SourceBreakpoint* curr_bpt = src_breakpoints_; |
787 while (curr_bpt != NULL) { | 930 while (curr_bpt != NULL) { |
788 if (bpt == curr_bpt) { | 931 if (bpt == curr_bpt) { |
789 if (prev_bpt == NULL) { | 932 if (prev_bpt == NULL) { |
790 breakpoints_ = breakpoints_->next(); | 933 src_breakpoints_ = src_breakpoints_->next(); |
791 } else { | 934 } else { |
792 prev_bpt->set_next(curr_bpt->next()); | 935 prev_bpt->set_next(curr_bpt->next()); |
793 } | 936 } |
794 UnsetBreakpoint(bpt); | 937 // Remove the code breakpoints associated with the source breakpoint. |
| 938 RemoveCodeBreakpoints(bpt); |
795 delete bpt; | 939 delete bpt; |
796 return; | 940 return; |
797 } | 941 } |
798 prev_bpt = curr_bpt; | 942 prev_bpt = curr_bpt; |
799 curr_bpt = curr_bpt->next(); | 943 curr_bpt = curr_bpt->next(); |
800 } | 944 } |
801 // bpt is not a registered breakpoint, nothing to do. | 945 // bpt is not a registered breakpoint, nothing to do. |
802 } | 946 } |
803 | 947 |
804 | 948 |
805 void Debugger::RemoveTemporaryBreakpoints() { | 949 // Remove and delete the code breakpoints that are associated with given |
806 Breakpoint* prev_bpt = NULL; | 950 // source breakpoint bpt. If bpt is null, remove the internal breakpoints. |
807 Breakpoint* curr_bpt = breakpoints_; | 951 void Debugger::RemoveCodeBreakpoints(SourceBreakpoint* src_bpt) { |
| 952 CodeBreakpoint* prev_bpt = NULL; |
| 953 CodeBreakpoint* curr_bpt = code_breakpoints_; |
808 while (curr_bpt != NULL) { | 954 while (curr_bpt != NULL) { |
809 if (curr_bpt->is_temporary()) { | 955 if (curr_bpt->src_bpt() == src_bpt) { |
810 if (prev_bpt == NULL) { | 956 if (prev_bpt == NULL) { |
811 breakpoints_ = breakpoints_->next(); | 957 code_breakpoints_ = code_breakpoints_->next(); |
812 } else { | 958 } else { |
813 prev_bpt->set_next(curr_bpt->next()); | 959 prev_bpt->set_next(curr_bpt->next()); |
814 } | 960 } |
815 Breakpoint* temp_bpt = curr_bpt; | 961 CodeBreakpoint* temp_bpt = curr_bpt; |
816 curr_bpt = curr_bpt->next(); | 962 curr_bpt = curr_bpt->next(); |
817 UnsetBreakpoint(temp_bpt); | 963 temp_bpt->Disable(); |
818 delete temp_bpt; | 964 delete temp_bpt; |
819 } else { | 965 } else { |
820 prev_bpt = curr_bpt; | 966 prev_bpt = curr_bpt; |
821 curr_bpt = curr_bpt->next(); | 967 curr_bpt = curr_bpt->next(); |
822 } | 968 } |
823 } | 969 } |
824 } | 970 } |
825 | 971 |
826 | 972 |
827 Breakpoint* Debugger::GetBreakpointByFunction(const Function& func, | 973 // Remove and delete all breakpoints that are not associated with a |
828 intptr_t token_index) { | 974 // user-defined source breakpoint. |
829 Breakpoint* bpt = this->breakpoints_; | 975 void Debugger::RemoveInternalBreakpoints() { |
| 976 RemoveCodeBreakpoints(NULL); |
| 977 } |
| 978 |
| 979 |
| 980 SourceBreakpoint* Debugger::GetSourceBreakpoint(const Function& func, |
| 981 intptr_t token_index) { |
| 982 SourceBreakpoint* bpt = src_breakpoints_; |
830 while (bpt != NULL) { | 983 while (bpt != NULL) { |
831 if ((bpt->function() == func.raw()) && | 984 if ((bpt->function() == func.raw()) && |
832 (bpt->token_index() == token_index)) { | 985 (bpt->token_index() == token_index)) { |
833 return bpt; | 986 return bpt; |
834 } | 987 } |
835 bpt = bpt->next(); | 988 bpt = bpt->next(); |
836 } | 989 } |
837 return NULL; | 990 return NULL; |
838 } | 991 } |
839 | 992 |
840 | 993 |
841 void Debugger::RegisterBreakpoint(Breakpoint* bpt) { | 994 void Debugger::RegisterSourceBreakpoint(SourceBreakpoint* bpt) { |
842 ASSERT(bpt->next() == NULL); | 995 ASSERT(bpt->next() == NULL); |
843 bpt->set_next(this->breakpoints_); | 996 bpt->set_next(src_breakpoints_); |
844 this->breakpoints_ = bpt; | 997 src_breakpoints_ = bpt; |
845 } | 998 } |
846 | 999 |
847 | 1000 |
| 1001 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { |
| 1002 ASSERT(bpt->next() == NULL); |
| 1003 bpt->set_next(code_breakpoints_); |
| 1004 code_breakpoints_ = bpt; |
| 1005 } |
| 1006 |
848 } // namespace dart | 1007 } // namespace dart |
OLD | NEW |