blob: 1c477d33d1037d99538bef6c3f1d3ed9dd15caac [file] [edit]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Host-side device race report generation.
///
//===----------------------------------------------------------------------===//
#include "csan_offload.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_offload.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_stacktrace_printer.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include "shared/rpc.h"
using namespace __sanitizer;
namespace __csan {
namespace {
class Decorator : public SanitizerCommonDecorator {
public:
const char *Access() { return Blue(); }
const char *Location() { return Green(); }
};
const char *KindName(u32 Kind) {
switch (Kind) {
case CSAN_RACE_UNKNOWN_ORIGIN:
return "data race of unknown origin";
case CSAN_RACE_INTRA_WAVE:
return "intra-wave data race";
default:
return "data race";
}
}
const char *MopDesc(bool First, u32 Type) {
const bool Write = Type & CSAN_ACCESS_WRITE;
return First ? (Write ? "Write" : "Read")
: (Write ? "Previous write" : "Previous read");
}
// A pair of PCs that have already been reported as racing. The device samples
// the same static access from thousands of threads, so without this every
// launch buries the user in duplicates of one bug.
struct RacyPcs {
u64 pc[2];
bool operator==(const RacyPcs &other) const {
if (pc[0] == other.pc[0] && pc[1] == other.pc[1])
return true;
return pc[0] == other.pc[1] && pc[1] == other.pc[0];
}
};
Mutex RacyMutex;
InternalMmapVectorNoCtor<RacyPcs> RacyPcsSeen;
bool FindRacyPcs(const RacyPcs &Racy) {
for (uptr I = 0; I < RacyPcsSeen.size(); ++I) {
if (Racy == RacyPcsSeen[I]) {
VReport(2, "%s: suppressing report as doubled\n", SanitizerToolName);
return true;
}
}
return false;
}
bool HandleRacyPcs(const __csan_gpu_race &R) {
RacyPcs Racy = {{R.pc, R.peer_pc}};
{
ReadLock L(&RacyMutex);
if (FindRacyPcs(Racy))
return true;
}
Lock L(&RacyMutex);
if (FindRacyPcs(Racy))
return true;
RacyPcsSeen.push_back(Racy);
return false;
}
void PrintFrames(SymbolizedStack *Frames, u64 PC) {
if (!Frames) {
Printf(" #0 (%p)\n", (void *)(uptr)PC);
return;
}
const SymbolizedStack *F = SkipInternalFrames(Frames);
if (!F)
F = Frames;
int N = 0;
for (; F; F = F->next, ++N) {
InternalScopedString Res;
StackTracePrinter::GetOrInit()->RenderFrame(
&Res, common_flags()->stack_trace_format, N, F->info.address, &F->info,
common_flags()->symbolize_vs_style, common_flags()->strip_path_prefix);
Printf("%s\n", Res.data());
}
}
} // namespace
void PrintOffloadReport(const __csan_gpu_race &R) {
if (HandleRacyPcs(R))
return;
Decorator D;
Printf("==================\n");
Printf("%s", D.Warning());
Printf("WARNING: ConcurrencySanitizer: %s\n", KindName(R.kind));
Printf("%s", D.Default());
Printf("%s", D.Access());
Printf(" %s of size %u at 0x%zx in block (%u,%u,%u) thread (%u,%u,%u) "
"lane %u:\n",
MopDesc(true, R.access_type), R.size, (uptr)R.addr, R.block[0],
R.block[1], R.block[2], R.thread[0], R.thread[1], R.thread[2], R.lane);
Printf("%s", D.Default());
SymbolizedStack *This = Offload::Get().Symbolize((uptr)R.pc);
PrintFrames(This, R.pc);
Printf("%s", D.Access());
if (R.peer_pc) {
Printf(" %s of size %u at 0x%zx:\n", MopDesc(false, R.peer_access_type),
R.peer_size, (uptr)R.addr);
Printf("%s", D.Default());
SymbolizedStack *Peer = Offload::Get().Symbolize((uptr)R.peer_pc);
PrintFrames(Peer, R.peer_pc);
if (Peer)
Peer->ClearAll();
} else if (R.kind == CSAN_RACE_INTRA_WAVE) {
Printf(" Previous access by lane %u in the same wave\n", R.peer_lane);
Printf("%s", D.Default());
} else {
Printf(" Previous access of unknown origin\n");
Printf("%s", D.Default());
}
DataInfo Loc;
if (Offload::Get().SymbolizeData((uptr)R.addr, &Loc)) {
Printf("%s", D.Location());
if (Loc.size)
Printf(" Location is global '%s' of size %zu at 0x%zx\n", Loc.name,
Loc.size, (uptr)R.addr);
else
Printf(" Location is global '%s' at 0x%zx\n", Loc.name, (uptr)R.addr);
Printf("%s", D.Default());
Loc.Clear();
}
if (This) {
const SymbolizedStack *User = SkipInternalFrames(This);
ReportErrorSummary(KindName(R.kind), (User ? User : This)->info);
This->ClearAll();
}
Printf("==================\n");
}
u32 HandleOffloadReport(void *PortPtr, u32) {
auto &Port = *reinterpret_cast<rpc::Server::Port *>(PortPtr);
if (Port.get_opcode() != SANITIZER_OFFLOAD_CSAN)
return rpc::RPC_UNHANDLED_OPCODE;
Port.recv([&](rpc::Buffer *Buffer, u32) {
__csan_gpu_race R;
internal_memcpy(&R, Buffer->data, sizeof(R));
PrintOffloadReport(R);
});
return rpc::RPC_SUCCESS;
}
} // namespace __csan