1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Snapshot loaded device code objects so llvm-symbolizer can name GPU PCs.
10//
11//===----------------------------------------------------------------------===//
12
13#include "sanitizer_common.h"
14#include "sanitizer_file.h"
15#include "sanitizer_libc.h"
16#include "sanitizer_mutex.h"
17#include "sanitizer_offload.h"
18#include "sanitizer_posix.h"
19
20namespace __sanitizer {
21namespace {
22
23struct DeviceImage {
24 uptr LoadBase;
25 uptr LoadSize;
26 void* Bytes;
27 uptr BytesSize;
28 char Path[256];
29};
30
31Mutex ImageMutex;
32InternalMmapVectorNoCtor<DeviceImage> Images;
33
34DeviceImage* ImageFor(uptr PC) {
35 for (uptr I = 0; I < Images.size(); ++I) {
36 DeviceImage& Img = Images[I];
37 if (PC >= Img.LoadBase && PC < Img.LoadBase + Img.LoadSize)
38 return &Img;
39 }
40 return nullptr;
41}
42
43void Drop(uptr I) {
44 DeviceImage& Img = Images[I];
45 if (Img.Path[0])
46 internal_unlink(path: Img.Path);
47 if (Img.Bytes)
48 UnmapOrDie(addr: Img.Bytes, size: Img.BytesSize);
49 if (I + 1 != Images.size())
50 Images[I] = Images.back();
51 Images.pop_back();
52}
53
54const char* PathFor(DeviceImage& Img) {
55 if (Img.Path[0])
56 return Img.Path;
57 if (!Img.Bytes)
58 return nullptr;
59
60 const char* Tmp = GetEnv(name: "TMPDIR");
61 char Binary[256];
62 const char* Name = SanitizerToolName ? SanitizerToolName : "sanitizer";
63 if (ReadBinaryNameCached(buf: Binary, buf_len: sizeof(Binary)))
64 Name = StripModuleName(module: Binary);
65 internal_snprintf(buffer: Img.Path, length: sizeof(Img.Path), format: "%s/%s.%d.%zx.elf",
66 Tmp ? Tmp : "/tmp", Name, (int)internal_getpid(),
67 Img.LoadBase);
68
69 fd_t Fd = OpenFile(filename: Img.Path, mode: WrOnly);
70 bool Ok = Fd != kInvalidFd && WriteToFile(fd: Fd, buff: Img.Bytes, buff_size: Img.BytesSize);
71 if (Fd != kInvalidFd)
72 CloseFile(Fd);
73 if (!Ok) {
74 VReport(1, "%s: could not write %s; device frames will not be symbolized\n",
75 SanitizerToolName, Img.Path);
76 internal_unlink(path: Img.Path);
77 Img.Path[0] = '\0';
78 return nullptr;
79 }
80 return Img.Path;
81}
82
83// True if Addr is in a tracked image. Path is null when the ELF could not
84// be written.
85bool SnapshotImage(uptr Addr, char** Path, uptr* Offset) {
86 *Path = nullptr;
87 Lock L(&ImageMutex);
88 DeviceImage* Img = ImageFor(PC: Addr);
89 if (!Img)
90 return false;
91 *Offset = Addr - Img->LoadBase;
92 if (const char* P = PathFor(Img&: *Img))
93 *Path = internal_strdup(s: P);
94 return true;
95}
96
97} // namespace
98
99void Offload::TrackImage(uptr LoadBase, uptr LoadSize, const void* Storage,
100 uptr StorageSize) {
101 Lock L(&ImageMutex);
102 if (ImageFor(PC: LoadBase))
103 return;
104 DeviceImage Img = {};
105 Img.LoadBase = LoadBase;
106 Img.LoadSize = LoadSize;
107 if (Storage && StorageSize) {
108 Img.Bytes = MmapOrDie(size: StorageSize, mem_type: "offload device image");
109 internal_memcpy(dest: Img.Bytes, src: Storage, n: StorageSize);
110 Img.BytesSize = StorageSize;
111 }
112 Images.push_back(element: Img);
113}
114
115void Offload::UntrackImage(uptr LoadBase) {
116 Lock L(&ImageMutex);
117 for (uptr I = 0; I < Images.size(); ++I) {
118 if (Images[I].LoadBase != LoadBase)
119 continue;
120 Drop(I);
121 return;
122 }
123}
124
125void Offload::UntrackImages() {
126 Lock L(&ImageMutex);
127 while (Images.size()) Drop(I: 0);
128}
129
130SymbolizedStack* Offload::Symbolize(uptr PC) {
131 if (!PC)
132 return nullptr;
133
134 char* Path = nullptr;
135 uptr Offset = 0;
136 if (!SnapshotImage(Addr: PC, Path: &Path, Offset: &Offset))
137 return nullptr;
138 if (!Path)
139 return SymbolizedStack::New(addr: PC);
140
141 SymbolizedStack* Frames = Symbolizer::GetOrInit()->SymbolizeModuleOffset(
142 module_name: Path, module_offset: Offset ? Offset - 1 : Offset);
143 InternalFree(p: Path);
144 for (SymbolizedStack* F = Frames; F; F = F->next) F->info.address = PC;
145 return Frames;
146}
147
148} // namespace __sanitizer
149