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 18 namespace __tsan { 19 20 void InitializeInterceptors() { 21 } 22 23 void InitializeDynamicAnnotations() { 24 } 25 26 bool IsExpectedReport(uptr addr, uptr size) { 27 return false; 28 } 29 30 void *internal_alloc(MBlockType typ, uptr sz) { 31 return InternalAlloc(sz); 32 } 33 34 void internal_free(void *p) { 35 InternalFree(p); 36 } 37 38 // Callback into Go. 39 static void (*go_runtime_cb)(uptr cmd, void *ctx); 40 41 enum { 42 CallbackGetProc = 0, 43 CallbackSymbolizeCode = 1, 44 CallbackSymbolizeData = 2, 45 }; 46 47 struct SymbolizeCodeContext { 48 uptr pc; 49 char *func; 50 char *file; 51 uptr line; 52 uptr off; 53 uptr res; 54 }; 55 56 SymbolizedStack *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 84 struct 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 95 ReportLocation *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 123 static ThreadState *main_thr; 124 static bool inited; 125 126 static 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 138 Processor *ThreadState::proc() { 139 return get_cur_proc(); 140 } 141 142 extern "C" { 143 144 static ThreadState *AllocGoroutine() { 145 ThreadState *thr = (ThreadState*)internal_alloc(MBlockThreadContex, 146 sizeof(ThreadState)); 147 internal_memset(thr, 0, sizeof(*thr)); 148 return thr; 149 } 150 151 void __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 161 void __tsan_fini() { 162 // FIXME: Not necessary thread 0. 163 ThreadState *thr = main_thr; 164 int res = Finalize(thr); 165 exit(res); 166 } 167 168 void __tsan_map_shadow(uptr addr, uptr size) { 169 MapShadow(addr, size); 170 } 171 172 void __tsan_read(ThreadState *thr, void *addr, void *pc) { 173 MemoryRead(thr, (uptr)pc, (uptr)addr, kSizeLog1); 174 } 175 176 void __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 184 void __tsan_write(ThreadState *thr, void *addr, void *pc) { 185 MemoryWrite(thr, (uptr)pc, (uptr)addr, kSizeLog1); 186 } 187 188 void __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 196 void __tsan_read_range(ThreadState *thr, void *addr, uptr size, uptr pc) { 197 MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, false); 198 } 199 200 void __tsan_write_range(ThreadState *thr, void *addr, uptr size, uptr pc) { 201 MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, true); 202 } 203 204 void __tsan_func_enter(ThreadState *thr, void *pc) { 205 FuncEntry(thr, (uptr)pc); 206 } 207 208 void __tsan_func_exit(ThreadState *thr) { 209 FuncExit(thr); 210 } 211 212 void __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 219 void __tsan_free(uptr p, uptr sz) { 220 ctx->metamap.FreeRange(get_cur_proc(), p, sz); 221 } 222 223 void __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 230 void __tsan_go_end(ThreadState *thr) { 231 ThreadFinish(thr); 232 internal_free(thr); 233 } 234 235 void __tsan_proc_create(Processor **pproc) { 236 *pproc = ProcCreate(); 237 } 238 239 void __tsan_proc_destroy(Processor *proc) { 240 ProcDestroy(proc); 241 } 242 243 void __tsan_acquire(ThreadState *thr, void *addr) { 244 Acquire(thr, 0, (uptr)addr); 245 } 246 247 void __tsan_release_acquire(ThreadState *thr, void *addr) { 248 ReleaseStoreAcquire(thr, 0, (uptr)addr); 249 } 250 251 void __tsan_release(ThreadState *thr, void *addr) { 252 ReleaseStore(thr, 0, (uptr)addr); 253 } 254 255 void __tsan_release_merge(ThreadState *thr, void *addr) { 256 Release(thr, 0, (uptr)addr); 257 } 258 259 void __tsan_finalizer_goroutine(ThreadState *thr) { 260 AcquireGlobal(thr, 0); 261 } 262 263 void __tsan_mutex_before_lock(ThreadState *thr, uptr addr, uptr write) { 264 if (write) 265 MutexPreLock(thr, 0, addr); 266 else 267 MutexPreReadLock(thr, 0, addr); 268 } 269 270 void __tsan_mutex_after_lock(ThreadState *thr, uptr addr, uptr write) { 271 if (write) 272 MutexPostLock(thr, 0, addr); 273 else 274 MutexPostReadLock(thr, 0, addr); 275 } 276 277 void __tsan_mutex_before_unlock(ThreadState *thr, uptr addr, uptr write) { 278 if (write) 279 MutexUnlock(thr, 0, addr); 280 else 281 MutexReadUnlock(thr, 0, addr); 282 } 283 284 void __tsan_go_ignore_sync_begin(ThreadState *thr) { 285 ThreadIgnoreSyncBegin(thr, 0); 286 } 287 288 void __tsan_go_ignore_sync_end(ThreadState *thr) { 289 ThreadIgnoreSyncEnd(thr, 0); 290 } 291 292 void __tsan_report_count(u64 *pn) { 293 Lock lock(&ctx->report_mtx); 294 *pn = ctx->nreported; 295 } 296 297 } // extern "C" 298 } // namespace __tsan 299