accel/tcg: Add debuginfo support
Add libdw-based functions for loading and querying debuginfo. Load debuginfo from the system and the linux-user loaders. This is useful for the upcoming perf support, which can then put human-readable guest symbols instead of raw guest PCs into perfmap and jitdump files. Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com> Message-Id: <20230112152013.125680-3-iii@linux.ibm.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
parent
da91c19202
commit
7c10cb38cc
96
accel/tcg/debuginfo.c
Normal file
96
accel/tcg/debuginfo.c
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Debug information support.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/lockable.h"
|
||||||
|
|
||||||
|
#include <elfutils/libdwfl.h>
|
||||||
|
|
||||||
|
#include "debuginfo.h"
|
||||||
|
|
||||||
|
static QemuMutex lock;
|
||||||
|
static Dwfl *dwfl;
|
||||||
|
static const Dwfl_Callbacks dwfl_callbacks = {
|
||||||
|
.find_elf = NULL,
|
||||||
|
.find_debuginfo = dwfl_standard_find_debuginfo,
|
||||||
|
.section_address = NULL,
|
||||||
|
.debuginfo_path = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor))
|
||||||
|
static void debuginfo_init(void)
|
||||||
|
{
|
||||||
|
qemu_mutex_init(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debuginfo_report_elf(const char *name, int fd, uint64_t bias)
|
||||||
|
{
|
||||||
|
QEMU_LOCK_GUARD(&lock);
|
||||||
|
|
||||||
|
if (dwfl) {
|
||||||
|
dwfl_report_begin_add(dwfl);
|
||||||
|
} else {
|
||||||
|
dwfl = dwfl_begin(&dwfl_callbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dwfl) {
|
||||||
|
dwfl_report_elf(dwfl, name, name, fd, bias, true);
|
||||||
|
dwfl_report_end(dwfl, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void debuginfo_lock(void)
|
||||||
|
{
|
||||||
|
qemu_mutex_lock(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debuginfo_query(struct debuginfo_query *q, size_t n)
|
||||||
|
{
|
||||||
|
const char *symbol, *file;
|
||||||
|
Dwfl_Module *dwfl_module;
|
||||||
|
Dwfl_Line *dwfl_line;
|
||||||
|
GElf_Off dwfl_offset;
|
||||||
|
GElf_Sym dwfl_sym;
|
||||||
|
size_t i;
|
||||||
|
int line;
|
||||||
|
|
||||||
|
if (!dwfl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
dwfl_module = dwfl_addrmodule(dwfl, q[i].address);
|
||||||
|
if (!dwfl_module) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q[i].flags & DEBUGINFO_SYMBOL) {
|
||||||
|
symbol = dwfl_module_addrinfo(dwfl_module, q[i].address,
|
||||||
|
&dwfl_offset, &dwfl_sym,
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
if (symbol) {
|
||||||
|
q[i].symbol = symbol;
|
||||||
|
q[i].offset = dwfl_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q[i].flags & DEBUGINFO_LINE) {
|
||||||
|
dwfl_line = dwfl_module_getsrc(dwfl_module, q[i].address);
|
||||||
|
if (dwfl_line) {
|
||||||
|
file = dwfl_lineinfo(dwfl_line, NULL, &line, 0, NULL, NULL);
|
||||||
|
if (file) {
|
||||||
|
q[i].file = file;
|
||||||
|
q[i].line = line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void debuginfo_unlock(void)
|
||||||
|
{
|
||||||
|
qemu_mutex_unlock(&lock);
|
||||||
|
}
|
77
accel/tcg/debuginfo.h
Normal file
77
accel/tcg/debuginfo.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Debug information support.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ACCEL_TCG_DEBUGINFO_H
|
||||||
|
#define ACCEL_TCG_DEBUGINFO_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Debuginfo describing a certain address.
|
||||||
|
*/
|
||||||
|
struct debuginfo_query {
|
||||||
|
uint64_t address; /* Input: address. */
|
||||||
|
int flags; /* Input: debuginfo subset. */
|
||||||
|
const char *symbol; /* Symbol that the address is part of. */
|
||||||
|
uint64_t offset; /* Offset from the symbol. */
|
||||||
|
const char *file; /* Source file associated with the address. */
|
||||||
|
int line; /* Line number in the source file. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Debuginfo subsets.
|
||||||
|
*/
|
||||||
|
#define DEBUGINFO_SYMBOL BIT(1)
|
||||||
|
#define DEBUGINFO_LINE BIT(2)
|
||||||
|
|
||||||
|
#if defined(CONFIG_TCG) && defined(CONFIG_LIBDW)
|
||||||
|
/*
|
||||||
|
* Load debuginfo for the specified guest ELF image.
|
||||||
|
* Return true on success, false on failure.
|
||||||
|
*/
|
||||||
|
void debuginfo_report_elf(const char *name, int fd, uint64_t bias);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take the debuginfo lock.
|
||||||
|
*/
|
||||||
|
void debuginfo_lock(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill each on N Qs with the debuginfo about Q->ADDRESS as specified by
|
||||||
|
* Q->FLAGS:
|
||||||
|
*
|
||||||
|
* - DEBUGINFO_SYMBOL: update Q->SYMBOL and Q->OFFSET. If symbol debuginfo is
|
||||||
|
* missing, then leave them as is.
|
||||||
|
* - DEBUINFO_LINE: update Q->FILE and Q->LINE. If line debuginfo is missing,
|
||||||
|
* then leave them as is.
|
||||||
|
*
|
||||||
|
* This function must be called under the debuginfo lock. The results can be
|
||||||
|
* accessed only until the debuginfo lock is released.
|
||||||
|
*/
|
||||||
|
void debuginfo_query(struct debuginfo_query *q, size_t n);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Release the debuginfo lock.
|
||||||
|
*/
|
||||||
|
void debuginfo_unlock(void);
|
||||||
|
#else
|
||||||
|
static inline void debuginfo_report_elf(const char *image_name, int image_fd,
|
||||||
|
uint64_t load_bias)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void debuginfo_lock(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void debuginfo_query(struct debuginfo_query *q, size_t n)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void debuginfo_unlock(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -12,6 +12,7 @@ tcg_ss.add(files(
|
|||||||
tcg_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c'))
|
tcg_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c'))
|
||||||
tcg_ss.add(when: 'CONFIG_SOFTMMU', if_false: files('user-exec-stub.c'))
|
tcg_ss.add(when: 'CONFIG_SOFTMMU', if_false: files('user-exec-stub.c'))
|
||||||
tcg_ss.add(when: 'CONFIG_PLUGIN', if_true: [files('plugin-gen.c')])
|
tcg_ss.add(when: 'CONFIG_PLUGIN', if_true: [files('plugin-gen.c')])
|
||||||
|
tcg_ss.add(when: libdw, if_true: files('debuginfo.c'))
|
||||||
specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss)
|
specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss)
|
||||||
|
|
||||||
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files(
|
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files(
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "sysemu/runstate.h"
|
#include "sysemu/runstate.h"
|
||||||
|
#include "accel/tcg/debuginfo.h"
|
||||||
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
@ -503,6 +504,10 @@ ssize_t load_elf_ram_sym(const char *filename,
|
|||||||
clear_lsb, data_swab, as, load_rom, sym_cb);
|
clear_lsb, data_swab, as, load_rom, sym_cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret != ELF_LOAD_FAILED) {
|
||||||
|
debuginfo_report_elf(filename, fd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
close(fd);
|
close(fd);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "qemu/selfmap.h"
|
#include "qemu/selfmap.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "target_signal.h"
|
#include "target_signal.h"
|
||||||
|
#include "accel/tcg/debuginfo.h"
|
||||||
|
|
||||||
#ifdef _ARCH_PPC64
|
#ifdef _ARCH_PPC64
|
||||||
#undef ARCH_DLINFO
|
#undef ARCH_DLINFO
|
||||||
@ -3261,6 +3262,8 @@ static void load_elf_image(const char *image_name, int image_fd,
|
|||||||
load_symbols(ehdr, image_fd, load_bias);
|
load_symbols(ehdr, image_fd, load_bias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debuginfo_report_elf(image_name, image_fd, load_bias);
|
||||||
|
|
||||||
mmap_unlock();
|
mmap_unlock();
|
||||||
|
|
||||||
close(image_fd);
|
close(image_fd);
|
||||||
|
@ -22,6 +22,7 @@ linux_user_ss.add(files(
|
|||||||
'uname.c',
|
'uname.c',
|
||||||
))
|
))
|
||||||
linux_user_ss.add(rt)
|
linux_user_ss.add(rt)
|
||||||
|
linux_user_ss.add(libdw)
|
||||||
|
|
||||||
linux_user_ss.add(when: 'TARGET_HAS_BFLT', if_true: files('flatload.c'))
|
linux_user_ss.add(when: 'TARGET_HAS_BFLT', if_true: files('flatload.c'))
|
||||||
linux_user_ss.add(when: 'TARGET_I386', if_true: files('vm86.c'))
|
linux_user_ss.add(when: 'TARGET_I386', if_true: files('vm86.c'))
|
||||||
|
@ -1648,6 +1648,12 @@ if libbpf.found() and not cc.links('''
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# libdw
|
||||||
|
libdw = dependency('libdw',
|
||||||
|
method: 'pkg-config',
|
||||||
|
kwargs: static_kwargs,
|
||||||
|
required: false)
|
||||||
|
|
||||||
#################
|
#################
|
||||||
# config-host.h #
|
# config-host.h #
|
||||||
#################
|
#################
|
||||||
@ -1923,6 +1929,7 @@ config_host_data.set('CONFIG_DBUS_DISPLAY', dbus_display)
|
|||||||
config_host_data.set('CONFIG_CFI', get_option('cfi'))
|
config_host_data.set('CONFIG_CFI', get_option('cfi'))
|
||||||
config_host_data.set('CONFIG_SELINUX', selinux.found())
|
config_host_data.set('CONFIG_SELINUX', selinux.found())
|
||||||
config_host_data.set('CONFIG_XEN_BACKEND', xen.found())
|
config_host_data.set('CONFIG_XEN_BACKEND', xen.found())
|
||||||
|
config_host_data.set('CONFIG_LIBDW', libdw.found())
|
||||||
if xen.found()
|
if xen.found()
|
||||||
# protect from xen.version() having less than three components
|
# protect from xen.version() having less than three components
|
||||||
xen_version = xen.version().split('.') + ['0', '0']
|
xen_version = xen.version().split('.') + ['0', '0']
|
||||||
@ -3976,6 +3983,7 @@ summary_info += {'libudev': libudev}
|
|||||||
# Dummy dependency, keep .found()
|
# Dummy dependency, keep .found()
|
||||||
summary_info += {'FUSE lseek': fuse_lseek.found()}
|
summary_info += {'FUSE lseek': fuse_lseek.found()}
|
||||||
summary_info += {'selinux': selinux}
|
summary_info += {'selinux': selinux}
|
||||||
|
summary_info += {'libdw': libdw}
|
||||||
summary(summary_info, bool_yn: true, section: 'Dependencies')
|
summary(summary_info, bool_yn: true, section: 'Dependencies')
|
||||||
|
|
||||||
if not supported_cpus.contains(cpu)
|
if not supported_cpus.contains(cpu)
|
||||||
|
Loading…
Reference in New Issue
Block a user