tsan: simple memory profiler

llvm-svn: 157248
This commit is contained in:
Dmitry Vyukov 2012-05-22 11:33:03 +00:00
parent d45982cb00
commit 15710c9220
9 changed files with 110 additions and 4 deletions

View File

@ -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,

View File

@ -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();

View File

@ -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

View File

@ -163,7 +163,7 @@ class Backoff {
if (iter_++ < kActiveSpinIters)
proc_yield(kActiveSpinCnt);
else
sched_yield();
internal_yield();
return true;
}

View File

@ -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;

View File

@ -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,

View File

@ -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<char> 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<char> 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());

View File

@ -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;
}

View File

@ -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;