diff --git a/compiler-rt/lib/tsan/rtl/tsan_flags.cc b/compiler-rt/lib/tsan/rtl/tsan_flags.cc index 95594c40b683..5a0b7cec6f25 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_flags.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_flags.cc @@ -46,6 +46,7 @@ void InitializeFlags(Flags *f, const char *env) { f->log_fileno = 2; f->atexit_sleep_ms = 1000; f->verbosity = 0; + f->profile_memory = ""; // Let a frontend override. OverrideFlags(f); @@ -63,6 +64,7 @@ void InitializeFlags(Flags *f, const char *env) { Flag(env, &f->log_fileno, "log_fileno"); Flag(env, &f->atexit_sleep_ms, "atexit_sleep_ms"); Flag(env, &f->verbosity, "verbosity"); + Flag(env, &f->profile_memory, "profile_memory"); } static const char *GetFlagValue(const char *env, const char *name, diff --git a/compiler-rt/lib/tsan/rtl/tsan_flags.h b/compiler-rt/lib/tsan/rtl/tsan_flags.h index 7a3c4aff2dd5..8e0dc815a4c3 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_flags.h +++ b/compiler-rt/lib/tsan/rtl/tsan_flags.h @@ -46,6 +46,8 @@ struct Flags { int atexit_sleep_ms; // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). int verbosity; + // If set, periodically write memory profile to that file. + const char *profile_memory; }; Flags *flags(); diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc index 025912c7cf1b..f67a646778f8 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc @@ -1551,4 +1551,10 @@ const char *internal_strchr(const char *where, char what) { return (const char*)REAL(strchr)((void*)where, what); } +void internal_start_thread(void(*func)(void *arg), void *arg) { + void *th; + REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg); + REAL(pthread_detach)(th); +} + } // namespace __tsan diff --git a/compiler-rt/lib/tsan/rtl/tsan_mutex.cc b/compiler-rt/lib/tsan/rtl/tsan_mutex.cc index a343a8bc26c8..e95b195aa93b 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_mutex.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_mutex.cc @@ -163,7 +163,7 @@ class Backoff { if (iter_++ < kActiveSpinIters) proc_yield(kActiveSpinCnt); else - sched_yield(); + internal_yield(); return true; } diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform.h b/compiler-rt/lib/tsan/rtl/tsan_platform.h index 9cfe4763f6c8..61b8a697ec74 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_platform.h +++ b/compiler-rt/lib/tsan/rtl/tsan_platform.h @@ -64,11 +64,16 @@ static inline uptr ShadowToMem(uptr shadow) { #endif } +uptr GetShadowMemoryConsumption(); + const char *InitializePlatform(); void FinalizePlatform(); int GetPid(); -void sched_yield(); +void internal_yield(); +void internal_sleep_ms(u32 ms); + +void internal_start_thread(void(*func)(void*), void *arg); typedef int fd_t; const fd_t kInvalidFd = -1; diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc index 96702b48c852..8bb2b483c732 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc @@ -59,6 +59,10 @@ void Die() { _exit(1); } +uptr GetShadowMemoryConsumption() { + return 0; +} + static void *my_mmap(void *addr, size_t length, int prot, int flags, int fd, u64 offset) { ScopedInRtl in_rtl; @@ -69,11 +73,15 @@ static void *my_mmap(void *addr, size_t length, int prot, int flags, # endif } -void sched_yield() { +void internal_yield() { ScopedInRtl in_rtl; syscall(__NR_sched_yield); } +void internal_sleep_ms(u32 ms) { + usleep(ms * 1000); +} + fd_t internal_open(const char *name, bool write) { ScopedInRtl in_rtl; return syscall(__NR_open, name, diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl.cc index 28006f00b961..af23edb1966f 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cc @@ -79,6 +79,64 @@ ThreadContext::ThreadContext(int tid) , dead_next() { } +static void WriteMemoryProfile(char *buf, uptr buf_size, int num) { + uptr shadow = GetShadowMemoryConsumption(); + + int nthread = 0; + int nlivethread = 0; + uptr threadmem = 0; + { + Lock l(&ctx->thread_mtx); + for (unsigned i = 0; i < kMaxTid; i++) { + ThreadContext *tctx = ctx->threads[i]; + if (tctx == 0) + continue; + nthread += 1; + threadmem += sizeof(ThreadContext); + if (tctx->status != ThreadStatusRunning) + continue; + nlivethread += 1; + threadmem += sizeof(ThreadState); + } + } + + uptr nsync = 0; + uptr syncmem = CTX()->synctab.GetMemoryConsumption(&nsync); + + Snprintf(buf, buf_size, "%d: shadow=%luMB" + " thread=%luMB(total=%d/live=%d)" + " sync=%luMB(cnt=%lu)\n", + num, + shadow >> 20, + threadmem >> 20, nthread, nlivethread, + syncmem >> 20, nsync); +} + +static void MemoryProfileThread(void *arg) { + ScopedInRtl in_rtl; + fd_t fd = (fd_t)(uptr)arg; + for (int i = 0; ; i++) { + InternalScopedBuf buf(4096); + WriteMemoryProfile(buf.Ptr(), buf.Size(), i); + internal_write(fd, buf.Ptr(), internal_strlen(buf.Ptr())); + internal_sleep_ms(1000); + } +} + +static void InitializeMemoryProfile() { + if (flags()->profile_memory == 0 || flags()->profile_memory[0] == 0) + return; + InternalScopedBuf filename(4096); + Snprintf(filename.Ptr(), filename.Size(), "%s.%d", + flags()->profile_memory, GetPid()); + fd_t fd = internal_open(filename.Ptr(), true); + if (fd == kInvalidFd) { + Printf("Failed to open memory profile file '%s'\n", &filename[0]); + Die(); + } + internal_start_thread(&MemoryProfileThread, (void*)(uptr)fd); +} + void Initialize(ThreadState *thr) { // Thread safe because done before all threads exist. static bool is_initialized = false; @@ -97,6 +155,7 @@ void Initialize(ThreadState *thr) { ctx->dead_list_tail = 0; InitializeFlags(&ctx->flags, env); InitializeSuppressions(); + InitializeMemoryProfile(); if (ctx->flags.verbosity) Printf("***** Running under ThreadSanitizer v2 (pid=%d) *****\n", GetPid()); diff --git a/compiler-rt/lib/tsan/rtl/tsan_sync.cc b/compiler-rt/lib/tsan/rtl/tsan_sync.cc index 0b31ab9ceca7..2910ead10ddc 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_sync.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_sync.cc @@ -107,6 +107,26 @@ SyncVar* SyncTab::GetAndRemove(ThreadState *thr, uptr pc, uptr addr) { return res; } +uptr SyncVar::GetMemoryConsumption() { + return sizeof(*this) + + clock.size() * sizeof(u64) + + read_clock.size() * sizeof(u64) + + creation_stack.Size() * sizeof(uptr); +} + +uptr SyncTab::GetMemoryConsumption(uptr *nsync) { + uptr mem = 0; + for (int i = 0; i < kPartCount; i++) { + Part *p = &tab_[i]; + Lock l(&p->mtx); + for (SyncVar *s = p->val; s; s = s->next) { + *nsync += 1; + mem += s->GetMemoryConsumption(); + } + } + return mem; +} + int SyncTab::PartIdx(uptr addr) { return (addr >> 3) % kPartCount; } diff --git a/compiler-rt/lib/tsan/rtl/tsan_sync.h b/compiler-rt/lib/tsan/rtl/tsan_sync.h index 45251f02ef70..516e46b66e22 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_sync.h +++ b/compiler-rt/lib/tsan/rtl/tsan_sync.h @@ -52,14 +52,16 @@ struct SyncVar { Mutex mtx; const uptr addr; SyncClock clock; - StackTrace creation_stack; SyncClock read_clock; // Used for rw mutexes only. + StackTrace creation_stack; int owner_tid; // Set only by exclusive owners. int recursion; bool is_rw; bool is_recursive; bool is_broken; SyncVar *next; // In SyncTab hashtable. + + uptr GetMemoryConsumption(); }; class SyncTab { @@ -74,6 +76,8 @@ class SyncTab { // If the SyncVar does not exist, returns 0. SyncVar* GetAndRemove(ThreadState *thr, uptr pc, uptr addr); + uptr GetMemoryConsumption(uptr *nsync); + private: struct Part { Mutex mtx;