Chromium Code Reviews| 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 "vm/code_generator.h" | 7 #include "vm/code_generator.h" |
| 8 #include "vm/code_patcher.h" | 8 #include "vm/code_patcher.h" |
| 9 #include "vm/compiler.h" | 9 #include "vm/compiler.h" |
| 10 #include "vm/dart_entry.h" | 10 #include "vm/dart_entry.h" |
| (...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 468 : isolate_(NULL), | 468 : isolate_(NULL), |
| 469 initialized_(false), | 469 initialized_(false), |
| 470 bp_handler_(NULL), | 470 bp_handler_(NULL), |
| 471 event_handler_(NULL), | 471 event_handler_(NULL), |
| 472 next_id_(1), | 472 next_id_(1), |
| 473 stack_trace_(NULL), | 473 stack_trace_(NULL), |
| 474 obj_cache_(NULL), | 474 obj_cache_(NULL), |
| 475 src_breakpoints_(NULL), | 475 src_breakpoints_(NULL), |
| 476 code_breakpoints_(NULL), | 476 code_breakpoints_(NULL), |
| 477 resume_action_(kContinue), | 477 resume_action_(kContinue), |
| 478 last_bpt_line_(-1), | |
| 478 ignore_breakpoints_(false) { | 479 ignore_breakpoints_(false) { |
| 479 } | 480 } |
| 480 | 481 |
| 481 | 482 |
| 482 Debugger::~Debugger() { | 483 Debugger::~Debugger() { |
| 483 ASSERT(src_breakpoints_ == NULL); | 484 ASSERT(src_breakpoints_ == NULL); |
| 484 ASSERT(code_breakpoints_ == NULL); | 485 ASSERT(code_breakpoints_ == NULL); |
| 485 ASSERT(stack_trace_ == NULL); | 486 ASSERT(stack_trace_ == NULL); |
| 486 ASSERT(obj_cache_ == NULL); | 487 ASSERT(obj_cache_ == NULL); |
| 487 } | 488 } |
| (...skipping 471 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 959 bp_handler_ = &DefaultBreakpointHandler; | 960 bp_handler_ = &DefaultBreakpointHandler; |
| 960 } | 961 } |
| 961 } | 962 } |
| 962 | 963 |
| 963 | 964 |
| 964 void Debugger::SetEventHandler(EventHandler* handler) { | 965 void Debugger::SetEventHandler(EventHandler* handler) { |
| 965 event_handler_ = handler; | 966 event_handler_ = handler; |
| 966 } | 967 } |
| 967 | 968 |
| 968 | 969 |
| 970 bool Debugger::IsDebuggable(const Function& func) { | |
| 971 RawFunction::Kind fkind = func.kind(); | |
| 972 if ((fkind == RawFunction::kImplicitGetter) || | |
| 973 (fkind == RawFunction::kImplicitSetter) || | |
| 974 (fkind == RawFunction::kConstImplicitGetter)) { | |
| 975 return false; | |
| 976 } | |
| 977 const Class& cls = Class::Handle(func.owner()); | |
| 978 const Library& lib = Library::Handle(cls.library()); | |
| 979 return lib.IsDebuggable(); | |
| 980 } | |
| 981 | |
| 982 | |
| 969 void Debugger::BreakpointCallback() { | 983 void Debugger::BreakpointCallback() { |
| 970 ASSERT(initialized_); | 984 ASSERT(initialized_); |
| 971 | 985 |
| 972 if (ignore_breakpoints_) { | 986 if (ignore_breakpoints_) { |
| 973 return; | 987 return; |
| 974 } | 988 } |
| 975 DartFrameIterator iterator; | 989 DartFrameIterator iterator; |
| 976 StackFrame* frame = iterator.NextFrame(); | 990 StackFrame* frame = iterator.NextFrame(); |
| 977 ASSERT(frame != NULL && frame->IsDartFrame()); | 991 ASSERT(frame != NULL && frame->IsDartFrame()); |
| 978 CodeBreakpoint* bpt = GetCodeBreakpoint(frame->pc()); | 992 CodeBreakpoint* bpt = GetCodeBreakpoint(frame->pc()); |
| 979 ASSERT(bpt != NULL); | 993 ASSERT(bpt != NULL); |
| 980 if (verbose) { | 994 if (verbose) { |
| 981 OS::Print(">>> %s breakpoint at %s:%d (Address %p)\n", | 995 OS::Print(">>> %s breakpoint at %s:%d (Address %p)\n", |
| 982 bpt->IsInternal() ? "hit internal" : "hit user", | 996 bpt->IsInternal() ? "hit internal" : "hit user", |
| 983 bpt ? String::Handle(bpt->SourceUrl()).ToCString() : "?", | 997 bpt ? String::Handle(bpt->SourceUrl()).ToCString() : "?", |
| 984 bpt ? bpt->LineNumber() : 0, | 998 bpt ? bpt->LineNumber() : 0, |
| 985 frame->pc()); | 999 frame->pc()); |
| 986 } | 1000 } |
| 987 | 1001 |
| 1002 if (!bpt->IsInternal()) { | |
| 1003 // This is a user-defined breakpoint, so we call the breakpoint callback | |
| 1004 // even if it is on the same line as the previous breakpoint. | |
| 1005 last_bpt_line_ = -1; | |
| 1006 } | |
| 1007 | |
| 1008 bool notify_frontend = | |
| 1009 (last_bpt_line_ < 0) || (last_bpt_line_ != bpt->LineNumber()); | |
| 1010 | |
| 988 DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8); | 1011 DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8); |
| 989 while (frame != NULL) { | 1012 while (frame != NULL) { |
| 990 ASSERT(frame->IsValid()); | 1013 ASSERT(frame->IsValid()); |
| 991 ASSERT(frame->IsDartFrame()); | 1014 ASSERT(frame->IsDartFrame()); |
| 992 ActivationFrame* activation = | 1015 ActivationFrame* activation = |
| 993 new ActivationFrame(frame->pc(), frame->fp(), frame->sp()); | 1016 new ActivationFrame(frame->pc(), frame->fp(), frame->sp()); |
| 994 stack_trace->AddActivation(activation); | 1017 stack_trace->AddActivation(activation); |
| 995 frame = iterator.NextFrame(); | 1018 frame = iterator.NextFrame(); |
| 996 } | 1019 } |
| 997 | 1020 |
| 998 resume_action_ = kContinue; | 1021 if (notify_frontend) { |
| 999 if (bp_handler_ != NULL) { | 1022 resume_action_ = kContinue; |
| 1000 SourceBreakpoint* src_bpt = bpt->src_bpt(); | 1023 if (bp_handler_ != NULL) { |
| 1001 ASSERT(stack_trace_ == NULL); | 1024 SourceBreakpoint* src_bpt = bpt->src_bpt(); |
| 1002 ASSERT(obj_cache_ == NULL); | 1025 ASSERT(stack_trace_ == NULL); |
| 1003 obj_cache_ = new RemoteObjectCache(64); | 1026 ASSERT(obj_cache_ == NULL); |
| 1004 stack_trace_ = stack_trace; | 1027 obj_cache_ = new RemoteObjectCache(64); |
| 1005 (*bp_handler_)(src_bpt, stack_trace); | 1028 stack_trace_ = stack_trace; |
| 1006 stack_trace_ = NULL; | 1029 (*bp_handler_)(src_bpt, stack_trace); |
| 1007 obj_cache_ = NULL; // Remote object cache is zone allocated. | 1030 stack_trace_ = NULL; |
| 1031 obj_cache_ = NULL; // Remote object cache is zone allocated. | |
| 1032 last_bpt_line_ = bpt->LineNumber(); | |
| 1033 } | |
| 1008 } | 1034 } |
| 1009 | 1035 |
| 1036 Function& currently_instrumented_func = Function::Handle(); | |
| 1037 if (bpt->IsInternal()) { | |
| 1038 currently_instrumented_func = bpt->function(); | |
| 1039 } | |
| 1040 Function& func_to_instrument = Function::Handle(); | |
| 1010 if (resume_action_ == kContinue) { | 1041 if (resume_action_ == kContinue) { |
| 1011 RemoveInternalBreakpoints(); | 1042 // Nothing to do here, any potential instrumentation will be removed |
| 1043 // below. | |
| 1012 } else if (resume_action_ == kStepOver) { | 1044 } else if (resume_action_ == kStepOver) { |
| 1013 Function& func = Function::Handle(bpt->function()); | 1045 func_to_instrument = bpt->function(); |
|
siva
2012/05/31 19:11:15
This will be an issue if we have resursive calls r
hausner
2012/05/31 20:04:14
We don't have recursive calls. Breakpoints are ign
siva
2012/05/31 20:26:56
I didn't mean recursive calls to the breakpoint ha
| |
| 1014 if (bpt->breakpoint_kind_ == PcDescriptors::kReturn) { | 1046 if (bpt->breakpoint_kind_ == PcDescriptors::kReturn) { |
| 1015 // If we are at the function return, do a StepOut action. | 1047 // If we are at the function return, do a StepOut action. |
| 1016 if (stack_trace->Length() > 1) { | 1048 if (stack_trace->Length() > 1) { |
| 1017 ActivationFrame* caller = stack_trace->ActivationFrameAt(1); | 1049 ActivationFrame* caller_frame = stack_trace->ActivationFrameAt(1); |
| 1018 func = caller->DartFunction().raw(); | 1050 func_to_instrument = caller_frame->DartFunction().raw(); |
| 1019 } | 1051 } |
| 1020 } | 1052 } |
| 1021 RemoveInternalBreakpoints(); // *bpt is now invalid. | |
| 1022 InstrumentForStepping(func); | |
| 1023 } else if (resume_action_ == kStepInto) { | 1053 } else if (resume_action_ == kStepInto) { |
| 1054 // If the call target is not debuggable, we treat StepInto like | |
| 1055 // a StepOver, that is we instrument the current function. | |
| 1024 if (bpt->breakpoint_kind_ == PcDescriptors::kIcCall) { | 1056 if (bpt->breakpoint_kind_ == PcDescriptors::kIcCall) { |
| 1057 func_to_instrument = bpt->function(); | |
| 1025 int num_args, num_named_args; | 1058 int num_args, num_named_args; |
| 1026 uword target; | 1059 uword target; |
| 1027 CodePatcher::GetInstanceCallAt(bpt->pc_, NULL, | 1060 CodePatcher::GetInstanceCallAt(bpt->pc_, NULL, |
| 1028 &num_args, &num_named_args, &target); | 1061 &num_args, &num_named_args, &target); |
| 1029 RemoveInternalBreakpoints(); // *bpt is now invalid. | |
| 1030 ActivationFrame* top_frame = stack_trace->ActivationFrameAt(0); | 1062 ActivationFrame* top_frame = stack_trace->ActivationFrameAt(0); |
| 1031 Instance& receiver = Instance::Handle( | 1063 Instance& receiver = Instance::Handle( |
| 1032 top_frame->GetInstanceCallReceiver(num_args)); | 1064 top_frame->GetInstanceCallReceiver(num_args)); |
| 1033 Code& code = Code::Handle( | 1065 Code& code = Code::Handle( |
| 1034 ResolveCompileInstanceCallTarget(isolate_, receiver)); | 1066 ResolveCompileInstanceCallTarget(isolate_, receiver)); |
| 1035 if (!code.IsNull()) { | 1067 if (!code.IsNull()) { |
| 1036 Function& callee = Function::Handle(code.function()); | 1068 Function& callee = Function::Handle(code.function()); |
| 1037 InstrumentForStepping(callee); | 1069 if (IsDebuggable(callee)) { |
| 1070 func_to_instrument = callee.raw(); | |
| 1071 } | |
| 1038 } | 1072 } |
| 1039 } else if (bpt->breakpoint_kind_ == PcDescriptors::kFuncCall) { | 1073 } else if (bpt->breakpoint_kind_ == PcDescriptors::kFuncCall) { |
| 1074 func_to_instrument = bpt->function(); | |
| 1040 Function& callee = Function::Handle(); | 1075 Function& callee = Function::Handle(); |
| 1041 uword target; | 1076 uword target; |
| 1042 CodePatcher::GetStaticCallAt(bpt->pc_, &callee, &target); | 1077 CodePatcher::GetStaticCallAt(bpt->pc_, &callee, &target); |
| 1043 RemoveInternalBreakpoints(); // *bpt is now invalid. | 1078 if (IsDebuggable(callee)) { |
| 1044 InstrumentForStepping(callee); | 1079 func_to_instrument = callee.raw(); |
| 1080 } | |
| 1045 } else { | 1081 } else { |
| 1046 ASSERT(bpt->breakpoint_kind_ == PcDescriptors::kReturn); | 1082 ASSERT(bpt->breakpoint_kind_ == PcDescriptors::kReturn); |
| 1047 RemoveInternalBreakpoints(); // *bpt is now invalid. | |
| 1048 // Treat like stepping out to caller. | 1083 // Treat like stepping out to caller. |
| 1049 if (stack_trace->Length() > 1) { | 1084 if (stack_trace->Length() > 1) { |
| 1050 ActivationFrame* caller = stack_trace->ActivationFrameAt(1); | 1085 ActivationFrame* caller_frame = stack_trace->ActivationFrameAt(1); |
| 1051 InstrumentForStepping(caller->DartFunction()); | 1086 func_to_instrument = caller_frame->DartFunction().raw(); |
| 1052 } | 1087 } |
| 1053 } | 1088 } |
| 1054 } else { | 1089 } else { |
| 1055 ASSERT(resume_action_ == kStepOut); | 1090 ASSERT(resume_action_ == kStepOut); |
| 1056 RemoveInternalBreakpoints(); // *bpt is now invalid. | |
| 1057 // Set stepping breakpoints in the caller. | 1091 // Set stepping breakpoints in the caller. |
| 1058 if (stack_trace->Length() > 1) { | 1092 if (stack_trace->Length() > 1) { |
| 1059 ActivationFrame* caller = stack_trace->ActivationFrameAt(1); | 1093 ActivationFrame* caller_frame = stack_trace->ActivationFrameAt(1); |
| 1060 InstrumentForStepping(caller->DartFunction()); | 1094 func_to_instrument = caller_frame->DartFunction().raw(); |
|
siva
2012/05/31 19:11:15
How does this work if we have recursive calls i.e
hausner
2012/05/31 20:04:14
Recursive functions are not properly treated yet.
siva
2012/05/31 20:26:56
Actually we won't step out to the previous activat
hausner
2012/05/31 20:32:28
Ah, you are right. I will put this on my todo list
| |
| 1095 } | |
| 1096 } | |
| 1097 | |
| 1098 if (func_to_instrument.raw() != currently_instrumented_func.raw()) { | |
| 1099 last_bpt_line_ = -1; | |
| 1100 RemoveInternalBreakpoints(); // *bpt is now invalid. | |
| 1101 if (!func_to_instrument.IsNull()) { | |
| 1102 InstrumentForStepping(func_to_instrument); | |
| 1061 } | 1103 } |
| 1062 } | 1104 } |
| 1063 } | 1105 } |
| 1064 | 1106 |
| 1065 | 1107 |
| 1066 void Debugger::Initialize(Isolate* isolate) { | 1108 void Debugger::Initialize(Isolate* isolate) { |
| 1067 if (initialized_) { | 1109 if (initialized_) { |
| 1068 return; | 1110 return; |
| 1069 } | 1111 } |
| 1070 isolate_ = isolate; | 1112 isolate_ = isolate; |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1139 ASSERT(src_breakpoints_ != NULL); | 1181 ASSERT(src_breakpoints_ != NULL); |
| 1140 SourceBreakpoint* prev_bpt = NULL; | 1182 SourceBreakpoint* prev_bpt = NULL; |
| 1141 SourceBreakpoint* curr_bpt = src_breakpoints_; | 1183 SourceBreakpoint* curr_bpt = src_breakpoints_; |
| 1142 while (curr_bpt != NULL) { | 1184 while (curr_bpt != NULL) { |
| 1143 if (curr_bpt->id() == bp_id) { | 1185 if (curr_bpt->id() == bp_id) { |
| 1144 if (prev_bpt == NULL) { | 1186 if (prev_bpt == NULL) { |
| 1145 src_breakpoints_ = src_breakpoints_->next(); | 1187 src_breakpoints_ = src_breakpoints_->next(); |
| 1146 } else { | 1188 } else { |
| 1147 prev_bpt->set_next(curr_bpt->next()); | 1189 prev_bpt->set_next(curr_bpt->next()); |
| 1148 } | 1190 } |
| 1149 // Remove the code breakpoints associated with the source breakpoint. | 1191 // Remove references from code breakpoints to this source breakpoint. |
| 1150 RemoveCodeBreakpoints(curr_bpt); | 1192 UnlinkCodeBreakpoints(curr_bpt); |
| 1151 delete curr_bpt; | 1193 delete curr_bpt; |
| 1152 return; | 1194 return; |
| 1153 } | 1195 } |
| 1154 prev_bpt = curr_bpt; | 1196 prev_bpt = curr_bpt; |
| 1155 curr_bpt = curr_bpt->next(); | 1197 curr_bpt = curr_bpt->next(); |
| 1156 } | 1198 } |
| 1157 // bpt is not a registered breakpoint, nothing to do. | 1199 // bpt is not a registered breakpoint, nothing to do. |
| 1158 } | 1200 } |
| 1159 | 1201 |
| 1160 | 1202 |
| 1161 // Remove and delete the code breakpoints that are associated with given | 1203 // Turn code breakpoints associated with the given source breakpoint into |
| 1162 // source breakpoint bpt. If bpt is null, remove the internal breakpoints. | 1204 // internal breakpoints. They will later be deleted when control |
| 1163 void Debugger::RemoveCodeBreakpoints(SourceBreakpoint* src_bpt) { | 1205 // returns from the user-defined breakpoint callback. |
|
siva
2012/05/31 19:11:15
Can you add a comment stating that breakpoints can
hausner
2012/05/31 20:04:14
Done.
| |
| 1206 void Debugger::UnlinkCodeBreakpoints(SourceBreakpoint* src_bpt) { | |
| 1207 ASSERT(src_bpt != NULL); | |
| 1208 CodeBreakpoint* curr_bpt = code_breakpoints_; | |
| 1209 while (curr_bpt != NULL) { | |
| 1210 if (curr_bpt->src_bpt() == src_bpt) { | |
| 1211 curr_bpt->set_src_bpt(NULL); | |
|
siva
2012/05/31 19:11:15
can you break out from here instead of looping til
hausner
2012/05/31 20:04:14
There can be multiple code breakpoints pointing to
| |
| 1212 } | |
| 1213 curr_bpt = curr_bpt->next(); | |
| 1214 } | |
| 1215 } | |
| 1216 | |
| 1217 | |
| 1218 // Remove and delete internal breakpoints, i.e. breakpoints that | |
| 1219 // are not associated with a source breakpoint. | |
| 1220 void Debugger::RemoveInternalBreakpoints() { | |
| 1164 CodeBreakpoint* prev_bpt = NULL; | 1221 CodeBreakpoint* prev_bpt = NULL; |
| 1165 CodeBreakpoint* curr_bpt = code_breakpoints_; | 1222 CodeBreakpoint* curr_bpt = code_breakpoints_; |
| 1166 while (curr_bpt != NULL) { | 1223 while (curr_bpt != NULL) { |
| 1167 if (curr_bpt->src_bpt() == src_bpt) { | 1224 if (curr_bpt->src_bpt() == NULL) { |
| 1168 if (prev_bpt == NULL) { | 1225 if (prev_bpt == NULL) { |
| 1169 code_breakpoints_ = code_breakpoints_->next(); | 1226 code_breakpoints_ = code_breakpoints_->next(); |
| 1170 } else { | 1227 } else { |
| 1171 prev_bpt->set_next(curr_bpt->next()); | 1228 prev_bpt->set_next(curr_bpt->next()); |
| 1172 } | 1229 } |
| 1173 CodeBreakpoint* temp_bpt = curr_bpt; | 1230 CodeBreakpoint* temp_bpt = curr_bpt; |
| 1174 curr_bpt = curr_bpt->next(); | 1231 curr_bpt = curr_bpt->next(); |
| 1175 temp_bpt->Disable(); | 1232 temp_bpt->Disable(); |
| 1176 delete temp_bpt; | 1233 delete temp_bpt; |
| 1177 } else { | 1234 } else { |
| 1178 prev_bpt = curr_bpt; | 1235 prev_bpt = curr_bpt; |
| 1179 curr_bpt = curr_bpt->next(); | 1236 curr_bpt = curr_bpt->next(); |
| 1180 } | 1237 } |
| 1181 } | 1238 } |
| 1182 } | 1239 } |
| 1183 | 1240 |
| 1184 | 1241 |
| 1185 // Remove and delete all breakpoints that are not associated with a | |
| 1186 // user-defined source breakpoint. | |
| 1187 void Debugger::RemoveInternalBreakpoints() { | |
| 1188 RemoveCodeBreakpoints(NULL); | |
| 1189 } | |
| 1190 | |
| 1191 | |
| 1192 SourceBreakpoint* Debugger::GetSourceBreakpoint(const Function& func, | 1242 SourceBreakpoint* Debugger::GetSourceBreakpoint(const Function& func, |
| 1193 intptr_t token_index) { | 1243 intptr_t token_index) { |
| 1194 SourceBreakpoint* bpt = src_breakpoints_; | 1244 SourceBreakpoint* bpt = src_breakpoints_; |
| 1195 while (bpt != NULL) { | 1245 while (bpt != NULL) { |
| 1196 if ((bpt->function() == func.raw()) && | 1246 if ((bpt->function() == func.raw()) && |
| 1197 (bpt->token_index() == token_index)) { | 1247 (bpt->token_index() == token_index)) { |
| 1198 return bpt; | 1248 return bpt; |
| 1199 } | 1249 } |
| 1200 bpt = bpt->next(); | 1250 bpt = bpt->next(); |
| 1201 } | 1251 } |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 1222 } | 1272 } |
| 1223 | 1273 |
| 1224 | 1274 |
| 1225 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { | 1275 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { |
| 1226 ASSERT(bpt->next() == NULL); | 1276 ASSERT(bpt->next() == NULL); |
| 1227 bpt->set_next(code_breakpoints_); | 1277 bpt->set_next(code_breakpoints_); |
| 1228 code_breakpoints_ = bpt; | 1278 code_breakpoints_ = bpt; |
| 1229 } | 1279 } |
| 1230 | 1280 |
| 1231 } // namespace dart | 1281 } // namespace dart |
| OLD | NEW |