When 'nochain' and 'in_asm' debug options are enabled, disassembled forms of all executed translation blocks (TB) are printed to log. For this task a mapping between disassembled instructions and executed TBs is created and used.
Signed-off-by: Sergey Smolov <smo...@ispras.ru> --- cpu-exec.c | 20 ++++++++++++++++++++ disas.c | 18 +++++++++++++++++- include/disas/disas.h | 14 ++++++++++++++ qemu-log.c | 2 +- 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/cpu-exec.c b/cpu-exec.c index 7eef083..b9385f9 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -345,6 +345,9 @@ int cpu_exec(CPUState *cpu) uintptr_t next_tb; SyncClocks sc; + hwaddr pc_prev; + bool pc_prev_valid = false; + if (cpu->halted) { #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { @@ -474,6 +477,23 @@ int cpu_exec(CPUState *cpu) qemu_log("Trace %p [" TARGET_FMT_lx "] %s\n", tb->tc_ptr, tb->pc, lookup_symbol(tb->pc)); } + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) + && qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { + struct insninfo *s; + for (s = insninfos; s != NULL; s = s->next) { + if (s->insn_addr == tb->pc + && pc_prev_valid + && s->insn_addr != pc_prev) { + qemu_log("%s\n", s->insn); + pc_prev = s->insn_addr; + if (!pc_prev_valid) { + pc_prev_valid = true; + } + } else if (s->insn_addr == pc_prev) { + pc_prev_valid = false; + } + } + } /* see if we can patch the calling TB. When the TB spans two pages, we cannot safely do a direct jump. */ diff --git a/disas.c b/disas.c index 4e11944..51bf68f 100644 --- a/disas.c +++ b/disas.c @@ -16,6 +16,9 @@ typedef struct CPUDebug { /* Filled in by elfload.c. Simplistic, but will do for now. */ struct syminfo *syminfos = NULL; +/* Filled in here. */ +struct insninfo *insninfos; + /* Get LENGTH bytes from info's buffer, at target address memaddr. Transfer them to myaddr. */ int @@ -236,7 +239,20 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, } for (pc = code; size > 0; pc += count, size -= count) { - fprintf(out, "0x" TARGET_FMT_lx ": ", pc); + + if (qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN) + && qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + struct insninfo *s = + (struct insninfo *)malloc( + sizeof(struct insninfo)); + s->insn_addr = pc; + + sprintf(s->insn, " " TARGET_FMT_lx " ", pc); + s->next = insninfos; + insninfos = s; + } else { + fprintf(out, "0x" TARGET_FMT_lx ": ", pc); + } count = s.info.print_insn(pc, &s.info); #if 0 { diff --git a/include/disas/disas.h b/include/disas/disas.h index 2b9293b..75a4c73 100644 --- a/include/disas/disas.h +++ b/include/disas/disas.h @@ -2,6 +2,7 @@ #define _QEMU_DISAS_H #include "qemu-common.h" +#include "exec/hwaddr.h" #ifdef NEED_CPU_H /* Disassemble this for me please... (debugging). */ @@ -40,4 +41,17 @@ struct syminfo { /* Filled in by elfload.c. Simplistic, but will do for now. */ extern struct syminfo *syminfos; +struct insninfo { + + /* Instruction address. */ + hwaddr insn_addr; + + /* Instruction string representation. */ + char insn[256]; + struct insninfo *next; +}; + +/* Filled in by disas.c - Information about instructions. */ +extern struct insninfo *insninfos; + #endif /* _QEMU_DISAS_H */ diff --git a/qemu-log.c b/qemu-log.c index efd07c8..bbc10e3 100644 --- a/qemu-log.c +++ b/qemu-log.c @@ -120,7 +120,7 @@ const QEMULogItem qemu_log_items[] = { "log when the guest OS does something invalid (eg accessing a\n" "non-existent register)" }, { CPU_LOG_TB_NOCHAIN, "nochain", - "do not chain compiled TBs so that \"exec\" and \"cpu\" show\n" + "do not chain compiled TBs so that \"exec\", \"in_asm\" and \"cpu\" show\n" "complete traces" }, { 0, NULL, NULL }, }; -- 1.7.10.4