@@ -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. */
@@ -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
{
@@ -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 */
@@ -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 },
};
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 <smolov@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(-)