// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Implementation of the race detector API. // +build race #include "runtime.h" #include "arch_GOARCH.h" #include "malloc.h" #include "race.h" #include "type.h" #include "typekind.h" #include "textflag.h" // Race runtime functions called via runtime·racecall. void __tsan_init(void); void __tsan_fini(void); void __tsan_map_shadow(void); void __tsan_finalizer_goroutine(void); void __tsan_go_start(void); void __tsan_go_end(void); void __tsan_malloc(void); void __tsan_acquire(void); void __tsan_release(void); void __tsan_release_merge(void); void __tsan_go_ignore_sync_begin(void); void __tsan_go_ignore_sync_end(void); // Mimic what cmd/cgo would do. #pragma cgo_import_static __tsan_init #pragma cgo_import_static __tsan_fini #pragma cgo_import_static __tsan_map_shadow #pragma cgo_import_static __tsan_finalizer_goroutine #pragma cgo_import_static __tsan_go_start #pragma cgo_import_static __tsan_go_end #pragma cgo_import_static __tsan_malloc #pragma cgo_import_static __tsan_acquire #pragma cgo_import_static __tsan_release #pragma cgo_import_static __tsan_release_merge #pragma cgo_import_static __tsan_go_ignore_sync_begin #pragma cgo_import_static __tsan_go_ignore_sync_end // These are called from race_amd64.s. #pragma cgo_import_static __tsan_read #pragma cgo_import_static __tsan_read_pc #pragma cgo_import_static __tsan_read_range #pragma cgo_import_static __tsan_write #pragma cgo_import_static __tsan_write_pc #pragma cgo_import_static __tsan_write_range #pragma cgo_import_static __tsan_func_enter #pragma cgo_import_static __tsan_func_exit #pragma cgo_import_static __tsan_go_atomic32_load #pragma cgo_import_static __tsan_go_atomic64_load #pragma cgo_import_static __tsan_go_atomic32_store #pragma cgo_import_static __tsan_go_atomic64_store #pragma cgo_import_static __tsan_go_atomic32_exchange #pragma cgo_import_static __tsan_go_atomic64_exchange #pragma cgo_import_static __tsan_go_atomic32_fetch_add #pragma cgo_import_static __tsan_go_atomic64_fetch_add #pragma cgo_import_static __tsan_go_atomic32_compare_exchange #pragma cgo_import_static __tsan_go_atomic64_compare_exchange extern byte runtime·noptrdata[]; extern byte runtime·enoptrdata[]; extern byte runtime·data[]; extern byte runtime·edata[]; extern byte runtime·bss[]; extern byte runtime·ebss[]; extern byte runtime·noptrbss[]; extern byte runtime·enoptrbss[]; // start/end of global data (data+bss). uintptr runtime·racedatastart; uintptr runtime·racedataend; // start/end of heap for race_amd64.s uintptr runtime·racearenastart; uintptr runtime·racearenaend; void runtime·racefuncenter(void *callpc); void runtime·racefuncexit(void); void runtime·racereadrangepc1(void *addr, uintptr sz, void *pc); void runtime·racewriterangepc1(void *addr, uintptr sz, void *pc); void runtime·racesymbolizethunk(void*); // racecall allows calling an arbitrary function f from C race runtime // with up to 4 uintptr arguments. void runtime·racecall(void(*f)(void), ...); // checks if the address has shadow (i.e. heap or data/bss) #pragma textflag NOSPLIT static bool isvalidaddr(uintptr addr) { if(addr >= runtime·racearenastart && addr < runtime·racearenaend) return true; if(addr >= runtime·racedatastart && addr < runtime·racedataend) return true; return false; } #pragma textflag NOSPLIT uintptr runtime·raceinit(void) { uintptr racectx, start, end, size; // cgo is required to initialize libc, which is used by race runtime if(!runtime·iscgo) runtime·throw("raceinit: race build must use cgo"); runtime·racecall(__tsan_init, &racectx, runtime·racesymbolizethunk); // Round data segment to page boundaries, because it's used in mmap(). // The relevant sections are noptrdata, data, bss, noptrbss. // In external linking mode, there may be other non-Go data mixed in, // and the sections may even occur out of order. // Work out a conservative range of addresses. start = ~(uintptr)0; end = 0; if(start > (uintptr)runtime·noptrdata) start = (uintptr)runtime·noptrdata; if(start > (uintptr)runtime·data) start = (uintptr)runtime·data; if(start > (uintptr)runtime·noptrbss) start = (uintptr)runtime·noptrbss; if(start > (uintptr)runtime·bss) start = (uintptr)runtime·bss; if(end < (uintptr)runtime·enoptrdata) end = (uintptr)runtime·enoptrdata; if(end < (uintptr)runtime·edata) end = (uintptr)runtime·edata; if(end < (uintptr)runtime·enoptrbss) end = (uintptr)runtime·enoptrbss; if(end < (uintptr)runtime·ebss) end = (uintptr)runtime·ebss; start = start & ~(PageSize-1); size = ROUND(end - start, PageSize); runtime·racecall(__tsan_map_shadow, start, size); runtime·racedatastart = start; runtime·racedataend = start + size; return racectx; } #pragma textflag NOSPLIT void runtime·racefini(void) { runtime·racecall(__tsan_fini); } #pragma textflag NOSPLIT void runtime·racemapshadow(void *addr, uintptr size) { if(runtime·racearenastart == 0) runtime·racearenastart = (uintptr)addr; if(runtime·racearenaend < (uintptr)addr+size) runtime·racearenaend = (uintptr)addr+size; runtime·racecall(__tsan_map_shadow, addr, size); } #pragma textflag NOSPLIT void runtime·racemalloc(void *p, uintptr sz) { runtime·racecall(__tsan_malloc, p, sz); } #pragma textflag NOSPLIT uintptr runtime·racegostart(void *pc) { uintptr racectx; G *spawng; if(g->m->curg != nil) spawng = g->m->curg; else spawng = g; runtime·racecall(__tsan_go_start, spawng->racectx, &racectx, pc); return racectx; } #pragma textflag NOSPLIT void runtime·racegoend(void) { runtime·racecall(__tsan_go_end, g->racectx); } #pragma textflag NOSPLIT void runtime·racewriterangepc(void *addr, uintptr sz, void *callpc, void *pc) { if(g != g->m->curg) { // The call is coming from manual instrumentation of Go code running on g0/gsignal. // Not interesting. return; } if(callpc != nil) runtime·racefuncenter(callpc); runtime·racewriterangepc1(addr, sz, pc); if(callpc != nil) runtime·racefuncexit(); } #pragma textflag NOSPLIT void runtime·racereadrangepc(void *addr, uintptr sz, void *callpc, void *pc) { if(g != g->m->curg) { // The call is coming from manual instrumentation of Go code running on g0/gsignal. // Not interesting. return; } if(callpc != nil) runtime·racefuncenter(callpc); runtime·racereadrangepc1(addr, sz, pc); if(callpc != nil) runtime·racefuncexit(); } #pragma textflag NOSPLIT void runtime·racewriteobjectpc(void *addr, Type *t, void *callpc, void *pc) { uint8 kind; kind = t->kind & KindMask; if(kind == KindArray || kind == KindStruct) runtime·racewriterangepc(addr, t->size, callpc, pc); else runtime·racewritepc(addr, callpc, pc); } #pragma textflag NOSPLIT void runtime·racereadobjectpc(void *addr, Type *t, void *callpc, void *pc) { uint8 kind; kind = t->kind & KindMask; if(kind == KindArray || kind == KindStruct) runtime·racereadrangepc(addr, t->size, callpc, pc); else runtime·racereadpc(addr, callpc, pc); } #pragma textflag NOSPLIT void runtime·raceacquire(void *addr) { runtime·raceacquireg(g, addr); } #pragma textflag NOSPLIT void runtime·raceacquireg(G *gp, void *addr) { if(g->raceignore || !isvalidaddr((uintptr)addr)) return; runtime·racecall(__tsan_acquire, gp->racectx, addr); } #pragma textflag NOSPLIT void runtime·racerelease(void *addr) { if(g->raceignore || !isvalidaddr((uintptr)addr)) return; runtime·racereleaseg(g, addr); } #pragma textflag NOSPLIT void runtime·racereleaseg(G *gp, void *addr) { if(g->raceignore || !isvalidaddr((uintptr)addr)) return; runtime·racecall(__tsan_release, gp->racectx, addr); } #pragma textflag NOSPLIT void runtime·racereleasemerge(void *addr) { runtime·racereleasemergeg(g, addr); } #pragma textflag NOSPLIT void runtime·racereleasemergeg(G *gp, void *addr) { if(g->raceignore || !isvalidaddr((uintptr)addr)) return; runtime·racecall(__tsan_release_merge, gp->racectx, addr); } #pragma textflag NOSPLIT void runtime·racefingo(void) { runtime·racecall(__tsan_finalizer_goroutine, g->racectx); } // func RaceAcquire(addr unsafe.Pointer) #pragma textflag NOSPLIT void runtime·RaceAcquire(void *addr) { runtime·raceacquire(addr); } // func RaceRelease(addr unsafe.Pointer) #pragma textflag NOSPLIT void runtime·RaceRelease(void *addr) { runtime·racerelease(addr); } // func RaceReleaseMerge(addr unsafe.Pointer) #pragma textflag NOSPLIT void runtime·RaceReleaseMerge(void *addr) { runtime·racereleasemerge(addr); } // func RaceDisable() #pragma textflag NOSPLIT void runtime·RaceDisable(void) { if(g->raceignore++ == 0) runtime·racecall(__tsan_go_ignore_sync_begin, g->racectx); } // func RaceEnable() #pragma textflag NOSPLIT void runtime·RaceEnable(void) { if(--g->raceignore == 0) runtime·racecall(__tsan_go_ignore_sync_end, g->racectx); }