| //===-- tsan_go.cc --------------------------------------------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // ThreadSanitizer runtime for Go language. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "tsan_rtl.h" |
| #include "tsan_symbolize.h" |
| #include "sanitizer_common/sanitizer_common.h" |
| #include <stdlib.h> |
| |
| namespace __tsan { |
| |
| void InitializeInterceptors() { |
| } |
| |
| void InitializeDynamicAnnotations() { |
| } |
| |
| bool IsExpectedReport(uptr addr, uptr size) { |
| return false; |
| } |
| |
| void internal_start_thread(void(*func)(void*), void *arg) { |
| } |
| |
| ReportLocation *SymbolizeData(uptr addr) { |
| return 0; |
| } |
| |
| ReportStack *NewReportStackEntry(uptr addr) { |
| ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack, |
| sizeof(ReportStack)); |
| internal_memset(ent, 0, sizeof(*ent)); |
| ent->pc = addr; |
| return ent; |
| } |
| |
| void *internal_alloc(MBlockType typ, uptr sz) { |
| return InternalAlloc(sz); |
| } |
| |
| void internal_free(void *p) { |
| InternalFree(p); |
| } |
| |
| // Callback into Go. |
| extern "C" int __tsan_symbolize(uptr pc, char **func, char **file, |
| int *line, int *off); |
| |
| ReportStack *SymbolizeCode(uptr addr) { |
| ReportStack *s = (ReportStack*)internal_alloc(MBlockReportStack, |
| sizeof(ReportStack)); |
| internal_memset(s, 0, sizeof(*s)); |
| s->pc = addr; |
| char *func = 0, *file = 0; |
| int line = 0, off = 0; |
| if (__tsan_symbolize(addr, &func, &file, &line, &off)) { |
| s->offset = off; |
| s->func = internal_strdup(func ? func : "??"); |
| s->file = internal_strdup(file ? file : "-"); |
| s->line = line; |
| s->col = 0; |
| free(func); |
| free(file); |
| } |
| return s; |
| } |
| |
| extern "C" { |
| |
| static ThreadState *main_thr; |
| |
| static ThreadState *AllocGoroutine() { |
| ThreadState *thr = (ThreadState*)internal_alloc(MBlockThreadContex, |
| sizeof(ThreadState)); |
| internal_memset(thr, 0, sizeof(*thr)); |
| return thr; |
| } |
| |
| void __tsan_init(ThreadState **thrp) { |
| ThreadState *thr = AllocGoroutine(); |
| main_thr = *thrp = thr; |
| thr->in_rtl++; |
| Initialize(thr); |
| thr->in_rtl--; |
| } |
| |
| void __tsan_fini() { |
| // FIXME: Not necessary thread 0. |
| ThreadState *thr = main_thr; |
| thr->in_rtl++; |
| int res = Finalize(thr); |
| thr->in_rtl--; |
| exit(res); |
| } |
| |
| void __tsan_map_shadow(uptr addr, uptr size) { |
| MapShadow(addr, size); |
| } |
| |
| void __tsan_read(ThreadState *thr, void *addr, void *pc) { |
| MemoryRead(thr, (uptr)pc, (uptr)addr, kSizeLog1); |
| } |
| |
| void __tsan_write(ThreadState *thr, void *addr, void *pc) { |
| MemoryWrite(thr, (uptr)pc, (uptr)addr, kSizeLog1); |
| } |
| |
| void __tsan_read_range(ThreadState *thr, void *addr, uptr size, uptr step, |
| void *pc) { |
| MemoryAccessRangeStep(thr, (uptr)pc, (uptr)addr, size, step, false); |
| } |
| |
| void __tsan_write_range(ThreadState *thr, void *addr, uptr size, uptr step, |
| void *pc) { |
| MemoryAccessRangeStep(thr, (uptr)pc, (uptr)addr, size, step, true); |
| } |
| |
| void __tsan_func_enter(ThreadState *thr, void *pc) { |
| FuncEntry(thr, (uptr)pc); |
| } |
| |
| void __tsan_func_exit(ThreadState *thr) { |
| FuncExit(thr); |
| } |
| |
| void __tsan_malloc(ThreadState *thr, void *p, uptr sz, void *pc) { |
| if (thr == 0) // probably before __tsan_init() |
| return; |
| thr->in_rtl++; |
| MemoryResetRange(thr, (uptr)pc, (uptr)p, sz); |
| thr->in_rtl--; |
| } |
| |
| void __tsan_free(void *p) { |
| (void)p; |
| } |
| |
| void __tsan_go_start(ThreadState *parent, ThreadState **pthr, void *pc) { |
| ThreadState *thr = AllocGoroutine(); |
| *pthr = thr; |
| thr->in_rtl++; |
| parent->in_rtl++; |
| int goid = ThreadCreate(parent, (uptr)pc, 0, true); |
| ThreadStart(thr, goid, 0); |
| parent->in_rtl--; |
| thr->in_rtl--; |
| } |
| |
| void __tsan_go_end(ThreadState *thr) { |
| thr->in_rtl++; |
| ThreadFinish(thr); |
| thr->in_rtl--; |
| internal_free(thr); |
| } |
| |
| void __tsan_acquire(ThreadState *thr, void *addr) { |
| thr->in_rtl++; |
| Acquire(thr, 0, (uptr)addr); |
| thr->in_rtl--; |
| } |
| |
| void __tsan_release(ThreadState *thr, void *addr) { |
| thr->in_rtl++; |
| ReleaseStore(thr, 0, (uptr)addr); |
| thr->in_rtl--; |
| } |
| |
| void __tsan_release_merge(ThreadState *thr, void *addr) { |
| thr->in_rtl++; |
| Release(thr, 0, (uptr)addr); |
| thr->in_rtl--; |
| } |
| |
| void __tsan_finalizer_goroutine(ThreadState *thr) { |
| AcquireGlobal(thr, 0); |
| } |
| |
| #ifdef _WIN32 |
| // MinGW gcc emits calls to the function. |
| void ___chkstk_ms(void) { |
| // The implementation must be along the lines of: |
| // .code64 |
| // PUBLIC ___chkstk_ms |
| // //cfi_startproc() |
| // ___chkstk_ms: |
| // push rcx |
| // //cfi_push(%rcx) |
| // push rax |
| // //cfi_push(%rax) |
| // cmp rax, PAGE_SIZE |
| // lea rcx, [rsp + 24] |
| // jb l_LessThanAPage |
| // .l_MoreThanAPage: |
| // sub rcx, PAGE_SIZE |
| // or rcx, 0 |
| // sub rax, PAGE_SIZE |
| // cmp rax, PAGE_SIZE |
| // ja l_MoreThanAPage |
| // .l_LessThanAPage: |
| // sub rcx, rax |
| // or [rcx], 0 |
| // pop rax |
| // //cfi_pop(%rax) |
| // pop rcx |
| // //cfi_pop(%rcx) |
| // ret |
| // //cfi_endproc() |
| // END |
| } |
| #endif |
| |
| } // extern "C" |
| } // namespace __tsan |