From nobody Sat May 30 17:44:38 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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=163.com ARC-Seal: i=1; a=rsa-sha256; t=1779626742; cv=none; d=zohomail.com; s=zohoarc; b=nc6GsLDK64MKrv4vguzkeh0JO4cA3dCjkNOGOH/tjEHYHnMZ+CxXS8AGTTfOpBSZk3joUOFKbiFFFUCoD0214bYztnBxq89Vq4MWUjyCwpMC9OHz26aAuISWuzB2j4HpnsV3OwxmD5IaPDPFpXaBFLo/hCQ8BogrObJqso1fECs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779626742; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=zzh60lKDY0sAa6r+JwG+P449lmNK2iJfGw1zjmk1AKo=; b=G2K1bEBie/vrZUsJz/8uaGLLLky0AIIzuTsdgmMGxRwCNEAftn2GkTOTwU1dmSJ0LyfDA7qcRZGMbZAss3DjeSKqubIyCIK7vBHBWtKaP60s0zAB+09SBzjUdE2mSTf+X1nilSjynJHBpT89f1xzeGL15IR/mkISeB/+fuHya54= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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 lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 177962674235750.42821674477841; Sun, 24 May 2026 05:45:42 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wR8Ch-0006WT-LT; Sun, 24 May 2026 08:45:15 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wR8Cf-0006Vb-14 for qemu-devel@nongnu.org; Sun, 24 May 2026 08:45:13 -0400 Received: from m16.mail.163.com ([117.135.210.5]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wR8CZ-0008F5-0C for qemu-devel@nongnu.org; Sun, 24 May 2026 08:45:12 -0400 Received: from alano.. (unknown []) by gzga-smtp-mtada-g1-3 (Coremail) with SMTP id _____wD3VwT78RJq4FHgDA--.19428S2; Sun, 24 May 2026 20:41:32 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=zz h60lKDY0sAa6r+JwG+P449lmNK2iJfGw1zjmk1AKo=; b=Qki9OrUavCZRoMFQ5n 0uB/xzbu8WwQg7DcL7J/LbJV3fwcFamHgF4ngBPD1uNuEdF2Mt2mpnTccaGYCgwc zcy6pZVLNKfQUnbByg9sZ20fs/4QJE247gfAB+rnEJ1X3qbZ4aPzkXR99M8phjbM ybvMml5v536ghIelqPJGWr+LM= From: AlanoSong@163.com To: qemu-devel@nongnu.org Cc: dave@treblig.org, pbonzini@redhat.com, zhao1.liu@intel.com, Alano Song Subject: [PATCH] target/i386/monitor: Refine `info tlb` command logic Date: Sun, 24 May 2026 20:41:29 +0800 Message-ID: <20260524124129.174254-1-AlanoSong@163.com> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3VwT78RJq4FHgDA--.19428S2 X-Coremail-Antispam: 1Uf129KBjvJXoW3Aw1fJw1xur1kWw13tw45trb_yoWkuF15pa 1ayr9Iqrs7tF13X34fAwnrtr13ArWru3yYgFnI9r10k3Z8Wwnru3s7tw43tr9xWFW8Xw4f uw1qka1UWF43XaDanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0pi9qXfUUUUU= X-Originating-IP: [59.173.201.222] X-CM-SenderInfo: xdod00pvrqwqqrwthudrp/xtbC0xza7moS8fw4qwAA3d 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=lists1p.gnu.org; Received-SPF: pass client-ip=117.135.210.5; envelope-from=alanosong@163.com; helo=m16.mail.163.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=0.001 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: qemu development 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 @163.com) X-ZM-MESSAGEID: 1779626744806158500 Content-Type: text/plain; charset="utf-8" When booting an i386 guest, the `info tlb` command may walk the entire page table hierarchy and emit an enormous amount of output (as many as 800+ million entries under my test). It will take dozens of minutes to print all the info, because each monitor print function will holds 'mon_lock'. This effectively hangs the qemu monitor, and even leading to program exit due to timeout. So it may be better to show only the first 32 entries and the last entry by default. And if full output is required, the user can redirect the output to a file by specifying a file name, e.g.: `info tlb xxx.txt`. Signed-off-by: Alano Song --- hmp-commands-info.hx | 10 +- target/i386/monitor.c | 230 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 195 insertions(+), 45 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 82134eb6c2..33b4604d11 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -188,17 +188,19 @@ ERST =20 { .name =3D "tlb", - .args_type =3D "", - .params =3D "", - .help =3D "show virtual to physical memory mappings", + .args_type =3D "file:s?", + .params =3D "[file]", + .help =3D "show virtual to physical memory mappings (optiona= lly save to file)", .cmd =3D hmp_info_tlb, .arch_bitmask =3D QEMU_ARCH_I386 | QEMU_ARCH_SH4 | QEMU_ARCH_SPARC= \ | QEMU_ARCH_PPC | QEMU_ARCH_XTENSA | QEMU_ARCH_M68= K, }, =20 SRST - ``info tlb`` + ``info tlb`` [*file*] Show virtual to physical memory mappings. + If *file* is specified, the output is saved to the given file + instead of being printed to the monitor. ERST =20 { diff --git a/target/i386/monitor.c b/target/i386/monitor.c index a536712c75..9b8be71cab 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -24,6 +24,7 @@ =20 #include "qemu/osdep.h" #include "cpu.h" +#include "qemu/qemu-print.h" #include "monitor/monitor.h" #include "monitor/hmp.h" #include "qobject/qdict.h" @@ -48,31 +49,48 @@ static hwaddr addr_canonical(CPUArchState *env, hwaddr = addr) return addr; } =20 -static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr, +static void G_GNUC_PRINTF(3, 4) +do_print_pte(Monitor *mon, FILE *fp, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (fp) { + qemu_vfprintf(fp, fmt, ap); + } else { + monitor_vprintf(mon, fmt, ap); + } + va_end(ap); +} + +static void print_pte(Monitor *mon, FILE *fp, CPUArchState *env, hwaddr ad= dr, hwaddr pte, hwaddr mask) { addr =3D addr_canonical(env, addr); =20 - monitor_printf(mon, HWADDR_FMT_plx ": " HWADDR_FMT_plx - " %c%c%c%c%c%c%c%c%c\n", - addr, - pte & mask, - pte & PG_NX_MASK ? 'X' : '-', - pte & PG_GLOBAL_MASK ? 'G' : '-', - pte & PG_PSE_MASK ? 'P' : '-', - pte & PG_DIRTY_MASK ? 'D' : '-', - pte & PG_ACCESSED_MASK ? 'A' : '-', - pte & PG_PCD_MASK ? 'C' : '-', - pte & PG_PWT_MASK ? 'T' : '-', - pte & PG_USER_MASK ? 'U' : '-', - pte & PG_RW_MASK ? 'W' : '-'); + do_print_pte(mon, fp, HWADDR_FMT_plx ": " HWADDR_FMT_plx + " %c%c%c%c%c%c%c%c%c\n", + addr, + pte & mask, + pte & PG_NX_MASK ? 'X' : '-', + pte & PG_GLOBAL_MASK ? 'G' : '-', + pte & PG_PSE_MASK ? 'P' : '-', + pte & PG_DIRTY_MASK ? 'D' : '-', + pte & PG_ACCESSED_MASK ? 'A' : '-', + pte & PG_PCD_MASK ? 'C' : '-', + pte & PG_PWT_MASK ? 'T' : '-', + pte & PG_USER_MASK ? 'U' : '-', + pte & PG_RW_MASK ? 'W' : '-'); } =20 -static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as) +static void tlb_info_32(Monitor *mon, FILE *fp, CPUArchState *env, + AddressSpace *as) { const MemTxAttrs attrs =3D MEMTXATTRS_UNSPECIFIED; unsigned int l1, l2; uint32_t pgd, pde, pte; + uint64_t count =3D 0; + hwaddr tmp_addr =3D (hwaddr)-1, tmp_pte =3D (hwaddr)-1, tmp_mask =3D 0; =20 pgd =3D env->cr[3] & ~0xfff; for(l1 =3D 0; l1 < 1024; l1++) { @@ -80,28 +98,53 @@ static void tlb_info_32(Monitor *mon, CPUArchState *env= , AddressSpace *as) if (pde & PG_PRESENT_MASK) { if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { /* 4M pages */ - print_pte(mon, env, (l1 << 22), pde, ~((1 << 21) - 1)); + tmp_addr =3D (l1 << 22); + tmp_pte =3D pde; + tmp_mask =3D ~((1 << 21) - 1); + if (fp || count++ < 32) { + print_pte(mon, fp, env, tmp_addr, tmp_pte, tmp_mask); + } } else { for(l2 =3D 0; l2 < 1024; l2++) { pte =3D address_space_ldl_le(as, (pde & ~0xfff) + l2 *= 4, attrs, NULL); if (pte & PG_PRESENT_MASK) { - print_pte(mon, env, (l1 << 22) + (l2 << 12), - pte & ~PG_PSE_MASK, - ~0xfff); + tmp_addr =3D (l1 << 22) + (l2 << 12); + tmp_pte =3D pte & ~PG_PSE_MASK; + tmp_mask =3D ~0xfff; + if (fp || count++ < 32) { + print_pte(mon, fp, env, tmp_addr, tmp_pte, + tmp_mask); + } } } } } } + + /* + * No need to print the last entry: + * 1) we already print all the entries into file. + * 2) entries number <=3D 32, and have been printed above. + */ + if (!fp && count > 32) { + if (count > 33) { + do_print_pte(mon, fp, "... (%lu entries skipped) ...\n", + (count - 33)); + } + print_pte(mon, fp, env, tmp_addr, tmp_pte, tmp_mask); + } } =20 -static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *= as) +static void tlb_info_pae32(Monitor *mon, FILE *fp, CPUArchState *env, + AddressSpace *as) { const MemTxAttrs attrs =3D MEMTXATTRS_UNSPECIFIED; unsigned int l1, l2, l3; uint64_t pdpe, pde, pte; uint64_t pdp_addr, pd_addr, pt_addr; + uint64_t count =3D 0; + hwaddr tmp_addr =3D (hwaddr)-1, tmp_pte =3D (hwaddr)-1, tmp_mask =3D 0; =20 pdp_addr =3D env->cr[3] & ~0x1f; for (l1 =3D 0; l1 < 4; l1++) { @@ -113,18 +156,27 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState= *env, AddressSpace *as) if (pde & PG_PRESENT_MASK) { if (pde & PG_PSE_MASK) { /* 2M pages with PAE, CR4.PSE is ignored */ - print_pte(mon, env, (l1 << 30) + (l2 << 21), pde, - ~((hwaddr)(1 << 20) - 1)); + tmp_addr =3D (l1 << 30) + (l2 << 21); + tmp_pte =3D pde; + tmp_mask =3D ~((hwaddr)(1 << 20) - 1); + if (fp || count++ < 32) { + print_pte(mon, fp, env, tmp_addr, tmp_pte, + tmp_mask); + } } else { pt_addr =3D pde & 0x3fffffffff000ULL; for (l3 =3D 0; l3 < 512; l3++) { pte =3D address_space_ldq_le(as, pt_addr + l3 = * 8, attrs, NULL); if (pte & PG_PRESENT_MASK) { - print_pte(mon, env, (l1 << 30) + (l2 << 21) - + (l3 << 12), - pte & ~PG_PSE_MASK, - ~(hwaddr)0xfff); + tmp_addr =3D (l1 << 30) + (l2 << 21) + + (l3 << 12); + tmp_pte =3D pte & ~PG_PSE_MASK; + tmp_mask =3D ~(hwaddr)0xfff; + if (fp || count++ < 32) { + print_pte(mon, fp, env, tmp_addr, tmp_= pte, + tmp_mask); + } } } } @@ -132,16 +184,37 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState= *env, AddressSpace *as) } } } + + /* + * No need to print the last entry: + * 1) we already print all the entries into file. + * 2) entries number <=3D 32, and have been printed above. + */ + if (!fp && count > 32) { + if (count > 33) { + monitor_printf(mon, "... (%lu entries skipped) ...\n", + (count - 33)); + } + print_pte(mon, fp, env, tmp_addr, tmp_pte, tmp_mask); + } } =20 #ifdef TARGET_X86_64 -static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *a= s, - uint64_t l0, uint64_t pml4_addr) +static void tlb_info_la48(Monitor *mon, FILE *fp, CPUArchState *env, + AddressSpace *as, uint64_t l0, uint64_t pml4_add= r, + uint64_t *init_count, hwaddr *last_addr, + hwaddr *last_pte, hwaddr *last_mask) { const MemTxAttrs attrs =3D MEMTXATTRS_UNSPECIFIED; uint64_t l1, l2, l3, l4; uint64_t pml4e, pdpe, pde, pte; uint64_t pdp_addr, pd_addr, pt_addr; + uint64_t count =3D 0; + hwaddr tmp_addr =3D (hwaddr)-1, tmp_pte=3D(hwaddr)-1, tmp_mask =3D 0; + + if (init_count) { + count =3D *init_count; + } =20 for (l1 =3D 0; l1 < 512; l1++) { pml4e =3D address_space_ldq_le(as, pml4_addr + l1 * 8, attrs, NULL= ); @@ -158,8 +231,12 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *= env, AddressSpace *as, =20 if (pdpe & PG_PSE_MASK) { /* 1G pages, CR4.PSE is ignored */ - print_pte(mon, env, (l0 << 48) + (l1 << 39) + (l2 << 30), - pdpe, 0x3ffffc0000000ULL); + tmp_addr =3D (l0 << 48) + (l1 << 39) + (l2 << 30); + tmp_pte =3D pdpe; + tmp_mask =3D 0x3ffffc0000000ULL; + if (fp || count++ < 32) { + print_pte(mon, fp, env, tmp_addr, tmp_pte, tmp_mask); + } continue; } =20 @@ -172,8 +249,13 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *= env, AddressSpace *as, =20 if (pde & PG_PSE_MASK) { /* 2M pages, CR4.PSE is ignored */ - print_pte(mon, env, (l0 << 48) + (l1 << 39) + (l2 << 3= 0) + - (l3 << 21), pde, 0x3ffffffe00000ULL); + tmp_addr =3D (l0 << 48) + (l1 << 39) + + (l2 << 30) + (l3 << 21); + tmp_pte =3D pde; + tmp_mask =3D 0x3ffffffe00000ULL; + if (fp || count++ < 32) { + print_pte(mon, fp, env, tmp_addr, tmp_pte, tmp_mas= k); + } continue; } =20 @@ -182,30 +264,77 @@ static void tlb_info_la48(Monitor *mon, CPUArchState = *env, AddressSpace *as, pte =3D address_space_ldq_le(as, pt_addr + l4 * 8, attrs, NULL); if (pte & PG_PRESENT_MASK) { - print_pte(mon, env, (l0 << 48) + (l1 << 39) + - (l2 << 30) + (l3 << 21) + (l4 << 12), - pte & ~PG_PSE_MASK, 0x3fffffffff000ULL); + tmp_addr =3D (l0 << 48) + (l1 << 39) + (l2 << 30) + + (l3 << 21) + (l4 << 12); + tmp_pte =3D pte & ~PG_PSE_MASK; + tmp_mask =3D 0x3fffffffff000ULL; + if (fp || count++ < 32) { + print_pte(mon, fp, env, tmp_addr, tmp_pte, + tmp_mask); + } } } } } } + + /* + * No need to print the current last entry: + * 1) we already print all the entries into file. + * 2) other function call this function, and only the caller + * know if reach the end, let caller print the last one. + * 3) entries number <=3D 32, and have been printed above. + */ + if (!fp && !init_count && count > 32) { + if (count > 33) { + do_print_pte(mon, fp, "... (%lu entries skipped) ...\n", + (count - 33)); + } + print_pte(mon, fp, env, tmp_addr, tmp_pte, tmp_mask); + } + + if (init_count) { + *init_count =3D count; + } + + if (last_addr && last_pte && last_mask) { + *last_addr =3D tmp_addr; + *last_pte =3D tmp_pte; + *last_mask =3D tmp_mask; + } } =20 -static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *a= s) +static void tlb_info_la57(Monitor *mon, FILE *fp, CPUArchState *env, + AddressSpace *as) { const MemTxAttrs attrs =3D MEMTXATTRS_UNSPECIFIED; uint64_t l0; uint64_t pml5e; uint64_t pml5_addr; + uint64_t count =3D 0; + hwaddr last_addr =3D (hwaddr)-1, last_pte=3D(hwaddr)-1, last_mask =3D = 0; =20 pml5_addr =3D env->cr[3] & 0x3fffffffff000ULL; for (l0 =3D 0; l0 < 512; l0++) { pml5e =3D address_space_ldq_le(as, pml5_addr + l0 * 8, attrs, NULL= ); if (pml5e & PG_PRESENT_MASK) { - tlb_info_la48(mon, env, as, l0, pml5e & 0x3fffffffff000ULL); + tlb_info_la48(mon, fp, env, as, l0, pml5e & 0x3fffffffff000ULL, + &count, &last_addr, &last_pte, &last_mask); } } + + /* + * No need to print the last entry: + * 1) we already print all the entries into file. + * 2) entries number <=3D 32, and have been printed above. + */ + if (!fp && count > 32) { + if (count > 33) { + monitor_printf(mon, "... (%lu entries skipped) ...\n", + (count - 33)); + } + print_pte(mon, fp, env, last_addr, last_pte, last_mask); + } } #endif /* TARGET_X86_64 */ =20 @@ -213,6 +342,8 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict) { CPUArchState *env; AddressSpace *as; + const char *filename; + FILE *fp =3D NULL; =20 env =3D mon_get_cpu_env(mon); if (!env) { @@ -224,22 +355,39 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict) monitor_printf(mon, "PG disabled\n"); return; } + + filename =3D qdict_get_try_str(qdict, "file"); + if (filename) { + fp =3D fopen(filename, "w"); + if (!fp) { + monitor_printf(mon, "Cannot open file '%s': %s\n", + filename, strerror(errno)); + return; + } + } + as =3D cpu_get_address_space(env_cpu(env), X86ASIdx_MEM); if (env->cr[4] & CR4_PAE_MASK) { #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { if (env->cr[4] & CR4_LA57_MASK) { - tlb_info_la57(mon, env, as); + tlb_info_la57(mon, fp, env, as); } else { - tlb_info_la48(mon, env, as, 0, env->cr[3] & 0x3fffffffff00= 0ULL); + tlb_info_la48(mon, fp, env, as, 0, + env->cr[3] & 0x3fffffffff000ULL, + NULL, NULL, NULL, NULL); } } else #endif { - tlb_info_pae32(mon, env, as); + tlb_info_pae32(mon, fp, env, as); } } else { - tlb_info_32(mon, env, as); + tlb_info_32(mon, fp, env, as); + } + + if (fp) { + fclose(fp); } } =20 --=20 2.43.0