OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 "include/dart_api.h" | 7 #include "include/dart_api.h" |
8 | 8 |
9 #include "vm/code_generator.h" | 9 #include "vm/code_generator.h" |
10 #include "vm/code_patcher.h" | 10 #include "vm/code_patcher.h" |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 } | 43 } |
44 | 44 |
45 private: | 45 private: |
46 GrowableObjectArray* objs_; | 46 GrowableObjectArray* objs_; |
47 | 47 |
48 DISALLOW_COPY_AND_ASSIGN(RemoteObjectCache); | 48 DISALLOW_COPY_AND_ASSIGN(RemoteObjectCache); |
49 }; | 49 }; |
50 | 50 |
51 | 51 |
52 SourceBreakpoint::SourceBreakpoint(intptr_t id, | 52 SourceBreakpoint::SourceBreakpoint(intptr_t id, |
53 const Function& func, | 53 const Script& script, |
54 intptr_t token_pos) | 54 intptr_t token_pos) |
55 : id_(id), | 55 : id_(id), |
56 function_(func.raw()), | 56 script_(script.raw()), |
57 token_pos_(token_pos), | 57 token_pos_(token_pos), |
58 line_number_(-1), | 58 is_resolved_(false), |
59 is_enabled_(false), | 59 is_enabled_(false), |
60 next_(NULL) { | 60 next_(NULL), |
61 ASSERT(!func.IsNull()); | 61 function_(Function::null()), |
62 ASSERT((func.token_pos() <= token_pos_) && | 62 line_number_(-1) { |
63 (token_pos_ <= func.end_token_pos())); | 63 ASSERT(id_ > 0); |
| 64 ASSERT(!script.IsNull()); |
| 65 ASSERT(token_pos_ >= 0); |
64 } | 66 } |
65 | 67 |
66 | 68 |
67 void SourceBreakpoint::Enable() { | 69 void SourceBreakpoint::Enable() { |
68 is_enabled_ = true; | 70 is_enabled_ = true; |
69 Isolate::Current()->debugger()->SyncBreakpoint(this); | 71 Isolate::Current()->debugger()->SyncBreakpoint(this); |
70 } | 72 } |
71 | 73 |
72 | 74 |
73 void SourceBreakpoint::Disable() { | 75 void SourceBreakpoint::Disable() { |
74 is_enabled_ = false; | 76 is_enabled_ = false; |
75 Isolate::Current()->debugger()->SyncBreakpoint(this); | 77 Isolate::Current()->debugger()->SyncBreakpoint(this); |
76 } | 78 } |
77 | 79 |
78 | 80 |
79 RawScript* SourceBreakpoint::SourceCode() { | 81 void SourceBreakpoint::SetResolved(const Function& func, intptr_t token_pos) { |
80 const Function& func = Function::Handle(function_); | 82 ASSERT(func.script() == script_); |
81 return func.script(); | 83 ASSERT((func.token_pos() <= token_pos) && |
| 84 (token_pos <= func.end_token_pos())); |
| 85 function_ = func.raw(); |
| 86 token_pos_ = token_pos; |
| 87 line_number_ = -1; // Recalcualte lazily. |
| 88 is_resolved_ = true; |
82 } | 89 } |
83 | 90 |
84 | 91 |
| 92 // TODO(hausner): Get rid of library parameter. A source breakpoint location |
| 93 // does not imply a library, since the same source code can be included |
| 94 // in more than one library, e.g. the text location of mixin functions. |
85 void SourceBreakpoint::GetCodeLocation( | 95 void SourceBreakpoint::GetCodeLocation( |
86 Library* lib, | 96 Library* lib, |
87 Script* script, | 97 Script* script, |
88 intptr_t* pos) const { | 98 intptr_t* pos) { |
89 const Function& func = Function::Handle(function_); | 99 *script = this->script(); |
90 const Class& cls = Class::Handle(func.origin()); | 100 *pos = token_pos_; |
91 *lib = cls.library(); | 101 if (IsResolved()) { |
92 *script = func.script(); | 102 const Function& func = Function::Handle(function_); |
93 *pos = token_pos(); | 103 ASSERT(!func.IsNull()); |
| 104 const Class& cls = Class::Handle(func.origin()); |
| 105 *lib = cls.library(); |
| 106 } else { |
| 107 *lib = Library::null(); |
| 108 } |
94 } | 109 } |
95 | 110 |
96 | 111 |
97 RawString* SourceBreakpoint::SourceUrl() { | 112 RawString* SourceBreakpoint::SourceUrl() { |
98 const Script& script = Script::Handle(SourceCode()); | 113 return Script::Handle(script()).url(); |
99 return script.url(); | |
100 } | 114 } |
101 | 115 |
102 | 116 |
103 intptr_t SourceBreakpoint::LineNumber() { | 117 intptr_t SourceBreakpoint::LineNumber() { |
104 // Compute line number lazily since it causes scanning of the script. | 118 // Compute line number lazily since it causes scanning of the script. |
105 if (line_number_ < 0) { | 119 if (line_number_ < 0) { |
106 const Script& script = Script::Handle(SourceCode()); | 120 const Script& script = Script::Handle(this->script()); |
107 script.GetTokenLocation(token_pos_, &line_number_, NULL); | 121 script.GetTokenLocation(token_pos_, &line_number_, NULL); |
108 } | 122 } |
109 return line_number_; | 123 return line_number_; |
110 } | 124 } |
111 | 125 |
112 | 126 |
113 void SourceBreakpoint::set_function(const Function& func) { | |
114 function_ = func.raw(); | |
115 } | |
116 | |
117 | |
118 void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { | 127 void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
| 128 visitor->VisitPointer(reinterpret_cast<RawObject**>(&script_)); |
119 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); | 129 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); |
120 } | 130 } |
121 | 131 |
122 | 132 |
123 void SourceBreakpoint::PrintToJSONStream(JSONStream* stream) const { | 133 void SourceBreakpoint::PrintToJSONStream(JSONStream* stream) { |
124 Isolate* isolate = Isolate::Current(); | 134 Isolate* isolate = Isolate::Current(); |
125 | 135 |
126 JSONObject jsobj(stream); | 136 JSONObject jsobj(stream); |
127 jsobj.AddProperty("type", "Breakpoint"); | 137 jsobj.AddProperty("type", "Breakpoint"); |
128 | 138 |
129 jsobj.AddProperty("id", id()); | 139 jsobj.AddProperty("id", id()); |
130 jsobj.AddProperty("enabled", IsEnabled()); | 140 jsobj.AddProperty("enabled", IsEnabled()); |
131 | 141 jsobj.AddProperty("resolved", IsResolved()); |
132 const Function& func = Function::Handle(function()); | |
133 jsobj.AddProperty("resolved", func.HasCode()); | |
134 | 142 |
135 Library& library = Library::Handle(isolate); | 143 Library& library = Library::Handle(isolate); |
136 Script& script = Script::Handle(isolate); | 144 Script& script = Script::Handle(isolate); |
137 intptr_t token_pos; | 145 intptr_t token_pos; |
138 GetCodeLocation(&library, &script, &token_pos); | 146 GetCodeLocation(&library, &script, &token_pos); |
139 { | 147 { |
140 JSONObject location(&jsobj, "location"); | 148 JSONObject location(&jsobj, "location"); |
141 location.AddProperty("type", "Location"); | 149 location.AddProperty("type", "Location"); |
142 location.AddProperty("libId", library.index()); | |
143 | 150 |
144 const String& url = String::Handle(script.url()); | 151 const String& url = String::Handle(script.url()); |
145 location.AddProperty("script", url.ToCString()); | 152 location.AddProperty("script", url.ToCString()); |
146 location.AddProperty("tokenPos", token_pos); | 153 location.AddProperty("tokenPos", token_pos); |
147 } | 154 } |
148 } | 155 } |
149 | 156 |
150 | 157 |
151 void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { | 158 void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
152 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); | 159 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); |
(...skipping 29 matching lines...) Expand all Loading... |
182 if (event_handler_ != NULL) { | 189 if (event_handler_ != NULL) { |
183 Debugger* debugger = Isolate::Current()->debugger(); | 190 Debugger* debugger = Isolate::Current()->debugger(); |
184 ASSERT(debugger != NULL); | 191 ASSERT(debugger != NULL); |
185 DebuggerEvent event(type); | 192 DebuggerEvent event(type); |
186 event.isolate_id = debugger->GetIsolateId(); | 193 event.isolate_id = debugger->GetIsolateId(); |
187 ASSERT(event.isolate_id != ILLEGAL_ISOLATE_ID); | 194 ASSERT(event.isolate_id != ILLEGAL_ISOLATE_ID); |
188 if (type == kIsolateInterrupted) { | 195 if (type == kIsolateInterrupted) { |
189 DebuggerStackTrace* stack_trace = debugger->CollectStackTrace(); | 196 DebuggerStackTrace* stack_trace = debugger->CollectStackTrace(); |
190 ASSERT(stack_trace->Length() > 0); | 197 ASSERT(stack_trace->Length() > 0); |
191 ASSERT(debugger->stack_trace_ == NULL); | 198 ASSERT(debugger->stack_trace_ == NULL); |
192 ASSERT(debugger->obj_cache_ == NULL); | |
193 debugger->obj_cache_ = new RemoteObjectCache(64); | |
194 debugger->stack_trace_ = stack_trace; | 199 debugger->stack_trace_ = stack_trace; |
195 (*event_handler_)(&event); | 200 debugger->Pause(&event); |
196 debugger->stack_trace_ = NULL; | 201 debugger->stack_trace_ = NULL; |
197 debugger->obj_cache_ = NULL; // Remote object cache is zone allocated. | |
198 // TODO(asiva): Need some work here to be able to single step after | 202 // TODO(asiva): Need some work here to be able to single step after |
199 // an interrupt. | 203 // an interrupt. |
200 } else { | 204 } else { |
201 (*event_handler_)(&event); | 205 (*event_handler_)(&event); |
202 } | 206 } |
203 } | 207 } |
204 } | 208 } |
205 | 209 |
206 | 210 |
207 const char* Debugger::QualifiedFunctionName(const Function& func) { | 211 const char* Debugger::QualifiedFunctionName(const Function& func) { |
208 const String& func_name = String::Handle(func.name()); | 212 const String& func_name = String::Handle(func.name()); |
209 Class& func_class = Class::Handle(func.Owner()); | 213 Class& func_class = Class::Handle(func.Owner()); |
210 String& class_name = String::Handle(func_class.Name()); | 214 String& class_name = String::Handle(func_class.Name()); |
211 | 215 |
212 const char* kFormat = "%s%s%s"; | 216 const char* kFormat = "%s%s%s"; |
213 intptr_t len = OS::SNPrint(NULL, 0, kFormat, | 217 intptr_t len = OS::SNPrint(NULL, 0, kFormat, |
214 func_class.IsTopLevel() ? "" : class_name.ToCString(), | 218 func_class.IsTopLevel() ? "" : class_name.ToCString(), |
215 func_class.IsTopLevel() ? "" : ".", | 219 func_class.IsTopLevel() ? "" : ".", |
216 func_name.ToCString()); | 220 func_name.ToCString()); |
217 len++; // String terminator. | 221 len++; // String terminator. |
218 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); | 222 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); |
219 OS::SNPrint(chars, len, kFormat, | 223 OS::SNPrint(chars, len, kFormat, |
220 func_class.IsTopLevel() ? "" : class_name.ToCString(), | 224 func_class.IsTopLevel() ? "" : class_name.ToCString(), |
221 func_class.IsTopLevel() ? "" : ".", | 225 func_class.IsTopLevel() ? "" : ".", |
222 func_name.ToCString()); | 226 func_name.ToCString()); |
223 return chars; | 227 return chars; |
224 } | 228 } |
225 | 229 |
226 | 230 |
| 231 // Returns true if function contains the token position in the given script. |
| 232 static bool FunctionContains(const Function& func, |
| 233 const Script& script, |
| 234 intptr_t token_pos) { |
| 235 if ((func.token_pos() <= token_pos) && (token_pos <= func.end_token_pos())) { |
| 236 // Check script equality second because it allocates |
| 237 // handles as a side effect. |
| 238 return func.script() == script.raw(); |
| 239 } |
| 240 return false; |
| 241 } |
| 242 |
| 243 |
227 bool Debugger::HasBreakpoint(const Function& func) { | 244 bool Debugger::HasBreakpoint(const Function& func) { |
228 if (!func.HasCode()) { | 245 if (!func.HasCode()) { |
229 // If the function is not compiled yet, just check whether there | 246 // If the function is not compiled yet, just check whether there |
230 // is a user-defined latent breakpoint. | 247 // is a user-defined breakpoint that falls into the token |
| 248 // range of the function. This may be a false positive: the breakpoint |
| 249 // might be inside a local closure. |
| 250 Script& script = Script::Handle(isolate_); |
231 SourceBreakpoint* sbpt = src_breakpoints_; | 251 SourceBreakpoint* sbpt = src_breakpoints_; |
232 while (sbpt != NULL) { | 252 while (sbpt != NULL) { |
233 if (func.raw() == sbpt->function()) { | 253 script = sbpt->script(); |
| 254 if (FunctionContains(func, script, sbpt->token_pos())) { |
234 return true; | 255 return true; |
235 } | 256 } |
236 sbpt = sbpt->next_; | 257 sbpt = sbpt->next_; |
237 } | 258 } |
238 return false; | 259 return false; |
239 } | 260 } |
240 CodeBreakpoint* cbpt = code_breakpoints_; | 261 CodeBreakpoint* cbpt = code_breakpoints_; |
241 while (cbpt != NULL) { | 262 while (cbpt != NULL) { |
242 if (func.raw() == cbpt->function()) { | 263 if (func.raw() == cbpt->function()) { |
243 return true; | 264 return true; |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
364 ASSERT(!code_.is_optimized()); | 385 ASSERT(!code_.is_optimized()); |
365 context_level_ = 0; | 386 context_level_ = 0; |
366 intptr_t pc_desc_idx = PcDescIndex(); | 387 intptr_t pc_desc_idx = PcDescIndex(); |
367 // TODO(hausner): What to do if there is no descriptor entry | 388 // TODO(hausner): What to do if there is no descriptor entry |
368 // for the code position of the frame? For now say we are at context | 389 // for the code position of the frame? For now say we are at context |
369 // level 0. | 390 // level 0. |
370 if (pc_desc_idx < 0) { | 391 if (pc_desc_idx < 0) { |
371 return context_level_; | 392 return context_level_; |
372 } | 393 } |
373 ASSERT(!pc_desc_.IsNull()); | 394 ASSERT(!pc_desc_.IsNull()); |
374 if (pc_desc_.DescriptorKind(pc_desc_idx) == PcDescriptors::kReturn) { | |
375 // Special case: the context chain has already been deallocated. | |
376 // The context level is 0. | |
377 return context_level_; | |
378 } | |
379 intptr_t innermost_begin_pos = 0; | 395 intptr_t innermost_begin_pos = 0; |
380 intptr_t activation_token_pos = TokenPos(); | 396 intptr_t activation_token_pos = TokenPos(); |
381 ASSERT(activation_token_pos >= 0); | 397 ASSERT(activation_token_pos >= 0); |
382 GetVarDescriptors(); | 398 GetVarDescriptors(); |
383 intptr_t var_desc_len = var_descriptors_.Length(); | 399 intptr_t var_desc_len = var_descriptors_.Length(); |
384 for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) { | 400 for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) { |
385 RawLocalVarDescriptors::VarInfo var_info; | 401 RawLocalVarDescriptors::VarInfo var_info; |
386 var_descriptors_.GetInfo(cur_idx, &var_info); | 402 var_descriptors_.GetInfo(cur_idx, &var_info); |
387 if ((var_info.kind == RawLocalVarDescriptors::kContextLevel) && | 403 if ((var_info.kind == RawLocalVarDescriptors::kContextLevel) && |
388 (var_info.begin_pos <= activation_token_pos) && | 404 (var_info.begin_pos <= activation_token_pos) && |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
573 | 589 |
574 | 590 |
575 RawInstance* ActivationFrame::GetLocalInstanceVar(intptr_t slot_index) { | 591 RawInstance* ActivationFrame::GetLocalInstanceVar(intptr_t slot_index) { |
576 Instance& instance = Instance::Handle(); | 592 Instance& instance = Instance::Handle(); |
577 instance ^= GetLocalVar(slot_index); | 593 instance ^= GetLocalVar(slot_index); |
578 return instance.raw(); | 594 return instance.raw(); |
579 } | 595 } |
580 | 596 |
581 | 597 |
582 RawContext* ActivationFrame::GetLocalContextVar(intptr_t slot_index) { | 598 RawContext* ActivationFrame::GetLocalContextVar(intptr_t slot_index) { |
583 Context& context = Context::Handle(); | 599 Object& context = Object::Handle(GetLocalVar(slot_index)); |
584 context ^= GetLocalVar(slot_index); | 600 if (context.IsContext()) { |
585 return context.raw(); | 601 return Context::Cast(context).raw(); |
| 602 } |
| 603 return Context::null(); |
586 } | 604 } |
587 | 605 |
588 | 606 |
589 void ActivationFrame::VariableAt(intptr_t i, | 607 void ActivationFrame::VariableAt(intptr_t i, |
590 String* name, | 608 String* name, |
591 intptr_t* token_pos, | 609 intptr_t* token_pos, |
592 intptr_t* end_pos, | 610 intptr_t* end_pos, |
593 Instance* value) { | 611 Instance* value) { |
594 GetDescIndices(); | 612 GetDescIndices(); |
595 ASSERT(i < desc_indices_.length()); | 613 ASSERT(i < desc_indices_.length()); |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
755 case PcDescriptors::kUnoptStaticCall: { | 773 case PcDescriptors::kUnoptStaticCall: { |
756 const Code& code = | 774 const Code& code = |
757 Code::Handle(Function::Handle(function_).unoptimized_code()); | 775 Code::Handle(Function::Handle(function_).unoptimized_code()); |
758 saved_bytes_.target_address_ = | 776 saved_bytes_.target_address_ = |
759 CodePatcher::GetStaticCallTargetAt(pc_, code); | 777 CodePatcher::GetStaticCallTargetAt(pc_, code); |
760 CodePatcher::PatchStaticCallAt(pc_, code, | 778 CodePatcher::PatchStaticCallAt(pc_, code, |
761 StubCode::BreakpointStaticEntryPoint()); | 779 StubCode::BreakpointStaticEntryPoint()); |
762 break; | 780 break; |
763 } | 781 } |
764 case PcDescriptors::kRuntimeCall: | 782 case PcDescriptors::kRuntimeCall: |
765 case PcDescriptors::kClosureCall: { | 783 case PcDescriptors::kClosureCall: |
| 784 case PcDescriptors::kReturn: { |
766 const Code& code = | 785 const Code& code = |
767 Code::Handle(Function::Handle(function_).unoptimized_code()); | 786 Code::Handle(Function::Handle(function_).unoptimized_code()); |
768 saved_bytes_.target_address_ = | 787 saved_bytes_.target_address_ = |
769 CodePatcher::GetStaticCallTargetAt(pc_, code); | 788 CodePatcher::GetStaticCallTargetAt(pc_, code); |
770 CodePatcher::PatchStaticCallAt(pc_, code, | 789 CodePatcher::PatchStaticCallAt(pc_, code, |
771 StubCode::BreakpointRuntimeEntryPoint()); | 790 StubCode::BreakpointRuntimeEntryPoint()); |
772 break; | 791 break; |
773 } | 792 } |
774 case PcDescriptors::kReturn: | |
775 PatchFunctionReturn(); | |
776 break; | |
777 default: | 793 default: |
778 UNREACHABLE(); | 794 UNREACHABLE(); |
779 } | 795 } |
780 is_enabled_ = true; | 796 is_enabled_ = true; |
781 } | 797 } |
782 | 798 |
783 | 799 |
784 void CodeBreakpoint::RestoreCode() { | 800 void CodeBreakpoint::RestoreCode() { |
785 ASSERT(is_enabled_); | 801 ASSERT(is_enabled_); |
786 switch (breakpoint_kind_) { | 802 switch (breakpoint_kind_) { |
787 case PcDescriptors::kIcCall: { | 803 case PcDescriptors::kIcCall: { |
788 const Code& code = | 804 const Code& code = |
789 Code::Handle(Function::Handle(function_).unoptimized_code()); | 805 Code::Handle(Function::Handle(function_).unoptimized_code()); |
790 CodePatcher::PatchInstanceCallAt(pc_, code, | 806 CodePatcher::PatchInstanceCallAt(pc_, code, |
791 saved_bytes_.target_address_); | 807 saved_bytes_.target_address_); |
792 break; | 808 break; |
793 } | 809 } |
794 case PcDescriptors::kUnoptStaticCall: | 810 case PcDescriptors::kUnoptStaticCall: |
795 case PcDescriptors::kClosureCall: | 811 case PcDescriptors::kClosureCall: |
796 case PcDescriptors::kRuntimeCall: { | 812 case PcDescriptors::kRuntimeCall: |
| 813 case PcDescriptors::kReturn: { |
797 const Code& code = | 814 const Code& code = |
798 Code::Handle(Function::Handle(function_).unoptimized_code()); | 815 Code::Handle(Function::Handle(function_).unoptimized_code()); |
799 CodePatcher::PatchStaticCallAt(pc_, code, | 816 CodePatcher::PatchStaticCallAt(pc_, code, |
800 saved_bytes_.target_address_); | 817 saved_bytes_.target_address_); |
801 break; | 818 break; |
802 } | 819 } |
803 case PcDescriptors::kReturn: | |
804 RestoreFunctionReturn(); | |
805 break; | |
806 default: | 820 default: |
807 UNREACHABLE(); | 821 UNREACHABLE(); |
808 } | 822 } |
809 is_enabled_ = false; | 823 is_enabled_ = false; |
810 } | 824 } |
811 | 825 |
812 | 826 |
813 void CodeBreakpoint::Enable() { | 827 void CodeBreakpoint::Enable() { |
814 if (!is_enabled_) { | 828 if (!is_enabled_) { |
815 PatchCode(); | 829 PatchCode(); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
848 ASSERT(IsValidId(obj_id)); | 862 ASSERT(IsValidId(obj_id)); |
849 return objs_->At(obj_id); | 863 return objs_->At(obj_id); |
850 } | 864 } |
851 | 865 |
852 | 866 |
853 Debugger::Debugger() | 867 Debugger::Debugger() |
854 : isolate_(NULL), | 868 : isolate_(NULL), |
855 isolate_id_(ILLEGAL_ISOLATE_ID), | 869 isolate_id_(ILLEGAL_ISOLATE_ID), |
856 initialized_(false), | 870 initialized_(false), |
857 next_id_(1), | 871 next_id_(1), |
858 stack_trace_(NULL), | |
859 obj_cache_(NULL), | |
860 src_breakpoints_(NULL), | 872 src_breakpoints_(NULL), |
861 code_breakpoints_(NULL), | 873 code_breakpoints_(NULL), |
862 resume_action_(kContinue), | 874 resume_action_(kContinue), |
863 ignore_breakpoints_(false), | 875 ignore_breakpoints_(false), |
864 in_event_notification_(false), | 876 pause_event_(NULL), |
| 877 obj_cache_(NULL), |
| 878 stack_trace_(NULL), |
865 exc_pause_info_(kNoPauseOnExceptions) { | 879 exc_pause_info_(kNoPauseOnExceptions) { |
866 } | 880 } |
867 | 881 |
868 | 882 |
869 Debugger::~Debugger() { | 883 Debugger::~Debugger() { |
870 isolate_id_ = ILLEGAL_ISOLATE_ID; | 884 isolate_id_ = ILLEGAL_ISOLATE_ID; |
871 ASSERT(!in_event_notification_); | 885 ASSERT(!IsPaused()); |
872 ASSERT(src_breakpoints_ == NULL); | 886 ASSERT(src_breakpoints_ == NULL); |
873 ASSERT(code_breakpoints_ == NULL); | 887 ASSERT(code_breakpoints_ == NULL); |
874 ASSERT(stack_trace_ == NULL); | 888 ASSERT(stack_trace_ == NULL); |
875 ASSERT(obj_cache_ == NULL); | 889 ASSERT(obj_cache_ == NULL); |
876 } | 890 } |
877 | 891 |
878 | 892 |
879 void Debugger::Shutdown() { | 893 void Debugger::Shutdown() { |
880 while (src_breakpoints_ != NULL) { | 894 while (src_breakpoints_ != NULL) { |
881 SourceBreakpoint* bpt = src_breakpoints_; | 895 SourceBreakpoint* bpt = src_breakpoints_; |
(...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1263 return false; | 1277 return false; |
1264 } | 1278 } |
1265 | 1279 |
1266 | 1280 |
1267 void Debugger::SignalExceptionThrown(const Instance& exc) { | 1281 void Debugger::SignalExceptionThrown(const Instance& exc) { |
1268 // We ignore this exception event when the VM is executing code invoked | 1282 // We ignore this exception event when the VM is executing code invoked |
1269 // by the debugger to evaluate variables values, when we see a nested | 1283 // by the debugger to evaluate variables values, when we see a nested |
1270 // breakpoint or exception event, or if the debugger is not | 1284 // breakpoint or exception event, or if the debugger is not |
1271 // interested in exception events. | 1285 // interested in exception events. |
1272 if (ignore_breakpoints_ || | 1286 if (ignore_breakpoints_ || |
1273 in_event_notification_ || | 1287 IsPaused() || |
1274 (event_handler_ == NULL) || | 1288 (event_handler_ == NULL) || |
1275 (exc_pause_info_ == kNoPauseOnExceptions)) { | 1289 (exc_pause_info_ == kNoPauseOnExceptions)) { |
1276 return; | 1290 return; |
1277 } | 1291 } |
1278 DebuggerStackTrace* stack_trace = CollectStackTrace(); | 1292 DebuggerStackTrace* stack_trace = CollectStackTrace(); |
1279 if (!ShouldPauseOnException(stack_trace, exc)) { | 1293 if (!ShouldPauseOnException(stack_trace, exc)) { |
1280 return; | 1294 return; |
1281 } | 1295 } |
| 1296 DebuggerEvent event(kExceptionThrown); |
| 1297 event.exception = &exc; |
1282 ASSERT(stack_trace_ == NULL); | 1298 ASSERT(stack_trace_ == NULL); |
1283 stack_trace_ = stack_trace; | 1299 stack_trace_ = stack_trace; |
1284 ASSERT(obj_cache_ == NULL); | 1300 Pause(&event); |
1285 in_event_notification_ = true; | |
1286 obj_cache_ = new RemoteObjectCache(64); | |
1287 DebuggerEvent event(kExceptionThrown); | |
1288 event.exception = &exc; | |
1289 (*event_handler_)(&event); | |
1290 in_event_notification_ = false; | |
1291 stack_trace_ = NULL; | 1301 stack_trace_ = NULL; |
1292 obj_cache_ = NULL; // Remote object cache is zone allocated. | |
1293 } | 1302 } |
1294 | 1303 |
1295 | 1304 |
1296 // Given a function and a token position range, return the best fit | 1305 // Given a function and a token position, return the best fit |
1297 // token position to set a breakpoint. | 1306 // token position to set a breakpoint. The best fit is the safe point |
1298 // If multiple possible breakpoint positions are within the given range, | 1307 // with the lowest compiled code address that follows the requsted |
1299 // the one with the lowest machine code address is picked. | 1308 // token position. |
1300 // If no possible breakpoint location exists in the given range, the closest | |
1301 // token position after the range is returned. | |
1302 intptr_t Debugger::ResolveBreakpointPos(const Function& func, | 1309 intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
1303 intptr_t first_token_pos, | 1310 intptr_t requested_token_pos) { |
1304 intptr_t last_token_pos) { | |
1305 ASSERT(func.HasCode()); | 1311 ASSERT(func.HasCode()); |
1306 ASSERT(!func.HasOptimizedCode()); | 1312 ASSERT(!func.HasOptimizedCode()); |
1307 Code& code = Code::Handle(func.unoptimized_code()); | 1313 Code& code = Code::Handle(func.unoptimized_code()); |
1308 ASSERT(!code.IsNull()); | 1314 ASSERT(!code.IsNull()); |
1309 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); | 1315 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
1310 intptr_t best_fit_index = -1; | 1316 intptr_t best_fit_index = -1; |
1311 intptr_t best_fit = INT_MAX; | 1317 intptr_t best_fit_pos = INT_MAX; |
1312 uword lowest_pc = kUwordMax; | 1318 uword lowest_pc = kUwordMax; |
1313 intptr_t lowest_pc_index = -1; | 1319 intptr_t lowest_pc_index = -1; |
1314 for (intptr_t i = 0; i < desc.Length(); i++) { | 1320 for (intptr_t i = 0; i < desc.Length(); i++) { |
1315 intptr_t desc_token_pos = desc.TokenPos(i); | 1321 intptr_t desc_token_pos = desc.TokenPos(i); |
1316 ASSERT(desc_token_pos >= 0); | 1322 ASSERT(desc_token_pos >= 0); |
1317 if (desc_token_pos < first_token_pos) { | 1323 if (desc_token_pos < requested_token_pos) { |
1318 // This descriptor is before the given range. | 1324 // This descriptor is before the first acceptable token position. |
1319 continue; | 1325 continue; |
1320 } | 1326 } |
1321 if (IsSafePoint(desc.DescriptorKind(i))) { | 1327 if (IsSafePoint(desc.DescriptorKind(i))) { |
1322 if ((desc_token_pos - first_token_pos) < best_fit) { | 1328 if (desc_token_pos < best_fit_pos) { |
1323 // So far, this descriptor has the closest token position to the | 1329 // So far, this descriptor has the lowest token position after |
1324 // beginning of the range. | 1330 // the first acceptable token position. |
1325 best_fit = desc_token_pos - first_token_pos; | 1331 best_fit_pos = desc_token_pos; |
1326 ASSERT(best_fit >= 0); | |
1327 best_fit_index = i; | 1332 best_fit_index = i; |
1328 } | 1333 } |
1329 if ((first_token_pos <= desc_token_pos) && | 1334 if (desc.PC(i) < lowest_pc) { |
1330 (desc_token_pos <= last_token_pos) && | 1335 // This descriptor so far has the lowest code address. |
1331 (desc.PC(i) < lowest_pc)) { | |
1332 // This descriptor is within the token position range and so | |
1333 // far has the lowest code address. | |
1334 lowest_pc = desc.PC(i); | 1336 lowest_pc = desc.PC(i); |
1335 lowest_pc_index = i; | 1337 lowest_pc_index = i; |
1336 } | 1338 } |
1337 } | 1339 } |
1338 } | 1340 } |
1339 if (lowest_pc_index >= 0) { | 1341 if (lowest_pc_index >= 0) { |
1340 // We found the the pc descriptor within the given token range that | 1342 // We found the pc descriptor that has the lowest execution address. |
1341 // has the lowest execution address. This is the first possible | 1343 // This is the first possible breakpoint after the requested token |
1342 // breakpoint on the line. We use this instead of the nearest | 1344 // position. We use this instead of the nearest PC descriptor |
1343 // PC descriptor measured in token index distance. | 1345 // measured in token index distance. |
1344 best_fit_index = lowest_pc_index; | 1346 best_fit_index = lowest_pc_index; |
1345 } | 1347 } |
1346 if (best_fit_index >= 0) { | 1348 if (best_fit_index >= 0) { |
1347 return desc.TokenPos(best_fit_index); | 1349 return desc.TokenPos(best_fit_index); |
1348 } | 1350 } |
1349 return -1; | 1351 return -1; |
1350 } | 1352 } |
1351 | 1353 |
1352 | 1354 |
1353 void Debugger::MakeCodeBreakpointsAt(const Function& func, | 1355 void Debugger::MakeCodeBreakpointsAt(const Function& func, |
1354 intptr_t token_pos, | |
1355 SourceBreakpoint* bpt) { | 1356 SourceBreakpoint* bpt) { |
| 1357 ASSERT((bpt != NULL) && bpt->IsResolved()); |
1356 ASSERT(!func.HasOptimizedCode()); | 1358 ASSERT(!func.HasOptimizedCode()); |
1357 Code& code = Code::Handle(func.unoptimized_code()); | 1359 Code& code = Code::Handle(func.unoptimized_code()); |
1358 ASSERT(!code.IsNull()); | 1360 ASSERT(!code.IsNull()); |
1359 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); | 1361 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
1360 for (intptr_t i = 0; i < desc.Length(); i++) { | 1362 for (intptr_t i = 0; i < desc.Length(); i++) { |
1361 intptr_t desc_token_pos = desc.TokenPos(i); | 1363 intptr_t desc_token_pos = desc.TokenPos(i); |
1362 if ((desc_token_pos == token_pos) && IsSafePoint(desc.DescriptorKind(i))) { | 1364 if ((desc_token_pos == bpt->token_pos_) && |
| 1365 IsSafePoint(desc.DescriptorKind(i))) { |
1363 CodeBreakpoint* code_bpt = GetCodeBreakpoint(desc.PC(i)); | 1366 CodeBreakpoint* code_bpt = GetCodeBreakpoint(desc.PC(i)); |
1364 if (code_bpt == NULL) { | 1367 if (code_bpt == NULL) { |
1365 // No code breakpoint for this code exists; create one. | 1368 // No code breakpoint for this code exists; create one. |
1366 code_bpt = new CodeBreakpoint(func, i); | 1369 code_bpt = new CodeBreakpoint(func, i); |
1367 RegisterCodeBreakpoint(code_bpt); | 1370 RegisterCodeBreakpoint(code_bpt); |
1368 } | 1371 } |
1369 code_bpt->set_src_bpt(bpt); | 1372 code_bpt->set_src_bpt(bpt); |
1370 } | 1373 if (bpt->IsEnabled()) { |
1371 } | 1374 code_bpt->Enable(); |
1372 } | 1375 } |
1373 | 1376 } |
1374 | 1377 } |
1375 SourceBreakpoint* Debugger::SetBreakpoint(const Function& target_function, | 1378 } |
1376 intptr_t first_token_pos, | 1379 |
1377 intptr_t last_token_pos) { | 1380 |
1378 if ((last_token_pos < target_function.token_pos()) || | 1381 void Debugger::FindEquivalentFunctions(const Script& script, |
1379 (target_function.end_token_pos() < first_token_pos)) { | 1382 intptr_t start_pos, |
1380 // The given token position is not within the target function. | 1383 intptr_t end_pos, |
| 1384 GrowableObjectArray* function_list) { |
| 1385 Class& cls = Class::Handle(isolate_); |
| 1386 Array& functions = Array::Handle(isolate_); |
| 1387 GrowableObjectArray& closures = GrowableObjectArray::Handle(isolate_); |
| 1388 Function& function = Function::Handle(isolate_); |
| 1389 |
| 1390 const ClassTable& class_table = *isolate_->class_table(); |
| 1391 const intptr_t num_classes = class_table.NumCids(); |
| 1392 for (intptr_t i = 1; i < num_classes; i++) { |
| 1393 if (class_table.HasValidClassAt(i)) { |
| 1394 cls = class_table.At(i); |
| 1395 // If the class is not finalized, e.g. if it hasn't been parsed |
| 1396 // yet entirely, we can ignore it. If it contains a function with |
| 1397 // a latent breakpoint, we will detect it if and when the function |
| 1398 // gets compiled. |
| 1399 if (!cls.is_finalized()) { |
| 1400 continue; |
| 1401 } |
| 1402 // Note: we need to check the functions of this class even if |
| 1403 // the class is defined in a differenct 'script'. There could |
| 1404 // be mixin functions from the given script in this class. |
| 1405 functions = cls.functions(); |
| 1406 if (!functions.IsNull()) { |
| 1407 const intptr_t num_functions = functions.Length(); |
| 1408 for (intptr_t pos = 0; pos < num_functions; pos++) { |
| 1409 function ^= functions.At(pos); |
| 1410 ASSERT(!function.IsNull()); |
| 1411 // Check token position first to avoid unnecessary calls |
| 1412 // to script() which allocates handles. |
| 1413 if ((function.token_pos() == start_pos) |
| 1414 && (function.end_token_pos() == end_pos) |
| 1415 && (function.script() == script.raw())) { |
| 1416 function_list->Add(function); |
| 1417 if (function.HasImplicitClosureFunction()) { |
| 1418 function = function.ImplicitClosureFunction(); |
| 1419 function_list->Add(function); |
| 1420 } |
| 1421 } |
| 1422 } |
| 1423 } |
| 1424 closures = cls.closures(); |
| 1425 if (!closures.IsNull()) { |
| 1426 const intptr_t num_closures = closures.Length(); |
| 1427 for (intptr_t pos = 0; pos < num_closures; pos++) { |
| 1428 function ^= closures.At(pos); |
| 1429 ASSERT(!function.IsNull()); |
| 1430 if ((function.token_pos() == start_pos) |
| 1431 && (function.end_token_pos() == end_pos) |
| 1432 && (function.script() == script.raw())) { |
| 1433 function_list->Add(function); |
| 1434 if (function.HasImplicitClosureFunction()) { |
| 1435 function = function.ImplicitClosureFunction(); |
| 1436 function_list->Add(function); |
| 1437 } |
| 1438 } |
| 1439 } |
| 1440 } |
| 1441 } |
| 1442 } |
| 1443 } |
| 1444 |
| 1445 |
| 1446 static void SelectBestFit(Function* best_fit, Function* func) { |
| 1447 if (best_fit->IsNull()) { |
| 1448 *best_fit = func->raw(); |
| 1449 } |
| 1450 if (func->token_pos() > best_fit->token_pos()) { |
| 1451 if (func->end_token_pos() <= best_fit->end_token_pos()) { |
| 1452 // func is contained within best_fit. Select it even if it |
| 1453 // has not been compiled yet. |
| 1454 *best_fit = func->raw(); |
| 1455 if (func->HasImplicitClosureFunction()) { |
| 1456 *func = func->ImplicitClosureFunction(); |
| 1457 if (func->HasCode()) { |
| 1458 *best_fit = func->raw(); |
| 1459 } |
| 1460 } |
| 1461 } |
| 1462 } else if ((func->token_pos() == best_fit->token_pos()) |
| 1463 && (func->end_token_pos() == best_fit->end_token_pos()) |
| 1464 && func->HasCode()) { |
| 1465 // If func covers the same range, it is considered a better fit if |
| 1466 // it has been compiled. |
| 1467 *best_fit = func->raw(); |
| 1468 } |
| 1469 } |
| 1470 |
| 1471 |
| 1472 RawFunction* Debugger::FindBestFit(const Script& script, |
| 1473 intptr_t token_pos) { |
| 1474 Class& cls = Class::Handle(isolate_); |
| 1475 Array& functions = Array::Handle(isolate_); |
| 1476 GrowableObjectArray& closures = GrowableObjectArray::Handle(isolate_); |
| 1477 Function& function = Function::Handle(isolate_); |
| 1478 Function& best_fit = Function::Handle(isolate_); |
| 1479 |
| 1480 const ClassTable& class_table = *isolate_->class_table(); |
| 1481 const intptr_t num_classes = class_table.NumCids(); |
| 1482 for (intptr_t i = 1; i < num_classes; i++) { |
| 1483 if (class_table.HasValidClassAt(i)) { |
| 1484 cls = class_table.At(i); |
| 1485 // Note: if this class has been parsed and finalized already, |
| 1486 // we need to check the functions of this class even if |
| 1487 // it is defined in a differenct 'script'. There could |
| 1488 // be mixin functions from the given script in this class. |
| 1489 // However, if this class is not parsed yet (not finalized), |
| 1490 // we can ignore it and avoid the side effect of parsing it. |
| 1491 if ((cls.script() != script.raw()) && !cls.is_finalized()) { |
| 1492 continue; |
| 1493 } |
| 1494 // Parse class definition if not done yet. |
| 1495 cls.EnsureIsFinalized(isolate_); |
| 1496 functions = cls.functions(); |
| 1497 if (!functions.IsNull()) { |
| 1498 const intptr_t num_functions = functions.Length(); |
| 1499 for (intptr_t pos = 0; pos < num_functions; pos++) { |
| 1500 function ^= functions.At(pos); |
| 1501 ASSERT(!function.IsNull()); |
| 1502 if (FunctionContains(function, script, token_pos)) { |
| 1503 SelectBestFit(&best_fit, &function); |
| 1504 } |
| 1505 } |
| 1506 } |
| 1507 |
| 1508 closures = cls.closures(); |
| 1509 if (!closures.IsNull()) { |
| 1510 const intptr_t num_closures = closures.Length(); |
| 1511 for (intptr_t pos = 0; pos < num_closures; pos++) { |
| 1512 function ^= closures.At(pos); |
| 1513 ASSERT(!function.IsNull()); |
| 1514 if (FunctionContains(function, script, token_pos)) { |
| 1515 SelectBestFit(&best_fit, &function); |
| 1516 } |
| 1517 } |
| 1518 } |
| 1519 } |
| 1520 } |
| 1521 return best_fit.raw(); |
| 1522 } |
| 1523 |
| 1524 |
| 1525 SourceBreakpoint* Debugger::SetBreakpoint(const Script& script, |
| 1526 intptr_t token_pos) { |
| 1527 Function& func = Function::Handle(isolate_); |
| 1528 func = FindBestFit(script, token_pos); |
| 1529 if (func.IsNull()) { |
1381 return NULL; | 1530 return NULL; |
1382 } | 1531 } |
1383 intptr_t breakpoint_pos = -1; | 1532 if (!func.IsNull() && func.HasCode()) { |
1384 Function& closure = Function::Handle(isolate_); | 1533 // A function containing this breakpoint location has already |
1385 if (target_function.HasImplicitClosureFunction()) { | 1534 // been compiled. We can resolve the breakpoint now. |
1386 // There is a closurized version of this function. | |
1387 closure = target_function.ImplicitClosureFunction(); | |
1388 } | |
1389 // Determine actual breakpoint location if the function or an | |
1390 // implicit closure of the function has been compiled already. | |
1391 if (target_function.HasCode()) { | |
1392 DeoptimizeWorld(); | 1535 DeoptimizeWorld(); |
1393 ASSERT(!target_function.HasOptimizedCode()); | 1536 intptr_t breakpoint_pos = ResolveBreakpointPos(func, token_pos); |
1394 breakpoint_pos = | 1537 if (breakpoint_pos >= 0) { |
1395 ResolveBreakpointPos(target_function, first_token_pos, last_token_pos); | 1538 SourceBreakpoint* bpt = GetSourceBreakpoint(script, breakpoint_pos); |
1396 } else if (!closure.IsNull() && closure.HasCode()) { | 1539 if (bpt != NULL) { |
1397 DeoptimizeWorld(); | 1540 // A source breakpoint for this location already exists. |
1398 ASSERT(!closure.HasOptimizedCode()); | 1541 return bpt; |
1399 breakpoint_pos = | 1542 } |
1400 ResolveBreakpointPos(closure, first_token_pos, last_token_pos); | 1543 bpt = new SourceBreakpoint(nextId(), script, token_pos); |
1401 } else { | 1544 bpt->SetResolved(func, breakpoint_pos); |
1402 // This function has not been compiled yet. Set a pending | 1545 RegisterSourceBreakpoint(bpt); |
1403 // breakpoint to be resolved later. | 1546 // There may be more than one function object for a given function |
1404 SourceBreakpoint* source_bpt = | 1547 // in source code. There may be implicit closure functions, and |
1405 GetSourceBreakpoint(target_function, first_token_pos); | 1548 // there may be copies of mixin functions. Collect all functions whose |
1406 if (source_bpt != NULL) { | 1549 // source code range matches exactly the best fit function we |
1407 // A pending source breakpoint for this uncompiled location | 1550 // found. |
1408 // already exists. | 1551 GrowableObjectArray& functions = |
1409 if (FLAG_verbose_debug) { | 1552 GrowableObjectArray::Handle(GrowableObjectArray::New()); |
1410 OS::Print("Pending breakpoint for uncompiled function" | 1553 FindEquivalentFunctions(script, |
1411 " '%s' at line %" Pd " already exists\n", | 1554 func.token_pos(), |
1412 target_function.ToFullyQualifiedCString(), | 1555 func.end_token_pos(), |
1413 source_bpt->LineNumber()); | 1556 &functions); |
1414 } | 1557 const intptr_t num_functions = functions.Length(); |
1415 return source_bpt; | 1558 // We must have found at least one function: func. |
1416 } | 1559 ASSERT(num_functions > 0); |
1417 source_bpt = | 1560 // Create code breakpoints for all compiled functions we found. |
1418 new SourceBreakpoint(nextId(), target_function, first_token_pos); | 1561 for (intptr_t i = 0; i < num_functions; i++) { |
1419 RegisterSourceBreakpoint(source_bpt); | 1562 func ^= functions.At(i); |
1420 if (FLAG_verbose_debug) { | 1563 if (func.HasCode()) { |
1421 OS::Print("Registering pending breakpoint for " | 1564 MakeCodeBreakpointsAt(func, bpt); |
1422 "uncompiled function '%s' at line %" Pd "\n", | 1565 } |
1423 target_function.ToFullyQualifiedCString(), | 1566 } |
1424 source_bpt->LineNumber()); | 1567 bpt->Enable(); |
1425 } | 1568 SignalBpResolved(bpt); |
1426 source_bpt->Enable(); | 1569 return bpt; |
1427 return source_bpt; | 1570 } |
1428 } | 1571 } |
1429 ASSERT(breakpoint_pos != -1); | 1572 // There is no compiled function at this token position. |
1430 SourceBreakpoint* source_bpt = | 1573 // Register an unresolved breakpoint. |
1431 GetSourceBreakpoint(target_function, breakpoint_pos); | 1574 if (FLAG_verbose_debug && !func.IsNull()) { |
1432 if (source_bpt != NULL) { | 1575 intptr_t line_number; |
1433 // A source breakpoint for this location already exists. | 1576 script.GetTokenLocation(token_pos, &line_number, NULL); |
1434 return source_bpt; | 1577 OS::Print("Registering pending breakpoint for " |
1435 } | 1578 "uncompiled function '%s' at line %" Pd "\n", |
1436 source_bpt = new SourceBreakpoint(nextId(), target_function, breakpoint_pos); | 1579 func.ToFullyQualifiedCString(), |
1437 RegisterSourceBreakpoint(source_bpt); | 1580 line_number); |
1438 if (target_function.HasCode()) { | 1581 } |
1439 MakeCodeBreakpointsAt(target_function, breakpoint_pos, source_bpt); | 1582 SourceBreakpoint* bpt = GetSourceBreakpoint(script, token_pos); |
1440 } | 1583 if (bpt == NULL) { |
1441 if (!closure.IsNull() && closure.HasCode()) { | 1584 bpt = new SourceBreakpoint(nextId(), script, token_pos); |
1442 MakeCodeBreakpointsAt(closure, breakpoint_pos, source_bpt); | 1585 } |
1443 } | 1586 RegisterSourceBreakpoint(bpt); |
1444 source_bpt->Enable(); | 1587 bpt->Enable(); |
1445 SignalBpResolved(source_bpt); | 1588 return bpt; |
1446 return source_bpt; | 1589 } |
1447 } | 1590 |
1448 | 1591 |
1449 | |
1450 // Synchronize the enabled/disabled state of all code breakpoints | 1592 // Synchronize the enabled/disabled state of all code breakpoints |
1451 // associated with the source breakpoint bpt. | 1593 // associated with the source breakpoint bpt. |
1452 void Debugger::SyncBreakpoint(SourceBreakpoint* bpt) { | 1594 void Debugger::SyncBreakpoint(SourceBreakpoint* bpt) { |
1453 CodeBreakpoint* cbpt = code_breakpoints_; | 1595 CodeBreakpoint* cbpt = code_breakpoints_; |
1454 while (cbpt != NULL) { | 1596 while (cbpt != NULL) { |
1455 if (bpt == cbpt->src_bpt()) { | 1597 if (bpt == cbpt->src_bpt()) { |
1456 if (bpt->IsEnabled()) { | 1598 if (bpt->IsEnabled()) { |
1457 cbpt->Enable(); | 1599 cbpt->Enable(); |
1458 } else { | 1600 } else { |
1459 cbpt->Disable(); | 1601 cbpt->Disable(); |
(...skipping 10 matching lines...) Expand all Loading... |
1470 const Function& closure_func = | 1612 const Function& closure_func = |
1471 Function::Handle(target_function.ImplicitClosureFunction()); | 1613 Function::Handle(target_function.ImplicitClosureFunction()); |
1472 InstrumentForStepping(closure_func); | 1614 InstrumentForStepping(closure_func); |
1473 } | 1615 } |
1474 } | 1616 } |
1475 | 1617 |
1476 | 1618 |
1477 SourceBreakpoint* Debugger::SetBreakpointAtEntry( | 1619 SourceBreakpoint* Debugger::SetBreakpointAtEntry( |
1478 const Function& target_function) { | 1620 const Function& target_function) { |
1479 ASSERT(!target_function.IsNull()); | 1621 ASSERT(!target_function.IsNull()); |
1480 return SetBreakpoint(target_function, | 1622 const Script& script = Script::Handle(target_function.script()); |
1481 target_function.token_pos(), | 1623 return SetBreakpoint(script, target_function.token_pos()); |
1482 target_function.end_token_pos()); | |
1483 } | 1624 } |
1484 | 1625 |
1485 | 1626 |
1486 SourceBreakpoint* Debugger::SetBreakpointAtLine(const String& script_url, | 1627 SourceBreakpoint* Debugger::SetBreakpointAtLine(const String& script_url, |
1487 intptr_t line_number) { | 1628 intptr_t line_number) { |
1488 Library& lib = Library::Handle(isolate_); | 1629 Library& lib = Library::Handle(isolate_); |
1489 Script& script = Script::Handle(isolate_); | 1630 Script& script = Script::Handle(isolate_); |
1490 const GrowableObjectArray& libs = | 1631 const GrowableObjectArray& libs = |
1491 GrowableObjectArray::Handle(isolate_->object_store()->libraries()); | 1632 GrowableObjectArray::Handle(isolate_->object_store()->libraries()); |
1492 for (intptr_t i = 0; i < libs.Length(); i++) { | 1633 for (intptr_t i = 0; i < libs.Length(); i++) { |
(...skipping 13 matching lines...) Expand all Loading... |
1506 intptr_t first_token_idx, last_token_idx; | 1647 intptr_t first_token_idx, last_token_idx; |
1507 script.TokenRangeAtLine(line_number, &first_token_idx, &last_token_idx); | 1648 script.TokenRangeAtLine(line_number, &first_token_idx, &last_token_idx); |
1508 if (first_token_idx < 0) { | 1649 if (first_token_idx < 0) { |
1509 // Script does not contain the given line number. | 1650 // Script does not contain the given line number. |
1510 if (FLAG_verbose_debug) { | 1651 if (FLAG_verbose_debug) { |
1511 OS::Print("Script '%s' does not contain line number %" Pd "\n", | 1652 OS::Print("Script '%s' does not contain line number %" Pd "\n", |
1512 script_url.ToCString(), line_number); | 1653 script_url.ToCString(), line_number); |
1513 } | 1654 } |
1514 return NULL; | 1655 return NULL; |
1515 } else if (last_token_idx < 0) { | 1656 } else if (last_token_idx < 0) { |
1516 // Line does not contain any tokens. first_token_index is the first | 1657 // Line does not contain any tokens. |
1517 // token after the given line. We check whether that token is | |
1518 // part of a function. | |
1519 last_token_idx = first_token_idx; | |
1520 } | |
1521 | |
1522 Function& func = Function::Handle(isolate_); | |
1523 while (first_token_idx <= last_token_idx) { | |
1524 func = lib.LookupFunctionInScript(script, first_token_idx); | |
1525 if (!func.IsNull()) { | |
1526 break; | |
1527 } | |
1528 first_token_idx++; | |
1529 } | |
1530 if (func.IsNull()) { | |
1531 if (FLAG_verbose_debug) { | 1658 if (FLAG_verbose_debug) { |
1532 OS::Print("No executable code at line %" Pd " in '%s'\n", | 1659 OS::Print("No executable code at line %" Pd " in '%s'\n", |
1533 line_number, script_url.ToCString()); | 1660 line_number, script_url.ToCString()); |
1534 } | 1661 } |
1535 return NULL; | 1662 return NULL; |
1536 } | 1663 } |
1537 if (last_token_idx < 0) { | 1664 |
1538 // The token at first_token_index is past the requested source line. | 1665 SourceBreakpoint* bpt = NULL; |
1539 // Set the breakpoint at the closest position after that line. | 1666 ASSERT(first_token_idx <= last_token_idx); |
1540 last_token_idx = func.end_token_pos(); | 1667 while ((bpt == NULL) && (first_token_idx <= last_token_idx)) { |
| 1668 bpt = SetBreakpoint(script, first_token_idx); |
| 1669 first_token_idx++; |
1541 } | 1670 } |
1542 return SetBreakpoint(func, first_token_idx, last_token_idx); | 1671 if ((bpt == NULL) && FLAG_verbose_debug) { |
| 1672 OS::Print("No executable code at line %" Pd " in '%s'\n", |
| 1673 line_number, script_url.ToCString()); |
| 1674 } |
| 1675 return bpt; |
1543 } | 1676 } |
1544 | 1677 |
1545 | 1678 |
1546 intptr_t Debugger::CacheObject(const Object& obj) { | 1679 intptr_t Debugger::CacheObject(const Object& obj) { |
1547 ASSERT(obj_cache_ != NULL); | 1680 ASSERT(obj_cache_ != NULL); |
1548 return obj_cache_->AddObject(obj); | 1681 return obj_cache_->AddObject(obj); |
1549 } | 1682 } |
1550 | 1683 |
1551 | 1684 |
1552 bool Debugger::IsValidObjectId(intptr_t obj_id) { | 1685 bool Debugger::IsValidObjectId(intptr_t obj_id) { |
(...skipping 10 matching lines...) Expand all Loading... |
1563 // TODO(hausner): Merge some of this functionality with the code in | 1696 // TODO(hausner): Merge some of this functionality with the code in |
1564 // dart_api_impl.cc. | 1697 // dart_api_impl.cc. |
1565 RawObject* Debugger::GetInstanceField(const Class& cls, | 1698 RawObject* Debugger::GetInstanceField(const Class& cls, |
1566 const String& field_name, | 1699 const String& field_name, |
1567 const Instance& object) { | 1700 const Instance& object) { |
1568 const Function& getter_func = | 1701 const Function& getter_func = |
1569 Function::Handle(cls.LookupGetterFunction(field_name)); | 1702 Function::Handle(cls.LookupGetterFunction(field_name)); |
1570 ASSERT(!getter_func.IsNull()); | 1703 ASSERT(!getter_func.IsNull()); |
1571 | 1704 |
1572 Object& result = Object::Handle(); | 1705 Object& result = Object::Handle(); |
1573 LongJump* base = isolate_->long_jump_base(); | |
1574 LongJump jump; | |
1575 isolate_->set_long_jump_base(&jump); | |
1576 bool saved_ignore_flag = ignore_breakpoints_; | 1706 bool saved_ignore_flag = ignore_breakpoints_; |
1577 ignore_breakpoints_ = true; | 1707 ignore_breakpoints_ = true; |
| 1708 |
| 1709 LongJumpScope jump; |
1578 if (setjmp(*jump.Set()) == 0) { | 1710 if (setjmp(*jump.Set()) == 0) { |
1579 const Array& args = Array::Handle(Array::New(1)); | 1711 const Array& args = Array::Handle(Array::New(1)); |
1580 args.SetAt(0, object); | 1712 args.SetAt(0, object); |
1581 result = DartEntry::InvokeFunction(getter_func, args); | 1713 result = DartEntry::InvokeFunction(getter_func, args); |
1582 } else { | 1714 } else { |
1583 result = isolate_->object_store()->sticky_error(); | 1715 result = isolate_->object_store()->sticky_error(); |
1584 } | 1716 } |
1585 ignore_breakpoints_ = saved_ignore_flag; | 1717 ignore_breakpoints_ = saved_ignore_flag; |
1586 isolate_->set_long_jump_base(base); | |
1587 return result.raw(); | 1718 return result.raw(); |
1588 } | 1719 } |
1589 | 1720 |
1590 | 1721 |
1591 RawObject* Debugger::GetStaticField(const Class& cls, | 1722 RawObject* Debugger::GetStaticField(const Class& cls, |
1592 const String& field_name) { | 1723 const String& field_name) { |
1593 const Field& fld = Field::Handle(cls.LookupStaticField(field_name)); | 1724 const Field& fld = Field::Handle(cls.LookupStaticField(field_name)); |
1594 if (!fld.IsNull()) { | 1725 if (!fld.IsNull()) { |
1595 // Return the value in the field if it has been initialized already. | 1726 // Return the value in the field if it has been initialized already. |
1596 const Instance& value = Instance::Handle(fld.value()); | 1727 const Instance& value = Instance::Handle(fld.value()); |
1597 ASSERT(value.raw() != Object::transition_sentinel().raw()); | 1728 ASSERT(value.raw() != Object::transition_sentinel().raw()); |
1598 if (value.raw() != Object::sentinel().raw()) { | 1729 if (value.raw() != Object::sentinel().raw()) { |
1599 return value.raw(); | 1730 return value.raw(); |
1600 } | 1731 } |
1601 } | 1732 } |
1602 // There is no field or the field has not been initialized yet. | 1733 // There is no field or the field has not been initialized yet. |
1603 // We must have a getter. Run the getter. | 1734 // We must have a getter. Run the getter. |
1604 const Function& getter_func = | 1735 const Function& getter_func = |
1605 Function::Handle(cls.LookupGetterFunction(field_name)); | 1736 Function::Handle(cls.LookupGetterFunction(field_name)); |
1606 ASSERT(!getter_func.IsNull()); | 1737 ASSERT(!getter_func.IsNull()); |
1607 if (getter_func.IsNull()) { | 1738 if (getter_func.IsNull()) { |
1608 return Object::null(); | 1739 return Object::null(); |
1609 } | 1740 } |
1610 | 1741 |
1611 Object& result = Object::Handle(); | 1742 Object& result = Object::Handle(); |
1612 LongJump* base = isolate_->long_jump_base(); | |
1613 LongJump jump; | |
1614 isolate_->set_long_jump_base(&jump); | |
1615 bool saved_ignore_flag = ignore_breakpoints_; | 1743 bool saved_ignore_flag = ignore_breakpoints_; |
1616 ignore_breakpoints_ = true; | 1744 ignore_breakpoints_ = true; |
| 1745 LongJumpScope jump; |
1617 if (setjmp(*jump.Set()) == 0) { | 1746 if (setjmp(*jump.Set()) == 0) { |
1618 result = DartEntry::InvokeFunction(getter_func, Object::empty_array()); | 1747 result = DartEntry::InvokeFunction(getter_func, Object::empty_array()); |
1619 } else { | 1748 } else { |
1620 result = isolate_->object_store()->sticky_error(); | 1749 result = isolate_->object_store()->sticky_error(); |
1621 } | 1750 } |
1622 ignore_breakpoints_ = saved_ignore_flag; | 1751 ignore_breakpoints_ = saved_ignore_flag; |
1623 isolate_->set_long_jump_base(base); | |
1624 return result.raw(); | 1752 return result.raw(); |
1625 } | 1753 } |
1626 | 1754 |
1627 | 1755 |
1628 RawArray* Debugger::GetInstanceFields(const Instance& obj) { | 1756 RawArray* Debugger::GetInstanceFields(const Instance& obj) { |
1629 Class& cls = Class::Handle(obj.clazz()); | 1757 Class& cls = Class::Handle(obj.clazz()); |
1630 Array& fields = Array::Handle(); | 1758 Array& fields = Array::Handle(); |
1631 Field& field = Field::Handle(); | 1759 Field& field = Field::Handle(); |
1632 const GrowableObjectArray& field_list = | 1760 const GrowableObjectArray& field_list = |
1633 GrowableObjectArray::Handle(GrowableObjectArray::New(8)); | 1761 GrowableObjectArray::Handle(GrowableObjectArray::New(8)); |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1752 cbpt = cbpt->next(); | 1880 cbpt = cbpt->next(); |
1753 } | 1881 } |
1754 } | 1882 } |
1755 | 1883 |
1756 | 1884 |
1757 void Debugger::SetEventHandler(EventHandler* handler) { | 1885 void Debugger::SetEventHandler(EventHandler* handler) { |
1758 event_handler_ = handler; | 1886 event_handler_ = handler; |
1759 } | 1887 } |
1760 | 1888 |
1761 | 1889 |
| 1890 void Debugger::Pause(DebuggerEvent* event) { |
| 1891 ASSERT(!IsPaused()); // No recursive pausing. |
| 1892 ASSERT(obj_cache_ == NULL); |
| 1893 |
| 1894 pause_event_ = event; |
| 1895 obj_cache_ = new RemoteObjectCache(64); |
| 1896 |
| 1897 (*event_handler_)(event); |
| 1898 |
| 1899 pause_event_ = NULL; |
| 1900 obj_cache_ = NULL; // Zone allocated |
| 1901 } |
| 1902 |
| 1903 |
1762 bool Debugger::IsDebuggable(const Function& func) { | 1904 bool Debugger::IsDebuggable(const Function& func) { |
1763 RawFunction::Kind fkind = func.kind(); | 1905 RawFunction::Kind fkind = func.kind(); |
1764 if ((fkind == RawFunction::kImplicitGetter) || | 1906 if ((fkind == RawFunction::kImplicitGetter) || |
1765 (fkind == RawFunction::kImplicitSetter) || | 1907 (fkind == RawFunction::kImplicitSetter) || |
1766 (fkind == RawFunction::kImplicitStaticFinalGetter) || | 1908 (fkind == RawFunction::kImplicitStaticFinalGetter) || |
1767 (fkind == RawFunction::kStaticInitializer) || | 1909 (fkind == RawFunction::kStaticInitializer) || |
1768 (fkind == RawFunction::kMethodExtractor) || | 1910 (fkind == RawFunction::kMethodExtractor) || |
1769 (fkind == RawFunction::kNoSuchMethodDispatcher) || | 1911 (fkind == RawFunction::kNoSuchMethodDispatcher) || |
1770 (fkind == RawFunction::kInvokeFieldDispatcher)) { | 1912 (fkind == RawFunction::kInvokeFieldDispatcher)) { |
1771 return false; | 1913 return false; |
1772 } | 1914 } |
1773 const Class& cls = Class::Handle(func.Owner()); | 1915 const Class& cls = Class::Handle(func.Owner()); |
1774 const Library& lib = Library::Handle(cls.library()); | 1916 const Library& lib = Library::Handle(cls.library()); |
1775 return lib.IsDebuggable(); | 1917 return lib.IsDebuggable(); |
1776 } | 1918 } |
1777 | 1919 |
1778 | 1920 |
1779 void Debugger::SignalPausedEvent(ActivationFrame* top_frame, | 1921 void Debugger::SignalPausedEvent(ActivationFrame* top_frame, |
1780 SourceBreakpoint* bpt) { | 1922 SourceBreakpoint* bpt) { |
1781 resume_action_ = kContinue; | 1923 resume_action_ = kContinue; |
1782 isolate_->set_single_step(false); | 1924 isolate_->set_single_step(false); |
1783 ASSERT(!in_event_notification_); | 1925 ASSERT(!IsPaused()); |
1784 ASSERT(obj_cache_ == NULL); | 1926 ASSERT(obj_cache_ == NULL); |
1785 in_event_notification_ = true; | |
1786 obj_cache_ = new RemoteObjectCache(64); | |
1787 DebuggerEvent event(kBreakpointReached); | 1927 DebuggerEvent event(kBreakpointReached); |
1788 event.top_frame = top_frame; | 1928 event.top_frame = top_frame; |
1789 event.breakpoint = bpt; | 1929 event.breakpoint = bpt; |
1790 (*event_handler_)(&event); | 1930 Pause(&event); |
1791 in_event_notification_ = false; | |
1792 obj_cache_ = NULL; // Remote object cache is zone allocated. | |
1793 } | 1931 } |
1794 | 1932 |
1795 | 1933 |
1796 void Debugger::SingleStepCallback() { | 1934 void Debugger::SingleStepCallback() { |
1797 ASSERT(resume_action_ == kSingleStep); | 1935 ASSERT(resume_action_ == kSingleStep); |
1798 ASSERT(isolate_->single_step()); | 1936 ASSERT(isolate_->single_step()); |
1799 // We can't get here unless the debugger event handler enabled | 1937 // We can't get here unless the debugger event handler enabled |
1800 // single stepping. | 1938 // single stepping. |
1801 ASSERT(event_handler_ != NULL); | 1939 ASSERT(event_handler_ != NULL); |
1802 // Don't pause recursively. | 1940 // Don't pause recursively. |
1803 if (in_event_notification_) return; | 1941 if (IsPaused()) return; |
1804 | 1942 |
1805 // Check whether we are in a Dart function that the user is | 1943 // Check whether we are in a Dart function that the user is |
1806 // interested in. | 1944 // interested in. |
1807 ActivationFrame* frame = TopDartFrame(); | 1945 ActivationFrame* frame = TopDartFrame(); |
1808 ASSERT(frame != NULL); | 1946 ASSERT(frame != NULL); |
1809 const Function& func = frame->function(); | 1947 const Function& func = frame->function(); |
1810 if (!IsDebuggable(func)) { | 1948 if (!IsDebuggable(func)) { |
1811 return; | 1949 return; |
1812 } | 1950 } |
1813 if (frame->TokenPos() == Scanner::kDummyTokenIndex) { | 1951 if (frame->TokenPos() == Scanner::kDummyTokenIndex) { |
1814 return; | 1952 return; |
1815 } | 1953 } |
1816 | 1954 |
1817 if (FLAG_verbose_debug) { | 1955 if (FLAG_verbose_debug) { |
1818 OS::Print(">>> single step break at %s:%" Pd " (func %s token %" Pd ")\n", | 1956 OS::Print(">>> single step break at %s:%" Pd " (func %s token %" Pd ")\n", |
1819 String::Handle(frame->SourceUrl()).ToCString(), | 1957 String::Handle(frame->SourceUrl()).ToCString(), |
1820 frame->LineNumber(), | 1958 frame->LineNumber(), |
1821 String::Handle(frame->QualifiedFunctionName()).ToCString(), | 1959 String::Handle(frame->QualifiedFunctionName()).ToCString(), |
1822 frame->TokenPos()); | 1960 frame->TokenPos()); |
1823 } | 1961 } |
1824 | 1962 |
| 1963 ASSERT(stack_trace_ == NULL); |
1825 stack_trace_ = CollectStackTrace(); | 1964 stack_trace_ = CollectStackTrace(); |
1826 SignalPausedEvent(frame, NULL); | 1965 SignalPausedEvent(frame, NULL); |
1827 | 1966 |
1828 RemoveInternalBreakpoints(); | 1967 RemoveInternalBreakpoints(); |
1829 if (resume_action_ == kStepOver) { | 1968 if (resume_action_ == kStepOver) { |
1830 InstrumentForStepping(func); | 1969 InstrumentForStepping(func); |
1831 } else if (resume_action_ == kStepOut) { | 1970 } else if (resume_action_ == kStepOut) { |
1832 if (stack_trace_->Length() > 1) { | 1971 if (stack_trace_->Length() > 1) { |
1833 ActivationFrame* caller_frame = stack_trace_->FrameAt(1); | 1972 ActivationFrame* caller_frame = stack_trace_->FrameAt(1); |
1834 InstrumentForStepping(caller_frame->function()); | 1973 InstrumentForStepping(caller_frame->function()); |
1835 } | 1974 } |
1836 } | 1975 } |
1837 stack_trace_ = NULL; | 1976 stack_trace_ = NULL; |
1838 } | 1977 } |
1839 | 1978 |
1840 | 1979 |
1841 void Debugger::SignalBpReached() { | 1980 void Debugger::SignalBpReached() { |
1842 // We ignore this breakpoint when the VM is executing code invoked | 1981 // We ignore this breakpoint when the VM is executing code invoked |
1843 // by the debugger to evaluate variables values, or when we see a nested | 1982 // by the debugger to evaluate variables values, or when we see a nested |
1844 // breakpoint or exception event. | 1983 // breakpoint or exception event. |
1845 if (ignore_breakpoints_ || in_event_notification_) { | 1984 if (ignore_breakpoints_ || IsPaused()) { |
1846 return; | 1985 return; |
1847 } | 1986 } |
1848 DebuggerStackTrace* stack_trace = CollectStackTrace(); | 1987 DebuggerStackTrace* stack_trace = CollectStackTrace(); |
1849 ASSERT(stack_trace->Length() > 0); | 1988 ASSERT(stack_trace->Length() > 0); |
1850 ActivationFrame* top_frame = stack_trace->FrameAt(0); | 1989 ActivationFrame* top_frame = stack_trace->FrameAt(0); |
1851 ASSERT(top_frame != NULL); | 1990 ASSERT(top_frame != NULL); |
1852 CodeBreakpoint* bpt = GetCodeBreakpoint(top_frame->pc()); | 1991 CodeBreakpoint* bpt = GetCodeBreakpoint(top_frame->pc()); |
1853 ASSERT(bpt != NULL); | 1992 ASSERT(bpt != NULL); |
1854 | 1993 |
1855 bool report_bp = true; | 1994 bool report_bp = true; |
1856 if (bpt->IsInternal() && !IsDebuggable(top_frame->function())) { | 1995 if (bpt->IsInternal() && !IsDebuggable(top_frame->function())) { |
1857 report_bp = false; | 1996 report_bp = false; |
1858 } | 1997 } |
1859 if (FLAG_verbose_debug) { | 1998 if (FLAG_verbose_debug) { |
1860 OS::Print(">>> %s %s breakpoint at %s:%" Pd " " | 1999 OS::Print(">>> %s %s breakpoint at %s:%" Pd " " |
1861 "(token %" Pd ") (address %#" Px ")\n", | 2000 "(token %" Pd ") (address %#" Px ")\n", |
1862 report_bp ? "hit" : "ignore", | 2001 report_bp ? "hit" : "ignore", |
1863 bpt->IsInternal() ? "internal" : "user", | 2002 bpt->IsInternal() ? "internal" : "user", |
1864 String::Handle(bpt->SourceUrl()).ToCString(), | 2003 String::Handle(bpt->SourceUrl()).ToCString(), |
1865 bpt->LineNumber(), | 2004 bpt->LineNumber(), |
1866 bpt->token_pos(), | 2005 bpt->token_pos(), |
1867 top_frame->pc()); | 2006 top_frame->pc()); |
1868 } | 2007 } |
1869 | 2008 |
1870 if (report_bp && (event_handler_ != NULL)) { | 2009 if (report_bp && (event_handler_ != NULL)) { |
| 2010 ASSERT(stack_trace_ == NULL); |
1871 stack_trace_ = stack_trace; | 2011 stack_trace_ = stack_trace; |
1872 SignalPausedEvent(top_frame, bpt->src_bpt_); | 2012 SignalPausedEvent(top_frame, bpt->src_bpt_); |
1873 stack_trace_ = NULL; | 2013 stack_trace_ = NULL; |
1874 } | 2014 } |
1875 | 2015 |
1876 Function& func_to_instrument = Function::Handle(); | 2016 Function& func_to_instrument = Function::Handle(); |
| 2017 if ((resume_action_ == kStepOver) && |
| 2018 (bpt->breakpoint_kind_ == PcDescriptors::kReturn)) { |
| 2019 resume_action_ = kStepOut; |
| 2020 } |
1877 if (resume_action_ == kStepOver) { | 2021 if (resume_action_ == kStepOver) { |
1878 if (bpt->breakpoint_kind_ == PcDescriptors::kReturn) { | 2022 ASSERT(bpt->breakpoint_kind_ != PcDescriptors::kReturn); |
1879 // Step over return is converted into a single step so we break at | 2023 func_to_instrument = bpt->function(); |
1880 // the caller. | |
1881 SetSingleStep(); | |
1882 } else { | |
1883 func_to_instrument = bpt->function(); | |
1884 } | |
1885 } else if (resume_action_ == kStepOut) { | 2024 } else if (resume_action_ == kStepOut) { |
1886 if (stack_trace->Length() > 1) { | 2025 if (stack_trace->Length() > 1) { |
1887 ActivationFrame* caller_frame = stack_trace->FrameAt(1); | 2026 ActivationFrame* caller_frame = stack_trace->FrameAt(1); |
1888 func_to_instrument = caller_frame->function().raw(); | 2027 func_to_instrument = caller_frame->function().raw(); |
1889 } | 2028 } |
1890 } else { | 2029 } else { |
1891 ASSERT((resume_action_ == kContinue) || (resume_action_ == kSingleStep)); | 2030 ASSERT((resume_action_ == kContinue) || (resume_action_ == kSingleStep)); |
1892 // Nothing to do here. Any potential instrumentation will be removed | 2031 // Nothing to do here. Any potential instrumentation will be removed |
1893 // below. Single stepping is handled by the single step callback. | 2032 // below. Single stepping is handled by the single step callback. |
1894 } | 2033 } |
(...skipping 17 matching lines...) Expand all Loading... |
1912 // This port will be used as a unique ID to represet the isolate in the | 2051 // This port will be used as a unique ID to represet the isolate in the |
1913 // debugger wire protocol messages. | 2052 // debugger wire protocol messages. |
1914 isolate_id_ = isolate->main_port(); | 2053 isolate_id_ = isolate->main_port(); |
1915 initialized_ = true; | 2054 initialized_ = true; |
1916 | 2055 |
1917 // Signal isolate creation event. | 2056 // Signal isolate creation event. |
1918 SignalIsolateEvent(Debugger::kIsolateCreated); | 2057 SignalIsolateEvent(Debugger::kIsolateCreated); |
1919 } | 2058 } |
1920 | 2059 |
1921 | 2060 |
1922 static RawFunction* GetOriginalFunction(const Function& func) { | 2061 // Return innermost closure contained in 'function' that contains |
1923 if (func.IsClosureFunction()) { | 2062 // the given token position. |
1924 // Local functions (closures) in mixin functions do not have | 2063 RawFunction* Debugger::FindInnermostClosure(const Function& function, |
1925 // an original function they were cloned from. | 2064 intptr_t token_pos) { |
1926 // Note: this is a problem when a breakpoint is set in a mixed-in | 2065 const Class& owner = Class::Handle(isolate_, function.Owner()); |
1927 // closure. The breakpoint is linked to the closure attached to the | 2066 if (owner.closures() == GrowableObjectArray::null()) { |
1928 // mixin application class, not the mixin class. When the same | 2067 return Function::null(); |
1929 // closure is compiled for another mixin application class, we | |
1930 // don't find the breakpoint since we'll be looking in the | |
1931 // mixin class. | |
1932 return func.raw(); | |
1933 } | 2068 } |
1934 const Class& origin_class = Class::Handle(func.origin()); | 2069 // Note that we need to check that the closure is in the same |
1935 if (origin_class.is_patch()) { | 2070 // script as the outer function. We could have closures originating |
1936 // Patched functions from patch classes are removed from the | 2071 // in mixin classes whose source code is contained in a different |
1937 // function array of the patch class, so we will not find an | 2072 // script. |
1938 // original function object. | 2073 const Script& outer_origin = Script::Handle(isolate_, function.script()); |
1939 return func.raw(); | 2074 const GrowableObjectArray& closures = |
1940 } | 2075 GrowableObjectArray::Handle(isolate_, owner.closures()); |
1941 const Array& functions = Array::Handle(origin_class.functions()); | 2076 const intptr_t num_closures = closures.Length(); |
1942 Object& orig_func = Object::Handle(); | 2077 Function& closure = Function::Handle(isolate_); |
1943 for (intptr_t i = 0; i < functions.Length(); i++) { | 2078 Function& best_fit = Function::Handle(isolate_); |
1944 orig_func = functions.At(i); | 2079 for (intptr_t i = 0; i < num_closures; i++) { |
1945 // Function names are symbols, so we can compare the raw pointers. | 2080 closure ^= closures.At(i); |
1946 if (func.name() == Function::Cast(orig_func).name()) { | 2081 if ((function.token_pos() < closure.token_pos()) && |
1947 return Function::Cast(orig_func).raw(); | 2082 (closure.end_token_pos() < function.end_token_pos()) && |
| 2083 (closure.token_pos() <= token_pos) && |
| 2084 (token_pos <= closure.end_token_pos()) && |
| 2085 (closure.script() == outer_origin.raw())) { |
| 2086 SelectBestFit(&best_fit, &closure); |
1948 } | 2087 } |
1949 } | 2088 } |
1950 // Uncommon case: not a mixin function. | 2089 return best_fit.raw(); |
1951 ASSERT(!Class::Handle(func.Owner()).IsMixinApplication()); | |
1952 return func.raw(); | |
1953 } | 2090 } |
1954 | 2091 |
1955 | 2092 |
1956 void Debugger::NotifyCompilation(const Function& func) { | 2093 void Debugger::NotifyCompilation(const Function& func) { |
1957 if (src_breakpoints_ == NULL) { | 2094 if (src_breakpoints_ == NULL) { |
1958 // Return with minimal overhead if there are no breakpoints. | 2095 // Return with minimal overhead if there are no breakpoints. |
1959 return; | 2096 return; |
1960 } | 2097 } |
1961 Function& lookup_function = Function::Handle(func.raw()); | 2098 // Iterate over all source breakpoints to check whether breakpoints |
1962 if (func.IsImplicitClosureFunction()) { | 2099 // need to be set in the newly compiled function. |
1963 // If the newly compiled function is a an implicit closure (a closure that | 2100 Script& script = Script::Handle(isolate_); |
1964 // was formed by assigning a static or instance method to a function | 2101 for (SourceBreakpoint* bpt = src_breakpoints_; |
1965 // object), we need to use the closure's parent function to see whether | 2102 bpt != NULL; |
1966 // there are any breakpoints. The parent function is the actual method on | 2103 bpt = bpt->next()) { |
1967 // which the user sets breakpoints. | 2104 script = bpt->script(); |
1968 lookup_function = func.parent_function(); | 2105 if (FunctionContains(func, script, bpt->token_pos())) { |
1969 ASSERT(!lookup_function.IsNull()); | 2106 Function& inner_function = Function::Handle(isolate_); |
1970 } | 2107 inner_function = FindInnermostClosure(func, bpt->token_pos()); |
1971 if (lookup_function.Owner() != lookup_function.origin()) { | 2108 if (!inner_function.IsNull()) { |
1972 // This is a cloned function from a mixin class. If a breakpoint | 2109 // The local function of a function we just compiled cannot |
1973 // was set in this function, it is registered using the function | 2110 // be compiled already. |
1974 // of the origin class. | 2111 ASSERT(!inner_function.HasCode()); |
1975 lookup_function = GetOriginalFunction(lookup_function); | |
1976 } | |
1977 SourceBreakpoint* bpt = src_breakpoints_; | |
1978 while (bpt != NULL) { | |
1979 if (lookup_function.raw() == bpt->function()) { | |
1980 // Check if the breakpoint is inside a closure or local function | |
1981 // within the newly compiled function. Note that we must look | |
1982 // in the owner class of the function that just got compiled | |
1983 // (i.e. func), not the owner class of the function we use to | |
1984 // record the breakpoint (lookup_function). | |
1985 Class& owner = Class::Handle(func.Owner()); | |
1986 Function& closure = | |
1987 Function::Handle(owner.LookupClosureFunction(bpt->token_pos())); | |
1988 if (!closure.IsNull() && (closure.raw() != lookup_function.raw())) { | |
1989 if (FLAG_verbose_debug) { | 2112 if (FLAG_verbose_debug) { |
1990 OS::Print("Resetting pending breakpoint to function %s\n", | 2113 OS::Print("Pending BP remains unresolved in inner function '%s'\n", |
1991 closure.ToFullyQualifiedCString()); | 2114 inner_function.ToFullyQualifiedCString()); |
1992 } | 2115 } |
1993 bpt->set_function(closure); | 2116 continue; |
1994 } else { | 2117 } |
| 2118 |
| 2119 // TODO(hausner): What should we do if function is optimized? |
| 2120 // Can we deoptimize the function? |
| 2121 ASSERT(!func.HasOptimizedCode()); |
| 2122 |
| 2123 // There is no local function within func that contains the |
| 2124 // breakpoint token position. Resolve the breakpoint if necessary |
| 2125 // and set the code breakpoints. |
| 2126 if (!bpt->IsResolved()) { |
| 2127 // Resolve source breakpoint in the newly compiled function. |
| 2128 intptr_t bp_pos = ResolveBreakpointPos(func, bpt->token_pos()); |
| 2129 if (bp_pos < 0) { |
| 2130 if (FLAG_verbose_debug) { |
| 2131 OS::Print("Failed resolving breakpoint for function '%s'\n", |
| 2132 String::Handle(func.name()).ToCString()); |
| 2133 } |
| 2134 continue; |
| 2135 } |
| 2136 intptr_t requested_pos = bpt->token_pos(); |
| 2137 bpt->SetResolved(func, bp_pos); |
1995 if (FLAG_verbose_debug) { | 2138 if (FLAG_verbose_debug) { |
1996 OS::Print("Enable pending breakpoint for function '%s'\n", | 2139 OS::Print("Resolved BP %" Pd " to pos %" Pd ", line %" Pd ", " |
1997 String::Handle(lookup_function.name()).ToCString()); | 2140 "function '%s' (requested pos %" Pd ")\n", |
| 2141 bpt->id(), |
| 2142 bpt->token_pos(), |
| 2143 bpt->LineNumber(), |
| 2144 func.ToFullyQualifiedCString(), |
| 2145 requested_pos); |
1998 } | 2146 } |
1999 const Script& script= Script::Handle(func.script()); | |
2000 intptr_t first_pos, last_pos; | |
2001 script.TokenRangeAtLine(bpt->LineNumber(), &first_pos, &last_pos); | |
2002 intptr_t bp_pos = | |
2003 ResolveBreakpointPos(func, bpt->token_pos(), last_pos); | |
2004 bpt->set_token_pos(bp_pos); | |
2005 MakeCodeBreakpointsAt(func, bp_pos, bpt); | |
2006 SignalBpResolved(bpt); | 2147 SignalBpResolved(bpt); |
2007 } | 2148 } |
2008 bpt->Enable(); // Enables the code breakpoint as well. | 2149 ASSERT(bpt->IsResolved()); |
| 2150 if (FLAG_verbose_debug) { |
| 2151 OS::Print("Setting breakpoint %" Pd " at line %" Pd " for %s '%s'\n", |
| 2152 bpt->id(), |
| 2153 bpt->LineNumber(), |
| 2154 func.IsClosureFunction() ? "closure" : "function", |
| 2155 String::Handle(func.name()).ToCString()); |
| 2156 } |
| 2157 MakeCodeBreakpointsAt(func, bpt); |
2009 } | 2158 } |
2010 bpt = bpt->next(); | |
2011 } | 2159 } |
2012 } | 2160 } |
2013 | 2161 |
2014 | 2162 |
2015 CodeBreakpoint* Debugger::GetCodeBreakpoint(uword breakpoint_address) { | 2163 CodeBreakpoint* Debugger::GetCodeBreakpoint(uword breakpoint_address) { |
2016 CodeBreakpoint* bpt = code_breakpoints_; | 2164 CodeBreakpoint* bpt = code_breakpoints_; |
2017 while (bpt != NULL) { | 2165 while (bpt != NULL) { |
2018 if (bpt->pc() == breakpoint_address) { | 2166 if (bpt->pc() == breakpoint_address) { |
2019 return bpt; | 2167 return bpt; |
2020 } | 2168 } |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2094 temp_bpt->Disable(); | 2242 temp_bpt->Disable(); |
2095 delete temp_bpt; | 2243 delete temp_bpt; |
2096 } else { | 2244 } else { |
2097 prev_bpt = curr_bpt; | 2245 prev_bpt = curr_bpt; |
2098 curr_bpt = curr_bpt->next(); | 2246 curr_bpt = curr_bpt->next(); |
2099 } | 2247 } |
2100 } | 2248 } |
2101 } | 2249 } |
2102 | 2250 |
2103 | 2251 |
2104 SourceBreakpoint* Debugger::GetSourceBreakpoint(const Function& func, | 2252 SourceBreakpoint* Debugger::GetSourceBreakpoint(const Script& script, |
2105 intptr_t token_pos) { | 2253 intptr_t token_pos) { |
2106 SourceBreakpoint* bpt = src_breakpoints_; | 2254 SourceBreakpoint* bpt = src_breakpoints_; |
2107 while (bpt != NULL) { | 2255 while (bpt != NULL) { |
2108 if ((bpt->function() == func.raw()) && | 2256 if ((bpt->script_ == script.raw()) && (bpt->token_pos_ == token_pos)) { |
2109 (bpt->token_pos() == token_pos)) { | |
2110 return bpt; | 2257 return bpt; |
2111 } | 2258 } |
2112 bpt = bpt->next(); | 2259 bpt = bpt->next(); |
2113 } | 2260 } |
2114 return NULL; | 2261 return NULL; |
2115 } | 2262 } |
2116 | 2263 |
2117 | 2264 |
2118 SourceBreakpoint* Debugger::GetBreakpointById(intptr_t id) { | 2265 SourceBreakpoint* Debugger::GetBreakpointById(intptr_t id) { |
2119 SourceBreakpoint* bpt = src_breakpoints_; | 2266 SourceBreakpoint* bpt = src_breakpoints_; |
(...skipping 14 matching lines...) Expand all Loading... |
2134 } | 2281 } |
2135 | 2282 |
2136 | 2283 |
2137 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { | 2284 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { |
2138 ASSERT(bpt->next() == NULL); | 2285 ASSERT(bpt->next() == NULL); |
2139 bpt->set_next(code_breakpoints_); | 2286 bpt->set_next(code_breakpoints_); |
2140 code_breakpoints_ = bpt; | 2287 code_breakpoints_ = bpt; |
2141 } | 2288 } |
2142 | 2289 |
2143 } // namespace dart | 2290 } // namespace dart |
OLD | NEW |