dart-sdk/runtime/vm/debugger.h
Martin Kustermann 163f30f1b5 [vm] Ensure unoptimized compilations do not have an active isolate
Our compiler shouldn't depend on current isolate, since it can
run on any isolate within an IG.

Doing this change, reveals two existing dependencies on current
isolate from compiler
- resolving native symbols in unoptimized compilations
- issuing of debug events for breakpoints

For the former we'll re-enter the currently active isolate that
triggered unoptimized compilation.
=> We may want to change that embedder API to not be based on
   handles and instead give embedder a simple `const char*`.

For the ladder we'll enter the isolate corresponding to the
breakpoint debug event to be issued. We are at place where
all mutators are stopped, so that does seem okish.
=> Future could remove this by making Object Id Ring per-IG

Issue https://github.com/dart-lang/sdk/issues/48523

TEST=service_2/break_on_function_many_child_isolates_test/dds

Change-Id: Id246db5972ae505e82f637ce04bb2302bed76257
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278901
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
2023-01-18 08:45:31 +00:00

997 lines
32 KiB
C++

// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_DEBUGGER_H_
#define RUNTIME_VM_DEBUGGER_H_
#include <memory>
#include "include/dart_tools_api.h"
#include "vm/kernel_isolate.h"
#include "vm/object.h"
#include "vm/port.h"
#include "vm/scopes.h"
#include "vm/service_event.h"
#include "vm/simulator.h"
#include "vm/stack_frame.h"
#include "vm/stack_trace.h"
#if !defined(PRODUCT)
DECLARE_FLAG(bool, verbose_debug);
// 'Trace Debugger' TD_Print.
#if defined(_MSC_VER)
#define TD_Print(format, ...) \
if (FLAG_verbose_debug) Log::Current()->Print(format, __VA_ARGS__)
#else
#define TD_Print(format, ...) \
if (FLAG_verbose_debug) Log::Current()->Print(format, ##__VA_ARGS__)
#endif
namespace dart {
class CodeBreakpoint;
class Isolate;
class JSONArray;
class JSONStream;
class ObjectPointerVisitor;
class BreakpointLocation;
class StackFrame;
// A user-defined breakpoint, which either fires once, for a particular closure,
// or always. The API's notion of a breakpoint corresponds to this object.
class Breakpoint {
public:
Breakpoint(intptr_t id, BreakpointLocation* bpt_location)
: id_(id),
kind_(Breakpoint::kNone),
next_(NULL),
closure_(Instance::null()),
bpt_location_(bpt_location),
is_synthetic_async_(false) {}
intptr_t id() const { return id_; }
Breakpoint* next() const { return next_; }
void set_next(Breakpoint* n) { next_ = n; }
BreakpointLocation* bpt_location() const { return bpt_location_; }
void set_bpt_location(BreakpointLocation* new_bpt_location);
bool IsRepeated() const { return kind_ == kRepeated; }
bool IsSingleShot() const { return kind_ == kSingleShot; }
bool IsPerClosure() const { return kind_ == kPerClosure; }
InstancePtr closure() const { return closure_; }
void SetIsRepeated() {
ASSERT(kind_ == kNone);
kind_ = kRepeated;
}
void SetIsSingleShot() {
ASSERT(kind_ == kNone);
kind_ = kSingleShot;
}
void SetIsPerClosure(const Instance& closure) {
ASSERT(kind_ == kNone);
kind_ = kPerClosure;
closure_ = closure.ptr();
}
void Enable() {
ASSERT(!enabled_);
enabled_ = true;
}
void Disable() {
ASSERT(enabled_);
enabled_ = false;
}
bool is_enabled() const { return enabled_; }
// Mark that this breakpoint is a result of a step OverAwait request.
void set_is_synthetic_async(bool is_synthetic_async) {
is_synthetic_async_ = is_synthetic_async;
}
bool is_synthetic_async() const { return is_synthetic_async_; }
void PrintJSON(JSONStream* stream);
private:
void VisitObjectPointers(ObjectPointerVisitor* visitor);
enum ConditionKind {
kNone,
kRepeated,
kSingleShot,
kPerClosure,
};
intptr_t id_;
ConditionKind kind_;
Breakpoint* next_;
InstancePtr closure_;
BreakpointLocation* bpt_location_;
bool is_synthetic_async_;
bool enabled_ = false;
friend class BreakpointLocation;
DISALLOW_COPY_AND_ASSIGN(Breakpoint);
};
// BreakpointLocation represents a collection of breakpoint conditions at the
// same token position in Dart source. There may be more than one CodeBreakpoint
// object per BreakpointLocation.
// An unresolved breakpoint is one where the underlying code has not
// been compiled yet. Since the code has not been compiled, we don't know
// the definitive source location yet. The requested source location may
// change when the underlying code gets compiled.
// A latent breakpoint represents a breakpoint location in Dart source
// that is not loaded in the VM when the breakpoint is requested.
// When a script with matching url is loaded, a latent breakpoint
// becomes an unresolved breakpoint.
class BreakpointLocation {
public:
// Create a new unresolved breakpoint.
BreakpointLocation(Debugger* debugger,
const GrowableHandlePtrArray<const Script>& scripts,
TokenPosition token_pos,
TokenPosition end_token_pos,
intptr_t requested_line_number,
intptr_t requested_column_number);
// Create a new latent breakpoint.
BreakpointLocation(Debugger* debugger,
const String& url,
intptr_t requested_line_number,
intptr_t requested_column_number);
~BreakpointLocation();
TokenPosition token_pos() const { return token_pos_.load(); }
intptr_t line_number();
TokenPosition end_token_pos() const { return end_token_pos_.load(); }
ScriptPtr script() const {
if (scripts_.length() == 0) {
return Script::null();
}
return scripts_.At(0);
}
StringPtr url() const { return url_; }
intptr_t requested_line_number() const { return requested_line_number_; }
intptr_t requested_column_number() const { return requested_column_number_; }
void GetCodeLocation(Script* script, TokenPosition* token_pos) const;
Breakpoint* AddRepeated(Debugger* dbg);
Breakpoint* AddSingleShot(Debugger* dbg);
Breakpoint* AddPerClosure(Debugger* dbg,
const Instance& closure,
bool for_over_await);
bool AnyEnabled() const;
bool IsResolved() const { return code_token_pos_.IsReal(); }
bool IsLatent() const { return !token_pos().IsReal(); }
bool EnsureIsResolved(const Function& target_function,
TokenPosition exact_token_pos);
Debugger* debugger() { return debugger_; }
private:
void VisitObjectPointers(ObjectPointerVisitor* visitor);
void SetResolved(const Function& func, TokenPosition token_pos);
BreakpointLocation* next() const { return this->next_; }
void set_next(BreakpointLocation* value) { next_ = value; }
void AddBreakpoint(Breakpoint* bpt, Debugger* dbg);
Breakpoint* breakpoints() const { return this->conditions_; }
void set_breakpoints(Breakpoint* head) { this->conditions_ = head; }
// Finds the breakpoint we hit at |location|.
Breakpoint* FindHitBreakpoint(ActivationFrame* top_frame);
SafepointRwLock* line_number_lock() { return line_number_lock_.get(); }
Debugger* debugger_;
MallocGrowableArray<ScriptPtr> scripts_;
StringPtr url_;
std::unique_ptr<SafepointRwLock> line_number_lock_;
intptr_t line_number_; // lazily computed for token_pos_
std::atomic<TokenPosition> token_pos_;
std::atomic<TokenPosition> end_token_pos_;
BreakpointLocation* next_;
Breakpoint* conditions_;
intptr_t requested_line_number_;
intptr_t requested_column_number_;
// Valid for resolved breakpoints:
TokenPosition code_token_pos_;
friend class Debugger;
friend class GroupDebugger;
DISALLOW_COPY_AND_ASSIGN(BreakpointLocation);
};
// CodeBreakpoint represents a location in compiled code.
// There may be more than one CodeBreakpoint for one BreakpointLocation,
// e.g. when a function gets compiled as a regular function and as a closure.
// There may be more than one BreakpointLocation associated with CodeBreakpoint,
// one for every isolate in a group that sets a breakpoint at particular
// code location represented by the CodeBreakpoint.
// Each BreakpointLocation might be enabled/disabled based on whether it has
// any actual breakpoints associated with it.
// The CodeBreakpoint is enabled if it has any such BreakpointLocations
// associated with it.
// The class is not thread-safe - users of this class need to ensure the access
// is synchronized, guarded by mutexes or run inside of a safepoint scope.
class CodeBreakpoint {
public:
// Unless CodeBreakpoint is unlinked and is no longer used there should be at
// least one BreakpointLocation associated with CodeBreakpoint. If there are
// more BreakpointLocation added assumption is is that all of them point to
// the same source so have the same token pos.
CodeBreakpoint(const Code& code,
BreakpointLocation* loc,
uword pc,
UntaggedPcDescriptors::Kind kind);
~CodeBreakpoint();
// Used by GroupDebugger to find CodeBreakpoint associated with
// particular function.
FunctionPtr function() const { return Code::Handle(code_).function(); }
uword pc() const { return pc_; }
bool HasBreakpointLocation(BreakpointLocation* breakpoint_location);
bool FindAndDeleteBreakpointLocation(BreakpointLocation* breakpoint_location);
bool HasNoBreakpointLocations() {
return breakpoint_locations_.length() == 0;
}
void Enable();
void Disable();
bool IsEnabled() const { return enabled_count_ > 0; }
CodePtr OrigStubAddress() const;
const char* ToCString() const;
private:
void VisitObjectPointers(ObjectPointerVisitor* visitor);
// Finds right BreakpointLocation for a given Isolate's debugger.
BreakpointLocation* FindBreakpointForDebugger(Debugger* debugger);
// Adds new BreakpointLocation for another isolate that wants to
// break at the same function/code location that this CodeBreakpoint
// represents.
void AddBreakpointLocation(BreakpointLocation* breakpoint_location) {
ASSERT(breakpoint_locations_.length() == 0 ||
(breakpoint_location->token_pos() ==
breakpoint_locations_.At(0)->token_pos() &&
breakpoint_location->url() == breakpoint_locations_.At(0)->url()));
breakpoint_locations_.Add(breakpoint_location);
}
void set_next(CodeBreakpoint* value) { next_ = value; }
CodeBreakpoint* next() const { return this->next_; }
void PatchCode();
void RestoreCode();
CodePtr code_;
uword pc_;
int enabled_count_; // incremented for every enabled breakpoint location
// Breakpoint locations from different debuggers/isolates that
// point to this code breakpoint.
MallocGrowableArray<BreakpointLocation*> breakpoint_locations_;
CodeBreakpoint* next_;
UntaggedPcDescriptors::Kind breakpoint_kind_;
CodePtr saved_value_;
friend class Debugger;
friend class GroupDebugger;
DISALLOW_COPY_AND_ASSIGN(CodeBreakpoint);
};
// ActivationFrame represents one dart function activation frame
// on the call stack.
class ActivationFrame : public ZoneAllocated {
public:
enum Kind {
kRegular,
kAsyncSuspensionMarker,
kAsyncCausal,
kAsyncActivation,
};
ActivationFrame(uword pc,
uword fp,
uword sp,
const Code& code,
const Array& deopt_frame,
intptr_t deopt_frame_offset,
Kind kind = kRegular);
ActivationFrame(uword pc, const Code& code);
explicit ActivationFrame(Kind kind);
ActivationFrame(const Closure& async_activation,
CallerClosureFinder* caller_closure_finder);
uword pc() const { return pc_; }
uword fp() const { return fp_; }
uword sp() const { return sp_; }
uword GetCallerSp() const { return fp() + (kCallerSpSlotFromFp * kWordSize); }
const Function& function() const {
return function_;
}
const Code& code() const {
ASSERT(!code_.IsNull());
return code_;
}
enum Relation {
kCallee,
kSelf,
kCaller,
};
Relation CompareTo(uword other_fp) const;
StringPtr QualifiedFunctionName();
StringPtr SourceUrl();
ScriptPtr SourceScript();
LibraryPtr Library();
TokenPosition TokenPos();
intptr_t LineNumber();
intptr_t ColumnNumber();
// Returns true if this frame is for a function that is visible
// to the user and can be debugged.
bool IsDebuggable() const;
// Returns true if it is possible to rewind the debugger to this frame.
bool IsRewindable() const;
// The context level of a frame is the context level at the
// PC/token index of the frame. It determines the depth of the context
// chain that belongs to the function of this activation frame.
intptr_t ContextLevel();
const char* ToCString();
intptr_t NumLocalVariables();
void VariableAt(intptr_t i,
String* name,
TokenPosition* declaration_token_pos,
TokenPosition* visible_start_token_pos,
TokenPosition* visible_end_token_pos,
Object* value);
ArrayPtr GetLocalVariables();
ObjectPtr GetParameter(intptr_t index);
ClosurePtr GetClosure();
ObjectPtr GetReceiver();
const Context& GetSavedCurrentContext();
ObjectPtr GetSuspendStateVar();
ObjectPtr GetSuspendableFunctionData();
TypeArgumentsPtr BuildParameters(
const GrowableObjectArray& param_names,
const GrowableObjectArray& param_values,
const GrowableObjectArray& type_params_names,
const GrowableObjectArray& type_params_bounds,
const GrowableObjectArray& type_params_defaults);
ObjectPtr EvaluateCompiledExpression(const ExternalTypedData& kernel_data,
const Array& arguments,
const Array& type_definitions,
const TypeArguments& type_arguments);
void PrintToJSONObject(JSONObject* jsobj);
// Get Closure that await'ed this async frame.
ObjectPtr GetAsyncAwaiter(CallerClosureFinder* caller_closure_finder);
bool HandlesException(const Instance& exc_obj);
private:
void PrintToJSONObjectRegular(JSONObject* jsobj);
void PrintToJSONObjectAsyncCausal(JSONObject* jsobj);
void PrintToJSONObjectAsyncSuspensionMarker(JSONObject* jsobj);
void PrintContextMismatchError(intptr_t ctx_slot,
intptr_t frame_ctx_level,
intptr_t var_ctx_level);
void PrintDescriptorsError(const char* message);
intptr_t TryIndex();
intptr_t DeoptId();
void GetPcDescriptors();
void GetVarDescriptors();
void GetDescIndices();
bool IsAsyncMachinery() const;
static const char* KindToCString(Kind kind) {
switch (kind) {
case kRegular:
return "Regular";
case kAsyncCausal:
return "AsyncCausal";
case kAsyncSuspensionMarker:
return "AsyncSuspensionMarker";
case kAsyncActivation:
return "AsyncActivation";
default:
UNREACHABLE();
return "";
}
}
ObjectPtr GetStackVar(VariableIndex var_index);
ObjectPtr GetRelativeContextVar(intptr_t ctxt_level,
intptr_t slot_index,
intptr_t frame_ctx_level);
ObjectPtr GetContextVar(intptr_t ctxt_level, intptr_t slot_index);
uword pc_;
uword fp_;
uword sp_;
// The anchor of the context chain for this function.
Context& ctx_;
Code& code_;
Function& function_;
bool live_frame_; // Is this frame a live frame?
bool token_pos_initialized_;
TokenPosition token_pos_;
intptr_t try_index_;
intptr_t deopt_id_;
intptr_t line_number_;
intptr_t column_number_;
intptr_t context_level_;
// Some frames are deoptimized into a side array in order to inspect them.
const Array& deopt_frame_;
const intptr_t deopt_frame_offset_;
Kind kind_;
bool vars_initialized_;
LocalVarDescriptors& var_descriptors_;
ZoneGrowableArray<intptr_t> desc_indices_;
PcDescriptors& pc_desc_;
friend class Debugger;
friend class DebuggerStackTrace;
DISALLOW_COPY_AND_ASSIGN(ActivationFrame);
};
// Array of function activations on the call stack.
class DebuggerStackTrace : public ZoneAllocated {
public:
explicit DebuggerStackTrace(int capacity) : trace_(capacity) {}
intptr_t Length() const { return trace_.length(); }
ActivationFrame* FrameAt(int i) const { return trace_[i]; }
ActivationFrame* GetHandlerFrame(const Instance& exc_obj) const;
static DebuggerStackTrace* CollectAwaiterReturn();
static DebuggerStackTrace* Collect();
// Returns a debugger stack trace corresponding to a dart.core.StackTrace.
// Frames corresponding to invisible functions are omitted. It is not valid
// to query local variables in the returned stack.
static DebuggerStackTrace* From(const class StackTrace& ex_trace);
private:
void AddActivation(ActivationFrame* frame);
void AddMarker(ActivationFrame::Kind marker);
void AddAsyncCausalFrame(uword pc, const Code& code);
void AppendCodeFrames(Thread* thread,
Isolate* isolate,
Zone* zone,
StackFrame* frame,
Code* code,
Code* inlined_code,
Array* deopt_frame);
static DebuggerStackTrace* CollectAsyncCausal();
ZoneGrowableArray<ActivationFrame*> trace_;
friend class Debugger;
DISALLOW_COPY_AND_ASSIGN(DebuggerStackTrace);
};
// On which exceptions to pause.
typedef enum {
kNoPauseOnExceptions = 1,
kPauseOnUnhandledExceptions,
kPauseOnAllExceptions,
kInvalidExceptionPauseInfo
} Dart_ExceptionPauseInfo;
class DebuggerKeyValueTrait : public AllStatic {
public:
typedef const Debugger* Key;
typedef bool Value;
struct Pair {
Key key;
Value value;
Pair() : key(NULL), value(false) {}
Pair(const Key key, const Value& value) : key(key), value(value) {}
Pair(const Pair& other) : key(other.key), value(other.value) {}
Pair& operator=(const Pair&) = default;
};
static Key KeyOf(Pair kv) { return kv.key; }
static Value ValueOf(Pair kv) { return kv.value; }
static uword Hash(Key key) {
return Utils::WordHash(reinterpret_cast<intptr_t>(key));
}
static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
};
class DebuggerSet : public MallocDirectChainedHashMap<DebuggerKeyValueTrait> {
public:
typedef DebuggerKeyValueTrait::Key Key;
typedef DebuggerKeyValueTrait::Value Value;
typedef DebuggerKeyValueTrait::Pair Pair;
virtual ~DebuggerSet() { Clear(); }
void Insert(const Key& key) {
Pair pair(key, /*value=*/true);
MallocDirectChainedHashMap<DebuggerKeyValueTrait>::Insert(pair);
}
void Remove(const Key& key) {
MallocDirectChainedHashMap<DebuggerKeyValueTrait>::Remove(key);
}
};
class BoolCallable : public ValueObject {
public:
BoolCallable() {}
virtual ~BoolCallable() {}
virtual bool Call() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(BoolCallable);
};
template <typename T>
class LambdaBoolCallable : public BoolCallable {
public:
explicit LambdaBoolCallable(T& lambda) : lambda_(lambda) {}
bool Call() { return lambda_(); }
private:
T& lambda_;
DISALLOW_COPY_AND_ASSIGN(LambdaBoolCallable);
};
class GroupDebugger {
public:
explicit GroupDebugger(IsolateGroup* isolate_group);
~GroupDebugger();
void MakeCodeBreakpointAt(const Function& func, BreakpointLocation* bpt);
// Returns [nullptr] if no breakpoint exists for the given address.
CodeBreakpoint* GetCodeBreakpoint(uword breakpoint_address);
BreakpointLocation* GetBreakpointLocationFor(Debugger* debugger,
uword breakpoint_address,
CodeBreakpoint** pcbpt);
CodePtr GetPatchedStubAddress(uword breakpoint_address);
void RegisterBreakpointLocation(BreakpointLocation* location);
void UnregisterBreakpointLocation(BreakpointLocation* location);
void RemoveBreakpointLocation(BreakpointLocation* bpt_location);
void UnlinkCodeBreakpoints(BreakpointLocation* bpt_location);
// Returns true if the call at address pc is patched to point to
// a debugger stub.
bool HasActiveBreakpoint(uword pc);
bool HasCodeBreakpointInFunction(const Function& func);
bool HasCodeBreakpointInCode(const Code& code);
bool HasBreakpointInFunction(const Function& func);
bool HasBreakpointInCode(const Code& code);
void SyncBreakpointLocation(BreakpointLocation* loc);
void Pause();
bool EnsureLocationIsInFunction(Zone* zone,
const Function& function,
BreakpointLocation* location);
void NotifyCompilation(const Function& func);
void VisitObjectPointers(ObjectPointerVisitor* visitor);
SafepointRwLock* code_breakpoints_lock() {
return code_breakpoints_lock_.get();
}
SafepointRwLock* breakpoint_locations_lock() {
return breakpoint_locations_lock_.get();
}
SafepointRwLock* single_stepping_set_lock() {
return single_stepping_set_lock_.get();
}
void RegisterSingleSteppingDebugger(Thread* thread, const Debugger* debugger);
void UnregisterSingleSteppingDebugger(Thread* thread,
const Debugger* debugger);
bool RunUnderReadLockIfNeededCallable(Thread* thread,
SafepointRwLock* rw_lock,
BoolCallable* callable);
template <typename T>
bool RunUnderReadLockIfNeeded(Thread* thread,
SafepointRwLock* rw_lock,
T function) {
LambdaBoolCallable<T> callable(function);
return RunUnderReadLockIfNeededCallable(thread, rw_lock, &callable);
}
// Returns [true] if there is at least one breakpoint set in function or code.
// Checks for both user-defined and internal temporary breakpoints.
bool HasBreakpoint(Thread* thread, const Function& function);
bool IsDebugging(Thread* thread, const Function& function);
private:
IsolateGroup* isolate_group_;
std::unique_ptr<SafepointRwLock> code_breakpoints_lock_;
CodeBreakpoint* code_breakpoints_;
// Secondary list of all breakpoint_locations_(primary is in Debugger class).
// This list is kept in sync with all the lists in Isolate Debuggers and is
// used to quickly scan BreakpointLocations when new Function is compiled.
std::unique_ptr<SafepointRwLock> breakpoint_locations_lock_;
MallocGrowableArray<BreakpointLocation*> breakpoint_locations_;
std::unique_ptr<SafepointRwLock> single_stepping_set_lock_;
DebuggerSet single_stepping_set_;
void RemoveUnlinkedCodeBreakpoints();
void RegisterCodeBreakpoint(CodeBreakpoint* bpt);
bool needs_breakpoint_cleanup_;
};
class Debugger {
public:
enum ResumeAction {
kContinue,
kStepInto,
kStepOver,
kStepOut,
kStepRewind,
kStepOverAsyncSuspension,
};
explicit Debugger(Isolate* isolate);
~Debugger();
Isolate* isolate() const { return isolate_; }
void NotifyIsolateCreated();
void Shutdown();
void NotifyDoneLoading();
// Set breakpoint at closest location to function entry.
Breakpoint* SetBreakpointAtEntry(const Function& target_function,
bool single_shot);
Breakpoint* SetBreakpointAtActivation(const Instance& closure,
bool for_over_await);
Breakpoint* BreakpointAtActivation(const Instance& closure);
// TODO(turnidge): script_url may no longer be specific enough.
Breakpoint* SetBreakpointAtLine(const String& script_url,
intptr_t line_number);
Breakpoint* SetBreakpointAtLineCol(const String& script_url,
intptr_t line_number,
intptr_t column_number);
BreakpointLocation* BreakpointLocationAtLineCol(const String& script_url,
intptr_t line_number,
intptr_t column_number);
// Returns true if the breakpoint's state changed.
bool SetBreakpointState(Breakpoint* bpt, bool enable);
void RemoveBreakpoint(intptr_t bp_id);
Breakpoint* GetBreakpointById(intptr_t id);
void MaybeAsyncStepInto(const Closure& async_op);
void AsyncStepInto(const Closure& async_op);
void Continue();
bool SetResumeAction(ResumeAction action,
intptr_t frame_index = 1,
const char** error = NULL);
bool IsStepping() const { return resume_action_ != kContinue; }
bool IsSingleStepping() const { return resume_action_ == kStepInto; }
bool IsPaused() const { return pause_event_ != NULL; }
bool ignore_breakpoints() const { return ignore_breakpoints_; }
void set_ignore_breakpoints(bool ignore_breakpoints) {
ignore_breakpoints_ = ignore_breakpoints;
}
// Put the isolate into single stepping mode when Dart code next runs.
//
// This is used by the vm service to allow the user to step while
// paused at isolate start.
void EnterSingleStepMode();
// Indicates why the debugger is currently paused. If the debugger
// is not paused, this returns NULL. Note that the debugger can be
// paused for breakpoints, isolate interruption, and (sometimes)
// exceptions.
const ServiceEvent* PauseEvent() const { return pause_event_; }
void SetExceptionPauseInfo(Dart_ExceptionPauseInfo pause_info);
Dart_ExceptionPauseInfo GetExceptionPauseInfo() const;
void VisitObjectPointers(ObjectPointerVisitor* visitor);
// Returns a stack trace with frames corresponding to invisible functions
// omitted. CurrentStackTrace always returns a new trace on the current stack.
// The trace returned by StackTrace may have been cached; it is suitable for
// use when stepping, but otherwise may be out of sync with the current stack.
DebuggerStackTrace* StackTrace();
DebuggerStackTrace* AsyncCausalStackTrace();
DebuggerStackTrace* AwaiterStackTrace();
// Pause execution for a breakpoint. Called from generated code.
ErrorPtr PauseBreakpoint();
// Pause execution due to stepping. Called from generated code.
ErrorPtr PauseStepping();
// Pause execution due to isolate interrupt.
ErrorPtr PauseInterrupted();
// Pause after a reload request.
ErrorPtr PausePostRequest();
// Pause execution due to an uncaught exception.
void PauseException(const Instance& exc);
// Pause execution due to a call to the debugger() function from
// Dart.
void PauseDeveloper(const String& msg);
void PrintBreakpointsToJSONArray(JSONArray* jsarr) const;
void PrintSettingsToJSONObject(JSONObject* jsobj) const;
static bool IsDebuggable(const Function& func);
intptr_t limitBreakpointId() { return next_id_; }
// Callback to the debugger to continue frame rewind, post-deoptimization.
void RewindPostDeopt();
// Sets breakpoint at resumption of a suspendable function
// with given function data (such as _Future or _AsyncStarStreamController).
void SetBreakpointAtResumption(const Object& function_data);
// Check breakpoints at frame resumption. Called from generated code.
void ResumptionBreakpoint();
private:
ErrorPtr PauseRequest(ServiceEvent::EventKind kind);
// Will return false if we are not at an await.
bool SetupStepOverAsyncSuspension(const char** error);
bool NeedsIsolateEvents();
bool NeedsDebugEvents();
void SendBreakpointEvent(ServiceEvent::EventKind kind, Breakpoint* bpt);
void FindCompiledFunctions(
const GrowableHandlePtrArray<const Script>& scripts,
TokenPosition start_pos,
TokenPosition end_pos,
GrowableObjectArray* code_function_list);
bool FindBestFit(const Script& script,
TokenPosition token_pos,
TokenPosition last_token_pos,
Function* best_fit);
void DeoptimizeWorld();
void NotifySingleStepping(bool value) const;
BreakpointLocation* SetCodeBreakpoints(
const GrowableHandlePtrArray<const Script>& scripts,
TokenPosition token_pos,
TokenPosition last_token_pos,
intptr_t requested_line,
intptr_t requested_column,
TokenPosition exact_token_pos,
const GrowableObjectArray& functions);
BreakpointLocation* SetBreakpoint(const Script& script,
TokenPosition token_pos,
TokenPosition last_token_pos,
intptr_t requested_line,
intptr_t requested_column,
const Function& function);
BreakpointLocation* SetBreakpoint(
const GrowableHandlePtrArray<const Script>& scripts,
TokenPosition token_pos,
TokenPosition last_token_pos,
intptr_t requested_line,
intptr_t requested_column,
const Function& function);
bool RemoveBreakpointFromTheList(intptr_t bp_id, BreakpointLocation** list);
Breakpoint* GetBreakpointByIdInTheList(intptr_t id, BreakpointLocation* list);
BreakpointLocation* GetLatentBreakpoint(const String& url,
intptr_t line,
intptr_t column);
void RegisterBreakpointLocation(BreakpointLocation* bpt);
BreakpointLocation* GetResolvedBreakpointLocation(
const String& script_url,
TokenPosition code_token_pos);
BreakpointLocation* GetBreakpointLocation(
const String& script_url,
TokenPosition token_pos,
intptr_t requested_line,
intptr_t requested_column,
TokenPosition code_token_pos = TokenPosition::kNoSource);
void PrintBreakpointsListToJSONArray(BreakpointLocation* sbpt,
JSONArray* jsarr) const;
void SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt);
intptr_t nextId() { return next_id_++; }
bool ShouldPauseOnException(DebuggerStackTrace* stack_trace,
const Instance& exc);
// Handles any events which pause vm execution. Breakpoints,
// interrupts, etc.
void Pause(ServiceEvent* event);
void HandleSteppingRequest(DebuggerStackTrace* stack_trace,
bool skip_next_step = false);
void CacheStackTraces(DebuggerStackTrace* stack_trace,
DebuggerStackTrace* async_causal_stack_trace,
DebuggerStackTrace* awaiter_stack_trace);
void ClearCachedStackTraces();
void RewindToFrame(intptr_t frame_index);
void RewindToUnoptimizedFrame(StackFrame* frame, const Code& code);
void RewindToOptimizedFrame(StackFrame* frame,
const Code& code,
intptr_t post_deopt_frame_index);
void ResetSteppingFramePointer();
bool SteppedForSyntheticAsyncBreakpoint() const;
void CleanupSyntheticAsyncBreakpoint();
void SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace);
GroupDebugger* group_debugger() { return isolate_->group()->debugger(); }
Isolate* isolate_;
// ID number generator.
intptr_t next_id_;
BreakpointLocation* latent_locations_;
BreakpointLocation* breakpoint_locations_;
// Tells debugger what to do when resuming execution after a breakpoint.
ResumeAction resume_action_;
void set_resume_action(ResumeAction action);
intptr_t resume_frame_index_;
intptr_t post_deopt_frame_index_;
// Do not call back to breakpoint handler if this flag is set.
// Effectively this means ignoring breakpoints. Set when Dart code may
// be run as a side effect of getting values of fields.
bool ignore_breakpoints_;
// Indicates why the debugger is currently paused. If the debugger
// is not paused, this is NULL. Note that the debugger can be
// paused for breakpoints, isolate interruption, and (sometimes)
// exceptions.
ServiceEvent* pause_event_;
// Current stack trace. Valid only while IsPaused().
DebuggerStackTrace* stack_trace_;
DebuggerStackTrace* async_causal_stack_trace_;
DebuggerStackTrace* awaiter_stack_trace_;
// When stepping through code, only pause the program if the top
// frame corresponds to this fp value, or if the top frame is
// lower on the stack.
uword stepping_fp_;
// When stepping through code, do not stop more than once in the same
// token position range.
uword last_stepping_fp_;
TokenPosition last_stepping_pos_;
// If we step while at a breakpoint, we would hit the same pc twice.
// We use this field to let us skip the next single-step after a
// breakpoint.
bool skip_next_step_;
// We keep this breakpoint alive until after the debugger does the step over
// async continuation machinery so that we can report that we've stopped
// at the breakpoint.
Breakpoint* synthetic_async_breakpoint_;
Dart_ExceptionPauseInfo exc_pause_info_;
// Holds function data corresponding to suspendable
// function which should be stopped when resumed.
MallocGrowableArray<ObjectPtr> breakpoints_at_resumption_;
friend class Isolate;
friend class BreakpointLocation;
DISALLOW_COPY_AND_ASSIGN(Debugger);
};
class DisableBreakpointsScope : public ValueObject {
public:
DisableBreakpointsScope(Debugger* debugger, bool disable)
: debugger_(debugger) {
ASSERT(debugger_ != NULL);
initial_state_ = debugger_->ignore_breakpoints();
debugger_->set_ignore_breakpoints(disable);
}
~DisableBreakpointsScope() {
debugger_->set_ignore_breakpoints(initial_state_);
}
private:
Debugger* debugger_;
bool initial_state_;
DISALLOW_COPY_AND_ASSIGN(DisableBreakpointsScope);
};
} // namespace dart
#endif // !defined(PRODUCT)
#endif // RUNTIME_VM_DEBUGGER_H_