[vm,dyn_modules] Fix/skip vm/cc tests that crash for dynamic modules.

Skip IR or snapshot-related tests when running from bytecode.

TEST=vm/cc/DartAPI_DeepStackTraceInfo
     vm/cc/DartAPI_HeapSampling_UserDefinedClass
     vm/cc/DartAPI_StackTraceInfo
     vm/cc/DartAPI_StackOverflowStackTraceInfoArrowFunction
     vm/cc/DartAPI_StackOverflowStackTraceInfoBraceFunction1
     vm/cc/DartAPI_StackOverflowStackTraceInfoBraceFunction2
     vm/cc/FrameLookup
     vm/cc/Service_LocalVarDescriptors

Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try
Change-Id: I396a4e8ddacdbb88b3844e4113dc5c4ff6287e30
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/490083
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
This commit is contained in:
Tess Strickland
2026-03-26 08:21:00 -07:00
committed by Commit Queue
parent f92673c89a
commit 0bbb2ae821
9 changed files with 78 additions and 60 deletions
+19 -20
View File
@@ -2282,12 +2282,12 @@ class BytecodeGenerator extends RecursiveVisitor {
}
}
// The CheckStack below is the instruction which should be used for function
// entry breakpoints.
_recordInitialSourcePositionForFunction(node, function);
// CheckStack must see a properly initialized context when stress-testing
// stack trace collection.
asm.emitCheckStack(0);
// After checking the stack, the next instruction is safe for setting
// the entry breakpoint for a function.
_emitSourcePositionForFunctionEntryBreakpoint(node, function);
if (locals.hasFunctionTypeArgsVar && isClosure) {
if (function!.typeParameters.isNotEmpty) {
@@ -2386,26 +2386,25 @@ class BytecodeGenerator extends RecursiveVisitor {
}
}
int _initialSourcePositionForFunction(TreeNode node, FunctionNode? function) {
// The debugger expects the initial source position to correspond to the
// declaration position of the last parameter, if any, or of the function.
if (function?.namedParameters.isNotEmpty ?? false) {
return function!.namedParameters.last.fileOffset;
} else if (function?.positionalParameters.isNotEmpty ?? false) {
return function!.positionalParameters.last.fileOffset;
} else if (function != null) {
return function.fileOffset;
} else {
return node.fileOffset;
}
}
void _recordInitialSourcePositionForFunction(
void _emitSourcePositionForFunctionEntryBreakpoint(
TreeNode node,
FunctionNode? function,
) {
final position = _initialSourcePositionForFunction(node, function);
_recordSourcePosition(position, 0);
// The debugger expects the source position for function entry to correspond
// to the declaration position of the last parameter, if any, otherwise
// the file offset of the function.
final int startOffset;
if (function?.namedParameters.isNotEmpty ?? false) {
startOffset = function!.namedParameters.last.fileOffset;
} else if (function?.positionalParameters.isNotEmpty ?? false) {
startOffset = function!.positionalParameters.last.fileOffset;
} else if (function != null) {
startOffset = function.fileOffset;
} else {
startOffset = node.fileOffset;
}
_recordSourcePosition(startOffset, 0);
asm.emitSourcePosition();
}
void _copyParamIfCaptured(VariableDeclaration variable) {
+2
View File
@@ -91,6 +91,7 @@ cc/DeoptimizeFramesWhenSettingBreakpoint: SkipByDesign # JIT compilation is not
cc/DerivedInduction: SkipByDesign # Building flow graph is not supported from bytecode.
cc/Ffi_StructSinking: SkipByDesign # Building flow graph is not supported from bytecode.
cc/FlowGraph_PhiUnboxingHeuristic_*: SkipByDesign # Building flow graph is not supported from bytecode.
cc/FullSnapshot: SkipByDesign # JIT compilation is not supported from bytecode.
cc/IRTest_*: SkipByDesign # Building flow graph is not supported from bytecode.
cc/Inliner_*: SkipByDesign # Building flow graph is not supported from bytecode.
cc/IsolateReload_EnumWithSet: SkipByDesign # Bytecode doesn't support KernelProgramInfo.
@@ -103,6 +104,7 @@ cc/NotEqualCondition*: SkipByDesign # Building flow graph is not supported from
cc/OptimizeCompileFunctionOnHelperThread: SkipByDesign # JIT compilation is not supported from bytecode.
cc/PeriodicAndDerived: SkipByDesign # Building flow graph is not supported from bytecode.
cc/RangeAnalysis_*: SkipByDesign # Building flow graph is not supported from bytecode.
cc/ReachabilityFence_*: SkipByDesign # Building flow graph is not supported from bytecode.
cc/SecondExit*: SkipByDesign # Building flow graph is not supported from bytecode.
cc/StackMapGC: SkipByDesign # JIT compilation is not supported from bytecode.
cc/StreamingFlowGraphBuilder_*: SkipByDesign # Building flow graph is not supported from bytecode.
+2 -5
View File
@@ -272,18 +272,15 @@ static void StackFrame_accessFrame(Dart_NativeArguments args) {
Thread* thread = Thread::Current();
TransitionNativeToVM transition(thread);
const int kNumIterations = 100;
Code& code = Code::Handle(thread->zone());
for (int i = 0; i < kNumIterations; i++) {
StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, thread,
StackFrameIterator::kNoCrossThreadIteration);
StackFrame* frame = frames.NextFrame();
while (frame != nullptr) {
if (frame->IsStubFrame()) {
code = frame->LookupDartCode();
EXPECT(code.function() == Function::null());
EXPECT(frame->LookupDartFunction() == Function::null());
} else if (frame->IsDartFrame()) {
code = frame->LookupDartCode();
EXPECT(code.function() != Function::null());
EXPECT(frame->LookupDartFunction() != Function::null());
}
frame = frames.NextFrame();
}
+4
View File
@@ -2425,6 +2425,10 @@ void BytecodeReaderHelper::ReadClassDeclaration(const Class& cls) {
if (!cls.is_type_finalized()) {
ClassFinalizer::FinalizeTypesInClass(cls);
}
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
cls.SetUserVisibleNameInClassTable();
#endif
}
void BytecodeReaderHelper::ReadLibraryDeclaration(const Library& library,
@@ -365,8 +365,6 @@ void KernelBytecodeDisassembler::Disassemble(uword start,
char hex_buffer[kHexadecimalBufferSize]; // Instruction in hexadecimal form.
char human_buffer[kUserReadableBufferSize]; // Human-readable instruction.
uword pc = start;
GrowableArray<const Function*> inlined_functions;
GrowableArray<TokenPosition> token_positions;
while (pc < end) {
int instruction_length;
Object* object;
+32 -26
View File
@@ -578,7 +578,7 @@ intptr_t ActivationFrame::DeoptId() {
intptr_t ActivationFrame::LineNumber() {
// Compute line number lazily since it causes scanning of the script.
const TokenPosition& token_pos = TokenPos();
const TokenPosition& token_pos = TokenPos().ToRealIfSynthetic();
if ((line_number_ < 0) && token_pos.IsReal()) {
const Script& script = Script::Handle(SourceScript());
script.GetTokenLocation(token_pos, &line_number_, &column_number_);
@@ -588,7 +588,7 @@ intptr_t ActivationFrame::LineNumber() {
intptr_t ActivationFrame::ColumnNumber() {
// Compute column number lazily since it causes scanning of the script.
const TokenPosition& token_pos = TokenPos();
const TokenPosition& token_pos = TokenPos().ToRealIfSynthetic();
if ((column_number_ < 0) && token_pos.IsReal()) {
const Script& script = Script::Handle(SourceScript());
script.GetTokenLocation(token_pos, &line_number_, &column_number_);
@@ -2063,36 +2063,42 @@ DebuggerStackTrace* DebuggerStackTrace::From(const class StackTrace& ex_trace) {
// pre-allocated trace (such as a stack overflow) or (b) because a stack has
// fewer frames that the pre-allocated trace (such as memory exhaustion with
// a shallow stack).
if (!code_object.IsNull()) {
code ^= code_object.ptr();
function = Function::null();
uword start = 0;
bool is_optimized_code = false;
if (code_object.IsCode()) {
const auto& code = Code::Cast(code_object);
ASSERT(code.IsFunctionCode());
function = code.function();
if (function.is_visible()) {
start = code.PayloadStart();
is_optimized_code = code.is_optimized();
} else if (code_object.IsBytecode()) {
const auto& bytecode = Bytecode::Cast(code_object);
function = bytecode.function();
start = bytecode.PayloadStart();
}
if (function.IsNull() || !function.is_visible()) continue;
const uword pc = start + ex_trace.PcOffsetAtFrame(i);
if (is_optimized_code && ex_trace.expand_inlined()) {
// Traverse inlined frames.
code ^= code_object.ptr();
for (InlinedFunctionsIterator it(code, pc); !it.Done(); it.Advance()) {
function = it.function();
code = it.code();
ASSERT(function.ptr() == code.function());
uword pc = code.PayloadStart() + ex_trace.PcOffsetAtFrame(i);
if (code.is_optimized() && ex_trace.expand_inlined()) {
// Traverse inlined frames.
for (InlinedFunctionsIterator it(code, pc); !it.Done();
it.Advance()) {
function = it.function();
code = it.code();
ASSERT(function.ptr() == code.function());
uword pc = it.pc();
ASSERT(pc != 0);
ASSERT(code.PayloadStart() <= pc);
ASSERT(pc < (code.PayloadStart() + code.Size()));
uword pc = it.pc();
ASSERT(pc != 0);
ASSERT(code.PayloadStart() <= pc);
ASSERT(pc < (code.PayloadStart() + code.Size()));
ActivationFrame* activation = new ActivationFrame(
pc, fp, sp, function, code, deopt_frame, deopt_frame_offset);
stack_trace->AddActivation(activation);
}
} else {
ActivationFrame* activation = new ActivationFrame(
pc, fp, sp, function, code, deopt_frame, deopt_frame_offset);
stack_trace->AddActivation(activation);
}
auto* const activation = new ActivationFrame(
pc, fp, sp, function, code, deopt_frame, deopt_frame_offset);
stack_trace->AddActivation(activation);
}
}
auto* const activation = new ActivationFrame(
pc, fp, sp, function, code_object, deopt_frame, deopt_frame_offset);
stack_trace->AddActivation(activation);
}
return stack_trace;
}
+3 -1
View File
@@ -6010,7 +6010,9 @@ void Class::set_user_name(const String& value) const {
#endif // !defined(PRODUCT)
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
void Class::SetUserVisibleNameInClassTable() {
void Class::SetUserVisibleNameInClassTable() const {
// Top level classes don't record a user visible name in the class table.
if (IsTopLevel()) return;
IsolateGroup* isolate_group = IsolateGroup::Current();
auto class_table = isolate_group->class_table();
if (class_table->UserVisibleNameFor(id()) == nullptr) {
+1 -1
View File
@@ -2098,7 +2098,7 @@ class Class : public Object {
const Field& field) const;
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
void SetUserVisibleNameInClassTable();
void SetUserVisibleNameInClassTable() const;
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
private:
+15 -5
View File
@@ -454,11 +454,21 @@ ISOLATE_UNIT_TEST_CASE(Service_LocalVarDescriptors) {
EXPECT(!class_a.IsNull());
const Function& function_c = Function::Handle(GetFunction(class_a, "c"));
EXPECT(!function_c.IsNull());
const Code& code_c = Code::Handle(function_c.CurrentCode());
EXPECT(!code_c.IsNull());
const LocalVarDescriptors& descriptors =
LocalVarDescriptors::Handle(code_c.GetLocalVarDescriptors());
LocalVarDescriptors& descriptors = LocalVarDescriptors::Handle();
if (function_c.IsInterpreted()) {
#if defined(DART_DYNAMIC_MODULES)
const Bytecode& bytecode_c = Bytecode::Handle(function_c.GetBytecode());
EXPECT(!bytecode_c.IsNull());
descriptors = bytecode_c.var_descriptors();
#else
UNREACHABLE();
#endif
} else {
const Code& code_c = Code::Handle(function_c.CurrentCode());
EXPECT(!code_c.IsNull());
descriptors = code_c.GetLocalVarDescriptors();
}
EXPECT(!descriptors.IsNull());
// Generate an ID for this object.
ServiceIdZone& default_id_zone = isolate->EnsureDefaultServiceIdZone();
const char* id = default_id_zone.GetServiceId(descriptors);