From nobody Sat Nov 15 10:54:09 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=1752644146; cv=none; d=zohomail.com; s=zohoarc; b=mX5Rv22FapnjC/xAMwf5DSJgHRT5Y+I+dmZEoQG6i7/45lZF5JYcaeIEllLlY31eaKDhXD34xR1rTsAUDuJPLYl6+G4TnlIWhwGIKE/kzZi1EHzA8DnTE7zfE4W3nJMMdtM2IDtkbUun71k+0XYDJI/cNGIgHqY9XdT2rRntuIE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1752644146; 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=BwgVGlpuVZMdGPEt0il7SpuQqcFCUGwkOsSd1PPAb10=; b=BboVD8l/FnF9qsSJYWQlKJnpZbfXD0vRXptPnu5i7dLnqWlamXKgEMT57MhKMXMIElRoNRZ6eJ8DpHwv85dl1+6fLhjopZQOjwFiow1t1V2oaZvs3mzw2Fb8nbJJSe+GNK0yB9Mt8Vn67qs53WoiJtNqeNz6yd5xRg3Gb8PNyNc= 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 1752644146812470.43423105627767; Tue, 15 Jul 2025 22:35:46 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ubuma-0006vk-Dm; Wed, 16 Jul 2025 01:34:20 -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 1ubumY-0006pM-3C for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:18 -0400 Received: from mail-pf1-x42d.google.com ([2607:f8b0:4864:20::42d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ubumU-0007rB-QK for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:17 -0400 Received: by mail-pf1-x42d.google.com with SMTP id d2e1a72fcca58-742c7a52e97so5133166b3a.3 for ; Tue, 15 Jul 2025 22:34:14 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-74eb9e06aa6sm13790162b3a.62.2025.07.15.22.34.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Jul 2025 22:34:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1752644053; x=1753248853; 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=BwgVGlpuVZMdGPEt0il7SpuQqcFCUGwkOsSd1PPAb10=; b=B40bqbuSub8EUULvrZquwU3v7EuG9BB/q+r6VVaJoFIu4/g8g7+B11n6DthkJl2LUN 9rwtcNjR5d9vMI5z3iuuSeXXRWCHu0Wtb92wuNhaif0wT/YKWSspMWNGJ6TnHz7CpA/1 /HubssyD0ChsbrQDJLyapOm5SRhOQ3evaF0Jpu5Vv36JA/xwvD5qYdEaDyCVutu2SDAM /3SXVjym89bQWCviZ+1OD4vWAxUFH98lFzDL6++CxLKernQv1389ZS3ivxndStI1YaWC YUjco2W4C3O6uovVTCWpcN1LbCTFOS9Re7GiNVDbLROfcAd+LtYrMO+s1uuF485prHZV 20Gw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752644053; x=1753248853; 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=BwgVGlpuVZMdGPEt0il7SpuQqcFCUGwkOsSd1PPAb10=; b=L8TWzFjN6Lxh97XynX2cjwPzcMRYg0j8uU/XFTtmpbqmFKG8QzNpHbe1h6+2MCtALk fpiaa2ZUwJqLZtwTR1CxaP4g+H+MVc0JCQl5o0Q9D/YutEXgOp62uu6UJO+BKUKSN3Hr q46yYOSoKOiUBPH1/OfFUs+Ym7BCe1GjSp2dV1h7vCi4tPS+iu7W1qnpQWS9fQu9UlNE M2g8EyLpadiIR1bUYVQCouXzwIXr7ZNLkt2rliMha8DjpJYcEROr256ISZxTtjvjY8TV uwdB85BS5TxV+FpPLLADCfolpp8NXQjUYpwDOI0hXtm4mfH0prZR64c91e39z5clN/OY DAYQ== X-Gm-Message-State: AOJu0Yx76fO0AVebFNpYd+WWJ9ZURItHp6hQVJ0bGgVjF8MU0fza84lA YsH2spjAL1ByX6f+E7VrqjKmWNZAnG/KXwFN3+mRWq/iFqTmwkRLw3yYB3Z3HS7xPhuxw1evuxB 33mUy X-Gm-Gg: ASbGncurfFbbWoMaHJtgzigeE+Ev1Hm7H6IOOORYcKI9qnLpYamnrzGvlLGN5XjUcOy GbnuxOcotstXRwrOifx99eUUsLxDctd1UXdDmTYj75MpsfXWVAGrmiRp387L58s7AsftNHDlc4C Hw9aiyfb9BxQqkEtYLfI9priqi0Im/cVIBOWz5eTOc10A2YTpIhJQosKCstVi+lQtyAjsnjGXzd 19DcoX6O9eQNUPskjjzvNO+BWzaTBaK4jtwegcMbBz25QNyFs1L05JCF13xRejJ7s0RLImbmst3 MzYyy89hnhmUPWv+0nuRqawBgAp2UuSusI3J6vTnPrmcwCEgnRs5oYh+v22xPLNLEgb7baIhKVh FPzX7+04XQWac/OpDb7khDY6eDNgYg/Vu X-Google-Smtp-Source: AGHT+IF99NxSn1WABM+WhkvVNAtfIRnci4TMeQl5x8SQzsxOB+8QpgQYb+qtB+CkJxDw/z0X2VEriw== X-Received: by 2002:a05:6a20:6a0b:b0:237:b53b:64dd with SMTP id adf61e73a8af0-237d5a0b1eemr3409016637.11.1752644053223; Tue, 15 Jul 2025 22:34:13 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Alexandre Iooss , Peter Maydell , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Manos Pitsidianakis , Mahmoud Mandour , Gustavo Romero , Pierrick Bouvier , =?UTF-8?q?Alex=20Benn=C3=A9e?= , rowan Hart , Richard Henderson Subject: [PATCH 1/6] contrib/plugins/uftrace: new uftrace plugin Date: Tue, 15 Jul 2025 22:34:02 -0700 Message-ID: <20250716053407.2814736-2-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250716053407.2814736-1-pierrick.bouvier@linaro.org> References: <20250716053407.2814736-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::42d; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pf1-x42d.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: 1752644147978116600 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 | 613 ++++++++++++++++++++++++++++++++++++ contrib/plugins/meson.build | 3 +- 2 files changed, 615 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..1f52e8e0f4e --- /dev/null +++ b/contrib/plugins/uftrace.c @@ -0,0 +1,613 @@ +/* + * 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 + +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) +{ + FILE *sid_map =3D fopen("./uftrace.data/sid-0.map", "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 *)); + 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); + g_array_append_val(traces, t); + } + } + + for (size_t i =3D 0; i < traces->len; ++i) { + trace *t =3D g_array_index(traces, trace*, i); + trace_flush(t, true); + } + + 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 10:54:09 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=1752644157; cv=none; d=zohomail.com; s=zohoarc; b=VSTOpLwMsY/3MzYTljbd9PORGqa0fbiCildoiIh8kc+d/CLpHopJwhOKe8koPdTcD5stUw4Z1A/k0Zyvp+s3M+5FCkni9jygdamxIf2Oyrns4Ak9Bp2e5N11zIAekGTLS1AmsDd4jxEfonOaPFHw5AJfL0fGFC9CA+v1b6xAvtM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1752644157; 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=oa5OA8K99UsScs6d3Jua3/OD+RHq2I0UwUyhgSBoPz4=; b=IDhdm4nABI679IWQWqvSkgV3XMEpdPkei4hj9d5O/IsBFmyXAoLbPedLub5zt3oZJ9iRuMck9S526eeKaGvHCFjkqDuB3cBj4faPDgUQHslsTHHifCO0m7q+N03qqqDTi6YvBM2xd9dsImRm7vn537cqQENwq0XlIZCY2E1ldUY= 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 1752644157790185.89288078064908; Tue, 15 Jul 2025 22:35:57 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ubuma-0006wU-L0; Wed, 16 Jul 2025 01:34:20 -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 1ubumY-0006qB-8S for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:18 -0400 Received: from mail-pf1-x429.google.com ([2607:f8b0:4864:20::429]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ubumV-0007rP-HA for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:17 -0400 Received: by mail-pf1-x429.google.com with SMTP id d2e1a72fcca58-74264d1832eso8029556b3a.0 for ; Tue, 15 Jul 2025 22:34:15 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-74eb9e06aa6sm13790162b3a.62.2025.07.15.22.34.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Jul 2025 22:34:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1752644054; x=1753248854; 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=oa5OA8K99UsScs6d3Jua3/OD+RHq2I0UwUyhgSBoPz4=; b=jnh+AfN1ytMvxt5XLl9haHFk53WZSiE6VdhGVunMtAlNcINdF4pvj655dQeoDnBoL+ bib8mu8pnrqMxjBkaHjPAVNadIu2qJaBgGZiFtY/WIk+xRMP07miyIdCmzyQL34wyxfc eFvoY5o48BUD+IB2qat3MPyggsgEXrN6k+zL83hF1x+B+ef/fzTVlwDwbTiA5DXLOH6X H+VzjPyKeDvPojStBw0+WGlL9DEWN/XRZdFtgCGBs4gTj6Oz50RS56/1mmQ/w52D1mit GmsnBtix9UnPsRvvCF6Cv8rDiFBDDBFw8WKQcWRR31/S/wtE6Gz6IjFHMgVOCZXfyJs+ s3zA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752644054; x=1753248854; 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=oa5OA8K99UsScs6d3Jua3/OD+RHq2I0UwUyhgSBoPz4=; b=Ij2ZP/kgZRZYnjzKIDthd5BcutxaPPGy6ctjMup/eeFE8RpRYPZug0hlGFlQQ2ItTG klB5acg7fak0JUZJ0qk/4sHZpCd8bY8QQP1WsvnFBxiFYNZVc8PsUqDwiZXasmqxdgIO FCiKDNYNyv2jJP32Bid78ztWlCeCjnPr7k5K+hqOilKgiW2d5x1OncaW/nRHBkQYD2o6 wzfkNmRobIOPVrN+vFlJ+AihVZuj5JPKqT4En3lT+cn1siaC2Xnjye0Zq+GqMdTy1tDU ByxwuYh05v5dWH2qYlY2iSbYybYMBUeHJtLbysmmSzn3a6Tmp2TqyEviRnFb1kWQJNnp DCkw== X-Gm-Message-State: AOJu0YwRTM8T4oO75EXvoXqky10iRugZJkRthUOhDA15zjU3q/vZvZDy MilmJ+zMpQ53yQh3SMRDlulDy3LhMlJgyYDgYwA90yzQqLNZ3NFcSRg6Ab+ShFZ+3mD8HfcpRqU o4G0N X-Gm-Gg: ASbGnct1CvFKL9KuHM9GDKmeAC/ziZwoFdrjQo5nP950Ec+J3yMwRZQVDBzSaCY7awH 1hR92aCGg9hbh91l+PILQtZo5NyWxnnwpR51HvM2RxOLB1zprlmReL2EquCKzkoWfK4h7apFhyH 56DQ98PJxQ2POrQt3nL4sbaidi/5Cl/2N2e0Qsihqxw3qQvxwRR8ODlj1BXriSI5kaWZx0TZKkx sgnc1M9pR8bFmzC5lZNQGxGFpAld4QLCnGp3/TCEGCVRhUxhDh+y/paJ0qRVecCA+55aZN8XDtI bxwMKLF3gJKPdY9EVb0YtswLrHBFlDaSdKy+Q4N0+BxEXfnKlMbIfgG9dK4B34pGHBbUflcEbSt +rE/wWsNX2cdyv3zKWJtX2A== X-Google-Smtp-Source: AGHT+IGAcuaY0tnljFtZ9vtOnA31Jx84t4R382N3QJFjtPQc+NmryXej0pRbK7bEyL8kQNB34xafcQ== X-Received: by 2002:a05:6a20:938c:b0:215:ead1:b867 with SMTP id adf61e73a8af0-23811345d42mr2211252637.14.1752644054160; Tue, 15 Jul 2025 22:34:14 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Alexandre Iooss , Peter Maydell , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Manos Pitsidianakis , Mahmoud Mandour , Gustavo Romero , Pierrick Bouvier , =?UTF-8?q?Alex=20Benn=C3=A9e?= , rowan Hart , Richard Henderson Subject: [PATCH 2/6] contrib/plugins/uftrace: add trace-sample option Date: Tue, 15 Jul 2025 22:34:03 -0700 Message-ID: <20250716053407.2814736-3-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250716053407.2814736-1-pierrick.bouvier@linaro.org> References: <20250716053407.2814736-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::429; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pf1-x429.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: 1752644159623116600 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 1f52e8e0f4e..bd3df10a218 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -43,6 +43,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[] */ @@ -69,6 +72,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) @@ -190,6 +194,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); @@ -382,6 +393,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; @@ -442,6 +473,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)) { /* @@ -450,7 +488,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 @@ -464,7 +504,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 @@ -473,27 +515,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. */ @@ -502,6 +582,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); @@ -527,6 +609,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; @@ -596,6 +679,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 10:54:09 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=1752644146; cv=none; d=zohomail.com; s=zohoarc; b=eZZBroUuO/Xs0tB5YAPvWb/pOLMXYwREJR+/eJfbsVTwrPZrpDOLR4Jsbw6rxVCckt0fE0WMjJ5/mgzS09nUwLZaTj1DBFFQGJE14kdkp98T56LuQbqrXjs43X5SLlxZ8xQHvmXu9k+j8TFBKUujqNZZUB2o9+xqksvJvRDD2Co= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1752644146; 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=PPWhAfV4U+PGvcq+dhhKuGRo/S91hqDw8kKnQb+tVRA=; b=MqdiVgnsApJO0TA8PMI3R+qjnhPqSRT8DKjn5QIW0cGgMqHtOl74jkRUY17AdhgdMI/+jUU0rE5f+0//78G4L9Fwtgagc+cAhyQPY2vmAkQWmrSv4DTUw9aDpYuBg0Ik3Nc4nKMJ2aF/v7NMtnxy8D1I0i4fGf85k89wrg7+J08= 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 1752644146941497.08816147374193; Tue, 15 Jul 2025 22:35:46 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ubumd-000792-Ph; Wed, 16 Jul 2025 01:34:23 -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 1ubumZ-0006sX-Fj for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:19 -0400 Received: from mail-pf1-x42a.google.com ([2607:f8b0:4864:20::42a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ubumW-0007rU-V1 for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:19 -0400 Received: by mail-pf1-x42a.google.com with SMTP id d2e1a72fcca58-7494999de5cso3909175b3a.3 for ; Tue, 15 Jul 2025 22:34:16 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-74eb9e06aa6sm13790162b3a.62.2025.07.15.22.34.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Jul 2025 22:34:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1752644055; x=1753248855; 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=PPWhAfV4U+PGvcq+dhhKuGRo/S91hqDw8kKnQb+tVRA=; b=pMVItSNzqV7wP704X5Blx1OpHCa6hBSvmFyLLyVcMrllEu1I1YzxVc+KtcwFNnjNBs RgQiaJ77yd3G1A1h0Iu8s6tF+TCAaLxVyyxRQVWGdBbMS49ikVrkkH4Ybb/AzuR08ury 3OxoqJO26Ole6TrHPDcJMs+I0EhqE5ofXWs4M2OYevpnXB1jRtAFMEQZfzVLUFzPH+0i PtW4krUNH6Sz4WSSA8Xh50ORlMapRE4WdY64J7rrBFdYEbkTtsf3uPbUz2BLDEOc8qut Mganj4q8brrJBM2uqmtaWOCLtpSVW4U0074JjS/pD+61IN5ZTHQQa45TqLtm1VWjYzR1 k8/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752644055; x=1753248855; 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=PPWhAfV4U+PGvcq+dhhKuGRo/S91hqDw8kKnQb+tVRA=; b=n6cl/ImRe764xueoYuAFIVSH3SqnpopF5/jtM/xiLyWgN2p6uywh8RoGVHDYatDB3s PC4F7nv5njcLGa+DvhbCvREg41wgiHuKzoBbILCSU+3cFt1NIyxYufbYKnG6CCRAXmIo 2iIkD27h5keHznl2rAauWeeQKhn3p7wMseOzhwnXrXlGW9wRfXzJ9nmvbmEV7zLbyfFW 1+6SfG2wE2UPEkrL/laBZTxsgVXe59ZJNNnAIAu1ePNIrugQdfDeLPTVIupCayvYZ2eR hRhCxryKlD1veIVp3MkirilVACf7vuqA9kin/QhxlXmMctHxAGIaVVq6CLvHiZgKhfgM l3+w== X-Gm-Message-State: AOJu0YxjXvLGnPQl+Cn/I5+e0dxDLbk3/CX3i1qa1sTc8JFiuwPbVCqt CVP3y38PMMl1t2+X3TKK4TINkU28RCNE3mpMDI35Go6D1+HBb/7zhXt1t8yYepf5DSMptiNEhk2 3BV7i X-Gm-Gg: ASbGncudEJV1LH55RdysM8qQ/XCg6JC27TmTmjjn+J88HT1flqnaFcitgXNGFXPekuY N3pOQLA2YYqUQBhoTKueKz940pj2kFyIk5HkOHlffLzlixt2zDdhfNdx2spRePpYqiZAs1uqGk4 cWR51mi7NBvme+I+MjrE5RiFncpQ1KUmfAYTEgKGFdcBDG0EBs6GRMK+Jd4s2lZ4S+Wp59zxDHH fWnLXPHjXcBUp7i/TjUOQ36ZR1gJn8fMTVnBRTxKOgKW318FhM236dTthRSdzDmtD15iUDRrJO5 ZlgzTHuFOshqvlgXVL1rbQ2JYnwsvjOOfAusxrD/FNjAF4FIv9akzhoCxCi/h0SOMDqESxJw1qg UpMb9CaGzzdHkALU217I/xg== X-Google-Smtp-Source: AGHT+IEO3ZTX8hMiEeoMBs2up9vhfygFuAzK0QBkkvohRL3kcrX7OIHXUnj5kjETQvVU/Bi8rb7y3g== X-Received: by 2002:a05:6a00:1412:b0:742:3fb4:f992 with SMTP id d2e1a72fcca58-756e8b5795emr2608927b3a.10.1752644055117; Tue, 15 Jul 2025 22:34:15 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Alexandre Iooss , Peter Maydell , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Manos Pitsidianakis , Mahmoud Mandour , Gustavo Romero , Pierrick Bouvier , =?UTF-8?q?Alex=20Benn=C3=A9e?= , rowan Hart , Richard Henderson Subject: [PATCH 3/6] contrib/plugins/uftrace: add trace-privilege-level option Date: Tue, 15 Jul 2025 22:34:04 -0700 Message-ID: <20250716053407.2814736-4-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250716053407.2814736-1-pierrick.bouvier@linaro.org> References: <20250716053407.2814736-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::42a; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pf1-x42a.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: 1752644147994116600 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 bd3df10a218..58805b7884a 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -38,6 +38,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 @@ -48,6 +51,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; @@ -55,8 +59,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 { @@ -73,6 +92,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) @@ -349,6 +369,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); @@ -418,6 +448,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; @@ -434,6 +526,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) { @@ -441,6 +537,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) @@ -462,9 +560,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; @@ -559,7 +691,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); @@ -572,6 +704,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 @@ -614,17 +752,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) @@ -689,6 +843,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 10:54:09 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=1752644166; cv=none; d=zohomail.com; s=zohoarc; b=RjSm3E9qfa5I3JqlgT8MkBnCzOT4QGD0bnF4QyUCxH02Shw0+/39ow+uuamBfZMnQ2OM8HZKqSrw5dc/3VG/Vc7XdkSVyjv3Hdw1hOyjJsz4vOg9V3zuyI8mRbSOK22VZgov/wof4jbutLczeFY720V3WFhsuPAp9Q0Yl6aY/QM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1752644166; 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=GtV+8U6r6cDalyfNmOH/gMyYclw6zVKJEtKnGE3JfIo=; b=Ja9ienNSuwPb1W5kzbP0rR6ce1TKODXbcogBXU1hGcqDe9DfEDlxDIld/r32WpL7UuyLnQuhO7ekrO+JFDDHo0Vjd42b0bff53Hrs0wuBzDN6Mdm/eQ/ZQAiZ5fLphhNAA7KNFqGm6YGYrPv9tuH42ialkm96f1YTM2PmVFHusY= 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 1752644166093386.2747854428683; Tue, 15 Jul 2025 22:36:06 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ubumc-00074L-Ui; Wed, 16 Jul 2025 01:34:22 -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 1ubumZ-0006sg-Ff for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:19 -0400 Received: from mail-pf1-x430.google.com ([2607:f8b0:4864:20::430]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ubumX-0007rp-Q6 for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:19 -0400 Received: by mail-pf1-x430.google.com with SMTP id d2e1a72fcca58-7481600130eso7021023b3a.3 for ; Tue, 15 Jul 2025 22:34:17 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-74eb9e06aa6sm13790162b3a.62.2025.07.15.22.34.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Jul 2025 22:34:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1752644056; x=1753248856; 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=GtV+8U6r6cDalyfNmOH/gMyYclw6zVKJEtKnGE3JfIo=; b=BwqBE3OX/04m1sx1qTUD00RACP3t9HygfNYeRSEONXYWOMTVLhNOvjM1+SgpojLSsQ fHKSTEEFRQACP/yRV9UoAVNziJXXZtXIgs92tCZOiDDZiIQoGXhmkwIyAA34vREGr5at OC3FHUDv99q3f1CINke+N2IyxoMr5sDrnijE9eygahNWLB+oHxKZwucK0B686rdgUcxL Yg5gJcipVFEJTHkkvIUa9DI29eDALd+UV0t1b9LHNeFVHsBEu93xMeWalhZMzTWtgVdA 5TuY1sTGzWnysZoXIzrP0EJRzJpACdQYrX4lCPKLHAKzMOxUvppHvjCdnlaliP6/fdVg oYQw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752644056; x=1753248856; 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=GtV+8U6r6cDalyfNmOH/gMyYclw6zVKJEtKnGE3JfIo=; b=R3+bkSSx8vWvV0LbPJlQRNZkeNEgiz76GQIp9SUFI0i1+9ZZH23Yu8kK5bzP8eqbMv aL919AceLJFnNRzbe7xkTaUvBbDRKNw8QrLSGaHh5KP5fZXZCGxsLlla2Skqn9IIqzai He4jaKrrlOBHP1YkBetbpbLGjc9tvlqU5UgjPFP9s89EeNZB6BWWzAOzqDAQH4cXsu2P rDhBJVhPYTMobyglnxUv8Fj+5cTLfU7mlnlX77IYRucU6U2RVZ1mCdjpWg5a92jA0Udb 3+cLf5Y2g8wAGv4LbRvdayejrPozlHrlYUd2BQapy8kxu29Xv4z46q16ncoi/0bAjoXM ePIg== X-Gm-Message-State: AOJu0YwdsTNkkh28kEMGFhm5a+Pv41DpdoOYZibFGGr0HS4a+86Velxl g/3Ghxg1n58J8f4jYLDXAFPD5Xgo0LKA/P1/rXPqt188pFEmGqiIo1fFrYERQ7K5XctLXaCD4/o fFmf6 X-Gm-Gg: ASbGncujMuArvVPC7KCklmGKoDFG4zl8kIoQmJg6sOmH1eK/ADkNCD+451xMTypyqQf y+hd5OeaicbC54QneOAtPRnMf0RxBCK85bDFk5DtKr0hfHivWZphndmu6GYSVPfD32mhiJ9U/jg Zd0DO004rkq1O+ucmDgQWaEMDmlQVNn/aKM3TpFjlEPEBIK3o/tuEpp3mhANX+vTkJXT4h6S/Oe CxjJJWZUlASULMUxm1XkIgDmLpt3sOBRKee/jJs0+ntXyPuns5MaasQ7EI4SSj024aj6oPq6tK1 IfNqJvSWrlufUv8ndujgKb0Z0nnJZ01kx9oQGpxGCesJ9+mdpch7nLvoy2YYRvPEqBFuSELe1Nq e5y3ijy9OVXlO+W2qLjQdAQ== X-Google-Smtp-Source: AGHT+IEzzm59LKTcy5TIr3J16GZfmyTzdXLT4TOGrDFr0a+Jokyn9yihXZqCJDVTaVIdTXf6pAHLvg== X-Received: by 2002:a05:6a00:2e92:b0:748:ef3d:3247 with SMTP id d2e1a72fcca58-75724876e61mr1791665b3a.20.1752644056113; Tue, 15 Jul 2025 22:34:16 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Alexandre Iooss , Peter Maydell , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Manos Pitsidianakis , Mahmoud Mandour , Gustavo Romero , Pierrick Bouvier , =?UTF-8?q?Alex=20Benn=C3=A9e?= , rowan Hart , Richard Henderson Subject: [PATCH 4/6] contrib/plugins/uftrace: add timestamp-based-on-real-time option Date: Tue, 15 Jul 2025 22:34:05 -0700 Message-ID: <20250716053407.2814736-5-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250716053407.2814736-1-pierrick.bouvier@linaro.org> References: <20250716053407.2814736-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::430; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pf1-x430.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: 1752644168022116600 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 | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index 58805b7884a..7bf658311b1 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -93,6 +93,7 @@ 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 void uftrace_write_map(bool system_emulation) @@ -445,7 +446,25 @@ static void cpu_set_new_sample(Cpu *cpu, uint64_t time= stamp) =20 static uint64_t cpu_get_timestamp(const Cpu *cpu) { - return cpu->insn_count; + if (!timestamp_based_on_real_time) { + return cpu->insn_count; + } + +#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; } =20 static uint8_t aarch64_num_privilege_levels(void) @@ -849,6 +868,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 10:54:09 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=1752644168; cv=none; d=zohomail.com; s=zohoarc; b=XrDyfsBt8bAtefS6utD3Z3e4EA+7dEJGauu9uxEFplhymbh+ZBPyO7eQJWPaPEhaWanqjsl1+7C8DIPQKqM528Y7n5t5ZDdKDefBZq0auMF/RkyM5X4yXHnFmMNWClfIOVhv5+ohTK+phpdiRidPy/chDXYpTag2PKMBW++q91Y= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1752644168; 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=oIQVoD2rGzW/sMcxSK5TJEUHotHPKo3PfvVv5i/8Bz8IkfTgzsGXFgavEJvbGgIKWLJPaaze34MRmyVo0Hng7p6CVELSOriYw3pyOx+89twvRgo9vpsOAMcXKyiVl2zqs/9tQmvneLPd1JnxIi08eFSYuUtBUYLIKgZf35s6maQ= 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 1752644168492905.0052882627572; Tue, 15 Jul 2025 22:36:08 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ubumg-0007Kl-1K; Wed, 16 Jul 2025 01:34:26 -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 1ubumc-00074F-Le for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:22 -0400 Received: from mail-pf1-x431.google.com ([2607:f8b0:4864:20::431]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ubumY-0007s9-K9 for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:22 -0400 Received: by mail-pf1-x431.google.com with SMTP id d2e1a72fcca58-7399a2dc13fso6625387b3a.2 for ; Tue, 15 Jul 2025 22:34:18 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-74eb9e06aa6sm13790162b3a.62.2025.07.15.22.34.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Jul 2025 22:34:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1752644057; x=1753248857; 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=QXW49XV9w1cdkc1JEIlcgB39cT3cQbKCN0Sam1Y//RC9dIESAsrbeg5AwYH/ITqHPQ 3XHi8cWhUGUWcGl6S5/uJj2jpj39FV2a/EN/Vv5DX8xagD4vKulqOIT0PcoXAqIeAJy1 48gbmzxfOTLUNyM5rfwIjrUu3pEZEFNlBGe8/KywTa32+qNWSLrCFS9p3w+S57YCj/E1 TUzMsxlrJjZI/Zvo8iYH/CPuVuRvRPvrk9Qja1V5AnApV+ONscE7277fhGeS082siE2n S0Q8MCWkEO8TbDuUGoc7mjhqRYARpC12Qsby5EFH6b7gdjQiq1rCOVJZOxLYQDQ0Xpgb 0AMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752644057; x=1753248857; 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=uZ8/rNSaKG1yb3Uf6QNXx5OER5+kh33Z/drc+9kzcwgQGQ67bgBXe4CLMvGo/4JKcs WgOVFswrkifnAzZ9oi9q6cAR6QY8Qr/oJVvv7Du3aSd/GqML1lMpyQ+yaUshMFvXdKtB AAamHOGLHw6/2J7dDJ0PnLwA/ewgOUMwnFX61p5g73qtGQvLRi1qmPhD1iRRY9CJrvcK hlJNrmIla2mzGVU3BNhFGb48w1nqPX5lQh78tA6aKACGP1u5Iscj6Dyyz9PH/5qwnIPa SDNWN60cOq9FO1azsJuAIaGWOy1h23dgAxLPjFGgs4o+zYOQLN90hKde4dmu7OH2Y4WJ Ln9Q== X-Gm-Message-State: AOJu0YyCEGHlPvjhmILynT5gxOQkiuEXv1Sib103ZPph3JEbJvmHTBxq ssA7wY5ZSEnb+6hGjTzMLhulcTuIMDn13dhKAOWnQq8G0Pe/6TEwJqEa10BrCD2jWTRUikSD+tR UsqN/ X-Gm-Gg: ASbGncsrpBI3ukLTEBEjLSrPctEiFuWXmCyTVUC8fmWRFGjwDHM/r4E9FYWqiDadKUM g1RliLJDJCi3A6WFvzKcvXY5yxs0px7gJSpHV8i7Seo/fkygtQBeSTIMej+16q7SCCeewUQyzOT l2WVD5VykRJXfVNCCSxm0XFzEs0wTPKDYWL/Cp4E1tNwlRAazuRUR3T6YGDjWf8mCGa+8fRYpvw m2N6tm9fc0hA3r8Gsn95F4Tsb4rGnDWEjAFu6p9ER/15Cb1QBog8Yfr9vOElmWsNlcBplceMNAA cOtTnqpcxerSWU7hW8N+lRorSPAmwMrT745CBTxJUsPeOmJsq4zAe7wqxR1xUvdc8sS3N2u1Hsd jEc99BfxvKbYu+OmX/GjdPA== X-Google-Smtp-Source: AGHT+IG7It6557I6UtY2Dczgj2ZS6s0XhsmYEL1MseYi71+gDXadyZ0K2zEOg15FPUajsSq+ecjnKQ== X-Received: by 2002:a05:6a00:244b:b0:748:eb38:8830 with SMTP id d2e1a72fcca58-7572427ae6dmr1673687b3a.13.1752644057071; Tue, 15 Jul 2025 22:34:17 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Alexandre Iooss , Peter Maydell , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Manos Pitsidianakis , Mahmoud Mandour , Gustavo Romero , Pierrick Bouvier , =?UTF-8?q?Alex=20Benn=C3=A9e?= , rowan Hart , Richard Henderson Subject: [PATCH 5/6] contrib/plugins/uftrace_symbols.py Date: Tue, 15 Jul 2025 22:34:06 -0700 Message-ID: <20250716053407.2814736-6-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250716053407.2814736-1-pierrick.bouvier@linaro.org> References: <20250716053407.2814736-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::431; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pf1-x431.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: 1752644169782116600 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 --- 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 10:54:09 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=1752644147; cv=none; d=zohomail.com; s=zohoarc; b=eK/2aDitL2EUif1eIVAILkhwhUE9m2N8Ka0rcz1HDRoEDamzYhLSF/dDz/FRAmDPqznyY7fdkYOap4nMB8GLBL/sPbPxyi4xnet5zMjOyvLYKXFGMM9fTXdDZMHCgNUROpGQhr4pCHr/O87Yvu4secJ6D+lFuFUlK1x6SMJgXys= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1752644147; 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=wasM4fPfefe8yCV5m/8wwSU7LP5OnhI1b90/+w6CwIM=; b=m4g7NWsIO0bPaJxxeDnVFYV+w4u1l3vUUFKbqGG4lklah7+eYlmWPVFZdKgJHTKBQm5GELhiGWa3jBmnhYvFC/T6N9+5B+VoTX4sAmroPZG0LnhP4pVKtWkY+Uwcw//9XQvR+9S7gQg0k3Xaz4r014IfY32H95dFkSV1md7SPzo= 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 1752644146995115.50936109066004; Tue, 15 Jul 2025 22:35:46 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ubumf-0007Fb-3M; Wed, 16 Jul 2025 01:34:25 -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 1ubumc-00074H-Ld for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:22 -0400 Received: from mail-pf1-x429.google.com ([2607:f8b0:4864:20::429]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ubuma-0007se-1e for qemu-devel@nongnu.org; Wed, 16 Jul 2025 01:34:22 -0400 Received: by mail-pf1-x429.google.com with SMTP id d2e1a72fcca58-748e81d37a7so3773057b3a.1 for ; Tue, 15 Jul 2025 22:34:19 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-74eb9e06aa6sm13790162b3a.62.2025.07.15.22.34.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Jul 2025 22:34:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1752644058; x=1753248858; 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=wasM4fPfefe8yCV5m/8wwSU7LP5OnhI1b90/+w6CwIM=; b=dAoPbp4od4qwcnwvLqWddaPVBUtSdFd/yItZRd6IjEDBv94+9YVj0FfPTr9+/VZ86j 0AdbiTUN3d7wi7zdEuDgOa+opILf5LdYN0IeJh/cvovN487hgzzVxvWj9wk9vRTLjbmW rtr3POs9zqa2LOp5pbEJjVEiYcre44PUvKMOLjGOkQcWrAuQiu295JQEQYCF66QF0pjs drjKORnS/1c93wZjf3tZVUDoJ3QvurwqYirlcMouSd7CCfgraDP62d7v7DFFo9ERoeIb uHWUkVnFRK0SvY1taPazm7V4z05kx0A1o/JRVD3+asC1aiqRJYKKtj6qnYkgfirMzr2I LMxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752644058; x=1753248858; 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=wasM4fPfefe8yCV5m/8wwSU7LP5OnhI1b90/+w6CwIM=; b=igAn3gQHfjgKI+n19hdWs30y25PXi/r5IJXPR9AwawxCD+gM5aixkWluStcvWDhFMD 437YgEYwyx+n/fA5FgkmiuSGNOBBBbyAX8480jJ/1kdORIYR7RIgH9GDY+sYYrd9coj+ vUSafTospAP378vE/FdaBoPYTqISEpQqA8RcDDfFFEoSJZvpZHttVJTyyxCtUxfvctr0 XXcJTbS/5GoRZJQgLa6RdinzDHihO/RoxR3Kq+MNfzJjeAHKuS5/X/4k+6zkVzBuh0OC vXDa7fW0YopdWWwANMs+vksjidRlJHp722hMSlZmkfPr0VqGU99GdXCUt9BzcOGEg3F+ aHmw== X-Gm-Message-State: AOJu0YzIKN2tIDu7b3wR8hPYXyaeL+1Tmo6uhdlp6N647OjEfXs0ntjT 2OPbY1+o9cTD65UlKpeOWVIaeLN1u8fgRox9+Pusz/bN9utAdmQyd568DqW4Vws09bB20hVpWKv UXtp+ X-Gm-Gg: ASbGncunb/vm1mql8pi+YX86awxnO1lEa+5LdHvN5FzMsv6oMzEzLVyT1tFD3k4xlG9 F7gUhIwUzCdbm3aPpJNbewk4Hn0ojYx6pnkpx2YxxQCT84Vq3Rk2zn2fsUCtcI/rNUlxNzRSgQ6 RQJOBfE5bkN74j/OiBJQrYYEaN41KMYZ0GYPYxXJv/4f4v5KGA5J6uDCklpWcQKP+LrKu/+iDjP he87U/sAqdHhBMjqUeOudoFIzfL/D5lcNr18PMSL0YZujrAJrN2Wwa8IwCq44GJp/mkC/TxlSuy onlnQoCi10dh64k6E2zSOQpV0+v/KH0wFzCZ03yGJtKro8h5Ni/0ve5XVlKKCh0fBfrwOeW2l1N 2lyQOb4nC7uSiFLJuMcVazQ/VX3XHQuxv X-Google-Smtp-Source: AGHT+IFLwHudL9d9qW6U7bWLmI6I4KSEOMPOt2iTDvqWbbXbh9ySEdZpDRhRbczUuaEPDf7pHSTRgw== X-Received: by 2002:a05:6a00:b70f:b0:748:e150:ac5c with SMTP id d2e1a72fcca58-756ea8b92dcmr2947582b3a.23.1752644058042; Tue, 15 Jul 2025 22:34:18 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Alexandre Iooss , Peter Maydell , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Manos Pitsidianakis , Mahmoud Mandour , Gustavo Romero , Pierrick Bouvier , =?UTF-8?q?Alex=20Benn=C3=A9e?= , rowan Hart , Richard Henderson Subject: [PATCH 6/6] contrib/plugins/uftrace: add documentation Date: Tue, 15 Jul 2025 22:34:07 -0700 Message-ID: <20250716053407.2814736-7-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250716053407.2814736-1-pierrick.bouvier@linaro.org> References: <20250716053407.2814736-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::429; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pf1-x429.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: 1752644147968116600 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 | 184 ++++++++++++++++++++++++++++++++++++++ contrib/plugins/uftrace.c | 2 + 2 files changed, 186 insertions(+) diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index 456d01d5b08..93921b12ae9 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -816,6 +816,190 @@ 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. + +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-binfmt`` to make sure it's easily reproducible and does not de= pend +on machine where you build it. + +To build the system:: + + # Install dependencies + $ sudo apt install -y podman qemu-user-binfmt + + $ 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/. + Other emulation features ------------------------ =20 diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index 7bf658311b1..a040fc6ad1f 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