forked from OSchip/llvm-project
parent
d45982cb00
commit
15710c9220
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -163,7 +163,7 @@ class Backoff {
|
|||
if (iter_++ < kActiveSpinIters)
|
||||
proc_yield(kActiveSpinCnt);
|
||||
else
|
||||
sched_yield();
|
||||
internal_yield();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue