diff --git a/compiler-rt/include/sanitizer/dfsan_interface.h b/compiler-rt/include/sanitizer/dfsan_interface.h --- a/compiler-rt/include/sanitizer/dfsan_interface.h +++ b/compiler-rt/include/sanitizer/dfsan_interface.h @@ -105,8 +105,8 @@ /// int len = dfsan_sprint_origin_trace(&var, nullptr, buf, sizeof(buf)); /// /// if (len < sizeof(buf)) { -/// ProcessOriginTrace(tmpbuf); -/// else { +/// ProcessOriginTrace(buf); +/// } else { /// char *tmpbuf = new char[len + 1]; /// dfsan_sprint_origin_trace(&var, nullptr, tmpbuf, len + 1); /// ProcessOriginTrace(tmpbuf); @@ -125,6 +125,19 @@ size_t dfsan_sprint_origin_trace(const void *addr, const char *description, char *out_buf, size_t out_buf_size); +/// Prints the stack trace leading to this call to a pre-allocated output +/// buffer. +/// +/// For usage examples, see dfsan_sprint_origin_trace. +/// +/// \param [out] out_buf The output buffer to write the results to. +/// \param out_buf_size The size of \p out_buf. +/// +/// \returns The number of symbols that should have been written to \p out_buf +/// (not including trailing null byte '\0'). Thus, the string is truncated iff +/// return value is not less than \p out_buf_size. +size_t dfsan_sprint_stack_trace(char *out_buf, size_t out_buf_size); + /// Retrieves the very first origin associated with the data at the given /// address. dfsan_origin dfsan_get_init_origin(const void *addr); diff --git a/compiler-rt/lib/dfsan/dfsan.cpp b/compiler-rt/lib/dfsan/dfsan.cpp --- a/compiler-rt/lib/dfsan/dfsan.cpp +++ b/compiler-rt/lib/dfsan/dfsan.cpp @@ -824,10 +824,6 @@ return origin_id; } -#define GET_FATAL_STACK_TRACE_PC_BP(pc, bp) \ - BufferedStackTrace stack; \ - stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal); - void __sanitizer::BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, void *context, bool request_fast, @@ -841,10 +837,19 @@ } extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_print_stack_trace() { - GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME()); + GET_CALLER_PC_BP; + GET_STORE_STACK_TRACE_PC_BP(pc, bp); stack.Print(); } +extern "C" SANITIZER_INTERFACE_ATTRIBUTE size_t +dfsan_sprint_stack_trace(char *out_buf, size_t out_buf_size) { + CHECK(out_buf); + GET_CALLER_PC_BP; + GET_STORE_STACK_TRACE_PC_BP(pc, bp); + return stack.PrintTo(out_buf, out_buf_size); +} + void Flags::SetDefaults() { #define DFSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; #include "dfsan_flags.inc" diff --git a/compiler-rt/lib/dfsan/done_abilist.txt b/compiler-rt/lib/dfsan/done_abilist.txt --- a/compiler-rt/lib/dfsan/done_abilist.txt +++ b/compiler-rt/lib/dfsan/done_abilist.txt @@ -32,6 +32,8 @@ fun:dfsan_print_origin_trace=discard fun:dfsan_sprint_origin_trace=uninstrumented fun:dfsan_sprint_origin_trace=discard +fun:dfsan_sprint_stack_trace=uninstrumented +fun:dfsan_sprint_stack_trace=discard fun:dfsan_get_origin=uninstrumented fun:dfsan_get_origin=custom fun:dfsan_get_init_origin=uninstrumented diff --git a/compiler-rt/test/dfsan/stack_trace.c b/compiler-rt/test/dfsan/stack_trace.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/dfsan/stack_trace.c @@ -0,0 +1,66 @@ +// RUN: %clang_dfsan -gmlt %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// +// REQUIRES: x86_64-target-arch + +#include +#include +#include +#include + +#define NOINLINE __attribute__((noinline)) + +NOINLINE size_t bar(int depth, char *buf, size_t len) { + if (!depth) { + return dfsan_sprint_stack_trace(buf, len); + } + + return bar(depth - 1, buf, len); +} + +NOINLINE size_t baz(int depth, char *buf, size_t len) { + return bar(depth, buf, len); +} + +int main(int argc, char *argv[]) { + char buf[3000]; + size_t length = dfsan_sprint_stack_trace(buf, sizeof(buf)); + assert(length < sizeof(buf)); + printf("==OUTPUT==\n%s==EOS==\n", buf); + + // CHECK: ==OUTPUT== + // CHECK: #0 {{.*}} in main [[FILEPATH:.*]]/stack_trace.c:[[# @LINE - 5 ]] + // CHECK: ==EOS== + + length = baz(8, buf, sizeof(buf)); + printf("==OUTPUT==\n%s==EOS==\n", buf); + + // CHECK: ==OUTPUT== + // CHECK: #0 {{.*}} in dfs$bar [[FILEPATH]]/stack_trace.c:15 + // CHECK-COUNT-8: #{{[1-9]+}} {{.*}} in dfs$bar [[FILEPATH]]/stack_trace.c:18 + // CHECK: #9 {{.*}} in dfs$baz [[FILEPATH]]/stack_trace.c:22 + // CHECK: #10 {{.*}} in main [[FILEPATH]]/stack_trace.c:[[# @LINE - 7 ]] + // CHECK: ==EOS== + + char tinybuf[8]; + size_t same_length = baz(8, tinybuf, sizeof(tinybuf)); + + printf("==TRUNCATED OUTPUT==\n%s==EOS==\n", tinybuf); + // CHECK: ==TRUNCATED OUTPUT== + // CHECK: #0 ==EOS== + + printf("Returned length: %zu\n", length); + printf("Actual length: %zu\n", strlen(buf)); + printf("Returned length with truncation: %zu\n", same_length); + + // CHECK: Returned length: [[#LEN:]] + // CHECK: Actual length: [[#LEN]] + // CHECK: Returned length with truncation: [[#LEN]] + + buf[0] = '\0'; + length = baz(8, buf, 0); + printf("Output=\"%s\"\n", buf); + printf("Returned length: %zu\n", length); + // CHECK: Output="" + // CHECK: Returned length: [[#LEN]] +}