Merge tag 'accel-20250715' of https://github.com/philmd/qemu into staging

Accelerators patches

- Unify x86/arm hw/xen/arch_hvm.h header
- Move non-system-specific 'accel/accel-ops.h' and 'accel-cpu-ops.h' to accel/
- Move KVM definitions qapi/accelerator.json
- Add @qom-type field to CpuInfoFast QAPI structure
- Display CPU model name in 'info cpus' HMP command
- Introduce @x-accel-stats QMP command
- Add 'info accel' on HMP
- Improve qemu_add_vm_change_state_handler*() docstring
- Extract TCG statistic related code to tcg-stats.c
- Implement AccelClass::get_[vcpu]_stats() handlers for TCG and HVF
- Do not dump NaN in TCG statistics
- Revert incomplete "accel/tcg: Unregister the RCU before exiting RR thread"

 # -----BEGIN PGP SIGNATURE-----
 #
 # iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmh2r4UACgkQ4+MsLN6t
 # wN5i6xAAkOvwFh1GmsPUdz5RxzsWoIUDvyENg6E8Axwe5tSEMRFiPjabbTQJomQg
 # GZt75XIS24LZFZ+hvqrLSA+dFgXTgWv08ZE81EjwjmAMBlLCOPhCgeN6C1p8100Y
 # scSvRJbP9k9lpA5K7et/1X4AkK2cZyh+LGJgCjr2Al2mbERpPueDF8fxqeohFvXQ
 # nTSks4XlA0yQ06+9r49aQAiuXvgg9lDT1wIglD2HEV7vOVs/ud+yyL8+z5YMeFzx
 # pSIc6wDu4PqdA46w4MZs90uTy7S/PMvBiYDEiV3tKzg0MLttvFGlT58/YjVtguTP
 # mNkfwIEwQtDQzoxsFIJO7yBTlTRBs95V4aIVk3pB+Gb/bideRPIkeVQvgMSEBKj7
 # N0pEXWOxfB9iIWO6b1utYpQ4uxeDOU/8DPUCit1IBbNgKTaJkJb77fboYk7NaB0K
 # KEtObAk6jMatB/xr+vUFWc4sMk9wlm72w8wcQzgKZ0xV2U3d1/Y/9nS4GvI510ev
 # TRQ3mKj7N319uCeId1czF6W8rillCJ2u8ZK53u+Nfp7R3PbsRSMc6IDJ1UdDUlyR
 # HFcWHxbcbEGhe8SnFGab4Qd6fWChcn2EaEoAJJz+Rqv0k3zcwqccNM5waCABAjTE
 # 0S22JIHePJKcpkMLGq3EOUAQuu+8Zsol7gPCLxSAMclVqPTl9ck=
 # =rAav
 # -----END PGP SIGNATURE-----
 # gpg: Signature made Tue 15 Jul 2025 15:44:05 EDT
 # gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
 # gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
 # Primary key fingerprint: FAAB E75E 1291 7221 DCFD  6BB2 E3E3 2C2C DEAD C0DE

* tag 'accel-20250715' of https://github.com/philmd/qemu:
  system/runstate: Document qemu_add_vm_change_state_handler_prio* in hdr
  system/runstate: Document qemu_add_vm_change_state_handler()
  accel/hvf: Implement AccelClass::get_vcpu_stats() handler
  accel/tcg: Implement AccelClass::get_stats() handler
  accel/tcg: Propagate AccelState to dump_accel_info()
  accel/system: Add 'info accel' on human monitor
  accel/system: Introduce @x-accel-stats QMP command
  accel/tcg: Extract statistic related code to tcg-stats.c
  Revert "accel/tcg: Unregister the RCU before exiting RR thread"
  accel: Extract AccelClass definition to 'accel/accel-ops.h'
  accel: Rename 'system/accel-ops.h' -> 'accel/accel-cpu-ops.h'
  accel/tcg: Do not dump NaN statistics
  hw/core/machine: Display CPU model name in 'info cpus' command
  qapi/machine: Add @qom-type field to CpuInfoFast structure
  qapi/accel: Move definitions related to accelerators in their own file
  hw/arm/xen-pvh: Remove unnecessary 'hw/xen/arch_hvm.h' header
  hw/xen/arch_hvm: Unify x86 and ARM variants

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>

Conflicts:
  qapi/machine.json
  Commit 0462da9d6b ("qapi: remove trivial "Returns:" sections")
  removed trivial "Returns:". This caused a conflict with the move from
  machine.json to accelerator.json.
This commit is contained in:
Stefan Hajnoczi
2025-07-16 07:13:40 -04:00
47 changed files with 513 additions and 333 deletions
+2 -1
View File
@@ -502,13 +502,14 @@ F: include/exec/target_long.h
F: include/qemu/accel.h
F: include/system/accel-*.h
F: include/system/cpus.h
F: include/accel/accel-cpu*.h
F: include/accel/accel-*.h
F: accel/accel-*.?
F: accel/dummy-cpus.?
F: accel/Makefile.objs
F: accel/stubs/Makefile.objs
F: cpu-common.c
F: cpu-target.c
F: qapi/accelerator.json
F: system/cpus.c
Apple Silicon HVF CPUs
+2
View File
@@ -10,7 +10,9 @@
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "qemu/target-info.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu.h"
#include "accel/accel-cpu-ops.h"
#include "accel-internal.h"
/* Lookup AccelClass from opt_name. Returns NULL if not found */
+35
View File
@@ -0,0 +1,35 @@
/*
* QMP commands related to accelerators
*
* Copyright (c) Linaro
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "qapi/type-helpers.h"
#include "qapi/qapi-commands-accelerator.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "hw/core/cpu.h"
HumanReadableText *qmp_x_accel_stats(Error **errp)
{
AccelState *accel = current_accel();
AccelClass *acc = ACCEL_GET_CLASS(accel);
g_autoptr(GString) buf = g_string_new("");
if (acc->get_stats) {
acc->get_stats(accel, buf);
}
if (acc->ops->get_vcpu_stats) {
CPUState *cpu;
CPU_FOREACH(cpu) {
acc->ops->get_vcpu_stats(cpu, buf);
}
}
return human_readable_text_from_str(buf);
}
+11 -1
View File
@@ -25,8 +25,12 @@
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "qapi/qapi-commands-accelerator.h"
#include "monitor/monitor.h"
#include "hw/boards.h"
#include "system/accel-ops.h"
#include "hw/core/cpu.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/cpus.h"
#include "qemu/error-report.h"
#include "accel-internal.h"
@@ -101,11 +105,17 @@ void accel_init_ops_interfaces(AccelClass *ac)
cpus_register_accel(ops);
}
static void accel_ops_class_init(ObjectClass *oc, const void *data)
{
monitor_register_hmp_info_hrt("accel", qmp_x_accel_stats);
}
static const TypeInfo accel_ops_type_info = {
.name = TYPE_ACCEL_OPS,
.parent = TYPE_OBJECT,
.abstract = true,
.class_size = sizeof(AccelOpsClass),
.class_init = accel_ops_class_init,
};
static void accel_system_register_types(void)
+26 -1
View File
@@ -54,10 +54,11 @@
#include "gdbstub/enums.h"
#include "exec/cpu-common.h"
#include "hw/core/cpu.h"
#include "system/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/cpus.h"
#include "system/hvf.h"
#include "system/hvf_int.h"
#include <mach/mach_time.h>
HVFState *hvf_state;
@@ -118,6 +119,12 @@ static void dummy_signal(int sig)
{
}
static void do_hvf_get_vcpu_exec_time(CPUState *cpu, run_on_cpu_data arg)
{
int r = hv_vcpu_get_exec_time(cpu->accel->fd, arg.host_ptr);
assert_hvf_ok(r);
}
static void hvf_vcpu_destroy(CPUState *cpu)
{
hv_return_t ret = hv_vcpu_destroy(cpu->accel->fd);
@@ -347,6 +354,21 @@ static void hvf_remove_all_breakpoints(CPUState *cpu)
}
}
static void hvf_get_vcpu_stats(CPUState *cpu, GString *buf)
{
uint64_t time_mach; /* units of mach_absolute_time() */
run_on_cpu(cpu, do_hvf_get_vcpu_exec_time, RUN_ON_CPU_HOST_PTR(&time_mach));
mach_timebase_info_data_t timebase;
mach_timebase_info(&timebase);
uint64_t time_ns = time_mach * timebase.numer / timebase.denom;
g_string_append_printf(buf, "HVF cumulative execution time: %llu.%.3llus\n",
time_ns / 1000000000,
(time_ns % 1000000000) / 1000000);
}
static void hvf_accel_ops_class_init(ObjectClass *oc, const void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
@@ -365,7 +387,10 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, const void *data)
ops->remove_all_breakpoints = hvf_remove_all_breakpoints;
ops->update_guest_debug = hvf_update_guest_debug;
ops->supports_guest_debug = hvf_arch_supports_guest_debug;
ops->get_vcpu_stats = hvf_get_vcpu_stats;
};
static const TypeInfo hvf_accel_ops_type = {
.name = ACCEL_OPS_NAME("hvf"),
+1
View File
@@ -10,6 +10,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "accel/accel-ops.h"
#include "system/address-spaces.h"
#include "system/memory.h"
#include "system/hvf.h"
+1 -1
View File
@@ -16,7 +16,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "system/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/kvm.h"
#include "system/kvm_int.h"
#include "system/runstate.h"
+1
View File
@@ -32,6 +32,7 @@
#include "system/runstate.h"
#include "system/cpus.h"
#include "system/accel-blocker.h"
#include "accel/accel-ops.h"
#include "qemu/bswap.h"
#include "exec/tswap.h"
#include "system/memory.h"
+1 -1
View File
@@ -1,6 +1,6 @@
common_ss.add(files('accel-common.c'))
specific_ss.add(files('accel-target.c'))
system_ss.add(files('accel-system.c', 'accel-blocker.c'))
system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c'))
user_ss.add(files('accel-user.c'))
subdir('tcg')
+2 -1
View File
@@ -18,7 +18,8 @@
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "qemu/accel.h"
#include "system/accel-ops.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/qtest.h"
#include "system/cpus.h"
#include "qemu/guest-random.h"
+1 -1
View File
@@ -139,6 +139,6 @@ G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr);
void tcg_dump_stats(GString *buf);
void tcg_get_stats(AccelState *accel, GString *buf);
#endif
+1
View File
@@ -11,6 +11,7 @@ tcg_ss.add(files(
'tcg-runtime-gvec.c',
'tb-maint.c',
'tcg-all.c',
'tcg-stats.c',
'translate-all.c',
'translator.c',
))
-192
View File
@@ -7,205 +7,13 @@
*/
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "qemu/qht.h"
#include "qapi/error.h"
#include "qapi/type-helpers.h"
#include "qapi/qapi-commands-machine.h"
#include "monitor/monitor.h"
#include "system/cpu-timers.h"
#include "exec/icount.h"
#include "system/tcg.h"
#include "tcg/tcg.h"
#include "internal-common.h"
#include "tb-context.h"
static void dump_drift_info(GString *buf)
{
if (!icount_enabled()) {
return;
}
g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n",
(cpu_get_clock() - icount_get()) / SCALE_MS);
if (icount_align_option) {
g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n",
-max_delay / SCALE_MS);
g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n",
max_advance / SCALE_MS);
} else {
g_string_append_printf(buf, "Max guest delay NA\n");
g_string_append_printf(buf, "Max guest advance NA\n");
}
}
static void dump_accel_info(GString *buf)
{
AccelState *accel = current_accel();
bool one_insn_per_tb = object_property_get_bool(OBJECT(accel),
"one-insn-per-tb",
&error_fatal);
g_string_append_printf(buf, "Accelerator settings:\n");
g_string_append_printf(buf, "one-insn-per-tb: %s\n\n",
one_insn_per_tb ? "on" : "off");
}
static void print_qht_statistics(struct qht_stats hst, GString *buf)
{
uint32_t hgram_opts;
size_t hgram_bins;
char *hgram;
if (!hst.head_buckets) {
return;
}
g_string_append_printf(buf, "TB hash buckets %zu/%zu "
"(%0.2f%% head buckets used)\n",
hst.used_head_buckets, hst.head_buckets,
(double)hst.used_head_buckets /
hst.head_buckets * 100);
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT;
if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) {
hgram_opts |= QDIST_PR_NODECIMAL;
}
hgram = qdist_pr(&hst.occupancy, 10, hgram_opts);
g_string_append_printf(buf, "TB hash occupancy %0.2f%% avg chain occ. "
"Histogram: %s\n",
qdist_avg(&hst.occupancy) * 100, hgram);
g_free(hgram);
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain);
if (hgram_bins > 10) {
hgram_bins = 10;
} else {
hgram_bins = 0;
hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
}
hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts);
g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. "
"Histogram: %s\n",
qdist_avg(&hst.chain), hgram);
g_free(hgram);
}
struct tb_tree_stats {
size_t nb_tbs;
size_t host_size;
size_t target_size;
size_t max_target_size;
size_t direct_jmp_count;
size_t direct_jmp2_count;
size_t cross_page;
};
static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data)
{
const TranslationBlock *tb = value;
struct tb_tree_stats *tst = data;
tst->nb_tbs++;
tst->host_size += tb->tc.size;
tst->target_size += tb->size;
if (tb->size > tst->max_target_size) {
tst->max_target_size = tb->size;
}
if (tb->page_addr[1] != -1) {
tst->cross_page++;
}
if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) {
tst->direct_jmp_count++;
if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) {
tst->direct_jmp2_count++;
}
}
return false;
}
static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide)
{
CPUState *cpu;
size_t full = 0, part = 0, elide = 0;
CPU_FOREACH(cpu) {
full += qatomic_read(&cpu->neg.tlb.c.full_flush_count);
part += qatomic_read(&cpu->neg.tlb.c.part_flush_count);
elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count);
}
*pfull = full;
*ppart = part;
*pelide = elide;
}
static void tcg_dump_flush_info(GString *buf)
{
size_t flush_full, flush_part, flush_elide;
g_string_append_printf(buf, "TB flush count %u\n",
qatomic_read(&tb_ctx.tb_flush_count));
g_string_append_printf(buf, "TB invalidate count %u\n",
qatomic_read(&tb_ctx.tb_phys_invalidate_count));
tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full);
g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part);
g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide);
}
static void dump_exec_info(GString *buf)
{
struct tb_tree_stats tst = {};
struct qht_stats hst;
size_t nb_tbs;
tcg_tb_foreach(tb_tree_stats_iter, &tst);
nb_tbs = tst.nb_tbs;
/* XXX: avoid using doubles ? */
g_string_append_printf(buf, "Translation buffer state:\n");
/*
* Report total code size including the padding and TB structs;
* otherwise users might think "-accel tcg,tb-size" is not honoured.
* For avg host size we use the precise numbers from tb_tree_stats though.
*/
g_string_append_printf(buf, "gen code size %zu/%zu\n",
tcg_code_size(), tcg_code_capacity());
g_string_append_printf(buf, "TB count %zu\n", nb_tbs);
g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n",
nb_tbs ? tst.target_size / nb_tbs : 0,
tst.max_target_size);
g_string_append_printf(buf, "TB avg host size %zu bytes "
"(expansion ratio: %0.1f)\n",
nb_tbs ? tst.host_size / nb_tbs : 0,
tst.target_size ?
(double)tst.host_size / tst.target_size : 0);
g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n",
tst.cross_page,
nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
g_string_append_printf(buf, "direct jump count %zu (%zu%%) "
"(2 jumps=%zu %zu%%)\n",
tst.direct_jmp_count,
nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
tst.direct_jmp2_count,
nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
qht_statistics_init(&tb_ctx.htable, &hst);
print_qht_statistics(hst, buf);
qht_statistics_destroy(&hst);
g_string_append_printf(buf, "\nStatistics:\n");
tcg_dump_flush_info(buf);
}
void tcg_dump_stats(GString *buf)
{
dump_accel_info(buf);
dump_exec_info(buf);
dump_drift_info(buf);
}
HumanReadableText *qmp_x_query_jit(Error **errp)
{
-2
View File
@@ -302,8 +302,6 @@ static void *rr_cpu_thread_fn(void *arg)
rr_deal_with_unplugged_cpus();
}
rcu_unregister_thread();
g_assert_not_reached();
}
+2 -1
View File
@@ -26,7 +26,8 @@
*/
#include "qemu/osdep.h"
#include "system/accel-ops.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/tcg.h"
#include "system/replay.h"
#include "exec/icount.h"
+3
View File
@@ -39,6 +39,8 @@
#ifndef CONFIG_USER_ONLY
#include "hw/boards.h"
#endif
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "accel/tcg/cpu-ops.h"
#include "internal-common.h"
@@ -241,6 +243,7 @@ static void tcg_accel_class_init(ObjectClass *oc, const void *data)
ac->init_machine = tcg_init_machine;
ac->cpu_common_realize = tcg_exec_realizefn;
ac->cpu_common_unrealize = tcg_exec_unrealizefn;
ac->get_stats = tcg_get_stats;
ac->allowed = &tcg_allowed;
ac->gdbstub_supported_sstep_flags = tcg_gdbstub_supported_sstep_flags;
+219
View File
@@ -0,0 +1,219 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* QEMU TCG statistics
*
* Copyright (c) 2003-2005 Fabrice Bellard
*/
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "qemu/qht.h"
#include "qapi/error.h"
#include "system/cpu-timers.h"
#include "exec/icount.h"
#include "hw/core/cpu.h"
#include "tcg/tcg.h"
#include "internal-common.h"
#include "tb-context.h"
#include <math.h>
static void dump_drift_info(GString *buf)
{
if (!icount_enabled()) {
return;
}
g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n",
(cpu_get_clock() - icount_get()) / SCALE_MS);
if (icount_align_option) {
g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n",
-max_delay / SCALE_MS);
g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n",
max_advance / SCALE_MS);
} else {
g_string_append_printf(buf, "Max guest delay NA\n");
g_string_append_printf(buf, "Max guest advance NA\n");
}
}
static void dump_accel_info(AccelState *accel, GString *buf)
{
bool one_insn_per_tb = object_property_get_bool(OBJECT(accel),
"one-insn-per-tb",
&error_fatal);
g_string_append_printf(buf, "Accelerator settings:\n");
g_string_append_printf(buf, "one-insn-per-tb: %s\n\n",
one_insn_per_tb ? "on" : "off");
}
static void print_qht_statistics(struct qht_stats hst, GString *buf)
{
uint32_t hgram_opts;
size_t hgram_bins;
char *hgram;
double avg;
if (!hst.head_buckets) {
return;
}
g_string_append_printf(buf, "TB hash buckets %zu/%zu "
"(%0.2f%% head buckets used)\n",
hst.used_head_buckets, hst.head_buckets,
(double)hst.used_head_buckets /
hst.head_buckets * 100);
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT;
if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) {
hgram_opts |= QDIST_PR_NODECIMAL;
}
hgram = qdist_pr(&hst.occupancy, 10, hgram_opts);
avg = qdist_avg(&hst.occupancy);
if (!isnan(avg)) {
g_string_append_printf(buf, "TB hash occupancy "
"%0.2f%% avg chain occ. "
"Histogram: %s\n",
avg * 100, hgram);
}
g_free(hgram);
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain);
if (hgram_bins > 10) {
hgram_bins = 10;
} else {
hgram_bins = 0;
hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
}
hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts);
avg = qdist_avg(&hst.chain);
if (!isnan(avg)) {
g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. "
"Histogram: %s\n",
avg, hgram);
}
g_free(hgram);
}
struct tb_tree_stats {
size_t nb_tbs;
size_t host_size;
size_t target_size;
size_t max_target_size;
size_t direct_jmp_count;
size_t direct_jmp2_count;
size_t cross_page;
};
static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data)
{
const TranslationBlock *tb = value;
struct tb_tree_stats *tst = data;
tst->nb_tbs++;
tst->host_size += tb->tc.size;
tst->target_size += tb->size;
if (tb->size > tst->max_target_size) {
tst->max_target_size = tb->size;
}
#ifndef CONFIG_USER_ONLY
if (tb->page_addr[1] != -1) {
tst->cross_page++;
}
#endif
if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) {
tst->direct_jmp_count++;
if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) {
tst->direct_jmp2_count++;
}
}
return false;
}
static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide)
{
CPUState *cpu;
size_t full = 0, part = 0, elide = 0;
CPU_FOREACH(cpu) {
full += qatomic_read(&cpu->neg.tlb.c.full_flush_count);
part += qatomic_read(&cpu->neg.tlb.c.part_flush_count);
elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count);
}
*pfull = full;
*ppart = part;
*pelide = elide;
}
static void tcg_dump_flush_info(GString *buf)
{
size_t flush_full, flush_part, flush_elide;
g_string_append_printf(buf, "TB flush count %u\n",
qatomic_read(&tb_ctx.tb_flush_count));
g_string_append_printf(buf, "TB invalidate count %u\n",
qatomic_read(&tb_ctx.tb_phys_invalidate_count));
tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full);
g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part);
g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide);
}
static void dump_exec_info(GString *buf)
{
struct tb_tree_stats tst = {};
struct qht_stats hst;
size_t nb_tbs;
tcg_tb_foreach(tb_tree_stats_iter, &tst);
nb_tbs = tst.nb_tbs;
/* XXX: avoid using doubles ? */
g_string_append_printf(buf, "Translation buffer state:\n");
/*
* Report total code size including the padding and TB structs;
* otherwise users might think "-accel tcg,tb-size" is not honoured.
* For avg host size we use the precise numbers from tb_tree_stats though.
*/
g_string_append_printf(buf, "gen code size %zu/%zu\n",
tcg_code_size(), tcg_code_capacity());
g_string_append_printf(buf, "TB count %zu\n", nb_tbs);
g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n",
nb_tbs ? tst.target_size / nb_tbs : 0,
tst.max_target_size);
g_string_append_printf(buf, "TB avg host size %zu bytes "
"(expansion ratio: %0.1f)\n",
nb_tbs ? tst.host_size / nb_tbs : 0,
tst.target_size ?
(double)tst.host_size / tst.target_size : 0);
g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n",
tst.cross_page,
nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
g_string_append_printf(buf, "direct jump count %zu (%zu%%) "
"(2 jumps=%zu %zu%%)\n",
tst.direct_jmp_count,
nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
tst.direct_jmp2_count,
nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
qht_statistics_init(&tb_ctx.htable, &hst);
print_qht_statistics(hst, buf);
qht_statistics_destroy(&hst);
g_string_append_printf(buf, "\nStatistics:\n");
tcg_dump_flush_info(buf);
}
void tcg_get_stats(AccelState *accel, GString *buf)
{
dump_accel_info(accel, buf);
dump_exec_info(buf);
dump_drift_info(buf);
}
void tcg_dump_stats(GString *buf)
{
tcg_get_stats(current_accel(), buf);
}
+2 -1
View File
@@ -19,7 +19,8 @@
#include "chardev/char.h"
#include "qemu/accel.h"
#include "accel/dummy-cpus.h"
#include "system/accel-ops.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/cpus.h"
#include "system/xen.h"
#include "system/runstate.h"
+1
View File
@@ -38,6 +38,7 @@
#include "qemu/plugin.h"
#include "user/guest-base.h"
#include "user/page-protection.h"
#include "accel/accel-ops.h"
#include "tcg/startup.h"
#include "qemu/timer.h"
#include "qemu/envlist.h"
+1 -1
View File
@@ -19,7 +19,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "system/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/cpus.h"
#include "exec/cpu-common.h"
#include "exec/replay-core.h"

Some files were not shown because too many files have changed in this diff Show More