// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_DEBUG_DEBUG_H_ #define V8_DEBUG_DEBUG_H_ #include #include #include #include "src/base/enum-set.h" #include "src/base/platform/elapsed-timer.h" #include "src/codegen/source-position-table.h" #include "src/common/globals.h" #include "src/debug/debug-interface.h" #include "src/debug/interface-types.h" #include "src/execution/interrupts-scope.h" #include "src/execution/isolate.h" #include "src/handles/handles.h" #include "src/objects/debug-objects.h" #include "src/objects/shared-function-info.h" namespace v8 { namespace internal { // Forward declarations. class AbstractCode; class DebugScope; class InterpretedFrame; class JavaScriptFrame; class JSGeneratorObject; class StackFrame; // Step actions. enum StepAction : int8_t { StepNone = -1, // Stepping not prepared. StepOut = 0, // Step out of the current function. StepOver = 1, // Step to the next statement in the current function. StepInto = 2, // Step into new functions invoked or the next statement // in the current function. LastStepAction = StepInto }; // Type of exception break. NOTE: These values are in macros.py as well. enum ExceptionBreakType { BreakCaughtException = 0, BreakUncaughtException = 1, }; // Type of debug break. NOTE: The order matters for the predicates // below inside BreakLocation, so be careful when adding / removing. enum DebugBreakType { NOT_DEBUG_BREAK, DEBUGGER_STATEMENT, DEBUG_BREAK_AT_ENTRY, DEBUG_BREAK_SLOT, DEBUG_BREAK_SLOT_AT_CALL, DEBUG_BREAK_SLOT_AT_RETURN, DEBUG_BREAK_SLOT_AT_SUSPEND, }; enum IgnoreBreakMode { kIgnoreIfAllFramesBlackboxed, kIgnoreIfTopFrameBlackboxed }; class BreakLocation { public: static BreakLocation Invalid() { return BreakLocation(-1, NOT_DEBUG_BREAK); } static BreakLocation FromFrame(Handle debug_info, JavaScriptFrame* frame); static bool IsPausedInJsFunctionEntry(JavaScriptFrame* frame); static void AllAtCurrentStatement(Handle debug_info, JavaScriptFrame* frame, std::vector* result_out); bool IsSuspend() const { return type_ == DEBUG_BREAK_SLOT_AT_SUSPEND; } bool IsReturn() const { return type_ == DEBUG_BREAK_SLOT_AT_RETURN; } bool IsReturnOrSuspend() const { return type_ >= DEBUG_BREAK_SLOT_AT_RETURN; } bool IsCall() const { return type_ == DEBUG_BREAK_SLOT_AT_CALL; } bool IsDebugBreakSlot() const { return type_ >= DEBUG_BREAK_SLOT; } bool IsDebuggerStatement() const { return type_ == DEBUGGER_STATEMENT; } bool IsDebugBreakAtEntry() const { return type_ == DEBUG_BREAK_AT_ENTRY; } bool HasBreakPoint(Isolate* isolate, Handle debug_info) const; int generator_suspend_id() { return generator_suspend_id_; } int position() const { return position_; } debug::BreakLocationType type() const; Tagged GetGeneratorObjectForSuspendedFrame( JavaScriptFrame* frame) const; private: BreakLocation(Handle abstract_code, DebugBreakType type, int code_offset, int position, int generator_obj_reg_index, int generator_suspend_id) : abstract_code_(abstract_code), code_offset_(code_offset), type_(type), position_(position), generator_obj_reg_index_(generator_obj_reg_index), generator_suspend_id_(generator_suspend_id) { DCHECK_NE(NOT_DEBUG_BREAK, type_); } BreakLocation(int position, DebugBreakType type) : code_offset_(0), type_(type), position_(position), generator_obj_reg_index_(0), generator_suspend_id_(-1) {} static int BreakIndexFromCodeOffset(Handle debug_info, Handle abstract_code, int offset); void SetDebugBreak(); void ClearDebugBreak(); Handle abstract_code_; int code_offset_; DebugBreakType type_; int position_; int generator_obj_reg_index_; int generator_suspend_id_; friend class BreakIterator; }; class V8_EXPORT_PRIVATE BreakIterator { public: explicit BreakIterator(Handle debug_info); BreakIterator(const BreakIterator&) = delete; BreakIterator& operator=(const BreakIterator&) = delete; BreakLocation GetBreakLocation(); bool Done() const { return source_position_iterator_.done(); } void Next(); void SkipToPosition(int position); void SkipTo(int count) { while (count-- > 0) Next(); } int code_offset() { return source_position_iterator_.code_offset(); } int break_index() const { return break_index_; } inline int position() const { return position_; } inline int statement_position() const { return statement_position_; } void ClearDebugBreak(); void SetDebugBreak(); DebugBreakType GetDebugBreakType(); private: int BreakIndexFromPosition(int position); Isolate* isolate(); Handle debug_info_; int break_index_; int position_; int statement_position_; SourcePositionTableIterator source_position_iterator_; DISALLOW_GARBAGE_COLLECTION(no_gc_) }; // Holds all active DebugInfo objects. This is a composite data structure // consisting of // // - an unsorted list-like structure for fast iteration and // deletion-during-iteration, and // - a map-like structure for fast SharedFunctionInfo-DebugInfo lookups. // // DebugInfos are held strongly through global handles. // // TODO(jgruber): Now that we use an unordered_map as the map-like structure, // which supports deletion-during-iteration, the list-like part of this data // structure could be removed. class DebugInfoCollection final { using HandleLocation = Address*; using SFIUniqueId = uint32_t; // The type of SFI::unique_id. public: explicit DebugInfoCollection(Isolate* isolate) : isolate_(isolate) {} void Insert(Tagged sfi, Tagged debug_info); bool Contains(Tagged sfi) const; base::Optional Find(Tagged sfi) const; void DeleteSlow(Tagged sfi); size_t Size() const { return list_.size(); } class Iterator final { public: explicit Iterator(DebugInfoCollection* collection) : collection_(collection) {} bool HasNext() const { return index_ < static_cast(collection_->list_.size()); } Tagged Next() const { DCHECK_GE(index_, 0); if (!HasNext()) return {}; return collection_->EntryAsDebugInfo(index_); } void Advance() { DCHECK(HasNext()); index_++; } void DeleteNext() { DCHECK_GE(index_, 0); DCHECK(HasNext()); collection_->DeleteIndex(index_); index_--; // `Advance` must be called next. } private: using HandleLocation = DebugInfoCollection::HandleLocation; DebugInfoCollection* const collection_; int index_ = 0; // `int` because deletion may rewind to -1. }; private: V8_EXPORT_PRIVATE Tagged EntryAsDebugInfo(size_t index) const; void DeleteIndex(size_t index); Isolate* const isolate_; std::vector list_; std::unordered_map map_; }; // This class contains the debugger support. The main purpose is to handle // setting break points in the code. // // This class controls the debug info for all functions which currently have // active breakpoints in them. This debug info is held in the heap root object // debug_info which is a FixedArray. Each entry in this list is of class // DebugInfo. class V8_EXPORT_PRIVATE Debug { public: Debug(const Debug&) = delete; Debug& operator=(const Debug&) = delete; // Debug event triggers. void OnDebugBreak(Handle break_points_hit, StepAction stepAction, debug::BreakReasons break_reasons = {}); debug::DebugDelegate::ActionAfterInstrumentation OnInstrumentationBreak(); base::Optional> OnThrow(Handle exception) V8_WARN_UNUSED_RESULT; void OnPromiseReject(Handle promise, Handle value); void OnCompileError(Handle