From nobody Mon Feb 9 16:35:22 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.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; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1565837760; cv=none; d=zoho.com; s=zohoarc; b=oF22oqjXcGmtXkQ54sWxw6wjg9+0R93l1bc0RLeQG/eHQJlVKxU5N4ZWmY3Pu16+pJlBtkpCVTB4tgbpFDrbskXsy+2r5XDAfy5/M9fS1H3E6c7CFaCx4MQFWju6auMaSxkT1L+NH8zm1ck8t8x6jFXUikveUvynK2Po7Iain6Q= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1565837760; h=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:ARC-Authentication-Results; bh=WSDfMgBc9FbkiGNR4Cnxk5bgFksmEKOgvYdXh+VD+X0=; b=feBGyE6EP25adyQZPk+pfeAnRWU7Q7MfyLcf/GXmTkH2EBdR9pzImoxG6GH7JnkTuJ6LHWk2qpF0xxS4/K9KWN7+0ZV2eHn3/yTU8VdQK4wDQcFUUddqcrS30woU3KANp/HAhtSWg9NROFij55j0jbV6ENCeV5uswS+D1FgCAAk= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1565837760460408.2396499543281; Wed, 14 Aug 2019 19:56:00 -0700 (PDT) Received: from localhost ([::1]:37904 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1hy5vi-0004pf-UX for importer@patchew.org; Wed, 14 Aug 2019 22:55:58 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:48694) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1hy5Mh-0000zl-0g for qemu-devel@nongnu.org; Wed, 14 Aug 2019 22:19:49 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hy5Me-0001qt-5P for qemu-devel@nongnu.org; Wed, 14 Aug 2019 22:19:46 -0400 Received: from mail-qt1-x843.google.com ([2607:f8b0:4864:20::843]:35418) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hy5Md-0001qj-Vh for qemu-devel@nongnu.org; Wed, 14 Aug 2019 22:19:44 -0400 Received: by mail-qt1-x843.google.com with SMTP id u34so951322qte.2 for ; Wed, 14 Aug 2019 19:19:43 -0700 (PDT) Received: from localhost.localdomain ([2804:14c:482:121::1]) by smtp.googlemail.com with ESMTPSA id o5sm757943qkf.10.2019.08.14.19.19.40 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Wed, 14 Aug 2019 19:19:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=WSDfMgBc9FbkiGNR4Cnxk5bgFksmEKOgvYdXh+VD+X0=; b=PUWLmKtj4sz0Y/OiRcTfG1EzzJWj6yXxzCOqbxuOB9Ko4PCbVWeux0IJ/txNK+ysCk r5hE0loKi7aTajDeAH7kNImVuXFjW/ISq9xvMm2e6D/J5rsEJahKNwQ7xaUZXUjuDGMD NSX/aW6jCgPLCHHiCYg3ZS+95xtz/fLxuEAWDJ8/HKQ7+rE3498Bllw1qywq8xBE0U7X wdzatwpIBxoos8upVGaNXouejkC3IEhGEyCE7I2bVEP8EOP6YNB1mefgdQYL6I+D2y8o wl9njt3DWVuF4WWvLH591uyT9Q5AHmUypQg7wt2qLyRg+mSe0R1tDcCZV+Svp7JTMyAm 3l1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=WSDfMgBc9FbkiGNR4Cnxk5bgFksmEKOgvYdXh+VD+X0=; b=fW/uNQkynvcGH1HqEhnvG8Uvi6Owk5wAVPpKJ8kBYdHPxvld7ViEeu5YRpXgJstkeY MpYbd0aA/r2TDJlDHJUWiiu4x9Qrol/KVDMPg2a1e2utOybPUtpqDZGAJ5fw80H0uB1t eAbX9rTPuYmFJDMcQTBBCmAXYnzKlZGCgLXbac2qkb6iSCTSY045C7JXq5UAl6O9xhoV vA0wFuu242LoQSCQWuNc7WA3OuA4TYFtNGEx3dOly3awAWNZnOlQMOU+oRzRJL0Q2iI2 4vGo34Ze0ZAlqIr4YP6x4i4koLOokJyqOfEE83RTei/sODxOxdGwbq92Lc2RDqDNK9TH koSg== X-Gm-Message-State: APjAAAU5aAUw8k3fwFuOBR7C+ATqgLfWvMkD4L9nVFJMtR9wxgWMY6pP P3SskagI9TRo6sgMz9YEvYfNVPW44yMerw== X-Google-Smtp-Source: APXvYqzGVpY7jp5YwJ+AzDgTC+Be4qat71xYknx2NlTNqL6zMjbcyPKuKmVnrmjvKKgzp/XbYLSvcw== X-Received: by 2002:aed:35b4:: with SMTP id c49mr1961112qte.313.1565835582936; Wed, 14 Aug 2019 19:19:42 -0700 (PDT) From: vandersonmr To: qemu-devel@nongnu.org Date: Wed, 14 Aug 2019 23:18:55 -0300 Message-Id: <20190815021857.19526-9-vandersonmr2@gmail.com> X-Mailer: git-send-email 2.22.0 In-Reply-To: <20190815021857.19526-1-vandersonmr2@gmail.com> References: <20190815021857.19526-1-vandersonmr2@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:4864:20::843 Subject: [Qemu-devel] [PATCH v5 08/10] Adding info [tbs|tb|coverset] commands to HMP. 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. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paolo Bonzini , vandersonmr , "Dr. David Alan Gilbert" , Markus Armbruster , Richard Henderson Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Type: text/plain; charset="utf-8" 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. Signed-off-by: Vanderson M. do Rosario --- accel/tcg/tb-stats.c | 398 ++++++++++++++++++++++++++++++++++- accel/tcg/translate-all.c | 2 +- disas.c | 31 ++- hmp-commands-info.hx | 24 +++ include/exec/tb-stats.h | 43 +++- include/qemu/log-for-trace.h | 4 + include/qemu/log.h | 2 + monitor/misc.c | 74 +++++++ util/log.c | 52 ++++- 9 files changed, 609 insertions(+), 21 deletions(-) diff --git a/accel/tcg/tb-stats.c b/accel/tcg/tb-stats.c index f28fd7b434..f5e519bdb7 100644 --- a/accel/tcg/tb-stats.c +++ b/accel/tcg/tb-stats.c @@ -11,9 +11,36 @@ =20 /* only accessed in safe work */ static GList *last_search; - +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; +} + +static TBStatistics *get_tbstats_by_addr(target_ulong pc) +{ + GList *iter; + for (iter =3D last_search; iter; iter =3D g_list_next(iter)) { + TBStatistics *tbs =3D iter->data; + if (tbs && tbs->pc =3D=3D pc) { + return tbs; + break; + } + } + return NULL; +} + struct jit_profile_info { uint64_t translations; uint64_t aborted; @@ -155,6 +182,7 @@ static void clean_tbstats(void) qht_destroy(&tb_ctx.tb_stats); } =20 + void do_hmp_tbstats_safe(CPUState *cpu, run_on_cpu_data icmd) { struct TbstatsCommand *cmdinfo =3D icmd.host_ptr; @@ -242,6 +270,374 @@ void init_tb_stats_htable_if_not(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 dump_tb_targets(TBStatistics *tbs) +{ + if (tbs && tbs->tb) { + uintptr_t dst1 =3D atomic_read(tbs->tb->jmp_dest); + uintptr_t dst2 =3D atomic_read(tbs->tb->jmp_dest + 1); + TranslationBlock* tb_dst1 =3D dst1 > 1 ? (TranslationBlock *) dst1= : 0; + TranslationBlock* tb_dst2 =3D dst2 > 1 ? (TranslationBlock *) dst2= : 0; + target_ulong pc1 =3D tb_dst1 ? tb_dst1->pc : 0; + target_ulong pc2 =3D tb_dst2 ? tb_dst2->pc : 0; + + /* if there is no display id from the last_search, then create one= */ + TBStatistics *tbstats_pc1 =3D get_tbstats_by_addr(pc1); + TBStatistics *tbstats_pc2 =3D get_tbstats_by_addr(pc2); + + if (!tbstats_pc1 && tb_dst1 && tb_dst1->tb_stats) { + last_search =3D g_list_append(last_search, tb_dst1->tb_stats); + tbstats_pc1 =3D tb_dst1->tb_stats; + } + + if (!tbstats_pc2 && tb_dst2 && tb_dst2->tb_stats) { + last_search =3D g_list_append(last_search, tb_dst2->tb_stats); + tbstats_pc2 =3D tb_dst2->tb_stats; + } + + if (tbstats_pc1 && tbstats_pc1->display_id =3D=3D 0) { + tbstats_pc1->display_id =3D id++; + } + + if (tbstats_pc2 && tbstats_pc2->display_id =3D=3D 0) { + tbstats_pc2->display_id =3D id++; + } + + if (pc1 && !pc2) { + qemu_log("\t| targets: 0x"TARGET_FMT_lx" (id:%d)\n", + pc1, tb_dst1 ? tbstats_pc1->display_id : -1); + } else if (pc1 && pc2) { + qemu_log("\t| targets: 0x"TARGET_FMT_lx" (id:%d), " + "0x"TARGET_FMT_lx" (id:%d)\n", + pc1, tb_dst1 ? tbstats_pc1->display_id : -1, + pc2, tb_dst2 ? tbstats_pc2->display_id : -1); + } else { + qemu_log("\t| targets: no direct target\n"); + } + } +} + +static void 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); + + float guest_host_prop =3D g ? ((float) h / g) : 0; + + qemu_log("TB id:%d | phys:0x"TB_PAGE_ADDR_FMT" virt:0x"TARGET_FMT_lx + " flags:%#08x\n", tbs->display_id, tbs->phys_pc, tbs->pc, tbs= ->flags); + + if (tbs_stats_enabled(tbs, TB_EXEC_STATS)) { + qemu_log("\t| exec:%lu/%lu\n", tbs->executions.normal, tbs->execut= ions.atomic); + } + + 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->time.code / 2.4, tbs->time.interm / 2.4); + } + + dump_tb_targets(tbs); + qemu_log("\n"); +} + +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 *((int *) psort_by); + unsigned long c1 =3D 0; + unsigned long c2 =3D 0; + + if (likely(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 (likely(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 (likely(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; + } + + float a =3D + (float) stat_per_translation(tbs1, code.out_len) / tbs1->code.= num_guest_inst; + float b =3D + (float) stat_per_translation(tbs2, code.out_len) / tbs2->code.= num_guest_inst; + c1 =3D a <=3D b ? 0 : 1; + c2 =3D a <=3D b ? 1 : 0; + } + + 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 *i; + for (i =3D last_search; i && count--; i =3D i->next) { + TBStatistics *tbs =3D (TBStatistics *) i->data; + dump_tb_header(tbs); + } +} + +static void do_dump_coverset_info(int percentage) +{ + uint64_t total_exec_count =3D 0; + uint64_t covered_exec_count =3D 0; + unsigned coverset_size =3D 0; + id =3D 1; + GList *i; + + g_list_free(last_search); + last_search =3D NULL; + + qht_iter(&tb_ctx.tb_stats, collect_tb_stats, NULL); + + int sort_by =3D SORT_BY_HOTNESS; + last_search =3D g_list_sort_with_data(last_search, inverse_sort_tbs, &= sort_by); + + if (!last_search) { + qemu_log("No data collected yet\n"); + return; + } + + /* 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.normal * tbs->code.num_guest= _inst; + } + + for (i =3D last_search; i; i =3D i->next) { + TBStatistics *tbs =3D (TBStatistics *) i->data; + covered_exec_count +=3D tbs->executions.normal * tbs->code.num_gue= st_inst; + tbs->display_id =3D id++; + coverset_size++; + + /* Iterate and display tbs until reach the percentage count cover = */ + if ((covered_exec_count * 100) / total_exec_count > percentage) { + break; + } + } + + qemu_log("\n------------------------------\n"); + qemu_log("# of TBs to reach %d%% of the total of guest insts exec: %u\= t", + percentage, coverset_size); + qemu_log("Total of guest insts exec: %lu\n", total_exec_count); + qemu_log("\n------------------------------\n"); + + /* 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(coverset_size); +} + +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; + } + + 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); +} + +static void +do_dump_coverset_info_safe(CPUState *cpu, run_on_cpu_data percentage) +{ + qemu_log_to_monitor(true); + do_dump_coverset_info(percentage.host_int); + qemu_log_to_monitor(false); +} + +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_coverset_info(int percentage, bool use_monitor) +{ + if (use_monitor) { + async_safe_run_on_cpu(first_cpu, do_dump_coverset_info_safe, + RUN_ON_CPU_HOST_INT(percentage)); + } else { + do_dump_coverset_info(percentage); + } +} + +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); + } +} + +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() | CF_NOCACHE; + TranslationBlock *tb =3D NULL; + + GString *code_s =3D g_string_new(NULL); + qemu_log_to_string(true, code_s); + + qemu_set_log(log_flags); + + 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. + */ + fprintf(stderr, "%s: dbg failed!\n", __func__); + qemu_log("\ncould not gen code for this TB\n"); + assert_no_pages_locked(); + } + + qemu_set_log(old_log_flags); + qemu_log_to_string(false, NULL); + + if (tb) { + tcg_tb_remove(tb); + } + + return code_s; +} + +static void do_tb_dump_with_statistics(TBStatistics *tbs, int log_flags) +{ + qemu_log("\n------------------------------\n\n"); + dump_tb_header(tbs); + + GString *code_s =3D get_code_string(tbs, log_flags); + qemu_log("%s", code_s->str); + g_string_free(code_s, true); + 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) { init_tb_stats_htable_if_not(); diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index b94a1d67b6..0807411a8c 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1779,7 +1779,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, * generation so we can count interesting things about this * generation. */ - if (tb_stats_collection_enabled()) { + if (tb_stats_collection_enabled() && !(tb->cflags & CF_NOCACHE)) { tb->tb_stats =3D tb_get_stats(phys_pc, pc, cs_base, flags, tb); uint32_t flag =3D get_default_tbstats_flag(); =20 diff --git a/disas.c b/disas.c index 3e2bfa572b..d5292d4246 100644 --- a/disas.c +++ b/disas.c @@ -8,6 +8,8 @@ #include "disas/disas.h" #include "disas/capstone.h" =20 +#include "qemu/log-for-trace.h" + typedef struct CPUDebug { struct disassemble_info info; CPUState *cpu; @@ -420,6 +422,22 @@ static bool cap_disas_monitor(disassemble_info *info, = uint64_t pc, int count) # define cap_disas_monitor(i, p, c) false #endif /* CONFIG_CAPSTONE */ =20 +static int fprintf_log(struct _IO_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, target_ulong code, target_ulong size) @@ -429,7 +447,7 @@ void target_disas(FILE *out, CPUState *cpu, target_ulon= g code, int count; CPUDebug s; =20 - INIT_DISASSEMBLE_INFO(s.info, out, fprintf); + INIT_DISASSEMBLE_INFO(s.info, out, fprintf_log); =20 s.cpu =3D cpu; s.info.read_memory_func =3D target_read_memory; @@ -460,11 +478,12 @@ void target_disas(FILE *out, CPUState *cpu, target_ul= ong code, } =20 for (pc =3D code; size > 0; pc +=3D count, size -=3D count) { - fprintf(out, "0x" TARGET_FMT_lx ": ", pc); - count =3D s.info.print_insn(pc, &s.info); - fprintf(out, "\n"); - if (count < 0) - break; + fprintf_log(out, "0x" TARGET_FMT_lx ": ", pc); + count =3D s.info.print_insn(pc, &s.info); + fprintf_log(out, "\n"); + if (count < 0) { + break; + } if (size < count) { fprintf(out, "Disassembler disagrees with translator over instructi= on " diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index c59444c461..6691303c59 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -289,6 +289,30 @@ ETEXI .help =3D "show dynamic compiler info", .cmd =3D hmp_info_jit, }, + { + .name =3D "tbs", + .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_tbs, + }, + { + .name =3D "tb", + .args_type =3D "id:i,flags:s?", + .params =3D "id [log1[,...] flags]", + .help =3D "show information about one translated block by id= ." + "a dump flag can be used to set dump code level: out= _asm in_asm op", + .cmd =3D hmp_info_tb, + }, + { + .name =3D "coverset", + .args_type =3D "coverage:i?", + .params =3D "[coverage]", + .help =3D "show hottest translated blocks neccesary to cover" + "[coverage]% of the execution count", + .cmd =3D hmp_info_coverset, + }, #endif =20 STEXI diff --git a/include/exec/tb-stats.h b/include/exec/tb-stats.h index 9271b90924..dc2a8155a0 100644 --- a/include/exec/tb-stats.h +++ b/include/exec/tb-stats.h @@ -9,8 +9,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) @@ -56,7 +59,6 @@ struct TBStatistics { =20 struct { unsigned long total; - unsigned long uncached; unsigned long spanning; } translations; =20 @@ -69,6 +71,9 @@ struct TBStatistics { uint64_t la; } time; =20 + /* HMP information - used for referring to previous search */ + int display_id; + /* current TB linked to this TBStatistics */ TranslationBlock *tb; }; @@ -89,6 +94,40 @@ struct TbstatsCommand { =20 void do_hmp_tbstats_safe(CPUState *cpu, run_on_cpu_data icmd); =20 +/** + * dump_coverset_info: report the hottest blocks to cover n% of execution + * + * @percentage: cover set percentage + * @use_monitor: redirect output to monitor + * + * Report the hottest blocks to either the log or monitor + */ +void dump_coverset_info(int percentage, bool use_monitor); + + +/** + * 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); + + /* TBStatistic collection controls */ void enable_collect_tb_stats(void); void disable_collect_tb_stats(void); diff --git a/include/qemu/log-for-trace.h b/include/qemu/log-for-trace.h index 2f0a5b080e..3de88484cb 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 @@ -31,5 +34,6 @@ static inline bool qemu_loglevel_mask(int mask) =20 /* main logging function */ int GCC_FMT_ATTR(1, 2) qemu_log(const char *fmt, ...); +int qemu_vlog(const char *fmt, va_list va); =20 #endif diff --git a/include/qemu/log.h b/include/qemu/log.h index a8d1997cde..804cf90f0f 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -114,6 +114,8 @@ typedef struct QEMULogItem { extern const QEMULogItem qemu_log_items[]; =20 void qemu_set_log(int log_flags); +void qemu_log_to_monitor(bool enable); +void qemu_log_to_string(bool enable, GString *s); void qemu_log_needs_buffers(void); void qemu_set_log_filename(const char *filename, Error **errp); void qemu_set_dfilter_ranges(const char *ranges, Error **errp); diff --git a/monitor/misc.c b/monitor/misc.c index 6902e8addb..4af70dba92 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -504,6 +504,80 @@ static void hmp_tbstats(Monitor *mon, const QDict *qdi= ct) =20 } =20 +static void hmp_info_tbs(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); +} + +static 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); +} + +static void hmp_info_coverset(Monitor *mon, const QDict *qdict) +{ + int coverage; + if (!tcg_enabled()) { + error_report("TB information is only available with accel=3Dtcg"); + return; + } + if (!tb_stats_collection_enabled()) { + error_report("TB information not being recorded"); + return; + } + + coverage =3D qdict_get_try_int(qdict, "coverage", 90); + + if (coverage < 0 || coverage > 100) { + error_report("Coverset percentage should be between 0 and 100"); + return; + } + + dump_coverset_info(coverage, true); +} + static void hmp_info_jit(Monitor *mon, const QDict *qdict) { if (!tcg_enabled()) { diff --git a/util/log.c b/util/log.c index 09cfb13b45..7d28a844c1 100644 --- a/util/log.c +++ b/util/log.c @@ -33,28 +33,58 @@ int qemu_loglevel; static int log_append =3D 0; static GArray *debug_regions; int32_t max_num_hot_tbs_to_dump; +static bool to_monitor; +bool to_string; =20 int tcg_collect_tb_stats; uint32_t default_tbstats_flag; =20 -/* Return the number of characters emitted. */ -int qemu_log(const char *fmt, ...) +GString *string; + +int qemu_vlog(const char *fmt, va_list va) { int ret =3D 0; - if (qemu_logfile) { - va_list ap; - va_start(ap, fmt); - ret =3D vfprintf(qemu_logfile, fmt, ap); - va_end(ap); - - /* Don't pass back error results. */ - if (ret < 0) { - ret =3D 0; + if (to_string) { + if (string) { + g_string_append_vprintf(string, fmt, va); } + } else if (to_monitor) { + ret =3D qemu_vprintf(fmt, va); + } else if (qemu_logfile) { + ret =3D vfprintf(qemu_logfile, fmt, va); + } + + /* Don't pass back error results. */ + if (ret < 0) { + ret =3D 0; } return ret; } =20 +/* 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; +} + static bool log_uses_own_buffers; =20 /* enable or disable low levels log */ --=20 2.22.0