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 */
25 static InstructionCount counts
[MAX_CPUS
];
26 static uint64_t inline_insn_count
;
28 static bool do_inline
;
34 uint64_t hits
[MAX_CPUS
];
35 uint64_t last_hit
[MAX_CPUS
];
36 uint64_t total_delta
[MAX_CPUS
];
37 GPtrArray
*history
[MAX_CPUS
];
40 static GArray
*matches
;
49 static void vcpu_insn_exec_before(unsigned int cpu_index
, void *udata
)
51 unsigned int i
= cpu_index
% MAX_CPUS
;
52 InstructionCount
*c
= &counts
[i
];
57 static void vcpu_insn_matched_exec_before(unsigned int cpu_index
, void *udata
)
59 unsigned int i
= cpu_index
% MAX_CPUS
;
60 Instruction
*insn
= (Instruction
*) udata
;
61 Match
*match
= insn
->match
;
62 g_autoptr(GString
) ts
= g_string_new("");
65 g_string_append_printf(ts
, "0x%" PRIx64
", '%s', %"PRId64
" hits",
66 insn
->vaddr
, insn
->disas
, insn
->hits
);
68 uint64_t icount
= counts
[i
].insn_count
;
69 uint64_t delta
= icount
- match
->last_hit
[i
];
72 match
->total_delta
[i
] += delta
;
74 g_string_append_printf(ts
,
75 ", %"PRId64
" match hits, "
76 "Δ+%"PRId64
" since last match,"
77 " %"PRId64
" avg insns/match\n",
78 match
->hits
[i
], delta
,
79 match
->total_delta
[i
] / match
->hits
[i
]);
81 match
->last_hit
[i
] = icount
;
83 qemu_plugin_outs(ts
->str
);
85 g_ptr_array_add(match
->history
[i
], insn
);
88 static void vcpu_tb_trans(qemu_plugin_id_t id
, struct qemu_plugin_tb
*tb
)
90 size_t n
= qemu_plugin_tb_n_insns(tb
);
93 for (i
= 0; i
< n
; i
++) {
94 struct qemu_plugin_insn
*insn
= qemu_plugin_tb_get_insn(tb
, i
);
97 qemu_plugin_register_vcpu_insn_exec_inline(
98 insn
, QEMU_PLUGIN_INLINE_ADD_U64
, &inline_insn_count
, 1);
100 uint64_t vaddr
= qemu_plugin_insn_vaddr(insn
);
101 qemu_plugin_register_vcpu_insn_exec_cb(
102 insn
, vcpu_insn_exec_before
, QEMU_PLUGIN_CB_NO_REGS
,
103 GUINT_TO_POINTER(vaddr
));
107 size_t sz
= qemu_plugin_insn_size(insn
);
108 if (sz
> sizes
->len
) {
109 g_array_set_size(sizes
, sz
);
111 unsigned long *cnt
= &g_array_index(sizes
, unsigned long, sz
);
116 * If we are tracking certain instructions we will need more
117 * information about the instruction which we also need to
118 * save if there is a hit.
121 char *insn_disas
= qemu_plugin_insn_disas(insn
);
123 for (j
= 0; j
< matches
->len
; j
++) {
124 Match
*m
= &g_array_index(matches
, Match
, j
);
125 if (g_str_has_prefix(insn_disas
, m
->match_string
)) {
126 Instruction
*rec
= g_new0(Instruction
, 1);
127 rec
->disas
= g_strdup(insn_disas
);
128 rec
->vaddr
= qemu_plugin_insn_vaddr(insn
);
130 qemu_plugin_register_vcpu_insn_exec_cb(
131 insn
, vcpu_insn_matched_exec_before
,
132 QEMU_PLUGIN_CB_NO_REGS
, rec
);
140 static void plugin_exit(qemu_plugin_id_t id
, void *p
)
142 g_autoptr(GString
) out
= g_string_new(NULL
);
146 for (i
= 0; i
<= sizes
->len
; i
++) {
147 unsigned long *cnt
= &g_array_index(sizes
, unsigned long, i
);
149 g_string_append_printf(out
,
150 "len %d bytes: %ld insns\n", i
, *cnt
);
153 } else if (do_inline
) {
154 g_string_append_printf(out
, "insns: %" PRIu64
"\n", inline_insn_count
);
156 uint64_t total_insns
= 0;
157 for (i
= 0; i
< MAX_CPUS
; i
++) {
158 InstructionCount
*c
= &counts
[i
];
160 g_string_append_printf(out
, "cpu %d insns: %" PRIu64
"\n",
162 total_insns
+= c
->insn_count
;
165 g_string_append_printf(out
, "total insns: %" PRIu64
"\n",
168 qemu_plugin_outs(out
->str
);
172 /* Add a match to the array of matches */
173 static void parse_match(char *match
)
175 Match new_match
= { .match_string
= match
};
177 for (i
= 0; i
< MAX_CPUS
; i
++) {
178 new_match
.history
[i
] = g_ptr_array_new();
181 matches
= g_array_new(false, true, sizeof(Match
));
183 g_array_append_val(matches
, new_match
);
186 QEMU_PLUGIN_EXPORT
int qemu_plugin_install(qemu_plugin_id_t id
,
187 const qemu_info_t
*info
,
188 int argc
, char **argv
)
190 for (int i
= 0; i
< argc
; i
++) {
192 g_auto(GStrv
) tokens
= g_strsplit(opt
, "=", 2);
193 if (g_strcmp0(tokens
[0], "inline") == 0) {
194 if (!qemu_plugin_bool_parse(tokens
[0], tokens
[1], &do_inline
)) {
195 fprintf(stderr
, "boolean argument parsing failed: %s\n", opt
);
198 } else if (g_strcmp0(tokens
[0], "sizes") == 0) {
199 if (!qemu_plugin_bool_parse(tokens
[0], tokens
[1], &do_size
)) {
200 fprintf(stderr
, "boolean argument parsing failed: %s\n", opt
);
203 } else if (g_strcmp0(tokens
[0], "match") == 0) {
204 parse_match(tokens
[1]);
206 fprintf(stderr
, "option parsing failed: %s\n", opt
);
212 sizes
= g_array_new(true, true, sizeof(unsigned long));
215 qemu_plugin_register_vcpu_tb_trans_cb(id
, vcpu_tb_trans
);
216 qemu_plugin_register_atexit_cb(id
, plugin_exit
, NULL
);