mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 17:59:39 +00:00
[vm, ffi] Make callbacks profiler-safe.
Delay changing Thread::vm_tag on callback entry and restore the tag early on callback return so that the profiler doesn't see the "running Dart" tag unless it can also see the fake return address marking the entry frame. TEST=ffi/async_void_function_callbacks, ffi/function_callbacks_subtype, ffi/function_callbacks, ffi/isolate_local_function_callbacks Bug: https://github.com/dart-lang/sdk/issues/52814 Change-Id: I40d80ec7c44063d078db0e211565e2d127c6b81e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/367460 Reviewed-by: Daco Harkes <dacoharkes@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
parent
761b4288a1
commit
1a9acb9171
|
@ -671,7 +671,8 @@ void Assembler::ExitFullSafepoint(Register tmp1,
|
|||
void Assembler::TransitionNativeToGenerated(Register addr,
|
||||
Register state,
|
||||
bool exit_safepoint,
|
||||
bool ignore_unwind_in_progress) {
|
||||
bool ignore_unwind_in_progress,
|
||||
bool set_tag) {
|
||||
if (exit_safepoint) {
|
||||
ExitFullSafepoint(addr, state, ignore_unwind_in_progress);
|
||||
} else {
|
||||
|
@ -691,8 +692,10 @@ void Assembler::TransitionNativeToGenerated(Register addr,
|
|||
}
|
||||
|
||||
// Mark that the thread is executing Dart code.
|
||||
LoadImmediate(state, target::Thread::vm_tag_dart_id());
|
||||
StoreToOffset(state, THR, target::Thread::vm_tag_offset());
|
||||
if (set_tag) {
|
||||
LoadImmediate(state, target::Thread::vm_tag_dart_id());
|
||||
StoreToOffset(state, THR, target::Thread::vm_tag_offset());
|
||||
}
|
||||
LoadImmediate(state, target::Thread::generated_execution_state());
|
||||
StoreToOffset(state, THR, target::Thread::execution_state_offset());
|
||||
|
||||
|
|
|
@ -622,7 +622,8 @@ class Assembler : public AssemblerBase {
|
|||
void TransitionNativeToGenerated(Register scratch0,
|
||||
Register scratch1,
|
||||
bool exit_safepoint,
|
||||
bool ignore_unwind_in_progress = false);
|
||||
bool ignore_unwind_in_progress = false,
|
||||
bool set_tag = true);
|
||||
void EnterFullSafepoint(Register scratch0, Register scratch1);
|
||||
void ExitFullSafepoint(Register scratch0,
|
||||
Register scratch1,
|
||||
|
|
|
@ -1635,7 +1635,8 @@ void Assembler::ExitFullSafepoint(Register state,
|
|||
|
||||
void Assembler::TransitionNativeToGenerated(Register state,
|
||||
bool exit_safepoint,
|
||||
bool ignore_unwind_in_progress) {
|
||||
bool ignore_unwind_in_progress,
|
||||
bool set_tag) {
|
||||
if (exit_safepoint) {
|
||||
ExitFullSafepoint(state, ignore_unwind_in_progress);
|
||||
} else {
|
||||
|
@ -1655,8 +1656,10 @@ void Assembler::TransitionNativeToGenerated(Register state,
|
|||
}
|
||||
|
||||
// Mark that the thread is executing Dart code.
|
||||
LoadImmediate(state, target::Thread::vm_tag_dart_id());
|
||||
StoreToOffset(state, THR, target::Thread::vm_tag_offset());
|
||||
if (set_tag) {
|
||||
LoadImmediate(state, target::Thread::vm_tag_dart_id());
|
||||
StoreToOffset(state, THR, target::Thread::vm_tag_offset());
|
||||
}
|
||||
LoadImmediate(state, target::Thread::generated_execution_state());
|
||||
StoreToOffset(state, THR, target::Thread::execution_state_offset());
|
||||
|
||||
|
|
|
@ -2077,7 +2077,8 @@ class Assembler : public AssemblerBase {
|
|||
bool enter_safepoint);
|
||||
void TransitionNativeToGenerated(Register scratch,
|
||||
bool exit_safepoint,
|
||||
bool ignore_unwind_in_progress = false);
|
||||
bool ignore_unwind_in_progress = false,
|
||||
bool set_tag = true);
|
||||
void EnterFullSafepoint(Register scratch);
|
||||
void ExitFullSafepoint(Register scratch, bool ignore_unwind_in_progress);
|
||||
|
||||
|
|
|
@ -2565,7 +2565,8 @@ void Assembler::ExitFullSafepoint(Register scratch,
|
|||
|
||||
void Assembler::TransitionNativeToGenerated(Register scratch,
|
||||
bool exit_safepoint,
|
||||
bool ignore_unwind_in_progress) {
|
||||
bool ignore_unwind_in_progress,
|
||||
bool set_tag) {
|
||||
if (exit_safepoint) {
|
||||
ExitFullSafepoint(scratch, ignore_unwind_in_progress);
|
||||
} else {
|
||||
|
@ -2583,7 +2584,10 @@ void Assembler::TransitionNativeToGenerated(Register scratch,
|
|||
}
|
||||
|
||||
// Mark that the thread is executing Dart code.
|
||||
movl(Assembler::VMTagAddress(), Immediate(target::Thread::vm_tag_dart_id()));
|
||||
if (set_tag) {
|
||||
movl(Assembler::VMTagAddress(),
|
||||
Immediate(target::Thread::vm_tag_dart_id()));
|
||||
}
|
||||
movl(Address(THR, target::Thread::execution_state_offset()),
|
||||
Immediate(target::Thread::generated_execution_state()));
|
||||
|
||||
|
|
|
@ -875,7 +875,8 @@ class Assembler : public AssemblerBase {
|
|||
bool enter_safepoint);
|
||||
void TransitionNativeToGenerated(Register scratch,
|
||||
bool exit_safepoint,
|
||||
bool ignore_unwind_in_progress = false);
|
||||
bool ignore_unwind_in_progress = false,
|
||||
bool set_tag = true);
|
||||
void EnterFullSafepoint(Register scratch);
|
||||
void ExitFullSafepoint(Register scratch, bool ignore_unwind_in_progress);
|
||||
|
||||
|
|
|
@ -3854,7 +3854,8 @@ void Assembler::TransitionGeneratedToNative(Register destination,
|
|||
|
||||
void Assembler::TransitionNativeToGenerated(Register state,
|
||||
bool exit_safepoint,
|
||||
bool ignore_unwind_in_progress) {
|
||||
bool ignore_unwind_in_progress,
|
||||
bool set_tag) {
|
||||
if (exit_safepoint) {
|
||||
ExitFullSafepoint(state, ignore_unwind_in_progress);
|
||||
} else {
|
||||
|
@ -3874,8 +3875,10 @@ void Assembler::TransitionNativeToGenerated(Register state,
|
|||
}
|
||||
|
||||
// Mark that the thread is executing Dart code.
|
||||
li(state, target::Thread::vm_tag_dart_id());
|
||||
sx(state, Address(THR, target::Thread::vm_tag_offset()));
|
||||
if (set_tag) {
|
||||
li(state, target::Thread::vm_tag_dart_id());
|
||||
sx(state, Address(THR, target::Thread::vm_tag_offset()));
|
||||
}
|
||||
li(state, target::Thread::generated_execution_state());
|
||||
sx(state, Address(THR, target::Thread::execution_state_offset()));
|
||||
|
||||
|
|
|
@ -1295,7 +1295,8 @@ class Assembler : public MicroAssembler {
|
|||
bool enter_safepoint);
|
||||
void TransitionNativeToGenerated(Register scratch,
|
||||
bool exit_safepoint,
|
||||
bool ignore_unwind_in_progress = false);
|
||||
bool ignore_unwind_in_progress = false,
|
||||
bool set_tag = true);
|
||||
void EnterFullSafepoint(Register scratch);
|
||||
void ExitFullSafepoint(Register scratch, bool ignore_unwind_in_progress);
|
||||
|
||||
|
|
|
@ -245,7 +245,8 @@ void Assembler::ExitFullSafepoint(bool ignore_unwind_in_progress) {
|
|||
}
|
||||
|
||||
void Assembler::TransitionNativeToGenerated(bool leave_safepoint,
|
||||
bool ignore_unwind_in_progress) {
|
||||
bool ignore_unwind_in_progress,
|
||||
bool set_tag) {
|
||||
if (leave_safepoint) {
|
||||
ExitFullSafepoint(ignore_unwind_in_progress);
|
||||
} else {
|
||||
|
@ -262,7 +263,10 @@ void Assembler::TransitionNativeToGenerated(bool leave_safepoint,
|
|||
#endif
|
||||
}
|
||||
|
||||
movq(Assembler::VMTagAddress(), Immediate(target::Thread::vm_tag_dart_id()));
|
||||
if (set_tag) {
|
||||
movq(Assembler::VMTagAddress(),
|
||||
Immediate(target::Thread::vm_tag_dart_id()));
|
||||
}
|
||||
movq(Address(THR, target::Thread::execution_state_offset()),
|
||||
Immediate(target::Thread::generated_execution_state()));
|
||||
|
||||
|
|
|
@ -325,7 +325,8 @@ class Assembler : public AssemblerBase {
|
|||
Register new_exit_through_ffi,
|
||||
bool enter_safepoint);
|
||||
void TransitionNativeToGenerated(bool leave_safepoint,
|
||||
bool ignore_unwind_in_progress = false);
|
||||
bool ignore_unwind_in_progress = false,
|
||||
bool set_tag = true);
|
||||
|
||||
// Register-register, register-address and address-register instructions.
|
||||
#define RR(width, name, ...) \
|
||||
|
|
|
@ -2217,6 +2217,9 @@ class FunctionEntryInstr : public BlockEntryWithInitialDefs {
|
|||
// NativeParameter instead (which doesn't count as an initial definition).
|
||||
class NativeEntryInstr : public FunctionEntryInstr {
|
||||
public:
|
||||
static constexpr intptr_t kVMTagOffsetFromFp =
|
||||
5 * compiler::target::kWordSize;
|
||||
|
||||
NativeEntryInstr(const compiler::ffi::CallbackMarshaller& marshaller,
|
||||
GraphEntryInstr* graph_entry,
|
||||
intptr_t block_id,
|
||||
|
|
|
@ -1902,6 +1902,11 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
EmitReturnMoves(compiler);
|
||||
|
||||
// Restore tag before the profiler's stack walker will no longer see the
|
||||
// InvokeDartCode return address.
|
||||
__ LoadFromOffset(TMP, FP, NativeEntryInstr::kVMTagOffsetFromFp);
|
||||
__ StoreToOffset(TMP, THR, compiler::target::Thread::vm_tag_offset());
|
||||
|
||||
__ LeaveDartFrame();
|
||||
|
||||
// The dummy return address is in LR, no need to pop it as on Intel.
|
||||
|
@ -1975,6 +1980,7 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
// Save the current VMTag on the stack.
|
||||
__ LoadFromOffset(R0, THR, compiler::target::Thread::vm_tag_offset());
|
||||
__ Push(R0);
|
||||
ASSERT(kVMTagOffsetFromFp == 5 * compiler::target::kWordSize);
|
||||
|
||||
// Save top resource.
|
||||
const intptr_t top_resource_offset =
|
||||
|
@ -1998,7 +2004,9 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
|
||||
// The callback trampoline (caller) has already left the safepoint for us.
|
||||
__ TransitionNativeToGenerated(/*scratch0=*/R0, /*scratch1=*/R1,
|
||||
/*exit_safepoint=*/false);
|
||||
/*exit_safepoint=*/false,
|
||||
/*ignore_unwind_in_progress=*/false,
|
||||
/*set_tag=*/false);
|
||||
|
||||
// Now that the safepoint has ended, we can touch Dart objects without
|
||||
// handles.
|
||||
|
@ -2039,6 +2047,11 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
});
|
||||
|
||||
FunctionEntryInstr::EmitNativeCode(compiler);
|
||||
|
||||
// Delay setting the tag until the profiler's stack walker will see the
|
||||
// InvokeDartCode return address.
|
||||
__ LoadImmediate(TMP, compiler::target::Thread::vm_tag_dart_id());
|
||||
__ StoreToOffset(TMP, THR, compiler::target::Thread::vm_tag_offset());
|
||||
}
|
||||
|
||||
#define R(r) (1 << r)
|
||||
|
|
|
@ -1706,6 +1706,11 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
EmitReturnMoves(compiler);
|
||||
|
||||
// Restore tag before the profiler's stack walker will no longer see the
|
||||
// InvokeDartCode return address.
|
||||
__ LoadFromOffset(TMP, FP, NativeEntryInstr::kVMTagOffsetFromFp);
|
||||
__ StoreToOffset(TMP, THR, compiler::target::Thread::vm_tag_offset());
|
||||
|
||||
__ LeaveDartFrame();
|
||||
|
||||
// The dummy return address is in LR, no need to pop it as on Intel.
|
||||
|
@ -1794,6 +1799,7 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
// Save the top resource.
|
||||
__ LoadFromOffset(R0, THR, compiler::target::Thread::top_resource_offset());
|
||||
__ PushPair(R0, TMP);
|
||||
ASSERT(kVMTagOffsetFromFp == 5 * compiler::target::kWordSize);
|
||||
|
||||
__ StoreToOffset(ZR, THR, compiler::target::Thread::top_resource_offset());
|
||||
|
||||
|
@ -1812,7 +1818,9 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
__ EmitEntryFrameVerification();
|
||||
|
||||
// The callback trampoline (caller) has already left the safepoint for us.
|
||||
__ TransitionNativeToGenerated(R0, /*exit_safepoint=*/false);
|
||||
__ TransitionNativeToGenerated(R0, /*exit_safepoint=*/false,
|
||||
/*ignore_unwind_in_progress=*/false,
|
||||
/*set_tag=*/false);
|
||||
|
||||
// Now that the safepoint has ended, we can touch Dart objects without
|
||||
// handles.
|
||||
|
@ -1857,6 +1865,11 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
});
|
||||
|
||||
FunctionEntryInstr::EmitNativeCode(compiler);
|
||||
|
||||
// Delay setting the tag until the profiler's stack walker will see the
|
||||
// InvokeDartCode return address.
|
||||
__ LoadImmediate(TMP, compiler::target::Thread::vm_tag_dart_id());
|
||||
__ StoreToOffset(TMP, THR, compiler::target::Thread::vm_tag_offset());
|
||||
}
|
||||
|
||||
#define R(r) (1 << r)
|
||||
|
|
|
@ -407,8 +407,6 @@ void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
return_in_st0 = true;
|
||||
}
|
||||
|
||||
__ LeaveDartFrame();
|
||||
|
||||
// EDI is the only sane choice for a temporary register here because:
|
||||
//
|
||||
// EDX is used for large return values.
|
||||
|
@ -416,6 +414,13 @@ void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
// Could be EBX or ECX, but that would make code below confusing.
|
||||
const Register tmp = EDI;
|
||||
|
||||
// Restore tag before the profiler's stack walker will no longer see the
|
||||
// InvokeDartCode return address.
|
||||
__ movl(tmp, compiler::Address(EBP, NativeEntryInstr::kVMTagOffsetFromFp));
|
||||
__ movl(compiler::Assembler::VMTagAddress(), tmp);
|
||||
|
||||
__ LeaveDartFrame();
|
||||
|
||||
// Pop dummy return address.
|
||||
__ popl(tmp);
|
||||
|
||||
|
@ -1400,6 +1405,7 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
// Save the current VMTag on the stack.
|
||||
__ movl(ECX, compiler::Assembler::VMTagAddress());
|
||||
__ pushl(ECX);
|
||||
ASSERT(kVMTagOffsetFromFp == 5 * compiler::target::kWordSize);
|
||||
|
||||
// Save top resource.
|
||||
__ pushl(
|
||||
|
@ -1420,7 +1426,9 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
__ EmitEntryFrameVerification();
|
||||
|
||||
// The callback trampoline (caller) has already left the safepoint for us.
|
||||
__ TransitionNativeToGenerated(EAX, /*exit_safepoint=*/false);
|
||||
__ TransitionNativeToGenerated(EAX, /*exit_safepoint=*/false,
|
||||
/*ignore_unwind_in_progress=*/false,
|
||||
/*set_tag=*/false);
|
||||
|
||||
// Now that the safepoint has ended, we can hold Dart objects with bare hands.
|
||||
|
||||
|
@ -1458,6 +1466,11 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
|
||||
// Continue with Dart frame setup.
|
||||
FunctionEntryInstr::EmitNativeCode(compiler);
|
||||
|
||||
// Delay setting the tag until the profiler's stack walker will see the
|
||||
// InvokeDartCode return address.
|
||||
__ movl(compiler::Assembler::VMTagAddress(),
|
||||
compiler::Immediate(compiler::target::Thread::vm_tag_dart_id()));
|
||||
}
|
||||
|
||||
#define R(r) (1 << r)
|
||||
|
|
|
@ -1806,6 +1806,11 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
EmitReturnMoves(compiler);
|
||||
|
||||
// Restore tag before the profiler's stack walker will no longer see the
|
||||
// InvokeDartCode return address.
|
||||
__ LoadFromOffset(TMP, FP, NativeEntryInstr::kVMTagOffsetFromFp);
|
||||
__ StoreToOffset(TMP, THR, compiler::target::Thread::vm_tag_offset());
|
||||
|
||||
__ LeaveDartFrame();
|
||||
|
||||
// The dummy return address is in RA, no need to pop it as on Intel.
|
||||
|
@ -1881,6 +1886,7 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
// Save the top resource.
|
||||
__ LoadFromOffset(A0, THR, compiler::target::Thread::top_resource_offset());
|
||||
__ PushRegisterPair(A0, TMP);
|
||||
ASSERT(kVMTagOffsetFromFp == 5 * compiler::target::kWordSize);
|
||||
|
||||
__ StoreToOffset(ZR, THR, compiler::target::Thread::top_resource_offset());
|
||||
|
||||
|
@ -1899,7 +1905,9 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
__ EmitEntryFrameVerification();
|
||||
|
||||
// The callback trampoline (caller) has already left the safepoint for us.
|
||||
__ TransitionNativeToGenerated(A0, /*exit_safepoint=*/false);
|
||||
__ TransitionNativeToGenerated(A0, /*exit_safepoint=*/false,
|
||||
/*ignore_unwind_in_progress=*/false,
|
||||
/*set_tag=*/false);
|
||||
|
||||
// Now that the safepoint has ended, we can touch Dart objects without
|
||||
// handles.
|
||||
|
@ -1941,6 +1949,11 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
__ LoadFieldFromOffset(RA, RA, compiler::target::Code::entry_point_offset());
|
||||
|
||||
FunctionEntryInstr::EmitNativeCode(compiler);
|
||||
|
||||
// Delay setting the tag until the profiler's stack walker will see the
|
||||
// InvokeDartCode return address.
|
||||
__ LoadImmediate(TMP, compiler::target::Thread::vm_tag_dart_id());
|
||||
__ StoreToOffset(TMP, THR, compiler::target::Thread::vm_tag_offset());
|
||||
}
|
||||
|
||||
#define R(r) (1 << r)
|
||||
|
|
|
@ -546,6 +546,11 @@ static const RegisterSet kCalleeSaveRegistersSet(
|
|||
void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
EmitReturnMoves(compiler);
|
||||
|
||||
// Restore tag before the profiler's stack walker will no longer see the
|
||||
// InvokeDartCode return address.
|
||||
__ movq(TMP, compiler::Address(RBP, NativeEntryInstr::kVMTagOffsetFromFp));
|
||||
__ movq(compiler::Assembler::VMTagAddress(), TMP);
|
||||
|
||||
__ LeaveDartFrame();
|
||||
|
||||
// Pop dummy return address.
|
||||
|
@ -1627,6 +1632,7 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
// Save the current VMTag on the stack.
|
||||
__ movq(RAX, compiler::Assembler::VMTagAddress());
|
||||
__ pushq(RAX);
|
||||
ASSERT(kVMTagOffsetFromFp == 5 * compiler::target::kWordSize);
|
||||
|
||||
// Save top resource.
|
||||
__ pushq(
|
||||
|
@ -1647,7 +1653,9 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
__ EmitEntryFrameVerification();
|
||||
|
||||
// The callback trampoline (caller) has already left the safepoint for us.
|
||||
__ TransitionNativeToGenerated(/*exit_safepoint=*/false);
|
||||
__ TransitionNativeToGenerated(/*exit_safepoint=*/false,
|
||||
/*ignore_unwind_in_progress=*/false,
|
||||
/*set_tag=*/false);
|
||||
|
||||
// Load the code object.
|
||||
const Function& target_function = marshaller_.dart_signature();
|
||||
|
@ -1694,6 +1702,11 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
|
||||
// Continue with Dart frame setup.
|
||||
FunctionEntryInstr::EmitNativeCode(compiler);
|
||||
|
||||
// Delay setting the tag until the profiler's stack walker will see the
|
||||
// InvokeDartCode return address.
|
||||
__ movq(compiler::Assembler::VMTagAddress(),
|
||||
compiler::Immediate(compiler::target::Thread::vm_tag_dart_id()));
|
||||
}
|
||||
|
||||
#define R(r) (1 << r)
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
// VMOptions=--use-slow-path --stacktrace-every=100
|
||||
// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
|
||||
// VMOptions=--test_il_serialization
|
||||
// VMOptions=--profiler
|
||||
// VMOptions=--profiler --profile_vm=true
|
||||
// VMOptions=--profiler --profile_vm=false
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
import 'dart:async';
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
// VMOptions=--use-slow-path --stacktrace-every=100
|
||||
// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
|
||||
// VMOptions=--test_il_serialization
|
||||
// VMOptions=--profiler
|
||||
// VMOptions=--profiler --profile_vm=true
|
||||
// VMOptions=--profiler --profile_vm=false
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
import 'dart:async';
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
// VMOptions=--use-slow-path --stacktrace-every=100
|
||||
// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
|
||||
// VMOptions=--test_il_serialization
|
||||
// VMOptions=--profiler
|
||||
// VMOptions=--profiler --profile_vm=true
|
||||
// VMOptions=--profiler --profile_vm=false
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
import 'dart:ffi';
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
// VMOptions=--use-slow-path --stacktrace-every=100
|
||||
// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
|
||||
// VMOptions=--test_il_serialization
|
||||
// VMOptions=--profiler
|
||||
// VMOptions=--profiler --profile_vm=true
|
||||
// VMOptions=--profiler --profile_vm=false
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
import 'dart:async';
|
||||
|
|
Loading…
Reference in a new issue