Skip to content

Commit e01d4b3

Browse files
rmacnak-googleCommit Queue
authored andcommitted
[vm, compiler] Get the full Dart stack in TSAN reports.
Call __tsan_func_entry/__tsan_func_exit in functions that use __tsan_read/__tsan_write or call other functions. Call __tsan_func_exit once per frame when unwinding for exceptions. Do so only in AOT, since TSAN won't be able to symbolize JIT functions anyway. TEST=ci Bug: #61352 Cq-Include-Trybots: luci.dart.try:vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try Change-Id: Ie52c978c25664d78b834e9b72ecf7eb2a12cc2ba Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/444181 Reviewed-by: Alexander Aprelev <[email protected]> Commit-Queue: Ryan Macnak <[email protected]>
1 parent 3bc1985 commit e01d4b3

30 files changed

+826
-384
lines changed

runtime/platform/thread_sanitizer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ extern "C" void __tsan_write2(void* addr);
3535
extern "C" void __tsan_write4(void* addr);
3636
extern "C" void __tsan_write8(void* addr);
3737
extern "C" void __tsan_write16(void* addr);
38+
extern "C" void __tsan_func_entry(void* pc);
39+
extern "C" void __tsan_func_exit();
3840
#else
3941
#define NO_SANITIZE_THREAD
4042
#endif

runtime/tests/vm/dart/tsan/array_data_race_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,6 @@ main(List<String> arguments) {
8383
Expect.notEquals(0, result.exitCode);
8484
Expect.contains("ThreadSanitizer: data race", result.stderr);
8585
Expect.contains("of size 8", result.stderr);
86-
Expect.contains("List.[]=", result.stderr);
87-
Expect.contains("_Array.[]", result.stderr);
86+
Expect.contains("dataRaceFromMain", result.stderr);
87+
Expect.contains("dataRaceFromChild", result.stderr);
8888
}

runtime/tests/vm/dart/tsan/field_data_race_test.dart

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,20 @@ dataRaceFromChild() {
4040
}
4141
}
4242

43+
@pragma("vm:never-inline")
44+
dataRaceFromMainCaller() => dataRaceFromMain();
45+
46+
@pragma("vm:never-inline")
47+
dataRaceFromMainCallerCaller() => dataRaceFromMainCaller();
48+
49+
@pragma("vm:never-inline")
50+
dataRaceFromChildCaller() => dataRaceFromChild();
51+
52+
@pragma("vm:never-inline")
53+
dataRaceFromChildCallerCaller() => dataRaceFromChildCaller();
54+
4355
child(_) {
44-
dataRaceFromChild();
56+
dataRaceFromChildCallerCaller();
4557
}
4658

4759
final nativeLib = dlopenPlatformSpecific('ffi_test_functions');
@@ -65,7 +77,7 @@ main(List<String> arguments) {
6577
unsafeSetSharedTo(getRootLibraryUrl(), "box", Box());
6678
print(box); // side effect initialization
6779
Isolate.spawn(child, null);
68-
dataRaceFromMain();
80+
dataRaceFromMainCallerCaller();
6981
return;
7082
}
7183

@@ -87,5 +99,9 @@ main(List<String> arguments) {
8799
Expect.contains("ThreadSanitizer: data race", result.stderr);
88100
Expect.contains("of size 8", result.stderr);
89101
Expect.contains("dataRaceFromMain", result.stderr);
102+
Expect.contains("dataRaceFromMainCaller", result.stderr);
103+
Expect.contains("dataRaceFromMainCallerCaller", result.stderr);
90104
Expect.contains("dataRaceFromChild", result.stderr);
105+
Expect.contains("dataRaceFromChildCaller", result.stderr);
106+
Expect.contains("dataRaceFromChildCallerCaller", result.stderr);
91107
}

runtime/vm/compiler/assembler/assembler_arm.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3273,7 +3273,8 @@ void Assembler::EmitEntryFrameVerification(Register scratch) {
32733273
}
32743274

32753275
void Assembler::CallRuntime(const RuntimeEntry& entry,
3276-
intptr_t argument_count) {
3276+
intptr_t argument_count,
3277+
bool tsan_enter_exit) {
32773278
ASSERT(!entry.is_leaf());
32783279
// Argument count is not checked here, but in the runtime entry for a more
32793280
// informative error message.

runtime/vm/compiler/assembler/assembler_arm.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,10 @@ class Assembler : public AssemblerBase {
430430
void StoreMemoryValue(Register src, Register base, int32_t offset) {
431431
StoreToOffset(src, base, offset);
432432
}
433+
434+
void TsanFuncEntry(bool preserve_registers = true) { UNREACHABLE(); }
435+
void TsanFuncExit(bool preserve_registers = true) { UNREACHABLE(); }
436+
433437
void LoadAcquire(Register dst,
434438
const Address& address,
435439
OperandSize size = kFourBytes) override {
@@ -1464,7 +1468,9 @@ class Assembler : public AssemblerBase {
14641468
void EmitEntryFrameVerification(Register scratch);
14651469

14661470
// For non-leaf runtime calls. For leaf runtime calls, use LeafRuntimeScope,
1467-
void CallRuntime(const RuntimeEntry& entry, intptr_t argument_count);
1471+
void CallRuntime(const RuntimeEntry& entry,
1472+
intptr_t argument_count,
1473+
bool tsan_enter_exit = true);
14681474

14691475
// Set up a Dart frame on entry with a frame pointer and PC information to
14701476
// enable easy access to the RawInstruction object of code corresponding

runtime/vm/compiler/assembler/assembler_arm64.cc

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,22 @@ void Assembler::TsanWrite(Register addr, intptr_t size) {
403403
}
404404
}
405405

406+
void Assembler::TsanFuncEntry(bool preserve_registers) {
407+
Comment("TsanFuncEntry");
408+
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
409+
ldr(R0, Address(FP, target::frame_layout.saved_caller_fp_from_fp *
410+
target::kWordSize));
411+
ldr(R0, Address(R0, target::frame_layout.saved_caller_pc_from_fp *
412+
target::kWordSize));
413+
rt.Call(kTsanFuncEntryRuntimeEntry, /*argument_count=*/1);
414+
}
415+
416+
void Assembler::TsanFuncExit(bool preserve_registers) {
417+
Comment("TsanFuncExit");
418+
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
419+
rt.Call(kTsanFuncExitRuntimeEntry, /*argument_count=*/0);
420+
}
421+
406422
static int CountLeadingZeros(uint64_t value, int width) {
407423
if (width == 64) return Utils::CountLeadingZeros64(value);
408424
if (width == 32) return Utils::CountLeadingZeros32(value);
@@ -1830,13 +1846,22 @@ void Assembler::VerifyNotInGenerated(Register scratch) {
18301846
}
18311847

18321848
void Assembler::CallRuntime(const RuntimeEntry& entry,
1833-
intptr_t argument_count) {
1849+
intptr_t argument_count,
1850+
bool tsan_enter_exit) {
18341851
ASSERT(!entry.is_leaf());
18351852
// Argument count is not checked here, but in the runtime entry for a more
18361853
// informative error message.
1854+
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
1855+
tsan_enter_exit) {
1856+
TsanFuncEntry(/*preserve_registers=*/false);
1857+
}
18371858
ldr(R5, compiler::Address(THR, entry.OffsetFromThread()));
18381859
LoadImmediate(R4, argument_count);
18391860
Call(Address(THR, target::Thread::call_to_runtime_entry_point_offset()));
1861+
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
1862+
tsan_enter_exit) {
1863+
TsanFuncExit(/*preserve_registers=*/false);
1864+
}
18401865
}
18411866

18421867
// FPU: Only the bottom 64-bits of v8-v15 are preserved by the caller. The upper

runtime/vm/compiler/assembler/assembler_arm64.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,8 @@ class Assembler : public AssemblerBase {
506506
void TsanStoreRelease(Register src, Register addr, OperandSize size);
507507
void TsanRead(Register addr, intptr_t size);
508508
void TsanWrite(Register addr, intptr_t size);
509+
void TsanFuncEntry(bool preserve_registers = true);
510+
void TsanFuncExit(bool preserve_registers = true);
509511

510512
void LoadAcquire(Register dst,
511513
const Address& address,
@@ -2141,7 +2143,9 @@ class Assembler : public AssemblerBase {
21412143
void LeaveDartFrame();
21422144

21432145
// For non-leaf runtime calls. For leaf runtime calls, use LeafRuntimeScope,
2144-
void CallRuntime(const RuntimeEntry& entry, intptr_t argument_count);
2146+
void CallRuntime(const RuntimeEntry& entry,
2147+
intptr_t argument_count,
2148+
bool tsan_enter_exit = true);
21452149

21462150
// Set up a stub frame so that the stack traversal code can easily identify
21472151
// a stub frame.

runtime/vm/compiler/assembler/assembler_ia32.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2607,7 +2607,8 @@ static const Register volatile_cpu_registers[kNumberOfVolatileCpuRegisters] = {
26072607
EAX, ECX, EDX};
26082608

26092609
void Assembler::CallRuntime(const RuntimeEntry& entry,
2610-
intptr_t argument_count) {
2610+
intptr_t argument_count,
2611+
bool tsan_enter_exit) {
26112612
ASSERT(!entry.is_leaf());
26122613
// Argument count is not checked here, but in the runtime entry for a more
26132614
// informative error message.

runtime/vm/compiler/assembler/assembler_ia32.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,9 @@ class Assembler : public AssemblerBase {
668668
}
669669
}
670670

671+
void TsanFuncEntry(bool preserve_registers = true) { UNREACHABLE(); }
672+
void TsanFuncExit(bool preserve_registers = true) { UNREACHABLE(); }
673+
671674
void LoadAcquire(Register dst,
672675
const Address& address,
673676
OperandSize size = kFourBytes) override {
@@ -906,7 +909,9 @@ class Assembler : public AssemblerBase {
906909
void ExitFullSafepoint(Register scratch);
907910

908911
// For non-leaf runtime calls. For leaf runtime calls, use LeafRuntimeScope,
909-
void CallRuntime(const RuntimeEntry& entry, intptr_t argument_count);
912+
void CallRuntime(const RuntimeEntry& entry,
913+
intptr_t argument_count,
914+
bool tsan_enter_exit = true);
910915

911916
void Call(const Code& code,
912917
bool movable_target = false,

runtime/vm/compiler/assembler/assembler_riscv.cc

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3222,6 +3222,22 @@ void Assembler::TsanWrite(Register addr, intptr_t size) {
32223222
}
32233223
}
32243224

3225+
void Assembler::TsanFuncEntry(bool preserve_registers) {
3226+
Comment("TsanFuncEntry");
3227+
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
3228+
lx(A0, Address(FP, target::frame_layout.saved_caller_fp_from_fp *
3229+
target::kWordSize));
3230+
lx(A0, Address(A0, target::frame_layout.saved_caller_pc_from_fp *
3231+
target::kWordSize));
3232+
rt.Call(kTsanFuncEntryRuntimeEntry, /*argument_count=*/1);
3233+
}
3234+
3235+
void Assembler::TsanFuncExit(bool preserve_registers) {
3236+
Comment("TsanFuncExit");
3237+
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
3238+
rt.Call(kTsanFuncExitRuntimeEntry, /*argument_count=*/0);
3239+
}
3240+
32253241
void Assembler::LoadAcquire(Register dst,
32263242
const Address& address,
32273243
OperandSize size) {
@@ -4854,13 +4870,22 @@ void Assembler::LeaveDartFrame(intptr_t fp_sp_dist) {
48544870
}
48554871

48564872
void Assembler::CallRuntime(const RuntimeEntry& entry,
4857-
intptr_t argument_count) {
4873+
intptr_t argument_count,
4874+
bool tsan_enter_exit) {
48584875
ASSERT(!entry.is_leaf());
48594876
// Argument count is not checked here, but in the runtime entry for a more
48604877
// informative error message.
4878+
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
4879+
tsan_enter_exit) {
4880+
TsanFuncEntry(/*preserve_registers=*/false);
4881+
}
48614882
lx(T5, compiler::Address(THR, entry.OffsetFromThread()));
48624883
li(T4, argument_count);
48634884
Call(Address(THR, target::Thread::call_to_runtime_entry_point_offset()));
4885+
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
4886+
tsan_enter_exit) {
4887+
TsanFuncExit(/*preserve_registers=*/false);
4888+
}
48644889
}
48654890

48664891
static const RegisterSet kRuntimeCallSavedRegisters(kDartVolatileCpuRegs,

0 commit comments

Comments
 (0)