From nobody Mon Feb 9 11:33:13 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass header.i=@intel.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1684418258; cv=none; d=zohomail.com; s=zohoarc; b=l5ZUCEnB/oKOgLmRyi6pJs4mcbdrWCBKa6tXpFnv+xVtLV11BM6dfuN9MsN1UO7hSoqxVvW6S5O58F3cQBL/8gtbSjQdWiWUNgrdHx4+28dye3wCyp+7G5gAHckJPx6Db6KCV5BmDkyRwdNOjW3NhERzxfEY2WbpaaYrke+FKS8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1684418258; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=vgKh4Ujuqs+tHgN+KyLmNBv8CqzeHCE7VJQGTWEFqNc=; b=POk+VM+brFAixYNqzcWyZ9n2wJ7JsY4kUXOGA+zJhLrWkzW01RZzj7SLc23fDOJxnzUPm9Aa379V00uRKdqKqagodveni/4WGh3+ik+W3S2Lx2vK4S1ALcl/g4LId/pByhEqzHSYP7Nuun+dgp45tuY5ii4rN9iopsD8ump+JiU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=@intel.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1684418258599679.5155598971248; Thu, 18 May 2023 06:57:38 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pze84-0004Aj-SI; Thu, 18 May 2023 09:57:16 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pze82-0003sL-Lf for qemu-devel@nongnu.org; Thu, 18 May 2023 09:57:14 -0400 Received: from mga12.intel.com ([192.55.52.136]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pze7z-0002Nb-2y for qemu-devel@nongnu.org; Thu, 18 May 2023 09:57:14 -0400 Received: from orsmga005.jf.intel.com ([10.7.209.41]) by fmsmga106.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 18 May 2023 06:57:10 -0700 Received: from wufei-optiplex-7090.sh.intel.com ([10.238.200.247]) by orsmga005.jf.intel.com with ESMTP; 18 May 2023 06:57:07 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1684418231; x=1715954231; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Ji6pWPBIt4XJSjDe1dBLSV5yGRc1HZZypP7bEDEXy4w=; b=YjQ25YX2GbsATIlMyu4KXpgCJl5v8aTCNwD6FV+RC9GJtJaRGLg3cYnz ImXQQBOt+T6Y/P/Wr6Z2ztRV1punQM1jAV9fqgQf76qd7+h+WvF9VTui+ DLosVoWC32HS6xzF2aKj1ovGQiBw3pqPK8Xg5tO1+Vq1Rg/JrftYWBZ9F uN3cytscFThK9pVPOklr7o8kY9qIQJkT/48RVMVnr0mt3TcF/n2CfeiMm VZ+QFZnjmSvxqnunZvhAxivA3394Hzq1hejv1zvellvoVAMxgOJZvcNUc o5n3zsopU+uSgbAVykNocSCybtkdaf2oQwffv14qUPOF8en9LPka7SKLN Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10714"; a="331685850" X-IronPort-AV: E=Sophos;i="5.99,285,1677571200"; d="scan'208";a="331685850" X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10714"; a="876428858" X-IronPort-AV: E=Sophos;i="5.99,285,1677571200"; d="scan'208";a="876428858" From: Fei Wu To: qemu-devel@nongnu.org, richard.henderson@linaro.org, alex.bennee@linaro.org, fei2.wu@intel.com Cc: "Vanderson M. do Rosario" , "Dr . David Alan Gilbert" , Paolo Bonzini , "Dr. David Alan Gilbert" Subject: [PATCH v12 12/15] Adding info [tb-list|tb] commands to HMP (WIP) Date: Thu, 18 May 2023 21:57:54 +0800 Message-Id: <20230518135757.1442654-13-fei2.wu@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230518135757.1442654-1-fei2.wu@intel.com> References: <20230518135757.1442654-1-fei2.wu@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=192.55.52.136; envelope-from=fei2.wu@intel.com; helo=mga12.intel.com X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @intel.com) X-ZM-MESSAGEID: 1684418259982100001 From: "Vanderson M. do Rosario" These commands allow the exploration of TBs generated by the TCG. Understand which one hotter, with more guest/host instructions... and examine their guest, host and IR code. The goal of this command is to allow the dynamic exploration of TCG behavior and code quality. Therefore, for now, a corresponding QMP command is not worthwhile. [AJB: WIP - we still can't be safely sure a translation will succeed] Example of output: TB id:1 | phys:0x34d54 virt:0x0000000000034d54 flags:0x0000f0 | exec:4828932/0 guest inst cov:16.38% | trans:1 ints: g:3 op:82 op_opt:34 spills:3 | h/g (host bytes / guest insts): 90.666664 | time to gen at 2.4GHz =3D> code:3150.83(ns) IR:712.08(ns) | targets: 0x0000000000034d5e (id:11), 0x0000000000034d0d (id:2) TB id:2 | phys:0x34d0d virt:0x0000000000034d0d flags:0x0000f0 | exec:4825842/0 guest inst cov:21.82% | trans:1 ints: g:4 op:80 op_opt:38 spills:2 | h/g (host bytes / guest insts): 84.000000 | time to gen at 2.4GHz =3D> code:3362.92(ns) IR:793.75(ns) | targets: 0x0000000000034d19 (id:12), 0x0000000000034d54 (id:1) TB id:2 | phys:0x34d0d virt:0x0000000000034d0d flags:0x0000f0 | exec:6956495/0 guest inst cov:21.82% | trans:2 ints: g:2 op:40 op_opt:19 spills:1 | h/g (host bytes / guest insts): 84.000000 | time to gen at 2.4GHz =3D> code:3130.83(ns) IR:722.50(ns) | targets: 0x0000000000034d19 (id:12), 0x0000000000034d54 (id:1) Acked-by: Dr. David Alan Gilbert ---------------- IN: 0x00034d0d: 89 de movl %ebx, %esi 0x00034d0f: 26 8b 0e movl %es:(%esi), %ecx 0x00034d12: 26 f6 46 08 80 testb $0x80, %es:8(%esi) 0x00034d17: 75 3b jne 0x34d54 ------------------------------ TB id:1 | phys:0x34d54 virt:0x0000000000034d54 flags:0x0000f0 | exec:5202686/0 guest inst cov:11.28% | trans:1 ints: g:3 op:82 op_opt:34 spills:3 | h/g (host bytes / guest insts): 90.666664 | time to gen at 2.4GHz =3D> code:2793.75(ns) IR:614.58(ns) | targets: 0x0000000000034d5e (id:3), 0x0000000000034d0d (id:2) TB id:2 | phys:0x34d0d virt:0x0000000000034d0d flags:0x0000f0 | exec:5199468/0 guest inst cov:15.03% | trans:1 ints: g:4 op:80 op_opt:38 spills:2 | h/g (host bytes / guest insts): 84.000000 | time to gen at 2.4GHz =3D> code:2958.75(ns) IR:719.58(ns) | targets: 0x0000000000034d19 (id:4), 0x0000000000034d54 (id:1) ------------------------------ 2 TBs to reach 25% of guest inst exec coverage Total of guest insts exec: 138346727 ------------------------------ Acked-by: Dr. David Alan Gilbert Signed-off-by: Vanderson M. do Rosario Message-Id: <20190829173437.5926-10-vandersonmr2@gmail.com> [AJB: fix authorship, dropped coverset] Signed-off-by: Alex Benn=C3=A9e Signed-off-by: Fei Wu --- accel/tcg/monitor.c | 54 ++++++ accel/tcg/tb-stats.c | 322 +++++++++++++++++++++++++++++++++++ disas/disas.c | 24 ++- hmp-commands-info.hx | 16 ++ include/exec/tb-stats.h | 33 +++- include/monitor/hmp.h | 2 + include/qemu/log-for-trace.h | 6 +- include/qemu/log.h | 2 + util/log.c | 66 +++++-- 9 files changed, 507 insertions(+), 18 deletions(-) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 2e00f10267..a1780c5920 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -8,6 +8,7 @@ =20 #include "qemu/osdep.h" #include "qemu/accel.h" +#include "qemu/log.h" #include "qapi/error.h" #include "qapi/type-helpers.h" #include "qapi/qapi-commands-machine.h" @@ -18,6 +19,7 @@ #include "sysemu/cpu-timers.h" #include "sysemu/tcg.h" #include "exec/tb-stats.h" +#include "tb-context.h" #include "internal.h" =20 =20 @@ -132,6 +134,58 @@ void hmp_tbstats(Monitor *mon, const QDict *qdict) =20 } =20 +void hmp_info_tblist(Monitor *mon, const QDict *qdict) +{ + int number_int; + const char *sortedby_str =3D NULL; + if (!tcg_enabled()) { + error_report("TB information is only available with accel=3Dtcg"); + return; + } + if (!tb_ctx.tb_stats.map) { + error_report("no TB information recorded"); + return; + } + + number_int =3D qdict_get_try_int(qdict, "number", 10); + sortedby_str =3D qdict_get_try_str(qdict, "sortedby"); + + int sortedby =3D SORT_BY_HOTNESS; + if (sortedby_str =3D=3D NULL || strcmp(sortedby_str, "hotness") =3D=3D= 0) { + sortedby =3D SORT_BY_HOTNESS; + } else if (strcmp(sortedby_str, "hg") =3D=3D 0) { + sortedby =3D SORT_BY_HG; + } else if (strcmp(sortedby_str, "spills") =3D=3D 0) { + sortedby =3D SORT_BY_SPILLS; + } else { + error_report("valid sort options are: hotness hg spills"); + return; + } + + dump_tbs_info(number_int, sortedby, true); +} + +void hmp_info_tb(Monitor *mon, const QDict *qdict) +{ + const int id =3D qdict_get_int(qdict, "id"); + const char *flags =3D qdict_get_try_str(qdict, "flags"); + int mask; + + if (!tcg_enabled()) { + error_report("TB information is only available with accel=3Dtcg"); + return; + } + + mask =3D flags ? qemu_str_to_log_mask(flags) : CPU_LOG_TB_IN_ASM; + + if (!mask) { + error_report("Unable to parse log flags, see 'help log'"); + return; + } + + dump_tb_info(id, mask, true); +} + HumanReadableText *qmp_x_query_profile(Error **errp) { g_autoptr(GString) buf =3D g_string_new(""); diff --git a/accel/tcg/tb-stats.c b/accel/tcg/tb-stats.c index 469e3e024b..cb28879a45 100644 --- a/accel/tcg/tb-stats.c +++ b/accel/tcg/tb-stats.c @@ -11,14 +11,18 @@ #include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg.h" +#include "qapi/error.h" =20 #include "qemu/qemu-print.h" #include "qemu/timer.h" +#include "qemu/log.h" =20 #include "exec/tb-stats.h" #include "exec/tb-flush.h" #include "tb-context.h" =20 +#include "internal.h" + /* TBStatistic collection controls */ enum TBStatsStatus { TB_STATS_DISABLED =3D 0, @@ -32,8 +36,23 @@ static uint32_t default_tbstats_flag; /* only accessed in safe work */ static GList *last_search; =20 +static int id =3D 1; /* display_id increment counter */ uint64_t dev_time; =20 +static TBStatistics *get_tbstats_by_id(int id) +{ + GList *iter; + + for (iter =3D last_search; iter; iter =3D g_list_next(iter)) { + TBStatistics *tbs =3D iter->data; + if (tbs && tbs->display_id =3D=3D id) { + return tbs; + break; + } + } + return NULL; +} + struct jit_profile_info { uint64_t translations; uint64_t aborted; @@ -296,6 +315,309 @@ void init_tb_stats_htable(void) } } =20 +static void collect_tb_stats(void *p, uint32_t hash, void *userp) +{ + last_search =3D g_list_prepend(last_search, p); +} + +static void count_invalid_tbs(gpointer data, gpointer user_data) +{ + TranslationBlock *tb =3D (TranslationBlock *) data; + unsigned *counter =3D (unsigned *) user_data; + if (tb->cflags & CF_INVALID) { + *counter =3D *counter + 1; + } +} + +static int dump_tb_header(TBStatistics *tbs) +{ + unsigned g =3D stat_per_translation(tbs, code.num_guest_inst); + unsigned ops =3D stat_per_translation(tbs, code.num_tcg_ops); + unsigned ops_opt =3D stat_per_translation(tbs, code.num_tcg_ops_opt); + unsigned spills =3D stat_per_translation(tbs, code.spills); + unsigned h =3D stat_per_translation(tbs, code.out_len); + unsigned act =3D tbs->tbs->len; + unsigned invalid =3D 0; + + float guest_host_prop =3D g ? ((float) h / g) : 0; + + g_ptr_array_foreach(tbs->tbs, &count_invalid_tbs, &invalid); + + qemu_log("TB id:%d | phys:0x"TB_PAGE_ADDR_FMT" virt:0x"TARGET_FMT_lx + " flags:0x%08x %d inv/%d\n", + tbs->display_id, tbs->phys_pc, tbs->pc, tbs->flags, + invalid, act); + + if (tbs_stats_enabled(tbs, TB_EXEC_STATS)) { + qemu_log("\t| exec:%lu/%lu guest inst cov:%.2f%%\n", + tbs->executions.normal, + tbs->executions.atomic, tbs->executions.coverage / 100.0f); + } + + if (tbs_stats_enabled(tbs, TB_JIT_STATS)) { + qemu_log("\t| trans:%lu ints: g:%u op:%u op_opt:%u spills:%d" + "\n\t| h/g (host bytes / guest insts): %f\n", + tbs->translations.total, g, ops, ops_opt, spills, guest_host_= prop); + } + + if (tbs_stats_enabled(tbs, TB_JIT_TIME)) { + qemu_log("\t| time to gen at 2.4GHz =3D> code:%0.2lf(ns) IR:%0.2lf= (ns)\n", + tbs->gen_times.code / 2.4, tbs->gen_times.ir / 2.4); + } + + qemu_log("\n"); + + return act - invalid; +} + +static gint +inverse_sort_tbs(gconstpointer p1, gconstpointer p2, gpointer psort_by) +{ + const TBStatistics *tbs1 =3D (TBStatistics *) p1; + const TBStatistics *tbs2 =3D (TBStatistics *) p2; + int sort_by =3D *((enum SortBy *) psort_by); + unsigned long c1 =3D 0; + unsigned long c2 =3D 0; + + if (sort_by =3D=3D SORT_BY_SPILLS) { + c1 =3D stat_per_translation(tbs1, code.spills); + c2 =3D stat_per_translation(tbs2, code.spills); + } else if (sort_by =3D=3D SORT_BY_HOTNESS) { + c1 =3D stat_per_translation(tbs1, executions.normal); + c2 =3D stat_per_translation(tbs2, executions.normal); + } else if (sort_by =3D=3D SORT_BY_HG) { + if (tbs1->code.num_guest_inst =3D=3D 0) { + return -1; + } + if (tbs2->code.num_guest_inst =3D=3D 0) { + return 1; + } + + c1 =3D tbs1->code.out_len / tbs1->code.num_guest_inst; + c2 =3D tbs2->code.out_len / tbs2->code.num_guest_inst; + } + return c1 < c2 ? 1 : c1 =3D=3D c2 ? 0 : -1; +} + +static void dump_last_search_headers(int count) +{ + if (!last_search) { + qemu_log("No data collected yet\n"); + return; + } + + GList *l =3D last_search; + while (l !=3D NULL && count--) { + TBStatistics *tbs =3D (TBStatistics *) l->data; + GList *next =3D l->next; + dump_tb_header(tbs); + l =3D next; + } +} + +static uint64_t calculate_last_search_coverages(void) +{ + uint64_t total_exec_count =3D 0; + GList *i; + + /* Compute total execution count for all tbs */ + for (i =3D last_search; i; i =3D i->next) { + TBStatistics *tbs =3D (TBStatistics *) i->data; + total_exec_count +=3D + (tbs->executions.atomic + tbs->executions.normal) + * tbs->code.num_guest_inst; + } + + for (i =3D last_search; i; i =3D i->next) { + TBStatistics *tbs =3D (TBStatistics *) i->data; + uint64_t tb_total_execs =3D + (tbs->executions.atomic + tbs->executions.normal) + * tbs->code.num_guest_inst; + tbs->executions.coverage =3D + (10000 * tb_total_execs) / (total_exec_count + 1); + } + + return total_exec_count; +} + +static void do_dump_tbs_info(int total, int sort_by) +{ + id =3D 1; + GList *i; + int count =3D total; + + g_list_free(last_search); + last_search =3D NULL; + + qht_iter(&tb_ctx.tb_stats, collect_tb_stats, NULL); + + last_search =3D g_list_sort_with_data(last_search, inverse_sort_tbs, + &sort_by); + + if (!last_search) { + qemu_printf("No data collected yet!\n"); + return; + } + + calculate_last_search_coverages(); + + for (i =3D last_search; i && count--; i =3D i->next) { + TBStatistics *tbs =3D (TBStatistics *) i->data; + tbs->display_id =3D id++; + } + + /* free the unused bits */ + if (i) { + if (i->next) { + i->next->prev =3D NULL; + } + g_list_free(i->next); + i->next =3D NULL; + } + + dump_last_search_headers(total); +} + +struct tbs_dump_info { + int count; + int sort_by; +}; + +static void do_dump_tbs_info_safe(CPUState *cpu, run_on_cpu_data tbdi) +{ + struct tbs_dump_info *info =3D tbdi.host_ptr; + qemu_log_to_monitor(true); + do_dump_tbs_info(info->count, info->sort_by); + qemu_log_to_monitor(false); + g_free(info); +} + +/* + * When we dump_tbs_info on a live system via the HMP we want to + * ensure the system is quiessent before we start outputting stuff. + * Otherwise we could pollute the output with other logging output. + */ + +void dump_tbs_info(int count, int sort_by, bool use_monitor) +{ + if (use_monitor) { + struct tbs_dump_info *tbdi =3D g_new(struct tbs_dump_info, 1); + tbdi->count =3D count; + tbdi->sort_by =3D sort_by; + async_safe_run_on_cpu(first_cpu, do_dump_tbs_info_safe, + RUN_ON_CPU_HOST_PTR(tbdi)); + } else { + do_dump_tbs_info(count, sort_by); + } +} + +/* + * We cannot always re-generate the code even if we know there are + * valid translations still in the cache. The reason being the guest + * may have un-mapped the page code. In real life this would be + * un-reachable as the jump cache is cleared and the following QHT + * lookup will do a get_page_addr_code() and fault. + * + * TODO: can we do this safely? We need to + * a) somehow recover the mmu_idx for this translation + * b) probe MMU_INST_FETCH to know it will succeed + */ +static GString *get_code_string(TBStatistics *tbs, int log_flags) +{ + int old_log_flags =3D qemu_loglevel; + + CPUState *cpu =3D first_cpu; + uint32_t cflags =3D curr_cflags(cpu); + TranslationBlock *tb =3D NULL; + + GString *code_s =3D g_string_new(NULL); + qemu_log_to_string(true, code_s); + + Error *err =3D NULL; + if (!qemu_set_log(log_flags, &err)) { + g_string_append_printf(code_s, "%s", error_get_pretty(err)); + error_free(err); + } + + if (sigsetjmp(cpu->jmp_env, 0) =3D=3D 0) { + mmap_lock(); + tb =3D tb_gen_code(cpu, tbs->pc, tbs->cs_base, tbs->flags, cflags); + tb_phys_invalidate(tb, -1); + mmap_unlock(); + } else { + /* + * The mmap_lock is dropped by tb_gen_code if it runs out of + * memory. + */ + qemu_log("\ncould not gen code for this TB (no longer mapped?)\n"); + assert_no_pages_locked(); + } + + qemu_set_log(old_log_flags, &err); + qemu_log_to_string(false, NULL); + + return code_s; +} + +static void do_tb_dump_with_statistics(TBStatistics *tbs, int log_flags) +{ + g_autoptr(GString) code_s =3D NULL; + + qemu_log("\n------------------------------\n\n"); + + if (dump_tb_header(tbs) > 0) { + code_s =3D get_code_string(tbs, log_flags); + } else { + code_s =3D g_string_new("cannot re-translate non-active translatio= n"); + } + qemu_log("%s", code_s->str); + qemu_log("------------------------------\n"); +} + +struct tb_dump_info { + int id; + int log_flags; + bool use_monitor; +}; + +static void do_dump_tb_info_safe(CPUState *cpu, run_on_cpu_data info) +{ + struct tb_dump_info *tbdi =3D (struct tb_dump_info *) info.host_ptr; + + if (!last_search) { + qemu_log("no search on record\n"); + return; + } + + qemu_log_to_monitor(tbdi->use_monitor); + + TBStatistics *tbs =3D get_tbstats_by_id(tbdi->id); + if (tbs) { + do_tb_dump_with_statistics(tbs, tbdi->log_flags); + } else { + qemu_log("no TB statitics found with id %d\n", tbdi->id); + } + + qemu_log_to_monitor(false); + + g_free(tbdi); +} + +void dump_tb_info(int id, int log_mask, bool use_monitor) +{ + struct tb_dump_info *tbdi =3D g_new(struct tb_dump_info, 1); + + tbdi->id =3D id; + tbdi->log_flags =3D log_mask; + tbdi->use_monitor =3D use_monitor; + + async_safe_run_on_cpu(first_cpu, do_dump_tb_info_safe, + RUN_ON_CPU_HOST_PTR(tbdi)); + + /* tbdi free'd by do_dump_tb_info_safe */ +} + + void enable_collect_tb_stats(void) { tcg_collect_tb_stats =3D TB_STATS_RUNNING; diff --git a/disas/disas.c b/disas/disas.c index 0d2d06c2ec..6cdd797874 100644 --- a/disas/disas.c +++ b/disas/disas.c @@ -8,6 +8,8 @@ #include "hw/core/cpu.h" #include "exec/memory.h" =20 +#include "qemu/log-for-trace.h" + /* Filled in by elfload.c. Simplistic, but will do for now. */ struct syminfo *syminfos =3D NULL; =20 @@ -199,6 +201,24 @@ static void initialize_debug_host(CPUDebug *s) #endif } =20 +static int +__attribute__((format(printf, 2, 3))) +fprintf_log(FILE *a, const char *b, ...) +{ + va_list ap; + va_start(ap, b); + + if (!to_string) { + vfprintf(a, b, ap); + } else { + qemu_vlog(b, ap); + } + + va_end(ap); + + return 1; +} + /* Disassemble this for me please... (debugging). */ void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size) { @@ -221,9 +241,9 @@ void target_disas(FILE *out, CPUState *cpu, uint64_t co= de, size_t size) } =20 for (pc =3D code; size > 0; pc +=3D count, size -=3D count) { - fprintf(out, "0x%08" PRIx64 ": ", pc); + fprintf_log(out, "0x%08" PRIx64 ": ", pc); count =3D s.info.print_insn(pc, &s.info); - fprintf(out, "\n"); + fprintf_log(out, "\n"); if (count < 0) { break; } diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 47d63d26db..01d9658aea 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -262,6 +262,22 @@ ERST .params =3D "", .help =3D "show dynamic compiler info", }, + { + .name =3D "tb-list", + .args_type =3D "number:i?,sortedby:s?", + .params =3D "[number sortedby]", + .help =3D "show a [number] translated blocks sorted by [sort= edby]" + "sortedby opts: hotness hg spills", + .cmd =3D hmp_info_tblist, + }, + { + .name =3D "tb", + .args_type =3D "id:i,flags:s?", + .params =3D "id [flag1,flag2,...]", + .help =3D "show information about one translated block by id= ." + "dump flags can be used to set dump code level: out_= asm in_asm op", + .cmd =3D hmp_info_tb, + }, #endif =20 SRST diff --git a/include/exec/tb-stats.h b/include/exec/tb-stats.h index 30b788f7b2..f2a6b09da9 100644 --- a/include/exec/tb-stats.h +++ b/include/exec/tb-stats.h @@ -36,8 +36,11 @@ enum SortBy { SORT_BY_HOTNESS, SORT_BY_HG /* Host/Guest */, SORT_BY_SPILLS= }; enum TbstatsCmd { START, PAUSE, STOP, FILTER }; =20 +#define tbs_stats_enabled(tbs, JIT_STATS) \ + (tbs && (tbs->stats_enabled & JIT_STATS)) + #define tb_stats_enabled(tb, JIT_STATS) \ - (tb && tb->tb_stats && (tb->tb_stats->stats_enabled & JIT_STATS)) + (tb && tb->tb_stats && tbs_stats_enabled(tb->tb_stats, JIT_STATS)) =20 #define stat_per_translation(stat, name) \ (stat->translations.total ? stat->name / stat->translations.total : 0) @@ -66,6 +69,8 @@ struct TBStatistics { struct { unsigned long normal; unsigned long atomic; + /* filled only when dumping x% cover set */ + uint16_t coverage; } executions; =20 /* JIT Stats - protected by lock */ @@ -88,7 +93,6 @@ struct TBStatistics { =20 struct { unsigned long total; - unsigned long uncached; unsigned long spanning; } translations; =20 @@ -108,6 +112,9 @@ struct TBStatistics { uint64_t la; uint64_t code; } gen_times; + + /* HMP information - used for referring to previous search */ + int display_id; }; =20 bool tb_stats_cmp(const void *ap, const void *bp); @@ -132,4 +139,26 @@ void do_hmp_tbstats_safe(CPUState *cpu, run_on_cpu_dat= a icmd); */ void tbstats_reset_tbs(void); =20 +/** + * dump_tbs_info: report the hottest blocks + * + * @count: the limit of hotblocks + * @sort_by: property in which the dump will be sorted + * @use_monitor: redirect output to monitor + * + * Report the hottest blocks to either the log or monitor + */ +void dump_tbs_info(int count, int sort_by, bool use_monitor); + +/** + * dump_tb_info: dump information about one TB + * + * @id: the display id of the block (from previous search) + * @mask: the temporary logging mask + * @Use_monitor: redirect output to monitor + * + * Re-run a translation of a block at addr for the purposes of debug output + */ +void dump_tb_info(int id, int log_mask, bool use_monitor); + #endif diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 2e7f141754..acdd6f1561 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -182,5 +182,7 @@ void hmp_boot_set(Monitor *mon, const QDict *qdict); void hmp_info_mtree(Monitor *mon, const QDict *qdict); void hmp_info_cryptodev(Monitor *mon, const QDict *qdict); void hmp_tbstats(Monitor *mon, const QDict *qdict); +void hmp_info_tblist(Monitor *mon, const QDict *qdict); +void hmp_info_tb(Monitor *mon, const QDict *qdict); =20 #endif diff --git a/include/qemu/log-for-trace.h b/include/qemu/log-for-trace.h index d47c9cd446..5d0afc56c7 100644 --- a/include/qemu/log-for-trace.h +++ b/include/qemu/log-for-trace.h @@ -20,6 +20,9 @@ =20 /* Private global variable, don't use */ extern int qemu_loglevel; +extern bool to_string; + +extern int32_t max_num_hot_tbs_to_dump; =20 #define LOG_TRACE (1 << 15) =20 @@ -30,6 +33,7 @@ static inline bool qemu_loglevel_mask(int mask) } =20 /* main logging function */ -void G_GNUC_PRINTF(1, 2) qemu_log(const char *fmt, ...); +int G_GNUC_PRINTF(1, 2) qemu_log(const char *fmt, ...); +int G_GNUC_PRINTF(1, 0) qemu_vlog(const char *fmt, va_list va); =20 #endif diff --git a/include/qemu/log.h b/include/qemu/log.h index 6f3b8091cd..26b33151f3 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -85,6 +85,8 @@ extern const QEMULogItem qemu_log_items[]; bool qemu_set_log(int log_flags, Error **errp); bool qemu_set_log_filename(const char *filename, Error **errp); bool qemu_set_log_filename_flags(const char *name, int flags, Error **errp= ); +void qemu_log_to_monitor(bool enable); +void qemu_log_to_string(bool enable, GString *s); void qemu_set_dfilter_ranges(const char *ranges, Error **errp); bool qemu_log_in_addr_range(uint64_t addr); int qemu_str_to_log_mask(const char *str); diff --git a/util/log.c b/util/log.c index 7ae471d97c..6477eb5a5f 100644 --- a/util/log.c +++ b/util/log.c @@ -50,6 +50,8 @@ int qemu_loglevel; static bool log_per_thread; static GArray *debug_regions; int32_t max_num_hot_tbs_to_dump; +static bool to_monitor; +bool to_string; =20 /* Returns true if qemu_log() will really write somewhere. */ bool qemu_log_enabled(void) @@ -146,19 +148,6 @@ void qemu_log_unlock(FILE *logfile) } } =20 -void qemu_log(const char *fmt, ...) -{ - FILE *f =3D qemu_log_trylock(); - if (f) { - va_list ap; - - va_start(ap, fmt); - vfprintf(f, fmt, ap); - va_end(ap); - qemu_log_unlock(f); - } -} - static void __attribute__((__constructor__)) startup(void) { qemu_mutex_init(&global_mutex); @@ -206,6 +195,55 @@ valid_filename_template(const char *filename, bool per= _thread, Error **errp) return filename ? vft_strdup : vft_stderr; } =20 +GString *string; + +int qemu_vlog(const char *fmt, va_list va) +{ + int ret =3D 0; + + if (to_string && string) { + g_string_append_vprintf(string, fmt, va); + } else if (to_monitor) { + ret =3D qemu_vprintf(fmt, va); + } else { + FILE *f =3D qemu_log_trylock(); + if (f) { + ret =3D vfprintf(f, fmt, va); + } + qemu_log_unlock(f); + } + + /* Don't pass back error results. */ + if (ret < 0) { + ret =3D 0; + } + return ret; +} + +/* Return the number of characters emitted. */ +int qemu_log(const char *fmt, ...) +{ + int ret =3D 0; + va_list ap; + + va_start(ap, fmt); + ret =3D qemu_vlog(fmt, ap); + va_end(ap); + + return ret; +} + +void qemu_log_to_monitor(bool enable) +{ + to_monitor =3D enable; +} + +void qemu_log_to_string(bool enable, GString *s) +{ + to_string =3D enable; + string =3D s; +} + /* enable or disable low levels log */ static bool qemu_set_log_internal(const char *filename, bool changed_name, int log_flags, Error **errp) @@ -523,6 +561,7 @@ int qemu_str_to_log_mask(const char *str) trace_enable_events((*tmp) + 6); mask |=3D LOG_TRACE; #endif +#ifdef CONFIG_TCG } else if (g_str_has_prefix(*tmp, "tb_stats")) { mask |=3D CPU_LOG_TB_STATS; set_default_tbstats_flag(TB_ALL_STATS); @@ -553,6 +592,7 @@ int qemu_str_to_log_mask(const char *str) } set_default_tbstats_flag(flags); } +#endif } else { for (item =3D qemu_log_items; item->mask !=3D 0; item++) { if (g_str_equal(*tmp, item->name)) { --=20 2.25.1