[Sanitizer] Teach external symbolizer to work with addr2line if llvm-symbolizer is unavailable. Allow this mode in TSan.

llvm-svn: 201218
This commit is contained in:
Alexey Samsonov 2014-02-12 08:29:42 +00:00
parent c34a997669
commit 7304b4201f
5 changed files with 120 additions and 16 deletions

View File

@ -23,6 +23,7 @@ CommonFlags common_flags_dont_use;
void SetCommonFlagsDefaults(CommonFlags *f) {
f->symbolize = true;
f->external_symbolizer_path = 0;
f->allow_addr2line = false;
f->strip_path_prefix = "";
f->fast_unwind_on_fatal = false;
f->fast_unwind_on_malloc = true;
@ -44,6 +45,7 @@ void SetCommonFlagsDefaults(CommonFlags *f) {
void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
ParseFlag(str, &f->symbolize, "symbolize");
ParseFlag(str, &f->external_symbolizer_path, "external_symbolizer_path");
ParseFlag(str, &f->allow_addr2line, "allow_addr2line");
ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix");
ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal");
ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc");
@ -63,8 +65,6 @@ void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
// Do a sanity check for certain flags.
if (f->malloc_context_size < 1)
f->malloc_context_size = 1;
if (!f->symbolize)
f->external_symbolizer_path = "";
}
static bool GetFlagValue(const char *env, const char *name,

View File

@ -30,6 +30,10 @@ struct CommonFlags {
// in PATH. If it is empty (or if "symbolize" is false), external symbolizer
// will not be started.
const char *external_symbolizer_path;
// If set, allows online symbolizer to run addr2line binary to symbolize
// stack traces (addr2line will only be used if llvm-symbolizer binary is not
// available.
bool allow_addr2line;
// Strips this prefix from file paths in error reports.
const char *strip_path_prefix;
// Use fast (frame-pointer-based) unwinder on fatal errors (if available).

View File

@ -16,6 +16,7 @@
#if SANITIZER_POSIX
#include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
#include "sanitizer_internal_defs.h"
#include "sanitizer_linux.h"
#include "sanitizer_placement_new.h"
@ -93,10 +94,20 @@ static const char *ExtractUptr(const char *str, const char *delims,
return ret;
}
class ExternalSymbolizerInterface {
public:
// Can't declare pure virtual functions in sanitizer runtimes:
// __cxa_pure_virtual might be unavailable.
virtual char *SendCommand(bool is_data, const char *module_name,
uptr module_offset) {
UNIMPLEMENTED();
}
};
// SymbolizerProcess encapsulates communication between the tool and
// external symbolizer program, running in a different subprocess.
// SymbolizerProcess may not be used from two threads simultaneously.
class SymbolizerProcess {
class SymbolizerProcess : public ExternalSymbolizerInterface {
public:
explicit SymbolizerProcess(const char *path)
: path_(path),
@ -153,7 +164,7 @@ class SymbolizerProcess {
uptr read_len = 0;
while (true) {
uptr just_read = internal_read(input_fd_, buffer + read_len,
max_length - read_len);
max_length - read_len - 1);
// We can't read 0 bytes, as we don't expect external symbolizer to close
// its stdout.
if (just_read == 0 || just_read == (uptr)-1) {
@ -164,6 +175,7 @@ class SymbolizerProcess {
if (ReachedEndOfOutput(buffer, read_len))
break;
}
buffer[read_len] = '\0';
return true;
}
@ -337,6 +349,74 @@ class LLVMSymbolizerProcess : public SymbolizerProcess {
}
};
class Addr2LineProcess : public SymbolizerProcess {
public:
Addr2LineProcess(const char *path, const char *module_name)
: SymbolizerProcess(path), module_name_(internal_strdup(module_name)) {}
const char *module_name() const { return module_name_; }
private:
bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
const char *module_name, uptr module_offset) const {
if (is_data)
return false;
CHECK_EQ(0, internal_strcmp(module_name, module_name_));
internal_snprintf(buffer, max_length, "0x%zx\n", module_offset);
return true;
}
bool ReachedEndOfOutput(const char *buffer, uptr length) const {
// Output should consist of two lines.
int num_lines = 0;
for (uptr i = 0; i < length; ++i) {
if (buffer[i] == '\n')
num_lines++;
if (num_lines >= 2)
return true;
}
return false;
}
void ExecuteWithDefaultArgs(const char *path_to_binary) const {
execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0);
}
const char *module_name_; // Owned, leaked.
};
class Addr2LinePool : public ExternalSymbolizerInterface {
public:
explicit Addr2LinePool(const char *addr2line_path,
LowLevelAllocator *allocator)
: addr2line_path_(addr2line_path), allocator_(allocator),
addr2line_pool_(16) {}
char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
if (is_data)
return 0;
Addr2LineProcess *addr2line = 0;
for (uptr i = 0; i < addr2line_pool_.size(); ++i) {
if (0 ==
internal_strcmp(module_name, addr2line_pool_[i]->module_name())) {
addr2line = addr2line_pool_[i];
break;
}
}
if (!addr2line) {
addr2line =
new(*allocator_) Addr2LineProcess(addr2line_path_, module_name);
addr2line_pool_.push_back(addr2line);
}
return addr2line->SendCommand(is_data, module_name, module_offset);
}
private:
const char *addr2line_path_;
LowLevelAllocator *allocator_;
InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
};
#if SANITIZER_SUPPORTS_WEAK_HOOKS
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
@ -418,7 +498,7 @@ class InternalSymbolizer {
class POSIXSymbolizer : public Symbolizer {
public:
POSIXSymbolizer(SymbolizerProcess *external_symbolizer,
POSIXSymbolizer(ExternalSymbolizerInterface *external_symbolizer,
InternalSymbolizer *internal_symbolizer,
LibbacktraceSymbolizer *libbacktrace_symbolizer)
: Symbolizer(),
@ -630,27 +710,41 @@ class POSIXSymbolizer : public Symbolizer {
bool modules_fresh_;
BlockingMutex mu_;
SymbolizerProcess *external_symbolizer_; // Leaked.
InternalSymbolizer *const internal_symbolizer_; // Leaked.
LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked.
ExternalSymbolizerInterface *external_symbolizer_; // Leaked.
InternalSymbolizer *const internal_symbolizer_; // Leaked.
LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked.
};
Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) {
if (!common_flags()->symbolize) {
return new(symbolizer_allocator_) POSIXSymbolizer(0, 0, 0);
}
InternalSymbolizer* internal_symbolizer =
InternalSymbolizer::get(&symbolizer_allocator_);
SymbolizerProcess *external_symbolizer = 0;
ExternalSymbolizerInterface *external_symbolizer = 0;
LibbacktraceSymbolizer *libbacktrace_symbolizer = 0;
if (!internal_symbolizer) {
libbacktrace_symbolizer =
LibbacktraceSymbolizer::get(&symbolizer_allocator_);
if (!libbacktrace_symbolizer) {
// Find path to llvm-symbolizer if it's not provided.
if (!path_to_external)
path_to_external = FindPathToBinary("llvm-symbolizer");
if (path_to_external && path_to_external[0] != '\0')
external_symbolizer = new(symbolizer_allocator_)
LLVMSymbolizerProcess(path_to_external);
if (path_to_external && path_to_external[0] == '\0') {
// External symbolizer is explicitly disabled. Do nothing.
} else {
// Find path to llvm-symbolizer if it's not provided.
if (!path_to_external)
path_to_external = FindPathToBinary("llvm-symbolizer");
if (path_to_external) {
external_symbolizer = new(symbolizer_allocator_)
LLVMSymbolizerProcess(path_to_external);
} else if (common_flags()->allow_addr2line) {
// If llvm-symbolizer is not found, try to use addr2line.
if (const char *addr2line_path = FindPathToBinary("addr2line")) {
external_symbolizer = new(symbolizer_allocator_)
Addr2LinePool(addr2line_path, &symbolizer_allocator_);
}
}
}
}
}

View File

@ -92,6 +92,8 @@ void InitializeFlags(Flags *f, const char *env) {
f->die_after_fork = true;
SetCommonFlagsDefaults(f);
// Override some common flags defaults.
f->allow_addr2line = true;
// Let a frontend override.
ParseFlags(f, __tsan_default_options());

View File

@ -63,6 +63,7 @@ static const char *options1 =
" symbolize=0"
" external_symbolizer_path=asdfgh"
" allow_addr2line=true"
" strip_path_prefix=zxcvb"
" fast_unwind_on_fatal=0"
" fast_unwind_on_malloc=0"
@ -105,6 +106,7 @@ static const char *options2 =
" symbolize=true"
" external_symbolizer_path=cccccc"
" allow_addr2line=false"
" strip_path_prefix=ddddddd"
" fast_unwind_on_fatal=true"
" fast_unwind_on_malloc=true"
@ -146,7 +148,8 @@ void VerifyOptions1(Flags *f) {
EXPECT_EQ(f->die_after_fork, true);
EXPECT_EQ(f->symbolize, 0);
EXPECT_EQ(f->external_symbolizer_path, std::string(""));
EXPECT_EQ(f->external_symbolizer_path, std::string("asdfgh"));
EXPECT_EQ(f->allow_addr2line, true);
EXPECT_EQ(f->strip_path_prefix, std::string("zxcvb"));
EXPECT_EQ(f->fast_unwind_on_fatal, 0);
EXPECT_EQ(f->fast_unwind_on_malloc, 0);
@ -189,6 +192,7 @@ void VerifyOptions2(Flags *f) {
EXPECT_EQ(f->symbolize, true);
EXPECT_EQ(f->external_symbolizer_path, std::string("cccccc"));
EXPECT_EQ(f->allow_addr2line, false);
EXPECT_EQ(f->strip_path_prefix, std::string("ddddddd"));
EXPECT_EQ(f->fast_unwind_on_fatal, true);
EXPECT_EQ(f->fast_unwind_on_malloc, true);