From nobody Sat Nov 15 09:50:14 2025 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=1753390057; cv=none; d=zohomail.com; s=zohoarc; b=lT1aC2yM/EtuNZlef/FuS9FmXexoEIWs8zu2FgbhyBoAsC6cS6kDEOyyIxGOVdYFuyU2v3Spx3O8rUUO+iTdlEblrvFaTpWvUaMqsrxIjb9xaHMw5hhqTA/DjHEMv5PM4E5gx/magwzIVdVeU+/VwHsPqWrN/LhM2FA090t8foU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753390057; h=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=pKUtWEEVUYGKmPHteBMDUYjRr3TkCuYDcFLmPPv/IAQ=; b=b84zYkdMa96ZxNrIvmm85xA+RCIM+Pjqogxnb+FYErrJFi5gof9M4BfBw/XmQZCGio2kLu1HpAeY5zxGXAhEDqtrYGYPQCDPoM8AQLZx2SUhCDwV9r74P5rNZ0o5+U3da4wE+spVbEOPw9OYuTajnyT8vC4H2kQnryca0GazjZM= 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 1753390057694326.7478238336296; Thu, 24 Jul 2025 13:47:37 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uf2pB-0005BD-Oa; Thu, 24 Jul 2025 16:45:57 -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 1uf2p6-00056f-F8 for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:52 -0400 Received: from mail-pg1-x533.google.com ([2607:f8b0:4864:20::533]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1uf2p1-0007MT-RA for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:52 -0400 Received: by mail-pg1-x533.google.com with SMTP id 41be03b00d2f7-b3bcb168fd5so1426018a12.3 for ; Thu, 24 Jul 2025 13:45:37 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31e662f6be0sm2008849a91.35.2025.07.24.13.45.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Jul 2025 13:45:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753389936; x=1753994736; 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=pKUtWEEVUYGKmPHteBMDUYjRr3TkCuYDcFLmPPv/IAQ=; b=ZbqSlLIsPhcwy9YD37eiuWNLEyQv4461+g/hsxGLllaDRXX/5qHB8kNACe8uQ85Nsd TGZUg4HzZLLfzSdFpTASajca5Op32PqXc+xUf9gSHunr0e0ELHu7rhRJt5B2ypyxM85a RBps0WS0xQXlSKFO2Nn/6HMJQ3fstcyrkgpniFTV9FdcCbJrWR+9fr69j5E8mCiAYFf+ tGs2dWoIh0Q7h1ZSqLTOJ2Wvvz0FNzF/ttcwEO11mo8tiFSBXFTFN/HesOvsLakn8ckk 0SO1IW0HI0h60J2OahfAjnn0QzfRhy1KZ1UvABqTjHhJCtKeX8AKqiDiSUYtgMu/xXtA uuoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753389936; x=1753994736; 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=pKUtWEEVUYGKmPHteBMDUYjRr3TkCuYDcFLmPPv/IAQ=; b=HXyyjH5OxbaA9JRz3kQ0bmFr+SBcRFayErWqOc0d3gZdnZzyTTavmlQSKtXAbi5M7p ep7N37BCSiJ610JdebgFMNbKjePScmG0kjljw2+K16ylU2zRcLsbGLVZxmAJX/SE24ju gYLgKicxhDMfZ/EjuPwS2fgjBphUp7HIYFzvbuyGgl/7POIxvk/FbMd/FxGffcF1uZFf XEG0AISBaLx06/ioHXViNmAWkweGVZ8S5pDIWQqbi1QVOnlqsMzG7JGgfqEE+ZKiRuNA f8ZPWtIHXIfb/IHKFBr6PA6ejRxkWp44Jf5imzypf7ZkIDv5yN+CzBdYA1NYR/EgK0GY C7/Q== X-Gm-Message-State: AOJu0Yzrc5y5cOaSkYrui4xs0Ht5Fr9iOyDcoZqhydxiRRVIw9JptU2x qGXI/xvWsDcNeD/1E3pds6unpaGFu/2YmYJapelw6nLFsChYb2meDxJqY27BgqNEeO6nLFFSRJQ tsox2 X-Gm-Gg: ASbGncsBhXbZ/igs9Gz52/+MfnxYCV7hzPPM3zYoMnGqA6F7dVBrKZI7ebrpazCjiOb 4ntViDDvkjoWRVuI3+lUrlDup8RG7msv/Lx4cu0cU49shDQAJKHNiem7z0MDWbKaIgYprpMSyb2 0kunspAvZK+XAxhYuC4NNp+9Tq3/ei1wRLo6ZDB3KbE76xJEcFX0/aw1Ni3snB8nYqyavsfzydg 7Fxx/3R0XXXeAqqpjNGilHpG+K0FvfbnDb0dCJ8XUy9e8ld8fTfWesmFU0AyVmTmd+p8+5sd4SB x5jtasTDyjsNW8Pjpu09EQAnVe6/XgDaqIbcYfir/vp6CTZlLOhUmNUK2hoYAq0xSqpJQJDcduA +36/3Mm/KRWQrK5J+jFZy4xWpTVKgn/rV X-Google-Smtp-Source: AGHT+IH1fQeRNCx/tWbI0ypLKoqOfn6hwlbaW5u6AV/qSXcfyNC2+4XOjKlJRkJN5huwZuoZUby0vA== X-Received: by 2002:a17:90a:ec83:b0:31e:3b27:98e5 with SMTP id 98e67ed59e1d1-31e5082cb45mr11192362a91.25.1753389935871; Thu, 24 Jul 2025 13:45:35 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Peter Maydell , Alexandre Iooss , Gustavo Romero , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Mahmoud Mandour , rowan Hart , Manos Pitsidianakis , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Richard Henderson , Pierrick Bouvier Subject: [PATCH v4 1/7] contrib/plugins/uftrace: new uftrace plugin Date: Thu, 24 Jul 2025 13:45:20 -0700 Message-ID: <20250724204527.3175839-2-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> References: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> MIME-Version: 1.0 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=2607:f8b0:4864:20::533; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pg1-x533.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+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @linaro.org) X-ZM-MESSAGEID: 1753390058840116600 Content-Type: text/plain; charset="utf-8" This plugin generates a binary trace compatible with: https://github.com/namhyung/uftrace It tracks frame pointer during execution, detecting function calls/returns and works in system and user mode. It's implemented for aarch64 only (adding other architecture should be trivial, especially x86_64 that share the same stack layout). Signed-off-by: Pierrick Bouvier --- contrib/plugins/uftrace.c | 630 ++++++++++++++++++++++++++++++++++++ contrib/plugins/meson.build | 3 +- 2 files changed, 632 insertions(+), 1 deletion(-) create mode 100644 contrib/plugins/uftrace.c diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c new file mode 100644 index 00000000000..4ca6d3fd68d --- /dev/null +++ b/contrib/plugins/uftrace.c @@ -0,0 +1,630 @@ +/* + * Copyright (C) 2025, Pierrick Bouvier + * + * Generates a trace compatible with uftrace (similar to uftrace record). + * https://github.com/namhyung/uftrace + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version =3D QEMU_PLUGIN_VERSION; + +typedef struct { + GArray *s; +} callstack; + +typedef struct { + uint64_t pc; + uint64_t frame_pointer; +} callstack_entry; + +typedef struct { + GArray *t; + GString *path; + GString *name; + uint32_t id; +} trace; + +typedef struct Cpu Cpu; + +typedef struct { + void (*init)(Cpu *cpu); + void (*end)(Cpu *cpu); + uint64_t (*get_frame_pointer)(Cpu *cpu); + bool (*does_insn_modify_frame_pointer)(const char *disas); +} CpuOps; + +typedef struct Cpu { + uint64_t insn_count; + trace *trace; + callstack *cs; + GArray *callstacks; /* callstack *callstacks[] */ + GArray *traces; /* trace *traces [] */ + GByteArray *buf; + CpuOps ops; + void *arch; +} Cpu; + +typedef struct { + struct qemu_plugin_register *reg_fp; +} Aarch64Cpu; + +typedef struct { + uint64_t timestamp; + uint64_t data; +} uftrace_entry; + +enum uftrace_record_type { + UFTRACE_ENTRY, + UFTRACE_EXIT, + UFTRACE_LOST, + UFTRACE_EVENT +}; + +static struct qemu_plugin_scoreboard *score; +static CpuOps arch_ops; + +static void uftrace_write_map(bool system_emulation) +{ + const char *path =3D "./uftrace.data/sid-0.map"; + + if (system_emulation && access(path, F_OK) =3D=3D 0) { + /* do not erase existing map in system emulation, as a custom one = might + * already have been generated by uftrace_symbols.py */ + return; + } + + FILE *sid_map =3D fopen(path, "w"); + g_assert(sid_map); + + if (system_emulation) { + fprintf(sid_map, + "# map stack on highest address possible, to prevent uftra= ce\n" + "# from considering any kernel address\n"); + fprintf(sid_map, + "ffffffffffff-ffffffffffff rw-p 00000000 00:00 0 [stack]\n"); + } else { + /* in user mode, copy /proc/self/maps instead */ + FILE *self_map =3D fopen("/proc/self/maps", "r"); + g_assert(self_map); + for (;;) { + int c =3D fgetc(self_map); + if (c =3D=3D EOF) { + break; + } + fputc(c, sid_map); + } + fclose(self_map); + } + fclose(sid_map); +} + +static void uftrace_write_task(const GArray *traces) +{ + FILE *task =3D fopen("./uftrace.data/task.txt", "w"); + g_assert(task); + for (int i =3D 0; i < traces->len; ++i) { + trace *t =3D g_array_index(traces, trace*, i); + fprintf(task, "SESS timestamp=3D0.0 pid=3D%"PRIu32" sid=3D0 exenam= e=3D\"%s\"\n", + t->id, t->name->str); + fprintf(task, "TASK timestamp=3D0.0 tid=3D%"PRIu32" pid=3D%"PRIu32= "\n", + t->id, t->id); + } + fclose(task); +} + +static void uftrace_write_info(const GArray *traces) +{ + g_autoptr(GString) taskinfo_tids =3D g_string_new("taskinfo:tids=3D"); + for (int i =3D 0; i < traces->len; ++i) { + trace *t =3D g_array_index(traces, trace*, i); + const char *delim =3D i > 0 ? "," : ""; + g_string_append_printf(taskinfo_tids, "%s%"PRIu32, delim, t->id); + } + + g_autoptr(GString) taskinfo_nr_tid =3D g_string_new("taskinfo:nr_tid= =3D"); + g_string_append_printf(taskinfo_nr_tid, "%d", traces->len); + + FILE *info =3D fopen("./uftrace.data/info", "w"); + g_assert(info); + /* + * $ uftrace dump --debug + * uftrace file header: magic =3D 4674726163652100 + * uftrace file header: version =3D 4 + * uftrace file header: header size =3D 40 + * uftrace file header: endian =3D 1 (little) + * ftrace file header: class =3D 2 (64 bit) + * uftrace file header: features =3D 0x1263 (PLTHOOK | ... + * uftrace file header: info =3D 0x7bff (EXE_NAME | ... + * <0000000000000000>: 46 74 72 61 63 65 21 00 04 00 00 00 28 00 01 = 02 + * <0000000000000010>: 63 12 00 00 00 00 00 00 ff 7b 00 00 00 00 00 = 00 + * <0000000000000020>: 00 04 00 00 00 00 00 00 + */ + const uint8_t header[] =3D {0x46, 0x74, 0x72, 0x61, 0x63, 0x65, 0x21, = 0x00, + 0x04, 0x00, 0x00, 0x00, 0x28, 0x00, 0x01, 0x= 02, + 0x63, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x= 00, + 0xff, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x= 00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x= 00}; + fwrite(header, sizeof(header), 1, info); + const char *info_data[] =3D { + "exename:from_qemu", + "build_id:0123456789abcdef0123456789abcdef01234567", + "exit_status:0", + "cmdline:uftrace record qemu", + "cpuinfo:lines=3D2", + "cpuinfo:nr_cpus=3D1 / 1 (online/possible)", + "cpuinfo:desc=3DIntel 8086", + "meminfo:1.0 / 1.0 GB (free / total)", + "osinfo:lines=3D3", + "osinfo:kernel=3DLinux 6.12.33", + "osinfo:hostname=3Dpc", + "osinfo:distro=3D\"Debian GNU/Linux 13 (trixie)\"", + "taskinfo:lines=3D2", + taskinfo_nr_tid->str, + taskinfo_tids->str, + "usageinfo:lines=3D6", + "usageinfo:systime=3D0.000000", + "usageinfo:usrtime=3D0.000000", + "usageinfo:ctxsw=3D0 / 0 (voluntary / involuntary)", + "usageinfo:maxrss=3D8016", + "usageinfo:pagefault=3D0 / 0 (major / minor)", + "usageinfo:iops=3D0 / 0 (read / write)", + "loadinfo:0.0 / 0.0 / 0.0", + "record_date:Mon Jan 1 00:00:00 2025", + "elapsed_time:1000000000000.0 sec", + "pattern_type:regex", + "uftrace_version:v0.17 ( x86_64 dwarf python3 luajit tui perf sche= d dynamic kernel )", + "utc_offset:1751552954", + 0}; + const char **info_data_it =3D info_data; + while (*(info_data_it)) { + fprintf(info, "%s\n", *info_data_it); + ++info_data_it; + } + fclose(info); +} + +static callstack *callstack_new(void) +{ + callstack *cs =3D g_malloc0(sizeof(callstack)); + cs->s =3D g_array_new(false, false, sizeof(callstack_entry)); + return cs; +} + +static void callstack_free(callstack *cs) +{ + g_array_free(cs->s, true); + cs->s =3D NULL; + g_free(cs); +} + +static size_t callstack_depth(const callstack *cs) +{ + return cs->s->len; +} + +static size_t callstack_empty(const callstack *cs) +{ + return callstack_depth(cs) =3D=3D 0; +} + +static void callstack_clear(callstack *cs) +{ + g_array_set_size(cs->s, 0); +} + +static const callstack_entry *callstack_at(const callstack *cs, size_t dep= th) +{ + g_assert(depth > 0); + g_assert(depth <=3D callstack_depth(cs)); + return &g_array_index(cs->s, callstack_entry, depth - 1); +} + +static callstack_entry callstack_top(const callstack *cs) +{ + if (callstack_depth(cs) >=3D 1) { + return *callstack_at(cs, callstack_depth(cs)); + } + return (callstack_entry){}; +} + +static callstack_entry callstack_caller(const callstack *cs) +{ + if (callstack_depth(cs) >=3D 2) { + return *callstack_at(cs, callstack_depth(cs) - 1); + } + return (callstack_entry){}; +} + +static void callstack_push(callstack *cs, callstack_entry e) +{ + g_array_append_val(cs->s, e); +} + +static callstack_entry callstack_pop(callstack *cs) +{ + g_assert(!callstack_empty(cs)); + callstack_entry e =3D callstack_top(cs); + g_array_set_size(cs->s, callstack_depth(cs) - 1); + return e; +} + +static trace *trace_new(uint32_t id, GString *name) +{ + trace *t =3D g_malloc0(sizeof(trace)); + t->t =3D g_array_new(false, false, sizeof(uftrace_entry)); + t->path =3D g_string_new(NULL); + g_string_append_printf(t->path, "./uftrace.data/%"PRIu32".dat", id); + t->name =3D g_string_new(name->str); + t->id =3D id; + return t; +} + +static void trace_free(trace *t) +{ + g_assert(t->t->len =3D=3D 0); + g_array_free(t->t, true); + t->t =3D NULL; + g_string_free(t->path, true); + t->path =3D NULL; + g_string_free(t->name, true); + t->name =3D NULL; + g_free(t); +} + +static void trace_flush(trace *t, bool append) +{ + int create_dir =3D g_mkdir_with_parents("./uftrace.data", + S_IRWXU | S_IRWXG | S_IRWXO); + g_assert(create_dir =3D=3D 0); + FILE *dat =3D fopen(t->path->str, append ? "a" : "w"); + g_assert(dat); + GArray *data =3D t->t; + if (data->len) { + fwrite(data->data, data->len, sizeof(uftrace_entry), dat); + } + fclose(dat); + g_array_set_size(data, 0); +} + +static void trace_add_entry(trace *t, uint64_t timestamp, uint64_t pc, + size_t depth, enum uftrace_record_type type) +{ + /* libmcount/record.c:record_event */ + const uint64_t record_magic =3D 0x5; + uint64_t data =3D type | record_magic << 3; + data +=3D depth << 6; + data +=3D pc << 16; + uftrace_entry e =3D {.timestamp =3D timestamp, .data =3D data}; + g_array_append_val(t->t, e); + if (t->t->len * sizeof(uftrace_entry) > 32 * 1024 * 1024) { + /* flush every 32 MB */ + trace_flush(t, true); + } +} + +static void trace_enter_function(trace *t, uint64_t timestamp, + uint64_t pc, size_t depth) +{ + trace_add_entry(t, timestamp, pc, depth, UFTRACE_ENTRY); +} + +static void trace_exit_function(trace *t, uint64_t timestamp, + uint64_t pc, size_t depth) +{ + trace_add_entry(t, timestamp, pc, depth, UFTRACE_EXIT); +} + +static void trace_enter_stack(trace *t, callstack *cs, uint64_t timestamp) +{ + for (size_t depth =3D 1; depth <=3D callstack_depth(cs); ++depth) { + trace_enter_function(t, timestamp, callstack_at(cs, depth)->pc, de= pth); + } +} + +static void trace_exit_stack(trace *t, callstack *cs, uint64_t timestamp) +{ + for (size_t depth =3D callstack_depth(cs); depth > 0; --depth) { + trace_exit_function(t, timestamp, callstack_at(cs, depth)->pc, dep= th); + } +} + +static uint64_t cpu_read_register64(Cpu *cpu, struct qemu_plugin_register = *reg) +{ + GByteArray *buf =3D cpu->buf; + g_byte_array_set_size(buf, 0); + size_t sz =3D qemu_plugin_read_register(reg, buf); + g_assert(sz =3D=3D 8); + g_assert(buf->len =3D=3D 8); + return *((uint64_t *) buf->data); +} + +static uint64_t cpu_read_memory64(Cpu *cpu, uint64_t addr) +{ + g_assert(addr); + GByteArray *buf =3D cpu->buf; + g_byte_array_set_size(buf, 0); + bool read =3D qemu_plugin_read_memory_vaddr(addr, buf, 8); + if (!read) { + return 0; + } + g_assert(buf->len =3D=3D 8); + return *((uint64_t *) buf->data); +} + +static void cpu_unwind_stack(Cpu *cpu, uint64_t frame_pointer, uint64_t pc) +{ + g_assert(callstack_empty(cpu->cs)); + + #define UNWIND_STACK_MAX_DEPTH 1024 + callstack_entry unwind[UNWIND_STACK_MAX_DEPTH]; + size_t depth =3D 0; + do { + /* check we don't have an infinite stack */ + for (size_t i =3D 0; i < depth; ++i) { + if (frame_pointer =3D=3D unwind[i].frame_pointer) { + break; + } + } + callstack_entry e =3D {.frame_pointer =3D frame_pointer, .pc =3D p= c}; + unwind[depth] =3D e; + depth++; + if (frame_pointer) { + frame_pointer =3D cpu_read_memory64(cpu, frame_pointer); + } + pc =3D cpu_read_memory64(cpu, frame_pointer + 8); /* read previous= lr */ + } while (frame_pointer && pc && depth < UNWIND_STACK_MAX_DEPTH); + #undef UNWIND_STACK_MAX_DEPTH + + /* push it from bottom to top */ + while (depth) { + callstack_push(cpu->cs, unwind[depth - 1]); + --depth; + } +} + +static uint64_t cpu_get_timestamp(const Cpu *cpu) +{ + return cpu->insn_count; +} + +static uint64_t aarch64_get_frame_pointer(Cpu *cpu_) +{ + Aarch64Cpu *cpu =3D cpu_->arch; + return cpu_read_register64(cpu_, cpu->reg_fp); +} + +static void aarch64_init(Cpu *cpu_) +{ + Aarch64Cpu *cpu =3D g_malloc0(sizeof(Aarch64Cpu)); + cpu_->arch =3D cpu; + g_autoptr(GArray) regs =3D qemu_plugin_get_registers(); + for (int i =3D 0; i < regs->len; ++i) { + qemu_plugin_reg_descriptor *reg; + reg =3D &g_array_index(regs, qemu_plugin_reg_descriptor, i); + if (!strcmp(reg->name, "x29")) { + cpu->reg_fp =3D reg->handle; + } + } + if (!cpu->reg_fp) { + fprintf(stderr, "uftrace plugin: frame pointer register (x29) is n= ot " + "available. Please use an AArch64 cpu (or -cpu max= ).\n"); + g_abort(); + } +} + +static void aarch64_end(Cpu *cpu) +{ + g_free(cpu->arch); +} + +static bool aarch64_does_insn_modify_frame_pointer(const char *disas) +{ + /* + * Check if current instruction concerns fp register "x29". + * We add a prefix space to make sure we don't match addresses dump + * in disassembly. + */ + return strstr(disas, " x29"); +} + +static CpuOps aarch64_ops =3D { + .init =3D aarch64_init, + .end =3D aarch64_end, + .get_frame_pointer =3D aarch64_get_frame_pointer, + .does_insn_modify_frame_pointer =3D aarch64_does_insn_modify_frame_poi= nter, +}; + +static void track_callstack(unsigned int cpu_index, void *udata) +{ + uint64_t pc =3D (uintptr_t) udata; + Cpu *cpu =3D qemu_plugin_scoreboard_find(score, cpu_index); + uint64_t timestamp =3D cpu_get_timestamp(cpu); + callstack *cs =3D cpu->cs; + trace *t =3D cpu->trace; + + uint64_t fp =3D cpu->ops.get_frame_pointer(cpu); + if (!fp && callstack_empty(cs)) { + /* + * We simply push current pc. Note that we won't detect symbol cha= nge as + * long as a proper call does not happen. + */ + callstack_push(cs, (callstack_entry){.frame_pointer =3D fp, + .pc =3D pc}); + trace_enter_function(t, timestamp, pc, callstack_depth(cs)); + return; + } + + callstack_entry top =3D callstack_top(cs); + if (fp =3D=3D top.frame_pointer) { + /* same function */ + return; + } + + callstack_entry caller =3D callstack_caller(cs); + if (fp =3D=3D caller.frame_pointer) { + /* return */ + callstack_entry e =3D callstack_pop(cs); + trace_exit_function(t, timestamp, e.pc, callstack_depth(cs)); + return; + } + + uint64_t caller_fp =3D fp ? cpu_read_memory64(cpu, fp) : 0; + if (caller_fp =3D=3D top.frame_pointer) { + /* call */ + callstack_push(cs, (callstack_entry){.frame_pointer =3D fp, + .pc =3D pc}); + trace_enter_function(t, timestamp, pc, callstack_depth(cs)); + return; + } + + /* discontinuity, exit current stack and unwind new one */ + trace_exit_stack(t, cs, timestamp); + callstack_clear(cs); + + cpu_unwind_stack(cpu, fp, pc); + trace_enter_stack(t, cs, timestamp); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + size_t n_insns =3D qemu_plugin_tb_n_insns(tb); + + qemu_plugin_u64 insn_count =3D qemu_plugin_scoreboard_u64_in_struct( + score, Cpu, insn_count); + + /* + * We instrument all instructions following one that might have updated + * the frame pointer. We always instrument first instruction in block,= as + * last executed instruction, in previous tb, may have modified it. + */ + bool instrument_insn =3D true; + for (int i =3D 0; i < n_insns; i++) { + struct qemu_plugin_insn *insn =3D qemu_plugin_tb_get_insn(tb, i); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); + + if (instrument_insn) { + uintptr_t pc =3D qemu_plugin_insn_vaddr(insn); + qemu_plugin_register_vcpu_insn_exec_cb(insn, track_callstack, + QEMU_PLUGIN_CB_R_REGS, + (void *) pc); + instrument_insn =3D false; + } + + char *disas =3D qemu_plugin_insn_disas(insn); + if (arch_ops.does_insn_modify_frame_pointer(disas)) { + instrument_insn =3D true; + } + } +} + +static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) +{ + Cpu *cpu =3D qemu_plugin_scoreboard_find(score, vcpu_index); + cpu->ops =3D arch_ops; + + cpu->ops.init(cpu); + cpu->buf =3D g_byte_array_new(); + cpu->callstacks =3D g_array_new(0, 0, sizeof(callstack *)); + cpu->traces =3D g_array_new(0, 0, sizeof(trace *)); + + g_assert(vcpu_index < 1000); + uint32_t trace_id =3D 1000 * 1000 + vcpu_index * 1000; + + g_autoptr(GString) trace_name =3D g_string_new(NULL); + g_string_append_printf(trace_name, "cpu%u", vcpu_index); + trace *t =3D trace_new(trace_id, trace_name); + g_array_append_val(cpu->traces, t); + callstack *cs =3D callstack_new(); + g_array_append_val(cpu->callstacks, cs); + /* create/truncate trace file */ + trace_flush(t, false); + + cpu->cs =3D cs; + cpu->trace =3D t; +} + +static void vcpu_end(unsigned int vcpu_index) +{ + Cpu *cpu =3D qemu_plugin_scoreboard_find(score, vcpu_index); + g_byte_array_free(cpu->buf, true); + + for (size_t i =3D 0; i < cpu->traces->len; ++i) { + trace *t =3D g_array_index(cpu->traces, trace*, i); + trace_free(t); + } + + for (size_t i =3D 0; i < cpu->callstacks->len; ++i) { + callstack *cs =3D g_array_index(cpu->callstacks, callstack*, i); + callstack_free(cs); + } + + g_array_free(cpu->traces, true); + g_array_free(cpu->callstacks, true); + memset(cpu, 0, sizeof(Cpu)); +} + +static void at_exit(qemu_plugin_id_t id, void *data) +{ + bool system_emulation =3D (bool) data; + g_autoptr(GArray) traces =3D g_array_new(0, 0, sizeof(trace *)); + + /* exit last active stacks */ + for (size_t i =3D 0; i < qemu_plugin_num_vcpus(); ++i) { + Cpu *cpu =3D qemu_plugin_scoreboard_find(score, i); + uint64_t timestamp =3D cpu_get_timestamp(cpu); + if (trace_sample) { + cpu_trace_last_sample(cpu, timestamp); + } + trace_exit_stack(cpu->trace, cpu->cs, timestamp); + callstack_clear(cpu->cs); + } + + for (size_t i =3D 0; i < qemu_plugin_num_vcpus(); ++i) { + Cpu *cpu =3D qemu_plugin_scoreboard_find(score, i); + for (size_t j =3D 0; j < cpu->traces->len; ++j) { + trace *t =3D g_array_index(cpu->traces, trace*, j); + trace_flush(t, true); + g_array_append_val(traces, t); + } + } + + uftrace_write_map(system_emulation); + uftrace_write_info(traces); + uftrace_write_task(traces); + + for (size_t i =3D 0; i < qemu_plugin_num_vcpus(); ++i) { + vcpu_end(i); + } + + qemu_plugin_scoreboard_free(score); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, + int argc, char **argv) +{ + if (!strcmp(info->target_name, "aarch64")) { + arch_ops =3D aarch64_ops; + } else { + fprintf(stderr, "plugin uftrace: %s target is not supported\n", + info->target_name); + return 1; + } + + score =3D qemu_plugin_scoreboard_new(sizeof(Cpu)); + qemu_plugin_register_vcpu_init_cb(id, vcpu_init); + qemu_plugin_register_atexit_cb(id, at_exit, (void *) info->system_emul= ation); + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + + return 0; +} diff --git a/contrib/plugins/meson.build b/contrib/plugins/meson.build index 1876bc78438..7eb3629c95d 100644 --- a/contrib/plugins/meson.build +++ b/contrib/plugins/meson.build @@ -1,5 +1,6 @@ contrib_plugins =3D ['bbv', 'cache', 'cflow', 'drcov', 'execlog', 'hotbloc= ks', - 'hotpages', 'howvec', 'hwprofile', 'ips', 'stoptrigger'] + 'hotpages', 'howvec', 'hwprofile', 'ips', 'stoptrigger', + 'uftrace'] if host_os !=3D 'windows' # lockstep uses socket.h contrib_plugins +=3D 'lockstep' --=20 2.47.2 From nobody Sat Nov 15 09:50:14 2025 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=1753390019; cv=none; d=zohomail.com; s=zohoarc; b=TB/ceYRw70UnDYVrXW3MajqLtQD1MhLtH61DBQ2Z6nKE9VSW6LZYXrGdQX+H7erUtDBQUfRxuBc5FcqSRu6dr0wcDDyIWDlkRvNzXL0cp9B9+i1VNMmBjfjq6RaWoTFg8DTk1QIa1FEBK0B2dnKH0YIQcXTqYRYiycVpx/4gyr4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753390019; h=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=aM+LEv7qMdQSxk6AvcKBeuN8XSEE2/g7YYBoLvBkt14=; b=H8K9c+QR7XZc9Y6tZolDkIDREmEUX5qQ1YsbZ1fvAiYQGlLMHOXAdXPXLA3s+og30rl7CL/OsYTHoenC2pM8x3ayO+kif5tQrSgMuL1wjrPIDUBwcKZdtp7GCQdhxL0rVhEaYeSKaaZGsZhgaW0cuLK1xsNaSuHk0QFMiRVKh34= 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 1753390019450880.4704064857224; Thu, 24 Jul 2025 13:46:59 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uf2p9-00059s-PA; Thu, 24 Jul 2025 16:45:55 -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 1uf2p5-00055j-M5 for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:51 -0400 Received: from mail-pg1-x534.google.com ([2607:f8b0:4864:20::534]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1uf2p2-0007MY-1Y for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:51 -0400 Received: by mail-pg1-x534.google.com with SMTP id 41be03b00d2f7-b3bcb168fd5so1426021a12.3 for ; Thu, 24 Jul 2025 13:45:37 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31e662f6be0sm2008849a91.35.2025.07.24.13.45.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Jul 2025 13:45:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753389937; x=1753994737; 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=aM+LEv7qMdQSxk6AvcKBeuN8XSEE2/g7YYBoLvBkt14=; b=MlU2HOTSxYQLjY+b/JfeBqMAWS7fupKfU0PkwVAkdbAZQcl4pwVt+OyZJuQKd0/RWK DChoB75+2dIJSfFOtf9bhNVayJ4OU++QS4znJiWyDgfYsWcM0Dybw4OJ21PwvTfjWtIZ 1aGs9xPJknVdRWvhmSV186WUEVngM1LNwtm/VPNb8KxbVOVfqvDsPggrfQD7bVXj0VHn N90pGC1m16HnbGCxqTH5ysgrQqIq72KgugX1Tx2MBNxURHTRkTCPJf3smqssPe4aXHUr 5x5UCN7Q7xKqNopz/79CK6RGu3DHpZZH2dm5/rBJ6+FHPRi0ZFMb7ICHcDjKEmHI+HCY /fNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753389937; x=1753994737; 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=aM+LEv7qMdQSxk6AvcKBeuN8XSEE2/g7YYBoLvBkt14=; b=F9DJM+4IdzMt7jjSNW0bLiC+FxVNtS45pH9tDAMNvs6vQmpuGiiAuqvShWcAnAr62j b9s/uvCiOojRtBQO/mdqql7+mcmXmoYjSgz/jktdKgz4DsfS14IpGA4Z94CPS8xka9rY TBWI2LJwdHH/29N9Jo0dqfTXtd1PS+UV4EVR+dgl4hju+FN/8Ikhk8rZTu5VR3KO7uoB h9fLye786llXXDjhWrfgJ8aauf/M8rlZ69yK668LpKv69lPF4U/c24fK5dylNeSdS7zQ zMERL7olrDhldpY3AB+hbVvEznOAdGHyELayGP5ICAxJgZmB73zg+2oj2FHvFlMKl8xC z8AA== X-Gm-Message-State: AOJu0YwBSxHkjfey+Ts8t0AWriQAK+J5i8jVYwwdQQj6PWl8shAeDrXA f2c4++FJ4M96D/EhhSUIne5gN3FwM6NztK3etduNryGMHDl1l6TvymW1NfO0BfnKAs282slPHTA YeDTY X-Gm-Gg: ASbGnctDbdvrfZwZyVzaZ7x657+b1trgxYBEF/a6p0mtQhmxE2JZztd6IcjkB96Af92 nI1P2aBL84hu8/qjkqy83wOZRI7bKYmSR7PfhpNqJOr2rOOUcig+o9VMenSf8iX7tacGZ3CUuVl fEosogkw6SGGZzpp+WRABZ9HMTkON/2zXhek/DEglX4n9lJ4Qe8pdlN5DO0wA3WuvBBkg/oYP/X 9ZTrEu3CpB9k40ePRWgZCMe7JqBdFjaF211MB8rmnogQs9FEl23mp45lvE95kdMdDtgxkmIRXFu hnAHs8UZtWWVhZDDCa1uSti8dvSNGe6k+deyKXDf0CHkIwzoBHt29k81gw+Yq7l/rWTDxWXcKHB apQyJoouXI6pITZKam9VN+A== X-Google-Smtp-Source: AGHT+IFwlU2lTF2PjWKRraQv0HeO2Ytpz3zDW5Sv6Z4ZWyDPa84YGOmd19ZvcJ28wSgdO9aiz3e3CQ== X-Received: by 2002:a17:903:1b28:b0:235:f70:fd37 with SMTP id d9443c01a7336-23f9814b043mr115832965ad.19.1753389936771; Thu, 24 Jul 2025 13:45:36 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Peter Maydell , Alexandre Iooss , Gustavo Romero , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Mahmoud Mandour , rowan Hart , Manos Pitsidianakis , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Richard Henderson , Pierrick Bouvier Subject: [PATCH v4 2/7] contrib/plugins/uftrace: add trace-sample option Date: Thu, 24 Jul 2025 13:45:21 -0700 Message-ID: <20250724204527.3175839-3-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> References: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> MIME-Version: 1.0 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=2607:f8b0:4864:20::534; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pg1-x534.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+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @linaro.org) X-ZM-MESSAGEID: 1753390020465116600 Content-Type: text/plain; charset="utf-8" usage: trace-sample=3DN Allow to use sampling (every N instructions) for tracking the stack. We implement a fast mode, where instrumentation is only per tb, and simply dump current stack, and unwind new one, instead of tracking every frame pointer change. Signed-off-by: Pierrick Bouvier --- contrib/plugins/uftrace.c | 111 +++++++++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 6 deletions(-) diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index 4ca6d3fd68d..a626030b7da 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -44,6 +44,9 @@ typedef struct { =20 typedef struct Cpu { uint64_t insn_count; + uint64_t sample_insn_count; + uint64_t sample_timestamp; + callstack *sample_cs; trace *trace; callstack *cs; GArray *callstacks; /* callstack *callstacks[] */ @@ -70,6 +73,7 @@ enum uftrace_record_type { }; =20 static struct qemu_plugin_scoreboard *score; +static uint64_t trace_sample; static CpuOps arch_ops; =20 static void uftrace_write_map(bool system_emulation) @@ -199,6 +203,13 @@ static callstack *callstack_new(void) return cs; } =20 +static callstack *callstack_clone(const callstack *cs) +{ + callstack *clone =3D g_malloc0(sizeof(callstack)); + clone->s =3D g_array_copy(cs->s); + return clone; +} + static void callstack_free(callstack *cs) { g_array_free(cs->s, true); @@ -391,6 +402,26 @@ static void cpu_unwind_stack(Cpu *cpu, uint64_t frame_= pointer, uint64_t pc) } } =20 +static void cpu_trace_last_sample(Cpu *cpu, uint64_t timestamp) +{ + if (!cpu->sample_cs) { + return; + } + uint64_t elapsed =3D timestamp - cpu->sample_timestamp; + uint64_t middle_timestamp =3D cpu->sample_timestamp + (elapsed / 2); + trace_exit_stack(cpu->trace, cpu->sample_cs, middle_timestamp); + callstack_free(cpu->sample_cs); + cpu->sample_cs =3D NULL; + trace_enter_stack(cpu->trace, cpu->cs, middle_timestamp); +} + +static void cpu_set_new_sample(Cpu *cpu, uint64_t timestamp) +{ + cpu->sample_insn_count =3D 0; + cpu->sample_cs =3D callstack_clone(cpu->cs); + cpu->sample_timestamp =3D timestamp; +} + static uint64_t cpu_get_timestamp(const Cpu *cpu) { return cpu->insn_count; @@ -451,6 +482,13 @@ static void track_callstack(unsigned int cpu_index, vo= id *udata) callstack *cs =3D cpu->cs; trace *t =3D cpu->trace; =20 + if (trace_sample && cpu->sample_insn_count >=3D trace_sample) { + cpu_trace_last_sample(cpu, timestamp); + cpu_set_new_sample(cpu, timestamp); + } + + bool trace_change =3D !trace_sample; + uint64_t fp =3D cpu->ops.get_frame_pointer(cpu); if (!fp && callstack_empty(cs)) { /* @@ -459,7 +497,9 @@ static void track_callstack(unsigned int cpu_index, voi= d *udata) */ callstack_push(cs, (callstack_entry){.frame_pointer =3D fp, .pc =3D pc}); - trace_enter_function(t, timestamp, pc, callstack_depth(cs)); + if (trace_change) { + trace_enter_function(t, timestamp, pc, callstack_depth(cs)); + } return; } =20 @@ -473,7 +513,9 @@ static void track_callstack(unsigned int cpu_index, voi= d *udata) if (fp =3D=3D caller.frame_pointer) { /* return */ callstack_entry e =3D callstack_pop(cs); - trace_exit_function(t, timestamp, e.pc, callstack_depth(cs)); + if (trace_change) { + trace_exit_function(t, timestamp, e.pc, callstack_depth(cs)); + } return; } =20 @@ -482,27 +524,65 @@ static void track_callstack(unsigned int cpu_index, v= oid *udata) /* call */ callstack_push(cs, (callstack_entry){.frame_pointer =3D fp, .pc =3D pc}); - trace_enter_function(t, timestamp, pc, callstack_depth(cs)); + if (trace_change) { + trace_enter_function(t, timestamp, pc, callstack_depth(cs)); + } return; } =20 /* discontinuity, exit current stack and unwind new one */ - trace_exit_stack(t, cs, timestamp); + if (trace_change) { + trace_exit_stack(t, cs, timestamp); + } callstack_clear(cs); =20 cpu_unwind_stack(cpu, fp, pc); - trace_enter_stack(t, cs, timestamp); + if (trace_change) { + trace_enter_stack(t, cs, timestamp); + } +} + +static void sample_callstack(unsigned int cpu_index, void *udata) +{ + uint64_t pc =3D (uintptr_t) udata; + Cpu *cpu =3D qemu_plugin_scoreboard_find(score, cpu_index); + uint64_t timestamp =3D cpu_get_timestamp(cpu); + + trace_exit_stack(cpu->trace, cpu->cs, timestamp); + callstack_clear(cpu->cs); + + cpu_unwind_stack(cpu, cpu->ops.get_frame_pointer(cpu), pc); + trace_enter_stack(cpu->trace, cpu->cs, timestamp); + + /* reset counter */ + cpu->sample_insn_count =3D 0; } =20 static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { size_t n_insns =3D qemu_plugin_tb_n_insns(tb); + uintptr_t tb_pc =3D qemu_plugin_tb_vaddr(tb); =20 qemu_plugin_u64 insn_count =3D qemu_plugin_scoreboard_u64_in_struct( score, Cpu, insn_count); + qemu_plugin_u64 sample_insn_count =3D qemu_plugin_scoreboard_u64_in_st= ruct( + score, Cpu, sample_insn_count); + + if (trace_sample) { + /* We can do a light instrumentation, per tb only */ + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, n_insns); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, sample_insn_count, n_insns); + qemu_plugin_register_vcpu_tb_exec_cond_cb( + tb, sample_callstack, QEMU_PLUGIN_CB_R_REGS, + QEMU_PLUGIN_COND_GE, sample_insn_count, trace_sample, + (void *) tb_pc); + return; + } =20 /* - * We instrument all instructions following one that might have updated + * We now instrument all instructions following one that might have up= dated * the frame pointer. We always instrument first instruction in block,= as * last executed instruction, in previous tb, may have modified it. */ @@ -511,6 +591,8 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct q= emu_plugin_tb *tb) struct qemu_plugin_insn *insn =3D qemu_plugin_tb_get_insn(tb, i); qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, sample_insn_count, 1); =20 if (instrument_insn) { uintptr_t pc =3D qemu_plugin_insn_vaddr(insn); @@ -536,6 +618,7 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned int= vcpu_index) cpu->buf =3D g_byte_array_new(); cpu->callstacks =3D g_array_new(0, 0, sizeof(callstack *)); cpu->traces =3D g_array_new(0, 0, sizeof(trace *)); + cpu->sample_timestamp =3D cpu_get_timestamp(cpu); =20 g_assert(vcpu_index < 1000); uint32_t trace_id =3D 1000 * 1000 + vcpu_index * 1000; @@ -613,6 +696,22 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin= _id_t id, const qemu_info_t *info, int argc, char **argv) { + for (int i =3D 0; i < argc; i++) { + char *opt =3D argv[i]; + g_auto(GStrv) tokens =3D g_strsplit(opt, "=3D", 2); + if (g_strcmp0(tokens[0], "trace-sample") =3D=3D 0) { + gint64 value =3D g_ascii_strtoll(tokens[1], NULL, 10); + if (value <=3D 0) { + fprintf(stderr, "bad trace-sample value: %s\n", tokens[1]); + return -1; + } + trace_sample =3D value; + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + if (!strcmp(info->target_name, "aarch64")) { arch_ops =3D aarch64_ops; } else { --=20 2.47.2 From nobody Sat Nov 15 09:50:14 2025 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=1753390005; cv=none; d=zohomail.com; s=zohoarc; b=gx8mCGeTkZ5184CorEN7ItuJD/HJIaKDwINJbwSIaAjZOVmhVpfzZ9SHHT27lOSH10Pz2QXU3t043PHl416nMXiTtMgkdNRoK6swde94gwpdegBuoyrpT6eWG6x0I9mFb1ZNmtr8VH0BMYpXkVXk6DgWYNDhaASNoA0VQQji8CI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753390005; h=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=LHM/wlC23GB8B+z+ofUSGekwvcloMeBSPT2vdT511OU=; b=PaHQweQkJqoheO9L1RQrFJ4i6ELVlCHg3M/smS5zGFCV0+9CPzv8QvaEAGfQplbsMCQeqcf+6UQzxwlU26wf1XG4HqTukVZGcnua3k1hDli4vkrZ+Q/NbkH5yh6DuwAgQtTStE92Q7IH28rw6NPDhA1MnMVJLltOxbfMGAvyKBg= 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 17533900051434.284657671419723; Thu, 24 Jul 2025 13:46:45 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uf2p7-00057S-L9; Thu, 24 Jul 2025 16:45:53 -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 1uf2p4-00055B-5l for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:51 -0400 Received: from mail-pg1-x52e.google.com ([2607:f8b0:4864:20::52e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1uf2p1-0007Mh-P1 for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:49 -0400 Received: by mail-pg1-x52e.google.com with SMTP id 41be03b00d2f7-b39011e5f8eso1389094a12.0 for ; Thu, 24 Jul 2025 13:45:39 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31e662f6be0sm2008849a91.35.2025.07.24.13.45.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Jul 2025 13:45:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753389938; x=1753994738; 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=LHM/wlC23GB8B+z+ofUSGekwvcloMeBSPT2vdT511OU=; b=RILjVQYMk2jF5K+yCHYJGEVYZv0MOm+aSEZUoDcy8LzJ+qIB1vd7+YIrJ7xBnDHN5Z gv6i+EC4f1urCOcErZFisHEkSKPaUrTFyD6vJUgvw7HQjPqVQq2hg8yjUSiFHNFuktxT BSvQoAqeLSs/GmcsalonJvgII7qbZEuXJojGQfdGOno0P6woIXhTNRG4IfQ3V2wrcX9s HQQE7ZccV0J945d6rocAezbAYAzAXED/1k1l+xtqR5gOCdGrkDDQjoM/l085Q35GXulJ x9In+D915YN5cqWTdvSdnQMnwYkIdXz3m0OYCd5Cj3PftXhr9qf/SVCxXhwK4DeOMfvC R2rQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753389938; x=1753994738; 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=LHM/wlC23GB8B+z+ofUSGekwvcloMeBSPT2vdT511OU=; b=I3saPdJSXXESmdoAAJKeyS7HKaq5ksgkafrpBYc0yofcDCFV29EvKpNllB1kzAR0nV UE6gTYMYNOvUANeo5U8w2HnzLItaUxjiao6luNUg0XXqJSP04L39Uv8/JweGn1FrtBhh I5cpsSFV7wKS3k0ddSOa/v7wTMP0TF5IMNylCk2So5wMMzPqiLY6I4O0LHj9q3IRww7L RHy4gT8V6RPD1E38Xs2kP0GtcL/rezmdyjxA6VlL6yTrvJ8CDUZNfeZpIRTWgYP5v5E7 cRWZucqWYa0YViBkn/HIR57hRRsA9NizrsBEt+zA3OIoVxA1htmZGlPLG34l2ksRKvyr ETIw== X-Gm-Message-State: AOJu0YzeQ4Vah8uH4Ozkol2j87ULJkuCCYO/iYqi7Jb+16R9FO2OeAWl zJZ9lKooHMavsya1gR8zu75LNPSKZQ6vCIgTbjx1IR9Dh759pQcqzgDHgbfRkhJ/nHpZ+oESe0a DsH8Z X-Gm-Gg: ASbGnctZtbfQond5CYPQaYX5N9cL2Dkr3qaTHBvhNUGvkBw9bCMXCx+cXIyrKhyO5oD X5kjrL7oe+9igUyv+vIuTHImDWTc0wZhy29hLjaShywlGVisk8cEiTID3niMvQGZ7rvsbYRMWBg dTc3XWelIj72L+IsDgX7SN5QGXSM4ZaYy60CxwnMAaqEl9IciFb6542ZeVYOhc5P+oGCURpVLF8 wnuxuvydDYTkwo+Xs0Hw5FjDrWJbReKVlLU9UvXvpKwGRPs6otFVZb/ZvLbyuYs8+HQM+ehrmM6 a2szk29QR7BZH4TwAHy9zwxHqyA1Pr9naL2qqQusAKVaprCEayE45ibHTeEv0GKHY9AISOOOm4H P5JJF39eYqmHsHsmAhE15FQ== X-Google-Smtp-Source: AGHT+IFTGNYN9W6ytkrwjutxatYSjx/LNt2mL0x8ES5EXH/Dzj2ZCkfgrvlpiEOPLEz15JSIhLNxcQ== X-Received: by 2002:a17:90b:3b4c:b0:313:352f:6620 with SMTP id 98e67ed59e1d1-31e5079f78bmr10091706a91.4.1753389937719; Thu, 24 Jul 2025 13:45:37 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Peter Maydell , Alexandre Iooss , Gustavo Romero , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Mahmoud Mandour , rowan Hart , Manos Pitsidianakis , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Richard Henderson , Pierrick Bouvier Subject: [PATCH v4 3/7] contrib/plugins/uftrace: add trace-privilege-level option Date: Thu, 24 Jul 2025 13:45:22 -0700 Message-ID: <20250724204527.3175839-4-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> References: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> MIME-Version: 1.0 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=2607:f8b0:4864:20::52e; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pg1-x52e.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+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @linaro.org) X-ZM-MESSAGEID: 1753390006570116600 Content-Type: text/plain; charset="utf-8" usage: trace-privilege-level=3D[on|off] This option generates different traces (represented as different processes in uftrace), allowing to follow privilege level changes. For aarch64, we track current EL and Security State. As well, we make sure that sampling works correctly with this option. If user requests sampling and privilege-level tracing, we perform an accurate callstack tracking (slower), and only sample the information we present. This gives accurate backtraces on all privilege switches, while keeping the trace sampled if needed. Signed-off-by: Pierrick Bouvier --- contrib/plugins/uftrace.c | 182 +++++++++++++++++++++++++++++++++++--- 1 file changed, 171 insertions(+), 11 deletions(-) diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index a626030b7da..bd4219db693 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -39,6 +39,9 @@ typedef struct { void (*init)(Cpu *cpu); void (*end)(Cpu *cpu); uint64_t (*get_frame_pointer)(Cpu *cpu); + uint8_t (*get_privilege_level)(Cpu *cpu); + uint8_t (*num_privilege_levels)(void); + const char *(*get_privilege_level_name)(uint8_t pl); bool (*does_insn_modify_frame_pointer)(const char *disas); } CpuOps; =20 @@ -49,6 +52,7 @@ typedef struct Cpu { callstack *sample_cs; trace *trace; callstack *cs; + uint8_t privilege_level; GArray *callstacks; /* callstack *callstacks[] */ GArray *traces; /* trace *traces [] */ GByteArray *buf; @@ -56,8 +60,23 @@ typedef struct Cpu { void *arch; } Cpu; =20 +typedef enum { + AARCH64_EL0_SECURE, + AARCH64_EL0_NONSECURE, + AARCH64_EL0_REALM, + AARCH64_EL1_SECURE, + AARCH64_EL1_NONSECURE, + AARCH64_EL1_REALM, + AARCH64_EL2_SECURE, + AARCH64_EL2_NONSECURE, + AARCH64_EL2_REALM, + AARCH64_EL3, +} Aarch64PrivilegeLevel; + typedef struct { struct qemu_plugin_register *reg_fp; + struct qemu_plugin_register *reg_cpsr; + struct qemu_plugin_register *reg_scr_el3; } Aarch64Cpu; =20 typedef struct { @@ -74,6 +93,7 @@ enum uftrace_record_type { =20 static struct qemu_plugin_scoreboard *score; static uint64_t trace_sample; +static bool trace_privilege_level; static CpuOps arch_ops; =20 static void uftrace_write_map(bool system_emulation) @@ -358,6 +378,16 @@ static uint64_t cpu_read_register64(Cpu *cpu, struct q= emu_plugin_register *reg) return *((uint64_t *) buf->data); } =20 +static uint32_t cpu_read_register32(Cpu *cpu, struct qemu_plugin_register = *reg) +{ + GByteArray *buf =3D cpu->buf; + g_byte_array_set_size(buf, 0); + size_t sz =3D qemu_plugin_read_register(reg, buf); + g_assert(sz =3D=3D 4); + g_assert(buf->len =3D=3D 4); + return *((uint32_t *) buf->data); +} + static uint64_t cpu_read_memory64(Cpu *cpu, uint64_t addr) { g_assert(addr); @@ -427,6 +457,68 @@ static uint64_t cpu_get_timestamp(const Cpu *cpu) return cpu->insn_count; } =20 +static uint8_t aarch64_num_privilege_levels(void) +{ + return AARCH64_EL3 + 1; +} + +static const char *aarch64_get_privilege_level_name(uint8_t pl) +{ + switch (pl) { + case AARCH64_EL0_SECURE: return "S-EL0"; + case AARCH64_EL0_NONSECURE: return "NS-EL0"; + case AARCH64_EL0_REALM: return "R-EL0"; + case AARCH64_EL1_SECURE: return "S-EL1"; + case AARCH64_EL1_NONSECURE: return "NS-EL1"; + case AARCH64_EL1_REALM: return "R-EL1"; + case AARCH64_EL2_SECURE: return "S-EL2"; + case AARCH64_EL2_NONSECURE: return "NS-EL2"; + case AARCH64_EL2_REALM: return "R-EL2"; + case AARCH64_EL3: return "EL3"; + default: + g_assert_not_reached(); + } +} + +static uint8_t aarch64_get_privilege_level(Cpu *cpu_) +{ + Aarch64Cpu *cpu =3D cpu_->arch; + /* + * QEMU gdbstub does not provide access to CurrentEL, + * so we use CPSR instead. + */ + uint8_t el =3D cpu_read_register32(cpu_, cpu->reg_cpsr) >> 2 & 0b11; + + if (el =3D=3D 3) { + return AARCH64_EL3; + } + + uint8_t ss =3D AARCH64_EL0_SECURE; + if (!cpu->reg_scr_el3) { + ss =3D AARCH64_EL0_NONSECURE; + } + uint64_t scr_el3 =3D cpu_read_register64(cpu_, cpu->reg_scr_el3); + uint64_t ns =3D (scr_el3 >> 0) & 0b1; + uint64_t nse =3D (scr_el3 >> 62) & 0b1; + switch (nse << 1 | ns) { + case 0b00: + ss =3D AARCH64_EL0_SECURE; + break; + case 0b01: + ss =3D AARCH64_EL0_NONSECURE; + break; + case 0b11: + ss =3D AARCH64_EL0_REALM; + break; + default: + g_assert_not_reached(); + } + + const uint8_t num_ss =3D 3; + Aarch64PrivilegeLevel pl =3D el * num_ss + ss; + return pl; +} + static uint64_t aarch64_get_frame_pointer(Cpu *cpu_) { Aarch64Cpu *cpu =3D cpu_->arch; @@ -443,6 +535,10 @@ static void aarch64_init(Cpu *cpu_) reg =3D &g_array_index(regs, qemu_plugin_reg_descriptor, i); if (!strcmp(reg->name, "x29")) { cpu->reg_fp =3D reg->handle; + } else if (!strcmp(reg->name, "cpsr")) { + cpu->reg_cpsr =3D reg->handle; + } else if (!strcmp(reg->name, "SCR_EL3")) { + cpu->reg_scr_el3 =3D reg->handle; } } if (!cpu->reg_fp) { @@ -450,6 +546,8 @@ static void aarch64_init(Cpu *cpu_) "available. Please use an AArch64 cpu (or -cpu max= ).\n"); g_abort(); } + g_assert(cpu->reg_cpsr); + /* scr_el3 is optional */ } =20 static void aarch64_end(Cpu *cpu) @@ -471,9 +569,43 @@ static CpuOps aarch64_ops =3D { .init =3D aarch64_init, .end =3D aarch64_end, .get_frame_pointer =3D aarch64_get_frame_pointer, + .get_privilege_level =3D aarch64_get_privilege_level, + .num_privilege_levels =3D aarch64_num_privilege_levels, + .get_privilege_level_name =3D aarch64_get_privilege_level_name, .does_insn_modify_frame_pointer =3D aarch64_does_insn_modify_frame_poi= nter, }; =20 +static void track_privilege_change(unsigned int cpu_index, void *udata) +{ + Cpu *cpu =3D qemu_plugin_scoreboard_find(score, cpu_index); + uint8_t new_pl =3D cpu->ops.get_privilege_level(cpu); + + if (new_pl =3D=3D cpu->privilege_level) { + return; + } + + uint64_t pc =3D (uintptr_t) udata; + uint64_t timestamp =3D cpu_get_timestamp(cpu); + + if (trace_sample) { + cpu_trace_last_sample(cpu, timestamp); + } + + trace_exit_stack(cpu->trace, cpu->cs, timestamp); + callstack_clear(cpu->cs); + + cpu->privilege_level =3D new_pl; + cpu->cs =3D g_array_index(cpu->callstacks, callstack*, new_pl); + cpu->trace =3D g_array_index(cpu->traces, trace*, new_pl); + + cpu_unwind_stack(cpu, cpu->ops.get_frame_pointer(cpu), pc); + trace_enter_stack(cpu->trace, cpu->cs, timestamp); + + if (trace_sample) { + cpu_set_new_sample(cpu, timestamp); + } +} + static void track_callstack(unsigned int cpu_index, void *udata) { uint64_t pc =3D (uintptr_t) udata; @@ -568,7 +700,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct q= emu_plugin_tb *tb) qemu_plugin_u64 sample_insn_count =3D qemu_plugin_scoreboard_u64_in_st= ruct( score, Cpu, sample_insn_count); =20 - if (trace_sample) { + if (trace_sample && !trace_privilege_level) { /* We can do a light instrumentation, per tb only */ qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( tb, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, n_insns); @@ -581,6 +713,12 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct = qemu_plugin_tb *tb) return; } =20 + if (trace_privilege_level) { + qemu_plugin_register_vcpu_tb_exec_cb(tb, track_privilege_change, + QEMU_PLUGIN_CB_R_REGS, + (void *) tb_pc); + } + /* * We now instrument all instructions following one that might have up= dated * the frame pointer. We always instrument first instruction in block,= as @@ -623,17 +761,33 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned i= nt vcpu_index) g_assert(vcpu_index < 1000); uint32_t trace_id =3D 1000 * 1000 + vcpu_index * 1000; =20 - g_autoptr(GString) trace_name =3D g_string_new(NULL); - g_string_append_printf(trace_name, "cpu%u", vcpu_index); - trace *t =3D trace_new(trace_id, trace_name); - g_array_append_val(cpu->traces, t); - callstack *cs =3D callstack_new(); - g_array_append_val(cpu->callstacks, cs); - /* create/truncate trace file */ - trace_flush(t, false); + if (trace_privilege_level) { + for (uint8_t pl =3D 0; pl < cpu->ops.num_privilege_levels(); ++pl)= { + g_autoptr(GString) trace_name =3D g_string_new(NULL); + g_string_append_printf(trace_name, "cpu%u %s", vcpu_index, + cpu->ops.get_privilege_level_name(pl)); + trace *t =3D trace_new(trace_id + pl, trace_name); + g_array_append_val(cpu->traces, t); + callstack *cs =3D callstack_new(); + g_array_append_val(cpu->callstacks, cs); + } + } else { + g_autoptr(GString) trace_name =3D g_string_new(NULL); + g_string_append_printf(trace_name, "cpu%u", vcpu_index); + trace *t =3D trace_new(trace_id, trace_name); + g_array_append_val(cpu->traces, t); + callstack *cs =3D callstack_new(); + g_array_append_val(cpu->callstacks, cs); + } =20 - cpu->cs =3D cs; - cpu->trace =3D t; + for (size_t i =3D 0; i < cpu->traces->len; ++i) { + /* create/truncate trace files */ + trace *t =3D g_array_index(cpu->traces, trace*, i); + trace_flush(t, false); + } + + cpu->cs =3D g_array_index(cpu->callstacks, callstack*, cpu->privilege_= level); + cpu->trace =3D g_array_index(cpu->traces, trace*, cpu->privilege_level= ); } =20 static void vcpu_end(unsigned int vcpu_index) @@ -706,6 +860,12 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin= _id_t id, return -1; } trace_sample =3D value; + } else if (g_strcmp0(tokens[0], "trace-privilege-level") =3D=3D 0)= { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], + &trace_privilege_level)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", o= pt); + return -1; + } } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; --=20 2.47.2 From nobody Sat Nov 15 09:50:14 2025 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=1753390119; cv=none; d=zohomail.com; s=zohoarc; b=cDQTEaVM5jqKEuDfW+y1/5+utC6M65G5zPf5FBd04HUZEtMzi8vdIb+d5yIXHxjFuket7sfYooa7biNAVid1VD/HjzzhCWfEXoMuGPvz6iZmfplGCvly5bsh0Syscc8IikC7kBKWzJ7QaTXenri1ekK/8d0RZhJdH6Pqyrd/wek= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753390119; h=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=bTTq9wgcgDFhDutfJJC82sCVw2rH7HCJB+ChDzBs1bA=; b=AuL0077XBRmPV8hN6lgnoBdOJW/EVFcKPRHjDbXcEgT/IKQWHTdgvA1naqjDQVF9ue8LClYJqpuebJZ50OdtVA+I3E04bHjKSWmiJZa0JK3iC1nPswXdtDMjPMCF7ey+vzqb6Rt5VlKcgiOHW1HLACCIrLkPCqnABWJuxC9j58g= 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 1753390119061973.3126140231992; Thu, 24 Jul 2025 13:48:39 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uf2p8-00058e-Rt; Thu, 24 Jul 2025 16:45:54 -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 1uf2p5-00055e-IA for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:51 -0400 Received: from mail-pg1-x52c.google.com ([2607:f8b0:4864:20::52c]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1uf2p2-0007Mk-3L for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:51 -0400 Received: by mail-pg1-x52c.google.com with SMTP id 41be03b00d2f7-af51596da56so1275694a12.0 for ; Thu, 24 Jul 2025 13:45:39 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31e662f6be0sm2008849a91.35.2025.07.24.13.45.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Jul 2025 13:45:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753389939; x=1753994739; 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=bTTq9wgcgDFhDutfJJC82sCVw2rH7HCJB+ChDzBs1bA=; b=Pzk9xKTSClPB5ZsDemF5998jIZVPDoZfQxE1n4dRLpjv1Xe1xw+KlZXiacwpKm5vnK 3enwe3omtpS6E5O8uU1BHLV4YQ23rENVat1RaOeriu2nM4Lqrsa7Nqy4TnRRwLB84ojJ oMkHk8GBC3Kz9Y8r6irn08KW3gy2SyTnWpeg1VDqxlv3/W2ticajsLdQwUjEe7vllTju 1MFp1jSfrN5NrOxMc2iKxOBbMK1JA7s39DGiRA+lJbuNidAwSy4/3/JeOzjxZKRWTZ73 mLLGK+xCUnTCtOy37ZlFjaoiiml3d4Taxm6oMTi77qFjo5El6xre9IH56d8N9Qyocc4Q TxiA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753389939; x=1753994739; 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=bTTq9wgcgDFhDutfJJC82sCVw2rH7HCJB+ChDzBs1bA=; b=s9hFsc1voxT/NzBa2MjiJFE9Du1nB7uOdC0Ty+Y1mJHiLP6hLFe8S9mdgbyB0zA4xD a3GwcEPP56bBtnXoNPTCPCdugniz5F0IcGkgugGgLIeXrGrHvtX1lhX260Thf4NSsuCk Hsj9HGt2M32JjtFwF8tpc/RG7ijHvCsx3dEHEir8A0TVS+dP4C8160QF0fwa9k4gDw8F Ol5ExKgxzAdsvrNTJgI1jx8naENiRJzt4jairIKZcOApcLoBhzEaqSUSNObdtmWHwebT 2Q/douUV1DdRDB7FmHiPH62JRyC14uGeiyBnz8TYYjjdbsBcjAvlXAXdVLBECGexLBe7 DiPQ== X-Gm-Message-State: AOJu0Yx/Gy+DV0CruOwySIu+7QkyRzc7X80azkbRB8cNW+PwwbDbEijV 2dUJJE107GSv1DvExd9oTwE+hNbzYCt1gT8Z1fOKrUhY7CBHFXzYMVPa+g0Mbeasv4LxW/weJPG L7tEA X-Gm-Gg: ASbGnct5JuKegKx1VWEn75dNVjwoSSvWJUkhk+oJsL35JHpibdqb2BVmuwfesSGGlab nJF8j5M24UNka1NVtt6TxFAz8Dat0PEFaT9miPY6eCXJ9X2+/SII7HNsHHxI0wDVP5UsmUp9ju/ 6aR2t68Qw4GZ6yljm731OEIiJWqTBuRL6UvuYXVNL9ilZMkEyhIvzWXlu5+tSqQhuk95GGCL6R8 5Sg61R/aaAkhov4NxN/5sTc612Cm9SwYpDHxi1JQz6CZjCUgpFupPldIsZY1VyUAcXQv7JwEADL CAY2fWavtSnNr7YeTCmzfTrqjdJpUfoZmMHvPOvHOTwA4zTsmcM/RDJah3klDWcDGYLjEUdCe41 +15ile2anLWOW6crRPoQnLw== X-Google-Smtp-Source: AGHT+IFrvehQdGDp6Ce10p9Emd+/01d8hzFSDocTN91FBpFX2LIxQ5phLrKiYOHtXoELqCh9syO6UA== X-Received: by 2002:a17:90b:35d1:b0:312:26d9:d5b4 with SMTP id 98e67ed59e1d1-31e50818b0emr13395434a91.17.1753389938624; Thu, 24 Jul 2025 13:45:38 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Peter Maydell , Alexandre Iooss , Gustavo Romero , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Mahmoud Mandour , rowan Hart , Manos Pitsidianakis , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Richard Henderson , Pierrick Bouvier Subject: [PATCH v4 4/7] contrib/plugins/uftrace: add timestamp-based-on-real-time option Date: Thu, 24 Jul 2025 13:45:23 -0700 Message-ID: <20250724204527.3175839-5-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> References: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> MIME-Version: 1.0 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=2607:f8b0:4864:20::52c; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pg1-x52c.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+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @linaro.org) X-ZM-MESSAGEID: 1753390121202116600 Content-Type: text/plain; charset="utf-8" usage: timestamp-based-on-real-time=3D[on|off] Instead of using number of instructions executed (which is per vcpu), we use the wall time for timestamps. This is useful when tracing user mode programs as well. Signed-off-by: Pierrick Bouvier --- contrib/plugins/uftrace.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index bd4219db693..9211f273700 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -94,8 +94,28 @@ enum uftrace_record_type { static struct qemu_plugin_scoreboard *score; static uint64_t trace_sample; static bool trace_privilege_level; +static bool timestamp_based_on_real_time; static CpuOps arch_ops; =20 +static uint64_t gettime_ns(void) +{ +#ifdef _WIN32 + /* + * On Windows, timespec_get is available only with UCRT, but not with + * MinGW64 environment. Simplify by using only gettimeofday on this + * platform. + */ + struct timeval tv; + gettimeofday(&tv, NULL); + uint64_t now_ns =3D tv.tv_sec * 1000 * 1000 * 1000 + tv.tv_usec * 1000; +#else + struct timespec ts; + timespec_get(&ts, TIME_UTC); + uint64_t now_ns =3D ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; +#endif + return now_ns; +} + static void uftrace_write_map(bool system_emulation) { const char *path =3D "./uftrace.data/sid-0.map"; @@ -454,6 +474,9 @@ static void cpu_set_new_sample(Cpu *cpu, uint64_t times= tamp) =20 static uint64_t cpu_get_timestamp(const Cpu *cpu) { + if (timestamp_based_on_real_time) { + return gettime_ns(); + } return cpu->insn_count; } =20 @@ -866,6 +889,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], "timestamp-based-on-real-time") = =3D=3D 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], + ×tamp_based_on_real_time)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", o= pt); + return -1; + } } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; --=20 2.47.2 From nobody Sat Nov 15 09:50:14 2025 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=1753390009; cv=none; d=zohomail.com; s=zohoarc; b=f+aQLV4sWRXy8/B+Ibq/t/wnzUkl7x9vLmcCBWWZjEGuwsyuNwJpUPPkt/CoBSOwWFHWVjvMzekoONeStTyVT97toxU+JN5TEd3KQwLrZnyQOsaWDXgEnxfNpgb4PXyatmEthGEJGgs51mOhO9R2LOZ78mWwfsEhdbF8Hhiqb1k= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753390009; h=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=6LQZIHbBuXid8iCd4uQRcE8cD4Q27Dve1/07zd+zCHI=; b=eMc9yHSYz7e2US7HQGYHhVNKV0SXk+kH6R/RDIEtHOAfgsKG4onPj+bNxpg8ogIm7vuiG74qf34/P3qObpJQnUrQTh2H/d3+vBaePgbcIAraQPPRlJkmcyb1sOqwQQT4i7gZWFfdxqJlRIDoDxyz9fBLIFyyX4vSY+WLNtb53nE= 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 175339000988069.86169765625573; Thu, 24 Jul 2025 13:46:49 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uf2pF-0005EB-41; Thu, 24 Jul 2025 16:46: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 1uf2p8-00058x-Rn for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:54 -0400 Received: from mail-pl1-x62b.google.com ([2607:f8b0:4864:20::62b]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1uf2p2-0007Mt-JG for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:54 -0400 Received: by mail-pl1-x62b.google.com with SMTP id d9443c01a7336-236377f00a1so12761795ad.3 for ; Thu, 24 Jul 2025 13:45:40 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31e662f6be0sm2008849a91.35.2025.07.24.13.45.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Jul 2025 13:45:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753389939; x=1753994739; 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=6LQZIHbBuXid8iCd4uQRcE8cD4Q27Dve1/07zd+zCHI=; b=i7OQR9R8q9wp+8i4LxJNvMe9jwjtqjqO/UplM+5/k2jiz0BzzCjsSyIMV9+vORjxh6 OG0OVwYhvCsssjx3qXUV/QBcbHs0R6tA4wEGwhjEYd0VaH9gIkz5852f6tpsn40ywpU8 rG5TNVbf34sQGlvZ87rgku1J2Gme+EJQzO7pw40uxq4MOL9CpF2U+r8erHERgjSdf9df 0xgd7ft/YHPoWtJR9CguPbWQMMK1HyNdA11YZ2+TvREUw6K1Zhn5s9YXIJn33TQfp5Gl Rx6TXJcL803s8kbQKKJUxVJpD4iVr4yo62W2ymjNkfusrswNFLPjH9QebNxAwQjRHUUn J2sw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753389939; x=1753994739; 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=6LQZIHbBuXid8iCd4uQRcE8cD4Q27Dve1/07zd+zCHI=; b=pDSED3/26URoAKCZk71gDCDPvWZZZq2lM5GudJAyXhP4eWz/UQoyyAamrP7egif8ib uHY0RX2JXX7suy73HAy0znn1gtMiBsHPlfJ1nOUTGpazzvyEaV9fcNmKtyTia66TKUfb 9vzs/DEzHBEBx5FhTixFzY8h++uZ7p/a1KH3zydrFAANHmcOSLiT0AQdbW7pbAt7Nbd/ Hk6afpwDyd/CHJOlm2j6XqF9xQJT+v4ruv7Az1GHkDkSabbHDmDa8sipQZ8NsAz0nOHP dx/yPvpToGsMEVHMxV0KG0+9XrxZ62/1/VJDKMI6LGTxGeQeZEts6jGaxX6JsCqr2N+F e6NQ== X-Gm-Message-State: AOJu0YyUUHPWfiZU0bRTPDaEDFNMKHp29qWovLKUhvrmLHrmoz95PjI3 ZohqFP1OQ4yCK68QXjKObZwQC0XCp0dF19y4Byn0OMyhhAslQUzOPg977auseJiCFo5ttWq+60n 9c/e8 X-Gm-Gg: ASbGncu1w85+7agxczWTtmLaR3bY77fZqGYZ5XNU+l6Dmkhqps+1ttDTelm2trmAzMF DQMkvEffxcKpHY5WxNmUwiZ/DfeSJxCCSyejs3ftFZrKK5+KwdnqvdugT0JecEvmSE854CbEReV 1o/XrYgrqv0tVBo0CiBoSaEsf+sjULCZblwMLNwGVTHk3jePdaY0cneXqsbLBmCnNFq1ksWpidL BKIFZitoUJxsZ3ke3n3CovifEcOJVXc5iXFIyvo2d52igV8kqxym/wp0r9YELJcK8+h5419+w0E /EUsiByywht9O62Gk0T4X9iXQqnszFn0RAJeg1mukkK5uZXIG2vBzTqWXBb8Ca6CTpWfEwqCICB s/Wr50VO3WDsua5jiPYRbdA== X-Google-Smtp-Source: AGHT+IF/nbCDDTyPg5kJzn/v76E0tpt3IzpkLeJG+EvFB/xou1txSI2zyiRfK8KJ/Cv9UcRn8NJP8A== X-Received: by 2002:a17:903:1b06:b0:235:779:edea with SMTP id d9443c01a7336-23f981e0223mr132216245ad.38.1753389939563; Thu, 24 Jul 2025 13:45:39 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Peter Maydell , Alexandre Iooss , Gustavo Romero , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Mahmoud Mandour , rowan Hart , Manos Pitsidianakis , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Richard Henderson , Pierrick Bouvier Subject: [PATCH v4 5/7] contrib/plugins/uftrace_symbols.py Date: Thu, 24 Jul 2025 13:45:24 -0700 Message-ID: <20250724204527.3175839-6-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> References: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> MIME-Version: 1.0 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=2607:f8b0:4864:20::62b; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pl1-x62b.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+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @linaro.org) X-ZM-MESSAGEID: 1753390012605116600 Content-Type: text/plain; charset="utf-8" usage: contrib/plugins/uftrace_symbols.py \ --prefix-symbols \ arm-trusted-firmware/build/qemu/debug/bl1/bl1.elf \ arm-trusted-firmware/build/qemu/debug/bl2/bl2.elf \ arm-trusted-firmware/build/qemu/debug/bl31/bl31.elf \ u-boot/u-boot:0x60000000 \ u-boot/u-boot.relocated:0x000000023f6b6000 \ linux/vmlinux Will generate symbols and memory mapping files for uftrace, allowing to have an enhanced trace, instead of raw addresses. It takes a collection of elf files, and automatically find all their symbols, and generate an ordered memory map based on that. This script uses the python (native) pyelftools module. Signed-off-by: Pierrick Bouvier Acked-by: Alex Benn=C3=A9e --- contrib/plugins/uftrace_symbols.py | 152 +++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100755 contrib/plugins/uftrace_symbols.py diff --git a/contrib/plugins/uftrace_symbols.py b/contrib/plugins/uftrace_s= ymbols.py new file mode 100755 index 00000000000..b49e03203c8 --- /dev/null +++ b/contrib/plugins/uftrace_symbols.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Create symbols and mapping files for uftrace. +# +# Copyright 2025 Linaro Ltd +# Author: Pierrick Bouvier +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import elftools # pip install pyelftools +import os + +from elftools.elf.elffile import ELFFile +from elftools.elf.sections import SymbolTableSection + +def elf_func_symbols(elf): + symbol_tables =3D [(idx, s) for idx, s in enumerate(elf.iter_sections(= )) + if isinstance(s, SymbolTableSection)] + symbols =3D [] + for _, section in symbol_tables: + for _, symbol in enumerate(section.iter_symbols()): + if symbol_size(symbol) =3D=3D 0: + continue + type =3D symbol['st_info']['type'] + if type =3D=3D 'STT_FUNC' or type =3D=3D 'STT_NOTYPE': + symbols.append(symbol) + symbols.sort(key =3D lambda x: symbol_addr(x)) + return symbols + +def symbol_size(symbol): + return symbol['st_size'] + +def symbol_addr(symbol): + addr =3D symbol['st_value'] + # clamp addr to 48 bits, like uftrace entries + return addr & 0xffffffffffff + +def symbol_name(symbol): + return symbol.name + +class BinaryFile: + def __init__(self, path, map_offset): + self.fullpath =3D os.path.realpath(path) + self.map_offset =3D map_offset + with open(path, 'rb') as f: + self.elf =3D ELFFile(f) + self.symbols =3D elf_func_symbols(self.elf) + + def path(self): + return self.fullpath + + def addr_start(self): + return self.map_offset + + def addr_end(self): + last_sym =3D self.symbols[-1] + return symbol_addr(last_sym) + symbol_size(last_sym) + self.map_of= fset + + def generate_symbol_file(self, prefix_symbols): + binary_name =3D os.path.basename(self.fullpath) + sym_file_path =3D f'./uftrace.data/{binary_name}.sym' + print(f'{sym_file_path} ({len(self.symbols)} symbols)') + with open(sym_file_path, 'w') as sym_file: + # print hexadecimal addresses on 48 bits + addrx =3D "0>12x" + for s in self.symbols: + addr =3D symbol_addr(s) + addr =3D f'{addr:{addrx}}' + size =3D f'{symbol_size(s):{addrx}}' + name =3D symbol_name(s) + if prefix_symbols: + name =3D f'{binary_name}:{name}' + print(addr, size, 'T', name, file=3Dsym_file) + +def parse_parameter(p): + s =3D p.split(":") + path =3D s[0] + if len(s) =3D=3D 1: + return path, 0 + if len(s) > 2: + raise ValueError('only one offset can be set') + offset =3D s[1] + if not offset.startswith('0x'): + err =3D f'offset "{offset}" is not an hexadecimal constant. ' + err +=3D 'It should starts with "0x".' + raise ValueError(err) + offset =3D int(offset, 16) + return path, offset + +def is_from_user_mode(map_file_path): + if os.path.exists(map_file_path): + with open(map_file_path, 'r') as map_file: + if not map_file.readline().startswith('# map stack on'): + return True + return False + +def generate_map(binaries): + map_file_path =3D './uftrace.data/sid-0.map' + + if is_from_user_mode(map_file_path): + print(f'do not overwrite {map_file_path} generated from qemu-user') + return + + mappings =3D [] + + # print hexadecimal addresses on 48 bits + addrx =3D "0>12x" + + mappings +=3D ['# map stack on highest address possible, to prevent uf= trace'] + mappings +=3D ['# from considering any kernel address'] + mappings +=3D ['ffffffffffff-ffffffffffff rw-p 00000000 00:00 0 [stack= ]'] + + for b in binaries: + m =3D f'{b.addr_start():{addrx}}-{b.addr_end():{addrx}}' + m +=3D f' r--p 00000000 00:00 0 {b.path()}' + mappings.append(m) + + with open(map_file_path, 'w') as map_file: + print('\n'.join(mappings), file=3Dmap_file) + print(f'{map_file_path}') + print('\n'.join(mappings)) + +def main(): + parser =3D argparse.ArgumentParser(description=3D + 'generate symbol files for uftrace') + parser.add_argument('elf_file', nargs=3D'+', + help=3D'path to an ELF file. ' + 'Use /path/to/file:0xdeadbeef to add a mapping off= set.') + parser.add_argument('--prefix-symbols', + help=3D'prepend binary name to symbols', + action=3Dargparse.BooleanOptionalAction) + args =3D parser.parse_args() + + if not os.path.exists('./uftrace.data'): + os.mkdir('./uftrace.data') + + binaries =3D [] + for file in args.elf_file: + path, offset =3D parse_parameter(file) + b =3D BinaryFile(path, offset) + binaries.append(b) + binaries.sort(key =3D lambda b: b.addr_end()); + + for b in binaries: + b.generate_symbol_file(args.prefix_symbols) + + generate_map(binaries) + +if __name__ =3D=3D '__main__': + main() --=20 2.47.2 From nobody Sat Nov 15 09:50:14 2025 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=1753390066; cv=none; d=zohomail.com; s=zohoarc; b=oHTOj8kasPn5EtWWQLOTA1L0wc1V02EiFgVysQQJjoOqFHU23/14ys10qHON2+wRnQ3/C3cTijXNws9KxUQO1BkED7f0EgcJoV7F9ScAwQpIj6ar3QBzUaDrQj1HN8+at4igsgYvyaLAfit+FC18WoUmYVhqSVXQXX9ZD0WlBUs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753390066; h=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=+l8Oaz4ADqnYHLgGIIAMGhUfDq/S7zi6obmicPSjKAo=; b=PCB308tAgQyiwnUO8egTLX/G7Ihn4T1jEG4IIJUM5Xh0sDQq++cWLrI3SGbOUNrUpgxwlowxrHFA/xnZ16a19GVaNxkBHmaVLLno97GQXFvHLfbrgz0SphY94/2NNa4RjcChvpYC0R1UDpm5vWXpvfUAmupTgpZGgmf4VUwoMbs= 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 1753390066101983.6152292200431; Thu, 24 Jul 2025 13:47:46 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uf2pD-0005Cs-EJ; Thu, 24 Jul 2025 16:45:59 -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 1uf2p7-00057E-88 for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:53 -0400 Received: from mail-pj1-x102f.google.com ([2607:f8b0:4864:20::102f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1uf2p2-0007N3-KM for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:52 -0400 Received: by mail-pj1-x102f.google.com with SMTP id 98e67ed59e1d1-311e46d38ddso1282608a91.0 for ; Thu, 24 Jul 2025 13:45:41 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31e662f6be0sm2008849a91.35.2025.07.24.13.45.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Jul 2025 13:45:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753389941; x=1753994741; 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=+l8Oaz4ADqnYHLgGIIAMGhUfDq/S7zi6obmicPSjKAo=; b=YcfgBV1H4t6U0cCCzwqLA+qzQDiQi6PD2yPh/VCK2+l2KDG5Wo7A3nJCI3ybTj3gh5 dU5dAIfC5BPDU3YePUndBncEsjtX4GROjztY41X7vXl9kl7xsIJd1We1GgJ4NgVi432a UAfL2flmCKGNIzJ/yUHBmSXK9v7gL4RD3b5QVpl7adPV6pSXr8bjrEedpZ6+ty58cshz cKRe3rMKj3/XhBYbUQEzzqA/9RSUr91joG6Rv/5w4c6CBavM1Al5U1dscpT682MIErNB 1l3hyb1AgngDY+6taiJcJ9x3wgxl15QjVRQbXhTnzObfNCf3Z2sGHTI7styLs6vudCEE 6ggw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753389941; x=1753994741; 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=+l8Oaz4ADqnYHLgGIIAMGhUfDq/S7zi6obmicPSjKAo=; b=txxITtowpeHSLLyN3AkXWyOa41IP7TwKaIGUCMqjRoO8EIUh8Fbis3M5BYUX/fi35o HzUZvPamMDZdGl0g6rTh0+gRbr3cCWaXmSUQt3qAFr+CHE6XpI37LzNYIfjHICKTdsnc msXdqRE1nlbNu1so6HdR963v2PL30JJhdlD2C6MTgefW7/veq+ejrUn6nsqjwUz1vldx wOo8mG1t53dn7oeZne1qat+473YeuQvLf86rBtfz2LgJ2ND7T8B02M9UahQbdQepipPy esWTvzduEWhWwb2P5wd/kicf6D8l2WqAWGUV7JAwGPI0u3M9aLbFg8EkkZVyTNgALwLW o2bQ== X-Gm-Message-State: AOJu0YxWCPbOgVrGlcoRIY97XPU/xwyHPFOWUsF5l0XFWQi4Cf5jHrAC CTQuL/skSlhHLEb1AhXpcAU8HhOWIH2fVxTlXNIEWXdqDv0Eu7I0ozJELUsamq4FWWVTfuXECEU WYnCx X-Gm-Gg: ASbGncu+hsLG4gyyYpPmj6er0BkHjcMpLPt485wn8MyOzSErSBTVquQUUn+Nqfj6iTw GU+ru7zbfMdSkqjLYDmINFqq6XVFOkkO+4JH4kUXDNMw315/KJFP8q+hbV749iHGKWsnVhfsM19 bkSzroUp8dW/wRxeowPA1wCxG+QJXWW5MQBcL/CX8seeYS5J8vtvDYrnAbClqPnEkPJcBg3Lj8z B8cH2KI/NIqVdYwHmbXUP+tVfGawxz/i5+62NPWm4OabxUQ1E4eGp0ryJE5yJ7KThOyNX70ip2Y uP6at5/B8L5nXFSGbxV8RDF2p8iCxB/8p/K6ylMi0zrtgtrR2lK3YSo6R6uQqt/PfEdN2e4V2oC cH+G6DaCPsV6NbhWsMQgDnb4lRBH89Gwo X-Google-Smtp-Source: AGHT+IFFjHy0WonHo6fbF213MWhnJoKlLeWshrqQEjSYxnp6EyodoFCh25ypMPywGDU7rnypd86J3A== X-Received: by 2002:a17:90b:4b0f:b0:311:d258:3473 with SMTP id 98e67ed59e1d1-31e5076a94emr10724866a91.13.1753389940493; Thu, 24 Jul 2025 13:45:40 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Peter Maydell , Alexandre Iooss , Gustavo Romero , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Mahmoud Mandour , rowan Hart , Manos Pitsidianakis , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Richard Henderson , Pierrick Bouvier Subject: [PATCH v4 6/7] contrib/plugins/uftrace: add documentation Date: Thu, 24 Jul 2025 13:45:25 -0700 Message-ID: <20250724204527.3175839-7-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> References: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> MIME-Version: 1.0 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=2607:f8b0:4864:20::102f; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pj1-x102f.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+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @linaro.org) X-ZM-MESSAGEID: 1753390067057116600 Content-Type: text/plain; charset="utf-8" This documentation summarizes how to use the plugin, and present two examples of the possibilities offered by it. As well, it explains how to rebuild and reproduce easily the system boot example. Signed-off-by: Pierrick Bouvier --- docs/about/emulation.rst | 207 ++++++++++++++++++++++++++++++++++++++ contrib/plugins/uftrace.c | 2 + 2 files changed, 209 insertions(+) diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index 456d01d5b08..118f059711f 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -816,6 +816,213 @@ This plugin can limit the number of Instructions Per = Second that are executed:: The lower the number the more accurate time will be, but the less ef= ficient the plugin. Defaults to ips/10 =20 +Uftrace +....... + +``contrib/plugins/uftrace.c`` + +This plugin generates a binary trace compatible with +`uftrace `_. + +Plugin supports aarch64 only (x64 support should be trivial to add), and w= orks +in user and system mode, allowing to trace a system boot, which is not som= ething +possible usually. + +In user mode, the memory mapping is directly copied from ``/proc/self/maps= `` at +the end of execution. Uftrace should be able to retrieve symbols by itself, +without any additional step. +In system mode, the default memory mapping is empty, and you can generate +one (and associated symbols) using ``contrib/plugins/uftrace_symbols.py``. +Symbols must be present in ELF binaries. + +It tracks the call stack (based on frame pointer analysis). Thus, your pro= gram +and its dependencies must be compiled using ``-fno-omit-frame-pointer +-mno-omit-leaf-frame-pointer``. In 2024, `Ubuntu and Fedora enabled it by +default again on x64 +`_. +On aarch64, this is less of a problem, as they are usually part of the ABI, +except for leaf functions. That's true for user space applications, but not +necessarily for bare metal code. + +Timestamps used for events are the number of instructions executed so far = by +default. As it's tracked per vcpu, each timeline should be considered +separately. It's possible to use real timestamps by using option +``timestamp-based-on-real-time``. This is not the default, as considering = real +time when doing emulation and instrumentation may not necessarily report c= orrect +things. However, it is quite useful when running multiple cpus scenarios, = or if +you want to generate a trace around a particular time of the execution. + +When tracing long scenarios (> 1 min), the generated trace can become very= long, +making it hard to extract data from it. In this case, a simple solution is= to +trace execution using ``timestamp-based-on-real-time=3Don``, and generate a +timestamped output log using ``qemu-system-aarch64 ... | ts "%s"``. Then, +``uftrace --time-range=3Dstart~end`` can be used to reduce trace for only = this +part of execution. + +Performance wise, overhead compared to normal tcg execution can vary from = x2 +(sampling only) to x10-x15 (precise stack tracking). + +.. list-table:: Uftrace plugin arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - trace-privilege-level=3D[on|off] + - Generate one trace per privilege level (Exception Level + Security S= tate + on aarch64). + * - trace-sample=3DN + - Instead of precise tracking, perform stack sampling every N instruct= ions. + If combined with ``trace-privilege-level``, it will still contain pr= ecise + stacks for privilege level changes, and will sample stack between th= ose. + * - timestamp-based-on-real-time=3D[on|off] + - Use real time for timestamps instead of number of instructions execu= ted. + +.. list-table:: uftrace_symbols.py arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - elf_file [elf_file ...] + - path to an ELF file. Use /path/to/file:0xdeadbeef to add a mapping o= ffset. + * - --prefix-symbols + - prepend binary name to symbols + +Example user trace +++++++++++++++++++ + +As an example, we can trace qemu itself running git:: + + $ ./build/qemu-aarch64 -plugin \ + build/contrib/plugins/libuftrace.so,timestamp-based-on-real-time=3Do= n \ + ./build/qemu-aarch64 /usr/bin/git --help + + # and generate a chrome trace directly + $ uftrace dump --chrome | gzip > ~/qemu_aarch64_git_help.json.gz + +For convenience, you can download this trace `qemu_aarch64_git_help.json.gz +`_. +Download it and open this trace on https://ui.perfetto.dev/. You can zoom = in/out +using w,a,s,d keys. Some sequences taken from this trace: + +- Loading program and its interpreter + +.. image:: https://fileserver.linaro.org/s/fie8JgX76yyL5cq/preview + :height: 200px + +- open syscall + +.. image:: https://fileserver.linaro.org/s/rsXPTeZZPza4PcE/preview + :height: 200px + +- TB creation + +.. image:: https://fileserver.linaro.org/s/GXY6NKMw5EeRCew/preview + :height: 200px + +It's usually better to use ``uftrace record`` directly. However, tracing +binaries through qemu-user can be convenient when you don't want to recomp= ile +them (``uftrace record`` requires instrumentation), as long as symbols are +present. + +Example system trace +++++++++++++++++++++ + +A full trace example (chrome trace, from instructions below) generated fro= m a +system boot can be found `here +`_. +Download it and open this trace on https://ui.perfetto.dev/. You can see c= ode +executed for all privilege levels, and zoom in/out using w,a,s,d keys. You= can +find below some sequences taken from this trace: + +- Two first stages of boot sequence in Arm Trusted Firmware (EL3 and S-EL1) + +.. image:: https://fileserver.linaro.org/s/kkxBS552W7nYESX/preview + :height: 200px + +- U-boot initialization (until code relocation, after which we can't track= it) + +.. image:: https://fileserver.linaro.org/s/LKTgsXNZFi5GFNC/preview + :height: 200px + +- Stat and open syscalls in kernel + +.. image:: https://fileserver.linaro.org/s/dXe4MfraKg2F476/preview + :height: 200px + +- Timer interrupt + +.. image:: https://fileserver.linaro.org/s/TM5yobYzJtP7P3C/preview + :height: 200px + +- Poweroff sequence (from kernel back to firmware, NS-EL2 to EL3) + +.. image:: https://fileserver.linaro.org/s/oR2PtyGKJrqnfRf/preview + :height: 200px + +Build and run system example +++++++++++++++++++++++++++++ + +Building a full system image with frame pointers is not trivial. + +We provide a `simple way `= _ to +build an aarch64 system, combining Arm Trusted firmware, U-boot, Linux ker= nel +and debian userland. It's based on containers (``podman`` only) and +``qemu-user-static (binfmt)`` to make sure it's easily reproducible and do= es not depend +on machine where you build it. + +To build the system:: + + # Install dependencies + $ sudo apt install -y podman qemu-user-static + + $ git clone https://github.com/pbo-linaro/qemu-linux-stack + $ cd qemu-linux-stack + $ ./build.sh + + # system can be started using: + $ ./run.sh /path/to/qemu-system-aarch64 + +To generate a uftrace for a system boot from that:: + + # run true and poweroff the system + $ env INIT=3Dtrue ./run.sh path/to/qemu-system-aarch64 \ + -plugin path/to/contrib/plugins/libuftrace.so,trace-privilege-level= =3Don + + # generate symbols and memory mapping + $ path/to/contrib/plugins/uftrace_symbols.py \ + --prefix-symbols \ + arm-trusted-firmware/build/qemu/debug/bl1/bl1.elf \ + arm-trusted-firmware/build/qemu/debug/bl2/bl2.elf \ + arm-trusted-firmware/build/qemu/debug/bl31/bl31.elf \ + u-boot/u-boot:0x60000000 \ + linux/vmlinux + + # inspect trace with + $ uftrace replay + +Uftrace allows to filter the trace, and dump flamegraphs, or a chrome trac= e. +This last one is very interesting to see visually the boot process:: + + $ uftrace dump --chrome > boot.json + # Open your browser, and load boot.json on https://ui.perfetto.dev/. + +Long visual chrome traces can't be easily opened, thus, it might be +interesting to generate them around a particular point of execution:: + + # execute qemu and timestamp output log + $ env INIT=3Dtrue ./run.sh path/to/qemu-system-aarch64 \ + -plugin path/to/contrib/plugins/libuftrace.so,trace-privilege-level= =3Don,timestamp-based-on-real-time=3Don |& + ts "%s" | tee exec.log + + $ cat exec.log | grep 'Run /init' + 1753122320 [ 11.834391] Run /init as init process + # init was launched at 1753122320 + + # generate trace around init execution (2 seconds): + $ uftrace dump --chrome --time-range=3D1753122320~1753122322 > init.js= on + Other emulation features ------------------------ =20 diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index 9211f273700..9cbadda0aae 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -4,6 +4,8 @@ * Generates a trace compatible with uftrace (similar to uftrace record). * https://github.com/namhyung/uftrace * + * See docs/about/emulation.rst|Uftrace for details and examples. + * * SPDX-License-Identifier: GPL-2.0-or-later */ =20 --=20 2.47.2 From nobody Sat Nov 15 09:50:14 2025 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=1753390130; cv=none; d=zohomail.com; s=zohoarc; b=lUDUwkIbrSf0LYAa6o9NQzrdk60i3Cezd2oG7+ZRng4OJyaNePwCoYHfmtlVMkCmGqVdCjWYot22xZoYkGlPMSDj3E52SMpxttelbu1DYQpqSTm+lHQ9PFqC7+SSMSCU6cdggoBdXjHpZ8RHihlJsZLEHNqjS4owW7fAYZwhHws= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753390130; h=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=ZnYNV82V/hf4sGHaMrMgGS5MLgMIqFR5EpaSxp5dO0I=; b=fbe/cS0eoimMhEZJRuwzmvvdRSRrc0yUTJS7rljsru9TugaDRQCL5R5bUSMIvJgvK+w4Z09LvC35COHU+brNihk4cPTTE/uMADoc0bLku3z77BzUeocBS3I7g7TgQ0/Ry+2d48w+LjLdfAiNwO6FTeG9QaovDbKr6jaaXj/HZtg= 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 1753390130183882.9844029796457; Thu, 24 Jul 2025 13:48:50 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uf2p8-00057n-3y; Thu, 24 Jul 2025 16:45:54 -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 1uf2p5-00055o-Ms for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:51 -0400 Received: from mail-pg1-x52b.google.com ([2607:f8b0:4864:20::52b]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1uf2p2-0007NL-3F for qemu-devel@nongnu.org; Thu, 24 Jul 2025 16:45:51 -0400 Received: by mail-pg1-x52b.google.com with SMTP id 41be03b00d2f7-b3f7404710aso464634a12.0 for ; Thu, 24 Jul 2025 13:45:42 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31e662f6be0sm2008849a91.35.2025.07.24.13.45.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Jul 2025 13:45:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753389942; x=1753994742; 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=ZnYNV82V/hf4sGHaMrMgGS5MLgMIqFR5EpaSxp5dO0I=; b=X9TyR/125lGSBu3gBtxdYrMJz/SxuU9D9LsCIb+e020A2Xl6lt401CiHTEMdcNhBQE wg0/IEdRkfOic6zEEXZFB+KusMEjcc0uxJINf9T1HHv0bijrgup6MprkMVPJTr2f/A7c QrWqqonBYN/DORwPU2LqAKUPguyhbRMkcWdkmeLqKd6JbLq3PPVjhojY0WTV4GYWx9dL oe9Iryg9bBfGuxcJNt7KmUChcpZ76tWF6DVC9BuaXNmC6nUTK6MGp0cpjHyJhY/2VKAw RPeKBvokiJ2PnAjiCjyLBm7vZDU1EFiZXhKbY9P3RcsXzlJYBF8fvzBg8BZ/jf2I1V+c cG8w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753389942; x=1753994742; 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=ZnYNV82V/hf4sGHaMrMgGS5MLgMIqFR5EpaSxp5dO0I=; b=Su4TcCDIR0tzGSmRZgkydDPxgjJQokTkXvqe9Y3t1WjBYtBzMvIr0lDk+XyHbXVKr2 haS4mII1xHLaatelmFIrgSL9PZeXzr6Z4g3+O15UrVM7js4AQ9OLA4Rs4IvoAGUQrZ4w OpGuBaV5yiEkelT+1LEouLLpd9Mwqr5oQ9+OKgoyi3VjCQ/CTQ0FzS+IWQZPiQhymtAw Mfjt7Fd+Qc1D1zPZWUj3DRHUXePV1DKb5FFvoKbT522smpXHAM4foXsuLUZrUKyMXVJd 7GTPHdIOFk2PeZBPwT7WcqwdboRK9pft0m6wDZQkYuIKpCNV0BqfNU6e69Afbsx8YD9T fDQw== X-Gm-Message-State: AOJu0Yw6iiaAq6Z99CgF0mKG+n0cCYPI91mpDWBI++8H56jregFtpN1/ XF8qB0iY4fYTdsH2HV1KHTYX6z/EXVlxx16UHoEox23xodkjlzo+VFvxgqbIuGe3Ky2eK5woebt f5CVk X-Gm-Gg: ASbGncvOkRwPuBOl30cGdBV4G5wrrHRi/BJAMEvc31dVC8svTIK+zflNLSwY245Y7XK jAoAoib3Pgi37yVK6pG/yr06hDZ9z3eUhyNTRy29E+bdluGHl180qIj8Eja+e2hvQSPBEmG26XW fDOIiH75c6FcHXGPZ6n6fitUv7zxQEDm243TRnZDgWUaVOGjU4mriSihZjL+YsolP4zQIB3D5r6 cyPzT0ilPGev0F+9Vn0ZdcPIw0K6pTDAsRWm4uOlpttqeNIGQJwAna/1aAzw6ztEPqwAjCPe976 wKcqG6yOvG6q1B4DjJvsnJQs+Ie1zIRz+TEhAap80zQXgOSzUilyKTsaWSI0OECLUsICyiIROvQ mNarAEnzvW0cXfrg3WudIig== X-Google-Smtp-Source: AGHT+IGmXppgDcILUdYyBMVlLqeBAuYT2d+CQGo50adD/LDYXpmQhGG2hdH7rXmvvrZPVw5ZWvmKXg== X-Received: by 2002:a17:90b:3809:b0:31e:326e:4d2d with SMTP id 98e67ed59e1d1-31e5073f566mr10507541a91.5.1753389941776; Thu, 24 Jul 2025 13:45:41 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Peter Maydell , Alexandre Iooss , Gustavo Romero , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Mahmoud Mandour , rowan Hart , Manos Pitsidianakis , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Richard Henderson , Pierrick Bouvier Subject: [PATCH v4 7/7] contrib/plugins/uftrace: add support for x64 Date: Thu, 24 Jul 2025 13:45:26 -0700 Message-ID: <20250724204527.3175839-8-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> References: <20250724204527.3175839-1-pierrick.bouvier@linaro.org> MIME-Version: 1.0 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=2607:f8b0:4864:20::52b; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pg1-x52b.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+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @linaro.org) X-ZM-MESSAGEID: 1753390131485116600 Content-Type: text/plain; charset="utf-8" Stack layout is the same as aarch64, so we can reuse the same code. We don't track stack in real mode, but it does not really matter as it's usually very short, when the machine boots. Signed-off-by: Pierrick Bouvier --- docs/about/emulation.rst | 14 ++++-- contrib/plugins/uftrace.c | 94 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index 118f059711f..7f45dc209b6 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -824,9 +824,8 @@ Uftrace This plugin generates a binary trace compatible with `uftrace `_. =20 -Plugin supports aarch64 only (x64 support should be trivial to add), and w= orks -in user and system mode, allowing to trace a system boot, which is not som= ething -possible usually. +Plugin supports aarch64 and x64, and works in user and system mode, allowi= ng to +trace a system boot, which is not something possible usually. =20 In user mode, the memory mapping is directly copied from ``/proc/self/maps= `` at the end of execution. Uftrace should be able to retrieve symbols by itself, @@ -842,7 +841,8 @@ default again on x64 `_. On aarch64, this is less of a problem, as they are usually part of the ABI, except for leaf functions. That's true for user space applications, but not -necessarily for bare metal code. +necessarily for bare metal code. You can read this `section +` to easily build a system with frame pointe= rs. =20 Timestamps used for events are the number of instructions executed so far = by default. As it's tracked per vcpu, each timeline should be considered @@ -964,6 +964,8 @@ find below some sequences taken from this trace: Build and run system example ++++++++++++++++++++++++++++ =20 +.. _uftrace_build_system_example: + Building a full system image with frame pointers is not trivial. =20 We provide a `simple way `= _ to @@ -972,6 +974,10 @@ and debian userland. It's based on containers (``podma= n`` only) and ``qemu-user-static (binfmt)`` to make sure it's easily reproducible and do= es not depend on machine where you build it. =20 +You can follow the exact same instructions for a x64 system, combining edk= 2, +Linux, and Ubuntu, simply by switching to +`x86_64 `_ bra= nch. + To build the system:: =20 # Install dependencies diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index 9cbadda0aae..8fc22302989 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -81,6 +81,20 @@ typedef struct { struct qemu_plugin_register *reg_scr_el3; } Aarch64Cpu; =20 +typedef enum { + X64_RING0, + X64_RING1, + X64_RING2, + X64_RING3, + X64_REAL_MODE, +} X64PrivilegeLevel; + +typedef struct { + struct qemu_plugin_register *reg_rbp; + struct qemu_plugin_register *reg_cs; + struct qemu_plugin_register *reg_cr0; +} X64Cpu; + typedef struct { uint64_t timestamp; uint64_t data; @@ -600,6 +614,84 @@ static CpuOps aarch64_ops =3D { .does_insn_modify_frame_pointer =3D aarch64_does_insn_modify_frame_poi= nter, }; =20 +static uint8_t x64_num_privilege_levels(void) +{ + return X64_REAL_MODE + 1; +} + +static const char *x64_get_privilege_level_name(uint8_t pl) +{ + switch (pl) { + case X64_RING0: return "Ring0"; + case X64_RING1: return "Ring1"; + case X64_RING2: return "Ring2"; + case X64_RING3: return "Ring3"; + case X64_REAL_MODE: return "RealMode"; + default: + g_assert_not_reached(); + } +} + +static uint8_t x64_get_privilege_level(Cpu *cpu_) +{ + X64Cpu *cpu =3D cpu_->arch; + uint64_t cr0 =3D cpu_read_register64(cpu_, cpu->reg_cr0); + uint64_t protected_mode =3D (cr0 >> 0) & 0b1; + if (!protected_mode) { + return X64_REAL_MODE; + } + uint32_t cs =3D cpu_read_register32(cpu_, cpu->reg_cs); + uint32_t ring_level =3D (cs >> 0) & 0b11; + return ring_level; +} + +static uint64_t x64_get_frame_pointer(Cpu *cpu_) +{ + X64Cpu *cpu =3D cpu_->arch; + return cpu_read_register64(cpu_, cpu->reg_rbp); +} + +static void x64_init(Cpu *cpu_) +{ + X64Cpu *cpu =3D g_malloc0(sizeof(X64Cpu)); + cpu_->arch =3D cpu; + g_autoptr(GArray) regs =3D qemu_plugin_get_registers(); + for (int i =3D 0; i < regs->len; ++i) { + qemu_plugin_reg_descriptor *reg; + reg =3D &g_array_index(regs, qemu_plugin_reg_descriptor, i); + if (!strcmp(reg->name, "rbp")) { + cpu->reg_rbp =3D reg->handle; + } else if (!strcmp(reg->name, "cs")) { + cpu->reg_cs =3D reg->handle; + } else if (!strcmp(reg->name, "cr0")) { + cpu->reg_cr0 =3D reg->handle; + } + } + g_assert(cpu->reg_rbp); + g_assert(cpu->reg_cs); + g_assert(cpu->reg_cr0); +} + +static void x64_end(Cpu *cpu) +{ + g_free(cpu->arch); +} + +static bool x64_does_insn_modify_frame_pointer(const char *disas) +{ + return strstr(disas, "rbp"); +} + +static CpuOps x64_ops =3D { + .init =3D x64_init, + .end =3D x64_end, + .get_frame_pointer =3D x64_get_frame_pointer, + .get_privilege_level =3D x64_get_privilege_level, + .num_privilege_levels =3D x64_num_privilege_levels, + .get_privilege_level_name =3D x64_get_privilege_level_name, + .does_insn_modify_frame_pointer =3D x64_does_insn_modify_frame_pointer, +}; + static void track_privilege_change(unsigned int cpu_index, void *udata) { Cpu *cpu =3D qemu_plugin_scoreboard_find(score, cpu_index); @@ -905,6 +997,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_= id_t id, =20 if (!strcmp(info->target_name, "aarch64")) { arch_ops =3D aarch64_ops; + } else if (!strcmp(info->target_name, "x86_64")) { + arch_ops =3D x64_ops; } else { fprintf(stderr, "plugin uftrace: %s target is not supported\n", info->target_name); --=20 2.47.2