diff mbox series

[RFC,2/2] linux-user: implement name lookup for dfilter

Message ID 20230811162830.2278032-3-alex.bennee@linaro.org
State New
Headers show
Series linux-user: extend -dfilter to accept paths | expand

Commit Message

Alex Bennée Aug. 11, 2023, 4:28 p.m. UTC
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(-)
diff mbox series

Patch

diff --git a/include/qemu/log.h b/include/qemu/log.h
index df59bfabcd..3dba364b05 100644
--- a/include/qemu/log.h
+++ b/include/qemu/log.h
@@ -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);
 
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index e191163c49..b724ec8df6 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -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)
 {
diff --git a/util/log.c b/util/log.c
index def88a9402..b4bd20fd72 100644
--- a/util/log.c
+++ b/util/log.c
@@ -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" },