diff options
Diffstat (limited to 'accel')
| -rw-r--r-- | accel/accel-common.c | 2 | ||||
| -rw-r--r-- | accel/accel-qmp.c | 35 | ||||
| -rw-r--r-- | accel/accel-system.c | 12 | ||||
| -rw-r--r-- | accel/hvf/hvf-accel-ops.c | 27 | ||||
| -rw-r--r-- | accel/hvf/hvf-all.c | 1 | ||||
| -rw-r--r-- | accel/kvm/kvm-accel-ops.c | 2 | ||||
| -rw-r--r-- | accel/kvm/kvm-all.c | 1 | ||||
| -rw-r--r-- | accel/meson.build | 2 | ||||
| -rw-r--r-- | accel/qtest/qtest.c | 3 | ||||
| -rw-r--r-- | accel/tcg/internal-common.h | 2 | ||||
| -rw-r--r-- | accel/tcg/meson.build | 1 | ||||
| -rw-r--r-- | accel/tcg/monitor.c | 192 | ||||
| -rw-r--r-- | accel/tcg/tcg-accel-ops-rr.c | 2 | ||||
| -rw-r--r-- | accel/tcg/tcg-accel-ops.c | 3 | ||||
| -rw-r--r-- | accel/tcg/tcg-all.c | 3 | ||||
| -rw-r--r-- | accel/tcg/tcg-stats.c | 219 | ||||
| -rw-r--r-- | accel/xen/xen-all.c | 3 |
17 files changed, 308 insertions, 202 deletions
diff --git a/accel/accel-common.c b/accel/accel-common.c index 591ff4cbb6..850c5ab4b8 100644 --- a/accel/accel-common.c +++ b/accel/accel-common.c @@ -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 */ diff --git a/accel/accel-qmp.c b/accel/accel-qmp.c new file mode 100644 index 0000000000..5fb70c6631 --- /dev/null +++ b/accel/accel-qmp.c @@ -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); +} diff --git a/accel/accel-system.c b/accel/accel-system.c index c54c30f18b..1e97c64fdc 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -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) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index be8724ac89..d488d6afba 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -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"), diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 1fa07c8b69..e67a8105a6 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -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" diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index 0eafc902c3..b709187c7d 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -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" diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 78fc2d26fc..890d5ea9f8 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -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" diff --git a/accel/meson.build b/accel/meson.build index 52909314bf..25b0f100b5 100644 --- a/accel/meson.build +++ b/accel/meson.build @@ -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') diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 2b83126020..1d4337d698 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -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" diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 77a3a0684a..6adfeefe13 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -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 diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 575e92bb9e..002aa8f458 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -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', )) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index e7ed7281a4..be5c195017 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -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) { diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index a578698d07..6eec5c9eee 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -302,8 +302,6 @@ static void *rr_cpu_thread_fn(void *arg) rr_deal_with_unplugged_cpus(); } - rcu_unregister_thread(); - g_assert_not_reached(); } diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 279dbfa1cf..3b0d7d298e 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -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" diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 5904582a68..5125e1a4e2 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -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; diff --git a/accel/tcg/tcg-stats.c b/accel/tcg/tcg-stats.c new file mode 100644 index 0000000000..ced5dec0c4 --- /dev/null +++ b/accel/tcg/tcg-stats.c @@ -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); +} diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index bd0ff64bef..97377d67d1 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -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" |