Support inline functions symbolization in Addr2Line symbolizer.

Patch by Maxim Ostapenko!

Summary:
Right now, Addr2Line symbolizer in asan_symbolize.py doesn't support inline functions symbolization. This might be a useful feature for using ASan on embedded systems.

Test results:

$ cat test.c

static inline void FooBarBaz() {
  __sanitizer_print_stack_trace();
}

int main() {
  FooBarBaz();
  return 0;
}

$ clang  test.c -fsanitize=address -g -O2 -o test.x && ./test.x &> /tmp/test.log
$ ./projects/compiler-rt/lib/asan/scripts/asan_symbolize.py  -l /tmp/test.log
    #0 0x42095e in __sanitizer_print_stack_trace _asan_rtl_
    #1 0x4cec07 in FooBarBaz /home/max/build/llvm/asan/test.c:4
    #2 0x4cec07 in main /home/max/build/llvm/asan/test.c:8
    #3 0x7f89f0891ec4 in __libc_start_main /build/buildd/eglibc-2.19/csu/libc-start.c:287

Reviewers: glider, samsonov

Subscribers: jevinskie, llvm-commits, ygribov

Differential Revision: http://reviews.llvm.org/D12153

llvm-svn: 247642
This commit is contained in:
Alexey Samsonov 2015-09-14 23:56:42 +00:00
parent 6c6eab162a
commit 1b76da6a6c
3 changed files with 62 additions and 26 deletions

View File

@ -135,12 +135,13 @@ class Addr2LineSymbolizer(Symbolizer):
super(Addr2LineSymbolizer, self).__init__()
self.binary = binary
self.pipe = self.open_addr2line()
self.output_terminator = -1
def open_addr2line(self):
addr2line_tool = 'addr2line'
if binutils_prefix:
addr2line_tool = binutils_prefix + addr2line_tool
cmd = [addr2line_tool, '-f']
cmd = [addr2line_tool, '-fi']
if demangle:
cmd += ['--demangle']
cmd += ['-e', self.binary]
@ -153,16 +154,23 @@ class Addr2LineSymbolizer(Symbolizer):
"""Overrides Symbolizer.symbolize."""
if self.binary != binary:
return None
lines = []
try:
print >> self.pipe.stdin, offset
function_name = self.pipe.stdout.readline().rstrip()
file_name = self.pipe.stdout.readline().rstrip()
print >> self.pipe.stdin, self.output_terminator
is_first_frame = True
while True:
function_name = self.pipe.stdout.readline().rstrip()
file_name = self.pipe.stdout.readline().rstrip()
if is_first_frame:
is_first_frame = False
elif function_name == '??':
assert file_name == '??:0'
break
lines.append((function_name, file_name));
except Exception:
function_name = ''
file_name = ''
file_name = fix_filename(file_name)
return ['%s in %s %s' % (addr, function_name, file_name)]
lines.append(('??', '??:0'))
return ['%s in %s %s' % (addr, function, fix_filename(file)) for (function, file) in lines]
class UnbufferedLineConverter(object):
"""

View File

@ -88,10 +88,11 @@ class SymbolizerProcess {
UNIMPLEMENTED();
}
virtual bool ReadFromSymbolizer(char *buffer, uptr max_length);
private:
bool Restart();
const char *SendCommandImpl(const char *command);
bool ReadFromSymbolizer(char *buffer, uptr max_length);
bool WriteToSymbolizer(const char *buffer, uptr length);
bool StartSymbolizerSubprocess();

View File

@ -194,30 +194,54 @@ class Addr2LineProcess : public SymbolizerProcess {
const char *module_name() const { return module_name_; }
private:
bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
// 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 GetArgV(const char *path_to_binary,
const char *(&argv)[kArgVMax]) const override {
int i = 0;
argv[i++] = path_to_binary;
argv[i++] = "-Cfe";
argv[i++] = "-iCfe";
argv[i++] = module_name_;
argv[i++] = nullptr;
}
bool ReachedEndOfOutput(const char *buffer, uptr length) const override;
bool ReadFromSymbolizer(char *buffer, uptr max_length) override {
if (!SymbolizerProcess::ReadFromSymbolizer(buffer, max_length))
return false;
// We should cut out output_terminator_ at the end of given buffer,
// appended by addr2line to mark the end of its meaningful output.
// We cannot scan buffer from it's beginning, because it is legal for it
// to start with output_terminator_ in case given offset is invalid. So,
// scanning from second character.
char *garbage = internal_strstr(buffer + 1, output_terminator_);
// This should never be NULL since buffer must end up with
// output_terminator_.
CHECK(garbage);
// Trim the buffer.
garbage[0] = '\0';
return true;
}
const char *module_name_; // Owned, leaked.
static const char output_terminator_[];
};
const char Addr2LineProcess::output_terminator_[] = "??\n??:0\n";
bool Addr2LineProcess::ReachedEndOfOutput(const char *buffer,
uptr length) const {
const size_t kTerminatorLen = sizeof(output_terminator_) - 1;
// Skip, if we read just kTerminatorLen bytes, because Addr2Line output
// should consist at least of two pairs of lines:
// 1. First one, corresponding to given offset to be symbolized
// (may be equal to output_terminator_, if offset is not valid).
// 2. Second one for output_terminator_, itself to mark the end of output.
if (length <= kTerminatorLen) return false;
// Addr2Line output should end up with output_terminator_.
return !internal_memcmp(buffer + length - kTerminatorLen,
output_terminator_, kTerminatorLen);
}
class Addr2LinePool : public SymbolizerTool {
public:
explicit Addr2LinePool(const char *addr2line_path,
@ -254,15 +278,18 @@ class Addr2LinePool : public SymbolizerTool {
addr2line_pool_.push_back(addr2line);
}
CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name()));
char buffer_[kBufferSize];
internal_snprintf(buffer_, kBufferSize, "0x%zx\n", module_offset);
return addr2line->SendCommand(buffer_);
char buffer[kBufferSize];
internal_snprintf(buffer, kBufferSize, "0x%zx\n0x%zx\n",
module_offset, dummy_address_);
return addr2line->SendCommand(buffer);
}
static const uptr kBufferSize = 32;
static const uptr kBufferSize = 64;
const char *addr2line_path_;
LowLevelAllocator *allocator_;
InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
static const uptr dummy_address_ =
FIRST_32_SECOND_64(UINT32_MAX, UINT64_MAX);
};
#if SANITIZER_SUPPORTS_WEAK_HOOKS