[vm] Record stack for JIT TSAN.

The main interesting thing is that deopt can expand one optimized frame to multiple unoptimized frames, so deopt needs to call  __tsan_func_entry enough times to rebalance the stack.

Also fixes stack balance for throw error slow paths for AOT.

TEST=tsan
Bug: https://github.com/dart-lang/sdk/issues/61352
Change-Id: I3fdc8481bc8db7a3aec0fa1938ac3e0e96ac13a5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/452943
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Ryan Macnak
2025-10-20 10:49:20 -07:00
committed by Commit Queue
parent 487e68e163
commit 0a1678c06c
27 changed files with 318 additions and 196 deletions
+2
View File
@@ -71,4 +71,6 @@ extern "C" void __tsan_func_exit();
#define DO_IF_NOT_TSAN(CODE) CODE
#endif
constexpr uintptr_t kExternalPCBit = 1ULL << 60;
#endif // RUNTIME_PLATFORM_THREAD_SANITIZER_H_
@@ -18,7 +18,7 @@ noopt() {}
@pragma("vm:never-inline")
dataRaceFromMain() {
final localBox = box;
for (var i = 0; i < 1000000; i++) {
for (var i = 0; i < 10000000; i++) {
localBox[0] += 1;
noopt();
}
@@ -27,7 +27,7 @@ dataRaceFromMain() {
@pragma("vm:never-inline")
dataRaceFromChild() {
final localBox = box;
for (var i = 0; i < 1000000; i++) {
for (var i = 0; i < 10000000; i++) {
localBox[0] += 1;
noopt();
}
+3 -3
View File
@@ -175,6 +175,9 @@ dart/unobfuscated_static_symbols_test: Pass, Slow # Test includes creating sever
dart/use_strip_flag_test: Pass, Slow # This test can take a longer time to complete.
dart/v8_snapshot_profile_writer_test: SkipSlow
[ $sanitizer != tsan ]
dart/tsan/*: SkipByDesign
[ $system == android ]
dart/isolates/dart_api_create_lightweight_isolate_test: SkipByDesign # On android this test does not work due to not being able to identify library uri.
dart/sdk_hash_test: SkipByDesign # The test doesn't know location of cross-platform gen_snapshot
@@ -403,9 +406,6 @@ dart/finalizer/finalizer_isolate_groups_run_gc_test: SkipByDesign # uses spawnUr
dart/isolates/send_object_to_spawn_uri_isolate_test: SkipByDesign # uses spawnUri
dart/issue32950_test: SkipByDesign # uses spawnUri.
[ $runtime != dart_precompiled || $sanitizer != tsan ]
dart/tsan/*: SkipByDesign
[ $runtime != dart_precompiled || $sanitizer != msan && $sanitizer != tsan ]
dart/sanitizer_compatibility_test: SkipByDesign
@@ -354,7 +354,6 @@ void Assembler::TsanStoreRelease(Register src,
}
void Assembler::TsanFuncEntry(bool preserve_registers) {
Comment("TsanFuncEntry");
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
ldr(R0, Address(FP, target::frame_layout.saved_caller_fp_from_fp *
target::kWordSize));
@@ -364,7 +363,6 @@ void Assembler::TsanFuncEntry(bool preserve_registers) {
}
void Assembler::TsanFuncExit(bool preserve_registers) {
Comment("TsanFuncExit");
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
rt.Call(kTsanFuncExitRuntimeEntry, /*argument_count=*/0);
}
@@ -1801,16 +1799,14 @@ void Assembler::CallRuntime(const RuntimeEntry& entry,
ASSERT(!entry.is_leaf());
// Argument count is not checked here, but in the runtime entry for a more
// informative error message.
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
tsan_enter_exit) {
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
TsanFuncEntry(/*preserve_registers=*/false);
}
ldr(R5, compiler::Address(THR, entry.OffsetFromThread()));
LoadImmediate(R4, argument_count);
Comment("Runtime call: %s", entry.name());
Call(Address(THR, target::Thread::call_to_runtime_entry_point_offset()));
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
tsan_enter_exit) {
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
TsanFuncExit(/*preserve_registers=*/false);
}
}
@@ -3173,7 +3173,6 @@ void Assembler::TsanStoreRelease(Register src,
}
void Assembler::TsanFuncEntry(bool preserve_registers) {
Comment("TsanFuncEntry");
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
lx(A0, Address(FP, target::frame_layout.saved_caller_fp_from_fp *
target::kWordSize));
@@ -3183,7 +3182,6 @@ void Assembler::TsanFuncEntry(bool preserve_registers) {
}
void Assembler::TsanFuncExit(bool preserve_registers) {
Comment("TsanFuncExit");
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
rt.Call(kTsanFuncExitRuntimeEntry, /*argument_count=*/0);
}
@@ -4825,16 +4823,14 @@ void Assembler::CallRuntime(const RuntimeEntry& entry,
ASSERT(!entry.is_leaf());
// Argument count is not checked here, but in the runtime entry for a more
// informative error message.
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
tsan_enter_exit) {
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
TsanFuncEntry(/*preserve_registers=*/false);
}
lx(T5, compiler::Address(THR, entry.OffsetFromThread()));
li(T4, argument_count);
Comment("Runtime call: %s", entry.name());
Call(Address(THR, target::Thread::call_to_runtime_entry_point_offset()));
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
tsan_enter_exit) {
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
TsanFuncExit(/*preserve_registers=*/false);
}
}
@@ -2063,16 +2063,14 @@ void Assembler::CallRuntime(const RuntimeEntry& entry,
ASSERT(!entry.is_leaf());
// Argument count is not checked here, but in the runtime entry for a more
// informative error message.
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
tsan_enter_exit) {
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
TsanFuncEntry(/*preserve_registers=*/false);
}
movq(RBX, compiler::Address(THR, entry.OffsetFromThread()));
LoadImmediate(R10, compiler::Immediate(argument_count));
Comment("Runtime call: %s", entry.name());
call(Address(THR, target::Thread::call_to_runtime_entry_point_offset()));
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
tsan_enter_exit) {
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
TsanFuncExit(/*preserve_registers=*/false);
}
}
@@ -2208,7 +2206,6 @@ void Assembler::TsanStoreRelease(Register src, Address addr, OperandSize size) {
}
void Assembler::TsanFuncEntry(bool preserve_registers) {
Comment("TsanFuncEntry");
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
movq(CallingConventions::kArg1Reg,
Address(RBP, target::frame_layout.saved_caller_fp_from_fp *
@@ -2221,7 +2218,6 @@ void Assembler::TsanFuncEntry(bool preserve_registers) {
}
void Assembler::TsanFuncExit(bool preserve_registers) {
Comment("TsanFuncExit");
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
rt.Call(kTsanFuncExitRuntimeEntry, /*argument_count=*/0);
}
+16 -15
View File
@@ -2716,16 +2716,19 @@ void FlowGraph::AddTsanInstrumentation() {
store->index_scale(), store->class_id(), store->source());
InsertBefore(store, tsan_write, /*env=*/nullptr, kEffect);
needs_entry_exit = true;
} else if (current->CanCallDart() || current->MayThrow()) {
} else if (current->CanCallDart() || current->MayThrow() ||
current->IsBox() || current->IsBoxLanes()) {
// Box instructions enter the runtime on allocation slow path, and might
// throw OutOfMemory.
needs_entry_exit = true;
} else if (current->CanDeoptimize() && !FLAG_precompiled_mode) {
// TSAN stack rebalancing at deopt after DLRT_DeoptimizeFillFrame
// assumes the optimized code has a frame.
needs_entry_exit = true;
}
}
}
// TSAN can't symbolize JIT functions anyway, so don't bother with this
// overhead.
if (!FLAG_precompiled_mode) return;
if (!needs_entry_exit) return;
for (BlockIterator block_it = reverse_postorder_iterator(); !block_it.Done();
@@ -2736,16 +2739,14 @@ void FlowGraph::AddTsanInstrumentation() {
TsanFuncEntryExitInstr::kEntry, block->source());
InsertAfter(block, tsan_entry, /*env=*/nullptr, kEffect);
}
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
Instruction* current = it.Current();
if ((current->IsDartReturn() &&
!parsed_function_.function().IsAsyncFunction() &&
!parsed_function_.function().IsAsyncGenerator()) ||
current->IsTailCall()) {
auto* tsan_exit = new (Z) TsanFuncEntryExitInstr(
TsanFuncEntryExitInstr::kExit, current->source());
InsertBefore(current, tsan_exit, /*env=*/nullptr, kEffect);
}
Instruction* last = block->last_instruction();
if ((last->IsDartReturn() &&
!parsed_function_.function().IsAsyncFunction() &&
!parsed_function_.function().IsAsyncGenerator()) ||
last->IsTailCall() || last->IsNativeReturn()) {
auto* tsan_exit = new (Z)
TsanFuncEntryExitInstr(TsanFuncEntryExitInstr::kExit, last->source());
InsertBefore(last, tsan_exit, /*env=*/nullptr, kEffect);
}
}
}
@@ -3087,7 +3087,6 @@ void ThrowErrorSlowPathCode::EmitNativeCode(FlowGraphCompiler* compiler) {
EmitCodeAtSlowPathEntry(compiler);
LocationSummary* locs = instruction()->locs();
const bool has_frame = compiler->flow_graph().graph_entry()->NeedsFrame();
bool need_tsan_exit = false;
if (use_shared_stub) {
if (!has_frame) {
#if !defined(TARGET_ARCH_IA32)
@@ -3095,7 +3094,7 @@ void ThrowErrorSlowPathCode::EmitNativeCode(FlowGraphCompiler* compiler) {
__ set_constant_pool_allowed(false);
#endif
__ EnterDartFrame(0);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
}
@@ -3111,14 +3110,9 @@ void ThrowErrorSlowPathCode::EmitNativeCode(FlowGraphCompiler* compiler) {
// Save registers as they are needed for lazy deopt / exception handling.
compiler->SaveLiveRegisters(locs);
PushArgumentsForRuntimeCall(compiler);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
need_tsan_exit = true;
__ TsanFuncEntry(/*preserve_registers=*/false);
}
// We need to emit TsanFuncEntry/Exit separately so the pc descriptors, etc
// are recordered for the call's return address.
bool tsan_enter_exit = false;
__ CallRuntime(runtime_entry_, num_args, tsan_enter_exit);
__ CallRuntime(runtime_entry_, num_args, /*tsan_enter_exit=*/false);
}
const intptr_t deopt_id = instruction()->deopt_id();
compiler->AddCurrentDescriptor(UntaggedPcDescriptors::kOther, deopt_id,
@@ -3145,8 +3139,6 @@ void ThrowErrorSlowPathCode::EmitNativeCode(FlowGraphCompiler* compiler) {
}
if (!use_shared_stub) {
__ Breakpoint();
} else if (need_tsan_exit) {
__ TsanFuncExit(/*preserve_registers=*/false);
}
}
@@ -258,6 +258,16 @@ void FlowGraphCompiler::EmitFrameEntry() {
} else if (FLAG_precompiled_mode) {
assembler()->set_constant_pool_allowed(true);
}
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
bool uses_args_desc = parsed_function().has_arg_desc_var();
if (uses_args_desc) {
__ MoveRegister(CALLEE_SAVED_TEMP, ARGS_DESC_REG);
}
__ TsanFuncEntry(/*preserve_registers=*/false);
if (uses_args_desc) {
__ MoveRegister(ARGS_DESC_REG, CALLEE_SAVED_TEMP);
}
}
}
const InstructionSource& PrologueSource() {
@@ -336,6 +346,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
ASSERT(!stub.IsNull());
if (CanPcRelativeCall(stub)) {
if (flow_graph().graph_entry()->NeedsFrame()) {
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
}
__ GenerateUnRelocatedPcRelativeTailCall();
@@ -346,6 +359,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
} else {
__ LoadObject(CODE_REG, stub);
if (flow_graph().graph_entry()->NeedsFrame()) {
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
}
__ ldr(TMP, compiler::FieldAddress(
@@ -248,6 +248,16 @@ void FlowGraphCompiler::EmitFrameEntry() {
} else if (FLAG_precompiled_mode) {
assembler()->set_constant_pool_allowed(true);
}
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
bool uses_args_desc = parsed_function().has_arg_desc_var();
if (uses_args_desc) {
__ MoveRegister(CALLEE_SAVED_TEMP, ARGS_DESC_REG);
}
__ TsanFuncEntry(/*preserve_registers=*/false);
if (uses_args_desc) {
__ MoveRegister(ARGS_DESC_REG, CALLEE_SAVED_TEMP);
}
}
}
const InstructionSource& PrologueSource() {
@@ -332,6 +342,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
ASSERT(!stub.IsNull());
if (CanPcRelativeCall(stub)) {
if (flow_graph().graph_entry()->NeedsFrame()) {
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
}
__ GenerateUnRelocatedPcRelativeTailCall();
@@ -342,6 +355,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
} else {
__ LoadObject(CODE_REG, stub);
if (flow_graph().graph_entry()->NeedsFrame()) {
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
}
__ lx(TMP, compiler::FieldAddress(
@@ -261,6 +261,16 @@ void FlowGraphCompiler::EmitFrameEntry() {
__ Comment("Enter frame");
__ EnterDartFrame(StackSize() * kWordSize);
}
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
bool uses_args_desc = parsed_function().has_arg_desc_var();
if (uses_args_desc) {
__ MoveRegister(CALLEE_SAVED_TEMP, ARGS_DESC_REG);
}
__ TsanFuncEntry(/*preserve_registers=*/false);
if (uses_args_desc) {
__ MoveRegister(ARGS_DESC_REG, CALLEE_SAVED_TEMP);
}
}
}
const InstructionSource& PrologueSource() {
@@ -340,6 +350,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
ASSERT(!stub.IsNull());
if (CanPcRelativeCall(stub)) {
if (flow_graph().graph_entry()->NeedsFrame()) {
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
}
__ GenerateUnRelocatedPcRelativeTailCall();
@@ -350,6 +363,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
} else {
__ LoadObject(CODE_REG, stub);
if (flow_graph().graph_entry()->NeedsFrame()) {
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
}
__ jmp(compiler::FieldAddress(
+12 -6
View File
@@ -540,6 +540,12 @@ void DartReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
return;
}
if (FLAG_target_thread_sanitizer && !compiler->is_optimizing()) {
RELEASE_ASSERT(locs()->in(0).IsRegister());
__ MoveRegister(CALLEE_SAVED_TEMP, locs()->in(0).reg());
__ TsanFuncExit(/*preserve_registers=*/false);
__ MoveRegister(locs()->in(0).reg(), CALLEE_SAVED_TEMP);
}
#if defined(DEBUG)
compiler::Label stack_ok;
__ Comment("Stack Check");
@@ -2393,7 +2399,7 @@ void GuardFieldClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ PushPair(value_reg, field_reg);
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
__ Drop(2); // Drop the field and the value.
} else {
__ b(fail);
@@ -2498,7 +2504,7 @@ void GuardFieldLengthInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ PushPair(value_reg, field_reg);
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
__ Drop(2); // Drop the field and the value.
} else {
__ b(deopt, NE);
@@ -2928,7 +2934,7 @@ class CheckStackOverflowSlowPath
ASSERT(__ constant_pool_allowed());
__ set_constant_pool_allowed(false);
__ EnterDartFrame(0);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
}
@@ -2955,7 +2961,7 @@ class CheckStackOverflowSlowPath
instruction()->deopt_id(),
instruction()->source());
if (!has_frame) {
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
@@ -3788,7 +3794,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(__ constant_pool_allowed());
__ set_constant_pool_allowed(false);
__ EnterDartFrame(0);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
}
@@ -3805,7 +3811,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
locs(), DeoptId::kNone, extended_env);
if (!has_frame) {
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
+12 -6
View File
@@ -601,6 +601,12 @@ void DartReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
return;
}
if (FLAG_target_thread_sanitizer && !compiler->is_optimizing()) {
RELEASE_ASSERT(locs()->in(0).IsRegister());
__ MoveRegister(CALLEE_SAVED_TEMP, locs()->in(0).reg());
__ TsanFuncExit(/*preserve_registers=*/false);
__ MoveRegister(locs()->in(0).reg(), CALLEE_SAVED_TEMP);
}
const intptr_t fp_sp_dist =
(compiler::target::frame_layout.first_local_from_fp + 1 -
compiler->StackSize()) *
@@ -2592,7 +2598,7 @@ void GuardFieldClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ PushRegisterPair(value_reg, field_reg);
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
__ Drop(2); // Drop the field and the value.
} else {
__ j(fail);
@@ -2696,7 +2702,7 @@ void GuardFieldLengthInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ PushRegisterPair(value_reg, field_reg);
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
__ Drop(2); // Drop the field and the value.
} else {
__ BranchIf(NE, deopt);
@@ -3124,7 +3130,7 @@ class CheckStackOverflowSlowPath
ASSERT(__ constant_pool_allowed());
__ set_constant_pool_allowed(false);
__ EnterDartFrame(0);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
}
@@ -3151,7 +3157,7 @@ class CheckStackOverflowSlowPath
instruction()->deopt_id(),
instruction()->source());
if (!has_frame) {
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
@@ -4155,7 +4161,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(__ constant_pool_allowed());
__ set_constant_pool_allowed(false);
__ EnterDartFrame(0);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
}
@@ -4172,7 +4178,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
locs(), DeoptId::kNone, extended_env);
if (!has_frame) {
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
+13 -7
View File
@@ -442,6 +442,12 @@ void DartReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
return;
}
if (FLAG_target_thread_sanitizer && !compiler->is_optimizing()) {
RELEASE_ASSERT(locs()->in(0).IsRegister());
__ MoveRegister(CALLEE_SAVED_TEMP, locs()->in(0).reg());
__ TsanFuncExit(/*preserve_registers=*/false);
__ MoveRegister(locs()->in(0).reg(), CALLEE_SAVED_TEMP);
}
#if defined(DEBUG)
__ Comment("Stack Check");
compiler::Label done;
@@ -2267,7 +2273,7 @@ void GuardFieldClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ pushq(field_reg);
__ pushq(value_reg);
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
__ Drop(2); // Drop the field and the value.
} else {
__ jmp(fail);
@@ -2373,7 +2379,7 @@ void GuardFieldLengthInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ pushq(field_reg);
__ pushq(value_reg);
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
__ Drop(2); // Drop the field and the value.
} else {
__ j(NOT_EQUAL, deopt);
@@ -2471,7 +2477,7 @@ void GuardFieldTypeInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ PushObject(original);
__ pushq(value_reg);
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
__ Drop(2);
}
@@ -2893,7 +2899,7 @@ class CheckStackOverflowSlowPath
ASSERT(__ constant_pool_allowed());
__ set_constant_pool_allowed(false);
__ EnterDartFrame(0);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
}
@@ -2907,7 +2913,7 @@ class CheckStackOverflowSlowPath
instruction()->deopt_id(),
instruction()->source());
if (!has_frame) {
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
@@ -4039,7 +4045,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(__ constant_pool_allowed());
__ set_constant_pool_allowed(false);
__ EnterDartFrame(0);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
}
@@ -4056,7 +4062,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
locs(), DeoptId::kNone, extended_env);
if (!has_frame) {
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
+15 -15
View File
@@ -123,11 +123,11 @@ void StubCodeCompiler::GenerateInitLateStaticFieldStub(bool is_final,
// Load a GC-safe value for the arguments descriptor (unused but tagged).
__ LoadImmediate(ARGS_DESC_REG, 0);
}
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
__ Call(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ MoveRegister(kResultReg, CallingConventions::kReturnReg);
@@ -239,11 +239,11 @@ void StubCodeCompiler::GenerateInitLateInstanceFieldStub(bool is_final) {
__ LoadImmediate(ARGS_DESC_REG, 0);
#endif // defined(DART_DYNAMIC_MODULES)
}
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
__ Call(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ Drop(1); // Drop argument.
@@ -1880,14 +1880,11 @@ static void CallDartCoreLibraryFunction(
intptr_t entry_point_offset_in_thread,
intptr_t function_offset_in_object_store,
bool uses_args_desc = false) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
if (FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
__ Call(Address(THR, entry_point_offset_in_thread));
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
} else {
__ LoadIsolateGroup(FUNCTION_REG);
__ LoadFromOffset(FUNCTION_REG, FUNCTION_REG,
@@ -1902,6 +1899,9 @@ static void CallDartCoreLibraryFunction(
}
__ Call(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
}
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
}
// Helper to generate allocation of _SuspendState instance.
@@ -2161,7 +2161,7 @@ void StubCodeCompiler::GenerateSuspendStub(
__ LeaveStubFrame();
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
#if !defined(TARGET_ARCH_X64) && !defined(TARGET_ARCH_IA32)
@@ -2339,7 +2339,7 @@ void StubCodeCompiler::GenerateResumeStub() {
// ... [SuspendState] [value] [exception] [stackTrace] [ReturnAddress]
__ EnterDartFrame(0);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
@@ -2554,7 +2554,7 @@ void StubCodeCompiler::GenerateReturnStub(
__ Bind(&okay);
}
#endif
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
@@ -2609,7 +2609,7 @@ void StubCodeCompiler::GenerateAsyncExceptionHandlerStub() {
__ CompareObject(kSuspendState, NullObject());
__ BranchIf(EQUAL, &rethrow_exception);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
@@ -2633,7 +2633,7 @@ void StubCodeCompiler::GenerateAsyncExceptionHandlerStub() {
#endif
__ Comment("Rethrow exception");
__ Bind(&rethrow_exception);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
@@ -1194,6 +1194,16 @@ static void GenerateDeoptimizationSequence(Assembler* assembler,
/*preserve_registers=*/false);
rt.Call(kDeoptimizeFillFrameRuntimeEntry, 1);
}
if (FLAG_target_thread_sanitizer) {
__ mov(CALLEE_SAVED_TEMP, R0); // Frames created
__ TsanFuncExit(/*preserve_registers=*/false); // Optimized frame.
Label loop;
__ Bind(&loop);
__ LoadImmediate(R0, 42);
__ TsanFuncEntry(/*preserve_registers=*/false); // Unoptimized frames.
__ sub(CALLEE_SAVED_TEMP, CALLEE_SAVED_TEMP, Operand(1));
__ cbnz(&loop, CALLEE_SAVED_TEMP);
}
if (kind == kLazyDeoptFromReturn) {
// Restore result into R1.
__ LoadFromOffset(
@@ -3464,7 +3474,7 @@ void StubCodeCompiler::GenerateJumpToFrameStub() {
ASSERT(kStackTraceObjectReg == R1);
__ set_lr_state(compiler::LRState::Clobbered());
__ mov(THR, R3);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
Label again, done;
__ mov(SP, CSP);
__ ldr(CALLEE_SAVED_TEMP,
@@ -1038,6 +1038,16 @@ static void GenerateDeoptimizationSequence(Assembler* assembler,
/*preserve_registers=*/false);
rt.Call(kDeoptimizeFillFrameRuntimeEntry, 1);
}
if (FLAG_target_thread_sanitizer) {
__ mv(CALLEE_SAVED_TEMP, A0); // Frames created
__ TsanFuncExit(/*preserve_registers=*/false); // Optimized frame.
Label loop;
__ Bind(&loop);
__ li(A0, 42);
__ TsanFuncEntry(/*preserve_registers=*/false); // Unoptimized frames.
__ subi(CALLEE_SAVED_TEMP, CALLEE_SAVED_TEMP, 1);
__ bnez(CALLEE_SAVED_TEMP, &loop);
}
if (kind == kLazyDeoptFromReturn) {
// Restore result into T1.
__ LoadFromOffset(
@@ -2962,7 +2972,7 @@ void StubCodeCompiler::GenerateJumpToFrameStub() {
ASSERT(kExceptionObjectReg == A0);
ASSERT(kStackTraceObjectReg == A1);
__ mv(THR, A3);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
Label again, done;
__ lx(CALLEE_SAVED_TEMP,
Address(THR, target::Thread::top_exit_frame_info_offset()));
+11 -1
View File
@@ -1190,6 +1190,16 @@ static void GenerateDeoptimizationSequence(Assembler* assembler,
/*preserve_registers=*/false);
rt.Call(kDeoptimizeFillFrameRuntimeEntry, 1);
}
if (FLAG_target_thread_sanitizer) {
__ movq(CALLEE_SAVED_TEMP, RAX); // Frames created
__ TsanFuncExit(/*preserve_registers=*/false); // Optimized frame.
Label loop;
__ Bind(&loop);
__ movq(CallingConventions::kArg1Reg, Immediate(42));
__ TsanFuncEntry(/*preserve_registers=*/false); // Unoptimized frames.
__ subq(CALLEE_SAVED_TEMP, Immediate(1));
__ j(NOT_ZERO, &loop);
}
if (kind == kLazyDeoptFromReturn) {
// Restore result into RBX.
__ movq(RBX, Address(RBP, target::frame_layout.first_local_from_fp *
@@ -3443,7 +3453,7 @@ void StubCodeCompiler::GenerateGetCStackPointerStub() {
// No Result.
void StubCodeCompiler::GenerateJumpToFrameStub() {
__ movq(THR, CallingConventions::kArg4Reg);
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
if (FLAG_target_thread_sanitizer) {
Label again, done;
__ movq(CALLEE_SAVED_TEMP,
Address(THR, target::Thread::top_exit_frame_info_offset()));
+7 -1
View File
@@ -255,7 +255,7 @@ static bool IsObjectInstruction(DeoptInstr::Kind kind) {
return false;
}
void DeoptContext::FillDestFrame() {
intptr_t DeoptContext::FillDestFrame() {
const Code& code = Code::Handle(code_);
const TypedData& deopt_info = TypedData::Handle(deopt_info_);
@@ -294,6 +294,7 @@ void DeoptContext::FillDestFrame() {
}
// Populate stack frames.
intptr_t frame_count = 0;
for (intptr_t to_index = frame_size - 1, from_index = len - 1; to_index >= 0;
to_index--, from_index--) {
intptr_t* to_addr = GetDestFrameAddressAt(to_index);
@@ -303,6 +304,9 @@ void DeoptContext::FillDestFrame() {
} else {
*reinterpret_cast<ObjectPtr*>(to_addr) = Object::null();
}
if (instr->kind() == DeoptInstr::kRetAddress) {
frame_count++;
}
}
if (FLAG_trace_deoptimization_verbose) {
@@ -312,6 +316,8 @@ void DeoptContext::FillDestFrame() {
deopt_instructions[i + (len - frame_size)]->ToCString());
}
}
return frame_count;
}
static void FillDeferredSlots(DeoptContext* deopt_context,
+2 -2
View File
@@ -144,8 +144,8 @@ class DeoptContext : public MallocAllocated {
TypedDataPtr deopt_info() const { return deopt_info_; }
// Fills the destination frame but defers materialization of
// objects.
void FillDestFrame();
// objects. Returns the number of unoptimized frames.
intptr_t FillDestFrame();
// Materializes all deferred objects. Returns the total number of
// artificial arguments used during deoptimization.
+6
View File
@@ -7975,6 +7975,9 @@ FutureOr<T?> bar<T>() { return null; }
static void GenerateInvokeInstantiateTAVStub(compiler::Assembler* assembler) {
__ EnterDartFrame(0);
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
// Load the arguments into the right stub calling convention registers.
const intptr_t uninstantiated_offset =
@@ -7996,6 +7999,9 @@ static void GenerateInvokeInstantiateTAVStub(compiler::Assembler* assembler) {
// Set the return from the stub.
__ MoveRegister(CallingConventions::kReturnReg,
InstantiationABI::kResultTypeArgumentsReg);
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
__ Ret();
}
+27 -16
View File
@@ -4474,7 +4474,7 @@ DEFINE_LEAF_RUNTIME_ENTRY(DeoptimizeCopyFrame, 2, DLRT_DeoptimizeCopyFrame);
// The stack has been adjusted to fit all values for unoptimized frame.
// Fill the unoptimized frame.
extern "C" void DLRT_DeoptimizeFillFrame(uword last_fp) {
extern "C" intptr_t DLRT_DeoptimizeFillFrame(uword last_fp) {
#if !defined(DART_PRECOMPILED_RUNTIME)
Thread* thread = Thread::Current();
StackZone zone(thread);
@@ -4504,10 +4504,15 @@ extern "C" void DLRT_DeoptimizeFillFrame(uword last_fp) {
#endif
deopt_context->set_dest_frame(caller_frame);
deopt_context->FillDestFrame();
intptr_t frame_count = deopt_context->FillDestFrame();
ASSERT(frame_count > 0);
if (FLAG_trace_deoptimization) {
THR_Print("Deopt created %" Pd " frames\n", frame_count);
}
return frame_count;
#else
UNREACHABLE();
return 0;
#endif // !DART_PRECOMPILED_RUNTIME
}
DEFINE_LEAF_RUNTIME_ENTRY(DeoptimizeFillFrame, 1, DLRT_DeoptimizeFillFrame);
@@ -5301,11 +5306,11 @@ extern "C" void __tsan_func_exit() {
#else
#define CASE(x) \
extern "C" NO_SANITIZE_THREAD DISABLE_SANITIZER_INSTRUMENTATION void \
jit_tsan_##x(void* addr) { \
dart_tsan_##x(void* addr) { \
__tsan_##x##_pc( \
addr, reinterpret_cast<void*>( \
reinterpret_cast<uintptr_t>(__builtin_return_address(0)) | \
(1ULL << 60))); \
kExternalPCBit)); \
}
CASE(read1)
@@ -5319,6 +5324,11 @@ CASE(write4)
CASE(write8)
CASE(write16)
#undef CASE
extern "C" NO_SANITIZE_THREAD DISABLE_SANITIZER_INSTRUMENTATION void
dart_tsan_func_entry(void* pc) {
__tsan_func_entry(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(pc) |
kExternalPCBit));
}
#endif
// These runtime entries are defined even when not using MSAN / TSAN to keep
@@ -5330,16 +5340,17 @@ DEFINE_LEAF_RUNTIME_ENTRY(TsanAtomic32Store, 3, __tsan_atomic32_store);
DEFINE_LEAF_RUNTIME_ENTRY(TsanAtomic64Load, 2, __tsan_atomic64_load);
DEFINE_LEAF_RUNTIME_ENTRY(TsanAtomic64Store, 3, __tsan_atomic64_store);
#if defined(USING_THREAD_SANITIZER) && !defined(DART_PRECOMPILED_RUNTIME)
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead1, 1, jit_tsan_read1);
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead2, 1, jit_tsan_read2);
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead4, 1, jit_tsan_read4);
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead8, 1, jit_tsan_read8);
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead16, 1, jit_tsan_read16);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite1, 1, jit_tsan_write1);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite2, 1, jit_tsan_write2);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite4, 1, jit_tsan_write4);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite8, 1, jit_tsan_write8);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite16, 1, jit_tsan_write16);
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead1, 1, dart_tsan_read1);
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead2, 1, dart_tsan_read2);
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead4, 1, dart_tsan_read4);
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead8, 1, dart_tsan_read8);
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead16, 1, dart_tsan_read16);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite1, 1, dart_tsan_write1);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite2, 1, dart_tsan_write2);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite4, 1, dart_tsan_write4);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite8, 1, dart_tsan_write8);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite16, 1, dart_tsan_write16);
DEFINE_LEAF_RUNTIME_ENTRY(TsanFuncEntry, 1, dart_tsan_func_entry);
#else
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead1, 1, __tsan_read1);
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead2, 1, __tsan_read2);
@@ -5351,8 +5362,8 @@ DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite2, 1, __tsan_write2);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite4, 1, __tsan_write4);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite8, 1, __tsan_write8);
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite16, 1, __tsan_write16);
#endif
DEFINE_LEAF_RUNTIME_ENTRY(TsanFuncEntry, 1, __tsan_func_entry);
#endif
DEFINE_LEAF_RUNTIME_ENTRY(TsanFuncExit, 0, __tsan_func_exit);
} // namespace dart
+1 -1
View File
@@ -95,7 +95,7 @@ namespace dart {
#define LEAF_RUNTIME_ENTRY_LIST(V) \
V(intptr_t, DeoptimizeCopyFrame, uword, uword) \
V(void, DeoptimizeFillFrame, uword) \
V(intptr_t, DeoptimizeFillFrame, uword) \
V(void, StoreBufferBlockProcess, Thread*) \
V(void, OldMarkingStackBlockProcess, Thread*) \
V(void, NewMarkingStackBlockProcess, Thread*) \
+3
View File
@@ -1925,6 +1925,9 @@ void Simulator::InterpretMISCMEM(Instr instr) {
switch (instr.funct3()) {
case FENCE:
memory_.FlushAll();
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wtsan"
#endif
std::atomic_thread_fence(std::memory_order_acq_rel);
break;
case FENCEI:
+98 -89
View File
@@ -4,6 +4,7 @@
#include "vm/tsan_symbolize.h"
#include "include/dart_api.h"
#include "platform/atomic.h"
#include "platform/thread_sanitizer.h"
#include "vm/code_descriptors.h"
@@ -36,86 +37,95 @@ static void WriteString(GrowableArray<uint8_t>& out, const char* str) {
}
void RegisterTsanSymbolize(const Code& code) {
if (!code.IsFunctionCode()) {
// TODO(rmacnak): Do something for stubs?
return;
}
const CodeSourceMap& map = CodeSourceMap::Handle(code.code_source_map());
RELEASE_ASSERT(!map.IsNull());
const Array& functions = Array::Handle(code.inlined_id_to_function());
Function& function = Function::Handle(code.function());
Script& script = Script::Handle(function.script());
String& url = String::Handle(script.url());
GrowableArray<Function*> function_stack(8);
GrowableArray<TokenPosition> token_positions(8);
function_stack.Add(&Function::Handle(function.ptr()));
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
GrowableArray<uint8_t> out(256);
out.Add(OP_PUSH_FUNCTION);
WriteString(out, function.QualifiedUserVisibleNameCString());
WriteString(out, url.ToCString());
NoSafepointScope no_safepoint;
ReadStream stream(map.Data(), map.Length());
while (stream.PendingBytes() > 0) {
int32_t arg;
switch (CodeSourceMapOps::Read(&stream, &arg)) {
case CodeSourceMapOps::kChangePosition: {
const TokenPosition& old_token = token_positions.Last();
token_positions.Last() = TokenPosition::Deserialize(
Utils::AddWithWrapAround(arg, old_token.Serialize()));
if (code.IsFunctionCode()) {
const CodeSourceMap& map = CodeSourceMap::Handle(code.code_source_map());
RELEASE_ASSERT(!map.IsNull());
const Array& functions = Array::Handle(code.inlined_id_to_function());
Function& function = Function::Handle(code.function());
Script& script = Script::Handle(function.script());
String& url = String::Handle(script.url());
GrowableArray<Function*> function_stack(8);
GrowableArray<TokenPosition> token_positions(8);
function_stack.Add(&Function::Handle(function.ptr()));
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
TokenPosition pos = token_positions.Last();
if (pos.IsNoSource()) {
pos = function_stack.Last()->token_pos();
out.Add(OP_PUSH_FUNCTION);
WriteString(out, function.QualifiedUserVisibleNameCString());
WriteString(out, url.ToCString());
NoSafepointScope no_safepoint;
ReadStream stream(map.Data(), map.Length());
while (stream.PendingBytes() > 0) {
int32_t arg;
switch (CodeSourceMapOps::Read(&stream, &arg)) {
case CodeSourceMapOps::kChangePosition: {
const TokenPosition& old_token = token_positions.Last();
token_positions.Last() = TokenPosition::Deserialize(
Utils::AddWithWrapAround(arg, old_token.Serialize()));
TokenPosition pos = token_positions.Last();
if (pos.IsNoSource()) {
pos = function_stack.Last()->token_pos();
}
intptr_t line = -1;
intptr_t column = -1;
script = function_stack.Last()->script();
script.GetTokenLocation(pos, &line, &column);
out.Add(OP_CHANGE_POSITION);
out.Add((line >> 0) & 0xFF);
out.Add((line >> 8) & 0xFF);
out.Add((column >> 0) & 0xFF);
out.Add((column >> 8) & 0xFF);
break;
}
case CodeSourceMapOps::kAdvancePC: {
out.Add(OP_ADVANCE_PC);
out.Add((arg >> 0) & 0xFF);
out.Add((arg >> 8) & 0xFF);
out.Add((arg >> 16) & 0xFF);
out.Add((arg >> 24) & 0xFF);
break;
}
case CodeSourceMapOps::kPushFunction: {
function ^= functions.At(arg);
function_stack.Add(&Function::Handle(function.ptr()));
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
intptr_t line = -1;
intptr_t column = -1;
script = function_stack.Last()->script();
script.GetTokenLocation(pos, &line, &column);
out.Add(OP_CHANGE_POSITION);
out.Add((line >> 0) & 0xFF);
out.Add((line >> 8) & 0xFF);
out.Add((column >> 0) & 0xFF);
out.Add((column >> 8) & 0xFF);
break;
out.Add(OP_PUSH_FUNCTION);
WriteString(out, function.QualifiedUserVisibleNameCString());
script = function_stack.Last()->script();
url = script.url();
WriteString(out, url.ToCString());
break;
}
case CodeSourceMapOps::kPopFunction: {
function_stack.RemoveLast();
token_positions.RemoveLast();
out.Add(OP_POP_FUNCTION);
break;
}
case CodeSourceMapOps::kNullCheck: {
break;
}
default:
UNREACHABLE();
}
case CodeSourceMapOps::kAdvancePC: {
out.Add(OP_ADVANCE_PC);
out.Add((arg >> 0) & 0xFF);
out.Add((arg >> 8) & 0xFF);
out.Add((arg >> 16) & 0xFF);
out.Add((arg >> 24) & 0xFF);
break;
}
case CodeSourceMapOps::kPushFunction: {
function ^= functions.At(arg);
function_stack.Add(&Function::Handle(function.ptr()));
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
out.Add(OP_PUSH_FUNCTION);
WriteString(out, function.QualifiedUserVisibleNameCString());
script = function_stack.Last()->script();
url = script.url();
WriteString(out, url.ToCString());
break;
}
case CodeSourceMapOps::kPopFunction: {
function_stack.RemoveLast();
token_positions.RemoveLast();
out.Add(OP_POP_FUNCTION);
break;
}
case CodeSourceMapOps::kNullCheck: {
break;
}
default:
UNREACHABLE();
}
} else {
out.Add(OP_PUSH_FUNCTION);
WriteString(out, code.Name());
WriteString(out, "stub");
int32_t arg = code.Size();
out.Add(OP_ADVANCE_PC);
out.Add((arg >> 0) & 0xFF);
out.Add((arg >> 8) & 0xFF);
out.Add((arg >> 16) & 0xFF);
out.Add((arg >> 24) & 0xFF);
}
out.Add(OP_STOP);
@@ -143,9 +153,8 @@ typedef void (*AddFrame)(void* ctxt,
// then symbolize using our normal PC descriptors, etc, but this function must
// not call any function that has been instrumented by TSAN or it might deadlock
// during __tsan_func_entry.
extern "C" NO_SANITIZE_THREAD DISABLE_SANITIZER_INSTRUMENTATION void
DART_EXPORT NO_SANITIZE_THREAD DISABLE_SANITIZER_INSTRUMENTATION void
__tsan_symbolize_external_ex(uintptr_t pc, AddFrame add_frame, void* ctxt) {
constexpr uintptr_t kExternalPCBit = 1ULL << 60;
const uword lookup_pc = pc & ~kExternalPCBit;
for (TsanLineNumberProgram* lnp = head.load(std::memory_order_acquire);
@@ -203,19 +212,6 @@ __tsan_symbolize_external_ex(uintptr_t pc, AddFrame add_frame, void* ctxt) {
uint32_t disp = a | (b << 8) | (c << 16) | (d << 24);
lnp_pc += disp;
if (lookup_pc <= lnp_pc) {
#if 0
char s[17];
s[16] = 0;
for (intptr_t k = 0; k < 16; k++) {
s[k] = "0123456789abcdef"[lookup_pc >> ((15 - k) << 2)) & 0xFF];
}
add_frame(ctxt, "lookup_pc", s, 0, 0);
for (intptr_t k = 0; k < 16; k++) {
s[k] = "0123456789abcdef"[lnp_pc >> ((15 - k) << 2)) & 0xFF];
}
add_frame(ctxt, "lnp_pc", s, 0, 0);
#endif
for (intptr_t i = depth - 1; i >= 0; i--) {
add_frame(ctxt, names[i], files[i], lines[i], columns[i]);
}
@@ -233,7 +229,20 @@ __tsan_symbolize_external_ex(uintptr_t pc, AddFrame add_frame, void* ctxt) {
UNREACHABLE();
}
}
add_frame(ctxt, "dart-code-lookup-failed", nullptr, 0, 0);
// TSAN doesn't fall back to reporting the PC if we provide no frame or a
// frame with a null location.
char pc_string[17];
for (intptr_t i = 0; i < 16; i++) {
pc_string[i] = "0123456789abcdef"[(lookup_pc >> ((15 - i) << 2)) & 0xF];
}
pc_string[16] = 0;
if (lookup_pc == 42) {
add_frame(ctxt, "dart-deoptimized-code", pc_string, 0, 0);
} else {
add_frame(ctxt, "dart-code-lookup-failed", pc_string, 0, 0);
}
}
#else
void RegisterTsanSymbolize(const Code& code) {}
+6
View File
@@ -72,6 +72,9 @@ static void GenerateInvokeTTSStub(compiler::Assembler* assembler) {
};
__ EnterDartFrame(0);
if (FLAG_target_thread_sanitizer) {
__ TsanFuncEntry();
}
for (intptr_t i = 0; i < kNumberOfCpuRegisters; ++i) {
if (((1 << i) & kDartAvailableCpuRegs) == 0) continue;
@@ -181,6 +184,9 @@ static void GenerateInvokeTTSStub(compiler::Assembler* assembler) {
// Set the return from the stub to be null.
__ LoadObject(CallingConventions::kReturnReg, Object::null_object());
if (FLAG_target_thread_sanitizer) {
__ TsanFuncExit();
}
__ LeaveDartFrame();
__ Ret();
}
+4 -2
View File
@@ -1519,7 +1519,8 @@
"arguments": [
"-nvm-${sanitizer}-${system}-${mode}-${arch}",
"ffi",
"standalone"
"standalone",
"vm/dart/tsan"
],
"fileset": "vm",
"shards": 2
@@ -1529,7 +1530,8 @@
"arguments": [
"-nvm-aot-${sanitizer}-${system}-${mode}-${arch}",
"ffi",
"standalone"
"standalone",
"vm/dart/tsan"
],
"fileset": "vm",
"shards": 3