mirror of
https://github.com/wolfpld/tracy.git
synced 2024-11-25 23:44:35 +00:00
Add support for libbacktrace to detect new elfs have been dynamically loaded after backtrace_initialize() has been called, and consider them for symbol resolution
This commit is contained in:
parent
55f53b9979
commit
b835d73590
@ -86,6 +86,7 @@ set_option(TRACY_NO_CRASH_HANDLER "Disable crash handling" OFF)
|
||||
set_option(TRACY_TIMER_FALLBACK "Use lower resolution timers" OFF)
|
||||
set_option(TRACE_CLIENT_LIBUNWIND_BACKTRACE "Use libunwind backtracing where supported" OFF)
|
||||
set_option(TRACY_SYMBOL_OFFLINE_RESOLVE "Instead of full runtime symbol resolution, only resolve the image path and offset to enable offline symbol resolution" OFF)
|
||||
set_option(TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT "Enable libbacktrace to support dynamically loaded elfs in symbol resolution resolution after the first symbol resolve operation" OFF)
|
||||
|
||||
if(NOT TRACY_STATIC)
|
||||
target_compile_definitions(TracyClient PRIVATE TRACY_EXPORTS)
|
||||
|
@ -4251,6 +4251,19 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
}
|
||||
}
|
||||
|
||||
bool dwarf_fileline_dwarf_lookup_pc_in_all_entries(struct backtrace_state *state, uintptr_t pc,
|
||||
backtrace_full_callback callback, backtrace_error_callback error_callback, void *data,
|
||||
int& found, int ret)
|
||||
{
|
||||
for (struct dwarf_data* ddata = (struct dwarf_data *)state->fileline_data;
|
||||
ddata != NULL;
|
||||
ddata = ddata->next)
|
||||
{
|
||||
ret = dwarf_lookup_pc(state, ddata, pc, callback, error_callback, data, &found);
|
||||
if (ret != 0 || found) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return the file/line information for a PC using the DWARF mapping
|
||||
we built earlier. */
|
||||
@ -4265,17 +4278,27 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
|
||||
int ret;
|
||||
|
||||
if (!state->threaded)
|
||||
{
|
||||
if (dwarf_fileline_dwarf_lookup_pc_in_all_entries(state, pc, callback, error_callback, data, found, ret))
|
||||
{
|
||||
for (ddata = (struct dwarf_data *) state->fileline_data;
|
||||
ddata != NULL;
|
||||
ddata = ddata->next)
|
||||
{
|
||||
ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
|
||||
data, &found);
|
||||
if (ret != 0 || found)
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// if we failed to obtain an entry in range, it can mean that the address map has been cahnges and new entries
|
||||
// have been loaded in the meantime. Request a refresh and try again
|
||||
if (state->request_known_address_ranges_refresh_fn)
|
||||
{
|
||||
int new_range_count = state->request_known_address_ranges_refresh_fn(state, pc);
|
||||
if (new_range_count > 0)
|
||||
{
|
||||
if (dwarf_fileline_dwarf_lookup_pc_in_all_entries(state, pc, callback, error_callback, data, found, ret))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
struct dwarf_data **pp;
|
||||
|
@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. */
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef HAVE_DL_ITERATE_PHDR
|
||||
#include <link.h>
|
||||
@ -7350,13 +7351,39 @@ struct PhdrIterate
|
||||
{
|
||||
char* dlpi_name;
|
||||
ElfW(Addr) dlpi_addr;
|
||||
ElfW(Addr) dlpi_end_addr;
|
||||
};
|
||||
FastVector<PhdrIterate> s_phdrData(16);
|
||||
|
||||
struct ElfAddrRange
|
||||
{
|
||||
ElfW(Addr) dlpi_addr;
|
||||
ElfW(Addr) dlpi_end_addr;
|
||||
};
|
||||
FastVector<ElfAddrRange> s_sortedKnownElfRanges(16);
|
||||
|
||||
static int address_in_known_elf_ranges(uintptr_t pc)
|
||||
{
|
||||
size_t range_count = s_sortedKnownElfRanges.size();
|
||||
|
||||
auto it = std::lower_bound( s_sortedKnownElfRanges.begin(), s_sortedKnownElfRanges.end(), pc,
|
||||
[]( const ElfAddrRange& lhs, const uintptr_t rhs ) { return uintptr_t(lhs.dlpi_addr) > rhs; } );
|
||||
if( it != s_sortedKnownElfRanges.end() && pc <= it->dlpi_end_addr )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
phdr_callback_mock (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED,
|
||||
void *pdata)
|
||||
{
|
||||
if( address_in_known_elf_ranges(info->dlpi_addr) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto ptr = s_phdrData.push_next();
|
||||
if (info->dlpi_name)
|
||||
{
|
||||
@ -7366,6 +7393,12 @@ phdr_callback_mock (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED,
|
||||
}
|
||||
else ptr->dlpi_name = nullptr;
|
||||
ptr->dlpi_addr = info->dlpi_addr;
|
||||
|
||||
// calculate the address range so we can quickly determine is a PC is within the range of this image
|
||||
ptr->dlpi_end_addr = info->dlpi_phnum ? uintptr_t( info->dlpi_addr +
|
||||
info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr +
|
||||
info->dlpi_phdr[info->dlpi_phnum - 1].p_memsz ) : 0x0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -7422,6 +7455,66 @@ phdr_callback (struct PhdrIterate *info, void *pdata)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elf_iterate_phdr_and_add_new_files(phdr_data *pd)
|
||||
{
|
||||
assert(s_phdrData.empty());
|
||||
// dl_iterate_phdr, will only add entries for elf files loaded in a previouly unseen range
|
||||
dl_iterate_phdr(phdr_callback_mock, nullptr);
|
||||
|
||||
if(s_phdrData.size() == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t headersAdded = 0;
|
||||
for (auto &v : s_phdrData)
|
||||
{
|
||||
phdr_callback(&v, (void *)pd);
|
||||
|
||||
auto newEntry = s_sortedKnownElfRanges.push_next();
|
||||
newEntry->dlpi_addr = v.dlpi_addr;
|
||||
newEntry->dlpi_end_addr = v.dlpi_end_addr;
|
||||
|
||||
tracy_free(v.dlpi_name);
|
||||
|
||||
headersAdded++;
|
||||
}
|
||||
|
||||
s_phdrData.clear();
|
||||
|
||||
std::sort( s_sortedKnownElfRanges.begin(), s_sortedKnownElfRanges.end(),
|
||||
[]( const ElfAddrRange& lhs, const ElfAddrRange& rhs ) { return lhs.dlpi_addr > rhs.dlpi_addr; } );
|
||||
|
||||
return headersAdded;
|
||||
}
|
||||
|
||||
#ifdef TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT
|
||||
/* Request an elf entry update if the pc passed in is not in any of the known elf ranges.
|
||||
This could mean that new images were dlopened and we need to add those new elf entries */
|
||||
static int elf_refresh_address_ranges_if_needed(struct backtrace_state *state, uintptr_t pc)
|
||||
{
|
||||
if ( address_in_known_elf_ranges(pc) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct phdr_data pd;
|
||||
int found_sym = 0;
|
||||
int found_dwarf = 0;
|
||||
fileline fileline_fn = nullptr;
|
||||
pd.state = state;
|
||||
pd.error_callback = nullptr;
|
||||
pd.data = nullptr;
|
||||
pd.fileline_fn = &fileline_fn;
|
||||
pd.found_sym = &found_sym;
|
||||
pd.found_dwarf = &found_dwarf;
|
||||
pd.exe_filename = nullptr;
|
||||
pd.exe_descriptor = -1;
|
||||
|
||||
return elf_iterate_phdr_and_add_new_files(&pd);
|
||||
}
|
||||
#endif //#ifdef TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT
|
||||
|
||||
/* Initialize the backtrace data we need from an ELF executable. At
|
||||
the ELF level, all we need to do is find the debug info
|
||||
sections. */
|
||||
@ -7452,14 +7545,7 @@ backtrace_initialize (struct backtrace_state *state, const char *filename,
|
||||
pd.exe_filename = filename;
|
||||
pd.exe_descriptor = ret < 0 ? descriptor : -1;
|
||||
|
||||
assert (s_phdrData.empty());
|
||||
dl_iterate_phdr (phdr_callback_mock, nullptr);
|
||||
for (auto& v : s_phdrData)
|
||||
{
|
||||
phdr_callback (&v, (void *) &pd);
|
||||
tracy_free (v.dlpi_name);
|
||||
}
|
||||
s_phdrData.clear();
|
||||
elf_iterate_phdr_and_add_new_files(&pd);
|
||||
|
||||
if (!state->threaded)
|
||||
{
|
||||
@ -7485,6 +7571,13 @@ backtrace_initialize (struct backtrace_state *state, const char *filename,
|
||||
if (*fileline_fn == NULL || *fileline_fn == elf_nodebug)
|
||||
*fileline_fn = elf_fileline_fn;
|
||||
|
||||
// install an address range refresh callback so we can cope with dynamically loaded elf files
|
||||
#ifdef TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT
|
||||
state->request_known_address_ranges_refresh_fn = elf_refresh_address_ranges_if_needed;
|
||||
#else
|
||||
state->request_known_address_ranges_refresh_fn = NULL;
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -133,6 +133,11 @@ typedef void (*syminfo) (struct backtrace_state *state, uintptr_t pc,
|
||||
backtrace_syminfo_callback callback,
|
||||
backtrace_error_callback error_callback, void *data);
|
||||
|
||||
/* The type of the function that will trigger an known address range refresh
|
||||
(if pc passed in is for an address whichs lies ourtisde of known ranges) */
|
||||
typedef int (*request_known_address_ranges_refresh)(struct backtrace_state *state,
|
||||
uintptr_t pc);
|
||||
|
||||
/* What the backtrace state pointer points to. */
|
||||
|
||||
struct backtrace_state
|
||||
@ -159,6 +164,8 @@ struct backtrace_state
|
||||
int lock_alloc;
|
||||
/* The freelist when using mmap. */
|
||||
struct backtrace_freelist_struct *freelist;
|
||||
/* Trigger an known address range refresh */
|
||||
request_known_address_ranges_refresh request_known_address_ranges_refresh_fn;
|
||||
};
|
||||
|
||||
/* Open a file for reading. Returns -1 on error. If DOES_NOT_EXIST
|
||||
|
Loading…
Reference in New Issue
Block a user