@@ -86,6 +86,18 @@ bool qemu_set_log(int log_flags, Error **errp);
bool qemu_set_log_filename(const char *filename, Error **errp);
bool qemu_set_log_filename_flags(const char *name, int flags, Error **errp);
void qemu_set_dfilter_ranges(const char *ranges, Error **errp);
+
+/**
+ * qemu_maybe_append_dfilter_range() - maybe add mapped binary range to dfilter
+ * @path - the full path to the mapped binary
+ * @start - start guest address
+ * @end - end guest address
+ *
+ * This allows *-user to add ranges to the dfilter list after the fact
+ * as binary sections are mapped in.
+ */
+void qemu_maybe_append_dfilter_range(const char *path, uint64_t start, uint64_t end);
+
bool qemu_log_in_addr_range(uint64_t addr);
int qemu_str_to_log_mask(const char *str);
@@ -6019,6 +6019,18 @@ static const bitmask_transtbl mmap_flags_tbl[] = {
#define TARGET_MAP_HUGE_1GB 0
#endif
+static char *get_fd_path_mapping(int fd);
+
+static void track_exec_segments(int fd, abi_ulong addr, abi_ulong len, off_t offset)
+{
+ g_autofree char *path = get_fd_path_mapping(fd);
+ if (path) {
+ uint64_t start = addr + offset;
+ uint64_t end = start + len;
+ qemu_maybe_append_dfilter_range(path, start, end);
+ }
+}
+
static abi_long do_mmap(abi_ulong addr, abi_ulong len, int prot,
int target_flags, int fd, off_t offset)
{
@@ -6045,6 +6057,7 @@ static abi_long do_mmap(abi_ulong addr, abi_ulong len, int prot,
| TARGET_MAP_HUGE_1GB
};
int host_flags;
+ abi_long map_addr;
switch (target_flags & TARGET_MAP_TYPE) {
case TARGET_MAP_PRIVATE:
@@ -6071,7 +6084,14 @@ static abi_long do_mmap(abi_ulong addr, abi_ulong len, int prot,
}
host_flags |= target_to_host_bitmask(target_flags, mmap_flags_tbl);
- return get_errno(target_mmap(addr, len, prot, host_flags, fd, offset));
+
+ map_addr = target_mmap(addr, len, prot, host_flags, fd, offset);
+ /* Have we successfully mapped an executable segment? */
+ if (map_addr > 0 /* && prot & PROT_EXEC */) {
+ track_exec_segments(fd, map_addr, len, offset);
+ }
+
+ return get_errno(map_addr);
}
/*
@@ -8571,6 +8591,15 @@ static void fd_tracking_init(void)
qemu_mutex_init(&fd_tracking_lock);
}
+/* caller owns result */
+static char * get_fd_path_mapping(int fd) {
+ gpointer value;
+ WITH_QEMU_LOCK_GUARD(&fd_tracking_lock) {
+ value = g_hash_table_lookup(fd_path, GINT_TO_POINTER(fd));
+ }
+ return g_strdup(value);
+}
+
static int do_plain_guest_openat(int dirfd, const char *pathname,
int flags, mode_t mode, bool safe)
{
@@ -48,6 +48,15 @@ int qemu_loglevel;
static bool log_per_thread;
static GArray *debug_regions;
+static QemuMutex debug_names_lock;
+static GPtrArray *debug_names; /* unresolved named ranges */
+
+__attribute__((constructor))
+static void debug_names_init(void)
+{
+ qemu_mutex_init(&debug_names_lock);
+}
+
/* Returns true if qemu_log() will really write somewhere. */
bool qemu_log_enabled(void)
{
@@ -393,6 +402,8 @@ void qemu_set_dfilter_ranges(const char *filter_spec, Error **errp)
debug_regions = g_array_sized_new(FALSE, FALSE,
sizeof(Range), g_strv_length(ranges));
+ debug_names = g_ptr_array_new();
+
for (i = 0; ranges[i]; i++) {
const char *r = ranges[i];
const char *range_op, *r2, *e;
@@ -410,7 +421,8 @@ void qemu_set_dfilter_ranges(const char *filter_spec, Error **errp)
r2 = range_op ? range_op + 2 : NULL;
}
if (!range_op) {
- error_setg(errp, "Bad range specifier");
+ /* this might be a libname, defer until we map stuff */
+ g_ptr_array_add(debug_names, g_strdup(r));
goto out;
}
@@ -453,6 +465,28 @@ out:
g_strfreev(ranges);
}
+void qemu_maybe_append_dfilter_range(const char *path, uint64_t start, uint64_t end)
+{
+ if (!debug_names) {
+ return;
+ }
+
+ WITH_QEMU_LOCK_GUARD(&debug_names_lock) {
+ int i;
+ for (i = 0; i < debug_names->len; i++) {
+ char *name = g_ptr_array_index(debug_names, i);
+
+ if (strstr(path, name) != NULL) {
+ struct Range range;
+ range_set_bounds(&range, start, end);
+ g_array_append_val(debug_regions, range);
+ g_free(g_ptr_array_remove_index(debug_names, i));
+ break;
+ }
+ }
+ }
+}
+
const QEMULogItem qemu_log_items[] = {
{ CPU_LOG_TB_OUT_ASM, "out_asm",
"show generated host assembly code for each compiled TB" },
This implements a simple extension to dfilter so we can use pathnames as a proxy for address ranges such that: ./qemu-x86_64 -d page,in_asm,op,op_opt,out_asm \ --dfilter libnative \ --native-bypass common-user/native/x86_64-linux-user/libnative.so \ /usr/bin/tar xvf audacity-sources-3.3.3.tar.gz will only output debug information for the thing I'm interested in. There are a couple of things that need fixing before we could merge but it works well enough for debug: - we don't do the PROT_EXEC check because the linker mprotects the region after the fact and that requires more plumbing - there is no locking in the hot path of qemu_log_in_addr_range(), in theory we only expand debug_regions but GArray could move the pointer Signed-off-by: Alex Bennée <alex.bennee@linaro.org> --- include/qemu/log.h | 12 ++++++++++++ linux-user/syscall.c | 31 ++++++++++++++++++++++++++++++- util/log.c | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 2 deletions(-)