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
3 // found in the LICENSE file.
4
5 #include "src/debug/debug.h"
6
7 #include <memory>
8 #include <unordered_set>
9
10 #include "src/api/api-inl.h"
11 #include "src/api/api-natives.h"
12 #include "src/base/platform/mutex.h"
13 #include "src/builtins/builtins.h"
14 #include "src/codegen/assembler-inl.h"
15 #include "src/codegen/compilation-cache.h"
16 #include "src/codegen/compiler.h"
17 #include "src/common/globals.h"
18 #include "src/common/message-template.h"
19 #include "src/debug/debug-evaluate.h"
20 #include "src/debug/liveedit.h"
21 #include "src/deoptimizer/deoptimizer.h"
22 #include "src/execution/arguments.h"
23 #include "src/execution/execution.h"
24 #include "src/execution/frames-inl.h"
25 #include "src/execution/isolate-inl.h"
26 #include "src/execution/v8threads.h"
27 #include "src/handles/global-handles.h"
28 #include "src/heap/heap-inl.h" // For NextDebuggingId.
29 #include "src/init/bootstrapper.h"
30 #include "src/interpreter/bytecode-array-accessor.h"
31 #include "src/interpreter/bytecode-array-iterator.h"
32 #include "src/interpreter/interpreter.h"
33 #include "src/logging/counters.h"
34 #include "src/objects/api-callbacks-inl.h"
35 #include "src/objects/debug-objects-inl.h"
36 #include "src/objects/js-generator-inl.h"
37 #include "src/objects/js-promise-inl.h"
38 #include "src/objects/slots.h"
39 #include "src/snapshot/snapshot.h"
40 #include "src/wasm/wasm-debug.h"
41 #include "src/wasm/wasm-objects-inl.h"
42
43 namespace v8 {
44 namespace internal {
45
46 class Debug::TemporaryObjectsTracker : public HeapObjectAllocationTracker {
47 public:
48 TemporaryObjectsTracker() = default;
49 ~TemporaryObjectsTracker() override = default;
50
AllocationEvent(Address addr,int)51 void AllocationEvent(Address addr, int) override { objects_.insert(addr); }
52
MoveEvent(Address from,Address to,int)53 void MoveEvent(Address from, Address to, int) override {
54 if (from == to) return;
55 base::MutexGuard guard(&mutex_);
56 auto it = objects_.find(from);
57 if (it == objects_.end()) {
58 // If temporary object was collected we can get MoveEvent which moves
59 // existing non temporary object to the address where we had temporary
60 // object. So we should mark new address as non temporary.
61 objects_.erase(to);
62 return;
63 }
64 objects_.erase(it);
65 objects_.insert(to);
66 }
67
HasObject(Handle<HeapObject> obj) const68 bool HasObject(Handle<HeapObject> obj) const {
69 if (obj->IsJSObject() &&
70 Handle<JSObject>::cast(obj)->GetEmbedderFieldCount()) {
71 // Embedder may store any pointers using embedder fields and implements
72 // non trivial logic, e.g. create wrappers lazily and store pointer to
73 // native object inside embedder field. We should consider all objects
74 // with embedder fields as non temporary.
75 return false;
76 }
77 return objects_.find(obj->address()) != objects_.end();
78 }
79
80 private:
81 std::unordered_set<Address> objects_;
82 base::Mutex mutex_;
83 DISALLOW_COPY_AND_ASSIGN(TemporaryObjectsTracker);
84 };
85
Debug(Isolate * isolate)86 Debug::Debug(Isolate* isolate)
87 : is_active_(false),
88 hook_on_function_call_(false),
89 is_suppressed_(false),
90 break_disabled_(false),
91 break_points_active_(true),
92 break_on_exception_(false),
93 break_on_uncaught_exception_(false),
94 side_effect_check_failed_(false),
95 debug_info_list_(nullptr),
96 feature_tracker_(isolate),
97 isolate_(isolate) {
98 ThreadInit();
99 }
100
~Debug()101 Debug::~Debug() { DCHECK_NULL(debug_delegate_); }
102
FromFrame(Handle<DebugInfo> debug_info,JavaScriptFrame * frame)103 BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info,
104 JavaScriptFrame* frame) {
105 if (debug_info->CanBreakAtEntry()) {
106 return BreakLocation(Debug::kBreakAtEntryPosition, DEBUG_BREAK_AT_ENTRY);
107 }
108 auto summary = FrameSummary::GetTop(frame).AsJavaScript();
109 int offset = summary.code_offset();
110 Handle<AbstractCode> abstract_code = summary.abstract_code();
111 BreakIterator it(debug_info);
112 it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
113 return it.GetBreakLocation();
114 }
115
AllAtCurrentStatement(Handle<DebugInfo> debug_info,JavaScriptFrame * frame,std::vector<BreakLocation> * result_out)116 void BreakLocation::AllAtCurrentStatement(
117 Handle<DebugInfo> debug_info, JavaScriptFrame* frame,
118 std::vector<BreakLocation>* result_out) {
119 DCHECK(!debug_info->CanBreakAtEntry());
120 auto summary = FrameSummary::GetTop(frame).AsJavaScript();
121 int offset = summary.code_offset();
122 Handle<AbstractCode> abstract_code = summary.abstract_code();
123 if (abstract_code->IsCode()) offset = offset - 1;
124 int statement_position;
125 {
126 BreakIterator it(debug_info);
127 it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
128 statement_position = it.statement_position();
129 }
130 for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
131 if (it.statement_position() == statement_position) {
132 result_out->push_back(it.GetBreakLocation());
133 }
134 }
135 }
136
GetGeneratorObjectForSuspendedFrame(JavaScriptFrame * frame) const137 JSGeneratorObject BreakLocation::GetGeneratorObjectForSuspendedFrame(
138 JavaScriptFrame* frame) const {
139 DCHECK(IsSuspend());
140 DCHECK_GE(generator_obj_reg_index_, 0);
141
142 Object generator_obj = InterpretedFrame::cast(frame)->ReadInterpreterRegister(
143 generator_obj_reg_index_);
144
145 return JSGeneratorObject::cast(generator_obj);
146 }
147
BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info,Handle<AbstractCode> abstract_code,int offset)148 int BreakLocation::BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info,
149 Handle<AbstractCode> abstract_code,
150 int offset) {
151 // Run through all break points to locate the one closest to the address.
152 int closest_break = 0;
153 int distance = kMaxInt;
154 DCHECK(0 <= offset && offset < abstract_code->Size());
155 for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
156 // Check if this break point is closer that what was previously found.
157 if (it.code_offset() <= offset && offset - it.code_offset() < distance) {
158 closest_break = it.break_index();
159 distance = offset - it.code_offset();
160 // Check whether we can't get any closer.
161 if (distance == 0) break;
162 }
163 }
164 return closest_break;
165 }
166
HasBreakPoint(Isolate * isolate,Handle<DebugInfo> debug_info) const167 bool BreakLocation::HasBreakPoint(Isolate* isolate,
168 Handle<DebugInfo> debug_info) const {
169 // First check whether there is a break point with the same source position.
170 if (!debug_info->HasBreakPoint(isolate, position_)) return false;
171 if (debug_info->CanBreakAtEntry()) {
172 DCHECK_EQ(Debug::kBreakAtEntryPosition, position_);
173 return debug_info->BreakAtEntry();
174 } else {
175 // Then check whether a break point at that source position would have
176 // the same code offset. Otherwise it's just a break location that we can
177 // step to, but not actually a location where we can put a break point.
178 DCHECK(abstract_code_->IsBytecodeArray());
179 BreakIterator it(debug_info);
180 it.SkipToPosition(position_);
181 return it.code_offset() == code_offset_;
182 }
183 }
184
type() const185 debug::BreakLocationType BreakLocation::type() const {
186 switch (type_) {
187 case DEBUGGER_STATEMENT:
188 return debug::kDebuggerStatementBreakLocation;
189 case DEBUG_BREAK_SLOT_AT_CALL:
190 return debug::kCallBreakLocation;
191 case DEBUG_BREAK_SLOT_AT_RETURN:
192 return debug::kReturnBreakLocation;
193
194 // Externally, suspend breaks should look like normal breaks.
195 case DEBUG_BREAK_SLOT_AT_SUSPEND:
196 default:
197 return debug::kCommonBreakLocation;
198 }
199 }
200
BreakIterator(Handle<DebugInfo> debug_info)201 BreakIterator::BreakIterator(Handle<DebugInfo> debug_info)
202 : debug_info_(debug_info),
203 break_index_(-1),
204 source_position_iterator_(
205 debug_info->DebugBytecodeArray().SourcePositionTable()) {
206 position_ = debug_info->shared().StartPosition();
207 statement_position_ = position_;
208 // There is at least one break location.
209 DCHECK(!Done());
210 Next();
211 }
212
BreakIndexFromPosition(int source_position)213 int BreakIterator::BreakIndexFromPosition(int source_position) {
214 int distance = kMaxInt;
215 int closest_break = break_index();
216 while (!Done()) {
217 int next_position = position();
218 if (source_position <= next_position &&
219 next_position - source_position < distance) {
220 closest_break = break_index();
221 distance = next_position - source_position;
222 // Check whether we can't get any closer.
223 if (distance == 0) break;
224 }
225 Next();
226 }
227 return closest_break;
228 }
229
Next()230 void BreakIterator::Next() {
231 DisallowHeapAllocation no_gc;
232 DCHECK(!Done());
233 bool first = break_index_ == -1;
234 while (!Done()) {
235 if (!first) source_position_iterator_.Advance();
236 first = false;
237 if (Done()) return;
238 position_ = source_position_iterator_.source_position().ScriptOffset();
239 if (source_position_iterator_.is_statement()) {
240 statement_position_ = position_;
241 }
242 DCHECK_LE(0, position_);
243 DCHECK_LE(0, statement_position_);
244
245 DebugBreakType type = GetDebugBreakType();
246 if (type != NOT_DEBUG_BREAK) break;
247 }
248 break_index_++;
249 }
250
GetDebugBreakType()251 DebugBreakType BreakIterator::GetDebugBreakType() {
252 BytecodeArray bytecode_array = debug_info_->OriginalBytecodeArray();
253 interpreter::Bytecode bytecode =
254 interpreter::Bytecodes::FromByte(bytecode_array.get(code_offset()));
255
256 // Make sure we read the actual bytecode, not a prefix scaling bytecode.
257 if (interpreter::Bytecodes::IsPrefixScalingBytecode(bytecode)) {
258 bytecode =
259 interpreter::Bytecodes::FromByte(bytecode_array.get(code_offset() + 1));
260 }
261
262 if (bytecode == interpreter::Bytecode::kDebugger) {
263 return DEBUGGER_STATEMENT;
264 } else if (bytecode == interpreter::Bytecode::kReturn) {
265 return DEBUG_BREAK_SLOT_AT_RETURN;
266 } else if (bytecode == interpreter::Bytecode::kSuspendGenerator) {
267 return DEBUG_BREAK_SLOT_AT_SUSPEND;
268 } else if (interpreter::Bytecodes::IsCallOrConstruct(bytecode)) {
269 return DEBUG_BREAK_SLOT_AT_CALL;
270 } else if (source_position_iterator_.is_statement()) {
271 return DEBUG_BREAK_SLOT;
272 } else {
273 return NOT_DEBUG_BREAK;
274 }
275 }
276
SkipToPosition(int position)277 void BreakIterator::SkipToPosition(int position) {
278 BreakIterator it(debug_info_);
279 SkipTo(it.BreakIndexFromPosition(position));
280 }
281
SetDebugBreak()282 void BreakIterator::SetDebugBreak() {
283 DebugBreakType debug_break_type = GetDebugBreakType();
284 if (debug_break_type == DEBUGGER_STATEMENT) return;
285 HandleScope scope(isolate());
286 DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
287 Handle<BytecodeArray> bytecode_array(debug_info_->DebugBytecodeArray(),
288 isolate());
289 interpreter::BytecodeArrayAccessor(bytecode_array, code_offset())
290 .ApplyDebugBreak();
291 }
292
ClearDebugBreak()293 void BreakIterator::ClearDebugBreak() {
294 DebugBreakType debug_break_type = GetDebugBreakType();
295 if (debug_break_type == DEBUGGER_STATEMENT) return;
296 DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
297 BytecodeArray bytecode_array = debug_info_->DebugBytecodeArray();
298 BytecodeArray original = debug_info_->OriginalBytecodeArray();
299 bytecode_array.set(code_offset(), original.get(code_offset()));
300 }
301
GetBreakLocation()302 BreakLocation BreakIterator::GetBreakLocation() {
303 Handle<AbstractCode> code(
304 AbstractCode::cast(debug_info_->DebugBytecodeArray()), isolate());
305 DebugBreakType type = GetDebugBreakType();
306 int generator_object_reg_index = -1;
307 if (type == DEBUG_BREAK_SLOT_AT_SUSPEND) {
308 // For suspend break, we'll need the generator object to be able to step
309 // over the suspend as if it didn't return. We get the interpreter register
310 // index that holds the generator object by reading it directly off the
311 // bytecode array, and we'll read the actual generator object off the
312 // interpreter stack frame in GetGeneratorObjectForSuspendedFrame.
313 BytecodeArray bytecode_array = debug_info_->OriginalBytecodeArray();
314 interpreter::BytecodeArrayAccessor accessor(
315 handle(bytecode_array, isolate()), code_offset());
316
317 DCHECK_EQ(accessor.current_bytecode(),
318 interpreter::Bytecode::kSuspendGenerator);
319 interpreter::Register generator_obj_reg = accessor.GetRegisterOperand(0);
320 generator_object_reg_index = generator_obj_reg.index();
321 }
322 return BreakLocation(code, type, code_offset(), position_,
323 generator_object_reg_index);
324 }
325
isolate()326 Isolate* BreakIterator::isolate() { return debug_info_->GetIsolate(); }
327
Track(DebugFeatureTracker::Feature feature)328 void DebugFeatureTracker::Track(DebugFeatureTracker::Feature feature) {
329 uint32_t mask = 1 << feature;
330 // Only count one sample per feature and isolate.
331 if (bitfield_ & mask) return;
332 isolate_->counters()->debug_feature_usage()->AddSample(feature);
333 bitfield_ |= mask;
334 }
335
336
337 // Threading support.
ThreadInit()338 void Debug::ThreadInit() {
339 thread_local_.break_frame_id_ = StackFrameId::NO_ID;
340 thread_local_.last_step_action_ = StepNone;
341 thread_local_.last_statement_position_ = kNoSourcePosition;
342 thread_local_.last_frame_count_ = -1;
343 thread_local_.fast_forward_to_return_ = false;
344 thread_local_.ignore_step_into_function_ = Smi::zero();
345 thread_local_.target_frame_count_ = -1;
346 thread_local_.return_value_ = Smi::zero();
347 thread_local_.last_breakpoint_id_ = 0;
348 clear_suspended_generator();
349 thread_local_.restart_fp_ = kNullAddress;
350 base::Relaxed_Store(&thread_local_.current_debug_scope_,
351 static_cast<base::AtomicWord>(0));
352 thread_local_.break_on_next_function_call_ = false;
353 UpdateHookOnFunctionCall();
354 }
355
356
ArchiveDebug(char * storage)357 char* Debug::ArchiveDebug(char* storage) {
358 MemCopy(storage, reinterpret_cast<char*>(&thread_local_),
359 ArchiveSpacePerThread());
360 return storage + ArchiveSpacePerThread();
361 }
362
RestoreDebug(char * storage)363 char* Debug::RestoreDebug(char* storage) {
364 MemCopy(reinterpret_cast<char*>(&thread_local_), storage,
365 ArchiveSpacePerThread());
366
367 // Enter the debugger.
368 DebugScope debug_scope(this);
369
370 // Clear any one-shot breakpoints that may have been set by the other
371 // thread, and reapply breakpoints for this thread.
372 ClearOneShot();
373
374 if (thread_local_.last_step_action_ != StepNone) {
375 int current_frame_count = CurrentFrameCount();
376 int target_frame_count = thread_local_.target_frame_count_;
377 DCHECK(current_frame_count >= target_frame_count);
378 StackTraceFrameIterator frames_it(isolate_);
379 while (current_frame_count > target_frame_count) {
380 current_frame_count -= frames_it.FrameFunctionCount();
381 frames_it.Advance();
382 }
383 DCHECK(current_frame_count == target_frame_count);
384 // Set frame to what it was at Step break
385 thread_local_.break_frame_id_ = frames_it.frame()->id();
386
387 // Reset the previous step action for this thread.
388 PrepareStep(thread_local_.last_step_action_);
389 }
390
391 return storage + ArchiveSpacePerThread();
392 }
393
ArchiveSpacePerThread()394 int Debug::ArchiveSpacePerThread() { return sizeof(ThreadLocal); }
395
Iterate(RootVisitor * v)396 void Debug::Iterate(RootVisitor* v) {
397 v->VisitRootPointer(Root::kDebug, nullptr,
398 FullObjectSlot(&thread_local_.return_value_));
399 v->VisitRootPointer(Root::kDebug, nullptr,
400 FullObjectSlot(&thread_local_.suspended_generator_));
401 v->VisitRootPointer(
402 Root::kDebug, nullptr,
403 FullObjectSlot(&thread_local_.ignore_step_into_function_));
404 }
405
DebugInfoListNode(Isolate * isolate,DebugInfo debug_info)406 DebugInfoListNode::DebugInfoListNode(Isolate* isolate, DebugInfo debug_info)
407 : next_(nullptr) {
408 // Globalize the request debug info object and make it weak.
409 GlobalHandles* global_handles = isolate->global_handles();
410 debug_info_ = global_handles->Create(debug_info).location();
411 }
412
~DebugInfoListNode()413 DebugInfoListNode::~DebugInfoListNode() {
414 if (debug_info_ == nullptr) return;
415 GlobalHandles::Destroy(debug_info_);
416 debug_info_ = nullptr;
417 }
418
Unload()419 void Debug::Unload() {
420 ClearAllBreakPoints();
421 ClearStepping();
422 RemoveAllCoverageInfos();
423 ClearAllDebuggerHints();
424 debug_delegate_ = nullptr;
425 }
426
Break(JavaScriptFrame * frame,Handle<JSFunction> break_target)427 void Debug::Break(JavaScriptFrame* frame, Handle<JSFunction> break_target) {
428 // Initialize LiveEdit.
429 LiveEdit::InitializeThreadLocal(this);
430
431 // Just continue if breaks are disabled or debugger cannot be loaded.
432 if (break_disabled()) return;
433
434 // Enter the debugger.
435 DebugScope debug_scope(this);
436 DisableBreak no_recursive_break(this);
437
438 // Return if we fail to retrieve debug info.
439 Handle<SharedFunctionInfo> shared(break_target->shared(), isolate_);
440 if (!EnsureBreakInfo(shared)) return;
441 PrepareFunctionForDebugExecution(shared);
442
443 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
444
445 // Find the break location where execution has stopped.
446 BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
447
448 // Find actual break points, if any, and trigger debug break event.
449 MaybeHandle<FixedArray> break_points_hit =
450 CheckBreakPoints(debug_info, &location);
451 if (!break_points_hit.is_null() || break_on_next_function_call()) {
452 StepAction lastStepAction = last_step_action();
453 // Clear all current stepping setup.
454 ClearStepping();
455 // Notify the debug event listeners.
456 OnDebugBreak(!break_points_hit.is_null()
457 ? break_points_hit.ToHandleChecked()
458 : isolate_->factory()->empty_fixed_array(),
459 lastStepAction);
460 return;
461 }
462
463 // Debug break at function entry, do not worry about stepping.
464 if (location.IsDebugBreakAtEntry()) {
465 DCHECK(debug_info->BreakAtEntry());
466 return;
467 }
468
469 DCHECK_NOT_NULL(frame);
470
471 // No break point. Check for stepping.
472 StepAction step_action = last_step_action();
473 int current_frame_count = CurrentFrameCount();
474 int target_frame_count = thread_local_.target_frame_count_;
475 int last_frame_count = thread_local_.last_frame_count_;
476
477 // StepOut at not return position was requested and return break locations
478 // were flooded with one shots.
479 if (thread_local_.fast_forward_to_return_) {
480 DCHECK(location.IsReturnOrSuspend());
481 // We have to ignore recursive calls to function.
482 if (current_frame_count > target_frame_count) return;
483 ClearStepping();
484 PrepareStep(StepOut);
485 return;
486 }
487
488 bool step_break = false;
489 switch (step_action) {
490 case StepNone:
491 return;
492 case StepOut:
493 // Step out should not break in a deeper frame than target frame.
494 if (current_frame_count > target_frame_count) return;
495 step_break = true;
496 break;
497 case StepNext:
498 // Step next should not break in a deeper frame than target frame.
499 if (current_frame_count > target_frame_count) return;
500 V8_FALLTHROUGH;
501 case StepIn: {
502 // Special case "next" and "in" for generators that are about to suspend.
503 if (location.IsSuspend()) {
504 DCHECK(!has_suspended_generator());
505 thread_local_.suspended_generator_ =
506 location.GetGeneratorObjectForSuspendedFrame(frame);
507 ClearStepping();
508 return;
509 }
510
511 FrameSummary summary = FrameSummary::GetTop(frame);
512 step_break = step_break || location.IsReturn() ||
513 current_frame_count != last_frame_count ||
514 thread_local_.last_statement_position_ !=
515 summary.SourceStatementPosition();
516 break;
517 }
518 }
519
520 StepAction lastStepAction = last_step_action();
521 // Clear all current stepping setup.
522 ClearStepping();
523
524 if (step_break) {
525 // Notify the debug event listeners.
526 OnDebugBreak(isolate_->factory()->empty_fixed_array(), lastStepAction);
527 } else {
528 // Re-prepare to continue.
529 PrepareStep(step_action);
530 }
531 }
532
533
534 // Find break point objects for this location, if any, and evaluate them.
535 // Return an array of break point objects that evaluated true, or an empty
536 // handle if none evaluated true.
CheckBreakPoints(Handle<DebugInfo> debug_info,BreakLocation * location,bool * has_break_points)537 MaybeHandle<FixedArray> Debug::CheckBreakPoints(Handle<DebugInfo> debug_info,
538 BreakLocation* location,
539 bool* has_break_points) {
540 bool has_break_points_to_check =
541 break_points_active_ && location->HasBreakPoint(isolate_, debug_info);
542 if (has_break_points) *has_break_points = has_break_points_to_check;
543 if (!has_break_points_to_check) return {};
544
545 return Debug::GetHitBreakPoints(debug_info, location->position());
546 }
547
548
IsMutedAtCurrentLocation(JavaScriptFrame * frame)549 bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) {
550 HandleScope scope(isolate_);
551 // A break location is considered muted if break locations on the current
552 // statement have at least one break point, and all of these break points
553 // evaluate to false. Aside from not triggering a debug break event at the
554 // break location, we also do not trigger one for debugger statements, nor
555 // an exception event on exception at this location.
556 FrameSummary summary = FrameSummary::GetTop(frame);
557 DCHECK(!summary.IsWasm());
558 Handle<JSFunction> function = summary.AsJavaScript().function();
559 if (!function->shared().HasBreakInfo()) return false;
560 Handle<DebugInfo> debug_info(function->shared().GetDebugInfo(), isolate_);
561 // Enter the debugger.
562 DebugScope debug_scope(this);
563 std::vector<BreakLocation> break_locations;
564 BreakLocation::AllAtCurrentStatement(debug_info, frame, &break_locations);
565 bool has_break_points_at_all = false;
566 for (size_t i = 0; i < break_locations.size(); i++) {
567 bool has_break_points;
568 MaybeHandle<FixedArray> check_result =
569 CheckBreakPoints(debug_info, &break_locations[i], &has_break_points);
570 has_break_points_at_all |= has_break_points;
571 if (has_break_points && !check_result.is_null()) return false;
572 }
573 return has_break_points_at_all;
574 }
575
576 // Check whether a single break point object is triggered.
CheckBreakPoint(Handle<BreakPoint> break_point,bool is_break_at_entry)577 bool Debug::CheckBreakPoint(Handle<BreakPoint> break_point,
578 bool is_break_at_entry) {
579 HandleScope scope(isolate_);
580
581 if (!break_point->condition().length()) return true;
582 Handle<String> condition(break_point->condition(), isolate_);
583 MaybeHandle<Object> maybe_result;
584 Handle<Object> result;
585
586 if (is_break_at_entry) {
587 maybe_result = DebugEvaluate::WithTopmostArguments(isolate_, condition);
588 } else {
589 // Since we call CheckBreakpoint only for deoptimized frame on top of stack,
590 // we can use 0 as index of inlined frame.
591 const int inlined_jsframe_index = 0;
592 const bool throw_on_side_effect = false;
593 maybe_result =
594 DebugEvaluate::Local(isolate_, break_frame_id(), inlined_jsframe_index,
595 condition, throw_on_side_effect);
596 }
597
598 if (!maybe_result.ToHandle(&result)) {
599 if (isolate_->has_pending_exception()) {
600 isolate_->clear_pending_exception();
601 }
602 return false;
603 }
604 return result->BooleanValue(isolate_);
605 }
606
SetBreakpoint(Handle<SharedFunctionInfo> shared,Handle<BreakPoint> break_point,int * source_position)607 bool Debug::SetBreakpoint(Handle<SharedFunctionInfo> shared,
608 Handle<BreakPoint> break_point,
609 int* source_position) {
610 HandleScope scope(isolate_);
611
612 // Make sure the function is compiled and has set up the debug info.
613 if (!EnsureBreakInfo(shared)) return false;
614 PrepareFunctionForDebugExecution(shared);
615
616 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
617 // Source positions starts with zero.
618 DCHECK_LE(0, *source_position);
619
620 // Find the break point and change it.
621 *source_position = FindBreakablePosition(debug_info, *source_position);
622 DebugInfo::SetBreakPoint(isolate_, debug_info, *source_position, break_point);
623 // At least one active break point now.
624 DCHECK_LT(0, debug_info->GetBreakPointCount(isolate_));
625
626 ClearBreakPoints(debug_info);
627 ApplyBreakPoints(debug_info);
628
629 feature_tracker()->Track(DebugFeatureTracker::kBreakPoint);
630 return true;
631 }
632
SetBreakPointForScript(Handle<Script> script,Handle<String> condition,int * source_position,int * id)633 bool Debug::SetBreakPointForScript(Handle<Script> script,
634 Handle<String> condition,
635 int* source_position, int* id) {
636 *id = ++thread_local_.last_breakpoint_id_;
637 Handle<BreakPoint> break_point =
638 isolate_->factory()->NewBreakPoint(*id, condition);
639 if (script->type() == Script::TYPE_WASM) {
640 RecordWasmScriptWithBreakpoints(script);
641 return WasmScript::SetBreakPoint(script, source_position, break_point);
642 }
643
644 HandleScope scope(isolate_);
645
646 // Obtain shared function info for the function.
647 Handle<Object> result =
648 FindSharedFunctionInfoInScript(script, *source_position);
649 if (result->IsUndefined(isolate_)) return false;
650
651 // Make sure the function has set up the debug info.
652 Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result);
653 if (!EnsureBreakInfo(shared)) return false;
654 PrepareFunctionForDebugExecution(shared);
655
656 // Find position within function. The script position might be before the
657 // source position of the first function.
658 if (shared->StartPosition() > *source_position) {
659 *source_position = shared->StartPosition();
660 }
661
662 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
663
664 // Find breakable position returns first breakable position after
665 // *source_position, it can return 0 if no break location is found after
666 // *source_position.
667 int breakable_position = FindBreakablePosition(debug_info, *source_position);
668 if (breakable_position < *source_position) return false;
669 *source_position = breakable_position;
670
671 DebugInfo::SetBreakPoint(isolate_, debug_info, *source_position, break_point);
672 // At least one active break point now.
673 DCHECK_LT(0, debug_info->GetBreakPointCount(isolate_));
674
675 ClearBreakPoints(debug_info);
676 ApplyBreakPoints(debug_info);
677
678 feature_tracker()->Track(DebugFeatureTracker::kBreakPoint);
679 return true;
680 }
681
FindBreakablePosition(Handle<DebugInfo> debug_info,int source_position)682 int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info,
683 int source_position) {
684 if (debug_info->CanBreakAtEntry()) {
685 return kBreakAtEntryPosition;
686 } else {
687 DCHECK(debug_info->HasInstrumentedBytecodeArray());
688 BreakIterator it(debug_info);
689 it.SkipToPosition(source_position);
690 return it.position();
691 }
692 }
693
ApplyBreakPoints(Handle<DebugInfo> debug_info)694 void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) {
695 DisallowHeapAllocation no_gc;
696 if (debug_info->CanBreakAtEntry()) {
697 debug_info->SetBreakAtEntry();
698 } else {
699 if (!debug_info->HasInstrumentedBytecodeArray()) return;
700 FixedArray break_points = debug_info->break_points();
701 for (int i = 0; i < break_points.length(); i++) {
702 if (break_points.get(i).IsUndefined(isolate_)) continue;
703 BreakPointInfo info = BreakPointInfo::cast(break_points.get(i));
704 if (info.GetBreakPointCount(isolate_) == 0) continue;
705 DCHECK(debug_info->HasInstrumentedBytecodeArray());
706 BreakIterator it(debug_info);
707 it.SkipToPosition(info.source_position());
708 it.SetDebugBreak();
709 }
710 }
711 debug_info->SetDebugExecutionMode(DebugInfo::kBreakpoints);
712 }
713
ClearBreakPoints(Handle<DebugInfo> debug_info)714 void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) {
715 if (debug_info->CanBreakAtEntry()) {
716 debug_info->ClearBreakAtEntry();
717 } else {
718 // If we attempt to clear breakpoints but none exist, simply return. This
719 // can happen e.g. CoverageInfos exist but no breakpoints are set.
720 if (!debug_info->HasInstrumentedBytecodeArray() ||
721 !debug_info->HasBreakInfo()) {
722 return;
723 }
724
725 DisallowHeapAllocation no_gc;
726 for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
727 it.ClearDebugBreak();
728 }
729 }
730 }
731
ClearBreakPoint(Handle<BreakPoint> break_point)732 void Debug::ClearBreakPoint(Handle<BreakPoint> break_point) {
733 HandleScope scope(isolate_);
734
735 for (DebugInfoListNode* node = debug_info_list_; node != nullptr;
736 node = node->next()) {
737 if (!node->debug_info()->HasBreakInfo()) continue;
738 Handle<Object> result = DebugInfo::FindBreakPointInfo(
739 isolate_, node->debug_info(), break_point);
740 if (result->IsUndefined(isolate_)) continue;
741 Handle<DebugInfo> debug_info = node->debug_info();
742 if (DebugInfo::ClearBreakPoint(isolate_, debug_info, break_point)) {
743 ClearBreakPoints(debug_info);
744 if (debug_info->GetBreakPointCount(isolate_) == 0) {
745 RemoveBreakInfoAndMaybeFree(debug_info);
746 } else {
747 ApplyBreakPoints(debug_info);
748 }
749 return;
750 }
751 }
752 }
753
GetFunctionDebuggingId(Handle<JSFunction> function)754 int Debug::GetFunctionDebuggingId(Handle<JSFunction> function) {
755 Handle<SharedFunctionInfo> shared = handle(function->shared(), isolate_);
756 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
757 int id = debug_info->debugging_id();
758 if (id == DebugInfo::kNoDebuggingId) {
759 id = isolate_->heap()->NextDebuggingId();
760 debug_info->set_debugging_id(id);
761 }
762 return id;
763 }
764
SetBreakpointForFunction(Handle<SharedFunctionInfo> shared,Handle<String> condition,int * id)765 bool Debug::SetBreakpointForFunction(Handle<SharedFunctionInfo> shared,
766 Handle<String> condition, int* id) {
767 *id = ++thread_local_.last_breakpoint_id_;
768 Handle<BreakPoint> breakpoint =
769 isolate_->factory()->NewBreakPoint(*id, condition);
770 int source_position = 0;
771 // Handle wasm function.
772 if (shared->HasWasmExportedFunctionData()) {
773 int func_index = shared->wasm_exported_function_data().function_index();
774 Handle<WasmInstanceObject> wasm_instance(
775 shared->wasm_exported_function_data().instance(), isolate_);
776 Handle<Script> script(Script::cast(wasm_instance->module_object().script()),
777 isolate_);
778 return WasmScript::SetBreakPointOnFirstBreakableForFunction(
779 script, func_index, breakpoint);
780 }
781 return SetBreakpoint(shared, breakpoint, &source_position);
782 }
783
RemoveBreakpoint(int id)784 void Debug::RemoveBreakpoint(int id) {
785 Handle<BreakPoint> breakpoint = isolate_->factory()->NewBreakPoint(
786 id, isolate_->factory()->empty_string());
787 ClearBreakPoint(breakpoint);
788 }
789
RemoveBreakpointForWasmScript(Handle<Script> script,int id)790 void Debug::RemoveBreakpointForWasmScript(Handle<Script> script, int id) {
791 if (script->type() == Script::TYPE_WASM) {
792 WasmScript::ClearBreakPointById(script, id);
793 }
794 }
795
RecordWasmScriptWithBreakpoints(Handle<Script> script)796 void Debug::RecordWasmScriptWithBreakpoints(Handle<Script> script) {
797 if (wasm_scripts_with_breakpoints_.is_null()) {
798 Handle<WeakArrayList> new_list = isolate_->factory()->NewWeakArrayList(4);
799 wasm_scripts_with_breakpoints_ =
800 isolate_->global_handles()->Create(*new_list);
801 }
802 {
803 DisallowHeapAllocation no_gc;
804 for (int idx = wasm_scripts_with_breakpoints_->length() - 1; idx >= 0;
805 --idx) {
806 HeapObject wasm_script;
807 if (wasm_scripts_with_breakpoints_->Get(idx).GetHeapObject(
808 &wasm_script) &&
809 wasm_script == *script) {
810 return;
811 }
812 }
813 }
814 Handle<WeakArrayList> new_list = WeakArrayList::Append(
815 isolate_, wasm_scripts_with_breakpoints_, MaybeObjectHandle{script});
816 if (*new_list != *wasm_scripts_with_breakpoints_) {
817 isolate_->global_handles()->Destroy(
818 wasm_scripts_with_breakpoints_.location());
819 wasm_scripts_with_breakpoints_ =
820 isolate_->global_handles()->Create(*new_list);
821 }
822 }
823
824 // Clear out all the debug break code.
ClearAllBreakPoints()825 void Debug::ClearAllBreakPoints() {
826 ClearAllDebugInfos([=](Handle<DebugInfo> info) {
827 ClearBreakPoints(info);
828 info->ClearBreakInfo(isolate_);
829 });
830 // Clear all wasm breakpoints.
831 if (!wasm_scripts_with_breakpoints_.is_null()) {
832 DisallowHeapAllocation no_gc;
833 for (int idx = wasm_scripts_with_breakpoints_->length() - 1; idx >= 0;
834 --idx) {
835 HeapObject raw_wasm_script;
836 if (wasm_scripts_with_breakpoints_->Get(idx).GetHeapObject(
837 &raw_wasm_script)) {
838 Script wasm_script = Script::cast(raw_wasm_script);
839 WasmScript::ClearAllBreakpoints(wasm_script);
840 wasm_script.wasm_native_module()->GetDebugInfo()->RemoveIsolate(
841 isolate_);
842 }
843 }
844 wasm_scripts_with_breakpoints_ = Handle<WeakArrayList>{};
845 }
846 }
847
FloodWithOneShot(Handle<SharedFunctionInfo> shared,bool returns_only)848 void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared,
849 bool returns_only) {
850 if (IsBlackboxed(shared)) return;
851 // Make sure the function is compiled and has set up the debug info.
852 if (!EnsureBreakInfo(shared)) return;
853 PrepareFunctionForDebugExecution(shared);
854
855 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
856 // Flood the function with break points.
857 DCHECK(debug_info->HasInstrumentedBytecodeArray());
858 for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
859 if (returns_only && !it.GetBreakLocation().IsReturnOrSuspend()) continue;
860 it.SetDebugBreak();
861 }
862 }
863
ChangeBreakOnException(ExceptionBreakType type,bool enable)864 void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) {
865 if (type == BreakUncaughtException) {
866 break_on_uncaught_exception_ = enable;
867 } else {
868 break_on_exception_ = enable;
869 }
870 }
871
872
IsBreakOnException(ExceptionBreakType type)873 bool Debug::IsBreakOnException(ExceptionBreakType type) {
874 if (type == BreakUncaughtException) {
875 return break_on_uncaught_exception_;
876 } else {
877 return break_on_exception_;
878 }
879 }
880
GetHitBreakPoints(Handle<DebugInfo> debug_info,int position)881 MaybeHandle<FixedArray> Debug::GetHitBreakPoints(Handle<DebugInfo> debug_info,
882 int position) {
883 Handle<Object> break_points = debug_info->GetBreakPoints(isolate_, position);
884 bool is_break_at_entry = debug_info->BreakAtEntry();
885 DCHECK(!break_points->IsUndefined(isolate_));
886 if (!break_points->IsFixedArray()) {
887 if (!CheckBreakPoint(Handle<BreakPoint>::cast(break_points),
888 is_break_at_entry)) {
889 return {};
890 }
891 Handle<FixedArray> break_points_hit = isolate_->factory()->NewFixedArray(1);
892 break_points_hit->set(0, *break_points);
893 return break_points_hit;
894 }
895
896 Handle<FixedArray> array(FixedArray::cast(*break_points), isolate_);
897 int num_objects = array->length();
898 Handle<FixedArray> break_points_hit =
899 isolate_->factory()->NewFixedArray(num_objects);
900 int break_points_hit_count = 0;
901 for (int i = 0; i < num_objects; ++i) {
902 Handle<Object> break_point(array->get(i), isolate_);
903 if (CheckBreakPoint(Handle<BreakPoint>::cast(break_point),
904 is_break_at_entry)) {
905 break_points_hit->set(break_points_hit_count++, *break_point);
906 }
907 }
908 if (break_points_hit_count == 0) return {};
909 break_points_hit->Shrink(isolate_, break_points_hit_count);
910 return break_points_hit;
911 }
912
SetBreakOnNextFunctionCall()913 void Debug::SetBreakOnNextFunctionCall() {
914 // This method forces V8 to break on next function call regardless current
915 // last_step_action_. If any break happens between SetBreakOnNextFunctionCall
916 // and ClearBreakOnNextFunctionCall, we will clear this flag and stepping. If
917 // break does not happen, e.g. all called functions are blackboxed or no
918 // function is called, then we will clear this flag and let stepping continue
919 // its normal business.
920 thread_local_.break_on_next_function_call_ = true;
921 UpdateHookOnFunctionCall();
922 }
923
ClearBreakOnNextFunctionCall()924 void Debug::ClearBreakOnNextFunctionCall() {
925 thread_local_.break_on_next_function_call_ = false;
926 UpdateHookOnFunctionCall();
927 }
928
PrepareStepIn(Handle<JSFunction> function)929 void Debug::PrepareStepIn(Handle<JSFunction> function) {
930 CHECK(last_step_action() >= StepIn || break_on_next_function_call());
931 if (ignore_events()) return;
932 if (in_debug_scope()) return;
933 if (break_disabled()) return;
934 Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
935 if (IsBlackboxed(shared)) return;
936 if (*function == thread_local_.ignore_step_into_function_) return;
937 thread_local_.ignore_step_into_function_ = Smi::zero();
938 FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
939 }
940
PrepareStepInSuspendedGenerator()941 void Debug::PrepareStepInSuspendedGenerator() {
942 CHECK(has_suspended_generator());
943 if (ignore_events()) return;
944 if (in_debug_scope()) return;
945 if (break_disabled()) return;
946 thread_local_.last_step_action_ = StepIn;
947 UpdateHookOnFunctionCall();
948 Handle<JSFunction> function(
949 JSGeneratorObject::cast(thread_local_.suspended_generator_).function(),
950 isolate_);
951 FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
952 clear_suspended_generator();
953 }
954
PrepareStepOnThrow()955 void Debug::PrepareStepOnThrow() {
956 if (last_step_action() == StepNone) return;
957 if (ignore_events()) return;
958 if (in_debug_scope()) return;
959 if (break_disabled()) return;
960
961 ClearOneShot();
962
963 int current_frame_count = CurrentFrameCount();
964
965 // Iterate through the JavaScript stack looking for handlers.
966 JavaScriptFrameIterator it(isolate_);
967 while (!it.done()) {
968 JavaScriptFrame* frame = it.frame();
969 if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break;
970 std::vector<SharedFunctionInfo> infos;
971 frame->GetFunctions(&infos);
972 current_frame_count -= infos.size();
973 it.Advance();
974 }
975
976 // No handler found. Nothing to instrument.
977 if (it.done()) return;
978
979 bool found_handler = false;
980 // Iterate frames, including inlined frames. First, find the handler frame.
981 // Then skip to the frame we want to break in, then instrument for stepping.
982 for (; !it.done(); it.Advance()) {
983 JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame());
984 if (last_step_action() == StepIn) {
985 // Deoptimize frame to ensure calls are checked for step-in.
986 Deoptimizer::DeoptimizeFunction(frame->function());
987 }
988 std::vector<FrameSummary> summaries;
989 frame->Summarize(&summaries);
990 for (size_t i = summaries.size(); i != 0; i--, current_frame_count--) {
991 const FrameSummary& summary = summaries[i - 1];
992 if (!found_handler) {
993 // We have yet to find the handler. If the frame inlines multiple
994 // functions, we have to check each one for the handler.
995 // If it only contains one function, we already found the handler.
996 if (summaries.size() > 1) {
997 Handle<AbstractCode> code = summary.AsJavaScript().abstract_code();
998 CHECK_EQ(CodeKind::INTERPRETED_FUNCTION, code->kind());
999 HandlerTable table(code->GetBytecodeArray());
1000 int code_offset = summary.code_offset();
1001 HandlerTable::CatchPrediction prediction;
1002 int index = table.LookupRange(code_offset, nullptr, &prediction);
1003 if (index > 0) found_handler = true;
1004 } else {
1005 found_handler = true;
1006 }
1007 }
1008
1009 if (found_handler) {
1010 // We found the handler. If we are stepping next or out, we need to
1011 // iterate until we found the suitable target frame to break in.
1012 if ((last_step_action() == StepNext || last_step_action() == StepOut) &&
1013 current_frame_count > thread_local_.target_frame_count_) {
1014 continue;
1015 }
1016 Handle<SharedFunctionInfo> info(
1017 summary.AsJavaScript().function()->shared(), isolate_);
1018 if (IsBlackboxed(info)) continue;
1019 FloodWithOneShot(info);
1020 return;
1021 }
1022 }
1023 }
1024 }
1025
PrepareStep(StepAction step_action)1026 void Debug::PrepareStep(StepAction step_action) {
1027 HandleScope scope(isolate_);
1028
1029 DCHECK(in_debug_scope());
1030
1031 // Get the frame where the execution has stopped and skip the debug frame if
1032 // any. The debug frame will only be present if execution was stopped due to
1033 // hitting a break point. In other situations (e.g. unhandled exception) the
1034 // debug frame is not present.
1035 StackFrameId frame_id = break_frame_id();
1036 // If there is no JavaScript stack don't do anything.
1037 if (frame_id == StackFrameId::NO_ID) return;
1038
1039 feature_tracker()->Track(DebugFeatureTracker::kStepping);
1040
1041 thread_local_.last_step_action_ = step_action;
1042
1043 StackTraceFrameIterator frames_it(isolate_, frame_id);
1044 CommonFrame* frame = frames_it.frame();
1045
1046 BreakLocation location = BreakLocation::Invalid();
1047 Handle<SharedFunctionInfo> shared;
1048 int current_frame_count = CurrentFrameCount();
1049
1050 if (frame->is_java_script()) {
1051 JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
1052 DCHECK(js_frame->function().IsJSFunction());
1053
1054 // Get the debug info (create it if it does not exist).
1055 auto summary = FrameSummary::GetTop(frame).AsJavaScript();
1056 Handle<JSFunction> function(summary.function());
1057 shared = Handle<SharedFunctionInfo>(function->shared(), isolate_);
1058 if (!EnsureBreakInfo(shared)) return;
1059 PrepareFunctionForDebugExecution(shared);
1060
1061 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
1062
1063 location = BreakLocation::FromFrame(debug_info, js_frame);
1064
1065 // Any step at a return is a step-out, and a step-out at a suspend behaves
1066 // like a return.
1067 if (location.IsReturn() ||
1068 (location.IsSuspend() && step_action == StepOut)) {
1069 // On StepOut we'll ignore our further calls to current function in
1070 // PrepareStepIn callback.
1071 if (last_step_action() == StepOut) {
1072 thread_local_.ignore_step_into_function_ = *function;
1073 }
1074 step_action = StepOut;
1075 thread_local_.last_step_action_ = StepIn;
1076 }
1077
1078 // We need to schedule DebugOnFunction call callback
1079 UpdateHookOnFunctionCall();
1080
1081 // A step-next in blackboxed function is a step-out.
1082 if (step_action == StepNext && IsBlackboxed(shared)) step_action = StepOut;
1083
1084 thread_local_.last_statement_position_ =
1085 summary.abstract_code()->SourceStatementPosition(summary.code_offset());
1086 thread_local_.last_frame_count_ = current_frame_count;
1087 // No longer perform the current async step.
1088 clear_suspended_generator();
1089 } else if (frame->is_wasm()) {
1090 // Handle stepping in Liftoff code.
1091 WasmFrame* wasm_frame = WasmFrame::cast(frame);
1092 wasm::WasmCodeRefScope code_ref_scope;
1093 wasm::WasmCode* code = wasm_frame->wasm_code();
1094 if (code->is_liftoff()) {
1095 wasm_frame->native_module()->GetDebugInfo()->PrepareStep(isolate_,
1096 frame_id);
1097 }
1098 // In case the wasm code returns, prepare the next frame (if JS) to break.
1099 step_action = StepOut;
1100 UpdateHookOnFunctionCall();
1101 }
1102
1103 switch (step_action) {
1104 case StepNone:
1105 UNREACHABLE();
1106 case StepOut: {
1107 // Clear last position info. For stepping out it does not matter.
1108 thread_local_.last_statement_position_ = kNoSourcePosition;
1109 thread_local_.last_frame_count_ = -1;
1110 if (!shared.is_null() && !location.IsReturnOrSuspend() &&
1111 !IsBlackboxed(shared)) {
1112 // At not return position we flood return positions with one shots and
1113 // will repeat StepOut automatically at next break.
1114 thread_local_.target_frame_count_ = current_frame_count;
1115 thread_local_.fast_forward_to_return_ = true;
1116 FloodWithOneShot(shared, true);
1117 return;
1118 }
1119 // Skip the current frame, find the first frame we want to step out to
1120 // and deoptimize every frame along the way.
1121 bool in_current_frame = true;
1122 for (; !frames_it.done(); frames_it.Advance()) {
1123 if (frames_it.frame()->is_wasm()) {
1124 in_current_frame = false;
1125 continue;
1126 }
1127 JavaScriptFrame* frame = JavaScriptFrame::cast(frames_it.frame());
1128 if (last_step_action() == StepIn) {
1129 // Deoptimize frame to ensure calls are checked for step-in.
1130 Deoptimizer::DeoptimizeFunction(frame->function());
1131 }
1132 HandleScope scope(isolate_);
1133 std::vector<Handle<SharedFunctionInfo>> infos;
1134 frame->GetFunctions(&infos);
1135 for (; !infos.empty(); current_frame_count--) {
1136 Handle<SharedFunctionInfo> info = infos.back();
1137 infos.pop_back();
1138 if (in_current_frame) {
1139 // We want to skip out, so skip the current frame.
1140 in_current_frame = false;
1141 continue;
1142 }
1143 if (IsBlackboxed(info)) continue;
1144 FloodWithOneShot(info);
1145 thread_local_.target_frame_count_ = current_frame_count;
1146 return;
1147 }
1148 }
1149 break;
1150 }
1151 case StepNext:
1152 thread_local_.target_frame_count_ = current_frame_count;
1153 V8_FALLTHROUGH;
1154 case StepIn:
1155 // TODO(clemensb): Implement stepping from JS into wasm.
1156 FloodWithOneShot(shared);
1157 break;
1158 }
1159 }
1160
1161 // Simple function for returning the source positions for active break points.
GetSourceBreakLocations(Isolate * isolate,Handle<SharedFunctionInfo> shared)1162 Handle<Object> Debug::GetSourceBreakLocations(
1163 Isolate* isolate, Handle<SharedFunctionInfo> shared) {
1164 if (!shared->HasBreakInfo()) {
1165 return isolate->factory()->undefined_value();
1166 }
1167
1168 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate);
1169 if (debug_info->GetBreakPointCount(isolate) == 0) {
1170 return isolate->factory()->undefined_value();
1171 }
1172 Handle<FixedArray> locations = isolate->factory()->NewFixedArray(
1173 debug_info->GetBreakPointCount(isolate));
1174 int count = 0;
1175 for (int i = 0; i < debug_info->break_points().length(); ++i) {
1176 if (!debug_info->break_points().get(i).IsUndefined(isolate)) {
1177 BreakPointInfo break_point_info =
1178 BreakPointInfo::cast(debug_info->break_points().get(i));
1179 int break_points = break_point_info.GetBreakPointCount(isolate);
1180 if (break_points == 0) continue;
1181 for (int j = 0; j < break_points; ++j) {
1182 locations->set(count++,
1183 Smi::FromInt(break_point_info.source_position()));
1184 }
1185 }
1186 }
1187 return locations;
1188 }
1189
ClearStepping()1190 void Debug::ClearStepping() {
1191 // Clear the various stepping setup.
1192 ClearOneShot();
1193
1194 thread_local_.last_step_action_ = StepNone;
1195 thread_local_.last_statement_position_ = kNoSourcePosition;
1196 thread_local_.ignore_step_into_function_ = Smi::zero();
1197 thread_local_.fast_forward_to_return_ = false;
1198 thread_local_.last_frame_count_ = -1;
1199 thread_local_.target_frame_count_ = -1;
1200 thread_local_.break_on_next_function_call_ = false;
1201 UpdateHookOnFunctionCall();
1202 }
1203
1204
1205 // Clears all the one-shot break points that are currently set. Normally this
1206 // function is called each time a break point is hit as one shot break points
1207 // are used to support stepping.
ClearOneShot()1208 void Debug::ClearOneShot() {
1209 // The current implementation just runs through all the breakpoints. When the
1210 // last break point for a function is removed that function is automatically
1211 // removed from the list.
1212 for (DebugInfoListNode* node = debug_info_list_; node != nullptr;
1213 node = node->next()) {
1214 Handle<DebugInfo> debug_info = node->debug_info();
1215 ClearBreakPoints(debug_info);
1216 ApplyBreakPoints(debug_info);
1217 }
1218 }
1219
DeoptimizeFunction(Handle<SharedFunctionInfo> shared)1220 void Debug::DeoptimizeFunction(Handle<SharedFunctionInfo> shared) {
1221 // Deoptimize all code compiled from this shared function info including
1222 // inlining.
1223 isolate_->AbortConcurrentOptimization(BlockingBehavior::kBlock);
1224
1225 bool found_something = false;
1226 Code::OptimizedCodeIterator iterator(isolate_);
1227 do {
1228 Code code = iterator.Next();
1229 if (code.is_null()) break;
1230 if (code.Inlines(*shared)) {
1231 code.set_marked_for_deoptimization(true);
1232 found_something = true;
1233 }
1234 } while (true);
1235
1236 if (found_something) {
1237 // Only go through with the deoptimization if something was found.
1238 Deoptimizer::DeoptimizeMarkedCode(isolate_);
1239 }
1240 }
1241
PrepareFunctionForDebugExecution(Handle<SharedFunctionInfo> shared)1242 void Debug::PrepareFunctionForDebugExecution(
1243 Handle<SharedFunctionInfo> shared) {
1244 // To prepare bytecode for debugging, we already need to have the debug
1245 // info (containing the debug copy) upfront, but since we do not recompile,
1246 // preparing for break points cannot fail.
1247 DCHECK(shared->is_compiled());
1248 DCHECK(shared->HasDebugInfo());
1249 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
1250 if (debug_info->flags() & DebugInfo::kPreparedForDebugExecution) return;
1251
1252 // Make a copy of the bytecode array if available.
1253 Handle<HeapObject> maybe_original_bytecode_array =
1254 isolate_->factory()->undefined_value();
1255 if (shared->HasBytecodeArray()) {
1256 Handle<BytecodeArray> original_bytecode_array =
1257 handle(shared->GetBytecodeArray(), isolate_);
1258 Handle<BytecodeArray> debug_bytecode_array =
1259 isolate_->factory()->CopyBytecodeArray(original_bytecode_array);
1260 debug_info->set_debug_bytecode_array(*debug_bytecode_array);
1261 shared->SetDebugBytecodeArray(*debug_bytecode_array);
1262 maybe_original_bytecode_array = original_bytecode_array;
1263 }
1264 debug_info->set_original_bytecode_array(*maybe_original_bytecode_array);
1265
1266 if (debug_info->CanBreakAtEntry()) {
1267 // Deopt everything in case the function is inlined anywhere.
1268 Deoptimizer::DeoptimizeAll(isolate_);
1269 InstallDebugBreakTrampoline();
1270 } else {
1271 DeoptimizeFunction(shared);
1272 // Update PCs on the stack to point to recompiled code.
1273 RedirectActiveFunctions redirect_visitor(
1274 *shared, RedirectActiveFunctions::Mode::kUseDebugBytecode);
1275 redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
1276 isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor);
1277 }
1278 debug_info->set_flags(debug_info->flags() |
1279 DebugInfo::kPreparedForDebugExecution);
1280 }
1281
InstallDebugBreakTrampoline()1282 void Debug::InstallDebugBreakTrampoline() {
1283 // Check the list of debug infos whether the debug break trampoline needs to
1284 // be installed. If that's the case, iterate the heap for functions to rewire
1285 // to the trampoline.
1286 HandleScope scope(isolate_);
1287 // If there is a breakpoint at function entry, we need to install trampoline.
1288 bool needs_to_use_trampoline = false;
1289 // If there we break at entry to an api callback, we need to clear ICs.
1290 bool needs_to_clear_ic = false;
1291 for (DebugInfoListNode* current = debug_info_list_; current != nullptr;
1292 current = current->next()) {
1293 if (current->debug_info()->CanBreakAtEntry()) {
1294 needs_to_use_trampoline = true;
1295 if (current->debug_info()->shared().IsApiFunction()) {
1296 needs_to_clear_ic = true;
1297 break;
1298 }
1299 }
1300 }
1301
1302 if (!needs_to_use_trampoline) return;
1303
1304 Handle<Code> trampoline = BUILTIN_CODE(isolate_, DebugBreakTrampoline);
1305 std::vector<Handle<JSFunction>> needs_compile;
1306 using AccessorPairWithContext =
1307 std::pair<Handle<AccessorPair>, Handle<NativeContext>>;
1308 std::vector<AccessorPairWithContext> needs_instantiate;
1309 {
1310 // Deduplicate {needs_instantiate} by recording all collected AccessorPairs.
1311 std::set<AccessorPair> recorded;
1312 HeapObjectIterator iterator(isolate_->heap());
1313 for (HeapObject obj = iterator.Next(); !obj.is_null();
1314 obj = iterator.Next()) {
1315 if (needs_to_clear_ic && obj.IsFeedbackVector()) {
1316 FeedbackVector::cast(obj).ClearSlots(isolate_);
1317 continue;
1318 } else if (obj.IsJSFunction()) {
1319 JSFunction fun = JSFunction::cast(obj);
1320 SharedFunctionInfo shared = fun.shared();
1321 if (!shared.HasDebugInfo()) continue;
1322 if (!shared.GetDebugInfo().CanBreakAtEntry()) continue;
1323 if (!fun.is_compiled()) {
1324 needs_compile.push_back(handle(fun, isolate_));
1325 } else {
1326 fun.set_code(*trampoline);
1327 }
1328 } else if (obj.IsJSObject()) {
1329 JSObject object = JSObject::cast(obj);
1330 DescriptorArray descriptors =
1331 object.map().instance_descriptors(kRelaxedLoad);
1332
1333 for (InternalIndex i : object.map().IterateOwnDescriptors()) {
1334 if (descriptors.GetDetails(i).kind() == PropertyKind::kAccessor) {
1335 Object value = descriptors.GetStrongValue(i);
1336 if (!value.IsAccessorPair()) continue;
1337
1338 AccessorPair accessor_pair = AccessorPair::cast(value);
1339 if (!accessor_pair.getter().IsFunctionTemplateInfo() &&
1340 !accessor_pair.setter().IsFunctionTemplateInfo()) {
1341 continue;
1342 }
1343 if (recorded.find(accessor_pair) != recorded.end()) continue;
1344
1345 needs_instantiate.emplace_back(handle(accessor_pair, isolate_),
1346 object.GetCreationContext());
1347 recorded.insert(accessor_pair);
1348 }
1349 }
1350 }
1351 }
1352 }
1353
1354 // Forcibly instantiate all lazy accessor pairs to make sure that they
1355 // properly hit the debug break trampoline.
1356 for (AccessorPairWithContext tuple : needs_instantiate) {
1357 Handle<AccessorPair> accessor_pair = tuple.first;
1358 Handle<NativeContext> native_context = tuple.second;
1359 if (accessor_pair->getter().IsFunctionTemplateInfo()) {
1360 Handle<JSFunction> fun =
1361 ApiNatives::InstantiateFunction(
1362 isolate_, native_context,
1363 handle(FunctionTemplateInfo::cast(accessor_pair->getter()),
1364 isolate_))
1365 .ToHandleChecked();
1366 accessor_pair->set_getter(*fun);
1367 }
1368 if (accessor_pair->setter().IsFunctionTemplateInfo()) {
1369 Handle<JSFunction> fun =
1370 ApiNatives::InstantiateFunction(
1371 isolate_, native_context,
1372 handle(FunctionTemplateInfo::cast(accessor_pair->setter()),
1373 isolate_))
1374 .ToHandleChecked();
1375 accessor_pair->set_setter(*fun);
1376 }
1377 }
1378
1379 // By overwriting the function code with DebugBreakTrampoline, which tailcalls
1380 // to shared code, we bypass CompileLazy. Perform CompileLazy here instead.
1381 for (Handle<JSFunction> fun : needs_compile) {
1382 IsCompiledScope is_compiled_scope;
1383 Compiler::Compile(fun, Compiler::CLEAR_EXCEPTION, &is_compiled_scope);
1384 DCHECK(is_compiled_scope.is_compiled());
1385 fun->set_code(*trampoline);
1386 }
1387 }
1388
1389 namespace {
1390 template <typename Iterator>
GetBreakablePositions(Iterator * it,int start_position,int end_position,std::vector<BreakLocation> * locations)1391 void GetBreakablePositions(Iterator* it, int start_position, int end_position,
1392 std::vector<BreakLocation>* locations) {
1393 while (!it->Done()) {
1394 if (it->position() >= start_position && it->position() < end_position) {
1395 locations->push_back(it->GetBreakLocation());
1396 }
1397 it->Next();
1398 }
1399 }
1400
FindBreakablePositions(Handle<DebugInfo> debug_info,int start_position,int end_position,std::vector<BreakLocation> * locations)1401 void FindBreakablePositions(Handle<DebugInfo> debug_info, int start_position,
1402 int end_position,
1403 std::vector<BreakLocation>* locations) {
1404 DCHECK(debug_info->HasInstrumentedBytecodeArray());
1405 BreakIterator it(debug_info);
1406 GetBreakablePositions(&it, start_position, end_position, locations);
1407 }
1408 } // namespace
1409
GetPossibleBreakpoints(Handle<Script> script,int start_position,int end_position,bool restrict_to_function,std::vector<BreakLocation> * locations)1410 bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position,
1411 int end_position, bool restrict_to_function,
1412 std::vector<BreakLocation>* locations) {
1413 if (restrict_to_function) {
1414 Handle<Object> result =
1415 FindSharedFunctionInfoInScript(script, start_position);
1416 if (result->IsUndefined(isolate_)) return false;
1417
1418 // Make sure the function has set up the debug info.
1419 Handle<SharedFunctionInfo> shared =
1420 Handle<SharedFunctionInfo>::cast(result);
1421 if (!EnsureBreakInfo(shared)) return false;
1422 PrepareFunctionForDebugExecution(shared);
1423
1424 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
1425 FindBreakablePositions(debug_info, start_position, end_position, locations);
1426 return true;
1427 }
1428
1429 while (true) {
1430 HandleScope scope(isolate_);
1431 std::vector<Handle<SharedFunctionInfo>> candidates;
1432 std::vector<IsCompiledScope> compiled_scopes;
1433 SharedFunctionInfo::ScriptIterator iterator(isolate_, *script);
1434 for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
1435 info = iterator.Next()) {
1436 if (info.EndPosition() < start_position ||
1437 info.StartPosition() >= end_position) {
1438 continue;
1439 }
1440 if (!info.IsSubjectToDebugging()) continue;
1441 if (!info.is_compiled() && !info.allows_lazy_compilation()) continue;
1442 candidates.push_back(i::handle(info, isolate_));
1443 }
1444
1445 bool was_compiled = false;
1446 for (const auto& candidate : candidates) {
1447 IsCompiledScope is_compiled_scope(candidate->is_compiled_scope(isolate_));
1448 if (!is_compiled_scope.is_compiled()) {
1449 // Code that cannot be compiled lazily are internal and not debuggable.
1450 DCHECK(candidate->allows_lazy_compilation());
1451 if (!Compiler::Compile(candidate, Compiler::CLEAR_EXCEPTION,
1452 &is_compiled_scope)) {
1453 return false;
1454 } else {
1455 was_compiled = true;
1456 }
1457 }
1458 DCHECK(is_compiled_scope.is_compiled());
1459 compiled_scopes.push_back(is_compiled_scope);
1460 if (!EnsureBreakInfo(candidate)) return false;
1461 PrepareFunctionForDebugExecution(candidate);
1462 }
1463 if (was_compiled) continue;
1464
1465 for (const auto& candidate : candidates) {
1466 CHECK(candidate->HasBreakInfo());
1467 Handle<DebugInfo> debug_info(candidate->GetDebugInfo(), isolate_);
1468 FindBreakablePositions(debug_info, start_position, end_position,
1469 locations);
1470 }
1471 return true;
1472 }
1473 UNREACHABLE();
1474 }
1475
1476 class SharedFunctionInfoFinder {
1477 public:
SharedFunctionInfoFinder(int target_position)1478 explicit SharedFunctionInfoFinder(int target_position)
1479 : current_start_position_(kNoSourcePosition),
1480 target_position_(target_position) {}
1481
NewCandidate(SharedFunctionInfo shared,JSFunction closure=JSFunction ())1482 void NewCandidate(SharedFunctionInfo shared,
1483 JSFunction closure = JSFunction()) {
1484 if (!shared.IsSubjectToDebugging()) return;
1485 int start_position = shared.function_token_position();
1486 if (start_position == kNoSourcePosition) {
1487 start_position = shared.StartPosition();
1488 }
1489
1490 if (start_position > target_position_) return;
1491 if (target_position_ > shared.EndPosition()) return;
1492
1493 if (!current_candidate_.is_null()) {
1494 if (current_start_position_ == start_position &&
1495 shared.EndPosition() == current_candidate_.EndPosition()) {
1496 // If we already have a matching closure, do not throw it away.
1497 if (!current_candidate_closure_.is_null() && closure.is_null()) return;
1498 // If a top-level function contains only one function
1499 // declaration the source for the top-level and the function
1500 // is the same. In that case prefer the non top-level function.
1501 if (!current_candidate_.is_toplevel() && shared.is_toplevel()) return;
1502 } else if (start_position < current_start_position_ ||
1503 current_candidate_.EndPosition() < shared.EndPosition()) {
1504 return;
1505 }
1506 }
1507
1508 current_start_position_ = start_position;
1509 current_candidate_ = shared;
1510 current_candidate_closure_ = closure;
1511 }
1512
Result()1513 SharedFunctionInfo Result() { return current_candidate_; }
1514
ResultClosure()1515 JSFunction ResultClosure() { return current_candidate_closure_; }
1516
1517 private:
1518 SharedFunctionInfo current_candidate_;
1519 JSFunction current_candidate_closure_;
1520 int current_start_position_;
1521 int target_position_;
1522 DisallowHeapAllocation no_gc_;
1523 };
1524
1525
1526 // We need to find a SFI for a literal that may not yet have been compiled yet,
1527 // and there may not be a JSFunction referencing it. Find the SFI closest to
1528 // the given position, compile it to reveal possible inner SFIs and repeat.
1529 // While we are at this, also ensure code with debug break slots so that we do
1530 // not have to compile a SFI without JSFunction, which is paifu for those that
1531 // cannot be compiled without context (need to find outer compilable SFI etc.)
FindSharedFunctionInfoInScript(Handle<Script> script,int position)1532 Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
1533 int position) {
1534 for (int iteration = 0;; iteration++) {
1535 // Go through all shared function infos associated with this script to
1536 // find the inner most function containing this position.
1537 // If there is no shared function info for this script at all, there is
1538 // no point in looking for it by walking the heap.
1539
1540 SharedFunctionInfo shared;
1541 IsCompiledScope is_compiled_scope;
1542 {
1543 SharedFunctionInfoFinder finder(position);
1544 SharedFunctionInfo::ScriptIterator iterator(isolate_, *script);
1545 for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
1546 info = iterator.Next()) {
1547 finder.NewCandidate(info);
1548 }
1549 shared = finder.Result();
1550 if (shared.is_null()) break;
1551 // We found it if it's already compiled.
1552 is_compiled_scope = shared.is_compiled_scope(isolate_);
1553 if (is_compiled_scope.is_compiled()) {
1554 Handle<SharedFunctionInfo> shared_handle(shared, isolate_);
1555 // If the iteration count is larger than 1, we had to compile the outer
1556 // function in order to create this shared function info. So there can
1557 // be no JSFunction referencing it. We can anticipate creating a debug
1558 // info while bypassing PrepareFunctionForDebugExecution.
1559 if (iteration > 1) {
1560 AllowHeapAllocation allow_before_return;
1561 CreateBreakInfo(shared_handle);
1562 }
1563 return shared_handle;
1564 }
1565 }
1566 // If not, compile to reveal inner functions.
1567 HandleScope scope(isolate_);
1568 // Code that cannot be compiled lazily are internal and not debuggable.
1569 DCHECK(shared.allows_lazy_compilation());
1570 if (!Compiler::Compile(handle(shared, isolate_), Compiler::CLEAR_EXCEPTION,
1571 &is_compiled_scope)) {
1572 break;
1573 }
1574 }
1575 return isolate_->factory()->undefined_value();
1576 }
1577
1578
1579 // Ensures the debug information is present for shared.
EnsureBreakInfo(Handle<SharedFunctionInfo> shared)1580 bool Debug::EnsureBreakInfo(Handle<SharedFunctionInfo> shared) {
1581 // Return if we already have the break info for shared.
1582 if (shared->HasBreakInfo()) return true;
1583 if (!shared->IsSubjectToDebugging() && !CanBreakAtEntry(shared)) {
1584 return false;
1585 }
1586 IsCompiledScope is_compiled_scope = shared->is_compiled_scope(isolate_);
1587 if (!is_compiled_scope.is_compiled() &&
1588 !Compiler::Compile(shared, Compiler::CLEAR_EXCEPTION,
1589 &is_compiled_scope)) {
1590 return false;
1591 }
1592 CreateBreakInfo(shared);
1593 return true;
1594 }
1595
CreateBreakInfo(Handle<SharedFunctionInfo> shared)1596 void Debug::CreateBreakInfo(Handle<SharedFunctionInfo> shared) {
1597 HandleScope scope(isolate_);
1598 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
1599
1600 // Initialize with break information.
1601
1602 DCHECK(!debug_info->HasBreakInfo());
1603
1604 Factory* factory = isolate_->factory();
1605 Handle<FixedArray> break_points(
1606 factory->NewFixedArray(DebugInfo::kEstimatedNofBreakPointsInFunction));
1607
1608 int flags = debug_info->flags();
1609 flags |= DebugInfo::kHasBreakInfo;
1610 if (CanBreakAtEntry(shared)) flags |= DebugInfo::kCanBreakAtEntry;
1611 debug_info->set_flags(flags);
1612 debug_info->set_break_points(*break_points);
1613
1614 SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate_, shared);
1615 }
1616
GetOrCreateDebugInfo(Handle<SharedFunctionInfo> shared)1617 Handle<DebugInfo> Debug::GetOrCreateDebugInfo(
1618 Handle<SharedFunctionInfo> shared) {
1619 if (shared->HasDebugInfo()) return handle(shared->GetDebugInfo(), isolate_);
1620
1621 // Create debug info and add it to the list.
1622 Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared);
1623 DebugInfoListNode* node = new DebugInfoListNode(isolate_, *debug_info);
1624 node->set_next(debug_info_list_);
1625 debug_info_list_ = node;
1626
1627 return debug_info;
1628 }
1629
InstallCoverageInfo(Handle<SharedFunctionInfo> shared,Handle<CoverageInfo> coverage_info)1630 void Debug::InstallCoverageInfo(Handle<SharedFunctionInfo> shared,
1631 Handle<CoverageInfo> coverage_info) {
1632 DCHECK(!coverage_info.is_null());
1633
1634 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
1635
1636 DCHECK(!debug_info->HasCoverageInfo());
1637
1638 debug_info->set_flags(debug_info->flags() | DebugInfo::kHasCoverageInfo);
1639 debug_info->set_coverage_info(*coverage_info);
1640 }
1641
RemoveAllCoverageInfos()1642 void Debug::RemoveAllCoverageInfos() {
1643 ClearAllDebugInfos(
1644 [=](Handle<DebugInfo> info) { info->ClearCoverageInfo(isolate_); });
1645 }
1646
ClearAllDebuggerHints()1647 void Debug::ClearAllDebuggerHints() {
1648 ClearAllDebugInfos(
1649 [=](Handle<DebugInfo> info) { info->set_debugger_hints(0); });
1650 }
1651
FindDebugInfo(Handle<DebugInfo> debug_info,DebugInfoListNode ** prev,DebugInfoListNode ** curr)1652 void Debug::FindDebugInfo(Handle<DebugInfo> debug_info,
1653 DebugInfoListNode** prev, DebugInfoListNode** curr) {
1654 HandleScope scope(isolate_);
1655 *prev = nullptr;
1656 *curr = debug_info_list_;
1657 while (*curr != nullptr) {
1658 if ((*curr)->debug_info().is_identical_to(debug_info)) return;
1659 *prev = *curr;
1660 *curr = (*curr)->next();
1661 }
1662
1663 UNREACHABLE();
1664 }
1665
ClearAllDebugInfos(const DebugInfoClearFunction & clear_function)1666 void Debug::ClearAllDebugInfos(const DebugInfoClearFunction& clear_function) {
1667 DebugInfoListNode* prev = nullptr;
1668 DebugInfoListNode* current = debug_info_list_;
1669 while (current != nullptr) {
1670 DebugInfoListNode* next = current->next();
1671 Handle<DebugInfo> debug_info = current->debug_info();
1672 clear_function(debug_info);
1673 if (debug_info->IsEmpty()) {
1674 FreeDebugInfoListNode(prev, current);
1675 current = next;
1676 } else {
1677 prev = current;
1678 current = next;
1679 }
1680 }
1681 }
1682
RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info)1683 void Debug::RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info) {
1684 debug_info->ClearBreakInfo(isolate_);
1685 if (debug_info->IsEmpty()) {
1686 DebugInfoListNode* prev;
1687 DebugInfoListNode* node;
1688 FindDebugInfo(debug_info, &prev, &node);
1689 FreeDebugInfoListNode(prev, node);
1690 }
1691 }
1692
FreeDebugInfoListNode(DebugInfoListNode * prev,DebugInfoListNode * node)1693 void Debug::FreeDebugInfoListNode(DebugInfoListNode* prev,
1694 DebugInfoListNode* node) {
1695 DCHECK(node->debug_info()->IsEmpty());
1696
1697 // Unlink from list. If prev is nullptr we are looking at the first element.
1698 if (prev == nullptr) {
1699 debug_info_list_ = node->next();
1700 } else {
1701 prev->set_next(node->next());
1702 }
1703
1704 // Pack script back into the
1705 // SFI::script_or_debug_info field.
1706 Handle<DebugInfo> debug_info(node->debug_info());
1707 debug_info->shared().set_script_or_debug_info(debug_info->script(),
1708 kReleaseStore);
1709
1710 delete node;
1711 }
1712
IsBreakAtReturn(JavaScriptFrame * frame)1713 bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
1714 HandleScope scope(isolate_);
1715
1716 // Get the executing function in which the debug break occurred.
1717 Handle<SharedFunctionInfo> shared(frame->function().shared(), isolate_);
1718
1719 // With no debug info there are no break points, so we can't be at a return.
1720 if (!shared->HasBreakInfo()) return false;
1721
1722 DCHECK(!frame->is_optimized());
1723 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
1724 BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
1725 return location.IsReturn();
1726 }
1727
ScheduleFrameRestart(StackFrame * frame)1728 void Debug::ScheduleFrameRestart(StackFrame* frame) {
1729 // Set a target FP for the FrameDropperTrampoline builtin to drop to once
1730 // we return from the debugger.
1731 DCHECK(frame->is_java_script());
1732 // Only reschedule to a frame further below a frame we already scheduled for.
1733 if (frame->fp() <= thread_local_.restart_fp_) return;
1734 // If the frame is optimized, trigger a deopt and jump into the
1735 // FrameDropperTrampoline in the deoptimizer.
1736 thread_local_.restart_fp_ = frame->fp();
1737
1738 // Reset break frame ID to the frame below the restarted frame.
1739 StackTraceFrameIterator it(isolate_);
1740 thread_local_.break_frame_id_ = StackFrameId::NO_ID;
1741 for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) {
1742 if (it.frame()->fp() > thread_local_.restart_fp_) {
1743 thread_local_.break_frame_id_ = it.frame()->id();
1744 return;
1745 }
1746 }
1747 }
1748
GetLoadedScripts()1749 Handle<FixedArray> Debug::GetLoadedScripts() {
1750 isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags,
1751 GarbageCollectionReason::kDebugger);
1752 Factory* factory = isolate_->factory();
1753 if (!factory->script_list()->IsWeakArrayList()) {
1754 return factory->empty_fixed_array();
1755 }
1756 Handle<WeakArrayList> array =
1757 Handle<WeakArrayList>::cast(factory->script_list());
1758 Handle<FixedArray> results = factory->NewFixedArray(array->length());
1759 int length = 0;
1760 {
1761 Script::Iterator iterator(isolate_);
1762 for (Script script = iterator.Next(); !script.is_null();
1763 script = iterator.Next()) {
1764 if (script.HasValidSource()) results->set(length++, script);
1765 }
1766 }
1767 return FixedArray::ShrinkOrEmpty(isolate_, results, length);
1768 }
1769
OnThrow(Handle<Object> exception)1770 base::Optional<Object> Debug::OnThrow(Handle<Object> exception) {
1771 if (in_debug_scope() || ignore_events()) return {};
1772 // Temporarily clear any scheduled_exception to allow evaluating
1773 // JavaScript from the debug event handler.
1774 HandleScope scope(isolate_);
1775 Handle<Object> scheduled_exception;
1776 if (isolate_->has_scheduled_exception()) {
1777 scheduled_exception = handle(isolate_->scheduled_exception(), isolate_);
1778 isolate_->clear_scheduled_exception();
1779 }
1780 Handle<Object> maybe_promise = isolate_->GetPromiseOnStackOnThrow();
1781 OnException(exception, maybe_promise,
1782 maybe_promise->IsJSPromise() ? v8::debug::kPromiseRejection
1783 : v8::debug::kException);
1784 if (!scheduled_exception.is_null()) {
1785 isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception;
1786 }
1787 PrepareStepOnThrow();
1788 // If the OnException handler requested termination, then indicated this to
1789 // our caller Isolate::Throw so it can deal with it immediatelly instead of
1790 // throwing the original exception.
1791 if (isolate_->stack_guard()->CheckTerminateExecution()) {
1792 isolate_->stack_guard()->ClearTerminateExecution();
1793 return isolate_->TerminateExecution();
1794 }
1795 return {};
1796 }
1797
OnPromiseReject(Handle<Object> promise,Handle<Object> value)1798 void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) {
1799 if (in_debug_scope() || ignore_events()) return;
1800 HandleScope scope(isolate_);
1801 // Check whether the promise has been marked as having triggered a message.
1802 Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
1803 if (!promise->IsJSObject() ||
1804 JSReceiver::GetDataProperty(Handle<JSObject>::cast(promise), key)
1805 ->IsUndefined(isolate_)) {
1806 OnException(value, promise, v8::debug::kPromiseRejection);
1807 }
1808 }
1809
IsExceptionBlackboxed(bool uncaught)1810 bool Debug::IsExceptionBlackboxed(bool uncaught) {
1811 // Uncaught exception is blackboxed if all current frames are blackboxed,
1812 // caught exception if top frame is blackboxed.
1813 StackTraceFrameIterator it(isolate_);
1814 while (!it.done() && it.is_wasm()) it.Advance();
1815 bool is_top_frame_blackboxed =
1816 !it.done() ? IsFrameBlackboxed(it.javascript_frame()) : true;
1817 if (!uncaught || !is_top_frame_blackboxed) return is_top_frame_blackboxed;
1818 return AllFramesOnStackAreBlackboxed();
1819 }
1820
IsFrameBlackboxed(JavaScriptFrame * frame)1821 bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) {
1822 HandleScope scope(isolate_);
1823 std::vector<Handle<SharedFunctionInfo>> infos;
1824 frame->GetFunctions(&infos);
1825 for (const auto& info : infos) {
1826 if (!IsBlackboxed(info)) return false;
1827 }
1828 return true;
1829 }
1830
OnException(Handle<Object> exception,Handle<Object> promise,v8::debug::ExceptionType exception_type)1831 void Debug::OnException(Handle<Object> exception, Handle<Object> promise,
1832 v8::debug::ExceptionType exception_type) {
1833 Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher();
1834
1835 // Don't notify listener of exceptions that are internal to a desugaring.
1836 if (catch_type == Isolate::CAUGHT_BY_DESUGARING) return;
1837
1838 bool uncaught = catch_type == Isolate::NOT_CAUGHT;
1839 if (promise->IsJSObject()) {
1840 Handle<JSObject> jspromise = Handle<JSObject>::cast(promise);
1841 // Mark the promise as already having triggered a message.
1842 Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
1843 Object::SetProperty(isolate_, jspromise, key, key, StoreOrigin::kMaybeKeyed,
1844 Just(ShouldThrow::kThrowOnError))
1845 .Assert();
1846 // Check whether the promise reject is considered an uncaught exception.
1847 if (jspromise->IsJSPromise()) {
1848 uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(
1849 Handle<JSPromise>::cast(jspromise));
1850 } else {
1851 uncaught = true;
1852 }
1853 }
1854
1855 if (!debug_delegate_) return;
1856
1857 // Bail out if exception breaks are not active
1858 if (uncaught) {
1859 // Uncaught exceptions are reported by either flags.
1860 if (!(break_on_uncaught_exception_ || break_on_exception_)) return;
1861 } else {
1862 // Caught exceptions are reported is activated.
1863 if (!break_on_exception_) return;
1864 }
1865
1866 {
1867 JavaScriptFrameIterator it(isolate_);
1868 // Check whether the top frame is blackboxed or the break location is muted.
1869 if (!it.done() && (IsMutedAtCurrentLocation(it.frame()) ||
1870 IsExceptionBlackboxed(uncaught))) {
1871 return;
1872 }
1873 if (it.done()) return; // Do not trigger an event with an empty stack.
1874 }
1875
1876 // Do not trigger exception event on stack overflow. We cannot perform
1877 // anything useful for debugging in that situation.
1878 StackLimitCheck stack_limit_check(isolate_);
1879 if (stack_limit_check.JsHasOverflowed()) return;
1880
1881 DebugScope debug_scope(this);
1882 HandleScope scope(isolate_);
1883 DisableBreak no_recursive_break(this);
1884
1885 Handle<Context> native_context(isolate_->native_context());
1886 debug_delegate_->ExceptionThrown(
1887 v8::Utils::ToLocal(native_context), v8::Utils::ToLocal(exception),
1888 v8::Utils::ToLocal(promise), uncaught, exception_type);
1889 }
1890
OnDebugBreak(Handle<FixedArray> break_points_hit,StepAction lastStepAction)1891 void Debug::OnDebugBreak(Handle<FixedArray> break_points_hit,
1892 StepAction lastStepAction) {
1893 DCHECK(!break_points_hit.is_null());
1894 // The caller provided for DebugScope.
1895 AssertDebugContext();
1896 // Bail out if there is no listener for this event
1897 if (ignore_events()) return;
1898
1899 #ifdef DEBUG
1900 PrintBreakLocation();
1901 #endif // DEBUG
1902
1903 if (!debug_delegate_) return;
1904 DCHECK(in_debug_scope());
1905 HandleScope scope(isolate_);
1906 DisableBreak no_recursive_break(this);
1907
1908 if ((lastStepAction == StepAction::StepNext ||
1909 lastStepAction == StepAction::StepIn) &&
1910 ShouldBeSkipped()) {
1911 PrepareStep(lastStepAction);
1912 return;
1913 }
1914
1915 std::vector<int> inspector_break_points_hit;
1916 int inspector_break_points_count = 0;
1917 // This array contains breakpoints installed using JS debug API.
1918 for (int i = 0; i < break_points_hit->length(); ++i) {
1919 BreakPoint break_point = BreakPoint::cast(break_points_hit->get(i));
1920 inspector_break_points_hit.push_back(break_point.id());
1921 ++inspector_break_points_count;
1922 }
1923
1924 Handle<Context> native_context(isolate_->native_context());
1925 debug_delegate_->BreakProgramRequested(v8::Utils::ToLocal(native_context),
1926 inspector_break_points_hit);
1927 }
1928
1929 namespace {
GetDebugLocation(Handle<Script> script,int source_position)1930 debug::Location GetDebugLocation(Handle<Script> script, int source_position) {
1931 Script::PositionInfo info;
1932 Script::GetPositionInfo(script, source_position, &info, Script::WITH_OFFSET);
1933 // V8 provides ScriptCompiler::CompileFunctionInContext method which takes
1934 // expression and compile it as anonymous function like (function() ..
1935 // expression ..). To produce correct locations for stmts inside of this
1936 // expression V8 compile this function with negative offset. Instead of stmt
1937 // position blackboxing use function start position which is negative in
1938 // described case.
1939 return debug::Location(std::max(info.line, 0), std::max(info.column, 0));
1940 }
1941 } // namespace
1942
IsBlackboxed(Handle<SharedFunctionInfo> shared)1943 bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
1944 if (!debug_delegate_) return !shared->IsSubjectToDebugging();
1945 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
1946 if (!debug_info->computed_debug_is_blackboxed()) {
1947 bool is_blackboxed =
1948 !shared->IsSubjectToDebugging() || !shared->script().IsScript();
1949 if (!is_blackboxed) {
1950 SuppressDebug while_processing(this);
1951 HandleScope handle_scope(isolate_);
1952 PostponeInterruptsScope no_interrupts(isolate_);
1953 DisableBreak no_recursive_break(this);
1954 DCHECK(shared->script().IsScript());
1955 Handle<Script> script(Script::cast(shared->script()), isolate_);
1956 DCHECK(script->IsUserJavaScript());
1957 debug::Location start = GetDebugLocation(script, shared->StartPosition());
1958 debug::Location end = GetDebugLocation(script, shared->EndPosition());
1959 is_blackboxed = debug_delegate_->IsFunctionBlackboxed(
1960 ToApiHandle<debug::Script>(script), start, end);
1961 }
1962 debug_info->set_debug_is_blackboxed(is_blackboxed);
1963 debug_info->set_computed_debug_is_blackboxed(true);
1964 }
1965 return debug_info->debug_is_blackboxed();
1966 }
1967
ShouldBeSkipped()1968 bool Debug::ShouldBeSkipped() {
1969 SuppressDebug while_processing(this);
1970 PostponeInterruptsScope no_interrupts(isolate_);
1971 DisableBreak no_recursive_break(this);
1972
1973 StackTraceFrameIterator iterator(isolate_);
1974 CommonFrame* frame = iterator.frame();
1975 FrameSummary summary = FrameSummary::GetTop(frame);
1976 Handle<Object> script_obj = summary.script();
1977 if (!script_obj->IsScript()) return false;
1978
1979 Handle<Script> script = Handle<Script>::cast(script_obj);
1980 summary.EnsureSourcePositionsAvailable();
1981 int source_position = summary.SourcePosition();
1982 int line = Script::GetLineNumber(script, source_position);
1983 int column = Script::GetColumnNumber(script, source_position);
1984
1985 return debug_delegate_->ShouldBeSkipped(ToApiHandle<debug::Script>(script),
1986 line, column);
1987 }
1988
AllFramesOnStackAreBlackboxed()1989 bool Debug::AllFramesOnStackAreBlackboxed() {
1990 HandleScope scope(isolate_);
1991 for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) {
1992 if (!it.is_javascript()) continue;
1993 if (!IsFrameBlackboxed(it.javascript_frame())) return false;
1994 }
1995 return true;
1996 }
1997
CanBreakAtEntry(Handle<SharedFunctionInfo> shared)1998 bool Debug::CanBreakAtEntry(Handle<SharedFunctionInfo> shared) {
1999 // Allow break at entry for builtin functions.
2000 if (shared->native() || shared->IsApiFunction()) {
2001 // Functions that are subject to debugging can have regular breakpoints.
2002 DCHECK(!shared->IsSubjectToDebugging());
2003 return true;
2004 }
2005 return false;
2006 }
2007
SetScriptSource(Handle<Script> script,Handle<String> source,bool preview,debug::LiveEditResult * result)2008 bool Debug::SetScriptSource(Handle<Script> script, Handle<String> source,
2009 bool preview, debug::LiveEditResult* result) {
2010 DebugScope debug_scope(this);
2011 feature_tracker()->Track(DebugFeatureTracker::kLiveEdit);
2012 running_live_edit_ = true;
2013 LiveEdit::PatchScript(isolate_, script, source, preview, result);
2014 running_live_edit_ = false;
2015 return result->status == debug::LiveEditResult::OK;
2016 }
2017
OnCompileError(Handle<Script> script)2018 void Debug::OnCompileError(Handle<Script> script) {
2019 ProcessCompileEvent(true, script);
2020 }
2021
OnAfterCompile(Handle<Script> script)2022 void Debug::OnAfterCompile(Handle<Script> script) {
2023 ProcessCompileEvent(false, script);
2024 }
2025
ProcessCompileEvent(bool has_compile_error,Handle<Script> script)2026 void Debug::ProcessCompileEvent(bool has_compile_error, Handle<Script> script) {
2027 // Ignore temporary scripts.
2028 if (script->id() == Script::kTemporaryScriptId) return;
2029 // TODO(kozyatinskiy): teach devtools to work with liveedit scripts better
2030 // first and then remove this fast return.
2031 if (running_live_edit_) return;
2032 // Attach the correct debug id to the script. The debug id is used by the
2033 // inspector to filter scripts by native context.
2034 script->set_context_data(isolate_->native_context()->debug_context_id());
2035 if (ignore_events()) return;
2036 if (!script->IsUserJavaScript() && script->type() != i::Script::TYPE_WASM) {
2037 return;
2038 }
2039 if (!debug_delegate_) return;
2040 SuppressDebug while_processing(this);
2041 DebugScope debug_scope(this);
2042 HandleScope scope(isolate_);
2043 DisableBreak no_recursive_break(this);
2044 AllowJavascriptExecution allow_script(isolate_);
2045 debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
2046 running_live_edit_, has_compile_error);
2047 }
2048
CurrentFrameCount()2049 int Debug::CurrentFrameCount() {
2050 StackTraceFrameIterator it(isolate_);
2051 if (break_frame_id() != StackFrameId::NO_ID) {
2052 // Skip to break frame.
2053 DCHECK(in_debug_scope());
2054 while (!it.done() && it.frame()->id() != break_frame_id()) it.Advance();
2055 }
2056 int counter = 0;
2057 for (; !it.done(); it.Advance()) {
2058 counter += it.FrameFunctionCount();
2059 }
2060 return counter;
2061 }
2062
SetDebugDelegate(debug::DebugDelegate * delegate)2063 void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) {
2064 debug_delegate_ = delegate;
2065 UpdateState();
2066 }
2067
UpdateState()2068 void Debug::UpdateState() {
2069 bool is_active = debug_delegate_ != nullptr;
2070 if (is_active == is_active_) return;
2071 if (is_active) {
2072 // Note that the debug context could have already been loaded to
2073 // bootstrap test cases.
2074 isolate_->compilation_cache()->DisableScriptAndEval();
2075 is_active = true;
2076 feature_tracker()->Track(DebugFeatureTracker::kActive);
2077 } else {
2078 isolate_->compilation_cache()->EnableScriptAndEval();
2079 Unload();
2080 }
2081 is_active_ = is_active;
2082 isolate_->PromiseHookStateUpdated();
2083 }
2084
UpdateHookOnFunctionCall()2085 void Debug::UpdateHookOnFunctionCall() {
2086 STATIC_ASSERT(LastStepAction == StepIn);
2087 hook_on_function_call_ =
2088 thread_local_.last_step_action_ == StepIn ||
2089 isolate_->debug_execution_mode() == DebugInfo::kSideEffects ||
2090 thread_local_.break_on_next_function_call_;
2091 }
2092
HandleDebugBreak(IgnoreBreakMode ignore_break_mode)2093 void Debug::HandleDebugBreak(IgnoreBreakMode ignore_break_mode) {
2094 // Initialize LiveEdit.
2095 LiveEdit::InitializeThreadLocal(this);
2096 // Ignore debug break during bootstrapping.
2097 if (isolate_->bootstrapper()->IsActive()) return;
2098 // Just continue if breaks are disabled.
2099 if (break_disabled()) return;
2100 // Ignore debug break if debugger is not active.
2101 if (!is_active()) return;
2102
2103 StackLimitCheck check(isolate_);
2104 if (check.HasOverflowed()) return;
2105
2106 { JavaScriptFrameIterator it(isolate_);
2107 DCHECK(!it.done());
2108 Object fun = it.frame()->function();
2109 if (fun.IsJSFunction()) {
2110 HandleScope scope(isolate_);
2111 Handle<JSFunction> function(JSFunction::cast(fun), isolate_);
2112 // Don't stop in builtin and blackboxed functions.
2113 Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
2114 bool ignore_break = ignore_break_mode == kIgnoreIfTopFrameBlackboxed
2115 ? IsBlackboxed(shared)
2116 : AllFramesOnStackAreBlackboxed();
2117 if (ignore_break) return;
2118 // Don't stop if the break location is muted.
2119 if (IsMutedAtCurrentLocation(it.frame())) return;
2120 }
2121 }
2122
2123 StepAction lastStepAction = last_step_action();
2124
2125 // Clear stepping to avoid duplicate breaks.
2126 ClearStepping();
2127
2128 HandleScope scope(isolate_);
2129 DebugScope debug_scope(this);
2130
2131 OnDebugBreak(isolate_->factory()->empty_fixed_array(), lastStepAction);
2132 }
2133
2134 #ifdef DEBUG
PrintBreakLocation()2135 void Debug::PrintBreakLocation() {
2136 if (!FLAG_print_break_location) return;
2137 HandleScope scope(isolate_);
2138 StackTraceFrameIterator iterator(isolate_);
2139 if (iterator.done()) return;
2140 CommonFrame* frame = iterator.frame();
2141 FrameSummary summary = FrameSummary::GetTop(frame);
2142 summary.EnsureSourcePositionsAvailable();
2143 int source_position = summary.SourcePosition();
2144 Handle<Object> script_obj = summary.script();
2145 PrintF("[debug] break in function '");
2146 summary.FunctionName()->PrintOn(stdout);
2147 PrintF("'.\n");
2148 if (script_obj->IsScript()) {
2149 Handle<Script> script = Handle<Script>::cast(script_obj);
2150 Handle<String> source(String::cast(script->source()), isolate_);
2151 Script::InitLineEnds(isolate_, script);
2152 int line =
2153 Script::GetLineNumber(script, source_position) - script->line_offset();
2154 int column = Script::GetColumnNumber(script, source_position) -
2155 (line == 0 ? script->column_offset() : 0);
2156 Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()),
2157 isolate_);
2158 int line_start = line == 0 ? 0 : Smi::ToInt(line_ends->get(line - 1)) + 1;
2159 int line_end = Smi::ToInt(line_ends->get(line));
2160 DisallowHeapAllocation no_gc;
2161 String::FlatContent content = source->GetFlatContent(no_gc);
2162 if (content.IsOneByte()) {
2163 PrintF("[debug] %.*s\n", line_end - line_start,
2164 content.ToOneByteVector().begin() + line_start);
2165 PrintF("[debug] ");
2166 for (int i = 0; i < column; i++) PrintF(" ");
2167 PrintF("^\n");
2168 } else {
2169 PrintF("[debug] at line %d column %d\n", line, column);
2170 }
2171 }
2172 }
2173 #endif // DEBUG
2174
DebugScope(Debug * debug)2175 DebugScope::DebugScope(Debug* debug)
2176 : debug_(debug),
2177 prev_(reinterpret_cast<DebugScope*>(
2178 base::Relaxed_Load(&debug->thread_local_.current_debug_scope_))),
2179 no_interrupts_(debug_->isolate_) {
2180 // Link recursive debugger entry.
2181 base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
2182 reinterpret_cast<base::AtomicWord>(this));
2183 // Store the previous frame id and return value.
2184 break_frame_id_ = debug_->break_frame_id();
2185
2186 // Create the new break info. If there is no proper frames there is no break
2187 // frame id.
2188 StackTraceFrameIterator it(isolate());
2189 bool has_frames = !it.done();
2190 debug_->thread_local_.break_frame_id_ =
2191 has_frames ? it.frame()->id() : StackFrameId::NO_ID;
2192
2193 debug_->UpdateState();
2194 }
2195
set_terminate_on_resume()2196 void DebugScope::set_terminate_on_resume() { terminate_on_resume_ = true; }
2197
~DebugScope()2198 DebugScope::~DebugScope() {
2199 // Terminate on resume must have been handled by retrieving it, if this is
2200 // the outer scope.
2201 if (terminate_on_resume_) {
2202 if (!prev_) {
2203 debug_->isolate_->stack_guard()->RequestTerminateExecution();
2204 } else {
2205 prev_->set_terminate_on_resume();
2206 }
2207 }
2208 // Leaving this debugger entry.
2209 base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
2210 reinterpret_cast<base::AtomicWord>(prev_));
2211
2212 // Restore to the previous break state.
2213 debug_->thread_local_.break_frame_id_ = break_frame_id_;
2214
2215 debug_->UpdateState();
2216 }
2217
ReturnValueScope(Debug * debug)2218 ReturnValueScope::ReturnValueScope(Debug* debug) : debug_(debug) {
2219 return_value_ = debug_->return_value_handle();
2220 }
2221
~ReturnValueScope()2222 ReturnValueScope::~ReturnValueScope() {
2223 debug_->set_return_value(*return_value_);
2224 }
2225
UpdateDebugInfosForExecutionMode()2226 void Debug::UpdateDebugInfosForExecutionMode() {
2227 // Walk all debug infos and update their execution mode if it is different
2228 // from the isolate execution mode.
2229 DebugInfoListNode* current = debug_info_list_;
2230 while (current != nullptr) {
2231 Handle<DebugInfo> debug_info = current->debug_info();
2232 if (debug_info->HasInstrumentedBytecodeArray() &&
2233 debug_info->DebugExecutionMode() != isolate_->debug_execution_mode()) {
2234 DCHECK(debug_info->shared().HasBytecodeArray());
2235 if (isolate_->debug_execution_mode() == DebugInfo::kBreakpoints) {
2236 ClearSideEffectChecks(debug_info);
2237 ApplyBreakPoints(debug_info);
2238 } else {
2239 ClearBreakPoints(debug_info);
2240 ApplySideEffectChecks(debug_info);
2241 }
2242 }
2243 current = current->next();
2244 }
2245 }
2246
SetTerminateOnResume()2247 void Debug::SetTerminateOnResume() {
2248 DebugScope* scope = reinterpret_cast<DebugScope*>(
2249 base::Acquire_Load(&thread_local_.current_debug_scope_));
2250 CHECK_NOT_NULL(scope);
2251 scope->set_terminate_on_resume();
2252 }
2253
StartSideEffectCheckMode()2254 void Debug::StartSideEffectCheckMode() {
2255 DCHECK(isolate_->debug_execution_mode() != DebugInfo::kSideEffects);
2256 isolate_->set_debug_execution_mode(DebugInfo::kSideEffects);
2257 UpdateHookOnFunctionCall();
2258 side_effect_check_failed_ = false;
2259
2260 DCHECK(!temporary_objects_);
2261 temporary_objects_.reset(new TemporaryObjectsTracker());
2262 isolate_->heap()->AddHeapObjectAllocationTracker(temporary_objects_.get());
2263 Handle<FixedArray> array(isolate_->native_context()->regexp_last_match_info(),
2264 isolate_);
2265 regexp_match_info_ =
2266 Handle<RegExpMatchInfo>::cast(isolate_->factory()->CopyFixedArray(array));
2267
2268 // Update debug infos to have correct execution mode.
2269 UpdateDebugInfosForExecutionMode();
2270 }
2271
StopSideEffectCheckMode()2272 void Debug::StopSideEffectCheckMode() {
2273 DCHECK(isolate_->debug_execution_mode() == DebugInfo::kSideEffects);
2274 if (side_effect_check_failed_) {
2275 DCHECK(isolate_->has_pending_exception());
2276 DCHECK_EQ(ReadOnlyRoots(isolate_).termination_exception(),
2277 isolate_->pending_exception());
2278 // Convert the termination exception into a regular exception.
2279 isolate_->CancelTerminateExecution();
2280 isolate_->Throw(*isolate_->factory()->NewEvalError(
2281 MessageTemplate::kNoSideEffectDebugEvaluate));
2282 }
2283 isolate_->set_debug_execution_mode(DebugInfo::kBreakpoints);
2284 UpdateHookOnFunctionCall();
2285 side_effect_check_failed_ = false;
2286
2287 DCHECK(temporary_objects_);
2288 isolate_->heap()->RemoveHeapObjectAllocationTracker(temporary_objects_.get());
2289 temporary_objects_.reset();
2290 isolate_->native_context()->set_regexp_last_match_info(*regexp_match_info_);
2291 regexp_match_info_ = Handle<RegExpMatchInfo>::null();
2292
2293 // Update debug infos to have correct execution mode.
2294 UpdateDebugInfosForExecutionMode();
2295 }
2296
ApplySideEffectChecks(Handle<DebugInfo> debug_info)2297 void Debug::ApplySideEffectChecks(Handle<DebugInfo> debug_info) {
2298 DCHECK(debug_info->HasInstrumentedBytecodeArray());
2299 Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
2300 isolate_);
2301 DebugEvaluate::ApplySideEffectChecks(debug_bytecode);
2302 debug_info->SetDebugExecutionMode(DebugInfo::kSideEffects);
2303 }
2304
ClearSideEffectChecks(Handle<DebugInfo> debug_info)2305 void Debug::ClearSideEffectChecks(Handle<DebugInfo> debug_info) {
2306 DCHECK(debug_info->HasInstrumentedBytecodeArray());
2307 Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
2308 isolate_);
2309 Handle<BytecodeArray> original(debug_info->OriginalBytecodeArray(), isolate_);
2310 for (interpreter::BytecodeArrayIterator it(debug_bytecode); !it.done();
2311 it.Advance()) {
2312 // Restore from original. This may copy only the scaling prefix, which is
2313 // correct, since we patch scaling prefixes to debug breaks if exists.
2314 debug_bytecode->set(it.current_offset(),
2315 original->get(it.current_offset()));
2316 }
2317 }
2318
PerformSideEffectCheck(Handle<JSFunction> function,Handle<Object> receiver)2319 bool Debug::PerformSideEffectCheck(Handle<JSFunction> function,
2320 Handle<Object> receiver) {
2321 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2322 DisallowJavascriptExecution no_js(isolate_);
2323 IsCompiledScope is_compiled_scope(
2324 function->shared().is_compiled_scope(isolate_));
2325 if (!function->is_compiled() &&
2326 !Compiler::Compile(function, Compiler::KEEP_EXCEPTION,
2327 &is_compiled_scope)) {
2328 return false;
2329 }
2330 DCHECK(is_compiled_scope.is_compiled());
2331 Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
2332 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
2333 DebugInfo::SideEffectState side_effect_state =
2334 debug_info->GetSideEffectState(isolate_);
2335 switch (side_effect_state) {
2336 case DebugInfo::kHasSideEffects:
2337 if (FLAG_trace_side_effect_free_debug_evaluate) {
2338 PrintF("[debug-evaluate] Function %s failed side effect check.\n",
2339 function->shared().DebugName().ToCString().get());
2340 }
2341 side_effect_check_failed_ = true;
2342 // Throw an uncatchable termination exception.
2343 isolate_->TerminateExecution();
2344 return false;
2345 case DebugInfo::kRequiresRuntimeChecks: {
2346 if (!shared->HasBytecodeArray()) {
2347 return PerformSideEffectCheckForObject(receiver);
2348 }
2349 // If function has bytecode array then prepare function for debug
2350 // execution to perform runtime side effect checks.
2351 DCHECK(shared->is_compiled());
2352 PrepareFunctionForDebugExecution(shared);
2353 ApplySideEffectChecks(debug_info);
2354 return true;
2355 }
2356 case DebugInfo::kHasNoSideEffect:
2357 return true;
2358 case DebugInfo::kNotComputed:
2359 UNREACHABLE();
2360 return false;
2361 }
2362 UNREACHABLE();
2363 return false;
2364 }
2365
return_value_handle()2366 Handle<Object> Debug::return_value_handle() {
2367 return handle(thread_local_.return_value_, isolate_);
2368 }
2369
PerformSideEffectCheckForCallback(Handle<Object> callback_info,Handle<Object> receiver,Debug::AccessorKind accessor_kind)2370 bool Debug::PerformSideEffectCheckForCallback(
2371 Handle<Object> callback_info, Handle<Object> receiver,
2372 Debug::AccessorKind accessor_kind) {
2373 DCHECK_EQ(!receiver.is_null(), callback_info->IsAccessorInfo());
2374 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2375 if (!callback_info.is_null() && callback_info->IsCallHandlerInfo() &&
2376 i::CallHandlerInfo::cast(*callback_info).NextCallHasNoSideEffect()) {
2377 return true;
2378 }
2379 // TODO(7515): always pass a valid callback info object.
2380 if (!callback_info.is_null()) {
2381 if (callback_info->IsAccessorInfo()) {
2382 // List of allowlisted internal accessors can be found in accessors.h.
2383 AccessorInfo info = AccessorInfo::cast(*callback_info);
2384 DCHECK_NE(kNotAccessor, accessor_kind);
2385 switch (accessor_kind == kSetter ? info.setter_side_effect_type()
2386 : info.getter_side_effect_type()) {
2387 case SideEffectType::kHasNoSideEffect:
2388 // We do not support setter accessors with no side effects, since
2389 // calling set accessors go through a store bytecode. Store bytecodes
2390 // are considered to cause side effects (to non-temporary objects).
2391 DCHECK_NE(kSetter, accessor_kind);
2392 return true;
2393 case SideEffectType::kHasSideEffectToReceiver:
2394 DCHECK(!receiver.is_null());
2395 if (PerformSideEffectCheckForObject(receiver)) return true;
2396 isolate_->OptionalRescheduleException(false);
2397 return false;
2398 case SideEffectType::kHasSideEffect:
2399 break;
2400 }
2401 if (FLAG_trace_side_effect_free_debug_evaluate) {
2402 PrintF("[debug-evaluate] API Callback '");
2403 info.name().ShortPrint();
2404 PrintF("' may cause side effect.\n");
2405 }
2406 } else if (callback_info->IsInterceptorInfo()) {
2407 InterceptorInfo info = InterceptorInfo::cast(*callback_info);
2408 if (info.has_no_side_effect()) return true;
2409 if (FLAG_trace_side_effect_free_debug_evaluate) {
2410 PrintF("[debug-evaluate] API Interceptor may cause side effect.\n");
2411 }
2412 } else if (callback_info->IsCallHandlerInfo()) {
2413 CallHandlerInfo info = CallHandlerInfo::cast(*callback_info);
2414 if (info.IsSideEffectFreeCallHandlerInfo()) return true;
2415 if (FLAG_trace_side_effect_free_debug_evaluate) {
2416 PrintF("[debug-evaluate] API CallHandlerInfo may cause side effect.\n");
2417 }
2418 }
2419 }
2420 side_effect_check_failed_ = true;
2421 // Throw an uncatchable termination exception.
2422 isolate_->TerminateExecution();
2423 isolate_->OptionalRescheduleException(false);
2424 return false;
2425 }
2426
PerformSideEffectCheckAtBytecode(InterpretedFrame * frame)2427 bool Debug::PerformSideEffectCheckAtBytecode(InterpretedFrame* frame) {
2428 using interpreter::Bytecode;
2429
2430 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2431 SharedFunctionInfo shared = frame->function().shared();
2432 BytecodeArray bytecode_array = shared.GetBytecodeArray();
2433 int offset = frame->GetBytecodeOffset();
2434 interpreter::BytecodeArrayAccessor bytecode_accessor(
2435 handle(bytecode_array, isolate_), offset);
2436
2437 Bytecode bytecode = bytecode_accessor.current_bytecode();
2438 interpreter::Register reg;
2439 switch (bytecode) {
2440 case Bytecode::kStaCurrentContextSlot:
2441 reg = interpreter::Register::current_context();
2442 break;
2443 default:
2444 reg = bytecode_accessor.GetRegisterOperand(0);
2445 break;
2446 }
2447 Handle<Object> object =
2448 handle(frame->ReadInterpreterRegister(reg.index()), isolate_);
2449 return PerformSideEffectCheckForObject(object);
2450 }
2451
PerformSideEffectCheckForObject(Handle<Object> object)2452 bool Debug::PerformSideEffectCheckForObject(Handle<Object> object) {
2453 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2454
2455 // We expect no side-effects for primitives.
2456 if (object->IsNumber()) return true;
2457 if (object->IsName()) return true;
2458
2459 if (temporary_objects_->HasObject(Handle<HeapObject>::cast(object))) {
2460 return true;
2461 }
2462
2463 if (FLAG_trace_side_effect_free_debug_evaluate) {
2464 PrintF("[debug-evaluate] failed runtime side effect check.\n");
2465 }
2466 side_effect_check_failed_ = true;
2467 // Throw an uncatchable termination exception.
2468 isolate_->TerminateExecution();
2469 return false;
2470 }
2471 } // namespace internal
2472 } // namespace v8
2473