[vm/debugger] Make use of variable descriptors in bytecode debugger.
Change-Id: Ibf4c8f638065c8173c5c21c8fa9c40ee5cecb587
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106427
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index c747fbd..51b1102 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -266,8 +266,16 @@
if (FLAG_dump_kernel_bytecode) {
KernelBytecodeDisassembler::Disassemble(closure);
}
+
+#if !defined(PRODUCT)
+ thread_->isolate()->debugger()->NotifyBytecodeLoaded(closure);
+#endif
}
}
+
+#if !defined(PRODUCT)
+ thread_->isolate()->debugger()->NotifyBytecodeLoaded(function);
+#endif
}
static intptr_t IndexFor(Zone* zone,
@@ -2343,13 +2351,6 @@
bytecode_reader.ReadCode(function, code_offset);
}
}
-
-#if !defined(PRODUCT)
- if (function.HasBytecode()) {
- thread->isolate()->debugger()->NotifyBytecodeLoaded(function);
- }
-#endif
-
return Error::null();
} else {
return thread->StealStickyError();
@@ -2416,6 +2417,7 @@
function.token_pos() <= var_info.end_pos) ||
(function.token_pos() <= var_info.begin_pos &&
var_info.begin_pos <= function.end_token_pos()))) {
+ var_info.scope_id++; // One level higher in the context chain.
vars.Add(
VarDesc{&String::Handle(zone, parent_vars.GetName(i)), var_info});
}
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 10412c0..9436d8d 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -652,8 +652,8 @@
void ActivationFrame::GetVarDescriptors() {
if (var_descriptors_.IsNull()) {
if (IsInterpreted()) {
- // TODO(regis): Kernel bytecode does not yet provide var descriptors.
- var_descriptors_ = Object::empty_var_descriptors().raw();
+ var_descriptors_ = bytecode().GetLocalVarDescriptors();
+ ASSERT(!var_descriptors_.IsNull());
return;
}
Code& unoptimized_code = Code::Handle(function().unoptimized_code());
@@ -684,9 +684,14 @@
OS::PrintErr("deopt_id_ %" Px "\n", deopt_id_);
OS::PrintErr("context_level_ %" Px "\n", context_level_);
DisassembleToStdout formatter;
- if (IsInterpreted()) {
- bytecode().Disassemble(&formatter);
- PcDescriptors::Handle(bytecode().pc_descriptors()).Print();
+ if (function().is_declared_in_bytecode()) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ ASSERT(function().HasBytecode());
+ const Bytecode& bytecode = Bytecode::Handle(function().bytecode());
+ bytecode.Disassemble(&formatter);
+#else
+ UNREACHABLE();
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
} else {
code().Disassemble(&formatter);
PcDescriptors::Handle(code().pc_descriptors()).Print();
@@ -702,38 +707,97 @@
OS::Abort();
}
-// Calculate the context level at the current token index of the frame.
+// Calculate the context level at the current bytecode pc or code deopt id
+// of the frame.
intptr_t ActivationFrame::ContextLevel() {
- // TODO(regis): get context level information using
- // BytecodeLocalVariablesIterator for interpreted frames and compiled frames
- // with a function coming from bytecode (function.is_declared_in_bytecode())
const Context& ctx = GetSavedCurrentContext();
if (context_level_ < 0 && !ctx.IsNull()) {
- ASSERT(!code_.is_optimized());
-
- GetVarDescriptors();
- intptr_t deopt_id = DeoptId();
- if (deopt_id == DeoptId::kNone) {
- PrintDescriptorsError("Missing deopt id");
- }
- intptr_t var_desc_len = var_descriptors_.Length();
- bool found = false;
- for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
- RawLocalVarDescriptors::VarInfo var_info;
- var_descriptors_.GetInfo(cur_idx, &var_info);
- const int8_t kind = var_info.kind();
- if ((kind == RawLocalVarDescriptors::kContextLevel) &&
- (deopt_id >= var_info.begin_pos.value()) &&
- (deopt_id <= var_info.end_pos.value())) {
- context_level_ = var_info.index();
- found = true;
- break;
+ ASSERT(IsInterpreted() || !code_.is_optimized());
+ if (function().is_declared_in_bytecode()) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ // Although this activation frame may not have bytecode, its code was
+ // compiled from bytecode.
+ if (!IsInterpreted()) {
+ // TODO(regis): If this frame was compiled from bytecode, pc_ does not
+ // reflect a bytecode pc. How do we map to one? We should generate new
+ // LocalVarDescriptors for code compiled from bytecode so that they
+ // provide deopt_id to context level mapping.
+ UNIMPLEMENTED();
}
+ ASSERT(function().HasBytecode());
+ Thread* thread = Thread::Current();
+ Zone* zone = thread->zone();
+ Bytecode& bytecode = Bytecode::Handle(zone, function().bytecode());
+ if (!bytecode.HasLocalVariablesInfo()) {
+ PrintDescriptorsError("Missing local variables info");
+ }
+ intptr_t pc_offset = pc_ - bytecode.PayloadStart();
+ kernel::BytecodeLocalVariablesIterator local_vars(zone, bytecode);
+ while (local_vars.MoveNext()) {
+ if (local_vars.Kind() ==
+ kernel::BytecodeLocalVariablesIterator::kScope) {
+ if (local_vars.StartPC() <= pc_offset &&
+ pc_offset < local_vars.EndPC()) {
+ context_level_ = local_vars.ContextLevel();
+ break;
+ }
+ }
+ }
+ if (context_level_ < 0 && function().IsClosureFunction()) {
+ // Obtain the context level from the parent function.
+ // TODO(alexmarkov): Define scope which includes the whole closure body.
+ Function& parent = Function::Handle(zone, function().parent_function());
+ intptr_t depth = 1;
+ do {
+ bytecode = parent.bytecode();
+ kernel::BytecodeLocalVariablesIterator local_vars(zone, bytecode);
+ while (local_vars.MoveNext()) {
+ if (local_vars.Kind() ==
+ kernel::BytecodeLocalVariablesIterator::kScope) {
+ if (local_vars.StartTokenPos() <= TokenPos() &&
+ TokenPos() <= local_vars.EndTokenPos()) {
+ context_level_ = local_vars.ContextLevel() + depth;
+ break;
+ }
+ }
+ }
+ if (context_level_ >= 0) break;
+ parent = parent.parent_function();
+ depth++;
+ } while (!parent.IsNull());
+ }
+ if (context_level_ < 0) {
+ PrintDescriptorsError("Missing context level in local variables info");
+ }
+#else
+ UNREACHABLE();
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+ } else {
+ ASSERT(!code_.is_optimized());
+ GetVarDescriptors();
+ intptr_t deopt_id = DeoptId();
+ if (deopt_id == DeoptId::kNone) {
+ PrintDescriptorsError("Missing deopt id");
+ }
+ intptr_t var_desc_len = var_descriptors_.Length();
+ bool found = false;
+ for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
+ RawLocalVarDescriptors::VarInfo var_info;
+ var_descriptors_.GetInfo(cur_idx, &var_info);
+ const int8_t kind = var_info.kind();
+ if ((kind == RawLocalVarDescriptors::kContextLevel) &&
+ (deopt_id >= var_info.begin_pos.value()) &&
+ (deopt_id <= var_info.end_pos.value())) {
+ context_level_ = var_info.index();
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ PrintDescriptorsError("Missing context level");
+ }
+ ASSERT(context_level_ >= 0);
}
- if (!found) {
- PrintDescriptorsError("Missing context level");
- }
- ASSERT(context_level_ >= 0);
}
return context_level_;
}
@@ -1135,10 +1199,22 @@
return *reinterpret_cast<RawObject**>(addr);
}
+// Caution: GetParameter only works for fixed parameters.
RawObject* ActivationFrame::GetParameter(intptr_t index) {
intptr_t num_parameters = function().num_fixed_parameters();
ASSERT(0 <= index && index < num_parameters);
+ if (IsInterpreted()) {
+ if (function().NumOptionalParameters() > 0) {
+ // Note that we do not access optional but only fixed parameters, hence
+ // we do not need to replicate the logic of IndexFor() in bytecode reader.
+ return GetVariableValue(fp() + index * kWordSize);
+ } else {
+ return GetVariableValue(
+ fp() - (kKBCParamEndSlotFromFp + num_parameters - index) * kWordSize);
+ }
+ }
+
if (function().NumOptionalParameters() > 0) {
// If the function has optional parameters, the first positional parameter
// can be in a number of places in the caller's frame depending on how many
@@ -1159,6 +1235,13 @@
}
RawObject* ActivationFrame::GetStackVar(VariableIndex variable_index) {
+ if (IsInterpreted()) {
+ intptr_t slot_index = -variable_index.value();
+ if (slot_index < 0) {
+ slot_index -= kKBCParamEndSlotFromFp; // Accessing a parameter.
+ }
+ return GetVariableValue(fp() + slot_index * kWordSize);
+ }
const intptr_t slot_index =
runtime_frame_layout.FrameSlotForVariableIndex(variable_index.value());
if (deopt_frame_.IsNull()) {
diff --git a/runtime/vm/scopes.h b/runtime/vm/scopes.h
index 70c6287..b1e0c2c 100644
--- a/runtime/vm/scopes.h
+++ b/runtime/vm/scopes.h
@@ -42,7 +42,7 @@
// c) [LocalVariable]s referring to values on the expression stack. Those are
// assigned by the flow graph builder. The indices of those variables are
// assigned by the flow graph builder (it simulates the expression stack
-// height), they go from -NumVariabables - ExpressionHeight.
+// height), they go from -NumVariables - ExpressionHeight.
//
// -> These variables participate only partially in SSA renaming and can
// therefore only be used with [LoadLocalInstr]s and with