forked from OSchip/llvm-project
[ASan] Add print_full_thread_history runtime option (on by default) that prints all full thread creation paths for threads involved in ASan error report
llvm-svn: 163200
This commit is contained in:
parent
2fa38f8ce0
commit
c402cb62fd
|
@ -609,14 +609,14 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
|
|||
alloc_thread->tid());
|
||||
|
||||
PrintStack(&alloc_stack);
|
||||
t->summary()->Announce();
|
||||
free_thread->Announce();
|
||||
alloc_thread->Announce();
|
||||
DescribeThread(t->summary());
|
||||
DescribeThread(free_thread);
|
||||
DescribeThread(alloc_thread);
|
||||
} else {
|
||||
Printf("allocated by thread T%d here:\n", alloc_thread->tid());
|
||||
PrintStack(&alloc_stack);
|
||||
t->summary()->Announce();
|
||||
alloc_thread->Announce();
|
||||
DescribeThread(t->summary());
|
||||
DescribeThread(alloc_thread);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,6 +89,10 @@ struct Flags {
|
|||
bool allow_reexec;
|
||||
// Strips this prefix from file paths in error reports.
|
||||
const char *strip_path_prefix;
|
||||
// If set, prints not only thread creation stacks for threads in error report,
|
||||
// but also thread creation stacks for threads that created those threads,
|
||||
// etc. up to main thread.
|
||||
bool print_full_thread_history;
|
||||
};
|
||||
|
||||
Flags *flags();
|
||||
|
|
|
@ -184,7 +184,7 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
|||
Printf("HINT: this may be a false positive if your program uses "
|
||||
"some custom stack unwind mechanism\n"
|
||||
" (longjmp and C++ exceptions *are* supported)\n");
|
||||
t->summary()->Announce();
|
||||
DescribeThread(t->summary());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -201,6 +201,26 @@ void DescribeAddress(uptr addr, uptr access_size) {
|
|||
DescribeHeapAddress(addr, access_size);
|
||||
}
|
||||
|
||||
// ------------------- Thread description -------------------- {{{1
|
||||
|
||||
void DescribeThread(AsanThreadSummary *summary) {
|
||||
CHECK(summary);
|
||||
// No need to announce the main thread.
|
||||
if (summary->tid() == 0 || summary->announced()) {
|
||||
return;
|
||||
}
|
||||
summary->set_announced(true);
|
||||
Printf("Thread T%d created by T%d here:\n",
|
||||
summary->tid(), summary->parent_tid());
|
||||
PrintStack(summary->stack());
|
||||
// Recursively described parent thread if needed.
|
||||
if (flags()->print_full_thread_history) {
|
||||
AsanThreadSummary *parent_summary =
|
||||
asanThreadRegistry().FindByTid(summary->parent_tid());
|
||||
DescribeThread(parent_summary);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- Different kinds of reports ----------------- {{{1
|
||||
|
||||
// Use ScopedInErrorReport to run common actions just before and
|
||||
|
@ -238,7 +258,7 @@ class ScopedInErrorReport {
|
|||
// Make sure the current thread is announced.
|
||||
AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
|
||||
if (curr_thread) {
|
||||
curr_thread->summary()->Announce();
|
||||
DescribeThread(curr_thread->summary());
|
||||
}
|
||||
// Print memory stats.
|
||||
__asan_print_accumulated_stats();
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_internal.h"
|
||||
#include "asan_thread.h"
|
||||
#include "sanitizer/asan_interface.h"
|
||||
|
||||
namespace __asan {
|
||||
|
@ -27,6 +28,8 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size);
|
|||
// Determines memory type on its own.
|
||||
void DescribeAddress(uptr addr, uptr access_size);
|
||||
|
||||
void DescribeThread(AsanThreadSummary *summary);
|
||||
|
||||
// Different kinds of error reports.
|
||||
void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr);
|
||||
void NORETURN ReportDoubleFree(uptr addr, StackTrace *stack);
|
||||
|
|
|
@ -102,6 +102,7 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
|
|||
ParseFlag(str, &f->disable_core, "disable_core");
|
||||
ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix");
|
||||
ParseFlag(str, &f->allow_reexec, "allow_reexec");
|
||||
ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history");
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
@ -139,6 +140,7 @@ void InitializeFlags(Flags *f, const char *env) {
|
|||
f->disable_core = (__WORDSIZE == 64);
|
||||
f->strip_path_prefix = "";
|
||||
f->allow_reexec = true;
|
||||
f->print_full_thread_history = true;
|
||||
|
||||
// Override from user-specified string.
|
||||
ParseFlagsFromString(f, __asan_default_options());
|
||||
|
|
|
@ -40,16 +40,12 @@ class AsanThreadSummary {
|
|||
}
|
||||
thread_ = 0;
|
||||
}
|
||||
void Announce() {
|
||||
if (tid_ == 0) return; // no need to announce the main thread.
|
||||
if (!announced_) {
|
||||
announced_ = true;
|
||||
Printf("Thread T%d created by T%d here:\n", tid_, parent_tid_);
|
||||
PrintStack(&stack_);
|
||||
}
|
||||
}
|
||||
u32 tid() { return tid_; }
|
||||
void set_tid(u32 tid) { tid_ = tid; }
|
||||
u32 parent_tid() { return parent_tid_; }
|
||||
bool announced() { return announced_; }
|
||||
void set_announced(bool announced) { announced_ = announced; }
|
||||
StackTrace *stack() { return &stack_; }
|
||||
AsanThread *thread() { return thread_; }
|
||||
void set_thread(AsanThread *thread) { thread_ = thread; }
|
||||
static void TSDDtor(void *tsd);
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
int *x;
|
||||
|
||||
void *AllocThread(void *arg) {
|
||||
x = new int;
|
||||
*x = 42;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *FreeThread(void *arg) {
|
||||
delete x;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *AccessThread(void *arg) {
|
||||
*x = 43; // BOOM
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef void* (*callback_type)(void* arg);
|
||||
|
||||
void *RunnerThread(void *function) {
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, (callback_type)function, NULL);
|
||||
pthread_join(thread, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void RunThread(callback_type function) {
|
||||
pthread_t runner;
|
||||
pthread_create(&runner, NULL, RunnerThread, (void*)function);
|
||||
pthread_join(runner, NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
RunThread(AllocThread);
|
||||
RunThread(FreeThread);
|
||||
RunThread(AccessThread);
|
||||
return (x != 0);
|
||||
}
|
||||
|
||||
// CHECK: AddressSanitizer heap-use-after-free
|
||||
// CHECK: WRITE of size 4 at 0x{{.*}} thread T[[ACCESS_THREAD:[0-9]+]]
|
||||
// CHECK: freed by thread T[[FREE_THREAD:[0-9]+]] here:
|
||||
// CHECK: previously allocated by thread T[[ALLOC_THREAD:[0-9]+]] here:
|
||||
// CHECK: Thread T[[ACCESS_THREAD]] created by T[[ACCESS_RUNNER:[0-9]+]] here:
|
||||
// CHECK: Thread T[[ACCESS_RUNNER]] created by T0 here:
|
||||
// CHECK: Thread T[[FREE_THREAD]] created by T[[FREE_RUNNER:[0-9]+]] here:
|
||||
// CHECK: Thread T[[FREE_RUNNER]] created by T0 here:
|
||||
// CHECK: Thread T[[ALLOC_THREAD]] created by T[[ALLOC_RUNNER:[0-9]+]] here:
|
||||
// CHECK: Thread T[[ALLOC_RUNNER]] created by T0 here:
|
Loading…
Reference in New Issue