[sanitizer] Add a 'print_module_map' flag which prints modules with UUIDs on Darwin

This patch add a new sanitizer flag, print_module_map, which enables printing a module map when the process exits, or after each report (for TSan). The output format is very similar to what Crash Reporter produces on Darwin (e.g. the format of module UUIDs). This enables users to use the existing symbol servers to offline symbolicate and aggregate reports.

Differential Revision: https://reviews.llvm.org/D27400

llvm-svn: 291277
This commit is contained in:
Kuba Mracek 2017-01-06 20:57:47 +00:00
parent a7276feed7
commit b6c6eaf226
12 changed files with 123 additions and 11 deletions

View File

@ -179,6 +179,8 @@ class ScopedInErrorReport {
if (common_flags()->print_cmdline)
PrintCmdline();
if (common_flags()->print_module_map == 2) PrintModuleMap();
// Copy the message buffer so that we could start logging without holding a
// lock that gets aquired during printing.
InternalScopedBuffer<char> buffer_copy(kErrorMessageBufferSize);

View File

@ -46,6 +46,7 @@ static void AsanDie() {
// Don't die twice - run a busy loop.
while (1) { }
}
if (common_flags()->print_module_map >= 1) PrintModuleMap();
if (flags()->sleep_before_dying) {
Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying);
SleepForSeconds(flags()->sleep_before_dying);

View File

@ -270,6 +270,8 @@ void LoadedModule::set(const char *module_name, uptr base_address,
void LoadedModule::clear() {
InternalFree(full_name_);
base_address_ = 0;
max_executable_address_ = 0;
full_name_ = nullptr;
arch_ = kModuleArchUnknown;
internal_memset(uuid_, 0, kModuleUUIDSize);
@ -285,6 +287,8 @@ void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
void *mem = InternalAlloc(sizeof(AddressRange));
AddressRange *r = new(mem) AddressRange(beg, end, executable);
ranges_.push_back(r);
if (executable && end > max_executable_address_)
max_executable_address_ = end;
}
bool LoadedModule::containsAddress(uptr address) const {

View File

@ -283,6 +283,7 @@ void UpdateProcessName();
void CacheBinaryName();
void DisableCoreDumperIfNecessary();
void DumpProcessMap();
void PrintModuleMap();
bool FileExists(const char *filename);
const char *GetEnv(const char *name);
bool SetEnv(const char *name, const char *value);
@ -665,6 +666,32 @@ enum ModuleArch {
kModuleArchARM64
};
// When adding a new architecture, don't forget to also update
// script/asan_symbolize.py and sanitizer_symbolizer_libcdep.cc.
inline const char *ModuleArchToString(ModuleArch arch) {
switch (arch) {
case kModuleArchUnknown:
return "";
case kModuleArchI386:
return "i386";
case kModuleArchX86_64:
return "x86_64";
case kModuleArchX86_64H:
return "x86_64h";
case kModuleArchARMV6:
return "armv6";
case kModuleArchARMV7:
return "armv7";
case kModuleArchARMV7S:
return "armv7s";
case kModuleArchARMV7K:
return "armv7k";
case kModuleArchARM64:
return "arm64";
}
CHECK(0 && "Invalid module arch");
}
const uptr kModuleUUIDSize = 16;
// Represents a binary loaded into virtual memory (e.g. this can be an
@ -674,6 +701,7 @@ class LoadedModule {
LoadedModule()
: full_name_(nullptr),
base_address_(0),
max_executable_address_(0),
arch_(kModuleArchUnknown),
instrumented_(false) {
internal_memset(uuid_, 0, kModuleUUIDSize);
@ -688,6 +716,7 @@ class LoadedModule {
const char *full_name() const { return full_name_; }
uptr base_address() const { return base_address_; }
uptr max_executable_address() const { return max_executable_address_; }
ModuleArch arch() const { return arch_; }
const u8 *uuid() const { return uuid_; }
bool instrumented() const { return instrumented_; }
@ -707,6 +736,7 @@ class LoadedModule {
private:
char *full_name_; // Owned.
uptr base_address_;
uptr max_executable_address_;
ModuleArch arch_;
u8 uuid_[kModuleUUIDSize];
bool instrumented_;

View File

@ -74,6 +74,9 @@ COMMON_FLAG(bool, allocator_may_return_null, false,
COMMON_FLAG(bool, print_summary, true,
"If false, disable printing error summaries in addition to error "
"reports.")
COMMON_FLAG(int, print_module_map, 0,
"OS X only. 0 = don't print, 1 = print only once before process "
"exits, 2 = print after each report.")
COMMON_FLAG(bool, check_printf, true, "Check printf arguments.")
COMMON_FLAG(bool, handle_segv, true,
"If set, registers the tool's custom SIGSEGV/SIGBUS handler.")

View File

@ -1393,6 +1393,8 @@ void MaybeReexec() {
// No need to re-exec on Linux.
}
void PrintModuleMap() { }
uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding) {
UNREACHABLE("FindAvailableMemoryRange is not available");
return 0;

View File

@ -854,6 +854,36 @@ void SignalContext::DumpAllRegisters(void *context) {
# undef DUMPREG
}
static inline bool CompareBaseAddress(const LoadedModule &a,
const LoadedModule &b) {
return a.base_address() < b.base_address();
}
void FormatUUID(char *out, uptr size, const u8 *uuid) {
internal_snprintf(out, size,
"<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-"
"%02X%02X%02X%02X%02X%02X>",
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
uuid[12], uuid[13], uuid[14], uuid[15]);
}
void PrintModuleMap() {
Printf("Process module map:\n");
MemoryMappingLayout memory_mapping(false);
InternalMmapVector<LoadedModule> modules(/*initial_capacity*/ 128);
memory_mapping.DumpListOfModules(&modules);
InternalSort(&modules, modules.size(), CompareBaseAddress);
for (uptr i = 0; i < modules.size(); ++i) {
char uuid_str[128];
FormatUUID(uuid_str, sizeof(uuid_str), modules[i].uuid());
Printf("0x%zx-0x%zx %s (%s) %s\n", modules[i].base_address(),
modules[i].max_executable_address(), modules[i].full_name(),
ModuleArchToString(modules[i].arch()), uuid_str);
}
Printf("End of module map.\n");
}
} // namespace __sanitizer
#endif // SANITIZER_MAC

View File

@ -43,7 +43,7 @@ static int AppendChar(char **buff, const char *buff_end, char c) {
// on the value of |pad_with_zero|.
static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,
u8 base, u8 minimal_num_length, bool pad_with_zero,
bool negative) {
bool negative, bool uppercase) {
uptr const kMaxLen = 30;
RAW_CHECK(base == 10 || base == 16);
RAW_CHECK(base == 10 || !negative);
@ -76,23 +76,25 @@ static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,
if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-');
for (; pos >= 0; pos--) {
char digit = static_cast<char>(num_buffer[pos]);
result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit
: 'a' + digit - 10);
digit = (digit < 10) ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10;
result += AppendChar(buff, buff_end, digit);
}
return result;
}
static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base,
u8 minimal_num_length, bool pad_with_zero) {
u8 minimal_num_length, bool pad_with_zero,
bool uppercase) {
return AppendNumber(buff, buff_end, num, base, minimal_num_length,
pad_with_zero, false /* negative */);
pad_with_zero, false /* negative */, uppercase);
}
static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num,
u8 minimal_num_length, bool pad_with_zero) {
bool negative = (num < 0);
return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10,
minimal_num_length, pad_with_zero, negative);
minimal_num_length, pad_with_zero, negative,
false /* uppercase */);
}
static int AppendString(char **buff, const char *buff_end, int precision,
@ -112,14 +114,16 @@ static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
int result = 0;
result += AppendString(buff, buff_end, -1, "0x");
result += AppendUnsigned(buff, buff_end, ptr_value, 16,
SANITIZER_POINTER_FORMAT_LENGTH, true);
SANITIZER_POINTER_FORMAT_LENGTH,
true /* pad_with_zero */, false /* uppercase */);
return result;
}
int VSNPrintf(char *buff, int buff_length,
const char *format, va_list args) {
static const char *kPrintfFormatsHelp =
"Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %(\\.\\*)?s; %c\n";
"Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; %(\\.\\*)?s; "
"%c\n";
RAW_CHECK(format);
RAW_CHECK(buff_length > 0);
const char *buff_end = &buff[buff_length - 1];
@ -164,12 +168,14 @@ int VSNPrintf(char *buff, int buff_length,
break;
}
case 'u':
case 'x': {
case 'x':
case 'X': {
uval = have_ll ? va_arg(args, u64)
: have_z ? va_arg(args, uptr)
: va_arg(args, unsigned);
result += AppendUnsigned(&buff, buff_end, uval,
(*cur == 'u') ? 10 : 16, width, pad_with_zero);
bool uppercase = (*cur == 'X');
result += AppendUnsigned(&buff, buff_end, uval, (*cur == 'u') ? 10 : 16,
width, pad_with_zero, uppercase);
break;
}
case 'p': {

View File

@ -388,6 +388,8 @@ void DumpProcessMap() {
}
#endif
void PrintModuleMap() { }
void DisableCoreDumperIfNecessary() {
// Do nothing.
}

View File

@ -358,6 +358,8 @@ void PrintReport(const ReportDesc *rep) {
ReportErrorSummary(rep_typ_str, frame->info);
}
if (common_flags()->print_module_map == 2) PrintModuleMap();
Printf("==================\n");
}

View File

@ -404,6 +404,8 @@ void Initialize(ThreadState *thr) {
int Finalize(ThreadState *thr) {
bool failed = false;
if (common_flags()->print_module_map == 1) PrintModuleMap();
if (flags()->atexit_sleep_ms > 0 && ThreadCount(thr) > 1)
SleepForMillis(flags()->atexit_sleep_ms);

View File

@ -0,0 +1,28 @@
// RUN: %clangxx_asan %s -o %t
// RUN: %env_asan_opts=print_module_map=1 not %run %t 2>&1 | FileCheck %s
// RUN: %env_asan_opts=print_module_map=2 not %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan %s -o %t -fsanitize-recover=address
// RUN: %env_asan_opts=print_module_map=2:halt_on_error=0 %run %t 2>&1 | FileCheck %s
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char buf[256];
snprintf(buf, sizeof(buf), "otool -l %s", argv[0]);
system(buf);
// CHECK: cmd LC_UUID
// CHECK-NEXT: cmdsize 24
// CHECK-NEXT: uuid [[UUID:[0-9A-F-]{36}]]
char *x = (char*)malloc(10 * sizeof(char));
free(x);
char mybuf[10];
memcpy(mybuf, x, 10);
// CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
// CHECK: Process module map:
// CHECK: uuid.cc.tmp {{.*}} <[[UUID]]>
fprintf(stderr, "Done.\n");
}