Skip to content

Emit AT_call_return_pc as an address, refactor MD_loop update logic #583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions lldb/include/lldb/Symbol/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,7 @@ class CallEdge {
/// made the call.
lldb::addr_t GetReturnPCAddress(Function &caller, Target &target) const;

/// Like \ref GetReturnPCAddress, but returns an unslid function-local PC
/// offset.
/// Like \ref GetReturnPCAddress, but returns an unresolved file address.
lldb::addr_t GetUnresolvedReturnPCAddress() const { return return_pc; }

/// Get the call site parameters available at this call edge.
Expand All @@ -294,9 +293,8 @@ class CallEdge {
CallEdge(lldb::addr_t return_pc, CallSiteParameterArray &&parameters)
: return_pc(return_pc), parameters(std::move(parameters)) {}

/// An invalid address if this is a tail call. Otherwise, the function-local
/// PC offset. Adding this PC offset to the function's base load address
/// gives the return PC for the call.
/// An invalid address if this is a tail call. Otherwise, the return PC for
/// the call. Note that this is a file address which must be resolved.
lldb::addr_t return_pc;

CallSiteParameterArray parameters;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
LD_EXTRAS := -L. -l$(LIB_PREFIX)One -l$(LIB_PREFIX)Two
C_SOURCES := main.c
CFLAGS_EXTRAS := -g -O2 -glldb

include Makefile.rules

.PHONY:
a.out: lib_One lib_Two

lib_One: lib_Two

lib_%:
$(MAKE) VPATH=$(SRCDIR)/$* -I $(SRCDIR) -f $(SRCDIR)/$*.mk DSYMUTIL=$(DSYMUTIL)

clean::
$(MAKE) -f $(SRCDIR)/One.mk clean
$(MAKE) -f $(SRCDIR)/Two.mk clean
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
DYLIB_NAME := One
DYLIB_C_SOURCES := One.c
DYLIB_ONLY := YES
CFLAGS_EXTRAS := -g -O2 -glldb
LD_EXTRAS := -L. -lTwo

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "shared.h"

__attribute__((noinline))
static void helper_in_a() {
tail_called_in_b_from_a();
}

__attribute__((disable_tail_calls))
void tail_called_in_a_from_main() {
helper_in_a();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Test that backtraces can follow cross-DSO tail calls"""



import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class TestCrossDSOTailCalls(TestBase):

mydir = TestBase.compute_mydir(__file__)

def setUp(self):
TestBase.setUp(self)

@skipIf(compiler="clang", compiler_version=['<', '8.0'])
@skipIf(dwarf_version=['<', '4'])
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr26265")
def test_cross_dso_tail_calls(self):
self.build()
exe = self.getBuildArtifact("a.out")
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)

# Register our shared libraries for remote targets so they get
# automatically uploaded
environment = self.registerSharedLibrariesWithTarget(
target, ['One', 'Two'])

lldbutil.run_break_set_by_source_regexp(self, '// break here',
extra_options='-f Two.c')

process = target.LaunchSimple(
None, environment, self.get_process_working_directory())
self.assertTrue(process, PROCESS_IS_VALID)

# We should be stopped in the second dylib.
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)

# Debug helper:
# self.runCmd("log enable -f /tmp/lldb.log lldb step")
# self.runCmd("bt")

# Check that the backtrace is what we expect:
# frame #0: 0x000000010d5e5f94 libTwo.dylib`tail_called_in_b_from_b at Two.c:7:3 [opt]
# frame #1: 0x000000010d5e5fa0 libTwo.dylib`tail_called_in_b_from_a [opt] [artificial]
# frame #2: 0x000000010d5dcf80 libOne.dylib`helper_in_a [opt] [artificial]
# frame #3: 0x000000010d5dcf79 libOne.dylib`tail_called_in_a_from_main at One.c:10:3 [opt]
# frame #4: 0x000000010d5d3f80 a.out`helper [opt] [artificial]
# frame #5: 0x000000010d5d3f79 a.out`main at main.c:10:3 [opt]
expected_frames = [
("tail_called_in_b_from_b", False),
("tail_called_in_b_from_a", True),
("helper_in_a", True),
("tail_called_in_a_from_main", False),
("helper", True),
("main", False)
]
for idx, (name, is_artificial) in enumerate(expected_frames):
frame = thread.GetFrameAtIndex(idx)
self.assertTrue(name in frame.GetDisplayFunctionName())
self.assertEqual(frame.IsArtificial(), is_artificial)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DYLIB_NAME := Two
DYLIB_C_SOURCES := Two.c
DYLIB_ONLY := YES
CFLAGS_EXTRAS := -g -O2 -glldb

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "shared.h"

volatile int x;

__attribute__((noinline))
void tail_called_in_b_from_b() {
++x; // break here
}

void tail_called_in_b_from_a() {
tail_called_in_b_from_b();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "shared.h"

__attribute__((noinline))
static void helper() {
tail_called_in_a_from_main();
}

__attribute__((disable_tail_calls))
int main() {
helper();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
void tail_called_in_a_from_main();

void tail_called_in_b_from_a();
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
C_SOURCES := main.c One.c Two.c

CFLAGS_EXTRAS := -g -O2 -glldb
include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "shared.h"

__attribute__((noinline))
static void helper_in_a() {
tail_called_in_b_from_a();
}

__attribute__((disable_tail_calls))
void tail_called_in_a_from_main() {
helper_in_a();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Test that backtraces can follow cross-object tail calls"""



import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class TestCrossObjectTailCalls(TestBase):

mydir = TestBase.compute_mydir(__file__)

def setUp(self):
TestBase.setUp(self)

@skipIf(compiler="clang", compiler_version=['<', '8.0'])
@skipIf(dwarf_version=['<', '4'])
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr26265")
def test_cross_object_tail_calls(self):
self.build()
exe = self.getBuildArtifact("a.out")
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)

lldbutil.run_break_set_by_source_regexp(self, '// break here',
extra_options='-f Two.c')

process = target.LaunchSimple(
None, None, self.get_process_working_directory())
self.assertTrue(process, PROCESS_IS_VALID)

# We should be stopped in the second dylib.
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)

# Debug helper:
# self.runCmd("log enable -f /tmp/lldb.log lldb step")
# self.runCmd("bt")

# Check that the backtrace is what we expect:
# frame #0: 0x000000010be73f94 a.out`tail_called_in_b_from_b at Two.c:7:3 [opt]
# frame #1: 0x000000010be73fa0 a.out`tail_called_in_b_from_a at Two.c:8:1 [opt] [artificial]
# frame #2: 0x000000010be73f80 a.out`helper_in_a at One.c:11:1 [opt] [artificial]
# frame #3: 0x000000010be73f79 a.out`tail_called_in_a_from_main at One.c:10:3 [opt]
# frame #4: 0x000000010be73f60 a.out`helper at main.c:11:3 [opt] [artificial]
# frame #5: 0x000000010be73f59 a.out`main at main.c:10:3 [opt]
expected_frames = [
("tail_called_in_b_from_b", False),
("tail_called_in_b_from_a", True),
("helper_in_a", True),
("tail_called_in_a_from_main", False),
("helper", True),
("main", False)
]
for idx, (name, is_artificial) in enumerate(expected_frames):
frame = thread.GetFrameAtIndex(idx)
self.assertTrue(name in frame.GetDisplayFunctionName())
self.assertEqual(frame.IsArtificial(), is_artificial)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "shared.h"

volatile int x;

__attribute__((noinline))
void tail_called_in_b_from_b() {
++x; // break here
}

void tail_called_in_b_from_a() {
tail_called_in_b_from_b();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "shared.h"

__attribute__((noinline))
static void helper() {
tail_called_in_a_from_main();
}

__attribute__((disable_tail_calls))
int main() {
helper();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
void tail_called_in_a_from_main();

void tail_called_in_b_from_a();
16 changes: 14 additions & 2 deletions lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,13 @@ Function *SymbolFileDWARF::ParseFunction(CompileUnit &comp_unit,
return dwarf_ast->ParseFunctionFromDWARF(comp_unit, die);
}

lldb::addr_t SymbolFileDWARF::FixupAddress(lldb::addr_t file_addr) {
SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
if (debug_map_symfile)
return debug_map_symfile->LinkOSOFileAddress(this, file_addr);
return file_addr;
}

bool SymbolFileDWARF::FixupAddress(Address &addr) {
SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
if (debug_map_symfile) {
Expand Down Expand Up @@ -3820,8 +3827,8 @@ CollectCallSiteParameters(ModuleSP module, DWARFDIE call_site_die) {
}

/// Collect call graph edges present in a function DIE.
static std::vector<std::unique_ptr<lldb_private::CallEdge>>
CollectCallEdges(ModuleSP module, DWARFDIE function_die) {
std::vector<std::unique_ptr<lldb_private::CallEdge>>
SymbolFileDWARF::CollectCallEdges(ModuleSP module, DWARFDIE function_die) {
// Check if the function has a supported call site-related attribute.
// TODO: In the future it may be worthwhile to support call_all_source_calls.
uint64_t has_call_edges =
Expand Down Expand Up @@ -3897,6 +3904,11 @@ CollectCallEdges(ModuleSP module, DWARFDIE function_die) {
continue;
}

// Adjust the return PC. It needs to be fixed up if the main executable
// contains a debug map (i.e. pointers to object files), because we need a
// file address relative to the executable's text section.
return_pc = FixupAddress(return_pc);

// Extract call site parameters.
CallSiteParameterArray parameters =
CollectCallSiteParameters(module, child);
Expand Down
12 changes: 12 additions & 0 deletions lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,18 @@ class SymbolFileDWARF : public lldb_private::SymbolFile,
bool ClassContainsSelector(const DWARFDIE &class_die,
lldb_private::ConstString selector);

/// Parse call site entries (DW_TAG_call_site), including any nested call site
/// parameters (DW_TAG_call_site_parameter).
std::vector<std::unique_ptr<lldb_private::CallEdge>>
CollectCallEdges(lldb::ModuleSP module, DWARFDIE function_die);

/// If this symbol file is linked to by a debug map (see
/// SymbolFileDWARFDebugMap), and \p file_addr is a file address relative to
/// an object file, adjust \p file_addr so that it is relative to the main
/// binary. Returns the adjusted address, or \p file_addr if no adjustment is
/// needed, on success and LLDB_INVALID_ADDRESS otherwise.
lldb::addr_t FixupAddress(lldb::addr_t file_addr);

bool FixupAddress(lldb_private::Address &addr);

typedef std::set<lldb_private::Type *> TypeSet;
Expand Down
21 changes: 19 additions & 2 deletions lldb/source/Symbol/Function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,25 @@ size_t InlineFunctionInfo::MemorySize() const {

lldb::addr_t CallEdge::GetReturnPCAddress(Function &caller,
Target &target) const {
const Address &base = caller.GetAddressRange().GetBaseAddress();
return base.GetLoadAddress(&target) + return_pc;
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));

const Address &caller_start_addr = caller.GetAddressRange().GetBaseAddress();

ModuleSP caller_module_sp = caller_start_addr.GetModule();
if (!caller_module_sp) {
LLDB_LOG(log, "GetReturnPCAddress: cannot get Module for caller");
return LLDB_INVALID_ADDRESS;
}

SectionList *section_list = caller_module_sp->GetSectionList();
if (!section_list) {
LLDB_LOG(log, "GetReturnPCAddress: cannot get SectionList for Module");
return LLDB_INVALID_ADDRESS;
}

Address return_pc_addr = Address(return_pc, section_list);
lldb::addr_t ret_addr = return_pc_addr.GetLoadAddress(&target);
return ret_addr;
}

void DirectCallEdge::ParseSymbolFileAndResolve(ModuleList &images) {
Expand Down
4 changes: 0 additions & 4 deletions llvm/include/llvm/CodeGen/DebugHandlerBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,6 @@ class DebugHandlerBase : public AsmPrinterHandler {
/// Return Label immediately following the instruction.
MCSymbol *getLabelAfterInsn(const MachineInstr *MI);

/// Return the function-local offset of an instruction. A label for the
/// instruction \p MI should exist (\ref getLabelAfterInsn).
const MCExpr *getFunctionLocalOffsetAfterInsn(const MachineInstr *MI);

/// If this type is derived from a base type then return base type size.
static uint64_t getBaseTypeSize(const DIType *Ty);
};
Expand Down
9 changes: 9 additions & 0 deletions llvm/include/llvm/IR/DebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#ifndef LLVM_IR_DEBUGINFO_H
#define LLVM_IR_DEBUGINFO_H

#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator_range.h"
Expand All @@ -25,6 +26,7 @@ namespace llvm {

class DbgDeclareInst;
class DbgValueInst;
class Instruction;
class Module;

/// Find subprogram that is enclosing this scope.
Expand All @@ -50,6 +52,13 @@ bool stripDebugInfo(Function &F);
/// All debug type metadata nodes are unreachable and garbage collected.
bool stripNonLineTableDebugInfo(Module &M);

/// Update the debug locations contained within the MD_loop metadata attached
/// to the instruction \p I, if one exists. \p Updater is applied to each debug
/// location in the MD_loop metadata: the returned value is included in the
/// updated loop metadata node if it is non-null.
void updateLoopMetadataDebugLocations(
Instruction &I, function_ref<DILocation *(const DILocation &)> Updater);

/// Return Debug Info Metadata Version by checking module flags.
unsigned getDebugMetadataVersionFromModule(const Module &M);

Expand Down
Loading