31
32
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
33
34
-static GArray *cpus;
35
-static GRWLock expand_array_lock;
34
+/*
35
+ * Per-vCPU state stored in a qemu_plugin_scoreboard. The scoreboard manages
36
+ * per-vCPU storage automatically, eliminating the need for manual array
37
+ * growth, locks, or pointer-stability workarounds.
38
+ */
39
+static struct qemu_plugin_scoreboard *cpus;
40
41
static GPtrArray *imatches;
42
static GArray *amatches;
45
static GMutex add_reg_name_lock;
46
static GPtrArray *all_reg_names;
47
44
-static CPU *get_cpu(int vcpu_index)
45
-{
46
- CPU *c;
47
- g_rw_lock_reader_lock(&expand_array_lock);
48
- c = &g_array_index(cpus, CPU, vcpu_index);
49
- g_rw_lock_reader_unlock(&expand_array_lock);
50
-
51
- return c;
52
-}
53
-
48
/**
49
* Add memory read or write information to current instruction log
50
*/
51
static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t info,
52
uint64_t vaddr, void *udata)
53
{
60
- CPU *c = get_cpu(cpu_index);
54
+ CPU *c = qemu_plugin_scoreboard_find(cpus, cpu_index);
55
GString *s = c->last_exec;
56
57
/* Find vCPU in array */
111
/* Log last instruction while checking registers */
112
static void vcpu_insn_exec_with_regs(unsigned int cpu_index, void *udata)
113
{
120
- CPU *cpu = get_cpu(cpu_index);
114
+ CPU *cpu = qemu_plugin_scoreboard_find(cpus, cpu_index);
115
116
/* Print previous instruction in cache */
117
if (cpu->last_exec->len) {
119
insn_check_regs(cpu);
120
}
121
122
+ g_string_append_c(cpu->last_exec, '\n');
123
qemu_plugin_outs(cpu->last_exec->str);
129
- qemu_plugin_outs("\n");
124
}
125
126
/* Store new instruction in cache */
132
/* Log last instruction while checking registers, ignore next */
133
static void vcpu_insn_exec_only_regs(unsigned int cpu_index, void *udata)
134
{
141
- CPU *cpu = get_cpu(cpu_index);
135
+ CPU *cpu = qemu_plugin_scoreboard_find(cpus, cpu_index);
136
137
/* Print previous instruction in cache */
138
if (cpu->last_exec->len) {
140
insn_check_regs(cpu);
141
}
142
143
+ g_string_append_c(cpu->last_exec, '\n');
144
qemu_plugin_outs(cpu->last_exec->str);
150
- qemu_plugin_outs("\n");
145
}
146
147
/* reset */
151
/* Log last instruction without checking regs, setup next */
152
static void vcpu_insn_exec(unsigned int cpu_index, void *udata)
153
{
160
- CPU *cpu = get_cpu(cpu_index);
154
+ CPU *cpu = qemu_plugin_scoreboard_find(cpus, cpu_index);
155
156
/* Print previous instruction in cache */
157
if (cpu->last_exec->len) {
158
+ g_string_append_c(cpu->last_exec, '\n');
159
qemu_plugin_outs(cpu->last_exec->str);
165
- qemu_plugin_outs("\n");
160
}
161
162
/* Store new instruction in cache */
372
* - last_exec tracking data
373
* - list of tracked registers
374
* - initial value of registers
381
- *
382
- * As we could have multiple threads trying to do this we need to
383
- * serialise the expansion under a lock.
375
*/
376
static void vcpu_init(unsigned int vcpu_index, void *userdata)
377
{
387
- CPU *c;
388
-
389
- g_rw_lock_writer_lock(&expand_array_lock);
390
- if (vcpu_index >= cpus->len) {
391
- g_array_set_size(cpus, vcpu_index + 1);
392
- }
393
- g_rw_lock_writer_unlock(&expand_array_lock);
394
-
395
- c = get_cpu(vcpu_index);
378
+ CPU *c = qemu_plugin_scoreboard_find(cpus, vcpu_index);
379
c->last_exec = g_string_new(NULL);
380
c->registers = registers_init(vcpu_index);
381
}
382
383
/**
401
- * On plugin exit, print last instruction in cache
384
+ * On plugin exit, flush any remaining cached instructions and free state.
385
*/
386
static void plugin_exit(void *p)
387
{
405
- guint i;
406
- g_rw_lock_reader_lock(&expand_array_lock);
407
- for (i = 0; i < cpus->len; i++) {
408
- CPU *c = get_cpu(i);
409
- if (c->last_exec && c->last_exec->str) {
388
+ int n = qemu_plugin_num_vcpus();
389
+ for (int i = 0; i < n; i++) {
390
+ CPU *c = qemu_plugin_scoreboard_find(cpus, i);
391
+ if (c->last_exec && c->last_exec->len) {
392
+ g_string_append_c(c->last_exec, '\n');
393
qemu_plugin_outs(c->last_exec->str);
411
- qemu_plugin_outs("\n");
394
}
395
}
414
- g_rw_lock_reader_unlock(&expand_array_lock);
396
+ qemu_plugin_scoreboard_free(cpus);
397
}
398
399
/* Add a match to the array of matches */
434
const qemu_info_t *info, int argc,
435
char **argv)
436
{
455
- /*
456
- * Initialize dynamic array to cache vCPU instruction. In user mode
457
- * we don't know the size before emulation.
458
- */
459
- cpus = g_array_sized_new(true, true, sizeof(CPU),
460
- info->system_emulation ? info->system.max_vcpus : 1);
437
+ /* Initialize scoreboard to cache per-vCPU instruction state. */
438
+ cpus = qemu_plugin_scoreboard_new(sizeof(CPU));
439
440
for (int i = 0; i < argc; i++) {
441
char *opt = argv[i];