2 * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
4 * License: GNU GPL, version 2 or later.
5 * See the COPYING file in the top-level directory.
15 #include <qemu-plugin.h>
17 QEMU_PLUGIN_EXPORT
int qemu_plugin_version
= QEMU_PLUGIN_VERSION
;
19 #define MAX_CPUS 8 /* lets not go nuts */
26 static InstructionCount counts
[MAX_CPUS
];
27 static uint64_t inline_insn_count
;
29 static bool do_inline
;
35 uint64_t hits
[MAX_CPUS
];
36 uint64_t last_hit
[MAX_CPUS
];
37 uint64_t total_delta
[MAX_CPUS
];
38 GPtrArray
*history
[MAX_CPUS
];
41 static GArray
*matches
;
50 static void vcpu_insn_exec_before(unsigned int cpu_index
, void *udata
)
52 unsigned int i
= cpu_index
% MAX_CPUS
;
53 InstructionCount
*c
= &counts
[i
];
54 uint64_t this_pc
= GPOINTER_TO_UINT(udata
);
55 if (this_pc
== c
->last_pc
) {
56 g_autofree gchar
*out
= g_strdup_printf("detected repeat execution @ 0x%"
57 PRIx64
"\n", this_pc
);
58 qemu_plugin_outs(out
);
64 static void vcpu_insn_matched_exec_before(unsigned int cpu_index
, void *udata
)
66 unsigned int i
= cpu_index
% MAX_CPUS
;
67 Instruction
*insn
= (Instruction
*) udata
;
68 Match
*match
= insn
->match
;
69 g_autoptr(GString
) ts
= g_string_new("");
72 g_string_append_printf(ts
, "0x%" PRIx64
", '%s', %"PRId64
" hits",
73 insn
->vaddr
, insn
->disas
, insn
->hits
);
75 uint64_t icount
= counts
[i
].insn_count
;
76 uint64_t delta
= icount
- match
->last_hit
[i
];
79 match
->total_delta
[i
] += delta
;
81 g_string_append_printf(ts
,
82 ", %"PRId64
" match hits, "
83 "Δ+%"PRId64
" since last match,"
84 " %"PRId64
" avg insns/match\n",
85 match
->hits
[i
], delta
,
86 match
->total_delta
[i
] / match
->hits
[i
]);
88 match
->last_hit
[i
] = icount
;
90 qemu_plugin_outs(ts
->str
);
92 g_ptr_array_add(match
->history
[i
], insn
);
95 static void vcpu_tb_trans(qemu_plugin_id_t id
, struct qemu_plugin_tb
*tb
)
97 size_t n
= qemu_plugin_tb_n_insns(tb
);
100 for (i
= 0; i
< n
; i
++) {
101 struct qemu_plugin_insn
*insn
= qemu_plugin_tb_get_insn(tb
, i
);
104 qemu_plugin_register_vcpu_insn_exec_inline(
105 insn
, QEMU_PLUGIN_INLINE_ADD_U64
, &inline_insn_count
, 1);
107 uint64_t vaddr
= qemu_plugin_insn_vaddr(insn
);
108 qemu_plugin_register_vcpu_insn_exec_cb(
109 insn
, vcpu_insn_exec_before
, QEMU_PLUGIN_CB_NO_REGS
,
110 GUINT_TO_POINTER(vaddr
));
114 size_t sz
= qemu_plugin_insn_size(insn
);
115 if (sz
> sizes
->len
) {
116 g_array_set_size(sizes
, sz
);
118 unsigned long *cnt
= &g_array_index(sizes
, unsigned long, sz
);
123 * If we are tracking certain instructions we will need more
124 * information about the instruction which we also need to
125 * save if there is a hit.
128 char *insn_disas
= qemu_plugin_insn_disas(insn
);
130 for (j
= 0; j
< matches
->len
; j
++) {
131 Match
*m
= &g_array_index(matches
, Match
, j
);
132 if (g_str_has_prefix(insn_disas
, m
->match_string
)) {
133 Instruction
*rec
= g_new0(Instruction
, 1);
134 rec
->disas
= g_strdup(insn_disas
);
135 rec
->vaddr
= qemu_plugin_insn_vaddr(insn
);
137 qemu_plugin_register_vcpu_insn_exec_cb(
138 insn
, vcpu_insn_matched_exec_before
,
139 QEMU_PLUGIN_CB_NO_REGS
, rec
);
147 static void plugin_exit(qemu_plugin_id_t id
, void *p
)
149 g_autoptr(GString
) out
= g_string_new(NULL
);
153 for (i
= 0; i
<= sizes
->len
; i
++) {
154 unsigned long *cnt
= &g_array_index(sizes
, unsigned long, i
);
156 g_string_append_printf(out
,
157 "len %d bytes: %ld insns\n", i
, *cnt
);
160 } else if (do_inline
) {
161 g_string_append_printf(out
, "insns: %" PRIu64
"\n", inline_insn_count
);
163 uint64_t total_insns
= 0;
164 for (i
= 0; i
< MAX_CPUS
; i
++) {
165 InstructionCount
*c
= &counts
[i
];
167 g_string_append_printf(out
, "cpu %d insns: %" PRIu64
"\n",
169 total_insns
+= c
->insn_count
;
172 g_string_append_printf(out
, "total insns: %" PRIu64
"\n",
175 qemu_plugin_outs(out
->str
);
179 /* Add a match to the array of matches */
180 static void parse_match(char *match
)
182 Match new_match
= { .match_string
= match
};
184 for (i
= 0; i
< MAX_CPUS
; i
++) {
185 new_match
.history
[i
] = g_ptr_array_new();
188 matches
= g_array_new(false, true, sizeof(Match
));
190 g_array_append_val(matches
, new_match
);
193 QEMU_PLUGIN_EXPORT
int qemu_plugin_install(qemu_plugin_id_t id
,
194 const qemu_info_t
*info
,
195 int argc
, char **argv
)
197 for (int i
= 0; i
< argc
; i
++) {
199 g_autofree
char **tokens
= g_strsplit(opt
, "=", 2);
200 if (g_strcmp0(tokens
[0], "inline") == 0) {
201 if (!qemu_plugin_bool_parse(tokens
[0], tokens
[1], &do_inline
)) {
202 fprintf(stderr
, "boolean argument parsing failed: %s\n", opt
);
205 } else if (g_strcmp0(tokens
[0], "sizes") == 0) {
206 if (!qemu_plugin_bool_parse(tokens
[0], tokens
[1], &do_size
)) {
207 fprintf(stderr
, "boolean argument parsing failed: %s\n", opt
);
210 } else if (g_strcmp0(tokens
[0], "match") == 0) {
211 parse_match(tokens
[1]);
213 fprintf(stderr
, "option parsing failed: %s\n", opt
);
219 sizes
= g_array_new(true, true, sizeof(unsigned long));
222 qemu_plugin_register_vcpu_tb_trans_cb(id
, vcpu_tb_trans
);
223 qemu_plugin_register_atexit_cb(id
, plugin_exit
, NULL
);