From patchwork Mon Jul 22 11:04:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Alex_Benn=C3=A9e?= X-Patchwork-Id: 813706 Delivered-To: patch@linaro.org Received: by 2002:adf:f288:0:b0:367:895a:4699 with SMTP id k8csp1811210wro; Mon, 22 Jul 2024 04:05:37 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCWqLp+Ea56WNSHJsd+SspwchU18y4s7a70nvwAsUQMnSvIC+Pk4zG24KhidFBuIVHmZredGe5VB0Q3w5i3M2Qzw X-Google-Smtp-Source: AGHT+IFY5CadAa9YFHT7DQPQEdBu64nK7eV1KcHyWGmBheOcg4X5lxHUumqtIyV3Xv8tmkH3fslc X-Received: by 2002:a05:620a:290d:b0:79e:ff0d:1f49 with SMTP id af79cd13be357-7a1a133e2abmr639704885a.34.1721646336875; Mon, 22 Jul 2024 04:05:36 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1721646336; cv=none; d=google.com; s=arc-20160816; b=FuFOBeoO5ANlwcW+ARZbBcUyBOk6e8rAXT5O3to4ytsMyhoCgZW2vgiO4Gr7hLe1Tx CK2LdRIYqYESRyTKpPOe7X7ekZG0WEvbfUgvyv9Fv37UidMhGHqhtHBozk0oN3KmZbb1 L2iPmQogO75LdgXGJidWTGQoNkuQaLWjO9oh49FMgoKMM68ewFBjjwKcQzuGZXoJeJXp 8pLHfOvkRzTvwhCz+dxJKTbyawQqy2jtP1IuYfIFXWhzdwS6ildRjra3pJIrXARYQUj2 dOayxGHpAwSuQWDGFu9EyjcpxNKKdRe4OR3c5C0m0j9buAPhYGUXa6vkRukWXSSX9Rzv JwYA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=rc/yY6n9+uzIblLVcOxQLSBJO6NUzRvL8eMhoaOnKuM=; fh=1PNDSUIgWMpxaJh2mfwmcRcPtq9I9Vv7ttnzcXFuxFg=; b=Ei9TvcfJs3I1RWWoBKjQhQHkeG57lSa395gCiRESm9LUboiUDrtWBB7BW+keUa3snt sYXLHhV+RUg3E5ZsrWglcae80EN0OamsK/v42Ncu4Jk0o2PlPoiehNJlegHPBilSW1ZT Y6+m+iRI3PBzdA0/k7Biy7opSeW7wOgihCQg3WkGEUVSuJ12zXhzaptlU8RKpwI6WMMS 0iZJCWgbb+CsbYUz5Od7kwfEqKaqcdJnhMxSWu/bu1MSdgnyxa4sZ5H6tOSHmrnmp0fA SqWb93s7jfEHIsdd2/pSxOFryjWEQkTTAj3KE5Lkcm7Vc/b6ZYK0aDSLl3Q1DxU530mF +Mbg==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=F9PJh7qh; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org; dara=neutral header.i=@linaro.org Return-Path: Received: from lists.gnu.org (lists.gnu.org. [209.51.188.17]) by mx.google.com with ESMTPS id af79cd13be357-7a198f98095si759956085a.3.2024.07.22.04.05.36 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Mon, 22 Jul 2024 04:05:36 -0700 (PDT) Received-SPF: pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=F9PJh7qh; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org; dara=neutral header.i=@linaro.org Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sVqq8-0005gz-UV; Mon, 22 Jul 2024 07:04:24 -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 1sVqq6-0005VX-3V for qemu-devel@nongnu.org; Mon, 22 Jul 2024 07:04:22 -0400 Received: from mail-lf1-x12d.google.com ([2a00:1450:4864:20::12d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1sVqq3-0002RO-Qk for qemu-devel@nongnu.org; Mon, 22 Jul 2024 07:04:21 -0400 Received: by mail-lf1-x12d.google.com with SMTP id 2adb3069b0e04-52efd530a4eso2374475e87.0 for ; Mon, 22 Jul 2024 04:04:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1721646258; x=1722251058; 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=rc/yY6n9+uzIblLVcOxQLSBJO6NUzRvL8eMhoaOnKuM=; b=F9PJh7qhTL7nfKidWlu0Y6z/JdxdaIotSS9jP4lKfb7DlwIKFLLZfzqGng0qcxmbLA PVOiv1iRenRx3Ws2uRsZ9txeq1fvB6j/2j2ZGiry2Od1CuwbJR04tTZkI5Zct2GQFXAe 8asmYqg/eLWnpXfmU3o1+Lj/iOeuCjCu1lHyb8N38LBNsHP1G3V7vpeOUxOunGq0bZmk TyNny389eBSCp7MJ14r1uqRQDx/RuDvffRh9qMPeVQboWVkIbYqK8JtkqqkbmY2bnDqn utOzVf36afpIMZgrCppJgAzaNXDmmSqqm0IEhHFZ2tbO+J9Lf+NvLlHx8VEKy6TlSe2v RfbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721646258; x=1722251058; 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=rc/yY6n9+uzIblLVcOxQLSBJO6NUzRvL8eMhoaOnKuM=; b=KD3raENBUcWpuBiG1whJ6P4JekuWmbvh/xfuQ5gzCF9bDbsCmWUPQAEAxXA2MjJESJ /6S4HipewuNL9f+Xft71pyuBEPiRqbM1Gz2ky6PxTKg2B1yxjarPuDPUO0C+/SCN2+TL sr+hWWNWdU0QnaybzdT39Y0hSfGrudXoKnchhSCcy9oa1NTaAr0rkhoY6rrMMG5DkOeu 0gtW1GGmzaYDqP5EiDyphhcPPLR23pKx9lQbsk0DkSYrjsxqe2xW5REKvFSriPL3OSkP oN4+fEF90W6bCKLdYOIo+m9cWjTnJFkJkvpr4FIVtAWbm1qZrQXP606y+F899zDLoeK+ eQeA== X-Gm-Message-State: AOJu0Yy/fTxcscQNiWgRT2ZzuamnSkEee+9nswMdqqyGIFwD2lNzsqTX 8eAQGHwStfrFzly70mHqQC03drRRAnLMIaEea33stE3gcXAb/vIekiOMsMBvFhY= X-Received: by 2002:a05:6512:a8d:b0:52e:9b9e:2c14 with SMTP id 2adb3069b0e04-52ee543f28amr10479329e87.60.1721646257613; Mon, 22 Jul 2024 04:04:17 -0700 (PDT) Received: from draig.lan ([85.9.250.243]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a7a3c786032sm408823766b.34.2024.07.22.04.04.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 22 Jul 2024 04:04:14 -0700 (PDT) Received: from draig.lan (localhost [IPv6:::1]) by draig.lan (Postfix) with ESMTP id F3E7E5F94C; Mon, 22 Jul 2024 12:04:13 +0100 (BST) From: =?utf-8?q?Alex_Benn=C3=A9e?= To: qemu-devel@nongnu.org Cc: Simon Hamelin , Alexandre Iooss , Pierrick Bouvier , =?utf-8?q?Alex_Benn=C3=A9e?= , Mahmoud Mandour , Richard Henderson , Paolo Bonzini Subject: [PULL 04/16] plugins/stoptrigger: TCG plugin to stop execution under conditions Date: Mon, 22 Jul 2024 12:04:01 +0100 Message-Id: <20240722110413.118418-5-alex.bennee@linaro.org> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240722110413.118418-1-alex.bennee@linaro.org> References: <20240722110413.118418-1-alex.bennee@linaro.org> MIME-Version: 1.0 Received-SPF: pass client-ip=2a00:1450:4864:20::12d; envelope-from=alex.bennee@linaro.org; helo=mail-lf1-x12d.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=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+patch=linaro.org@nongnu.org Sender: qemu-devel-bounces+patch=linaro.org@nongnu.org From: Simon Hamelin This new plugin allows to stop emulation using conditions on the emulation state. By setting this plugin arguments, it is possible to set an instruction count limit and/or trigger address(es) to stop at. The code returned at emulation exit can be customized. This plugin demonstrates how someone could stop QEMU execution. It could be used for research purposes to launch some code and deterministically stop it and understand where its execution flow went. Co-authored-by: Alexandre Iooss Signed-off-by: Simon Hamelin Signed-off-by: Alexandre Iooss Reviewed-by: Pierrick Bouvier Message-Id: <20240715081521.19122-2-simon.hamelin@grenoble-inp.org> Signed-off-by: Alex Bennée Message-Id: <20240718094523.1198645-5-alex.bennee@linaro.org> diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index f7d7b9e3a4..954623f9bf 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -642,6 +642,28 @@ The plugin has a number of arguments, all of them are optional: configuration arguments implies ``l2=on``. (default: N = 2097152 (2MB), B = 64, A = 16) +- contrib/plugins/stoptrigger.c + +The stoptrigger plugin allows to setup triggers to stop emulation. +It can be used for research purposes to launch some code and precisely stop it +and understand where its execution flow went. + +Two types of triggers can be configured: a count of instructions to stop at, +or an address to stop at. Multiple triggers can be set at once. + +By default, QEMU will exit with return code 0. A custom return code can be +configured for each trigger using ``:CODE`` syntax. + +For example, to stop at the 20-th instruction with return code 41, at address +0xd4 with return code 0 or at address 0xd8 with return code 42:: + + $ qemu-system-aarch64 $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libstoptrigger.so,icount=20:41,addr=0xd4,addr=0xd8:42 -d plugin + +The plugin will log the reason of exit, for example:: + + 0xd4 reached, exiting + Plugin API ========== diff --git a/contrib/plugins/stoptrigger.c b/contrib/plugins/stoptrigger.c new file mode 100644 index 0000000000..03ee22f4c6 --- /dev/null +++ b/contrib/plugins/stoptrigger.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2024, Simon Hamelin + * + * Stop execution once a given address is reached or if the + * count of executed instructions reached a specified limit + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +#include +#include +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +/* Scoreboard to track executed instructions count */ +typedef struct { + uint64_t insn_count; +} InstructionsCount; +static struct qemu_plugin_scoreboard *insn_count_sb; +static qemu_plugin_u64 insn_count; + +static uint64_t icount; +static int icount_exit_code; + +static bool exit_on_icount; +static bool exit_on_address; + +/* Map trigger addresses to exit code */ +static GHashTable *addrs_ht; + +static void exit_emulation(int return_code, char *message) +{ + qemu_plugin_outs(message); + g_free(message); + exit(return_code); +} + +static void exit_icount_reached(unsigned int cpu_index, void *udata) +{ + uint64_t insn_vaddr = GPOINTER_TO_UINT(udata); + char *msg = g_strdup_printf("icount reached at 0x%" PRIx64 ", exiting\n", + insn_vaddr); + + exit_emulation(icount_exit_code, msg); +} + +static void exit_address_reached(unsigned int cpu_index, void *udata) +{ + uint64_t insn_vaddr = GPOINTER_TO_UINT(udata); + char *msg = g_strdup_printf("0x%" PRIx64 " reached, exiting\n", insn_vaddr); + int exit_code; + + exit_code = GPOINTER_TO_INT( + g_hash_table_lookup(addrs_ht, GUINT_TO_POINTER(insn_vaddr))); + + exit_emulation(exit_code, msg); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + size_t tb_n = qemu_plugin_tb_n_insns(tb); + for (size_t i = 0; i < tb_n; i++) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); + gpointer insn_vaddr = GUINT_TO_POINTER(qemu_plugin_insn_vaddr(insn)); + + if (exit_on_icount) { + /* Increment and check scoreboard for each instruction */ + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); + qemu_plugin_register_vcpu_insn_exec_cond_cb( + insn, exit_icount_reached, QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_COND_EQ, insn_count, icount + 1, insn_vaddr); + } + + if (exit_on_address) { + if (g_hash_table_contains(addrs_ht, insn_vaddr)) { + /* Exit triggered by address */ + qemu_plugin_register_vcpu_insn_exec_cb( + insn, exit_address_reached, QEMU_PLUGIN_CB_NO_REGS, + insn_vaddr); + } + } + } +} + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + g_hash_table_destroy(addrs_ht); + qemu_plugin_scoreboard_free(insn_count_sb); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, + char **argv) +{ + addrs_ht = g_hash_table_new(NULL, g_direct_equal); + + insn_count_sb = qemu_plugin_scoreboard_new(sizeof(InstructionsCount)); + insn_count = qemu_plugin_scoreboard_u64_in_struct( + insn_count_sb, InstructionsCount, insn_count); + + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "icount") == 0) { + g_auto(GStrv) icount_tokens = g_strsplit(tokens[1], ":", 2); + icount = g_ascii_strtoull(icount_tokens[0], NULL, 0); + if (icount < 1 || g_strrstr(icount_tokens[0], "-") != NULL) { + fprintf(stderr, + "icount parsing failed: '%s' must be a positive " + "integer\n", + icount_tokens[0]); + return -1; + } + if (icount_tokens[1]) { + icount_exit_code = g_ascii_strtoull(icount_tokens[1], NULL, 0); + } + exit_on_icount = true; + } else if (g_strcmp0(tokens[0], "addr") == 0) { + g_auto(GStrv) addr_tokens = g_strsplit(tokens[1], ":", 2); + uint64_t exit_addr = g_ascii_strtoull(addr_tokens[0], NULL, 0); + int exit_code = 0; + if (addr_tokens[1]) { + exit_code = g_ascii_strtoull(addr_tokens[1], NULL, 0); + } + g_hash_table_insert(addrs_ht, GUINT_TO_POINTER(exit_addr), + GINT_TO_POINTER(exit_code)); + exit_on_address = true; + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + if (!exit_on_icount && !exit_on_address) { + fprintf(stderr, "'icount' or 'addr' argument missing\n"); + return -1; + } + + /* Register translation block and exit callbacks */ + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + + return 0; +} diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index 449ead1130..98a89d5c40 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -28,6 +28,7 @@ NAMES += hwprofile NAMES += cache NAMES += drcov NAMES += ips +NAMES += stoptrigger ifeq ($(CONFIG_WIN32),y) SO_SUFFIX := .dll