From 13ddcf0a7a52ce17c836edbe6fc161a40269ce13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 5 Mar 2024 12:09:37 +0000 Subject: [PATCH 01/29] tests: bump QOS_PATH_MAX_ELEMENT_SIZE again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We "fixed" a bug with LTO builds with 100c459f194 (tests/qtest: bump up QOS_PATH_MAX_ELEMENT_SIZE) but it seems it has triggered again. The array is sized according to the maximum anticipated length of a path on the graph. However, the worst case for a depth-first search is to push all nodes on the graph. So it's not really LTO, it depends on the ordering of the constructors. Lets be more assertive raising QOS_PATH_MAX_ELEMENT_SIZE to make it go away again. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1186 (again) Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-2-alex.bennee@linaro.org> --- tests/qtest/libqos/qgraph.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/libqos/qgraph.h b/tests/qtest/libqos/qgraph.h index 287022a67c..1b5de02e7b 100644 --- a/tests/qtest/libqos/qgraph.h +++ b/tests/qtest/libqos/qgraph.h @@ -24,7 +24,7 @@ #include "libqos-malloc.h" /* maximum path length */ -#define QOS_PATH_MAX_ELEMENT_SIZE 64 +#define QOS_PATH_MAX_ELEMENT_SIZE 128 typedef struct QOSGraphObject QOSGraphObject; typedef struct QOSGraphNode QOSGraphNode; From 1ea96f1ded914c64c5a6df78b8d0663daebc828c Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:38 +0000 Subject: [PATCH 02/29] gdbstub: Support disablement in a multi-threaded process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upcoming follow-fork-mode child support will require disabling gdbstub in the parent process, which may have multiple threads (which are represented as CPUs). Loop over all CPUs in order to remove breakpoints and disable single-step. Move the respective code into a separate function. Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Signed-off-by: Ilya Leoshkevich Message-Id: <20240219141628.246823-2-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-3-alex.bennee@linaro.org> --- gdbstub/user.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/gdbstub/user.c b/gdbstub/user.c index 14918d1a21..3ce20b7bbf 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -356,16 +356,27 @@ int gdbserver_start(const char *port_or_path) return -1; } +static void disable_gdbstub(CPUState *thread_cpu) +{ + CPUState *cpu; + + close(gdbserver_user_state.fd); + gdbserver_user_state.fd = -1; + CPU_FOREACH(cpu) { + cpu_breakpoint_remove_all(cpu, BP_GDB); + /* no cpu_watchpoint_remove_all for user-mode */ + cpu_single_step(cpu, 0); + } + tb_flush(thread_cpu); +} + /* Disable gdb stub for child processes. */ void gdbserver_fork(CPUState *cpu) { if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { return; } - close(gdbserver_user_state.fd); - gdbserver_user_state.fd = -1; - cpu_breakpoint_remove_all(cpu, BP_GDB); - /* no cpu_watchpoint_remove_all for user-mode */ + disable_gdbstub(cpu); } /* From e4e5cb4a54403558defb4669a8c64cbd67713e1e Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:39 +0000 Subject: [PATCH 03/29] {linux,bsd}-user: Introduce get_task_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A CPU's TaskState is stored in the CPUState's void *opaque field, accessing which is somewhat awkward due to having to use a cast. Introduce a wrapper and use it everywhere. Suggested-by: Alex Bennée Signed-off-by: Ilya Leoshkevich Reviewed-by: Warner Losh Reviewed-by: Richard Henderson Message-Id: <20240219141628.246823-3-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-4-alex.bennee@linaro.org> --- bsd-user/bsd-file.h | 2 +- bsd-user/qemu.h | 5 +++++ bsd-user/signal.c | 20 ++++++++++---------- gdbstub/user-target.c | 4 ++-- include/user/safe-syscall.h | 2 +- linux-user/aarch64/cpu_loop.c | 2 +- linux-user/arm/cpu_loop.c | 4 ++-- linux-user/arm/signal.c | 2 +- linux-user/cris/cpu_loop.c | 2 +- linux-user/elfload.c | 2 +- linux-user/hppa/signal.c | 2 +- linux-user/linuxload.c | 2 +- linux-user/m68k/cpu_loop.c | 2 +- linux-user/m68k/target_cpu.h | 2 +- linux-user/mips/cpu_loop.c | 2 +- linux-user/ppc/signal.c | 4 ++-- linux-user/qemu.h | 5 +++++ linux-user/riscv/cpu_loop.c | 2 +- linux-user/signal-common.h | 2 +- linux-user/signal.c | 30 +++++++++++++++--------------- linux-user/syscall.c | 26 +++++++++++++------------- linux-user/vm86.c | 18 +++++++++--------- linux-user/xtensa/signal.c | 2 +- plugins/api.c | 8 ++++---- semihosting/arm-compat-semi.c | 8 ++++---- 25 files changed, 85 insertions(+), 75 deletions(-) diff --git a/bsd-user/bsd-file.h b/bsd-user/bsd-file.h index 3c00dc0056..6fa2c30b4d 100644 --- a/bsd-user/bsd-file.h +++ b/bsd-user/bsd-file.h @@ -641,7 +641,7 @@ static abi_long do_bsd_readlink(CPUArchState *env, abi_long arg1, } if (strcmp(p1, "/proc/curproc/file") == 0) { CPUState *cpu = env_cpu(env); - TaskState *ts = (TaskState *)cpu->opaque; + TaskState *ts = get_task_state(cpu); strncpy(p2, ts->bprm->fullpath, arg3); ret = MIN((abi_long)strlen(ts->bprm->fullpath), arg3); } else { diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index c05c512767..4adb75d19f 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -117,6 +117,11 @@ typedef struct TaskState { struct target_sigaltstack sigaltstack_used; } __attribute__((aligned(16))) TaskState; +static inline TaskState *get_task_state(CPUState *cs) +{ + return cs->opaque; +} + void stop_all_tasks(void); extern const char *interp_prefix; extern const char *qemu_uname_release; diff --git a/bsd-user/signal.c b/bsd-user/signal.c index f4352e4530..e9f80a06d3 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -319,7 +319,7 @@ void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info) int block_signals(void) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); sigset_t set; /* @@ -359,7 +359,7 @@ void dump_core_and_abort(int target_sig) { CPUState *cpu = thread_cpu; CPUArchState *env = cpu_env(cpu); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); int core_dumped = 0; int host_sig; struct sigaction act; @@ -421,7 +421,7 @@ void queue_signal(CPUArchState *env, int sig, int si_type, target_siginfo_t *info) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); trace_user_queue_signal(env, sig); @@ -476,7 +476,7 @@ void force_sig_fault(int sig, int code, abi_ulong addr) static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) { CPUState *cpu = thread_cpu; - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); target_siginfo_t tinfo; ucontext_t *uc = puc; struct emulated_sigtable *k; @@ -585,7 +585,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) /* compare to kern/kern_sig.c sys_sigaltstack() and kern_sigaltstack() */ abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); int ret; target_stack_t oss; @@ -714,7 +714,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, static inline abi_ulong get_sigframe(struct target_sigaction *ka, CPUArchState *env, size_t frame_size) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); abi_ulong sp; /* Use default user stack */ @@ -789,7 +789,7 @@ static int reset_signal_mask(target_ucontext_t *ucontext) int i; sigset_t blocked; target_sigset_t target_set; - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); for (i = 0; i < TARGET_NSIG_WORDS; i++) { __get_user(target_set.__bits[i], &ucontext->uc_sigmask.__bits[i]); @@ -839,7 +839,7 @@ badframe: void signal_init(void) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); struct sigaction act; struct sigaction oact; int i; @@ -878,7 +878,7 @@ static void handle_pending_signal(CPUArchState *env, int sig, struct emulated_sigtable *k) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct target_sigaction *sa; int code; sigset_t set; @@ -967,7 +967,7 @@ void process_pending_signals(CPUArchState *env) int sig; sigset_t *blocked_set, set; struct emulated_sigtable *k; - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); while (qatomic_read(&ts->signal_pending)) { sigfillset(&set); diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c index b7d4c37cd8..6646684a4c 100644 --- a/gdbstub/user-target.c +++ b/gdbstub/user-target.c @@ -204,7 +204,7 @@ int gdb_target_signal_to_gdb(int sig) int gdb_get_cpu_index(CPUState *cpu) { - TaskState *ts = (TaskState *) cpu->opaque; + TaskState *ts = get_task_state(cpu); return ts ? ts->ts_tid : -1; } @@ -399,7 +399,7 @@ void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx) return; } - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); if (!ts || !ts->bprm || !ts->bprm->filename) { gdb_put_packet("E00"); return; diff --git a/include/user/safe-syscall.h b/include/user/safe-syscall.h index 27b71cdbd8..aa075f4d5c 100644 --- a/include/user/safe-syscall.h +++ b/include/user/safe-syscall.h @@ -134,7 +134,7 @@ extern char safe_syscall_start[]; extern char safe_syscall_end[]; #define safe_syscall(...) \ - safe_syscall_base(&((TaskState *)thread_cpu->opaque)->signal_pending, \ + safe_syscall_base(&get_task_state(thread_cpu)->signal_pending, \ __VA_ARGS__) #endif diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 8c20dc8a39..71cdc8be50 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -189,7 +189,7 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { ARMCPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); struct image_info *info = ts->info; int i; diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index b404117ff3..db1a41e27f 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -263,7 +263,7 @@ static bool insn_is_linux_bkpt(uint32_t opcode, bool is_thumb) static bool emulate_arm_fpa11(CPUARMState *env, uint32_t opcode) { - TaskState *ts = env_cpu(env)->opaque; + TaskState *ts = get_task_state(env_cpu(env)); int rc = EmulateAll(opcode, &ts->fpa, env); int raise, enabled; @@ -514,7 +514,7 @@ void cpu_loop(CPUARMState *env) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct image_info *info = ts->info; int i; diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index f77f692c63..59806335f5 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -177,7 +177,7 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, int usig, abi_ulong handler = 0; abi_ulong handler_fdpic_GOT = 0; abi_ulong retcode; - bool is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info); + bool is_fdpic = info_is_fdpic(get_task_state(thread_cpu)->info); bool is_rt = ka->sa_flags & TARGET_SA_SIGINFO; bool thumb; diff --git a/linux-user/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c index 01e6ff16fc..04c9086b6d 100644 --- a/linux-user/cris/cpu_loop.c +++ b/linux-user/cris/cpu_loop.c @@ -72,7 +72,7 @@ void cpu_loop(CPUCRISState *env) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct image_info *info = ts->info; env->regs[0] = regs->r0; diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0c299a7c15..4dbca05646 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -4404,7 +4404,7 @@ static int wmr_write_region(void *opaque, target_ulong start, static int elf_core_dump(int signr, const CPUArchState *env) { const CPUState *cpu = env_cpu((CPUArchState *)env); - const TaskState *ts = (const TaskState *)cpu->opaque; + const TaskState *ts = (const TaskState *)get_task_state((CPUState *)cpu); struct rlimit dumpsize; CountAndSizeRegions css; off_t offset, note_offset, data_offset; diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c index d08a97dae6..c84557e906 100644 --- a/linux-user/hppa/signal.c +++ b/linux-user/hppa/signal.c @@ -112,7 +112,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, abi_ulong frame_addr, sp, haddr; struct target_rt_sigframe *frame; int i; - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); sp = get_sp_from_cpustate(env); if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 4a794f8cea..37f132be4a 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -89,7 +89,7 @@ static int prepare_binprm(struct linux_binprm *bprm) abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, abi_ulong stringp, int push_ptr) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); int n = sizeof(abi_ulong); abi_ulong envp; abi_ulong argv; diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index caead1cb74..f79b8e4ab0 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -95,7 +95,7 @@ void cpu_loop(CPUM68KState *env) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct image_info *info = ts->info; env->pc = regs->pc; diff --git a/linux-user/m68k/target_cpu.h b/linux-user/m68k/target_cpu.h index c3f288dfe8..4b40c09a8d 100644 --- a/linux-user/m68k/target_cpu.h +++ b/linux-user/m68k/target_cpu.h @@ -37,7 +37,7 @@ static inline void cpu_clone_regs_parent(CPUM68KState *env, unsigned flags) static inline void cpu_set_tls(CPUM68KState *env, target_ulong newtls) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); ts->tp_value = newtls; } diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 990b03e727..462387a073 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -214,7 +214,7 @@ done_syscall: void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct image_info *info = ts->info; int i; diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index 7e7302823b..c232424c1e 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -486,7 +486,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, int i, err = 0; #if defined(TARGET_PPC64) struct target_sigcontext *sc = 0; - struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; + struct image_info *image = get_task_state(thread_cpu)->info; #endif rt_sf_addr = get_sigframe(ka, env, sizeof(*rt_sf)); @@ -673,7 +673,7 @@ abi_long do_swapcontext(CPUArchState *env, abi_ulong uold_ctx, } if (uold_ctx) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); if (!lock_user_struct(VERIFY_WRITE, uctx, uold_ctx, 1)) { return -TARGET_EFAULT; diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 4de9ec783f..32cd43d9ef 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -162,6 +162,11 @@ typedef struct TaskState { uint64_t start_boottime; } TaskState; +static inline TaskState *get_task_state(CPUState *cs) +{ + return cs->opaque; +} + abi_long do_brk(abi_ulong new_brk); int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int flags, mode_t mode, bool safe); diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index bffca7db12..52c49c2e42 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -97,7 +97,7 @@ void cpu_loop(CPURISCVState *env) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct image_info *info = ts->info; env->pc = regs->sepc; diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h index 3e2dc604c2..a7df12fc44 100644 --- a/linux-user/signal-common.h +++ b/linux-user/signal-common.h @@ -113,7 +113,7 @@ int process_sigsuspend_mask(sigset_t **pset, target_ulong sigset, static inline void finish_sigsuspend_mask(int ret) { if (ret != -QEMU_ERESTARTSYS) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); ts->in_sigsuspend = 1; } } diff --git a/linux-user/signal.c b/linux-user/signal.c index d3e62ab030..cc7dd78e41 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -172,7 +172,7 @@ void target_to_host_old_sigset(sigset_t *sigset, int block_signals(void) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); sigset_t set; /* It's OK to block everything including SIGSEGV, because we won't @@ -194,7 +194,7 @@ int block_signals(void) */ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); if (oldset) { *oldset = ts->signal_mask; @@ -237,7 +237,7 @@ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) */ void set_sigmask(const sigset_t *set) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); ts->signal_mask = *set; } @@ -246,7 +246,7 @@ void set_sigmask(const sigset_t *set) int on_sig_stack(unsigned long sp) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); return (sp - ts->sigaltstack_used.ss_sp < ts->sigaltstack_used.ss_size); @@ -254,7 +254,7 @@ int on_sig_stack(unsigned long sp) int sas_ss_flags(unsigned long sp) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); return (ts->sigaltstack_used.ss_size == 0 ? SS_DISABLE : on_sig_stack(sp) ? SS_ONSTACK : 0); @@ -265,7 +265,7 @@ abi_ulong target_sigsp(abi_ulong sp, struct target_sigaction *ka) /* * This is the X/Open sanctioned signal stack switching. */ - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { return ts->sigaltstack_used.ss_sp + ts->sigaltstack_used.ss_size; @@ -275,7 +275,7 @@ abi_ulong target_sigsp(abi_ulong sp, struct target_sigaction *ka) void target_save_altstack(target_stack_t *uss, CPUArchState *env) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); __put_user(ts->sigaltstack_used.ss_sp, &uss->ss_sp); __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &uss->ss_flags); @@ -284,7 +284,7 @@ void target_save_altstack(target_stack_t *uss, CPUArchState *env) abi_long target_restore_altstack(target_stack_t *uss, CPUArchState *env) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); size_t minstacksize = TARGET_MINSIGSTKSZ; target_stack_t ss; @@ -571,7 +571,7 @@ static void signal_table_init(void) void signal_init(void) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); struct sigaction act, oact; /* initialize signal conversion tables */ @@ -730,7 +730,7 @@ static G_NORETURN void dump_core_and_abort(CPUArchState *env, int target_sig) { CPUState *cpu = env_cpu(env); - TaskState *ts = (TaskState *)cpu->opaque; + TaskState *ts = get_task_state(cpu); int host_sig, core_dumped = 0; /* On exit, undo the remapping of SIGABRT. */ @@ -769,7 +769,7 @@ void queue_signal(CPUArchState *env, int sig, int si_type, target_siginfo_t *info) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); trace_user_queue_signal(env, sig); @@ -954,7 +954,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) { CPUState *cpu = thread_cpu; CPUArchState *env = cpu_env(cpu); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); target_siginfo_t tinfo; host_sigcontext *uc = puc; struct emulated_sigtable *k; @@ -1174,7 +1174,7 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, sigset_t set; target_sigset_t target_old_set; struct target_sigaction *sa; - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); trace_user_handle_signal(cpu_env, sig); /* dequeue signal */ @@ -1256,7 +1256,7 @@ void process_pending_signals(CPUArchState *cpu_env) { CPUState *cpu = env_cpu(cpu_env); int sig; - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); sigset_t set; sigset_t *blocked_set; @@ -1316,7 +1316,7 @@ void process_pending_signals(CPUArchState *cpu_env) int process_sigsuspend_mask(sigset_t **pset, target_ulong sigset, target_ulong sigsize) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); sigset_t *host_set = &ts->sigsuspend_mask; target_sigset_t *target_sigset; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index bc8c06522f..c233d4eb30 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6515,7 +6515,7 @@ static void *clone_func(void *arg) env = info->env; cpu = env_cpu(env); thread_cpu = cpu; - ts = (TaskState *)cpu->opaque; + ts = get_task_state(cpu); info->tid = sys_gettid(); task_settid(ts); if (info->child_tidptr) @@ -6557,7 +6557,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, flags &= ~(CLONE_VFORK | CLONE_VM); if (flags & CLONE_VM) { - TaskState *parent_ts = (TaskState *)cpu->opaque; + TaskState *parent_ts = get_task_state(cpu); new_thread_info info; pthread_attr_t attr; @@ -6680,7 +6680,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, put_user_u32(sys_gettid(), child_tidptr); if (flags & CLONE_PARENT_SETTID) put_user_u32(sys_gettid(), parent_tidptr); - ts = (TaskState *)cpu->opaque; + ts = get_task_state(cpu); if (flags & CLONE_SETTLS) cpu_set_tls (env, newtls); if (flags & CLONE_CHILD_CLEARTID) @@ -7946,7 +7946,7 @@ int host_to_target_waitstatus(int status) static int open_self_cmdline(CPUArchState *cpu_env, int fd) { CPUState *cpu = env_cpu(cpu_env); - struct linux_binprm *bprm = ((TaskState *)cpu->opaque)->bprm; + struct linux_binprm *bprm = get_task_state(cpu)->bprm; int i; for (i = 0; i < bprm->argc; i++) { @@ -8146,7 +8146,7 @@ static int open_self_smaps(CPUArchState *cpu_env, int fd) static int open_self_stat(CPUArchState *cpu_env, int fd) { CPUState *cpu = env_cpu(cpu_env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); g_autoptr(GString) buf = g_string_new(NULL); int i; @@ -8187,7 +8187,7 @@ static int open_self_stat(CPUArchState *cpu_env, int fd) static int open_self_auxv(CPUArchState *cpu_env, int fd) { CPUState *cpu = env_cpu(cpu_env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); abi_ulong auxv = ts->info->saved_auxv; abi_ulong len = ts->info->auxv_len; char *ptr; @@ -9012,7 +9012,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, pthread_mutex_lock(&clone_lock); if (CPU_NEXT(first_cpu)) { - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); if (ts->child_tidptr) { put_user_u32(0, ts->child_tidptr); @@ -9439,7 +9439,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_pause /* not on alpha */ case TARGET_NR_pause: if (!block_signals()) { - sigsuspend(&((TaskState *)cpu->opaque)->signal_mask); + sigsuspend(&get_task_state(cpu)->signal_mask); } return -TARGET_EINTR; #endif @@ -10005,7 +10005,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, sigset_t *set; #if defined(TARGET_ALPHA) - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); /* target_to_host_old_sigset will bswap back */ abi_ulong mask = tswapal(arg1); set = &ts->sigsuspend_mask; @@ -10406,7 +10406,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_mprotect: arg1 = cpu_untagged_addr(cpu, arg1); { - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); /* Special hack to detect libc making the stack executable. */ if ((arg3 & PROT_GROWSDOWN) && arg1 >= ts->info->stack_limit @@ -12537,7 +12537,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return do_set_thread_area(cpu_env, arg1); #elif defined(TARGET_M68K) { - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); ts->tp_value = arg1; return 0; } @@ -12551,7 +12551,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return do_get_thread_area(cpu_env, arg1); #elif defined(TARGET_M68K) { - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); return ts->tp_value; } #else @@ -12676,7 +12676,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #if defined(TARGET_NR_set_tid_address) case TARGET_NR_set_tid_address: { - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); ts->child_tidptr = arg1; /* do not call host set_tid_address() syscall, instead return tid() */ return get_errno(sys_gettid()); diff --git a/linux-user/vm86.c b/linux-user/vm86.c index c2facf3fc2..9f512a2242 100644 --- a/linux-user/vm86.c +++ b/linux-user/vm86.c @@ -74,7 +74,7 @@ static inline unsigned int vm_getl(CPUX86State *env, void save_v86_state(CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); struct target_vm86plus_struct * target_v86; if (!lock_user_struct(VERIFY_WRITE, target_v86, ts->target_v86, 0)) @@ -134,7 +134,7 @@ static inline void return_to_32bit(CPUX86State *env, int retval) static inline int set_IF(CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); ts->v86flags |= VIF_MASK; if (ts->v86flags & VIP_MASK) { @@ -147,7 +147,7 @@ static inline int set_IF(CPUX86State *env) static inline void clear_IF(CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); ts->v86flags &= ~VIF_MASK; } @@ -165,7 +165,7 @@ static inline void clear_AC(CPUX86State *env) static inline int set_vflags_long(unsigned long eflags, CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); set_flags(ts->v86flags, eflags, ts->v86mask); set_flags(env->eflags, eflags, SAFE_MASK); @@ -179,7 +179,7 @@ static inline int set_vflags_long(unsigned long eflags, CPUX86State *env) static inline int set_vflags_short(unsigned short flags, CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); set_flags(ts->v86flags, flags, ts->v86mask & 0xffff); set_flags(env->eflags, flags, SAFE_MASK); @@ -193,7 +193,7 @@ static inline int set_vflags_short(unsigned short flags, CPUX86State *env) static inline unsigned int get_vflags(CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); unsigned int flags; flags = env->eflags & RETURN_MASK; @@ -210,7 +210,7 @@ static inline unsigned int get_vflags(CPUX86State *env) static void do_int(CPUX86State *env, int intno) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); uint32_t int_addr, segoffs, ssp; unsigned int sp; @@ -269,7 +269,7 @@ void handle_vm86_trap(CPUX86State *env, int trapno) void handle_vm86_fault(CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); uint32_t csp, ssp; unsigned int ip, sp, newflags, newip, newcs, opcode, intno; int data32, pref_done; @@ -394,7 +394,7 @@ void handle_vm86_fault(CPUX86State *env) int do_vm86(CPUX86State *env, long subfunction, abi_ulong vm86_addr) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); struct target_vm86plus_struct * target_v86; int ret; diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c index 32dcfa5229..003208a916 100644 --- a/linux-user/xtensa/signal.c +++ b/linux-user/xtensa/signal.c @@ -157,7 +157,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, { abi_ulong frame_addr; struct target_rt_sigframe *frame; - int is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info); + int is_fdpic = info_is_fdpic(get_task_state(thread_cpu)->info); abi_ulong handler = 0; abi_ulong handler_fdpic_GOT = 0; uint32_t ra; diff --git a/plugins/api.c b/plugins/api.c index 81f43c9ce8..e905e995bd 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -378,7 +378,7 @@ const char *qemu_plugin_path_to_binary(void) { char *path = NULL; #ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; + TaskState *ts = get_task_state(current_cpu); path = g_strdup(ts->bprm->filename); #endif return path; @@ -388,7 +388,7 @@ uint64_t qemu_plugin_start_code(void) { uint64_t start = 0; #ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; + TaskState *ts = get_task_state(current_cpu); start = ts->info->start_code; #endif return start; @@ -398,7 +398,7 @@ uint64_t qemu_plugin_end_code(void) { uint64_t end = 0; #ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; + TaskState *ts = get_task_state(current_cpu); end = ts->info->end_code; #endif return end; @@ -408,7 +408,7 @@ uint64_t qemu_plugin_entry_code(void) { uint64_t entry = 0; #ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; + TaskState *ts = get_task_state(current_cpu); entry = ts->info->entry; #endif return entry; diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 329ea11260..d78c6428b9 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -214,7 +214,7 @@ static target_ulong syscall_err; static inline uint32_t get_swi_errno(CPUState *cs) { #ifdef CONFIG_USER_ONLY - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); return ts->swi_errno; #else @@ -226,7 +226,7 @@ static void common_semi_cb(CPUState *cs, uint64_t ret, int err) { if (err) { #ifdef CONFIG_USER_ONLY - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); ts->swi_errno = err; #else syscall_err = err; @@ -586,7 +586,7 @@ void do_common_semihosting(CPUState *cs) #if !defined(CONFIG_USER_ONLY) const char *cmdline; #else - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); #endif GET_ARG(0); GET_ARG(1); @@ -664,7 +664,7 @@ void do_common_semihosting(CPUState *cs) target_ulong retvals[4]; int i; #ifdef CONFIG_USER_ONLY - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); target_ulong limit; #else LayoutInfo info = common_semi_find_bases(cs); From d4e1369abe67a4ffae2abbbd943aaeadc9595cc2 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:40 +0000 Subject: [PATCH 04/29] {linux,bsd}-user: Update ts_tid after fork() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently ts_tid contains the parent tid after fork(), which is not correct. So far it has not affected anything, but the upcoming follow-fork-mode child support relies on the correct value, so fix it. Reviewed-by: Alex Bennée Signed-off-by: Ilya Leoshkevich Reviewed-by: Warner Losh Message-Id: <20240219141628.246823-4-iii@linux.ibm.com> Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-5-alex.bennee@linaro.org> --- bsd-user/main.c | 1 + linux-user/main.c | 1 + 2 files changed, 2 insertions(+) diff --git a/bsd-user/main.c b/bsd-user/main.c index 512d4ab69f..e39eef3040 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -134,6 +134,7 @@ void fork_end(int child) * state, so we don't need to end_exclusive() here. */ qemu_init_cpu_list(); + get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id(); gdbserver_fork(thread_cpu); } else { mmap_fork_end(child); diff --git a/linux-user/main.c b/linux-user/main.c index 551acf1661..699da77371 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -161,6 +161,7 @@ void fork_end(int child) } } qemu_init_cpu_list(); + get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id(); gdbserver_fork(thread_cpu); } else { cpu_list_unlock(); From 3d6ed98da82e0bb2384bfbc83fadb518271fdbca Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:41 +0000 Subject: [PATCH 05/29] gdbstub: Introduce gdbserver_fork_start() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upcoming follow-fork-mode child support requires knowing when fork() is about to happen in order to initialize its state. Add a hook for that. Reviewed-by: Alex Bennée Signed-off-by: Ilya Leoshkevich Message-Id: <20240219141628.246823-5-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-6-alex.bennee@linaro.org> --- bsd-user/main.c | 1 + gdbstub/user.c | 4 ++++ include/gdbstub/user.h | 5 +++++ linux-user/main.c | 1 + 4 files changed, 11 insertions(+) diff --git a/bsd-user/main.c b/bsd-user/main.c index e39eef3040..517c6b3ec2 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -113,6 +113,7 @@ void fork_start(void) start_exclusive(); cpu_list_lock(); mmap_fork_start(); + gdbserver_fork_start(); } void fork_end(int child) diff --git a/gdbstub/user.c b/gdbstub/user.c index 3ce20b7bbf..536fb43b03 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -356,6 +356,10 @@ int gdbserver_start(const char *port_or_path) return -1; } +void gdbserver_fork_start(void) +{ +} + static void disable_gdbstub(CPUState *thread_cpu) { CPUState *cpu; diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h index 68b6534130..e33f8d9a9a 100644 --- a/include/gdbstub/user.h +++ b/include/gdbstub/user.h @@ -45,6 +45,11 @@ static inline int gdb_handlesig(CPUState *cpu, int sig) */ void gdb_signalled(CPUArchState *as, int sig); +/** + * gdbserver_fork_start() - inform gdb of the upcoming fork() + */ +void gdbserver_fork_start(void); + /** * gdbserver_fork() - disable gdb stub for child processes. * @cs: CPU diff --git a/linux-user/main.c b/linux-user/main.c index 699da77371..755c566d6d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -145,6 +145,7 @@ void fork_start(void) mmap_fork_start(); cpu_list_lock(); qemu_plugin_user_prefork_lock(); + gdbserver_fork_start(); } void fork_end(int child) From 4edc98fcc8c8124a2e345144a75e1b33dac7632c Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:42 +0000 Subject: [PATCH 06/29] {linux,bsd}-user: Pass pid to fork_end() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upcoming follow-fork-mode child support requires knowing the child pid. Pass it down. Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Signed-off-by: Ilya Leoshkevich Message-Id: <20240219141628.246823-6-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-7-alex.bennee@linaro.org> --- bsd-user/freebsd/os-proc.h | 6 +++--- bsd-user/main.c | 4 +++- bsd-user/qemu.h | 2 +- linux-user/main.c | 4 +++- linux-user/syscall.c | 6 +++--- linux-user/user-internals.h | 2 +- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/bsd-user/freebsd/os-proc.h b/bsd-user/freebsd/os-proc.h index d641878034..3003c8cb63 100644 --- a/bsd-user/freebsd/os-proc.h +++ b/bsd-user/freebsd/os-proc.h @@ -208,7 +208,7 @@ static inline abi_long do_freebsd_fork(void *cpu_env) */ set_second_rval(cpu_env, child_flag); - fork_end(child_flag); + fork_end(ret); return ret; } @@ -252,7 +252,7 @@ static inline abi_long do_freebsd_rfork(void *cpu_env, abi_long flags) * value: 0 for parent process, 1 for child process. */ set_second_rval(cpu_env, child_flag); - fork_end(child_flag); + fork_end(ret); return ret; @@ -285,7 +285,7 @@ static inline abi_long do_freebsd_pdfork(void *cpu_env, abi_ulong target_fdp, * value: 0 for parent process, 1 for child process. */ set_second_rval(cpu_env, child_flag); - fork_end(child_flag); + fork_end(ret); return ret; } diff --git a/bsd-user/main.c b/bsd-user/main.c index 517c6b3ec2..fca9b30204 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -116,8 +116,10 @@ void fork_start(void) gdbserver_fork_start(); } -void fork_end(int child) +void fork_end(pid_t pid) { + bool child = pid == 0; + if (child) { CPUState *cpu, *next_cpu; /* diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 4adb75d19f..1b0a591d2d 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -192,7 +192,7 @@ void cpu_loop(CPUArchState *env); char *target_strerror(int err); int get_osversion(void); void fork_start(void); -void fork_end(int child); +void fork_end(pid_t pid); #include "qemu/log.h" diff --git a/linux-user/main.c b/linux-user/main.c index 755c566d6d..cab95f5b0a 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -148,8 +148,10 @@ void fork_start(void) gdbserver_fork_start(); } -void fork_end(int child) +void fork_end(pid_t pid) { + bool child = pid == 0; + qemu_plugin_user_postfork(child); mmap_fork_end(child); if (child) { diff --git a/linux-user/syscall.c b/linux-user/syscall.c index c233d4eb30..7f30defcb1 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6669,7 +6669,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, if (ret == 0) { /* Child Process. */ cpu_clone_regs_child(env, newsp, flags); - fork_end(1); + fork_end(ret); /* There is a race condition here. The parent process could theoretically read the TID in the child process before the child tid is set. This would require using either ptrace @@ -6700,8 +6700,8 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, } #endif put_user_u32(pid_fd, parent_tidptr); - } - fork_end(0); + } + fork_end(ret); } g_assert(!cpu_in_exclusive_context(cpu)); } diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index c63ef45fc7..ce11d9e21c 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -71,7 +71,7 @@ const char *target_strerror(int err); int get_osversion(void); void init_qemu_uname_release(void); void fork_start(void); -void fork_end(int child); +void fork_end(pid_t pid); /** * probe_guest_base: From 9d456e092da670324f37f269d29300eedb915c3b Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:43 +0000 Subject: [PATCH 07/29] {linux,bsd}-user: Pass pid to gdbserver_fork() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upcoming follow-fork-mode child support requires knowing the child pid. Pass it down. Reviewed-by: Richard Henderson Signed-off-by: Ilya Leoshkevich Message-Id: <20240219141628.246823-7-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-8-alex.bennee@linaro.org> --- bsd-user/main.c | 2 +- gdbstub/user.c | 2 +- include/gdbstub/user.h | 2 +- linux-user/main.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index fca9b30204..0dbd1cf880 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -138,7 +138,7 @@ void fork_end(pid_t pid) */ qemu_init_cpu_list(); get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id(); - gdbserver_fork(thread_cpu); + gdbserver_fork(thread_cpu, pid); } else { mmap_fork_end(child); cpu_list_unlock(); diff --git a/gdbstub/user.c b/gdbstub/user.c index 536fb43b03..c61e1a0d1f 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -375,7 +375,7 @@ static void disable_gdbstub(CPUState *thread_cpu) } /* Disable gdb stub for child processes. */ -void gdbserver_fork(CPUState *cpu) +void gdbserver_fork(CPUState *cpu, pid_t pid) { if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { return; diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h index e33f8d9a9a..3f9f45946e 100644 --- a/include/gdbstub/user.h +++ b/include/gdbstub/user.h @@ -54,7 +54,7 @@ void gdbserver_fork_start(void); * gdbserver_fork() - disable gdb stub for child processes. * @cs: CPU */ -void gdbserver_fork(CPUState *cs); +void gdbserver_fork(CPUState *cs, pid_t pid); /** * gdb_syscall_entry() - inform gdb of syscall entry and yield control to it diff --git a/linux-user/main.c b/linux-user/main.c index cab95f5b0a..70314e0ab6 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -165,7 +165,7 @@ void fork_end(pid_t pid) } qemu_init_cpu_list(); get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id(); - gdbserver_fork(thread_cpu); + gdbserver_fork(thread_cpu, pid); } else { cpu_list_unlock(); } From 6604b05763515f6329bf508ff1284651ebffc33e Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:44 +0000 Subject: [PATCH 08/29] gdbstub: Call gdbserver_fork() both in parent and in child MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upcoming follow-fork-mode child support requires post-fork message exchange between the parent and the child. Prepare gdbserver_fork() for this purpose. Rename it to gdbserver_fork_end() to better reflect its purpose. Reviewed-by: Richard Henderson Signed-off-by: Ilya Leoshkevich Message-Id: <20240219141628.246823-8-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-9-alex.bennee@linaro.org> --- bsd-user/main.c | 3 ++- gdbstub/user.c | 5 ++--- include/gdbstub/user.h | 5 +++-- linux-user/main.c | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 0dbd1cf880..3dc285e5b7 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -138,10 +138,11 @@ void fork_end(pid_t pid) */ qemu_init_cpu_list(); get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id(); - gdbserver_fork(thread_cpu, pid); + gdbserver_fork_end(thread_cpu, pid); } else { mmap_fork_end(child); cpu_list_unlock(); + gdbserver_fork_end(thread_cpu, pid); end_exclusive(); } } diff --git a/gdbstub/user.c b/gdbstub/user.c index c61e1a0d1f..866a25f9c0 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -374,10 +374,9 @@ static void disable_gdbstub(CPUState *thread_cpu) tb_flush(thread_cpu); } -/* Disable gdb stub for child processes. */ -void gdbserver_fork(CPUState *cpu, pid_t pid) +void gdbserver_fork_end(CPUState *cpu, pid_t pid) { - if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { + if (pid != 0 || !gdbserver_state.init || gdbserver_user_state.fd < 0) { return; } disable_gdbstub(cpu); diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h index 3f9f45946e..4c4e5c4c58 100644 --- a/include/gdbstub/user.h +++ b/include/gdbstub/user.h @@ -51,10 +51,11 @@ void gdb_signalled(CPUArchState *as, int sig); void gdbserver_fork_start(void); /** - * gdbserver_fork() - disable gdb stub for child processes. + * gdbserver_fork_end() - inform gdb of the completed fork() * @cs: CPU + * @pid: 0 if in child process, -1 if fork failed, child process pid otherwise */ -void gdbserver_fork(CPUState *cs, pid_t pid); +void gdbserver_fork_end(CPUState *cs, pid_t pid); /** * gdb_syscall_entry() - inform gdb of syscall entry and yield control to it diff --git a/linux-user/main.c b/linux-user/main.c index 70314e0ab6..41caa77cb5 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -165,10 +165,10 @@ void fork_end(pid_t pid) } qemu_init_cpu_list(); get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id(); - gdbserver_fork(thread_cpu, pid); } else { cpu_list_unlock(); } + gdbserver_fork_end(thread_cpu, pid); /* * qemu_init_cpu_list() reinitialized the child exclusive state, but we * also need to keep current_cpu consistent, so call end_exclusive() for From 6d923112fd18f73bd55cbae23ae5a8023457b85c Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:45 +0000 Subject: [PATCH 09/29] gdbstub: Introduce gdb_handle_query_supported_user() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upcoming follow-fork-mode child support requires advertising the fork-events feature, which is user-specific. Introduce a user-specific hook for this. Reviewed-by: Richard Henderson Signed-off-by: Ilya Leoshkevich Message-Id: <20240219141628.246823-9-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-10-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 12 +++++++++--- gdbstub/internals.h | 1 + gdbstub/user.c | 4 ++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 2909bc8c69..7be4418dcb 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -1655,9 +1655,15 @@ static void handle_query_supported(GArray *params, void *user_ctx) g_string_append(gdbserver_state.str_buf, ";qXfer:exec-file:read+"); #endif - if (params->len && - strstr(get_param(params, 0)->data, "multiprocess+")) { - gdbserver_state.multiprocess = true; + if (params->len) { + const char *gdb_supported = get_param(params, 0)->data; + + if (strstr(gdb_supported, "multiprocess+")) { + gdbserver_state.multiprocess = true; + } +#if defined(CONFIG_USER_ONLY) + gdb_handle_query_supported_user(gdb_supported); +#endif } g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); diff --git a/gdbstub/internals.h b/gdbstub/internals.h index 56b7c13b75..e6063835b1 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -196,6 +196,7 @@ void gdb_handle_v_file_pread(GArray *params, void *user_ctx); /* user */ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */ void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user */ void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */ +void gdb_handle_query_supported_user(const char *gdb_supported); /* user */ void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */ diff --git a/gdbstub/user.c b/gdbstub/user.c index 866a25f9c0..c9e8b83d72 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -382,6 +382,10 @@ void gdbserver_fork_end(CPUState *cpu, pid_t pid) disable_gdbstub(cpu); } +void gdb_handle_query_supported_user(const char *gdb_supported) +{ +} + /* * Execution state helpers */ From e454f2fe24793afd1fa596deb42b714478adf73b Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:46 +0000 Subject: [PATCH 10/29] gdbstub: Introduce gdb_handle_set_thread_user() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upcoming follow-fork-mode child support needs to perform certain actions when GDB switches between the stopped parent and the stopped child. Introduce a user-specific hook for this. Signed-off-by: Ilya Leoshkevich Message-Id: <20240219141628.246823-10-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-11-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 11 +++++++++-- gdbstub/internals.h | 1 + gdbstub/user.c | 5 +++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 7be4418dcb..3eb93162aa 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -1099,6 +1099,7 @@ static void handle_cont_with_sig(GArray *params, void *user_ctx) static void handle_set_thread(GArray *params, void *user_ctx) { + uint32_t pid, tid; CPUState *cpu; if (params->len != 2) { @@ -1116,8 +1117,14 @@ static void handle_set_thread(GArray *params, void *user_ctx) return; } - cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid, - get_param(params, 1)->thread_id.tid); + pid = get_param(params, 1)->thread_id.pid; + tid = get_param(params, 1)->thread_id.tid; +#ifdef CONFIG_USER_ONLY + if (gdb_handle_set_thread_user(pid, tid)) { + return; + } +#endif + cpu = gdb_get_cpu(pid, tid); if (!cpu) { gdb_put_packet("E22"); return; diff --git a/gdbstub/internals.h b/gdbstub/internals.h index e6063835b1..b4905c7181 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -197,6 +197,7 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */ void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user */ void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */ void gdb_handle_query_supported_user(const char *gdb_supported); /* user */ +bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */ void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */ diff --git a/gdbstub/user.c b/gdbstub/user.c index c9e8b83d72..b048754c4f 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -386,6 +386,11 @@ void gdb_handle_query_supported_user(const char *gdb_supported) { } +bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid) +{ + return false; +} + /* * Execution state helpers */ From 539cb4ec49ec96aeb9dddba235c2f8bafc1c4fab Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:47 +0000 Subject: [PATCH 11/29] gdbstub: Introduce gdb_handle_detach_user() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upcoming follow-fork-mode child support needs to perform certain actions when GDB detaches from the stopped parent or the stopped child. Introduce a user-specific hook for this. Signed-off-by: Ilya Leoshkevich Message-Id: <20240219141628.246823-11-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-12-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 6 ++++++ gdbstub/internals.h | 1 + gdbstub/user.c | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 3eb93162aa..17efcae0d0 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -1024,6 +1024,12 @@ static void handle_detach(GArray *params, void *user_ctx) pid = get_param(params, 0)->val_ul; } +#ifdef CONFIG_USER_ONLY + if (gdb_handle_detach_user(pid)) { + return; + } +#endif + process = gdb_get_process(pid); gdb_process_breakpoint_remove_all(process); process->attached = false; diff --git a/gdbstub/internals.h b/gdbstub/internals.h index b4905c7181..b472459838 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -198,6 +198,7 @@ void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user */ void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */ void gdb_handle_query_supported_user(const char *gdb_supported); /* user */ bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */ +bool gdb_handle_detach_user(uint32_t pid); /* user */ void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */ diff --git a/gdbstub/user.c b/gdbstub/user.c index b048754c4f..1a7b582a40 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -391,6 +391,11 @@ bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid) return false; } +bool gdb_handle_detach_user(uint32_t pid) +{ + return false; +} + /* * Execution state helpers */ From d547e711a8a520b1a160958443c0851a6767f95b Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:48 +0000 Subject: [PATCH 12/29] gdbstub: Implement follow-fork-mode child MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently it's not possible to use gdbstub for debugging linux-user code that runs in a forked child, which is normally done using the `set follow-fork-mode child` GDB command. Purely on the protocol level, the missing piece is the fork-events feature. However, a deeper problem is supporting $Hg switching between different processes - right now it can do only threads. Implementing this for the general case would be quite complicated, but, fortunately, for the follow-fork-mode case there are a few factors that greatly simplify things: fork() happens in the exclusive section, there are only two processes involved, and before one of them is resumed, the second one is detached. This makes it possible to implement a simplified scheme: the parent and the child share the gdbserver socket, it's used only by one of them at any given time, which is coordinated through a separate socketpair. The processes can read from the gdbserver socket only one byte at a time, which is not great for performance, but, fortunately, the follow-fork-mode handling involves only a few messages. Advertise the fork-events support, and remember whether GDB has it as well. Implement the state machine that is initialized on fork(), decides the current owner of the gdbserver socket, and is terminated when one of the two processes is detached. The logic for the parent and the child is the same, only the initial state is different. Signed-off-by: Ilya Leoshkevich Message-Id: <20240219141628.246823-12-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-13-alex.bennee@linaro.org> --- gdbstub/user.c | 212 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 210 insertions(+), 2 deletions(-) diff --git a/gdbstub/user.c b/gdbstub/user.c index 1a7b582a40..7f9f19a124 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -25,6 +25,61 @@ #define GDB_NR_SYSCALLS 1024 typedef unsigned long GDBSyscallsMask[BITS_TO_LONGS(GDB_NR_SYSCALLS)]; +/* + * Forked child talks to its parent in order to let GDB enforce the + * follow-fork-mode. This happens inside a start_exclusive() section, so that + * the other threads, which may be forking too, do not interfere. The + * implementation relies on GDB not sending $vCont until it has detached + * either from the parent (follow-fork-mode child) or from the child + * (follow-fork-mode parent). + * + * The parent and the child share the GDB socket; at any given time only one + * of them is allowed to use it, as is reflected in the respective fork_state. + * This is negotiated via the fork_sockets pair as a reaction to $Hg. + * + * Below is a short summary of the possible state transitions: + * + * ENABLED : Terminal state. + * DISABLED : Terminal state. + * ACTIVE : Parent initial state. + * INACTIVE : Child initial state. + * ACTIVE -> DEACTIVATING: On $Hg. + * ACTIVE -> ENABLING : On $D. + * ACTIVE -> DISABLING : On $D. + * ACTIVE -> DISABLED : On communication error. + * DEACTIVATING -> INACTIVE : On gdb_read_byte() return. + * DEACTIVATING -> DISABLED : On communication error. + * INACTIVE -> ACTIVE : On $Hg in the peer. + * INACTIVE -> ENABLE : On $D in the peer. + * INACTIVE -> DISABLE : On $D in the peer. + * INACTIVE -> DISABLED : On communication error. + * ENABLING -> ENABLED : On gdb_read_byte() return. + * ENABLING -> DISABLED : On communication error. + * DISABLING -> DISABLED : On gdb_read_byte() return. + */ +enum GDBForkState { + /* Fully owning the GDB socket. */ + GDB_FORK_ENABLED, + /* Working with the GDB socket; the peer is inactive. */ + GDB_FORK_ACTIVE, + /* Handing off the GDB socket to the peer. */ + GDB_FORK_DEACTIVATING, + /* The peer is working with the GDB socket. */ + GDB_FORK_INACTIVE, + /* Asking the peer to close its GDB socket fd. */ + GDB_FORK_ENABLING, + /* Asking the peer to take over, closing our GDB socket fd. */ + GDB_FORK_DISABLING, + /* The peer has taken over, our GDB socket fd is closed. */ + GDB_FORK_DISABLED, +}; + +enum GDBForkMessage { + GDB_FORK_ACTIVATE = 'a', + GDB_FORK_ENABLE = 'e', + GDB_FORK_DISABLE = 'd', +}; + /* User-mode specific state */ typedef struct { int fd; @@ -36,6 +91,10 @@ typedef struct { */ bool catch_all_syscalls; GDBSyscallsMask catch_syscalls_mask; + bool fork_events; + enum GDBForkState fork_state; + int fork_sockets[2]; + pid_t fork_peer_pid, fork_peer_tid; } GDBUserState; static GDBUserState gdbserver_user_state; @@ -358,6 +417,18 @@ int gdbserver_start(const char *port_or_path) void gdbserver_fork_start(void) { + if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { + return; + } + if (!gdbserver_user_state.fork_events || + qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, + gdbserver_user_state.fork_sockets) < 0) { + gdbserver_user_state.fork_state = GDB_FORK_DISABLED; + return; + } + gdbserver_user_state.fork_state = GDB_FORK_INACTIVE; + gdbserver_user_state.fork_peer_pid = getpid(); + gdbserver_user_state.fork_peer_tid = qemu_get_thread_id(); } static void disable_gdbstub(CPUState *thread_cpu) @@ -376,23 +447,160 @@ static void disable_gdbstub(CPUState *thread_cpu) void gdbserver_fork_end(CPUState *cpu, pid_t pid) { - if (pid != 0 || !gdbserver_state.init || gdbserver_user_state.fd < 0) { + char b; + int fd; + + if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { return; } - disable_gdbstub(cpu); + + if (pid == -1) { + if (gdbserver_user_state.fork_state != GDB_FORK_DISABLED) { + g_assert(gdbserver_user_state.fork_state == GDB_FORK_INACTIVE); + close(gdbserver_user_state.fork_sockets[0]); + close(gdbserver_user_state.fork_sockets[1]); + } + return; + } + + if (gdbserver_user_state.fork_state == GDB_FORK_DISABLED) { + if (pid == 0) { + disable_gdbstub(cpu); + } + return; + } + + if (pid == 0) { + close(gdbserver_user_state.fork_sockets[0]); + fd = gdbserver_user_state.fork_sockets[1]; + g_assert(gdbserver_state.process_num == 1); + g_assert(gdbserver_state.processes[0].pid == + gdbserver_user_state.fork_peer_pid); + g_assert(gdbserver_state.processes[0].attached); + gdbserver_state.processes[0].pid = getpid(); + } else { + close(gdbserver_user_state.fork_sockets[1]); + fd = gdbserver_user_state.fork_sockets[0]; + gdbserver_user_state.fork_state = GDB_FORK_ACTIVE; + gdbserver_user_state.fork_peer_pid = pid; + gdbserver_user_state.fork_peer_tid = pid; + + if (!gdbserver_state.allow_stop_reply) { + goto fail; + } + g_string_printf(gdbserver_state.str_buf, + "T%02xfork:p%02x.%02x;thread:p%02x.%02x;", + gdb_target_signal_to_gdb(gdb_target_sigtrap()), + pid, pid, (int)getpid(), qemu_get_thread_id()); + gdb_put_strbuf(); + } + + gdbserver_state.state = RS_IDLE; + gdbserver_state.allow_stop_reply = false; + gdbserver_user_state.running_state = 0; + for (;;) { + switch (gdbserver_user_state.fork_state) { + case GDB_FORK_ENABLED: + if (gdbserver_user_state.running_state) { + return; + } + QEMU_FALLTHROUGH; + case GDB_FORK_ACTIVE: + if (read(gdbserver_user_state.fd, &b, 1) != 1) { + goto fail; + } + gdb_read_byte(b); + break; + case GDB_FORK_DEACTIVATING: + b = GDB_FORK_ACTIVATE; + if (write(fd, &b, 1) != 1) { + goto fail; + } + gdbserver_user_state.fork_state = GDB_FORK_INACTIVE; + break; + case GDB_FORK_INACTIVE: + if (read(fd, &b, 1) != 1) { + goto fail; + } + switch (b) { + case GDB_FORK_ACTIVATE: + gdbserver_user_state.fork_state = GDB_FORK_ACTIVE; + break; + case GDB_FORK_ENABLE: + close(fd); + gdbserver_user_state.fork_state = GDB_FORK_ENABLED; + break; + case GDB_FORK_DISABLE: + gdbserver_user_state.fork_state = GDB_FORK_DISABLED; + break; + default: + g_assert_not_reached(); + } + break; + case GDB_FORK_ENABLING: + b = GDB_FORK_DISABLE; + if (write(fd, &b, 1) != 1) { + goto fail; + } + close(fd); + gdbserver_user_state.fork_state = GDB_FORK_ENABLED; + break; + case GDB_FORK_DISABLING: + b = GDB_FORK_ENABLE; + if (write(fd, &b, 1) != 1) { + goto fail; + } + gdbserver_user_state.fork_state = GDB_FORK_DISABLED; + break; + case GDB_FORK_DISABLED: + close(fd); + disable_gdbstub(cpu); + return; + default: + g_assert_not_reached(); + } + } + +fail: + close(fd); + if (pid == 0) { + disable_gdbstub(cpu); + } } void gdb_handle_query_supported_user(const char *gdb_supported) { + if (strstr(gdb_supported, "fork-events+")) { + gdbserver_user_state.fork_events = true; + } + g_string_append(gdbserver_state.str_buf, ";fork-events+"); } bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid) { + if (gdbserver_user_state.fork_state == GDB_FORK_ACTIVE && + pid == gdbserver_user_state.fork_peer_pid && + tid == gdbserver_user_state.fork_peer_tid) { + gdbserver_user_state.fork_state = GDB_FORK_DEACTIVATING; + gdb_put_packet("OK"); + return true; + } return false; } bool gdb_handle_detach_user(uint32_t pid) { + bool enable; + + if (gdbserver_user_state.fork_state == GDB_FORK_ACTIVE) { + enable = pid == gdbserver_user_state.fork_peer_pid; + if (enable || pid == getpid()) { + gdbserver_user_state.fork_state = enable ? GDB_FORK_ENABLING : + GDB_FORK_DISABLING; + gdb_put_packet("OK"); + return true; + } + } return false; } From b9504c9ad980b97e6888bf07c36c2be567b4c65d Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 5 Mar 2024 12:09:49 +0000 Subject: [PATCH 13/29] tests/tcg: Add two follow-fork-mode tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add follow-fork-mode child and and follow-fork-mode parent tests. Check for the obvious pitfalls, such as lingering breakpoints, catchpoints, and single-step mode. Signed-off-by: Ilya Leoshkevich Message-Id: <20240219141628.246823-13-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-14-alex.bennee@linaro.org> --- tests/tcg/multiarch/Makefile.target | 17 +++++- tests/tcg/multiarch/follow-fork-mode.c | 56 +++++++++++++++++++ .../gdbstub/follow-fork-mode-child.py | 40 +++++++++++++ .../gdbstub/follow-fork-mode-parent.py | 16 ++++++ 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/multiarch/follow-fork-mode.c create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index f11f3b084d..979a0dd1bc 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -106,6 +106,20 @@ run-gdbstub-catch-syscalls: catch-syscalls --bin $< --test $(MULTIARCH_SRC)/gdbstub/catch-syscalls.py, \ hitting a syscall catchpoint) +run-gdbstub-follow-fork-mode-child: follow-fork-mode + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/follow-fork-mode-child.py, \ + following children on fork) + +run-gdbstub-follow-fork-mode-parent: follow-fork-mode + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/follow-fork-mode-parent.py, \ + following parents on fork) + else run-gdbstub-%: $(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst -%,,$(TARGET_NAME)) support") @@ -113,7 +127,8 @@ endif EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \ run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint \ run-gdbstub-registers run-gdbstub-prot-none \ - run-gdbstub-catch-syscalls + run-gdbstub-catch-syscalls run-gdbstub-follow-fork-mode-child \ + run-gdbstub-follow-fork-mode-parent # ARM Compatible Semi Hosting Tests # diff --git a/tests/tcg/multiarch/follow-fork-mode.c b/tests/tcg/multiarch/follow-fork-mode.c new file mode 100644 index 0000000000..cb6b032b38 --- /dev/null +++ b/tests/tcg/multiarch/follow-fork-mode.c @@ -0,0 +1,56 @@ +/* + * Test GDB's follow-fork-mode. + * + * fork() a chain of processes. + * Parents sends one byte to their children, and children return their + * position in the chain, in order to prove that they survived GDB's fork() + * handling. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include + +void break_after_fork(void) +{ +} + +int main(void) +{ + int depth = 42, err, i, fd[2], status; + pid_t child, pid; + ssize_t n; + char b; + + for (i = 0; i < depth; i++) { + err = pipe(fd); + assert(err == 0); + child = fork(); + break_after_fork(); + assert(child != -1); + if (child == 0) { + close(fd[1]); + + n = read(fd[0], &b, 1); + close(fd[0]); + assert(n == 1); + assert(b == (char)i); + } else { + close(fd[0]); + + b = (char)i; + n = write(fd[1], &b, 1); + close(fd[1]); + assert(n == 1); + + pid = waitpid(child, &status, 0); + assert(pid == child); + assert(WIFEXITED(status)); + return WEXITSTATUS(status) - 1; + } + } + + return depth; +} diff --git a/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py b/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py new file mode 100644 index 0000000000..72a6e440c0 --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py @@ -0,0 +1,40 @@ +"""Test GDB's follow-fork-mode child. + +SPDX-License-Identifier: GPL-2.0-or-later +""" +from test_gdbstub import main, report + + +def run_test(): + """Run through the tests one by one""" + gdb.execute("set follow-fork-mode child") + # Check that the parent breakpoints are unset. + gdb.execute("break break_after_fork") + # Check that the parent syscall catchpoints are unset. + # Skip this check on the architectures that don't have them. + have_fork_syscall = False + for fork_syscall in ("fork", "clone", "clone2", "clone3"): + try: + gdb.execute("catch syscall {}".format(fork_syscall)) + except gdb.error: + pass + else: + have_fork_syscall = True + gdb.execute("continue") + for i in range(42): + if have_fork_syscall: + # syscall entry. + if i % 2 == 0: + # Check that the parent single-stepping is turned off. + gdb.execute("si") + else: + gdb.execute("continue") + # syscall exit. + gdb.execute("continue") + # break_after_fork() + gdb.execute("continue") + exitcode = int(gdb.parse_and_eval("$_exitcode")) + report(exitcode == 42, "{} == 42".format(exitcode)) + + +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py b/tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py new file mode 100644 index 0000000000..5c2fe72208 --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py @@ -0,0 +1,16 @@ +"""Test GDB's follow-fork-mode parent. + +SPDX-License-Identifier: GPL-2.0-or-later +""" +from test_gdbstub import main, report + + +def run_test(): + """Run through the tests one by one""" + gdb.execute("set follow-fork-mode parent") + gdb.execute("continue") + exitcode = int(gdb.parse_and_eval("$_exitcode")) + report(exitcode == 0, "{} == 0".format(exitcode)) + + +main(run_test) From a3c2cf0b891d00ef502e9902baa9fb7fe1f8e7a3 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:09:50 +0000 Subject: [PATCH 14/29] plugins: scoreboard API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We introduce a cpu local storage, automatically managed (and extended) by QEMU itself. Plugin allocate a scoreboard, and don't have to deal with how many cpus are launched. This API will be used by new inline functions but callbacks can benefit from this as well. This way, they can operate without a global lock for simple operations. At any point during execution, any scoreboard will be dimensioned with at least qemu_plugin_num_vcpus entries. New functions: - qemu_plugin_scoreboard_find - qemu_plugin_scoreboard_free - qemu_plugin_scoreboard_new Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-2-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-15-alex.bennee@linaro.org> --- include/qemu/plugin.h | 6 ++++ include/qemu/qemu-plugin.h | 31 ++++++++++++++++++++ plugins/api.c | 19 ++++++++++++ plugins/core.c | 57 ++++++++++++++++++++++++++++++++++++ plugins/plugin.h | 6 ++++ plugins/qemu-plugins.symbols | 3 ++ 6 files changed, 122 insertions(+) diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index b3c94a34aa..bf96d2c2aa 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -112,6 +112,12 @@ struct qemu_plugin_insn { bool mem_only; }; +/* A scoreboard is an array of values, indexed by vcpu_index */ +struct qemu_plugin_scoreboard { + GArray *data; + QLIST_ENTRY(qemu_plugin_scoreboard) entry; +}; + /* * qemu_plugin_insn allocate and cleanup functions. We don't expect to * cleanup many of these structures. They are reused for each fresh diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 45e2ebc8f8..31c468ddb2 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -222,6 +222,8 @@ void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id, struct qemu_plugin_tb; /** struct qemu_plugin_insn - Opaque handle for a translated instruction */ struct qemu_plugin_insn; +/** struct qemu_plugin_scoreboard - Opaque handle for a scoreboard */ +struct qemu_plugin_scoreboard; /** * enum qemu_plugin_cb_flags - type of callback @@ -752,5 +754,34 @@ QEMU_PLUGIN_API int qemu_plugin_read_register(struct qemu_plugin_register *handle, GByteArray *buf); +/** + * qemu_plugin_scoreboard_new() - alloc a new scoreboard + * + * @element_size: size (in bytes) for one entry + * + * Returns a pointer to a new scoreboard. It must be freed using + * qemu_plugin_scoreboard_free. + */ +QEMU_PLUGIN_API +struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size); + +/** + * qemu_plugin_scoreboard_free() - free a scoreboard + * @score: scoreboard to free + */ +QEMU_PLUGIN_API +void qemu_plugin_scoreboard_free(struct qemu_plugin_scoreboard *score); + +/** + * qemu_plugin_scoreboard_find() - get pointer to an entry of a scoreboard + * @score: scoreboard to query + * @vcpu_index: entry index + * + * Returns address of entry of a scoreboard matching a given vcpu_index. This + * address can be modified later if scoreboard is resized. + */ +QEMU_PLUGIN_API +void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score, + unsigned int vcpu_index); #endif /* QEMU_QEMU_PLUGIN_H */ diff --git a/plugins/api.c b/plugins/api.c index e905e995bd..76b2e652b9 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -465,3 +465,22 @@ int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg)); } + +struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) +{ + return plugin_scoreboard_new(element_size); +} + +void qemu_plugin_scoreboard_free(struct qemu_plugin_scoreboard *score) +{ + plugin_scoreboard_free(score); +} + +void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score, + unsigned int vcpu_index) +{ + g_assert(vcpu_index < qemu_plugin_num_vcpus()); + /* we can't use g_array_index since entry size is not statically known */ + char *base_ptr = score->data->data; + return base_ptr + vcpu_index * g_array_get_element_size(score->data); +} diff --git a/plugins/core.c b/plugins/core.c index 2db4d31354..63f4c6c6ce 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -18,6 +18,7 @@ #include "qemu/lockable.h" #include "qemu/option.h" #include "qemu/plugin.h" +#include "qemu/queue.h" #include "qemu/rcu_queue.h" #include "qemu/xxhash.h" #include "qemu/rcu.h" @@ -215,6 +216,35 @@ CPUPluginState *qemu_plugin_create_vcpu_state(void) return g_new0(CPUPluginState, 1); } +static void plugin_grow_scoreboards__locked(CPUState *cpu) +{ + if (cpu->cpu_index < plugin.scoreboard_alloc_size) { + return; + } + + bool need_realloc = FALSE; + while (cpu->cpu_index >= plugin.scoreboard_alloc_size) { + plugin.scoreboard_alloc_size *= 2; + need_realloc = TRUE; + } + + + if (!need_realloc || QLIST_EMPTY(&plugin.scoreboards)) { + /* nothing to do, we just updated sizes for future scoreboards */ + return; + } + + /* cpus must be stopped, as tb might still use an existing scoreboard. */ + start_exclusive(); + struct qemu_plugin_scoreboard *score; + QLIST_FOREACH(score, &plugin.scoreboards, entry) { + g_array_set_size(score->data, plugin.scoreboard_alloc_size); + } + /* force all tb to be flushed, as scoreboard pointers were changed. */ + tb_flush(cpu); + end_exclusive(); +} + void qemu_plugin_vcpu_init_hook(CPUState *cpu) { bool success; @@ -225,6 +255,7 @@ void qemu_plugin_vcpu_init_hook(CPUState *cpu) success = g_hash_table_insert(plugin.cpu_ht, &cpu->cpu_index, &cpu->cpu_index); g_assert(success); + plugin_grow_scoreboards__locked(cpu); qemu_rec_mutex_unlock(&plugin.lock); plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT); @@ -578,6 +609,8 @@ static void __attribute__((__constructor__)) plugin_init(void) qemu_rec_mutex_init(&plugin.lock); plugin.id_ht = g_hash_table_new(g_int64_hash, g_int64_equal); plugin.cpu_ht = g_hash_table_new(g_int_hash, g_int_equal); + QLIST_INIT(&plugin.scoreboards); + plugin.scoreboard_alloc_size = 16; /* avoid frequent reallocation */ QTAILQ_INIT(&plugin.ctxs); qht_init(&plugin.dyn_cb_arr_ht, plugin_dyn_cb_arr_cmp, 16, QHT_MODE_AUTO_RESIZE); @@ -588,3 +621,27 @@ int plugin_num_vcpus(void) { return plugin.num_vcpus; } + +struct qemu_plugin_scoreboard *plugin_scoreboard_new(size_t element_size) +{ + struct qemu_plugin_scoreboard *score = + g_malloc0(sizeof(struct qemu_plugin_scoreboard)); + score->data = g_array_new(FALSE, TRUE, element_size); + g_array_set_size(score->data, plugin.scoreboard_alloc_size); + + qemu_rec_mutex_lock(&plugin.lock); + QLIST_INSERT_HEAD(&plugin.scoreboards, score, entry); + qemu_rec_mutex_unlock(&plugin.lock); + + return score; +} + +void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score) +{ + qemu_rec_mutex_lock(&plugin.lock); + QLIST_REMOVE(score, entry); + qemu_rec_mutex_unlock(&plugin.lock); + + g_array_free(score->data, TRUE); + g_free(score); +} diff --git a/plugins/plugin.h b/plugins/plugin.h index 00b3509f70..043c740067 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -31,6 +31,8 @@ struct qemu_plugin_state { * but with the HT we avoid adding a field to CPUState. */ GHashTable *cpu_ht; + QLIST_HEAD(, qemu_plugin_scoreboard) scoreboards; + size_t scoreboard_alloc_size; DECLARE_BITMAP(mask, QEMU_PLUGIN_EV_MAX); /* * @lock protects the struct as well as ctx->uninstalling. @@ -101,4 +103,8 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb); int plugin_num_vcpus(void); +struct qemu_plugin_scoreboard *plugin_scoreboard_new(size_t element_size); + +void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score); + #endif /* PLUGIN_H */ diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols index 27fe97239b..3f93e7d6b1 100644 --- a/plugins/qemu-plugins.symbols +++ b/plugins/qemu-plugins.symbols @@ -37,6 +37,9 @@ qemu_plugin_register_vcpu_tb_exec_inline; qemu_plugin_register_vcpu_tb_trans_cb; qemu_plugin_reset; + qemu_plugin_scoreboard_free; + qemu_plugin_scoreboard_find; + qemu_plugin_scoreboard_new; qemu_plugin_start_code; qemu_plugin_tb_get_insn; qemu_plugin_tb_n_insns; From 8042e2eadfd61dcdae321c3632f06d188521bdf5 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:09:51 +0000 Subject: [PATCH 15/29] plugins: define qemu_plugin_u64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additionally to the scoreboard, we define a qemu_plugin_u64, which is a simple struct holding a pointer to a scoreboard, and a given offset. This allows to have a scoreboard containing structs, without having to bring offset to operate on a specific field. Since most of the plugins are simply collecting a sum of per-cpu values, qemu_plugin_u64 directly support this operation as well. All inline operations defined later will use a qemu_plugin_u64 as input. New functions: - qemu_plugin_u64_add - qemu_plugin_u64_get - qemu_plugin_u64_set - qemu_plugin_u64_sum New macros: - qemu_plugin_scoreboard_u64 - qemu_plugin_scoreboard_u64_in_struct Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-3-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-16-alex.bennee@linaro.org> --- include/qemu/qemu-plugin.h | 52 ++++++++++++++++++++++++++++++++++++ plugins/api.c | 34 +++++++++++++++++++++++ plugins/qemu-plugins.symbols | 4 +++ 3 files changed, 90 insertions(+) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 31c468ddb2..ebf9a645e1 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -225,6 +225,17 @@ struct qemu_plugin_insn; /** struct qemu_plugin_scoreboard - Opaque handle for a scoreboard */ struct qemu_plugin_scoreboard; +/** + * typedef qemu_plugin_u64 - uint64_t member of an entry in a scoreboard + * + * This field allows to access a specific uint64_t member in one given entry, + * located at a specified offset. Inline operations expect this as entry. + */ +typedef struct { + struct qemu_plugin_scoreboard *score; + size_t offset; +} qemu_plugin_u64; + /** * enum qemu_plugin_cb_flags - type of callback * @@ -784,4 +795,45 @@ QEMU_PLUGIN_API void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score, unsigned int vcpu_index); +/* Macros to define a qemu_plugin_u64 */ +#define qemu_plugin_scoreboard_u64(score) \ + (qemu_plugin_u64) {score, 0} +#define qemu_plugin_scoreboard_u64_in_struct(score, type, member) \ + (qemu_plugin_u64) {score, offsetof(type, member)} + +/** + * qemu_plugin_u64_add() - add a value to a qemu_plugin_u64 for a given vcpu + * @entry: entry to query + * @vcpu_index: entry index + * @added: value to add + */ +QEMU_PLUGIN_API +void qemu_plugin_u64_add(qemu_plugin_u64 entry, unsigned int vcpu_index, + uint64_t added); + +/** + * qemu_plugin_u64_get() - get value of a qemu_plugin_u64 for a given vcpu + * @entry: entry to query + * @vcpu_index: entry index + */ +QEMU_PLUGIN_API +uint64_t qemu_plugin_u64_get(qemu_plugin_u64 entry, unsigned int vcpu_index); + +/** + * qemu_plugin_u64_set() - set value of a qemu_plugin_u64 for a given vcpu + * @entry: entry to query + * @vcpu_index: entry index + * @val: new value + */ +QEMU_PLUGIN_API +void qemu_plugin_u64_set(qemu_plugin_u64 entry, unsigned int vcpu_index, + uint64_t val); + +/** + * qemu_plugin_u64_sum() - return sum of all vcpu entries in a scoreboard + * @entry: entry to sum + */ +QEMU_PLUGIN_API +uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry); + #endif /* QEMU_QEMU_PLUGIN_H */ diff --git a/plugins/api.c b/plugins/api.c index 76b2e652b9..8910cbb2c4 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -484,3 +484,37 @@ void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score, char *base_ptr = score->data->data; return base_ptr + vcpu_index * g_array_get_element_size(score->data); } + +static uint64_t *plugin_u64_address(qemu_plugin_u64 entry, + unsigned int vcpu_index) +{ + char *ptr = qemu_plugin_scoreboard_find(entry.score, vcpu_index); + return (uint64_t *)(ptr + entry.offset); +} + +void qemu_plugin_u64_add(qemu_plugin_u64 entry, unsigned int vcpu_index, + uint64_t added) +{ + *plugin_u64_address(entry, vcpu_index) += added; +} + +uint64_t qemu_plugin_u64_get(qemu_plugin_u64 entry, + unsigned int vcpu_index) +{ + return *plugin_u64_address(entry, vcpu_index); +} + +void qemu_plugin_u64_set(qemu_plugin_u64 entry, unsigned int vcpu_index, + uint64_t val) +{ + *plugin_u64_address(entry, vcpu_index) = val; +} + +uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry) +{ + uint64_t total = 0; + for (int i = 0, n = qemu_plugin_num_vcpus(); i < n; ++i) { + total += qemu_plugin_u64_get(entry, i); + } + return total; +} diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols index 3f93e7d6b1..6204453d0f 100644 --- a/plugins/qemu-plugins.symbols +++ b/plugins/qemu-plugins.symbols @@ -44,6 +44,10 @@ qemu_plugin_tb_get_insn; qemu_plugin_tb_n_insns; qemu_plugin_tb_vaddr; + qemu_plugin_u64_add; + qemu_plugin_u64_get; + qemu_plugin_u64_set; + qemu_plugin_u64_sum; qemu_plugin_uninstall; qemu_plugin_vcpu_for_each; }; From 62f92b8d978aba5de931323c3de788ccb2dbd87c Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:09:52 +0000 Subject: [PATCH 16/29] plugins: implement inline operation relative to cpu_index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of working on a fixed memory location, allow to address it based on cpu_index, an element size and a given offset. Result address: ptr + offset + cpu_index * element_size. With this, we can target a member in a struct array from a base pointer. Current semantic is not modified, thus inline operation still targets always the same memory location. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-4-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-17-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 69 ++++++++++++++++++++++++++++++++++-------- plugins/api.c | 3 +- plugins/core.c | 12 +++++--- plugins/plugin.h | 2 +- 4 files changed, 67 insertions(+), 19 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index ac6b52b9ec..0f8be53d39 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -133,16 +133,28 @@ static void gen_empty_udata_cb_no_rwg(void) */ static void gen_empty_inline_cb(void) { + TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); + TCGv_ptr cpu_index_as_ptr = tcg_temp_ebb_new_ptr(); TCGv_i64 val = tcg_temp_ebb_new_i64(); TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); + tcg_gen_ld_i32(cpu_index, tcg_env, + -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + /* second operand will be replaced by immediate value */ + tcg_gen_mul_i32(cpu_index, cpu_index, cpu_index); + tcg_gen_ext_i32_ptr(cpu_index_as_ptr, cpu_index); + tcg_gen_movi_ptr(ptr, 0); + tcg_gen_add_ptr(ptr, ptr, cpu_index_as_ptr); tcg_gen_ld_i64(val, ptr, 0); - /* pass an immediate != 0 so that it doesn't get optimized away */ - tcg_gen_addi_i64(val, val, 0xdeadface); + /* second operand will be replaced by immediate value */ + tcg_gen_add_i64(val, val, val); + tcg_gen_st_i64(val, ptr, 0); tcg_temp_free_ptr(ptr); tcg_temp_free_i64(val); + tcg_temp_free_ptr(cpu_index_as_ptr); + tcg_temp_free_i32(cpu_index); } static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info) @@ -290,12 +302,37 @@ static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr) return op; } +static TCGOp *copy_ld_i32(TCGOp **begin_op, TCGOp *op) +{ + return copy_op(begin_op, op, INDEX_op_ld_i32); +} + +static TCGOp *copy_ext_i32_ptr(TCGOp **begin_op, TCGOp *op) +{ + if (UINTPTR_MAX == UINT32_MAX) { + op = copy_op(begin_op, op, INDEX_op_mov_i32); + } else { + op = copy_op(begin_op, op, INDEX_op_ext_i32_i64); + } + return op; +} + +static TCGOp *copy_add_ptr(TCGOp **begin_op, TCGOp *op) +{ + if (UINTPTR_MAX == UINT32_MAX) { + op = copy_op(begin_op, op, INDEX_op_add_i32); + } else { + op = copy_op(begin_op, op, INDEX_op_add_i64); + } + return op; +} + static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op) { if (TCG_TARGET_REG_BITS == 32) { /* 2x ld_i32 */ - op = copy_op(begin_op, op, INDEX_op_ld_i32); - op = copy_op(begin_op, op, INDEX_op_ld_i32); + op = copy_ld_i32(begin_op, op); + op = copy_ld_i32(begin_op, op); } else { /* ld_i64 */ op = copy_op(begin_op, op, INDEX_op_ld_i64); @@ -331,6 +368,13 @@ static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op, uint64_t v) return op; } +static TCGOp *copy_mul_i32(TCGOp **begin_op, TCGOp *op, uint32_t v) +{ + op = copy_op(begin_op, op, INDEX_op_mul_i32); + op->args[2] = tcgv_i32_arg(tcg_constant_i32(v)); + return op; +} + static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op) { if (UINTPTR_MAX == UINT32_MAX) { @@ -396,18 +440,17 @@ static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb, TCGOp *begin_op, TCGOp *op, int *unused) { - /* const_ptr */ - op = copy_const_ptr(&begin_op, op, cb->userp); - - /* ld_i64 */ + char *ptr = cb->userp; + size_t elem_size = 0; + size_t offset = 0; + op = copy_ld_i32(&begin_op, op); + op = copy_mul_i32(&begin_op, op, elem_size); + op = copy_ext_i32_ptr(&begin_op, op); + op = copy_const_ptr(&begin_op, op, ptr + offset); + op = copy_add_ptr(&begin_op, op); op = copy_ld_i64(&begin_op, op); - - /* add_i64 */ op = copy_add_i64(&begin_op, op, cb->inline_insn.imm); - - /* st_i64 */ op = copy_st_i64(&begin_op, op); - return op; } diff --git a/plugins/api.c b/plugins/api.c index 8910cbb2c4..fa1daee825 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -106,7 +106,8 @@ void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, void *ptr, uint64_t imm) { if (!tb->mem_only) { - plugin_register_inline_op(&tb->cbs[PLUGIN_CB_INLINE], 0, op, ptr, imm); + plugin_register_inline_op(&tb->cbs[PLUGIN_CB_INLINE], + 0, op, ptr, imm); } } diff --git a/plugins/core.c b/plugins/core.c index 63f4c6c6ce..65d5611f79 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -318,7 +318,8 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr) void plugin_register_inline_op(GArray **arr, enum qemu_plugin_mem_rw rw, - enum qemu_plugin_op op, void *ptr, + enum qemu_plugin_op op, + void *ptr, uint64_t imm) { struct qemu_plugin_dyn_cb *dyn_cb; @@ -474,9 +475,12 @@ void qemu_plugin_flush_cb(void) plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH); } -void exec_inline_op(struct qemu_plugin_dyn_cb *cb) +void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index) { - uint64_t *val = cb->userp; + char *ptr = cb->userp; + size_t elem_size = 0; + size_t offset = 0; + uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size); switch (cb->inline_insn.op) { case QEMU_PLUGIN_INLINE_ADD_U64: @@ -509,7 +513,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, vaddr, cb->userp); break; case PLUGIN_CB_INLINE: - exec_inline_op(cb); + exec_inline_op(cb, cpu->cpu_index); break; default: g_assert_not_reached(); diff --git a/plugins/plugin.h b/plugins/plugin.h index 043c740067..3bf1aaf5c2 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -99,7 +99,7 @@ void plugin_register_vcpu_mem_cb(GArray **arr, enum qemu_plugin_mem_rw rw, void *udata); -void exec_inline_op(struct qemu_plugin_dyn_cb *cb); +void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index); int plugin_num_vcpus(void); From 0bcebaba45c2cc3e671e002f3c15266f0ff03b8c Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:09:53 +0000 Subject: [PATCH 17/29] plugins: add inline operation per vcpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends API with three new functions: qemu_plugin_register_vcpu_{tb, insn, mem}_exec_inline_per_vcpu(). Those functions takes a qemu_plugin_u64 as input. This allows to have a thread-safe and type-safe version of inline operations. Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-5-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-18-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 7 +++++ include/qemu/plugin.h | 1 + include/qemu/qemu-plugin.h | 51 +++++++++++++++++++++++++++++++++++- plugins/api.c | 37 +++++++++++++++++++++++++- plugins/core.c | 23 ++++++++++++++++ plugins/plugin.h | 6 +++++ plugins/qemu-plugins.symbols | 3 +++ 7 files changed, 126 insertions(+), 2 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 0f8be53d39..47e05ec634 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -443,6 +443,13 @@ static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb, char *ptr = cb->userp; size_t elem_size = 0; size_t offset = 0; + if (!ptr) { + /* use inline entry */ + ptr = cb->inline_insn.entry.score->data->data; + elem_size = g_array_get_element_size(cb->inline_insn.entry.score->data); + offset = cb->inline_insn.entry.offset; + } + op = copy_ld_i32(&begin_op, op); op = copy_mul_i32(&begin_op, op, elem_size); op = copy_ext_i32_ptr(&begin_op, op); diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index bf96d2c2aa..12a96cea2a 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -92,6 +92,7 @@ struct qemu_plugin_dyn_cb { /* fields specific to each dyn_cb type go here */ union { struct { + qemu_plugin_u64 entry; enum qemu_plugin_op op; uint64_t imm; } inline_insn; diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index ebf9a645e1..6bbad068c0 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -328,6 +328,22 @@ void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, enum qemu_plugin_op op, void *ptr, uint64_t imm); +/** + * qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu() - execution inline op + * @tb: the opaque qemu_plugin_tb handle for the translation + * @op: the type of qemu_plugin_op (e.g. ADD_U64) + * @entry: entry to run op + * @imm: the op data (e.g. 1) + * + * Insert an inline op on a given scoreboard entry. + */ +QEMU_PLUGIN_API +void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + struct qemu_plugin_tb *tb, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm); + /** * qemu_plugin_register_vcpu_insn_exec_cb() - register insn execution cb * @insn: the opaque qemu_plugin_insn handle for an instruction @@ -358,6 +374,22 @@ void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, enum qemu_plugin_op op, void *ptr, uint64_t imm); +/** + * qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu() - insn exec inline op + * @insn: the opaque qemu_plugin_insn handle for an instruction + * @op: the type of qemu_plugin_op (e.g. ADD_U64) + * @entry: entry to run op + * @imm: the op data (e.g. 1) + * + * Insert an inline op to every time an instruction executes. + */ +QEMU_PLUGIN_API +void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + struct qemu_plugin_insn *insn, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm); + /** * qemu_plugin_tb_n_insns() - query helper for number of insns in TB * @tb: opaque handle to TB passed to callback @@ -583,7 +615,24 @@ void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, enum qemu_plugin_op op, void *ptr, uint64_t imm); - +/** + * qemu_plugin_register_vcpu_mem_inline_per_vcpu() - inline op for mem access + * @insn: handle for instruction to instrument + * @rw: apply to reads, writes or both + * @op: the op, of type qemu_plugin_op + * @entry: entry to run op + * @imm: immediate data for @op + * + * This registers a inline op every memory access generated by the + * instruction. + */ +QEMU_PLUGIN_API +void qemu_plugin_register_vcpu_mem_inline_per_vcpu( + struct qemu_plugin_insn *insn, + enum qemu_plugin_mem_rw rw, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm); typedef void (*qemu_plugin_vcpu_syscall_cb_t)(qemu_plugin_id_t id, unsigned int vcpu_index, diff --git a/plugins/api.c b/plugins/api.c index fa1daee825..6470f1dc0f 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -111,6 +111,18 @@ void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, } } +void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + struct qemu_plugin_tb *tb, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm) +{ + if (!tb->mem_only) { + plugin_register_inline_op_on_entry( + &tb->cbs[PLUGIN_CB_INLINE], 0, op, entry, imm); + } +} + void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, qemu_plugin_vcpu_udata_cb_t cb, enum qemu_plugin_cb_flags flags, @@ -136,6 +148,18 @@ void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, } } +void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + struct qemu_plugin_insn *insn, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm) +{ + if (!insn->mem_only) { + plugin_register_inline_op_on_entry( + &insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], 0, op, entry, imm); + } +} + /* * We always plant memory instrumentation because they don't finalise until @@ -148,7 +172,7 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, void *udata) { plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], - cb, flags, rw, udata); + cb, flags, rw, udata); } void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, @@ -160,6 +184,17 @@ void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, rw, op, ptr, imm); } +void qemu_plugin_register_vcpu_mem_inline_per_vcpu( + struct qemu_plugin_insn *insn, + enum qemu_plugin_mem_rw rw, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm) +{ + plugin_register_inline_op_on_entry( + &insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], rw, op, entry, imm); +} + void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_tb_trans_cb_t cb) { diff --git a/plugins/core.c b/plugins/core.c index 65d5611f79..7852590da8 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -332,6 +332,23 @@ void plugin_register_inline_op(GArray **arr, dyn_cb->inline_insn.imm = imm; } +void plugin_register_inline_op_on_entry(GArray **arr, + enum qemu_plugin_mem_rw rw, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm) +{ + struct qemu_plugin_dyn_cb *dyn_cb; + + dyn_cb = plugin_get_dyn_cb(arr); + dyn_cb->userp = NULL; + dyn_cb->type = PLUGIN_CB_INLINE; + dyn_cb->rw = rw; + dyn_cb->inline_insn.entry = entry; + dyn_cb->inline_insn.op = op; + dyn_cb->inline_insn.imm = imm; +} + void plugin_register_dyn_cb__udata(GArray **arr, qemu_plugin_vcpu_udata_cb_t cb, enum qemu_plugin_cb_flags flags, @@ -480,6 +497,12 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index) char *ptr = cb->userp; size_t elem_size = 0; size_t offset = 0; + if (!ptr) { + /* use inline entry */ + ptr = cb->inline_insn.entry.score->data->data; + elem_size = g_array_get_element_size(cb->inline_insn.entry.score->data); + offset = cb->inline_insn.entry.offset; + } uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size); switch (cb->inline_insn.op) { diff --git a/plugins/plugin.h b/plugins/plugin.h index 3bf1aaf5c2..f6fa10a0f5 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -73,6 +73,12 @@ void plugin_register_inline_op(GArray **arr, enum qemu_plugin_op op, void *ptr, uint64_t imm); +void plugin_register_inline_op_on_entry(GArray **arr, + enum qemu_plugin_mem_rw rw, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm); + void plugin_reset_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb, bool reset); diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols index 6204453d0f..0d8141b85f 100644 --- a/plugins/qemu-plugins.symbols +++ b/plugins/qemu-plugins.symbols @@ -28,13 +28,16 @@ qemu_plugin_register_vcpu_init_cb; qemu_plugin_register_vcpu_insn_exec_cb; qemu_plugin_register_vcpu_insn_exec_inline; + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu; qemu_plugin_register_vcpu_mem_cb; qemu_plugin_register_vcpu_mem_inline; + qemu_plugin_register_vcpu_mem_inline_per_vcpu; qemu_plugin_register_vcpu_resume_cb; qemu_plugin_register_vcpu_syscall_cb; qemu_plugin_register_vcpu_syscall_ret_cb; qemu_plugin_register_vcpu_tb_exec_cb; qemu_plugin_register_vcpu_tb_exec_inline; + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu; qemu_plugin_register_vcpu_tb_trans_cb; qemu_plugin_reset; qemu_plugin_scoreboard_free; From b480f7a621c8a896aa7a79b22f179c3c153f3adc Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:09:54 +0000 Subject: [PATCH 18/29] tests/plugin: add test plugin for inline operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For now, it simply performs instruction, bb and mem count, and ensure that inline vs callback versions have the same result. Later, we'll extend it when new inline operations are added. Use existing plugins to test everything works is a bit cumbersome, as different events are treated in different plugins. Thus, this new one. Reviewed-by: Alex Bennée Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-6-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-19-alex.bennee@linaro.org> --- tests/plugin/inline.c | 186 +++++++++++++++++++++++++++++++++++++++ tests/plugin/meson.build | 2 +- 2 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 tests/plugin/inline.c diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c new file mode 100644 index 0000000000..0163e9b51c --- /dev/null +++ b/tests/plugin/inline.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2023, Pierrick Bouvier + * + * Demonstrates and tests usage of inline ops. + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +#include + +#include + +typedef struct { + uint64_t count_tb; + uint64_t count_tb_inline; + uint64_t count_insn; + uint64_t count_insn_inline; + uint64_t count_mem; + uint64_t count_mem_inline; +} CPUCount; + +static struct qemu_plugin_scoreboard *counts; +static qemu_plugin_u64 count_tb; +static qemu_plugin_u64 count_tb_inline; +static qemu_plugin_u64 count_insn; +static qemu_plugin_u64 count_insn_inline; +static qemu_plugin_u64 count_mem; +static qemu_plugin_u64 count_mem_inline; + +static uint64_t global_count_tb; +static uint64_t global_count_insn; +static uint64_t global_count_mem; +static unsigned int max_cpu_index; +static GMutex tb_lock; +static GMutex insn_lock; +static GMutex mem_lock; + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +static void stats_insn(void) +{ + const uint64_t expected = global_count_insn; + const uint64_t per_vcpu = qemu_plugin_u64_sum(count_insn); + const uint64_t inl_per_vcpu = + qemu_plugin_u64_sum(count_insn_inline); + printf("insn: %" PRIu64 "\n", expected); + printf("insn: %" PRIu64 " (per vcpu)\n", per_vcpu); + printf("insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + g_assert(expected > 0); + g_assert(per_vcpu == expected); + g_assert(inl_per_vcpu == expected); +} + +static void stats_tb(void) +{ + const uint64_t expected = global_count_tb; + const uint64_t per_vcpu = qemu_plugin_u64_sum(count_tb); + const uint64_t inl_per_vcpu = + qemu_plugin_u64_sum(count_tb_inline); + printf("tb: %" PRIu64 "\n", expected); + printf("tb: %" PRIu64 " (per vcpu)\n", per_vcpu); + printf("tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + g_assert(expected > 0); + g_assert(per_vcpu == expected); + g_assert(inl_per_vcpu == expected); +} + +static void stats_mem(void) +{ + const uint64_t expected = global_count_mem; + const uint64_t per_vcpu = qemu_plugin_u64_sum(count_mem); + const uint64_t inl_per_vcpu = + qemu_plugin_u64_sum(count_mem_inline); + printf("mem: %" PRIu64 "\n", expected); + printf("mem: %" PRIu64 " (per vcpu)\n", per_vcpu); + printf("mem: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + g_assert(expected > 0); + g_assert(per_vcpu == expected); + g_assert(inl_per_vcpu == expected); +} + +static void plugin_exit(qemu_plugin_id_t id, void *udata) +{ + const unsigned int num_cpus = qemu_plugin_num_vcpus(); + g_assert(num_cpus == max_cpu_index + 1); + + for (int i = 0; i < num_cpus ; ++i) { + const uint64_t tb = qemu_plugin_u64_get(count_tb, i); + const uint64_t tb_inline = qemu_plugin_u64_get(count_tb_inline, i); + const uint64_t insn = qemu_plugin_u64_get(count_insn, i); + const uint64_t insn_inline = qemu_plugin_u64_get(count_insn_inline, i); + const uint64_t mem = qemu_plugin_u64_get(count_mem, i); + const uint64_t mem_inline = qemu_plugin_u64_get(count_mem_inline, i); + printf("cpu %d: tb (%" PRIu64 ", %" PRIu64 ") | " + "insn (%" PRIu64 ", %" PRIu64 ") | " + "mem (%" PRIu64 ", %" PRIu64 ")" + "\n", + i, tb, tb_inline, insn, insn_inline, mem, mem_inline); + g_assert(tb == tb_inline); + g_assert(insn == insn_inline); + g_assert(mem == mem_inline); + } + + stats_tb(); + stats_insn(); + stats_mem(); + + qemu_plugin_scoreboard_free(counts); +} + +static void vcpu_tb_exec(unsigned int cpu_index, void *udata) +{ + qemu_plugin_u64_add(count_tb, cpu_index, 1); + g_mutex_lock(&tb_lock); + max_cpu_index = MAX(max_cpu_index, cpu_index); + global_count_tb++; + g_mutex_unlock(&tb_lock); +} + +static void vcpu_insn_exec(unsigned int cpu_index, void *udata) +{ + qemu_plugin_u64_add(count_insn, cpu_index, 1); + g_mutex_lock(&insn_lock); + global_count_insn++; + g_mutex_unlock(&insn_lock); +} + +static void vcpu_mem_access(unsigned int cpu_index, + qemu_plugin_meminfo_t info, + uint64_t vaddr, + void *userdata) +{ + qemu_plugin_u64_add(count_mem, cpu_index, 1); + g_mutex_lock(&mem_lock); + global_count_mem++; + g_mutex_unlock(&mem_lock); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + qemu_plugin_register_vcpu_tb_exec_cb( + tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, 0); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, count_tb_inline, 1); + + for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx); + qemu_plugin_register_vcpu_insn_exec_cb( + insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, 0); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, count_insn_inline, 1); + qemu_plugin_register_vcpu_mem_cb(insn, &vcpu_mem_access, + QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_MEM_RW, 0); + qemu_plugin_register_vcpu_mem_inline_per_vcpu( + insn, QEMU_PLUGIN_MEM_RW, + QEMU_PLUGIN_INLINE_ADD_U64, + count_mem_inline, 1); + } +} + +QEMU_PLUGIN_EXPORT +int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, + int argc, char **argv) +{ + counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); + count_tb = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_tb); + count_insn = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_insn); + count_mem = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_mem); + count_tb_inline = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_tb_inline); + count_insn_inline = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_insn_inline); + count_mem_inline = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_mem_inline); + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + + return 0; +} diff --git a/tests/plugin/meson.build b/tests/plugin/meson.build index e18183aaed..9eece5bab5 100644 --- a/tests/plugin/meson.build +++ b/tests/plugin/meson.build @@ -1,6 +1,6 @@ t = [] if get_option('plugins') - foreach i : ['bb', 'empty', 'insn', 'mem', 'syscall'] + foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'syscall'] if host_os == 'windows' t += shared_module(i, files(i + '.c') + '../../contrib/plugins/win32_linker.c', include_directories: '../../include/qemu', From 4f8d886085de51cbe9699078fd8264450ba2e09a Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:09:55 +0000 Subject: [PATCH 19/29] tests/plugin/mem: migrate to new per_vcpu API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luc Michel Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-7-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-20-alex.bennee@linaro.org> --- tests/plugin/mem.c | 46 ++++++++++++++++++++++++++------------- tests/tcg/Makefile.target | 2 +- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/tests/plugin/mem.c b/tests/plugin/mem.c index 44e91065ba..b650dddcce 100644 --- a/tests/plugin/mem.c +++ b/tests/plugin/mem.c @@ -16,9 +16,14 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; -static uint64_t inline_mem_count; -static uint64_t cb_mem_count; -static uint64_t io_count; +typedef struct { + uint64_t mem_count; + uint64_t io_count; +} CPUCount; + +static struct qemu_plugin_scoreboard *counts; +static qemu_plugin_u64 mem_count; +static qemu_plugin_u64 io_count; static bool do_inline, do_callback; static bool do_haddr; static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; @@ -27,16 +32,16 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) out = g_string_new(""); - if (do_inline) { - g_string_printf(out, "inline mem accesses: %" PRIu64 "\n", inline_mem_count); - } - if (do_callback) { - g_string_append_printf(out, "callback mem accesses: %" PRIu64 "\n", cb_mem_count); + if (do_inline || do_callback) { + g_string_printf(out, "mem accesses: %" PRIu64 "\n", + qemu_plugin_u64_sum(mem_count)); } if (do_haddr) { - g_string_append_printf(out, "io accesses: %" PRIu64 "\n", io_count); + g_string_append_printf(out, "io accesses: %" PRIu64 "\n", + qemu_plugin_u64_sum(io_count)); } qemu_plugin_outs(out->str); + qemu_plugin_scoreboard_free(counts); } static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, @@ -46,12 +51,12 @@ static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, struct qemu_plugin_hwaddr *hwaddr; hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr); if (qemu_plugin_hwaddr_is_io(hwaddr)) { - io_count++; + qemu_plugin_u64_add(io_count, cpu_index, 1); } else { - cb_mem_count++; + qemu_plugin_u64_add(mem_count, cpu_index, 1); } } else { - cb_mem_count++; + qemu_plugin_u64_add(mem_count, cpu_index, 1); } } @@ -64,9 +69,10 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); if (do_inline) { - qemu_plugin_register_vcpu_mem_inline(insn, rw, - QEMU_PLUGIN_INLINE_ADD_U64, - &inline_mem_count, 1); + qemu_plugin_register_vcpu_mem_inline_per_vcpu( + insn, rw, + QEMU_PLUGIN_INLINE_ADD_U64, + mem_count, 1); } if (do_callback) { qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem, @@ -117,6 +123,16 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } } + if (do_inline && do_callback) { + fprintf(stderr, + "can't enable inline and callback counting at the same time\n"); + return -1; + } + + counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); + mem_count = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, mem_count); + io_count = qemu_plugin_scoreboard_u64_in_struct(counts, CPUCount, io_count); qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); return 0; diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index a4c25908fb..f21be50d3b 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -168,7 +168,7 @@ RUN_TESTS+=$(EXTRA_RUNS) # Some plugins need additional arguments above the default to fully # exercise things. We can define them on a per-test basis here. -run-plugin-%-with-libmem.so: PLUGIN_ARGS=$(COMMA)inline=true$(COMMA)callback=true +run-plugin-%-with-libmem.so: PLUGIN_ARGS=$(COMMA)inline=true ifeq ($(filter %-softmmu, $(TARGET)),) run-%: % From 3d15c8d87e45d104da1485a4b34d8d6808935fbd Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:09:56 +0000 Subject: [PATCH 20/29] tests/plugin/insn: migrate to new per_vcpu API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luc Michel Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-8-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-21-alex.bennee@linaro.org> --- tests/plugin/insn.c | 106 +++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 56 deletions(-) diff --git a/tests/plugin/insn.c b/tests/plugin/insn.c index 54da06fcf2..5e0aa03223 100644 --- a/tests/plugin/insn.c +++ b/tests/plugin/insn.c @@ -16,25 +16,21 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; -#define MAX_CPUS 8 /* lets not go nuts */ - -typedef struct { - uint64_t insn_count; -} InstructionCount; - -static InstructionCount counts[MAX_CPUS]; -static uint64_t inline_insn_count; +static qemu_plugin_u64 insn_count; static bool do_inline; static bool do_size; static GArray *sizes; +typedef struct { + uint64_t hits; + uint64_t last_hit; + uint64_t total_delta; +} MatchCount; + typedef struct { char *match_string; - uint64_t hits[MAX_CPUS]; - uint64_t last_hit[MAX_CPUS]; - uint64_t total_delta[MAX_CPUS]; - GPtrArray *history[MAX_CPUS]; + struct qemu_plugin_scoreboard *counts; /* MatchCount */ } Match; static GArray *matches; @@ -67,41 +63,40 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata) { - unsigned int i = cpu_index % MAX_CPUS; - InstructionCount *c = &counts[i]; - - c->insn_count++; + qemu_plugin_u64_add(insn_count, cpu_index, 1); } static void vcpu_insn_matched_exec_before(unsigned int cpu_index, void *udata) { - unsigned int i = cpu_index % MAX_CPUS; Instruction *insn = (Instruction *) udata; - Match *match = insn->match; + Match *insn_match = insn->match; + MatchCount *match = qemu_plugin_scoreboard_find(insn_match->counts, + cpu_index); + g_autoptr(GString) ts = g_string_new(""); insn->hits++; g_string_append_printf(ts, "0x%" PRIx64 ", '%s', %"PRId64 " hits", insn->vaddr, insn->disas, insn->hits); - uint64_t icount = counts[i].insn_count; - uint64_t delta = icount - match->last_hit[i]; + uint64_t icount = qemu_plugin_u64_get(insn_count, cpu_index); + uint64_t delta = icount - match->last_hit; - match->hits[i]++; - match->total_delta[i] += delta; + match->hits++; + match->total_delta += delta; g_string_append_printf(ts, - ", %"PRId64" match hits, " - "Δ+%"PRId64 " since last match," + " , cpu %u," + " %"PRId64" match hits," + " Δ+%"PRId64 " since last match," " %"PRId64 " avg insns/match\n", - match->hits[i], delta, - match->total_delta[i] / match->hits[i]); + cpu_index, + match->hits, delta, + match->total_delta / match->hits); - match->last_hit[i] = icount; + match->last_hit = icount; qemu_plugin_outs(ts->str); - - g_ptr_array_add(match->history[i], insn); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -113,8 +108,8 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); if (do_inline) { - qemu_plugin_register_vcpu_insn_exec_inline( - insn, QEMU_PLUGIN_INLINE_ADD_U64, &inline_insn_count, 1); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); } else { uint64_t vaddr = qemu_plugin_insn_vaddr(insn); qemu_plugin_register_vcpu_insn_exec_cb( @@ -136,10 +131,9 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) * information about the instruction which we also need to * save if there is a hit. */ - if (matches) { + if (matches->len) { char *insn_disas = qemu_plugin_insn_disas(insn); - int j; - for (j = 0; j < matches->len; j++) { + for (int j = 0; j < matches->len; j++) { Match *m = &g_array_index(matches, Match, j); if (g_str_has_prefix(insn_disas, m->match_string)) { Instruction *rec = g_new0(Instruction, 1); @@ -169,36 +163,33 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) "len %d bytes: %ld insns\n", i, *cnt); } } - } else if (do_inline) { - g_string_append_printf(out, "insns: %" PRIu64 "\n", inline_insn_count); } else { - uint64_t total_insns = 0; - for (i = 0; i < MAX_CPUS; i++) { - InstructionCount *c = &counts[i]; - if (c->insn_count) { - g_string_append_printf(out, "cpu %d insns: %" PRIu64 "\n", - i, c->insn_count); - total_insns += c->insn_count; - } + for (i = 0; i < qemu_plugin_num_vcpus(); i++) { + g_string_append_printf(out, "cpu %d insns: %" PRIu64 "\n", + i, qemu_plugin_u64_get(insn_count, i)); } g_string_append_printf(out, "total insns: %" PRIu64 "\n", - total_insns); + qemu_plugin_u64_sum(insn_count)); } qemu_plugin_outs(out->str); + + qemu_plugin_scoreboard_free(insn_count.score); + for (i = 0; i < matches->len; ++i) { + Match *m = &g_array_index(matches, Match, i); + g_free(m->match_string); + qemu_plugin_scoreboard_free(m->counts); + } + g_array_free(matches, TRUE); + g_array_free(sizes, TRUE); } /* Add a match to the array of matches */ static void parse_match(char *match) { - Match new_match = { .match_string = match }; - int i; - for (i = 0; i < MAX_CPUS; i++) { - new_match.history[i] = g_ptr_array_new(); - } - if (!matches) { - matches = g_array_new(false, true, sizeof(Match)); - } + Match new_match = { + .match_string = g_strdup(match), + .counts = qemu_plugin_scoreboard_new(sizeof(MatchCount)) }; g_array_append_val(matches, new_match); } @@ -206,6 +197,10 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { + matches = g_array_new(false, true, sizeof(Match)); + /* null terminated so 0 is not a special case */ + sizes = g_array_new(true, true, sizeof(unsigned long)); + for (int i = 0; i < argc; i++) { char *opt = argv[i]; g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); @@ -227,9 +222,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } } - if (do_size) { - sizes = g_array_new(true, true, sizeof(unsigned long)); - } + insn_count = qemu_plugin_scoreboard_u64( + qemu_plugin_scoreboard_new(sizeof(uint64_t))); /* Register init, translation block and exit callbacks */ qemu_plugin_register_vcpu_init_cb(id, vcpu_init); From dd0f775074b62ac9ac7159f6d04d1dcc2fe09626 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:09:57 +0000 Subject: [PATCH 21/29] tests/plugin/bb: migrate to new per_vcpu API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luc Michel Reviewed-by: Alex Bennée Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-9-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-22-alex.bennee@linaro.org> --- tests/plugin/bb.c | 63 +++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 37 deletions(-) diff --git a/tests/plugin/bb.c b/tests/plugin/bb.c index df50d1fd3b..36776dee1e 100644 --- a/tests/plugin/bb.c +++ b/tests/plugin/bb.c @@ -17,27 +17,25 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; typedef struct { - GMutex lock; - int index; uint64_t bb_count; uint64_t insn_count; } CPUCount; -/* Used by the inline & linux-user counts */ -static bool do_inline; -static CPUCount inline_count; +static struct qemu_plugin_scoreboard *counts; +static qemu_plugin_u64 bb_count; +static qemu_plugin_u64 insn_count; +static bool do_inline; /* Dump running CPU total on idle? */ static bool idle_report; -static GPtrArray *counts; -static int max_cpus; -static void gen_one_cpu_report(CPUCount *count, GString *report) +static void gen_one_cpu_report(CPUCount *count, GString *report, + unsigned int cpu_index) { if (count->bb_count) { g_string_append_printf(report, "CPU%d: " "bb's: %" PRIu64", insns: %" PRIu64 "\n", - count->index, + cpu_index, count->bb_count, count->insn_count); } } @@ -46,20 +44,23 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) report = g_string_new(""); - if (do_inline || !max_cpus) { - g_string_printf(report, "bb's: %" PRIu64", insns: %" PRIu64 "\n", - inline_count.bb_count, inline_count.insn_count); - } else { - g_ptr_array_foreach(counts, (GFunc) gen_one_cpu_report, report); + for (int i = 0; i < qemu_plugin_num_vcpus(); ++i) { + CPUCount *count = qemu_plugin_scoreboard_find(counts, i); + gen_one_cpu_report(count, report, i); } + g_string_append_printf(report, "Total: " + "bb's: %" PRIu64", insns: %" PRIu64 "\n", + qemu_plugin_u64_sum(bb_count), + qemu_plugin_u64_sum(insn_count)); qemu_plugin_outs(report->str); + qemu_plugin_scoreboard_free(counts); } static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index) { - CPUCount *count = g_ptr_array_index(counts, cpu_index); + CPUCount *count = qemu_plugin_scoreboard_find(counts, cpu_index); g_autoptr(GString) report = g_string_new(""); - gen_one_cpu_report(count, report); + gen_one_cpu_report(count, report, cpu_index); if (report->len > 0) { g_string_prepend(report, "Idling "); @@ -69,14 +70,11 @@ static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index) static void vcpu_tb_exec(unsigned int cpu_index, void *udata) { - CPUCount *count = max_cpus ? - g_ptr_array_index(counts, cpu_index) : &inline_count; + CPUCount *count = qemu_plugin_scoreboard_find(counts, cpu_index); uintptr_t n_insns = (uintptr_t)udata; - g_mutex_lock(&count->lock); count->insn_count += n_insns; count->bb_count++; - g_mutex_unlock(&count->lock); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -84,11 +82,10 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) size_t n_insns = qemu_plugin_tb_n_insns(tb); if (do_inline) { - qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64, - &inline_count.bb_count, 1); - qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64, - &inline_count.insn_count, - n_insns); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, bb_count, 1); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, n_insns); } else { qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, @@ -121,18 +118,10 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } } - if (info->system_emulation && !do_inline) { - max_cpus = info->system.max_vcpus; - counts = g_ptr_array_new(); - for (i = 0; i < max_cpus; i++) { - CPUCount *count = g_new0(CPUCount, 1); - g_mutex_init(&count->lock); - count->index = i; - g_ptr_array_add(counts, count); - } - } else if (!do_inline) { - g_mutex_init(&inline_count.lock); - } + counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); + bb_count = qemu_plugin_scoreboard_u64_in_struct(counts, CPUCount, bb_count); + insn_count = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, insn_count); if (idle_report) { qemu_plugin_register_vcpu_idle_cb(id, vcpu_idle); From 2d0d551442d6ea5fda99d38d59eee2886130d977 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:09:58 +0000 Subject: [PATCH 22/29] contrib/plugins/hotblocks: migrate to new per_vcpu API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-10-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-23-alex.bennee@linaro.org> --- contrib/plugins/hotblocks.c | 50 ++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/contrib/plugins/hotblocks.c b/contrib/plugins/hotblocks.c index 4de1b13494..02bc5078bd 100644 --- a/contrib/plugins/hotblocks.c +++ b/contrib/plugins/hotblocks.c @@ -34,8 +34,8 @@ static guint64 limit = 20; */ typedef struct { uint64_t start_addr; - uint64_t exec_count; - int trans_count; + struct qemu_plugin_scoreboard *exec_count; + int trans_count; unsigned long insns; } ExecCount; @@ -43,7 +43,17 @@ static gint cmp_exec_count(gconstpointer a, gconstpointer b) { ExecCount *ea = (ExecCount *) a; ExecCount *eb = (ExecCount *) b; - return ea->exec_count > eb->exec_count ? -1 : 1; + uint64_t count_a = + qemu_plugin_u64_sum(qemu_plugin_scoreboard_u64(ea->exec_count)); + uint64_t count_b = + qemu_plugin_u64_sum(qemu_plugin_scoreboard_u64(eb->exec_count)); + return count_a > count_b ? -1 : 1; +} + +static void exec_count_free(gpointer key, gpointer value, gpointer user_data) +{ + ExecCount *cnt = value; + qemu_plugin_scoreboard_free(cnt->exec_count); } static void plugin_exit(qemu_plugin_id_t id, void *p) @@ -52,7 +62,6 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) GList *counts, *it; int i; - g_mutex_lock(&lock); g_string_append_printf(report, "%d entries in the hash table\n", g_hash_table_size(hotblocks)); counts = g_hash_table_get_values(hotblocks); @@ -63,16 +72,21 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) for (i = 0; i < limit && it->next; i++, it = it->next) { ExecCount *rec = (ExecCount *) it->data; - g_string_append_printf(report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n", - rec->start_addr, rec->trans_count, - rec->insns, rec->exec_count); + g_string_append_printf( + report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n", + rec->start_addr, rec->trans_count, + rec->insns, + qemu_plugin_u64_sum( + qemu_plugin_scoreboard_u64(rec->exec_count))); } g_list_free(it); } - g_mutex_unlock(&lock); qemu_plugin_outs(report->str); + + g_hash_table_foreach(hotblocks, exec_count_free, NULL); + g_hash_table_destroy(hotblocks); } static void plugin_init(void) @@ -82,15 +96,9 @@ static void plugin_init(void) static void vcpu_tb_exec(unsigned int cpu_index, void *udata) { - ExecCount *cnt; - uint64_t hash = (uint64_t) udata; - - g_mutex_lock(&lock); - cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) hash); - /* should always succeed */ - g_assert(cnt); - cnt->exec_count++; - g_mutex_unlock(&lock); + ExecCount *cnt = (ExecCount *)udata; + qemu_plugin_u64_add(qemu_plugin_scoreboard_u64(cnt->exec_count), + cpu_index, 1); } /* @@ -114,18 +122,20 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) cnt->start_addr = pc; cnt->trans_count = 1; cnt->insns = insns; + cnt->exec_count = qemu_plugin_scoreboard_new(sizeof(uint64_t)); g_hash_table_insert(hotblocks, (gpointer) hash, (gpointer) cnt); } g_mutex_unlock(&lock); if (do_inline) { - qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64, - &cnt->exec_count, 1); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, + qemu_plugin_scoreboard_u64(cnt->exec_count), 1); } else { qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, - (void *)hash); + (void *)cnt); } } From c125a8ab04a495a89cadd8beb2729489265abe73 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:09:59 +0000 Subject: [PATCH 23/29] contrib/plugins/howvec: migrate to new per_vcpu API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alex Bennée Tested-by: Alex Bennée Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-11-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-24-alex.bennee@linaro.org> --- contrib/plugins/howvec.c | 53 ++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/contrib/plugins/howvec.c b/contrib/plugins/howvec.c index 644a7856bb..2d10c87e0f 100644 --- a/contrib/plugins/howvec.c +++ b/contrib/plugins/howvec.c @@ -43,13 +43,13 @@ typedef struct { uint32_t mask; uint32_t pattern; CountType what; - uint64_t count; + qemu_plugin_u64 count; } InsnClassExecCount; typedef struct { char *insn; uint32_t opcode; - uint64_t count; + qemu_plugin_u64 count; InsnClassExecCount *class; } InsnExecCount; @@ -159,7 +159,9 @@ static gint cmp_exec_count(gconstpointer a, gconstpointer b) { InsnExecCount *ea = (InsnExecCount *) a; InsnExecCount *eb = (InsnExecCount *) b; - return ea->count > eb->count ? -1 : 1; + uint64_t count_a = qemu_plugin_u64_sum(ea->count); + uint64_t count_b = qemu_plugin_u64_sum(eb->count); + return count_a > count_b ? -1 : 1; } static void free_record(gpointer data) @@ -167,12 +169,14 @@ static void free_record(gpointer data) InsnExecCount *rec = (InsnExecCount *) data; g_free(rec->insn); g_free(rec); + qemu_plugin_scoreboard_free(rec->count.score); } static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) report = g_string_new("Instruction Classes:\n"); int i; + uint64_t total_count; GList *counts; InsnClassExecCount *class = NULL; @@ -180,11 +184,12 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) class = &class_table[i]; switch (class->what) { case COUNT_CLASS: - if (class->count || verbose) { + total_count = qemu_plugin_u64_sum(class->count); + if (total_count || verbose) { g_string_append_printf(report, "Class: %-24s\t(%" PRId64 " hits)\n", class->class, - class->count); + total_count); } break; case COUNT_INDIVIDUAL: @@ -212,7 +217,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) "Instr: %-24s\t(%" PRId64 " hits)" "\t(op=0x%08x/%s)\n", rec->insn, - rec->count, + qemu_plugin_u64_sum(rec->count), rec->opcode, rec->class ? rec->class->class : "un-categorised"); @@ -221,6 +226,12 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) } g_hash_table_destroy(insns); + for (i = 0; i < ARRAY_SIZE(class_tables); i++) { + for (int j = 0; j < class_tables[i].table_sz; ++j) { + qemu_plugin_scoreboard_free(class_tables[i].table[j].count.score); + } + } + qemu_plugin_outs(report->str); } @@ -232,11 +243,12 @@ static void plugin_init(void) static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata) { - uint64_t *count = (uint64_t *) udata; - (*count)++; + struct qemu_plugin_scoreboard *score = udata; + qemu_plugin_u64_add(qemu_plugin_scoreboard_u64(score), cpu_index, 1); } -static uint64_t *find_counter(struct qemu_plugin_insn *insn) +static struct qemu_plugin_scoreboard *find_counter( + struct qemu_plugin_insn *insn) { int i; uint64_t *cnt = NULL; @@ -265,7 +277,7 @@ static uint64_t *find_counter(struct qemu_plugin_insn *insn) case COUNT_NONE: return NULL; case COUNT_CLASS: - return &class->count; + return class->count.score; case COUNT_INDIVIDUAL: { InsnExecCount *icount; @@ -279,13 +291,16 @@ static uint64_t *find_counter(struct qemu_plugin_insn *insn) icount->opcode = opcode; icount->insn = qemu_plugin_insn_disas(insn); icount->class = class; + struct qemu_plugin_scoreboard *score = + qemu_plugin_scoreboard_new(sizeof(uint64_t)); + icount->count = qemu_plugin_scoreboard_u64(score); g_hash_table_insert(insns, GUINT_TO_POINTER(opcode), (gpointer) icount); } g_mutex_unlock(&lock); - return &icount->count; + return icount->count.score; } default: g_assert_not_reached(); @@ -300,14 +315,14 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) size_t i; for (i = 0; i < n; i++) { - uint64_t *cnt; struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); - cnt = find_counter(insn); + struct qemu_plugin_scoreboard *cnt = find_counter(insn); if (cnt) { if (do_inline) { - qemu_plugin_register_vcpu_insn_exec_inline( - insn, QEMU_PLUGIN_INLINE_ADD_U64, cnt, 1); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, + qemu_plugin_scoreboard_u64(cnt), 1); } else { qemu_plugin_register_vcpu_insn_exec_cb( insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, cnt); @@ -322,6 +337,14 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, { int i; + for (i = 0; i < ARRAY_SIZE(class_tables); i++) { + for (int j = 0; j < class_tables[i].table_sz; ++j) { + struct qemu_plugin_scoreboard *score = + qemu_plugin_scoreboard_new(sizeof(uint64_t)); + class_tables[i].table[j].count = qemu_plugin_scoreboard_u64(score); + } + } + /* Select a class table appropriate to the guest architecture */ for (i = 0; i < ARRAY_SIZE(class_tables); i++) { ClassSelector *entry = &class_tables[i]; From fba3b490a26cb278dfa183d7fcc375746e312980 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:10:00 +0000 Subject: [PATCH 24/29] plugins: remove non per_vcpu inline operation from API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have a thread-safe equivalent of inline operation, and that all plugins were changed to use it, there is no point to keep the old API. In more, it will help when we implement more functionality (conditional callbacks), as we can assume that we operate on a scoreboard. API version bump was already done as part of this series. Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-12-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-25-alex.bennee@linaro.org> --- include/qemu/qemu-plugin.h | 58 ++++-------------------------------- plugins/api.c | 29 ------------------ plugins/qemu-plugins.symbols | 3 -- 3 files changed, 5 insertions(+), 85 deletions(-) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 6bbad068c0..4fc6c3739b 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -52,7 +52,11 @@ typedef uint64_t qemu_plugin_id_t; * The plugins export the API they were built against by exposing the * symbol qemu_plugin_version which can be checked. * - * version 2: removed qemu_plugin_n_vcpus and qemu_plugin_n_max_vcpus + * version 2: + * - removed qemu_plugin_n_vcpus and qemu_plugin_n_max_vcpus + * - Remove qemu_plugin_register_vcpu_{tb, insn, mem}_exec_inline. + * Those functions are replaced by *_per_vcpu variants, which guarantee + * thread-safety for operations. */ extern QEMU_PLUGIN_EXPORT int qemu_plugin_version; @@ -309,25 +313,6 @@ enum qemu_plugin_op { QEMU_PLUGIN_INLINE_ADD_U64, }; -/** - * qemu_plugin_register_vcpu_tb_exec_inline() - execution inline op - * @tb: the opaque qemu_plugin_tb handle for the translation - * @op: the type of qemu_plugin_op (e.g. ADD_U64) - * @ptr: the target memory location for the op - * @imm: the op data (e.g. 1) - * - * Insert an inline op to every time a translated unit executes. - * Useful if you just want to increment a single counter somewhere in - * memory. - * - * Note: ops are not atomic so in multi-threaded/multi-smp situations - * you will get inexact results. - */ -QEMU_PLUGIN_API -void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, - enum qemu_plugin_op op, - void *ptr, uint64_t imm); - /** * qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu() - execution inline op * @tb: the opaque qemu_plugin_tb handle for the translation @@ -359,21 +344,6 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, enum qemu_plugin_cb_flags flags, void *userdata); -/** - * qemu_plugin_register_vcpu_insn_exec_inline() - insn execution inline op - * @insn: the opaque qemu_plugin_insn handle for an instruction - * @op: the type of qemu_plugin_op (e.g. ADD_U64) - * @ptr: the target memory location for the op - * @imm: the op data (e.g. 1) - * - * Insert an inline op to every time an instruction executes. Useful - * if you just want to increment a single counter somewhere in memory. - */ -QEMU_PLUGIN_API -void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, - enum qemu_plugin_op op, - void *ptr, uint64_t imm); - /** * qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu() - insn exec inline op * @insn: the opaque qemu_plugin_insn handle for an instruction @@ -597,24 +567,6 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, enum qemu_plugin_mem_rw rw, void *userdata); -/** - * qemu_plugin_register_vcpu_mem_inline() - register an inline op to any memory access - * @insn: handle for instruction to instrument - * @rw: apply to reads, writes or both - * @op: the op, of type qemu_plugin_op - * @ptr: pointer memory for the op - * @imm: immediate data for @op - * - * This registers a inline op every memory access generated by the - * instruction. This provides for a lightweight but not thread-safe - * way of counting the number of operations done. - */ -QEMU_PLUGIN_API -void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, - enum qemu_plugin_mem_rw rw, - enum qemu_plugin_op op, void *ptr, - uint64_t imm); - /** * qemu_plugin_register_vcpu_mem_inline_per_vcpu() - inline op for mem access * @insn: handle for instruction to instrument diff --git a/plugins/api.c b/plugins/api.c index 6470f1dc0f..8fa5a600ac 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -101,16 +101,6 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, } } -void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, - enum qemu_plugin_op op, - void *ptr, uint64_t imm) -{ - if (!tb->mem_only) { - plugin_register_inline_op(&tb->cbs[PLUGIN_CB_INLINE], - 0, op, ptr, imm); - } -} - void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( struct qemu_plugin_tb *tb, enum qemu_plugin_op op, @@ -138,16 +128,6 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, } } -void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, - enum qemu_plugin_op op, - void *ptr, uint64_t imm) -{ - if (!insn->mem_only) { - plugin_register_inline_op(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], - 0, op, ptr, imm); - } -} - void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( struct qemu_plugin_insn *insn, enum qemu_plugin_op op, @@ -175,15 +155,6 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, cb, flags, rw, udata); } -void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, - enum qemu_plugin_mem_rw rw, - enum qemu_plugin_op op, void *ptr, - uint64_t imm) -{ - plugin_register_inline_op(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], - rw, op, ptr, imm); -} - void qemu_plugin_register_vcpu_mem_inline_per_vcpu( struct qemu_plugin_insn *insn, enum qemu_plugin_mem_rw rw, diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols index 0d8141b85f..a9fac056c7 100644 --- a/plugins/qemu-plugins.symbols +++ b/plugins/qemu-plugins.symbols @@ -27,16 +27,13 @@ qemu_plugin_register_vcpu_idle_cb; qemu_plugin_register_vcpu_init_cb; qemu_plugin_register_vcpu_insn_exec_cb; - qemu_plugin_register_vcpu_insn_exec_inline; qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu; qemu_plugin_register_vcpu_mem_cb; - qemu_plugin_register_vcpu_mem_inline; qemu_plugin_register_vcpu_mem_inline_per_vcpu; qemu_plugin_register_vcpu_resume_cb; qemu_plugin_register_vcpu_syscall_cb; qemu_plugin_register_vcpu_syscall_ret_cb; qemu_plugin_register_vcpu_tb_exec_cb; - qemu_plugin_register_vcpu_tb_exec_inline; qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu; qemu_plugin_register_vcpu_tb_trans_cb; qemu_plugin_reset; From 3077be254538b9fbb4bd5a7a9c60058b4580eb01 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 5 Mar 2024 12:10:01 +0000 Subject: [PATCH 25/29] plugins: cleanup codepath for previous inline operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Pierrick Bouvier Message-Id: <20240304130036.124418-13-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-26-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 13 ++++--------- plugins/core.c | 29 ++++------------------------- plugins/plugin.h | 5 ----- 3 files changed, 8 insertions(+), 39 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 47e05ec634..8028786c7b 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -440,15 +440,10 @@ static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb, TCGOp *begin_op, TCGOp *op, int *unused) { - char *ptr = cb->userp; - size_t elem_size = 0; - size_t offset = 0; - if (!ptr) { - /* use inline entry */ - ptr = cb->inline_insn.entry.score->data->data; - elem_size = g_array_get_element_size(cb->inline_insn.entry.score->data); - offset = cb->inline_insn.entry.offset; - } + char *ptr = cb->inline_insn.entry.score->data->data; + size_t elem_size = g_array_get_element_size( + cb->inline_insn.entry.score->data); + size_t offset = cb->inline_insn.entry.offset; op = copy_ld_i32(&begin_op, op); op = copy_mul_i32(&begin_op, op, elem_size); diff --git a/plugins/core.c b/plugins/core.c index 7852590da8..11ca20e626 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -316,22 +316,6 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr) return &g_array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1); } -void plugin_register_inline_op(GArray **arr, - enum qemu_plugin_mem_rw rw, - enum qemu_plugin_op op, - void *ptr, - uint64_t imm) -{ - struct qemu_plugin_dyn_cb *dyn_cb; - - dyn_cb = plugin_get_dyn_cb(arr); - dyn_cb->userp = ptr; - dyn_cb->type = PLUGIN_CB_INLINE; - dyn_cb->rw = rw; - dyn_cb->inline_insn.op = op; - dyn_cb->inline_insn.imm = imm; -} - void plugin_register_inline_op_on_entry(GArray **arr, enum qemu_plugin_mem_rw rw, enum qemu_plugin_op op, @@ -494,15 +478,10 @@ void qemu_plugin_flush_cb(void) void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index) { - char *ptr = cb->userp; - size_t elem_size = 0; - size_t offset = 0; - if (!ptr) { - /* use inline entry */ - ptr = cb->inline_insn.entry.score->data->data; - elem_size = g_array_get_element_size(cb->inline_insn.entry.score->data); - offset = cb->inline_insn.entry.offset; - } + char *ptr = cb->inline_insn.entry.score->data->data; + size_t elem_size = g_array_get_element_size( + cb->inline_insn.entry.score->data); + size_t offset = cb->inline_insn.entry.offset; uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size); switch (cb->inline_insn.op) { diff --git a/plugins/plugin.h b/plugins/plugin.h index f6fa10a0f5..7c34f23cfc 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -68,11 +68,6 @@ struct qemu_plugin_ctx { struct qemu_plugin_ctx *plugin_id_to_ctx_locked(qemu_plugin_id_t id); -void plugin_register_inline_op(GArray **arr, - enum qemu_plugin_mem_rw rw, - enum qemu_plugin_op op, void *ptr, - uint64_t imm); - void plugin_register_inline_op_on_entry(GArray **arr, enum qemu_plugin_mem_rw rw, enum qemu_plugin_op op, From 83b4613ba835d6ed6dcee3001c7fc56dc7b21685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 5 Mar 2024 12:10:02 +0000 Subject: [PATCH 26/29] disas: introduce show_opcodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For plugins we don't expect the raw opcodes in the disassembly. We already deal with this by hand crafting our capstone call but for other diassemblers we need a flag. Introduce show_opcodes which defaults to off. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-27-alex.bennee@linaro.org> --- disas/disas.c | 1 + include/disas/dis-asm.h | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/disas/disas.c b/disas/disas.c index 0d2d06c2ec..17170d291e 100644 --- a/disas/disas.c +++ b/disas/disas.c @@ -299,6 +299,7 @@ void disas(FILE *out, const void *code, size_t size) s.info.buffer = code; s.info.buffer_vma = (uintptr_t)code; s.info.buffer_length = size; + s.info.show_opcodes = true; if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) { return; diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index 2324f6b1a4..b26867b641 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -396,6 +396,14 @@ typedef struct disassemble_info { /* Command line options specific to the target disassembler. */ char * disassembler_options; + /* + * When true instruct the disassembler it may preface the + * disassembly with the opcodes values if it wants to. This is + * mainly for the benefit of the plugin interface which doesn't want + * that. + */ + bool show_opcodes; + /* Field intended to be used by targets in any way they deem suitable. */ void *target_info; From 7cff154b48d5ce64b6d65388c43a9a818672ec00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 5 Mar 2024 12:10:03 +0000 Subject: [PATCH 27/29] disas/hppa: honour show_opcodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-28-alex.bennee@linaro.org> --- disas/hppa.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/disas/hppa.c b/disas/hppa.c index 22dce9b41b..49e2231ae6 100644 --- a/disas/hppa.c +++ b/disas/hppa.c @@ -1972,9 +1972,11 @@ print_insn_hppa (bfd_vma memaddr, disassemble_info *info) insn = bfd_getb32 (buffer); - info->fprintf_func(info->stream, " %02x %02x %02x %02x ", - (insn >> 24) & 0xff, (insn >> 16) & 0xff, - (insn >> 8) & 0xff, insn & 0xff); + if (info->show_opcodes) { + info->fprintf_func(info->stream, " %02x %02x %02x %02x ", + (insn >> 24) & 0xff, (insn >> 16) & 0xff, + (insn >> 8) & 0xff, insn & 0xff); + } for (i = 0; i < NUMOPCODES; ++i) { From f6e0eabdb4647275d98c63982c0065dd80be7caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 5 Mar 2024 12:10:04 +0000 Subject: [PATCH 28/29] target/loongarch: honour show_opcodes when disassembling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the output suitable when used for plugins. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-29-alex.bennee@linaro.org> --- target/loongarch/disas.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c index 2040f3e44d..63989a6282 100644 --- a/target/loongarch/disas.c +++ b/target/loongarch/disas.c @@ -120,10 +120,15 @@ static const char *get_csr_name(unsigned num) csr_names[num] : "Undefined CSR"; } -#define output(C, INSN, FMT, ...) \ -{ \ - (C)->info->fprintf_func((C)->info->stream, "%08x %-9s\t" FMT, \ - (C)->insn, INSN, ##__VA_ARGS__); \ +#define output(C, INSN, FMT, ...) \ + { \ + if ((C)->info->show_opcodes) { \ + (C)->info->fprintf_func((C)->info->stream, "%08x %-9s\t" FMT,\ + (C)->insn, INSN, ##__VA_ARGS__); \ + } else { \ + (C)->info->fprintf_func((C)->info->stream, "%-9s\t" FMT, \ + INSN, ##__VA_ARGS__); \ + } \ } #include "decode-insns.c.inc" From db7e8b1f75662cf957f6bfad938ed112488518ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 5 Mar 2024 12:10:05 +0000 Subject: [PATCH 29/29] target/riscv: honour show_opcodes when disassembling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the output suitable when used for plugins. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240305121005.3528075-30-alex.bennee@linaro.org> --- disas/riscv.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 8a546d5ea5..e236c8b5b7 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -5192,19 +5192,21 @@ print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) } } - switch (len) { - case 2: - (*info->fprintf_func)(info->stream, INST_FMT_2, inst); - break; - case 4: - (*info->fprintf_func)(info->stream, INST_FMT_4, inst); - break; - case 6: - (*info->fprintf_func)(info->stream, INST_FMT_6, inst); - break; - default: - (*info->fprintf_func)(info->stream, INST_FMT_8, inst); - break; + if (info->show_opcodes) { + switch (len) { + case 2: + (*info->fprintf_func)(info->stream, INST_FMT_2, inst); + break; + case 4: + (*info->fprintf_func)(info->stream, INST_FMT_4, inst); + break; + case 6: + (*info->fprintf_func)(info->stream, INST_FMT_6, inst); + break; + default: + (*info->fprintf_func)(info->stream, INST_FMT_8, inst); + break; + } } disasm_inst(buf, sizeof(buf), isa, memaddr, inst,