| 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_patcher.h" | 8 #include "vm/code_patcher.h" |
| 8 #include "vm/compiler.h" | 9 #include "vm/compiler.h" |
| 9 #include "vm/flags.h" | 10 #include "vm/flags.h" |
| 10 #include "vm/globals.h" | 11 #include "vm/globals.h" |
| 11 #include "vm/object.h" | 12 #include "vm/object.h" |
| 12 #include "vm/object_store.h" | 13 #include "vm/object_store.h" |
| 13 #include "vm/os.h" | 14 #include "vm/os.h" |
| 15 #include "vm/stack_frame.h" |
| 14 #include "vm/stub_code.h" | 16 #include "vm/stub_code.h" |
| 15 #include "vm/visitor.h" | 17 #include "vm/visitor.h" |
| 16 | 18 |
| 17 | 19 |
| 18 namespace dart { | 20 namespace dart { |
| 19 | 21 |
| 20 DEFINE_FLAG(bool, debugger, false, "Debug breakpoint at main"); | 22 DEFINE_FLAG(bool, debugger, false, "Debug breakpoint at main"); |
| 21 DEFINE_FLAG(charp, bpt, NULL, "Debug breakpoint at <func>"); | 23 DEFINE_FLAG(charp, bpt, NULL, "Debug breakpoint at <func>"); |
| 22 | 24 |
| 23 | 25 |
| 24 class Breakpoint { | 26 |
| 25 public: | 27 Breakpoint::Breakpoint(const Function& func, intptr_t pc_desc_index) |
| 26 Breakpoint(const Function& func, intptr_t pc_desc_index, uword pc) | 28 : function_(func.raw()), |
| 27 : function_(func.raw()), | 29 pc_desc_index_(pc_desc_index), |
| 28 pc_desc_index_(pc_desc_index), | 30 pc_(0), |
| 29 pc_(pc), | 31 line_number_(-1), |
| 30 next_(NULL) { | 32 next_(NULL) { |
| 33 Code& code = Code::Handle(func.code()); |
| 34 ASSERT(!code.IsNull()); // Function must be compiled. |
| 35 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
| 36 ASSERT(pc_desc_index < desc.Length()); |
| 37 this->token_index_ = desc.TokenIndex(pc_desc_index); |
| 38 ASSERT(this->token_index_ > 0); |
| 39 this->pc_ = desc.PC(pc_desc_index); |
| 40 ASSERT(this->pc_ != 0); |
| 41 } |
| 42 |
| 43 |
| 44 RawScript* Breakpoint::SourceCode() { |
| 45 const Function& func = Function::Handle(this->function_); |
| 46 const Class& cls = Class::Handle(func.owner()); |
| 47 return cls.script(); |
| 48 } |
| 49 |
| 50 |
| 51 RawString* Breakpoint::SourceUrl() { |
| 52 const Script& script = Script::Handle(this->SourceCode()); |
| 53 return script.url(); |
| 54 } |
| 55 |
| 56 |
| 57 intptr_t Breakpoint::LineNumber() { |
| 58 // Compute line number lazily since it causes scanning of the script. |
| 59 if (this->line_number_ < 0) { |
| 60 const Script& script = Script::Handle(this->SourceCode()); |
| 61 intptr_t ignore_column; |
| 62 script.GetTokenLocation(this->token_index_, |
| 63 &this->line_number_, &ignore_column); |
| 31 } | 64 } |
| 65 return this->line_number_; |
| 66 } |
| 32 | 67 |
| 33 RawFunction* function() const { return function_; } | |
| 34 uword pc() const { return pc_; } | |
| 35 | 68 |
| 36 void set_next(Breakpoint* value) { next_ = value; } | 69 void Breakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
| 37 Breakpoint* next() const { return this->next_; } | 70 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); |
| 71 } |
| 38 | 72 |
| 39 void VisitObjectPointers(ObjectPointerVisitor* visitor) { | 73 |
| 40 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); | 74 ActivationFrame::ActivationFrame(uword pc) |
| 75 : pc_(pc), |
| 76 function_(Function::null()), |
| 77 token_index_(-1), |
| 78 line_number_(-1) { |
| 79 } |
| 80 |
| 81 |
| 82 RawFunction* ActivationFrame::DartFunction() { |
| 83 if (function_ == Function::null()) { |
| 84 ASSERT(Isolate::Current() != NULL); |
| 85 CodeIndexTable* code_index_table = Isolate::Current()->code_index_table(); |
| 86 ASSERT(code_index_table != NULL); |
| 87 function_ = code_index_table->LookupFunction(pc_); |
| 41 } | 88 } |
| 89 return function_; |
| 90 } |
| 42 | 91 |
| 43 private: | 92 |
| 44 RawFunction* function_; | 93 RawString* ActivationFrame::SourceUrl() { |
| 45 intptr_t pc_desc_index_; | 94 const Script& script = Script::Handle(SourceScript()); |
| 46 uword pc_; | 95 return script.url(); |
| 47 Breakpoint* next_; | 96 } |
| 48 DISALLOW_COPY_AND_ASSIGN(Breakpoint); | 97 |
| 49 }; | 98 |
| 99 RawScript* ActivationFrame::SourceScript() { |
| 100 const Function& func = Function::Handle(DartFunction()); |
| 101 const Class& cls = Class::Handle(func.owner()); |
| 102 return cls.script(); |
| 103 } |
| 104 |
| 105 |
| 106 intptr_t ActivationFrame::TokenIndex() { |
| 107 if (token_index_ < 0) { |
| 108 const Function& func = Function::Handle(DartFunction()); |
| 109 Code& code = Code::Handle(func.code()); |
| 110 ASSERT(!code.IsNull()); |
| 111 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
| 112 for (int i = 0; i < desc.Length(); i++) { |
| 113 if (desc.PC(i) == pc_) { |
| 114 token_index_ = desc.TokenIndex(i); |
| 115 break; |
| 116 } |
| 117 } |
| 118 ASSERT(token_index_ >= 0); |
| 119 } |
| 120 return token_index_; |
| 121 } |
| 122 |
| 123 |
| 124 intptr_t ActivationFrame::LineNumber() { |
| 125 // Compute line number lazily since it causes scanning of the script. |
| 126 if (line_number_ < 0) { |
| 127 const Script& script = Script::Handle(SourceScript()); |
| 128 intptr_t ignore_column; |
| 129 script.GetTokenLocation(TokenIndex(), &line_number_, &ignore_column); |
| 130 } |
| 131 return line_number_; |
| 132 } |
| 133 |
| 134 |
| 135 RawArray* ActivationFrame::Variables() { |
| 136 UNIMPLEMENTED(); |
| 137 } |
| 138 |
| 139 |
| 140 RawInstance* ActivationFrame::Value(const String& variable_name) { |
| 141 UNIMPLEMENTED(); |
| 142 } |
| 143 |
| 144 |
| 145 const char* ActivationFrame::ToCString() { |
| 146 const char* kFormat = "Function: '%s%s%s' url: '%s' line: %d"; |
| 147 |
| 148 Function& func = Function::Handle(DartFunction()); |
| 149 String& func_name = String::Handle(func.name()); |
| 150 Class& func_class = Class::Handle(func.owner()); |
| 151 String& class_name = String::Handle(func_class.Name()); |
| 152 String& url = String::Handle(SourceUrl()); |
| 153 intptr_t line = LineNumber(); |
| 154 |
| 155 intptr_t len = OS::SNPrint(NULL, 0, kFormat, |
| 156 class_name.ToCString(), |
| 157 func_class.IsTopLevel() ? "" : ".", |
| 158 func_name.ToCString(), |
| 159 url.ToCString(), |
| 160 line); |
| 161 len++; // String terminator. |
| 162 char* chars = reinterpret_cast<char*>( |
| 163 Isolate::Current()->current_zone()->Allocate(len)); |
| 164 OS::SNPrint(chars, len, kFormat, |
| 165 class_name.ToCString(), |
| 166 func_class.IsTopLevel() ? "" : ".", |
| 167 func_name.ToCString(), |
| 168 url.ToCString(), |
| 169 line); |
| 170 return chars; |
| 171 } |
| 172 |
| 173 |
| 174 void StackTrace::AddActivation(ActivationFrame* frame) { |
| 175 this->trace_.Add(frame); |
| 176 } |
| 50 | 177 |
| 51 | 178 |
| 52 Debugger::Debugger() | 179 Debugger::Debugger() |
| 53 : initialized_(false), | 180 : initialized_(false), |
| 181 bp_handler_(NULL), |
| 54 breakpoints_(NULL) { | 182 breakpoints_(NULL) { |
| 55 } | 183 } |
| 56 | 184 |
| 57 | 185 |
| 58 static RawFunction* ResolveLibraryFunction(const String& fname) { | 186 static RawFunction* ResolveLibraryFunction(const String& fname) { |
| 59 Isolate* isolate = Isolate::Current(); | 187 Isolate* isolate = Isolate::Current(); |
| 60 const Library& root_lib = | 188 const Library& root_lib = |
| 61 Library::Handle(isolate->object_store()->root_library()); | 189 Library::Handle(isolate->object_store()->root_library()); |
| 62 ASSERT(!root_lib.IsNull()); | 190 ASSERT(!root_lib.IsNull()); |
| 63 Function& function = Function::Handle(); | 191 Function& function = Function::Handle(); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 80 if (!cls.IsNull()) { | 208 if (!cls.IsNull()) { |
| 81 function = cls.LookupStaticFunction(function_name); | 209 function = cls.LookupStaticFunction(function_name); |
| 82 if (function.IsNull()) { | 210 if (function.IsNull()) { |
| 83 function = cls.LookupDynamicFunction(function_name); | 211 function = cls.LookupDynamicFunction(function_name); |
| 84 } | 212 } |
| 85 } | 213 } |
| 86 return function.raw(); | 214 return function.raw(); |
| 87 } | 215 } |
| 88 | 216 |
| 89 | 217 |
| 90 | |
| 91 void Debugger::SetBreakpointAtEntry(const String& class_name, | 218 void Debugger::SetBreakpointAtEntry(const String& class_name, |
| 92 const String& function_name) { | 219 const String& function_name) { |
| 93 Function& func = Function::Handle(); | 220 Function& func = Function::Handle(); |
| 94 if (class_name.IsNull() || (class_name.Length() == 0)) { | 221 if (class_name.IsNull() || (class_name.Length() == 0)) { |
| 95 func = ResolveLibraryFunction(function_name); | 222 func = ResolveLibraryFunction(function_name); |
| 96 } else { | 223 } else { |
| 97 func = ResolveFunction(class_name, function_name); | 224 func = ResolveFunction(class_name, function_name); |
| 98 } | 225 } |
| 99 if (func.IsNull()) { | 226 if (func.IsNull()) { |
| 100 OS::Print("could not find function '%s' in class '%s'\n", | 227 OS::Print("could not find function '%s'\n", function_name.ToCString()); |
| 101 function_name.ToCString(), class_name.ToCString()); | |
| 102 return; | 228 return; |
| 103 } | 229 } |
| 104 if (!func.HasCode()) { | 230 if (!func.HasCode()) { |
| 105 Compiler::CompileFunction(func); | 231 Compiler::CompileFunction(func); |
| 106 } | 232 } |
| 107 Code& code = Code::Handle(func.code()); | 233 Code& code = Code::Handle(func.code()); |
| 108 ASSERT(!code.IsNull()); | 234 ASSERT(!code.IsNull()); |
| 109 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); | 235 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
| 110 for (int i = 0; i < desc.Length(); i++) { | 236 for (int i = 0; i < desc.Length(); i++) { |
| 111 PcDescriptors::Kind kind = desc.DescriptorKind(i); | 237 PcDescriptors::Kind kind = desc.DescriptorKind(i); |
| 238 Breakpoint* bpt = NULL; |
| 112 if (kind == PcDescriptors::kIcCall) { | 239 if (kind == PcDescriptors::kIcCall) { |
| 113 OS::Print("patching dynamic call at %p\n", desc.PC(i)); | |
| 114 CodePatcher::PatchInstanceCallAt( | 240 CodePatcher::PatchInstanceCallAt( |
| 115 desc.PC(i), StubCode::BreakpointDynamicEntryPoint()); | 241 desc.PC(i), StubCode::BreakpointDynamicEntryPoint()); |
| 116 AddBreakpoint(new Breakpoint(func, i, desc.PC(i))); | 242 bpt = new Breakpoint(func, i); |
| 117 return; | |
| 118 } else if (kind == PcDescriptors::kOther) { | 243 } else if (kind == PcDescriptors::kOther) { |
| 119 if (CodePatcher::IsDartCall(desc.PC(i))) { | 244 if (CodePatcher::IsDartCall(desc.PC(i))) { |
| 120 OS::Print("patching static call at %p\n", desc.PC(i)); | |
| 121 CodePatcher::PatchStaticCallAt( | 245 CodePatcher::PatchStaticCallAt( |
| 122 desc.PC(i), StubCode::BreakpointStaticEntryPoint()); | 246 desc.PC(i), StubCode::BreakpointStaticEntryPoint()); |
| 123 AddBreakpoint(new Breakpoint(func, i, desc.PC(i))); | 247 bpt = new Breakpoint(func, i); |
| 124 return; | |
| 125 } | 248 } |
| 126 } | 249 } |
| 250 if (bpt != NULL) { |
| 251 OS::Print("Setting breakpoint at '%s' line %d (PC %p)\n", |
| 252 String::Handle(bpt->SourceUrl()).ToCString(), |
| 253 bpt->LineNumber(), |
| 254 bpt->pc()); |
| 255 AddBreakpoint(bpt); |
| 256 return; |
| 257 } |
| 127 } | 258 } |
| 128 OS::Print("no breakpoint location found in function '%s'\n", | 259 OS::Print("no breakpoint location found in function '%s'\n", |
| 129 function_name.ToCString()); | 260 function_name.ToCString()); |
| 130 } | 261 } |
| 131 | 262 |
| 132 | 263 |
| 133 void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) { | 264 void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
| 134 ASSERT(visitor != NULL); | 265 ASSERT(visitor != NULL); |
| 135 Breakpoint* bpt = this->breakpoints_; | 266 Breakpoint* bpt = this->breakpoints_; |
| 136 while (bpt != NULL) { | 267 while (bpt != NULL) { |
| 137 bpt->VisitObjectPointers(visitor); | 268 bpt->VisitObjectPointers(visitor); |
| 138 bpt = bpt->next(); | 269 bpt = bpt->next(); |
| 139 } | 270 } |
| 140 } | 271 } |
| 141 | 272 |
| 142 | 273 |
| 274 static void DefaultBreakpointHandler(Breakpoint* bpt, StackTrace* stack) { |
| 275 for (intptr_t i = 0; i < stack->Length(); i++) { |
| 276 OS::Print(" %d. %s\n", |
| 277 i + 1, stack->ActivationFrameAt(i)->ToCString()); |
| 278 } |
| 279 } |
| 280 |
| 281 |
| 282 void Debugger::SetBreakpointHandler(BreakpointHandler* handler) { |
| 283 bp_handler_ = handler; |
| 284 if (bp_handler_ == NULL) { |
| 285 bp_handler_ = &DefaultBreakpointHandler; |
| 286 } |
| 287 } |
| 288 |
| 289 |
| 290 void Debugger::BreakpointCallback() { |
| 291 ASSERT(initialized_); |
| 292 DartFrameIterator iterator; |
| 293 DartFrame* frame = iterator.NextFrame(); |
| 294 Function& func = Function::Handle(); |
| 295 ASSERT(frame != NULL); |
| 296 Breakpoint* bpt = GetBreakpoint(frame->pc()); |
| 297 ASSERT(bpt != NULL); |
| 298 OS::Print(">>> Breakpoint at %s:%d (Address %p)\n", |
| 299 bpt ? String::Handle(bpt->SourceUrl()).ToCString() : "?", |
| 300 bpt ? bpt->LineNumber() : 0, |
| 301 frame->pc()); |
| 302 StackTrace* stack_trace = new StackTrace(8); |
| 303 while (frame != NULL) { |
| 304 ASSERT(frame->IsValid()); |
| 305 ASSERT(frame->IsDartFrame()); |
| 306 ActivationFrame* activation = new ActivationFrame(frame->pc()); |
| 307 stack_trace->AddActivation(activation); |
| 308 frame = iterator.NextFrame(); |
| 309 } |
| 310 |
| 311 if (bp_handler_ != NULL) { |
| 312 (*bp_handler_)(bpt, stack_trace); |
| 313 } |
| 314 } |
| 315 |
| 316 |
| 143 void Debugger::Initialize(Isolate* isolate) { | 317 void Debugger::Initialize(Isolate* isolate) { |
| 144 if (initialized_) { | 318 if (initialized_) { |
| 145 return; | 319 return; |
| 146 } | 320 } |
| 147 initialized_ = true; | 321 initialized_ = true; |
| 148 if (!FLAG_debugger) { | 322 if (!FLAG_debugger) { |
| 149 return; | 323 return; |
| 150 } | 324 } |
| 151 if (FLAG_bpt == NULL) { | 325 if (FLAG_bpt == NULL) { |
| 152 FLAG_bpt = "main"; | 326 FLAG_bpt = "main"; |
| 153 } | 327 } |
| 154 String& cname = String::Handle(); | 328 String& cname = String::Handle(); |
| 155 String& fname = String::Handle(); | 329 String& fname = String::Handle(); |
| 156 const char* dot = strchr(FLAG_bpt, '.'); | 330 const char* dot = strchr(FLAG_bpt, '.'); |
| 157 if (dot == NULL) { | 331 if (dot == NULL) { |
| 158 fname = String::New(FLAG_bpt); | 332 fname = String::New(FLAG_bpt); |
| 159 } else { | 333 } else { |
| 160 fname = String::New(dot + 1); | 334 fname = String::New(dot + 1); |
| 161 cname = String::New(reinterpret_cast<const uint8_t*>(FLAG_bpt), | 335 cname = String::New(reinterpret_cast<const uint8_t*>(FLAG_bpt), |
| 162 dot - FLAG_bpt); | 336 dot - FLAG_bpt); |
| 163 } | 337 } |
| 338 SetBreakpointHandler(DefaultBreakpointHandler); |
| 164 SetBreakpointAtEntry(cname, fname); | 339 SetBreakpointAtEntry(cname, fname); |
| 165 } | 340 } |
| 166 | 341 |
| 167 | 342 |
| 168 Breakpoint* Debugger::GetBreakpoint(uword breakpoint_address) { | 343 Breakpoint* Debugger::GetBreakpoint(uword breakpoint_address) { |
| 169 Breakpoint* bpt = this->breakpoints_; | 344 Breakpoint* bpt = this->breakpoints_; |
| 170 while (bpt != NULL) { | 345 while (bpt != NULL) { |
| 171 if (bpt->pc() == breakpoint_address) { | 346 if (bpt->pc() == breakpoint_address) { |
| 172 return bpt; | 347 return bpt; |
| 173 } | 348 } |
| 174 bpt = bpt->next(); | 349 bpt = bpt->next(); |
| 175 } | 350 } |
| 176 return NULL; | 351 return NULL; |
| 177 } | 352 } |
| 178 | 353 |
| 179 | 354 |
| 180 void Debugger::AddBreakpoint(Breakpoint* bpt) { | 355 void Debugger::AddBreakpoint(Breakpoint* bpt) { |
| 181 ASSERT(bpt->next() == NULL); | 356 ASSERT(bpt->next() == NULL); |
| 182 bpt->set_next(this->breakpoints_); | 357 bpt->set_next(this->breakpoints_); |
| 183 this->breakpoints_ = bpt; | 358 this->breakpoints_ = bpt; |
| 184 } | 359 } |
| 185 | 360 |
| 186 | 361 |
| 187 } // namespace dart | 362 } // namespace dart |
| OLD | NEW |