[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:
Ryan Macnak 2024-05-28 21:16:41 +00:00 committed by Commit Queue
parent 761b4288a1
commit 1a9acb9171
20 changed files with 123 additions and 29 deletions

View file

@ -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());

View file

@ -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,

View file

@ -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());

View file

@ -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);

View file

@ -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()));

View file

@ -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);

View file

@ -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()));

View file

@ -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);

View file

@ -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()));

View file

@ -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, ...) \

View file

@ -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,

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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';

View file

@ -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';

View file

@ -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';

View file

@ -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';