Fix a corner case with the handling of noreturn functions.

If a noreturn function was the last function in a section,
we wouldn't correctly back up the saved-pc value into the
correct section leading to us showing the wrong function in
the backtrace.

Also add a backtrace test with an attempt to elicit this 
particular layout.  It happens to work out with clang -Os
but other compilers may not quite get the same layout I'm
getting at that opt setting.  We'll still be exercising the
basic noreturn handling in the unwinder even if we don't get
one function at the very end of a section.

<rdar://problem/16051613> 

llvm-svn: 221575
This commit is contained in:
Jason Molenda 2014-11-08 05:38:17 +00:00
parent 13307f5f24
commit cf29675d95
5 changed files with 200 additions and 9 deletions

View File

@ -172,6 +172,21 @@ RegisterContextLLDB::InitializeZerothFrame()
m_sym_ctx_valid = true; m_sym_ctx_valid = true;
} }
if (m_sym_ctx.symbol)
{
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", symbol name is '%s'",
current_pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString());
}
else if (m_sym_ctx.function)
{
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", function name is '%s'",
current_pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.function->GetName().AsCString());
}
else
{
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", no symbol/function name is known.", current_pc);
}
AddressRange addr_range; AddressRange addr_range;
m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range); m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range);
@ -294,12 +309,12 @@ RegisterContextLLDB::InitializeNonZerothFrame()
if (log) if (log)
{ {
UnwindLogMsg ("pc = 0x%16.16" PRIx64, pc); UnwindLogMsg ("pc = 0x%" PRIx64, pc);
addr_t reg_val; addr_t reg_val;
if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val)) if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val))
UnwindLogMsg ("fp = 0x%16.16" PRIx64, reg_val); UnwindLogMsg ("fp = 0x%" PRIx64, reg_val);
if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val)) if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val))
UnwindLogMsg ("sp = 0x%16.16" PRIx64, reg_val); UnwindLogMsg ("sp = 0x%" PRIx64, reg_val);
} }
// A pc of 0x0 means it's the end of the stack crawl // A pc of 0x0 means it's the end of the stack crawl
@ -442,6 +457,21 @@ RegisterContextLLDB::InitializeNonZerothFrame()
m_sym_ctx_valid = true; m_sym_ctx_valid = true;
} }
if (m_sym_ctx.symbol)
{
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", symbol name is '%s'",
pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString());
}
else if (m_sym_ctx.function)
{
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", function name is '%s'",
pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.function->GetName().AsCString());
}
else
{
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", no symbol/function name is known.", pc);
}
AddressRange addr_range; AddressRange addr_range;
if (!m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range)) if (!m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range))
{ {
@ -472,17 +502,21 @@ RegisterContextLLDB::InitializeNonZerothFrame()
// to the ABI plugin and consult that. // to the ABI plugin and consult that.
if (decr_pc_and_recompute_addr_range) if (decr_pc_and_recompute_addr_range)
{ {
Address temporary_pc(m_current_pc); UnwindLogMsg ("Backing up the pc value of 0x%" PRIx64 " by 1 and re-doing symbol lookup; old symbol was %s",
temporary_pc.SetOffset(m_current_pc.GetOffset() - 1); pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString());
m_sym_ctx.Clear(false); Address temporary_pc;
temporary_pc.SetLoadAddress (pc - 1, &process->GetTarget());
m_sym_ctx.Clear (false);
m_sym_ctx_valid = false; m_sym_ctx_valid = false;
uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol;
if (pc_module_sp->ResolveSymbolContextForAddress (temporary_pc, resolve_scope, m_sym_ctx) & resolve_scope) ModuleSP temporary_module_sp = temporary_pc.GetModule();
if (temporary_module_sp->ResolveSymbolContextForAddress (temporary_pc, resolve_scope, m_sym_ctx) & resolve_scope)
{ {
if (m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range)) if (m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range))
m_sym_ctx_valid = true; m_sym_ctx_valid = true;
} }
UnwindLogMsg ("Symbol is now %s", m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString());
} }
// If we were able to find a symbol/function, set addr_range_ptr to the bounds of that symbol/function. // If we were able to find a symbol/function, set addr_range_ptr to the bounds of that symbol/function.
@ -490,13 +524,15 @@ RegisterContextLLDB::InitializeNonZerothFrame()
if (addr_range.GetBaseAddress().IsValid()) if (addr_range.GetBaseAddress().IsValid())
{ {
m_start_pc = addr_range.GetBaseAddress(); m_start_pc = addr_range.GetBaseAddress();
m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset(); m_current_offset = pc - m_start_pc.GetLoadAddress (&process->GetTarget());
m_current_offset_backed_up_one = m_current_offset; m_current_offset_backed_up_one = m_current_offset;
if (decr_pc_and_recompute_addr_range && m_current_offset_backed_up_one > 0) if (decr_pc_and_recompute_addr_range && m_current_offset_backed_up_one > 0)
{ {
m_current_offset_backed_up_one--; m_current_offset_backed_up_one--;
if (m_sym_ctx_valid) if (m_sym_ctx_valid)
m_current_pc.SetOffset(m_current_pc.GetOffset() - 1); {
m_current_pc.SetLoadAddress (pc - 1, &process->GetTarget());
}
} }
} }
else else

View File

@ -384,7 +384,31 @@ StackFrame::GetSymbolContext (uint32_t resolve_scope)
{ {
addr_t offset = lookup_addr.GetOffset(); addr_t offset = lookup_addr.GetOffset();
if (offset > 0) if (offset > 0)
{
lookup_addr.SetOffset(offset - 1); lookup_addr.SetOffset(offset - 1);
}
else
{
// lookup_addr is the start of a section. We need
// do the math on the actual load address and re-compute
// the section. We're working with a 'noreturn' function
// at the end of a section.
ThreadSP thread_sp (GetThread());
if (thread_sp)
{
TargetSP target_sp (thread_sp->CalculateTarget());
if (target_sp)
{
addr_t addr_minus_one = lookup_addr.GetLoadAddress(target_sp.get()) - 1;
lookup_addr.SetLoadAddress (addr_minus_one, target_sp.get());
}
else
{
lookup_addr.SetOffset(offset - 1);
}
}
}
} }

View File

@ -0,0 +1,7 @@
LEVEL = ../../../make
C_SOURCES := main.c
CFLAGS ?= -g -Os
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,87 @@
"""
Test that we can backtrace correctly with 'noreturn' functions on the stack
"""
import os, time
import unittest2
import lldb
from lldbtest import *
import lldbutil
class NoreturnUnwind(TestBase):
mydir = TestBase.compute_mydir(__file__)
@dsym_test
def test_with_dsym (self):
"""Test that we can backtrace correctly with 'noreturn' functions on the stack"""
self.buildDsym()
self.setTearDownCleanup()
self.noreturn_unwind_tests()
@dwarf_test
def test_with_dsym (self):
"""Test that we can backtrace correctly with 'noreturn' functions on the stack"""
self.buildDsym()
self.setTearDownCleanup()
self.noreturn_unwind_tests()
def noreturn_unwind_tests (self):
exe = os.path.join(os.getcwd(), "a.out")
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)
process = target.LaunchSimple (None, None, self.get_process_working_directory())
if not process:
self.fail("SBTarget.Launch() failed")
if process.GetState() != lldb.eStateStopped:
self.fail("Process should be in the 'stopped' state, "
"instead the actual state is: '%s'" %
lldbutil.state_type_to_str(process.GetState()))
thread = process.GetThreadAtIndex(0)
abort_frame_number = 0
for f in thread.frames:
if f.GetFunctionName() == "abort":
break
abort_frame_number = abort_frame_number + 1
if self.TraceOn():
print "Backtrace once we're stopped:"
for f in thread.frames:
print " %d %s" % (f.GetFrameID(), f.GetFunctionName())
# I'm going to assume that abort() ends up calling/invoking another
# function before halting the process. In which case if abort_frame_number
# equals 0, we didn't find abort() in the backtrace.
if abort_frame_number == 0:
self.fail("Unable to find abort() in backtrace.")
func_c_frame_number = abort_frame_number + 1
if thread.GetFrameAtIndex (func_c_frame_number).GetFunctionName() != "func_c":
self.fail("Did not find func_c() above abort().")
# This depends on whether we see the func_b inlined function in the backtrace
# or not. I'm not interested in testing that aspect of the backtrace here
# right now.
if thread.GetFrameAtIndex (func_c_frame_number + 1).GetFunctionName() == "func_b":
func_a_frame_number = func_c_frame_number + 2
else:
func_a_frame_number = func_c_frame_number + 1
if thread.GetFrameAtIndex (func_a_frame_number).GetFunctionName() != "func_a":
self.fail("Did not find func_a() above func_c().")
main_frame_number = func_a_frame_number + 1
if thread.GetFrameAtIndex (main_frame_number).GetFunctionName() != "main":
self.fail("Did not find main() above func_a().")
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -0,0 +1,37 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void func_a (void) __attribute__((noinline));
static void func_b (void) __attribute__((noreturn));
static void func_c (void) __attribute__((noinline));
static void
func_c (void)
{
abort ();
}
static void
func_b (void)
{
func_c ();
while (1)
;
}
static void
func_a (void)
{
func_b ();
}
int
main (int argc, char *argv[])
{
sleep (2);
func_a ();
return 0;
}