qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] linux-user: extend -dfilter to accept paths
@ 2023-08-11 16:28 Alex Bennée
  2023-08-11 16:28 ` [RFC PATCH 1/2] linux-user: implement some basic FD<->path tracking Alex Bennée
  2023-08-11 16:28 ` [RFC PATCH 2/2] linux-user: implement name lookup for dfilter Alex Bennée
  0 siblings, 2 replies; 3+ messages in thread
From: Alex Bennée @ 2023-08-11 16:28 UTC (permalink / raw)
  To: qemu-devel, Yeqi Fu; +Cc: Laurent Vivier, Alex Bennée

I was getting lost on debugging output and I couldn't get the mapped
address of libnative to be stable. So to help I hacked up this
extension which may or may not be worth developing further.

Consider this a debugging RFC tool for now.

Alex Bennée (2):
  linux-user: implement some basic FD<->path tracking
  linux-user: implement name lookup for dfilter

 include/qemu/log.h   | 12 ++++++
 linux-user/syscall.c | 90 +++++++++++++++++++++++++++++++++++++++++---
 util/log.c           | 36 +++++++++++++++++-
 3 files changed, 131 insertions(+), 7 deletions(-)

-- 
2.39.2



^ permalink raw reply	[flat|nested] 3+ messages in thread

* [RFC PATCH 1/2] linux-user: implement some basic FD<->path tracking
  2023-08-11 16:28 [RFC PATCH 0/2] linux-user: extend -dfilter to accept paths Alex Bennée
@ 2023-08-11 16:28 ` Alex Bennée
  2023-08-11 16:28 ` [RFC PATCH 2/2] linux-user: implement name lookup for dfilter Alex Bennée
  1 sibling, 0 replies; 3+ messages in thread
From: Alex Bennée @ 2023-08-11 16:28 UTC (permalink / raw)
  To: qemu-devel, Yeqi Fu; +Cc: Laurent Vivier, Alex Bennée

This will be useful in later patches for tracking the paths associated
with mmap operations. This will be useful to the upcoming -dfilter
changes to track execution only certain libraries.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 linux-user/syscall.c | 59 ++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 54 insertions(+), 5 deletions(-)

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 9353268cc1..e191163c49 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -8557,6 +8557,58 @@ static int open_hardware(CPUArchState *cpu_env, int fd)
 }
 #endif
 
+/*
+ * Handle non-intercepted guest open operations. This gives us the
+ * opportunity to track some information
+ */
+
+static QemuMutex fd_tracking_lock;
+static GHashTable *fd_path;
+
+__attribute__((constructor))
+static void fd_tracking_init(void)
+{
+    qemu_mutex_init(&fd_tracking_lock);
+}
+
+static int do_plain_guest_openat(int dirfd, const char *pathname,
+                                 int flags, mode_t mode, bool safe)
+{
+    const char * real_path = path(pathname);
+    int fd;
+
+    if (safe) {
+        fd = safe_openat(dirfd, real_path, flags, mode);
+    } else {
+        fd = openat(dirfd, real_path, flags, mode);
+    }
+
+    /* If we opened an fd save some details */
+    if (fd >= 0) {
+        WITH_QEMU_LOCK_GUARD(&fd_tracking_lock) {
+            if (!fd_path) {
+                fd_path = g_hash_table_new(NULL, NULL);
+            }
+
+            if (!g_hash_table_insert(fd_path, GINT_TO_POINTER(fd), g_strdup(real_path))) {
+                fprintf(stderr, "%s: duplicate fd %d in fd_path hash\n", __func__, fd);
+            }
+        }
+    }
+
+    return fd;
+}
+
+static void fd_path_cleanup(int fd) {
+    WITH_QEMU_LOCK_GUARD(&fd_tracking_lock) {
+        /*
+         * Assume success, if we failed to cleanup its totally
+         * possible the guest got confused and closed something twice.
+         */
+        g_hash_table_remove(fd_path, GINT_TO_POINTER(fd));
+    }
+}
+
 
 int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname,
                     int flags, mode_t mode, bool safe)
@@ -8643,11 +8695,7 @@ int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname,
         return fd;
     }
 
-    if (safe) {
-        return safe_openat(dirfd, path(pathname), flags, mode);
-    } else {
-        return openat(dirfd, path(pathname), flags, mode);
-    }
+    return do_plain_guest_openat(dirfd, pathname, flags, mode, safe);
 }
 
 ssize_t do_guest_readlink(const char *pathname, char *buf, size_t bufsiz)
@@ -9355,6 +9403,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
         return get_errno(pidfd_getfd(arg1, arg2, arg3));
 #endif
     case TARGET_NR_close:
+        fd_path_cleanup(arg1);
         fd_trans_unregister(arg1);
         return get_errno(close(arg1));
 #if defined(__NR_close_range) && defined(TARGET_NR_close_range)
-- 
2.39.2



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [RFC PATCH 2/2] linux-user: implement name lookup for dfilter
  2023-08-11 16:28 [RFC PATCH 0/2] linux-user: extend -dfilter to accept paths Alex Bennée
  2023-08-11 16:28 ` [RFC PATCH 1/2] linux-user: implement some basic FD<->path tracking Alex Bennée
@ 2023-08-11 16:28 ` Alex Bennée
  1 sibling, 0 replies; 3+ messages in thread
From: Alex Bennée @ 2023-08-11 16:28 UTC (permalink / raw)
  To: qemu-devel, Yeqi Fu; +Cc: Laurent Vivier, Alex Bennée

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 --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" },
-- 
2.39.2



^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2023-08-11 16:29 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-08-11 16:28 [RFC PATCH 0/2] linux-user: extend -dfilter to accept paths Alex Bennée
2023-08-11 16:28 ` [RFC PATCH 1/2] linux-user: implement some basic FD<->path tracking Alex Bennée
2023-08-11 16:28 ` [RFC PATCH 2/2] linux-user: implement name lookup for dfilter Alex Bennée

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).