forked from OSchip/llvm-project
[asan] intercept prctl(PR_SET_NAME) and set the thread name. Output the thread names (if non-empty) in asan reports
llvm-svn: 169601
This commit is contained in:
parent
5f4ab30be0
commit
e7108227ca
|
@ -177,6 +177,23 @@ INTERCEPTOR(void, siglongjmp, void *env, int val) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#define PR_SET_NAME 15
|
||||
|
||||
INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5) {
|
||||
int res = REAL(prctl(option, arg2, arg3, arg4, arg5));
|
||||
if (option == PR_SET_NAME) {
|
||||
AsanThread *t = asanThreadRegistry().GetCurrent();
|
||||
if (t) {
|
||||
char buff[17];
|
||||
internal_strncpy(buff, (char*)arg2, 16);
|
||||
buff[16] = 0;
|
||||
t->summary()->set_name(buff);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#if ASAN_INTERCEPT___CXA_THROW
|
||||
INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
|
||||
CHECK(REAL(__cxa_throw));
|
||||
|
@ -720,6 +737,7 @@ void InitializeAsanInterceptors() {
|
|||
#if ASAN_INTERCEPT_SIGLONGJMP
|
||||
ASAN_INTERCEPT_FUNC(siglongjmp);
|
||||
#endif
|
||||
ASAN_INTERCEPT_FUNC(prctl);
|
||||
|
||||
// Intercept exception handling functions.
|
||||
#if ASAN_INTERCEPT___CXA_THROW
|
||||
|
|
|
@ -203,6 +203,25 @@ static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr,
|
|||
(void*)(chunk.Beg()), (void*)(chunk.End()));
|
||||
}
|
||||
|
||||
// Return " (thread_name) " or an empty string if the name is empty.
|
||||
const char *ThreadNameWithParenthesis(AsanThreadSummary *t, char buff[],
|
||||
uptr buff_len) {
|
||||
const char *name = t->name();
|
||||
if (*name == 0) return "";
|
||||
buff[0] = 0;
|
||||
internal_strncat(buff, " (", 3);
|
||||
internal_strncat(buff, name, buff_len - 4);
|
||||
internal_strncat(buff, ")", 2);
|
||||
return buff;
|
||||
}
|
||||
|
||||
const char *ThreadNameWithParenthesis(u32 tid, char buff[],
|
||||
uptr buff_len) {
|
||||
if (tid == kInvalidTid) return "";
|
||||
AsanThreadSummary *t = asanThreadRegistry().FindByTid(tid);
|
||||
return ThreadNameWithParenthesis(t, buff, buff_len);
|
||||
}
|
||||
|
||||
void DescribeHeapAddress(uptr addr, uptr access_size) {
|
||||
AsanChunkView chunk = FindHeapChunkByAddress(addr);
|
||||
if (!chunk.IsValid()) return;
|
||||
|
@ -214,20 +233,25 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
|
|||
chunk.GetAllocStack(&alloc_stack);
|
||||
AsanThread *t = asanThreadRegistry().GetCurrent();
|
||||
CHECK(t);
|
||||
char tname[128];
|
||||
if (chunk.FreeTid() != kInvalidTid) {
|
||||
AsanThreadSummary *free_thread =
|
||||
asanThreadRegistry().FindByTid(chunk.FreeTid());
|
||||
Printf("freed by thread T%d here:\n", free_thread->tid());
|
||||
Printf("freed by thread T%d%s here:\n", free_thread->tid(),
|
||||
ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)));
|
||||
StackTrace free_stack;
|
||||
chunk.GetFreeStack(&free_stack);
|
||||
PrintStack(&free_stack);
|
||||
Printf("previously allocated by thread T%d here:\n", alloc_thread->tid());
|
||||
Printf("previously allocated by thread T%d%s here:\n",
|
||||
alloc_thread->tid(),
|
||||
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)));
|
||||
PrintStack(&alloc_stack);
|
||||
DescribeThread(t->summary());
|
||||
DescribeThread(free_thread);
|
||||
DescribeThread(alloc_thread);
|
||||
} else {
|
||||
Printf("allocated by thread T%d here:\n", alloc_thread->tid());
|
||||
Printf("allocated by thread T%d%s here:\n", alloc_thread->tid(),
|
||||
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)));
|
||||
PrintStack(&alloc_stack);
|
||||
DescribeThread(t->summary());
|
||||
DescribeThread(alloc_thread);
|
||||
|
@ -256,8 +280,13 @@ void DescribeThread(AsanThreadSummary *summary) {
|
|||
return;
|
||||
}
|
||||
summary->set_announced(true);
|
||||
Printf("Thread T%d created by T%d here:\n",
|
||||
summary->tid(), summary->parent_tid());
|
||||
char tname[128];
|
||||
Printf("Thread T%d%s", summary->tid(),
|
||||
ThreadNameWithParenthesis(summary->tid(), tname, sizeof(tname)));
|
||||
Printf(" created by T%d%s here:\n",
|
||||
summary->parent_tid(),
|
||||
ThreadNameWithParenthesis(summary->parent_tid(),
|
||||
tname, sizeof(tname)));
|
||||
PrintStack(summary->stack());
|
||||
// Recursively described parent thread if needed.
|
||||
if (flags()->print_full_thread_history) {
|
||||
|
@ -471,9 +500,11 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp,
|
|||
bug_descr, (void*)addr, pc, bp, sp);
|
||||
|
||||
u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
|
||||
Printf("%s of size %zu at %p thread T%d\n",
|
||||
char tname[128];
|
||||
Printf("%s of size %zu at %p thread T%d%s\n",
|
||||
access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
|
||||
access_size, (void*)addr, curr_tid);
|
||||
access_size, (void*)addr, curr_tid,
|
||||
ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
|
||||
|
||||
GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp);
|
||||
PrintStack(&stack);
|
||||
|
|
|
@ -39,6 +39,7 @@ class AsanThreadSummary {
|
|||
internal_memcpy(&stack_, stack, sizeof(*stack));
|
||||
}
|
||||
thread_ = 0;
|
||||
name_[0] = 0;
|
||||
}
|
||||
u32 tid() { return tid_; }
|
||||
void set_tid(u32 tid) { tid_ = tid; }
|
||||
|
@ -49,6 +50,10 @@ class AsanThreadSummary {
|
|||
AsanThread *thread() { return thread_; }
|
||||
void set_thread(AsanThread *thread) { thread_ = thread; }
|
||||
static void TSDDtor(void *tsd);
|
||||
void set_name(const char *name) {
|
||||
internal_strncpy(name_, name, sizeof(name_) - 1);
|
||||
}
|
||||
const char *name() { return name_; }
|
||||
|
||||
private:
|
||||
u32 tid_;
|
||||
|
@ -56,8 +61,12 @@ class AsanThreadSummary {
|
|||
bool announced_;
|
||||
StackTrace stack_;
|
||||
AsanThread *thread_;
|
||||
char name_[128];
|
||||
};
|
||||
|
||||
// AsanThreadSummary objects are never freed, so we need many of them.
|
||||
COMPILER_CHECK(sizeof(AsanThreadSummary) <= 4094);
|
||||
|
||||
// AsanThread are stored in TSD and destroyed when the thread dies.
|
||||
class AsanThread {
|
||||
public:
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
#include <setjmp.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef __linux__
|
||||
# include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
@ -1619,19 +1623,28 @@ TEST(AddressSanitizer, DISABLED_MallocFreeUnwindAndSymbolizeTest) {
|
|||
"malloc_fff.*malloc_eee.*malloc_ddd");
|
||||
}
|
||||
|
||||
static void TryToSetThreadName(const char *name) {
|
||||
#ifdef __linux__
|
||||
prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void *ThreadedTestAlloc(void *a) {
|
||||
TryToSetThreadName("AllocThr");
|
||||
int **p = (int**)a;
|
||||
*p = new int;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ThreadedTestFree(void *a) {
|
||||
TryToSetThreadName("FreeThr");
|
||||
int **p = (int**)a;
|
||||
delete *p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ThreadedTestUse(void *a) {
|
||||
TryToSetThreadName("UseThr");
|
||||
int **p = (int**)a;
|
||||
**p = 1;
|
||||
return 0;
|
||||
|
@ -1656,6 +1669,22 @@ TEST(AddressSanitizer, ThreadedTest) {
|
|||
".*Thread T.*created");
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
TEST(AddressSanitizer, ThreadNamesTest) {
|
||||
// ThreadedTestSpawn();
|
||||
EXPECT_DEATH(ThreadedTestSpawn(),
|
||||
ASAN_PCRE_DOTALL
|
||||
"WRITE .*thread T3 .UseThr."
|
||||
".*freed by thread T2 .FreeThr. here:"
|
||||
".*previously allocated by thread T1 .AllocThr. here:"
|
||||
".*Thread T3 .UseThr. created by T0 here:"
|
||||
".*Thread T2 .FreeThr. created by T0 here:"
|
||||
".*Thread T1 .AllocThr. created by T0 here:"
|
||||
"");
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ASAN_NEEDS_SEGV
|
||||
TEST(AddressSanitizer, ShadowGapTest) {
|
||||
#if SANITIZER_WORDSIZE == 32
|
||||
|
|
Loading…
Reference in New Issue