OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/debug/debug.h" | 5 #include "src/debug/debug.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 | 8 |
9 #include "src/api.h" | 9 #include "src/api.h" |
10 #include "src/arguments.h" | 10 #include "src/arguments.h" |
(...skipping 24 matching lines...) Expand all Loading... |
35 namespace internal { | 35 namespace internal { |
36 | 36 |
37 Debug::Debug(Isolate* isolate) | 37 Debug::Debug(Isolate* isolate) |
38 : debug_context_(Handle<Context>()), | 38 : debug_context_(Handle<Context>()), |
39 event_listener_(Handle<Object>()), | 39 event_listener_(Handle<Object>()), |
40 event_listener_data_(Handle<Object>()), | 40 event_listener_data_(Handle<Object>()), |
41 message_handler_(NULL), | 41 message_handler_(NULL), |
42 command_received_(0), | 42 command_received_(0), |
43 command_queue_(isolate->logger(), kQueueInitialSize), | 43 command_queue_(isolate->logger(), kQueueInitialSize), |
44 is_active_(false), | 44 is_active_(false), |
| 45 hook_on_function_call_(false), |
45 is_suppressed_(false), | 46 is_suppressed_(false), |
46 live_edit_enabled_(true), // TODO(yangguo): set to false by default. | 47 live_edit_enabled_(true), // TODO(yangguo): set to false by default. |
47 break_disabled_(false), | 48 break_disabled_(false), |
48 break_points_active_(true), | 49 break_points_active_(true), |
49 in_debug_event_listener_(false), | 50 in_debug_event_listener_(false), |
50 break_on_exception_(false), | 51 break_on_exception_(false), |
51 break_on_uncaught_exception_(false), | 52 break_on_uncaught_exception_(false), |
| 53 needs_side_effect_check_(false), |
| 54 side_effect_check_failed_(false), |
52 debug_info_list_(NULL), | 55 debug_info_list_(NULL), |
53 feature_tracker_(isolate), | 56 feature_tracker_(isolate), |
54 isolate_(isolate) { | 57 isolate_(isolate) { |
55 ThreadInit(); | 58 ThreadInit(); |
56 } | 59 } |
57 | 60 |
58 BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info, | 61 BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info, |
59 JavaScriptFrame* frame) { | 62 JavaScriptFrame* frame) { |
60 FrameSummary summary = FrameSummary::GetFirst(frame); | 63 FrameSummary summary = FrameSummary::GetFirst(frame); |
61 int offset = summary.code_offset(); | 64 int offset = summary.code_offset(); |
(...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
398 thread_local_.break_frame_id_ = StackFrame::NO_ID; | 401 thread_local_.break_frame_id_ = StackFrame::NO_ID; |
399 thread_local_.last_step_action_ = StepNone; | 402 thread_local_.last_step_action_ = StepNone; |
400 thread_local_.last_statement_position_ = kNoSourcePosition; | 403 thread_local_.last_statement_position_ = kNoSourcePosition; |
401 thread_local_.last_fp_ = 0; | 404 thread_local_.last_fp_ = 0; |
402 thread_local_.target_fp_ = 0; | 405 thread_local_.target_fp_ = 0; |
403 thread_local_.return_value_ = Handle<Object>(); | 406 thread_local_.return_value_ = Handle<Object>(); |
404 clear_suspended_generator(); | 407 clear_suspended_generator(); |
405 // TODO(isolates): frames_are_dropped_? | 408 // TODO(isolates): frames_are_dropped_? |
406 base::NoBarrier_Store(&thread_local_.current_debug_scope_, | 409 base::NoBarrier_Store(&thread_local_.current_debug_scope_, |
407 static_cast<base::AtomicWord>(0)); | 410 static_cast<base::AtomicWord>(0)); |
| 411 UpdateHookOnFunctionCall(); |
408 } | 412 } |
409 | 413 |
410 | 414 |
411 char* Debug::ArchiveDebug(char* storage) { | 415 char* Debug::ArchiveDebug(char* storage) { |
412 // Simply reset state. Don't archive anything. | 416 // Simply reset state. Don't archive anything. |
413 ThreadInit(); | 417 ThreadInit(); |
414 return storage + ArchiveSpacePerThread(); | 418 return storage + ArchiveSpacePerThread(); |
415 } | 419 } |
416 | 420 |
417 | 421 |
(...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
897 if (type == BreakUncaughtException) { | 901 if (type == BreakUncaughtException) { |
898 return break_on_uncaught_exception_; | 902 return break_on_uncaught_exception_; |
899 } else { | 903 } else { |
900 return break_on_exception_; | 904 return break_on_exception_; |
901 } | 905 } |
902 } | 906 } |
903 | 907 |
904 | 908 |
905 void Debug::PrepareStepIn(Handle<JSFunction> function) { | 909 void Debug::PrepareStepIn(Handle<JSFunction> function) { |
906 CHECK(last_step_action() >= StepIn); | 910 CHECK(last_step_action() >= StepIn); |
907 if (!is_active()) return; | 911 if (ignore_events()) return; |
908 if (in_debug_scope()) return; | 912 if (in_debug_scope()) return; |
| 913 if (break_disabled()) return; |
909 FloodWithOneShot(function); | 914 FloodWithOneShot(function); |
910 } | 915 } |
911 | 916 |
912 void Debug::PrepareStepInSuspendedGenerator() { | 917 void Debug::PrepareStepInSuspendedGenerator() { |
913 CHECK(has_suspended_generator()); | 918 CHECK(has_suspended_generator()); |
914 if (!is_active()) return; | 919 if (ignore_events()) return; |
915 if (in_debug_scope()) return; | 920 if (in_debug_scope()) return; |
| 921 if (break_disabled()) return; |
916 thread_local_.last_step_action_ = StepIn; | 922 thread_local_.last_step_action_ = StepIn; |
| 923 UpdateHookOnFunctionCall(); |
917 Handle<JSFunction> function( | 924 Handle<JSFunction> function( |
918 JSGeneratorObject::cast(thread_local_.suspended_generator_)->function()); | 925 JSGeneratorObject::cast(thread_local_.suspended_generator_)->function()); |
919 FloodWithOneShot(function); | 926 FloodWithOneShot(function); |
920 clear_suspended_generator(); | 927 clear_suspended_generator(); |
921 } | 928 } |
922 | 929 |
923 void Debug::PrepareStepOnThrow() { | 930 void Debug::PrepareStepOnThrow() { |
924 if (!is_active()) return; | |
925 if (last_step_action() == StepNone) return; | 931 if (last_step_action() == StepNone) return; |
| 932 if (ignore_events()) return; |
926 if (in_debug_scope()) return; | 933 if (in_debug_scope()) return; |
| 934 if (break_disabled()) return; |
927 | 935 |
928 ClearOneShot(); | 936 ClearOneShot(); |
929 | 937 |
930 // Iterate through the JavaScript stack looking for handlers. | 938 // Iterate through the JavaScript stack looking for handlers. |
931 JavaScriptFrameIterator it(isolate_); | 939 JavaScriptFrameIterator it(isolate_); |
932 while (!it.done()) { | 940 while (!it.done()) { |
933 JavaScriptFrame* frame = it.frame(); | 941 JavaScriptFrame* frame = it.frame(); |
934 if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break; | 942 if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break; |
935 it.Advance(); | 943 it.Advance(); |
936 } | 944 } |
(...skipping 30 matching lines...) Expand all Loading... |
967 StackFrame::Id frame_id = break_frame_id(); | 975 StackFrame::Id frame_id = break_frame_id(); |
968 // If there is no JavaScript stack don't do anything. | 976 // If there is no JavaScript stack don't do anything. |
969 if (frame_id == StackFrame::NO_ID) return; | 977 if (frame_id == StackFrame::NO_ID) return; |
970 | 978 |
971 JavaScriptFrameIterator frames_it(isolate_, frame_id); | 979 JavaScriptFrameIterator frames_it(isolate_, frame_id); |
972 JavaScriptFrame* frame = frames_it.frame(); | 980 JavaScriptFrame* frame = frames_it.frame(); |
973 | 981 |
974 feature_tracker()->Track(DebugFeatureTracker::kStepping); | 982 feature_tracker()->Track(DebugFeatureTracker::kStepping); |
975 | 983 |
976 thread_local_.last_step_action_ = step_action; | 984 thread_local_.last_step_action_ = step_action; |
| 985 UpdateHookOnFunctionCall(); |
977 | 986 |
978 // If the function on the top frame is unresolved perform step out. This will | 987 // If the function on the top frame is unresolved perform step out. This will |
979 // be the case when calling unknown function and having the debugger stopped | 988 // be the case when calling unknown function and having the debugger stopped |
980 // in an unhandled exception. | 989 // in an unhandled exception. |
981 if (!frame->function()->IsJSFunction()) { | 990 if (!frame->function()->IsJSFunction()) { |
982 // Step out: Find the calling JavaScript frame and flood it with | 991 // Step out: Find the calling JavaScript frame and flood it with |
983 // breakpoints. | 992 // breakpoints. |
984 frames_it.Advance(); | 993 frames_it.Advance(); |
985 // Fill the function to return to with one-shot break points. | 994 // Fill the function to return to with one-shot break points. |
986 JSFunction* function = frames_it.frame()->function(); | 995 JSFunction* function = frames_it.frame()->function(); |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1097 } | 1106 } |
1098 | 1107 |
1099 void Debug::ClearStepping() { | 1108 void Debug::ClearStepping() { |
1100 // Clear the various stepping setup. | 1109 // Clear the various stepping setup. |
1101 ClearOneShot(); | 1110 ClearOneShot(); |
1102 | 1111 |
1103 thread_local_.last_step_action_ = StepNone; | 1112 thread_local_.last_step_action_ = StepNone; |
1104 thread_local_.last_statement_position_ = kNoSourcePosition; | 1113 thread_local_.last_statement_position_ = kNoSourcePosition; |
1105 thread_local_.last_fp_ = 0; | 1114 thread_local_.last_fp_ = 0; |
1106 thread_local_.target_fp_ = 0; | 1115 thread_local_.target_fp_ = 0; |
| 1116 UpdateHookOnFunctionCall(); |
1107 } | 1117 } |
1108 | 1118 |
1109 | 1119 |
1110 // Clears all the one-shot break points that are currently set. Normally this | 1120 // Clears all the one-shot break points that are currently set. Normally this |
1111 // function is called each time a break point is hit as one shot break points | 1121 // function is called each time a break point is hit as one shot break points |
1112 // are used to support stepping. | 1122 // are used to support stepping. |
1113 void Debug::ClearOneShot() { | 1123 void Debug::ClearOneShot() { |
1114 // The current implementation just runs through all the breakpoints. When the | 1124 // The current implementation just runs through all the breakpoints. When the |
1115 // last break point for a function is removed that function is automatically | 1125 // last break point for a function is removed that function is automatically |
1116 // removed from the list. | 1126 // removed from the list. |
(...skipping 954 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2071 // bootstrap test cases. | 2081 // bootstrap test cases. |
2072 isolate_->compilation_cache()->Disable(); | 2082 isolate_->compilation_cache()->Disable(); |
2073 is_active = Load(); | 2083 is_active = Load(); |
2074 } else if (is_loaded()) { | 2084 } else if (is_loaded()) { |
2075 isolate_->compilation_cache()->Enable(); | 2085 isolate_->compilation_cache()->Enable(); |
2076 Unload(); | 2086 Unload(); |
2077 } | 2087 } |
2078 is_active_ = is_active; | 2088 is_active_ = is_active; |
2079 } | 2089 } |
2080 | 2090 |
| 2091 void Debug::UpdateHookOnFunctionCall() { |
| 2092 hook_on_function_call_ = |
| 2093 thread_local_.last_step_action_ >= StepIn || needs_side_effect_check_; |
| 2094 } |
| 2095 |
2081 // Calls the registered debug message handler. This callback is part of the | 2096 // Calls the registered debug message handler. This callback is part of the |
2082 // public API. | 2097 // public API. |
2083 void Debug::InvokeMessageHandler(MessageImpl message) { | 2098 void Debug::InvokeMessageHandler(MessageImpl message) { |
2084 if (message_handler_ != NULL) message_handler_(message); | 2099 if (message_handler_ != NULL) message_handler_(message); |
2085 } | 2100 } |
2086 | 2101 |
2087 // Puts a command coming from the public API on the queue. Creates | 2102 // Puts a command coming from the public API on the queue. Creates |
2088 // a copy of the command string managed by the debugger. Up to this | 2103 // a copy of the command string managed by the debugger. Up to this |
2089 // point, the command data was managed by the API client. Called | 2104 // point, the command data was managed by the API client. Called |
2090 // by the API client thread. | 2105 // by the API client thread. |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2268 reinterpret_cast<base::AtomicWord>(prev_)); | 2283 reinterpret_cast<base::AtomicWord>(prev_)); |
2269 | 2284 |
2270 // Restore to the previous break state. | 2285 // Restore to the previous break state. |
2271 debug_->thread_local_.break_frame_id_ = break_frame_id_; | 2286 debug_->thread_local_.break_frame_id_ = break_frame_id_; |
2272 debug_->thread_local_.break_id_ = break_id_; | 2287 debug_->thread_local_.break_id_ = break_id_; |
2273 debug_->thread_local_.return_value_ = return_value_; | 2288 debug_->thread_local_.return_value_ = return_value_; |
2274 | 2289 |
2275 debug_->UpdateState(); | 2290 debug_->UpdateState(); |
2276 } | 2291 } |
2277 | 2292 |
| 2293 bool Debug::PerformSideEffectCheck(Handle<JSFunction> function) { |
| 2294 DCHECK(needs_side_effect_check_); |
| 2295 if (!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) return false; |
| 2296 Deoptimizer::DeoptimizeFunction(*function); |
| 2297 if (!function->shared()->HasNoSideEffect()) { |
| 2298 if (FLAG_trace_side_effect_free_debug_evaluate) { |
| 2299 PrintF("[debug-evaluate] Function %s failed side effect check.\n", |
| 2300 function->shared()->DebugName()->ToCString().get()); |
| 2301 } |
| 2302 side_effect_check_failed_ = true; |
| 2303 // Throw an uncatchable termination exception. |
| 2304 isolate_->TerminateExecution(); |
| 2305 return false; |
| 2306 } |
| 2307 return true; |
| 2308 } |
| 2309 |
| 2310 NoSideEffectScope::~NoSideEffectScope() { |
| 2311 Isolate* isolate = debug_->isolate_; |
| 2312 if (debug_->needs_side_effect_check_ && debug_->side_effect_check_failed_) { |
| 2313 DCHECK(isolate->has_pending_exception()); |
| 2314 DCHECK_EQ(isolate->heap()->termination_exception(), |
| 2315 isolate->pending_exception()); |
| 2316 // Convert the termination exception into a regular exception. |
| 2317 isolate->CancelTerminateExecution(); |
| 2318 isolate->Throw(*isolate->factory()->NewEvalError( |
| 2319 MessageTemplate::kNoSideEffectDebugEvaluate)); |
| 2320 } |
| 2321 debug_->needs_side_effect_check_ = old_needs_side_effect_check_; |
| 2322 debug_->UpdateHookOnFunctionCall(); |
| 2323 debug_->side_effect_check_failed_ = false; |
| 2324 } |
| 2325 |
2278 MessageImpl MessageImpl::NewEvent(DebugEvent event, bool running, | 2326 MessageImpl MessageImpl::NewEvent(DebugEvent event, bool running, |
2279 Handle<JSObject> exec_state, | 2327 Handle<JSObject> exec_state, |
2280 Handle<JSObject> event_data) { | 2328 Handle<JSObject> event_data) { |
2281 MessageImpl message(true, event, running, exec_state, event_data, | 2329 MessageImpl message(true, event, running, exec_state, event_data, |
2282 Handle<String>(), NULL); | 2330 Handle<String>(), NULL); |
2283 return message; | 2331 return message; |
2284 } | 2332 } |
2285 | 2333 |
2286 MessageImpl MessageImpl::NewResponse(DebugEvent event, bool running, | 2334 MessageImpl MessageImpl::NewResponse(DebugEvent event, bool running, |
2287 Handle<JSObject> exec_state, | 2335 Handle<JSObject> exec_state, |
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2496 logger_->DebugEvent("Put", message.text()); | 2544 logger_->DebugEvent("Put", message.text()); |
2497 } | 2545 } |
2498 | 2546 |
2499 void LockingCommandMessageQueue::Clear() { | 2547 void LockingCommandMessageQueue::Clear() { |
2500 base::LockGuard<base::Mutex> lock_guard(&mutex_); | 2548 base::LockGuard<base::Mutex> lock_guard(&mutex_); |
2501 queue_.Clear(); | 2549 queue_.Clear(); |
2502 } | 2550 } |
2503 | 2551 |
2504 } // namespace internal | 2552 } // namespace internal |
2505 } // namespace v8 | 2553 } // namespace v8 |
OLD | NEW |