From nobody Sun Nov 24 08:24:58 2024 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=linaro.org ARC-Seal: i=1; a=rsa-sha256; t=1725977689; cv=none; d=zohomail.com; s=zohoarc; b=l/nHBJZVM7KeV/sFomjxc7oPNFddDVxcaQfSxDERbU+gVT78lPoJOnVGpths4tGotadMUJBrsqMaBelo/LcyM1LhsRXlVSOvIdEPIdxMQvxILwBqYVu7eqanHffPxA4kH1C4Q4Um948eq9hCd7kgJ9O1ap+AqvSj2+SxgNyAkWM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1725977689; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=Zroaak8xFFAJBnGJiz25P6JCeHv1e5w1wtwssc9iSGg=; b=eI9QvnfbTHlXw8YDKmeTZ0dl88hobzLpJRWVR2MSZuaPBt27IWhtkEG1FEBSsbwPBsC95GcDrRb1vwxjZaNBL4Ip2EjvPs19sk+2aY0K2Ff6pWh0yaqwcbLzF+xRrXYhjTKWTQfzVuIz3Z+md3eywyZqxuP5pUag77fg5k/r5t0= 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 lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1725977689980660.6015669103168; Tue, 10 Sep 2024 07:14:49 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1so1d3-00030i-I2; Tue, 10 Sep 2024 10:14:01 -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 1so1d1-0002jy-6J for qemu-devel@nongnu.org; Tue, 10 Sep 2024 10:13:59 -0400 Received: from mail-lj1-x22c.google.com ([2a00:1450:4864:20::22c]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1so1cx-0007Ub-Qh for qemu-devel@nongnu.org; Tue, 10 Sep 2024 10:13:58 -0400 Received: by mail-lj1-x22c.google.com with SMTP id 38308e7fff4ca-2f029e9c9cfso1351701fa.2 for ; Tue, 10 Sep 2024 07:13:55 -0700 (PDT) Received: from draig.lan ([85.9.250.243]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8d2597a660sm485506266b.75.2024.09.10.07.13.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Sep 2024 07:13:47 -0700 (PDT) Received: from draig.lan (localhost [IPv6:::1]) by draig.lan (Postfix) with ESMTP id BF45F5F78C; Tue, 10 Sep 2024 15:07:35 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1725977634; x=1726582434; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Zroaak8xFFAJBnGJiz25P6JCeHv1e5w1wtwssc9iSGg=; b=HhONXx0xTryf6DjGZNASbMK047t84N9O8UJKqZCuUYU19vA8j0NS8xF/tVVu5+SI5P R1QOJeTk0J6IQL3IJfYQTFd4f+U8a1bB5ZUKL/H/MviOEpBhp7uFaGG5/boe+DEY8F6v Hp9SkSjpimURxO8iHN8lmd+HLS4RFmTOXuhDmTPMJCnHzrNUttDqQZStEt3hTeCr5krC tM6DKh2Q7l5/m5V3VwZrhxDfcGF92CrhWIAFttVOjHR8mKC960suk2AXVbykLe31hKPw YfCjghMQo799RitKPgSUqR77UQa9E7VFk3Y2C676pBKCxhD7dkSBapWGQmHjrVEa4RTG 4K0A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1725977634; x=1726582434; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Zroaak8xFFAJBnGJiz25P6JCeHv1e5w1wtwssc9iSGg=; b=wOYan0oF/eMlZ86FAOVZLOP+kSZKuLl5wjBo3J41+uLv9YX7EcmW6hqzB+rXhYvnky INcOQL/uxsODGUEwJwyuVgW761S2BNmpHbZH9bIkraaKTrAIJxflW+V34kIXG5bCJR5V GjNch1RdfRGyVjRPb4ioq/nFj7Bi55zHxr+8kuXMYRFMb+0jNIRS67xkXkZRABBd2UhJ FYB2fZjWvvDEs0J4TM1e6vQWNdRZHd5P+ytl8MZI0szW5c3KUdNW9zRHJTlG2Vm69Foo 9y0GnBYQKbxaYEw7TSduVJfDsLXp2Bt8jFs/CdFkx+y1ZrTT4UWrX8OjfEisRwZur0gb dzfg== X-Gm-Message-State: AOJu0YxYlZKnLHuDm/vN5ZrNKvI7PlrNJ+kjyX0zZARybn3npkKgDJm9 bBuiWJizGWVeZk7AleJuKyiMnlzJUvWWg1atQFc83hfn09eiIPLq/guZZG4ziPo= X-Google-Smtp-Source: AGHT+IGsJkcb234kPMUwzZHCkiUcDRpmtSNvGnEqJULGzYtF86etGrETcfSgYxqk0A37XstmKdzd3g== X-Received: by 2002:a2e:b88a:0:b0:2ee:52f4:266 with SMTP id 38308e7fff4ca-2f75a96c43bmr108676401fa.3.1725977633258; Tue, 10 Sep 2024 07:13:53 -0700 (PDT) From: =?UTF-8?q?Alex=20Benn=C3=A9e?= To: qemu-devel@nongnu.org Cc: Eduardo Habkost , Zhao Liu , Marcel Apfelbaum , Beraldo Leal , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Alexandre Iooss , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Pierrick Bouvier , Yanan Wang , Peter Maydell , Mahmoud Mandour , Thomas Huth , qemu-arm@nongnu.org, devel@lists.libvirt.org, Jiaxun Yang , Paolo Bonzini , Richard Henderson , Wainer dos Santos Moschetta Subject: [PATCH 21/26] tests/tcg: add a system test to check memory instrumentation Date: Tue, 10 Sep 2024 15:07:28 +0100 Message-Id: <20240910140733.4007719-22-alex.bennee@linaro.org> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240910140733.4007719-1-alex.bennee@linaro.org> References: <20240910140733.4007719-1-alex.bennee@linaro.org> 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=2a00:1450:4864:20::22c; envelope-from=alex.bennee@linaro.org; helo=mail-lj1-x22c.google.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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 @linaro.org) X-ZM-MESSAGEID: 1725977690537116600 At first I thought I could compile the user-mode test for system mode however we already have a fairly comprehensive test case for system mode in "memory" so lets use that. First we extend the test to report where the test_data region is. Then we expand the pdot() function to track the total number of reads and writes to the region. We have to add some addition pdot() calls to take into account multiple reads/writes in the test loops. As tracking every access will quickly build up with "print-access" we add a new mode to track groups of reads and writes to regions. Because the test_data is page aligned we can be sure all accesses to it are ones we can count. Finally we add a python script to integrate the data from the plugin and the output of the test and validate they both agree on the total counts. Signed-off-by: Alex Benn=C3=A9e --- v2 - aggressively align test_data on "region size" - sort the regions in the final report - ensure alpha-softmmu uses byte access when it can --- tests/tcg/multiarch/system/memory.c | 50 +++-- tests/tcg/plugins/mem.c | 178 +++++++++++++++++- tests/tcg/alpha/Makefile.softmmu-target | 2 +- .../multiarch/system/Makefile.softmmu-target | 6 + .../system/validate-memory-counts.py | 115 +++++++++++ 5 files changed, 332 insertions(+), 19 deletions(-) create mode 100755 tests/tcg/multiarch/system/validate-memory-counts.py diff --git a/tests/tcg/multiarch/system/memory.c b/tests/tcg/multiarch/syst= em/memory.c index 680dd4800b..ccff699015 100644 --- a/tests/tcg/multiarch/system/memory.c +++ b/tests/tcg/multiarch/system/memory.c @@ -14,26 +14,35 @@ =20 #include #include +#include #include =20 #ifndef CHECK_UNALIGNED # error "Target does not specify CHECK_UNALIGNED" #endif =20 +uint32_t test_read_count; +uint32_t test_write_count; + #define MEM_PAGE_SIZE 4096 /* nominal 4k "pages" */ #define TEST_SIZE (MEM_PAGE_SIZE * 4) /* 4 pages */ =20 #define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0]))) =20 -__attribute__((aligned(MEM_PAGE_SIZE))) +__attribute__((aligned(TEST_SIZE))) static uint8_t test_data[TEST_SIZE]; =20 typedef void (*init_ufn) (int offset); typedef bool (*read_ufn) (int offset); typedef bool (*read_sfn) (int offset, bool nf); =20 -static void pdot(int count) +static void pdot(int count, bool write) { + if (write) { + test_write_count++; + } else { + test_read_count++; + } if (count % 128 =3D=3D 0) { ml_printf("."); } @@ -67,7 +76,7 @@ static void init_test_data_u8(int unused_offset) =20 for (i =3D 0; i < TEST_SIZE; i++) { *ptr++ =3D BYTE_NEXT(count); - pdot(i); + pdot(i, true); } =20 ml_printf("done %d @ %p\n", i, ptr); @@ -93,8 +102,9 @@ static void init_test_data_s8(bool neg_first) neg_first ? "neg first" : "pos first"); for (i =3D 0; i < TEST_SIZE / 2; i++) { *ptr++ =3D get_byte(i, neg_first); + pdot(i, true); *ptr++ =3D get_byte(i, !neg_first); - pdot(i); + pdot(i, true); } ml_printf("done %d @ %p\n", i * 2, ptr); } @@ -116,6 +126,7 @@ static void reset_start_data(int offset) =20 for (i =3D 0; i < offset; i++) { *ptr++ =3D 0; + pdot(i, true); } =20 ml_printf("done %d @ %p\n", i, ptr); @@ -136,7 +147,7 @@ static void init_test_data_u16(int offset) uint16_t low =3D BYTE_NEXT(count), high =3D BYTE_NEXT(count); word =3D BYTE_SHIFT(high, 1) | BYTE_SHIFT(low, 0); *ptr++ =3D word; - pdot(i); + pdot(i, true); } ml_printf("done %d @ %p\n", i, ptr); } @@ -158,7 +169,7 @@ static void init_test_data_u32(int offset) word =3D BYTE_SHIFT(b1, 3) | BYTE_SHIFT(b2, 2) | BYTE_SHIFT(b3, 1)= | BYTE_SHIFT(b4, 0); *ptr++ =3D word; - pdot(i); + pdot(i, true); } ml_printf("done %d @ %p\n", i, ptr); } @@ -184,7 +195,7 @@ static void init_test_data_u64(int offset) BYTE_SHIFT(b4, 4) | BYTE_SHIFT(b5, 3) | BYTE_SHIFT(b6, 2) | BYTE_SHIFT(b7, 1) | BYTE_SHIFT(b8, 0); *ptr++ =3D word; - pdot(i); + pdot(i, true); } ml_printf("done %d @ %p\n", i, ptr); } @@ -207,7 +218,7 @@ static bool read_test_data_u16(int offset) ml_printf("Error %d < %d\n", high, low); return false; } else { - pdot(i); + pdot(i, false); } =20 } @@ -249,7 +260,7 @@ static bool read_test_data_u32(int offset) ml_printf("Error %d, %d, %d, %d", b1, b2, b3, b4); return false; } else { - pdot(i); + pdot(i, false); } } ml_printf("done %d @ %p\n", i, ptr); @@ -304,7 +315,7 @@ static bool read_test_data_u64(int offset) b1, b2, b3, b4, b5, b6, b7, b8); return false; } else { - pdot(i); + pdot(i, false); } } ml_printf("done %d @ %p\n", i, ptr); @@ -376,9 +387,11 @@ static bool read_test_data_s8(int offset, bool neg_fir= st) second =3D *ptr++; =20 if (neg_first && first < 0 && second > 0) { - pdot(i); + pdot(i, false); + pdot(i, false); } else if (!neg_first && first > 0 && second < 0) { - pdot(i); + pdot(i, false); + pdot(i, false); } else { ml_printf("Error %d %c %d\n", first, neg_first ? '<' : '>', se= cond); return false; @@ -409,9 +422,9 @@ static bool read_test_data_s16(int offset, bool neg_fir= st) int32_t data =3D *ptr++; =20 if (neg_first && data < 0) { - pdot(i); + pdot(i, false); } else if (!neg_first && data > 0) { - pdot(i); + pdot(i, false); } else { ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>'); return false; @@ -442,9 +455,9 @@ static bool read_test_data_s32(int offset, bool neg_fir= st) int64_t data =3D *ptr++; =20 if (neg_first && data < 0) { - pdot(i); + pdot(i, false); } else if (!neg_first && data > 0) { - pdot(i); + pdot(i, false); } else { ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>'); return false; @@ -498,6 +511,9 @@ int main(void) int i; bool ok =3D true; =20 + ml_printf("Test data start: 0x%"PRIxPTR"\n", &test_data[0]); + ml_printf("Test data end: 0x%"PRIxPTR"\n", &test_data[TEST_SIZE]); + /* Run through the unsigned tests first */ for (i =3D 0; i < ARRAY_SIZE(init_ufns) && ok; i++) { ok =3D do_unsigned_test(init_ufns[i]); @@ -513,6 +529,8 @@ int main(void) ok =3D do_signed_reads(true); } =20 + ml_printf("Test data read: %"PRId32"\n", test_read_count); + ml_printf("Test data write: %"PRId32"\n", test_write_count); ml_printf("Test complete: %s\n", ok ? "PASSED" : "FAILED"); return ok ? 0 : -1; } diff --git a/tests/tcg/plugins/mem.c b/tests/tcg/plugins/mem.c index 086e6f5bdf..42d735d5c8 100644 --- a/tests/tcg/plugins/mem.c +++ b/tests/tcg/plugins/mem.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include =20 @@ -26,13 +27,46 @@ typedef struct { const char *sym; } InsnInfo; =20 +/* + * For the "memory" system test we need to track accesses to + * individual regions. We mirror the data written to the region and + * then check when it is read that it matches up. + * + * We do this as regions rather than pages to save on complications + * with page crossing and the fact the test only cares about the + * test_data region. + */ +static uint64_t region_size =3D 4096 * 4; +static uint64_t region_mask; + +typedef struct { + uint64_t region_address; + uint64_t reads; + uint64_t writes; + uint8_t *data; + bool seen_all; /* Did we see every write and read with correct va= lues? */ +} RegionInfo; + static struct qemu_plugin_scoreboard *counts; static qemu_plugin_u64 mem_count; static qemu_plugin_u64 io_count; -static bool do_inline, do_callback, do_print_accesses; +static bool do_inline, do_callback, do_print_accesses, do_region_summary; static bool do_haddr; static enum qemu_plugin_mem_rw rw =3D QEMU_PLUGIN_MEM_RW; =20 + +static GMutex lock; +static GHashTable *regions; + +static gint addr_order(gconstpointer a, gconstpointer b) +{ + RegionInfo *na =3D (RegionInfo *) a; + RegionInfo *nb =3D (RegionInfo *) b; + + return na->region_address > nb->region_address ? 1 : -1; +} + + static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) out =3D g_string_new(""); @@ -46,9 +80,129 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) qemu_plugin_u64_sum(io_count)); } qemu_plugin_outs(out->str); + + + if (do_region_summary) { + GList *counts =3D g_hash_table_get_values(regions); + + counts =3D g_list_sort(counts, addr_order); + + g_string_printf(out, "Region Base, Reads, Writes, Seen all\n"); + + if (counts && g_list_next(counts)) { + for (/* counts */; counts->next; counts =3D counts->next) { + RegionInfo *ri =3D (RegionInfo *) counts->data; + + g_string_append_printf(out, + "0x%016"PRIx64", " + "%"PRId64", %"PRId64", %s\n", + ri->region_address, + ri->reads, + ri->writes, + ri->seen_all ? "true" : "false"); + } + } + qemu_plugin_outs(out->str); + } + qemu_plugin_scoreboard_free(counts); } =20 +/* + * Update the region tracking info for the access. We split up accesses + * that span regions even though the plugin infrastructure will deliver + * it as a single access. + */ +static void update_region_info(uint64_t region, uint64_t offset, + qemu_plugin_meminfo_t meminfo, + qemu_plugin_mem_value value, + unsigned size) +{ + bool be =3D qemu_plugin_mem_is_big_endian(meminfo); + bool is_store =3D qemu_plugin_mem_is_store(meminfo); + RegionInfo *ri; + bool unseen_data =3D false; + + g_assert(offset + size <=3D region_size); + + g_mutex_lock(&lock); + ri =3D (RegionInfo *) g_hash_table_lookup(regions, GUINT_TO_POINTER(re= gion)); + + if (!ri) { + ri =3D g_new0(RegionInfo, 1); + ri->region_address =3D region; + ri->data =3D g_malloc0(region_size); + ri->seen_all =3D true; + g_hash_table_insert(regions, GUINT_TO_POINTER(region), (gpointer) = ri); + } + + if (is_store) { + ri->writes++; + } else { + ri->reads++; + } + + switch (value.type) { + case QEMU_PLUGIN_MEM_VALUE_U8: + if (is_store) { + ri->data[offset] =3D value.data.u8; + } else if (ri->data[offset] !=3D value.data.u8) { + unseen_data =3D true; + } + break; + case QEMU_PLUGIN_MEM_VALUE_U16: + { + uint16_t *p =3D (uint16_t *) &ri->data[offset]; + if (is_store) { + *p =3D be ? be16toh(value.data.u16) : value.data.u16; + } else if (*p !=3D value.data.u16) { + unseen_data =3D true; + } + break; + } + case QEMU_PLUGIN_MEM_VALUE_U32: + { + uint32_t *p =3D (uint32_t *) &ri->data[offset]; + if (is_store) { + *p =3D be ? be32toh(value.data.u32) : value.data.u32; + } else if (*p !=3D value.data.u32) { + unseen_data =3D true; + } + break; + } + case QEMU_PLUGIN_MEM_VALUE_U64: + { + uint64_t *p =3D (uint64_t *) &ri->data[offset]; + if (is_store) { + *p =3D be ? be64toh(value.data.u64) : value.data.u64; + } else if (*p !=3D value.data.u64) { + unseen_data =3D true; + } + break; + } + case QEMU_PLUGIN_MEM_VALUE_U128: + /* skip */ + break; + default: + g_assert_not_reached(); + } + + /* + * This is expected for regions initialised by QEMU (.text etc) but we + * expect to see all data read and written to the test_data region + * of the memory test. + */ + if (unseen_data && ri->seen_all) { + g_autoptr(GString) error =3D g_string_new("Warning: "); + g_string_append_printf(error, "0x%016"PRIx64":%"PRId64" read an un= -instrumented value\n", + region, offset); + qemu_plugin_outs(error->str); + ri->seen_all =3D false; + } + + g_mutex_unlock(&lock); +} + static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, uint64_t vaddr, void *udata) { @@ -63,6 +217,15 @@ static void vcpu_mem(unsigned int cpu_index, qemu_plugi= n_meminfo_t meminfo, } else { qemu_plugin_u64_add(mem_count, cpu_index, 1); } + + if (do_region_summary) { + uint64_t region =3D vaddr & ~region_mask; + uint64_t offset =3D vaddr & region_mask; + qemu_plugin_mem_value value =3D qemu_plugin_mem_get_value(meminfo); + unsigned size =3D 1 << qemu_plugin_mem_size_shift(meminfo); + + update_region_info(region, offset, meminfo, value, size); + } } =20 static void print_access(unsigned int cpu_index, qemu_plugin_meminfo_t mem= info, @@ -117,7 +280,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct q= emu_plugin_tb *tb) QEMU_PLUGIN_INLINE_ADD_U64, mem_count, 1); } - if (do_callback) { + if (do_callback || do_region_summary) { qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem, QEMU_PLUGIN_CB_NO_REGS, rw, NULL); @@ -176,6 +339,12 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin= _id_t id, fprintf(stderr, "boolean argument parsing failed: %s\n", o= pt); return -1; } + } else if (g_strcmp0(tokens[0], "region-summary") =3D=3D 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], + &do_region_summary)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", o= pt); + return -1; + } } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; @@ -196,6 +365,11 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin= _id_t id, qemu_plugin_outs(out->str); } =20 + if (do_region_summary) { + region_mask =3D (region_size - 1); + regions =3D g_hash_table_new(NULL, g_direct_equal); + } + counts =3D qemu_plugin_scoreboard_new(sizeof(CPUCount)); mem_count =3D qemu_plugin_scoreboard_u64_in_struct( counts, CPUCount, mem_count); diff --git a/tests/tcg/alpha/Makefile.softmmu-target b/tests/tcg/alpha/Make= file.softmmu-target index a0eca4d6ea..a944102a3c 100644 --- a/tests/tcg/alpha/Makefile.softmmu-target +++ b/tests/tcg/alpha/Makefile.softmmu-target @@ -28,7 +28,7 @@ LDFLAGS+=3D-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) = -lgcc %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) =20 -memory: CFLAGS+=3D-DCHECK_UNALIGNED=3D0 +memory: CFLAGS+=3D-DCHECK_UNALIGNED=3D0 -mbwx =20 # Running QEMU_OPTS+=3D-serial chardev:output -kernel diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg= /multiarch/system/Makefile.softmmu-target index 32dc0f9830..07be001102 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -65,3 +65,9 @@ endif =20 MULTIARCH_RUNS +=3D run-gdbstub-memory run-gdbstub-interrupt \ run-gdbstub-untimely-packet run-gdbstub-registers + +# Test plugin memory access instrumentation +run-plugin-memory-with-libmem.so: \ + PLUGIN_ARGS=3D$(COMMA)region-summary=3Dtrue +run-plugin-memory-with-libmem.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND=3D$(MULTIARCH_SYSTEM_SRC)/validate-memory-cou= nts.py $@.out diff --git a/tests/tcg/multiarch/system/validate-memory-counts.py b/tests/t= cg/multiarch/system/validate-memory-counts.py new file mode 100755 index 0000000000..16c2788384 --- /dev/null +++ b/tests/tcg/multiarch/system/validate-memory-counts.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# +# validate-memory-counts.py: check we instrumented memory properly +# +# This program takes two inputs: +# - the mem plugin output +# - the memory binary output +# +# Copyright (C) 2024 Linaro Ltd +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import sys + +def extract_counts(path): + """ + Load the output from path and extract the lines containing: + + Test data start: 0x40214000 + Test data end: 0x40218001 + Test data read: 2522280 + Test data write: 262111 + + From the stream of data. Extract the values for use in the + validation function. + """ + start_address =3D None + end_address =3D None + read_count =3D 0 + write_count =3D 0 + with open(path, 'r') as f: + for line in f: + if line.startswith("Test data start:"): + start_address =3D int(line.split(':')[1].strip(), 16) + elif line.startswith("Test data end:"): + end_address =3D int(line.split(':')[1].strip(), 16) + elif line.startswith("Test data read:"): + read_count =3D int(line.split(':')[1].strip()) + elif line.startswith("Test data write:"): + write_count =3D int(line.split(':')[1].strip()) + return start_address, end_address, read_count, write_count + + +def parse_plugin_output(path, start, end): + """ + Load the plugin output from path in the form of: + + Region Base, Reads, Writes, Seen all + 0x0000000040004000, 31093, 0, false + 0x0000000040214000, 2522280, 278579, true + 0x0000000040000000, 137398, 0, false + 0x0000000040210000, 54727397, 33721956, false + + And extract the ranges that match test data start and end and + return the results. + """ + total_reads =3D 0 + total_writes =3D 0 + seen_all =3D False + + with open(path, 'r') as f: + next(f) # Skip the header + for line in f: + + if line.startswith("Region Base"): + continue + + parts =3D line.strip().split(', ') + if len(parts) !=3D 4: + continue + + region_base =3D int(parts[0], 16) + reads =3D int(parts[1]) + writes =3D int(parts[2]) + + if start <=3D region_base < end: # Checking if within range + total_reads +=3D reads + total_writes +=3D writes + seen_all =3D parts[3] =3D=3D "true" + + return total_reads, total_writes, seen_all + +def main(): + if len(sys.argv) !=3D 3: + print("Usage: .py ") + sys.exit(1) + + memory_binary_output_path =3D sys.argv[1] + mem_plugin_output_path =3D sys.argv[2] + + # Extract counts from memory binary + start, end, expected_reads, expected_writes =3D extract_counts(memory_= binary_output_path) + + if start is None or end is None: + print("Failed to extract start or end address from memory binary o= utput.") + sys.exit(1) + + # Parse plugin output + actual_reads, actual_writes, seen_all =3D parse_plugin_output(mem_plug= in_output_path, start, end) + + if not seen_all: + print("Fail: didn't instrument all accesses to test_data.") + sys.exit(1) + + # Compare and report + if actual_reads =3D=3D expected_reads and actual_writes =3D=3D expecte= d_writes: + sys.exit(0) + else: + print("Fail: The memory reads and writes count does not match.") + print(f"Expected Reads: {expected_reads}, Actual Reads: {actual_re= ads}") + print(f"Expected Writes: {expected_writes}, Actual Writes: {actual= _writes}") + sys.exit(1) + +if __name__ =3D=3D "__main__": + main() --=20 2.39.2