forked from OSchip/llvm-project
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:
parent
13307f5f24
commit
cf29675d95
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
LEVEL = ../../../make
|
||||||
|
|
||||||
|
C_SOURCES := main.c
|
||||||
|
|
||||||
|
CFLAGS ?= -g -Os
|
||||||
|
|
||||||
|
include $(LEVEL)/Makefile.rules
|
|
@ -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()
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue