[vm,dyn_modules] Fix ActivationFrame::ContextLevel for bytecode frames.

Instead of using the computed LocalVarDescriptors when the
frame is interpreted, use the LocalVariableInfo in the serialized
bytecode to find which Scope is currently active.

Add printing of local variable information to the bytecode
disassembler.

TEST=pkg/vm_service/test

Change-Id: I7bd15056e4e2a947ad16ffb83a50447c2ba59994
Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try,vm-dyn-mac-debug-arm64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/449340
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
This commit is contained in:
Tess Strickland
2025-09-15 08:14:47 -07:00
committed by Commit Queue
parent 0a8129447a
commit 827551f8e0
5 changed files with 151 additions and 21 deletions
+7
View File
@@ -2902,6 +2902,13 @@ void BytecodeReader::CollectScriptTokenPositionsFromBytecode(
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
const char* BytecodeLocalVariablesIterator::kKindNames[] = {
"Invalid",
"Scope",
"VariableDeclaration",
"ContextVariable",
};
LocalVarDescriptorsPtr BytecodeReader::ComputeLocalVarDescriptors(
Zone* zone,
const Function& function,
+2
View File
@@ -594,6 +594,7 @@ class BytecodeLocalVariablesIterator : ValueObject {
kContextVariable,
};
static const char* kKindNames[];
static const intptr_t kKindMask = 0xF;
static const intptr_t kIsCapturedFlag = 1 << 4;
@@ -641,6 +642,7 @@ class BytecodeLocalVariablesIterator : ValueObject {
bool IsDone() const { return entries_remaining_ < 0; }
intptr_t Kind() const { return cur_kind_and_flags_ & kKindMask; }
const char* KindName() const { return kKindNames[Kind()]; }
bool IsScope() const { return Kind() == kScope; }
bool IsVariableDeclaration() const { return Kind() == kVariableDeclaration; }
bool IsContextVariable() const { return Kind() == kContextVariable; }
@@ -354,6 +354,23 @@ void KernelBytecodeDisassembler::Disassemble(uword start,
#endif
}
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
static const int kLocalVariableKindMaxWidth = strlen(
bytecode::BytecodeLocalVariablesIterator::kKindNames
[bytecode::BytecodeLocalVariablesIterator::kVariableDeclaration]);
static const int kLocalVariableColumnWidths[] = {
kLocalVariableKindMaxWidth, // kind
14, // start pc
14, // end pc
7, // context level
7, // index
7, // start token pos
7, // end token pos
7, // decl token pos
};
#endif
void KernelBytecodeDisassembler::Disassemble(const Function& function) {
#if !defined(PRODUCT)
ASSERT(function.HasBytecode());
@@ -397,6 +414,69 @@ void KernelBytecodeDisassembler::Disassemble(const Function& function) {
THR_Print("}\n");
}
if (bytecode.HasLocalVariablesInfo()) {
#if !defined(DART_PRECOMPILED_RUNTIME)
auto& name = String::Handle(zone);
auto& type = AbstractType::Handle(zone);
THR_Print("Local variable information for function '%s' {\n",
function_fullname);
// "*" in a printf format specifier tells it to read the field width from
// the printf argument list.
THR_Print(
" %*s %*s %*s %*s %*s %*s %*s %*s name\n",
kLocalVariableColumnWidths[0], "kind", kLocalVariableColumnWidths[1],
"start pc", kLocalVariableColumnWidths[2], "end pc",
kLocalVariableColumnWidths[3], "ctx", kLocalVariableColumnWidths[4],
"index", kLocalVariableColumnWidths[5], "start",
kLocalVariableColumnWidths[6], "end", kLocalVariableColumnWidths[7],
"decl");
bytecode::BytecodeLocalVariablesIterator iter(zone, bytecode);
while (iter.MoveNext()) {
THR_Print(" %*s %-#*" Px "", kLocalVariableColumnWidths[0],
iter.KindName(), kLocalVariableColumnWidths[1],
base + iter.StartPC());
if (iter.IsVariableDeclaration() || iter.IsScope()) {
THR_Print(" %-#*" Px "", kLocalVariableColumnWidths[2],
base + iter.EndPC());
} else {
THR_Print(" %*s", kLocalVariableColumnWidths[2], "");
}
if (iter.IsScope()) {
THR_Print(" %*" Pd "", kLocalVariableColumnWidths[3],
iter.ContextLevel());
} else {
THR_Print(" %*s", kLocalVariableColumnWidths[3], "");
}
if (iter.IsContextVariable() || iter.IsVariableDeclaration()) {
THR_Print(" %*" Pd "", kLocalVariableColumnWidths[4], iter.Index());
} else {
THR_Print(" %*s", kLocalVariableColumnWidths[4], "");
}
if (iter.IsVariableDeclaration() || iter.IsScope()) {
THR_Print(" %*s %*s", kLocalVariableColumnWidths[5],
iter.StartTokenPos().ToCString(),
kLocalVariableColumnWidths[6],
iter.EndTokenPos().ToCString());
} else {
THR_Print(" %*s %*s", kLocalVariableColumnWidths[5], "",
kLocalVariableColumnWidths[6], "");
}
if (iter.IsVariableDeclaration()) {
name = iter.Name();
type = iter.Type();
THR_Print(" %*s %s: %s%s", kLocalVariableColumnWidths[7],
iter.DeclarationTokenPos().ToCString(), name.ToCString(),
type.ToCString(), iter.IsCaptured() ? " (captured)" : "");
}
THR_Print("\n");
}
THR_Print("}\n");
#else
UNREACHABLE();
#endif
}
THR_Print("Exception Handlers for function '%s' {\n", function_fullname);
const ExceptionHandlers& handlers =
ExceptionHandlers::Handle(zone, bytecode.exception_handlers());
+53 -18
View File
@@ -636,7 +636,15 @@ void ActivationFrame::PrintDescriptorsError(const char* message) {
OS::PrintErr("deopt_id_ %" Px "\n", deopt_id_);
OS::PrintErr("context_level_ %" Px "\n", context_level_);
OS::PrintErr("token_pos_ %s\n", token_pos_.ToCString());
{
if (IsInterpreted()) {
#if defined(DART_DYNAMIC_MODULES)
DisassembleToStdout formatter;
bytecode().Disassemble(&formatter);
OS::PrintErr("%s\n", var_descriptors_.ToCString());
#else
UNREACHABLE();
#endif
} else {
DisassembleToStdout formatter;
code().Disassemble(&formatter);
PcDescriptors::Handle(code().pc_descriptors()).Print();
@@ -658,24 +666,51 @@ intptr_t ActivationFrame::ContextLevel() {
const Context& ctx = GetSavedCurrentContext();
if (context_level_ < 0 && !ctx.IsNull()) {
ASSERT(IsInterpreted() || !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;
// We store the deopt ids as real token positions.
const auto to_compare = TokenPosition::Deserialize(deopt_id);
for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
UntaggedLocalVarDescriptors::VarInfo var_info;
var_descriptors_.GetInfo(cur_idx, &var_info);
const int8_t kind = var_info.kind();
if ((kind == UntaggedLocalVarDescriptors::kContextLevel) &&
to_compare.IsWithin(var_info.begin_pos, var_info.end_pos)) {
context_level_ = var_info.index();
found = true;
break;
if (IsInterpreted()) {
#if defined(DART_DYNAMIC_MODULES) && !defined(PRODUCT) && \
!defined(DART_PRECOMPILED_RUNTIME)
const intptr_t pc_offset = pc() - PayloadStart();
DEBUG_ONLY(intptr_t closest_start = 0);
bytecode::BytecodeLocalVariablesIterator local_vars(
Thread::Current()->zone(), bytecode());
while (local_vars.MoveNext()) {
if (local_vars.IsScope()) {
if (local_vars.StartPC() <= pc_offset &&
pc_offset < local_vars.EndPC()) {
DEBUG_ASSERT(!found || local_vars.StartPC() > closest_start);
found = true;
context_level_ = local_vars.ContextLevel();
DEBUG_ONLY(closest_start = local_vars.StartPC());
} else if (local_vars.StartPC() > pc_offset) {
// The scopes in the local variables info are ordered by starting
// PC offset, so no need to search further.
break;
}
}
}
#else
UNREACHABLE();
#endif
} else {
GetVarDescriptors();
intptr_t var_desc_len = var_descriptors_.Length();
// We store the deopt ids as real token positions.
intptr_t deopt_id = DeoptId();
if (deopt_id == DeoptId::kNone) {
PrintDescriptorsError("Missing deopt id");
}
const TokenPosition to_compare = TokenPosition::Deserialize(deopt_id);
for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
UntaggedLocalVarDescriptors::VarInfo var_info;
var_descriptors_.GetInfo(cur_idx, &var_info);
const int8_t kind = var_info.kind();
if ((kind == UntaggedLocalVarDescriptors::kContextLevel) &&
to_compare.IsWithin(var_info.begin_pos, var_info.end_pos)) {
context_level_ = var_info.index();
found = true;
break;
}
}
}
if (!found) {
+9 -3
View File
@@ -7601,9 +7601,6 @@ class Bytecode : public Object {
void set_local_variables_binary_offset(intptr_t value) const {
StoreNonPointer(&untag()->local_variables_binary_offset_, value);
}
bool HasLocalVariablesInfo() const {
return (local_variables_binary_offset() != 0);
}
LocalVarDescriptorsPtr var_descriptors() const {
return untag()->var_descriptors<std::memory_order_acquire>();
@@ -7616,6 +7613,15 @@ class Bytecode : public Object {
// Will compute local var descriptors if necessary.
LocalVarDescriptorsPtr GetLocalVarDescriptors() const;
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
bool HasLocalVariablesInfo() const {
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
return (local_variables_binary_offset() != 0);
#else
return false;
#endif
}
const char* Name() const;
const char* QualifiedName() const;
const char* FullyQualifiedName() const;