1//===-- tsan_go.cpp -------------------------------------------------------===//
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// ThreadSanitizer runtime for Go language.
10//
11//===----------------------------------------------------------------------===//
12
13#include "tsan_rtl.h"
14#include "tsan_symbolize.h"
15#include "sanitizer_common/sanitizer_common.h"
16#include <stdlib.h>
17
18namespace __tsan {
19
20void InitializeInterceptors() {
21}
22
23void InitializeDynamicAnnotations() {
24}
25
26bool IsExpectedReport(uptr addr, uptr size) {
27  return false;
28}
29
30void *internal_alloc(MBlockType typ, uptr sz) {
31  return InternalAlloc(sz);
32}
33
34void internal_free(void *p) {
35  InternalFree(p);
36}
37
38// Callback into Go.
39static void (*go_runtime_cb)(uptr cmd, void *ctx);
40
41enum {
42  CallbackGetProc = 0,
43  CallbackSymbolizeCode = 1,
44  CallbackSymbolizeData = 2,
45};
46
47struct SymbolizeCodeContext {
48  uptr pc;
49  char *func;
50  char *file;
51  uptr line;
52  uptr off;
53  uptr res;
54};
55
56SymbolizedStack *SymbolizeCode(uptr addr) {
57  SymbolizedStack *first = SymbolizedStack::New(addr);
58  SymbolizedStack *s = first;
59  for (;;) {
60    SymbolizeCodeContext cbctx;
61    internal_memset(&cbctx, 0, sizeof(cbctx));
62    cbctx.pc = addr;
63    go_runtime_cb(CallbackSymbolizeCode, &cbctx);
64    if (cbctx.res == 0)
65      break;
66    AddressInfo &info = s->info;
67    info.module_offset = cbctx.off;
68    info.function = internal_strdup(cbctx.func ? cbctx.func : "??");
69    info.file = internal_strdup(cbctx.file ? cbctx.file : "-");
70    info.line = cbctx.line;
71    info.column = 0;
72
73    if (cbctx.pc == addr) // outermost (non-inlined) function
74      break;
75    addr = cbctx.pc;
76    // Allocate a stack entry for the parent of the inlined function.
77    SymbolizedStack *s2 = SymbolizedStack::New(addr);
78    s->next = s2;
79    s = s2;
80  }
81  return first;
82}
83
84struct SymbolizeDataContext {
85  uptr addr;
86  uptr heap;
87  uptr start;
88  uptr size;
89  char *name;
90  char *file;
91  uptr line;
92  uptr res;
93};
94
95ReportLocation *SymbolizeData(uptr addr) {
96  SymbolizeDataContext cbctx;
97  internal_memset(&cbctx, 0, sizeof(cbctx));
98  cbctx.addr = addr;
99  go_runtime_cb(CallbackSymbolizeData, &cbctx);
100  if (!cbctx.res)
101    return 0;
102  if (cbctx.heap) {
103    MBlock *b = ctx->metamap.GetBlock(cbctx.start);
104    if (!b)
105      return 0;
106    ReportLocation *loc = ReportLocation::New(ReportLocationHeap);
107    loc->heap_chunk_start = cbctx.start;
108    loc->heap_chunk_size = b->siz;
109    loc->tid = b->tid;
110    loc->stack = SymbolizeStackId(b->stk);
111    return loc;
112  } else {
113    ReportLocation *loc = ReportLocation::New(ReportLocationGlobal);
114    loc->global.name = internal_strdup(cbctx.name ? cbctx.name : "??");
115    loc->global.file = internal_strdup(cbctx.file ? cbctx.file : "??");
116    loc->global.line = cbctx.line;
117    loc->global.start = cbctx.start;
118    loc->global.size = cbctx.size;
119    return loc;
120  }
121}
122
123static ThreadState *main_thr;
124static bool inited;
125
126static Processor* get_cur_proc() {
127  if (UNLIKELY(!inited)) {
128    // Running Initialize().
129    // We have not yet returned the Processor to Go, so we cannot ask it back.
130    // Currently, Initialize() does not use the Processor, so return nullptr.
131    return nullptr;
132  }
133  Processor *proc;
134  go_runtime_cb(CallbackGetProc, &proc);
135  return proc;
136}
137
138Processor *ThreadState::proc() {
139  return get_cur_proc();
140}
141
142extern "C" {
143
144static ThreadState *AllocGoroutine() {
145  ThreadState *thr = (ThreadState*)internal_alloc(MBlockThreadContex,
146      sizeof(ThreadState));
147  internal_memset(thr, 0, sizeof(*thr));
148  return thr;
149}
150
151void __tsan_init(ThreadState **thrp, Processor **procp,
152                 void (*cb)(uptr cmd, void *cb)) {
153  go_runtime_cb = cb;
154  ThreadState *thr = AllocGoroutine();
155  main_thr = *thrp = thr;
156  Initialize(thr);
157  *procp = thr->proc1;
158  inited = true;
159}
160
161void __tsan_fini() {
162  // FIXME: Not necessary thread 0.
163  ThreadState *thr = main_thr;
164  int res = Finalize(thr);
165  exit(res);
166}
167
168void __tsan_map_shadow(uptr addr, uptr size) {
169  MapShadow(addr, size);
170}
171
172void __tsan_read(ThreadState *thr, void *addr, void *pc) {
173  MemoryRead(thr, (uptr)pc, (uptr)addr, kSizeLog1);
174}
175
176void __tsan_read_pc(ThreadState *thr, void *addr, uptr callpc, uptr pc) {
177  if (callpc != 0)
178    FuncEntry(thr, callpc);
179  MemoryRead(thr, (uptr)pc, (uptr)addr, kSizeLog1);
180  if (callpc != 0)
181    FuncExit(thr);
182}
183
184void __tsan_write(ThreadState *thr, void *addr, void *pc) {
185  MemoryWrite(thr, (uptr)pc, (uptr)addr, kSizeLog1);
186}
187
188void __tsan_write_pc(ThreadState *thr, void *addr, uptr callpc, uptr pc) {
189  if (callpc != 0)
190    FuncEntry(thr, callpc);
191  MemoryWrite(thr, (uptr)pc, (uptr)addr, kSizeLog1);
192  if (callpc != 0)
193    FuncExit(thr);
194}
195
196void __tsan_read_range(ThreadState *thr, void *addr, uptr size, uptr pc) {
197  MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, false);
198}
199
200void __tsan_write_range(ThreadState *thr, void *addr, uptr size, uptr pc) {
201  MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, true);
202}
203
204void __tsan_func_enter(ThreadState *thr, void *pc) {
205  FuncEntry(thr, (uptr)pc);
206}
207
208void __tsan_func_exit(ThreadState *thr) {
209  FuncExit(thr);
210}
211
212void __tsan_malloc(ThreadState *thr, uptr pc, uptr p, uptr sz) {
213  CHECK(inited);
214  if (thr && pc)
215    ctx->metamap.AllocBlock(thr, pc, p, sz);
216  MemoryResetRange(0, 0, (uptr)p, sz);
217}
218
219void __tsan_free(uptr p, uptr sz) {
220  ctx->metamap.FreeRange(get_cur_proc(), p, sz);
221}
222
223void __tsan_go_start(ThreadState *parent, ThreadState **pthr, void *pc) {
224  ThreadState *thr = AllocGoroutine();
225  *pthr = thr;
226  int goid = ThreadCreate(parent, (uptr)pc, 0, true);
227  ThreadStart(thr, goid, 0, ThreadType::Regular);
228}
229
230void __tsan_go_end(ThreadState *thr) {
231  ThreadFinish(thr);
232  internal_free(thr);
233}
234
235void __tsan_proc_create(Processor **pproc) {
236  *pproc = ProcCreate();
237}
238
239void __tsan_proc_destroy(Processor *proc) {
240  ProcDestroy(proc);
241}
242
243void __tsan_acquire(ThreadState *thr, void *addr) {
244  Acquire(thr, 0, (uptr)addr);
245}
246
247void __tsan_release(ThreadState *thr, void *addr) {
248  ReleaseStore(thr, 0, (uptr)addr);
249}
250
251void __tsan_release_merge(ThreadState *thr, void *addr) {
252  Release(thr, 0, (uptr)addr);
253}
254
255void __tsan_finalizer_goroutine(ThreadState *thr) {
256  AcquireGlobal(thr, 0);
257}
258
259void __tsan_mutex_before_lock(ThreadState *thr, uptr addr, uptr write) {
260  if (write)
261    MutexPreLock(thr, 0, addr);
262  else
263    MutexPreReadLock(thr, 0, addr);
264}
265
266void __tsan_mutex_after_lock(ThreadState *thr, uptr addr, uptr write) {
267  if (write)
268    MutexPostLock(thr, 0, addr);
269  else
270    MutexPostReadLock(thr, 0, addr);
271}
272
273void __tsan_mutex_before_unlock(ThreadState *thr, uptr addr, uptr write) {
274  if (write)
275    MutexUnlock(thr, 0, addr);
276  else
277    MutexReadUnlock(thr, 0, addr);
278}
279
280void __tsan_go_ignore_sync_begin(ThreadState *thr) {
281  ThreadIgnoreSyncBegin(thr, 0);
282}
283
284void __tsan_go_ignore_sync_end(ThreadState *thr) {
285  ThreadIgnoreSyncEnd(thr, 0);
286}
287
288void __tsan_report_count(u64 *pn) {
289  Lock lock(&ctx->report_mtx);
290  *pn = ctx->nreported;
291}
292
293}  // extern "C"
294}  // namespace __tsan
295