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