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=1753160248; cv=none; d=zohomail.com; s=zohoarc; b=i8gMZHCsiSv4Zr7qyF7ion4rDx5atL1icJcxVN2s5At6ijo1029qR3lrhy4uJtfMDdowOB9tHzr7t+wdNRwYR4vKdCeCNV5ss+kzatEzvzRR92j7aLXZce6NHL4JyWuLmwFGbH2wo9d8rprnIoM87uN9ySaaUrrZ9vtyB54KJfQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753160248; 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=XRO7ghqISg9FEfPaVIFWy6KJDuJuwub7QFRtV4YLxpFm4fNiosAwKm2o1occdKVLi7J4dS5ZW10LnmE9bauVbc7G6N+teMSuyzkKSvTqSh8MqsD6H6J7Qvmi9Czvh4RTqeH+5ddxkp9su1ZxuPdPptTTBrf7rGtWr0sWQ44Uz0I= 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 1753160248797799.7693315152429; Mon, 21 Jul 2025 21:57:28 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ue52o-0005ka-JQ; Tue, 22 Jul 2025 00:56:02 -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 1ue52U-0005Ze-7w for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:44 -0400 Received: from mail-pg1-x52f.google.com ([2607:f8b0:4864:20::52f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ue52Q-0004G9-QM for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:41 -0400 Received: by mail-pg1-x52f.google.com with SMTP id 41be03b00d2f7-b34a71d9208so3447731a12.3 for ; Mon, 21 Jul 2025 21:55:38 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31cc3e5b038sm7065311a91.10.2025.07.21.21.55.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Jul 2025 21:55:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753160137; x=1753764937; 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=XpHGSqCMvXAqmDkiivCg2pZZ73GQK57TMaSdvifmkTcNUM+kxHtK5jn/SEhIS6JIis FdRqZ0f3E1OXBjD4M2ZCKM2kLcHQiW4ToTLQHvT5X9V3apodHTWsVCWKeTLP2ljGOnaP rA/VBlNWwg9F8gau7ozEnn4SXS4H3fDltJEuFbKb7i7fYL7bb2aEYcWbIXBypPULcHDm BA1Un4D7HoP7pBeejQmpkHcobYgKgzCud+EZCaUQ5PpzyIcy9i1Ta4CgOoInJksxOpma entTGHeej0uW5O4wYxuwT6pgYl964ENq9RdPbJIN/FdhWH+m8oghNAgnVwwInT8AD4mi kmvg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753160137; x=1753764937; 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=uLeJdIzqM2Ny9nVsT5RDFbg2uuhw8Ehg/v1DaJYV6rG6EH5ypUoLdE4nHKDOAXtW5T l/yQYWU3vhsRuM2JC8YDAsvx6hArBjejd+K5SOOAv/kny+uBM7vxvEdJv3umf0R+h/Gw uqOUfLAlmpF744UgmoiRPrVoiNO5EaC+yZPHhXHLwjSgtTw6OuYnO5isYemteevIFdIT Tq6KXzFEHO/HoPw1qdtusObuskNVQg+qmQLhNPqtsXv8FyWjeokjR339h1R/68oRtpn9 XGRfRyd7ZYVUx8vwGluh6E2VC5pp2m8cvXqTdZgXC7w0l7t3lZDpmPq75utYNoirfkSY 9rJg== X-Gm-Message-State: AOJu0YzujjP22N2wd2HwF9glcMnX3loefTKe4TryCWqQAskM2ZhgHH4S JuX16tp5Ns+LB8D4gyFklpIzdBNEj9hQSmFMRPmm1RLrPold4HvST/EjeSS3eYBzSq5Wrs8GMU3 slzaa X-Gm-Gg: ASbGncs2tjfVmSOVURIZqLokwNlO8TUMAI5HT7lMorWMCXWGswqytJmEq2zunR/Be/l c4xIT8VXFLTepSHYDPG4WA/vU4g9ylg78stwrflkN3X+Zts8uGv/UWIYZRSk/HFBgwf6hTVAIdK C5bov+lPgZ7K3XggRv9gLny3Lojm8UDOvV2u0s7jSMxP/p1phYqk9gWp1dymm+LEpdO330DuqLj HPnvBiF1cKFWEf5hhP1LyiaJgI21+he+H9CPHts0r3RKHN+n6+A9LSfFOjlEHwdociU16KqvkcZ ROzdC5E/hVJ0gNgXQERQUp19iq9dnGnU3HpMFa1mLUkQGJGyf3w/3ol5i75AUTIaQxmG10kzMph 1rh1AHrC3LdO7kt3mIhV1ew== X-Google-Smtp-Source: AGHT+IE/7HgQ6HuJRKfsHrzq/FaNedNgCLhQaUsJIo1XdZoqqPIEkN8lmYq44h9jpxvjmIG3HUN8OA== X-Received: by 2002:a17:90b:4985:b0:311:ea13:2e63 with SMTP id 98e67ed59e1d1-31c9f47c919mr29675967a91.13.1753160136773; Mon, 21 Jul 2025 21:55:36 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Gustavo Romero , Mahmoud Mandour , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Manos Pitsidianakis , rowan Hart , Alexandre Iooss , Richard Henderson , Peter Maydell , Pierrick Bouvier , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Subject: [PATCH v3 1/6] contrib/plugins/uftrace: new uftrace plugin Date: Mon, 21 Jul 2025 21:55:22 -0700 Message-ID: <20250722045527.1164751-2-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250722045527.1164751-1-pierrick.bouvier@linaro.org> References: <20250722045527.1164751-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::52f; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pg1-x52f.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: 1753160250300116600 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=1753160193; cv=none; d=zohomail.com; s=zohoarc; b=Bw0zC8P18xCfn6Lyxmtdelk6vV6gMLc4r6CMLZqytYJJ57N6Hm3m6+jIf2IIUlstWUEtknQsmkxS3RtfTUQDaJLQTuIhkxCTpJat8S5KzurgxU8hpBT3Q9CgchGqiiFe7712ntWAJt12hd8VywdF9deID+XpyePZj1YzQdWICOs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753160193; 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=HAF3tIGf3KttfI0zfvBa4x2aaFyEjuJo7mqXeSQ8L3tlasGTB/+ecdeg9VJ90LKFPo+1yh8FaUJJfPx8fYB0T0w3IcF+J2meVYHRjyBgGcRfK82ntg/CxH01Zq0hkwI++OPerr/BHCaDw5kVTWWJVg36UclOwm6SNMEGYYJtlBs= 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 1753160193735581.5378184842156; Mon, 21 Jul 2025 21:56:33 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ue52h-0005bc-DI; Tue, 22 Jul 2025 00:55:56 -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 1ue52T-0005Yr-Hb for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:41 -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 1ue52R-0004GF-5i for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:41 -0400 Received: by mail-pf1-x431.google.com with SMTP id d2e1a72fcca58-747e41d5469so5416137b3a.3 for ; Mon, 21 Jul 2025 21:55:38 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31cc3e5b038sm7065311a91.10.2025.07.21.21.55.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Jul 2025 21:55:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753160138; x=1753764938; 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=SE+K72VBYL2UohlX/kGC4cI+IXvxm+BxMbnEHyElXFQtt/HcN2LRBgch3K+LXSzPuu OgW+BFfxSl/4ucF0VCpGceI6/WQwYaOEMn9aOEr1szuYsTlmB+nyUO5MagncJWHMXx5+ nHoMN7BFOd8eB8vlSJqwxc9n9gshE51+dr+FTSgk/prSnOGSCRLOCWVbd7178tjyrxDc kp4viMgg417FEGS/LP7ExFG+QDktrWl3zGrmqjQOwaAwFYJxFTa+7fCg6Wdf60lhKaBj Wp5Efu9bjKkKdisD5f1fubhYKbopC30b7G2KV140qR4oY8trrDF9w6DpPFdxn1swDHfx r2lw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753160138; x=1753764938; 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=BSj2oHsHrGTTJ+HtuBr3Ezy6QsbKSDQ9HbXvuwuxwn1nfepuD7KPqlKvF0r3UhXjtK MdKJg0EXHROWzZ656E8srHzeCWqCTIIXdx8Q9HQz/c9fppNqVNgZnlHmcouCJsk6Vrn0 ZtojfPkcussUI2JGQBeUyzMNTiTXAsv84YXqX7sIbpCqG94ZBwWIUl+Dw1BVw632iwn1 5Lmixujk9sNGkeeURZv831EbKQ/MkFqs1Fh7B8LicgVNiVQA+ZV13MSPNx7F82Auvc46 JorLeZ0gNkr8BPaUwIW/szrAFO4Tod52G1NkibeZkeGLS2R0G0mW2LTZGX+lsQOLXu/I GOkg== X-Gm-Message-State: AOJu0YxoQy8JUw0H5NnOyyXHar9q04kzZ1lcftpxqSRZ5rJvldU52wWb QgFvLHHAp5dYFWRNyoxm2MSMynlmtRMfkmAEvdWxahg53aOzivur5eAmqb6XBnhn7LUxJBOmsQ2 CckIn X-Gm-Gg: ASbGnctpHWABDhb+56tltBtDKc5gPPy8eip51CCrHsG3HXIOskYCR/wa2hZwUAWNuI2 7uUWBOSgBacm4VEIHij8PTumq0R9USRL4CiJ1lKRWhkM+ZMKXxP3RYVX7m631mruicBmtXs2vbf Dc6GtXno85vvvJfRjq6CbK8bd8mXqQVtUIKPBF66CYnpU+biappIxPKYLmEeeAHtIKb8bZ5ZJZ4 NvxuPNhlYYH2RRfoIkW2D/f65H8lWP/JDo3dQ4qvEYQnqYlKlfMm5C184KWouD8IrLY6Dh607LY bPoZjHrafTnN88ekgiqEt3TVckfU+gnBsBgRGPewwsjtiJQLN/lPnf84mxPc9vEnOBL+kNHWdAd olV2tcjUN6OlWFXdtpJmkQw== X-Google-Smtp-Source: AGHT+IG+0QzQH0+l+E9PIDtaRMy4Ey2eZkRjqrWSCJb95s9Rj+QrURb7yLdSL4DNpqy5Ed4hhZk88w== X-Received: by 2002:a05:6a20:4324:b0:203:bb3b:5f1d with SMTP id adf61e73a8af0-23810d53c0amr33099499637.6.1753160137721; Mon, 21 Jul 2025 21:55:37 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Gustavo Romero , Mahmoud Mandour , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Manos Pitsidianakis , rowan Hart , Alexandre Iooss , Richard Henderson , Peter Maydell , Pierrick Bouvier , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Subject: [PATCH v3 2/6] contrib/plugins/uftrace: add trace-sample option Date: Mon, 21 Jul 2025 21:55:23 -0700 Message-ID: <20250722045527.1164751-3-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250722045527.1164751-1-pierrick.bouvier@linaro.org> References: <20250722045527.1164751-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: 1753160195585116600 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=1753160216; cv=none; d=zohomail.com; s=zohoarc; b=fipIgMlfognKHnbUMC0pA0yc5r4RIqRmZBlN6PJT4R1j5tK78vvswKzHqgVLBezsCi1HZHWSiahEfV9haPdi1L9g2EvW2mqj+uwP/wdDYDXUZUvCRtbZUxcxUdF9i+pk4MwiGPkFSeLqZ3sbZc+S84jbPNExHJPIVjTb4Bpo0GM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753160216; 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=bmbgsvlSPd6sFpIgK4uvAKju4iPxQT/MfBcanyzj4FxHR295SKI//D1mmTufgN+9MOCgkUtFVLrE7rKJD6PeVbIHj93ronG9hEB72uaKxEfGiOEiUNHS6LeQtZ5Veu7606Jy+K13MZo0b18LxjqmNJBmFEU/SmBfcMLsKMshbxA= 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 1753160216837802.1597057450902; Mon, 21 Jul 2025 21:56:56 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ue52p-0005lj-TP; Tue, 22 Jul 2025 00:56:03 -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 1ue52U-0005Zo-EX for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:44 -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 1ue52S-0004GT-55 for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:42 -0400 Received: by mail-pj1-x102f.google.com with SMTP id 98e67ed59e1d1-311d5fdf1f0so4028457a91.1 for ; Mon, 21 Jul 2025 21:55:39 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31cc3e5b038sm7065311a91.10.2025.07.21.21.55.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Jul 2025 21:55:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753160139; x=1753764939; 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=w8awkK1HOb1nVBJEj7jjU9P782pHv5ApcwV1dpGiELrjGU2giE1a5NEQIMLXnWpf8K KkGTALkYr1rdMThv611M7oEIUAMnIOi5vhSt/amNWHZwKCzdB88u9tymB2eCJK9JXEqW n7v2EIPrHoOnl9/j9d27YTZHsYZOKRfnfy/m+Ew4ykpMRaQi9XWVJS3cKbKouh6JSrdl O68pNlRoTPPyN9H3dyJ0Mpe/SvM4A+KyiF5RMraQYt62sUCqzG7xkRylzUqJ92BZ5ZUP Yreyv7Gtkmbl9MDHERyCgZOpfPn41w1jFjwOZp9STlG3msxw/dFxuiOzA01m1ZSj1ilJ 21wg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753160139; x=1753764939; 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=isvwmiW1V7912qjAPNDk3SarNcRzFqHSSiV1EZzN7f/kEfRA2dq7AAa6QAw2P+ChT8 lxSfpwXYEA+QC8ABcpFik6Z2OquHPwwM5Hh0C+qBLSkVrjAqFCDx5XD9qUcGoMk0PqZ2 h0IFsVKJqxFNxKNRxBI100k7raaswxsG7CEyZCndP25jc2JnklsypTyYk5nG+LRuFhMj 9g+pJV85ZA3naeXfLuQskauY5bF3TSbCv1jPT6ChVHqjqkFGdGublgMbz1wMdpLF1Wm9 UCAIQ4KV2otGX1alTTCLT442zHIVzkwXUCVbf+esWP5jwwFLQmFzAJdSdfb+OJyrLBpx 7piQ== X-Gm-Message-State: AOJu0YxrmR02jAgkm3sxXS3BMdbR0ogD4mbn9TtTLZWuyxm2e9rRrAcy n0l6E2WnWird0NtIzd0xHaMquQsbpW14cvSmPSjMvco9FksRQqd3I+IGqmPg0Lp8giRKWgoMPuC oB8Pt X-Gm-Gg: ASbGnct93uTMZg1eF5AfPMJNC0lyYOtakpveg6mAVATBmw2O6jRZ9wujjZ+WtC/QEOm Ld/SSL8tlQiFzfKV0kotFMvD8Thn6gO0hNCVskAgzKSHNEKrELDx6k9ZdCxM41M18QVNnXEIZ5E 6jJArP5zoDEnaoyEGdXCvc03iBkhDfFi+AoTRjRZTnqyCoum4y4pwcUJYLOYgIX5o0fwe7qUUl2 05X5wyYfC+WO+Wd3p+TwyvzJg6Ny55ymGipN7T3lIsSxzIFTyLr/FTcpbhpuNJVJ2Xw/k7nIPL1 Muafn6bLY8RNLoeDwUaHLf8dIu/6KruySrgm/8eYiRApVgTloTkV2dLrm/Kk35b9fD/Wy+C90vh +qKbxN+lHGQ2fgRjdu1SBCA== X-Google-Smtp-Source: AGHT+IFBedV0jz1lXe1F0JRu05rpCZUvGki7lhdk7wY8AuXRpiHuRzT0hjyghXWwYYJ5gXh75W+4rw== X-Received: by 2002:a17:90b:5385:b0:311:c939:c851 with SMTP id 98e67ed59e1d1-31c9f3c3635mr31775456a91.4.1753160138627; Mon, 21 Jul 2025 21:55:38 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Gustavo Romero , Mahmoud Mandour , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Manos Pitsidianakis , rowan Hart , Alexandre Iooss , Richard Henderson , Peter Maydell , Pierrick Bouvier , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Subject: [PATCH v3 3/6] contrib/plugins/uftrace: add trace-privilege-level option Date: Mon, 21 Jul 2025 21:55:24 -0700 Message-ID: <20250722045527.1164751-4-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250722045527.1164751-1-pierrick.bouvier@linaro.org> References: <20250722045527.1164751-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: 1753160217924116600 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=1753160312; cv=none; d=zohomail.com; s=zohoarc; b=H05lEPLiZS9CSj9+KCSB50jFJtgvwbiX6sPg4N27yFCtZ4JDtTDqgbSZ+vVHXt572XUQN4SPIAmx+t7RHYSbJ68qPaC2mTyec2UVJ+9V+FfCIOfEzZOXhhr7Ybafo4iuila4icgm1crapdvSbCcsuz3A0AoLpwyvRLJ95d19fUo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753160312; 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=efcPIPAp+l3HQlZ02vM5I1H69ihDtBBJ/O/CWARsZJjr3wxP4zu//yogbPQkllKPW21PIh/DD2lFX/ilHjnJO/OEMPI7Gaq5l7WBV4ViCfe84CYIz22bCZfWAXnj0No5GrQpMzJddIJyjVlFSwVnFNI2sghHNn0YO0BEKupeCMg= 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 1753160312120216.36388278791776; Mon, 21 Jul 2025 21:58:32 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ue52o-0005jE-1U; Tue, 22 Jul 2025 00:56:02 -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 1ue52X-0005bs-Jt for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:47 -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 1ue52T-0004Gp-0Z for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:42 -0400 Received: by mail-pg1-x534.google.com with SMTP id 41be03b00d2f7-b34a6d0c9a3so5147418a12.3 for ; Mon, 21 Jul 2025 21:55:40 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31cc3e5b038sm7065311a91.10.2025.07.21.21.55.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Jul 2025 21:55:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753160140; x=1753764940; 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=G/yc22ixehPlgLZEg6MPFAm1Xikgx9F4KXyOKHzBNuxCdwsScazFF9WxR6wKFO0j77 7obrH3nhZdRbOb0y+OxQqzZrSXsKLnclx965JQ68g9FwPk/wNC7X+FJPRY+6g8C46gvG hYf58A3Tnt1Rg44Kpk4qLtGphcfpxFVmm1X4JKws+np0pDGnwAHHbqB2VhayOCJxUmEe wYXGIL1/wNL4Epc6PY1pXZm5NRvHNKs2aeM094wQBU2wh5hpvG2yhIootgnVO+/A/1rH E9yEys//DEbAmZnlL34/zAz61xu+fIuGntrmvGTf6z7owp5lE55iZqoRerGGAYlwMvZL K1UQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753160140; x=1753764940; 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=weuuvYh53VK41XGTXhnu8IMqvfjPurjKtb3WLLyij/GJratA0VVukNjCuebpqKrm8+ +w9Vr+RYanb7qlTE94YItZFSeOu029DJ6G99BfulbGQ1zj32NAdikg1FycEIQXmXLm9R 33AaPCK2M2ANA1lwke+drbXEqKplq4klzghFfuDUJGyFhHO4/3gUaVqmSJJYAEkkvD8Y /4zaObyCzb/GHkyRZSKa2WngaVsSUZgX4XD7ItwTZQw29m0A6RV+cjZ45HFWeqhSJVrq Riue29j5LVMzLUOtP0JSdjOj8mSQoa0lL4zZ/VVAvIP87gXDcmce+f3JfHtKcqd1qioq xRNg== X-Gm-Message-State: AOJu0Yx9IQ0VXbeI9HxEEovIvmn4ASaOsyIzRZuThqWjE9gl6rkLtSLB jVQIptZ0MfezOAkc3LBKySQpQrFmdVNtQjp+WOuvoUvdSBdMy3IFtVO0eEzd2kFUurJ/fkzqP/L p+bd2 X-Gm-Gg: ASbGncsONeM4WKjvjK3PLduntMMcI+oI1cnJb/VTOu1FvptNmrm3vny9qVqOkqYJ1u8 iBUQGPaq7wjOtoR1nzc2/I6Gbs5LXA1JBEA7gqkX/fipoMuSVmfs6nYNDRDDiiYpRx/Oae4FNGT f/byAF0/xQi+g2E7+kkUGGEV8TPyiXycy1Sk8BW4sBRUZCXPdAkSuZ2PQD/Q4tl5qcG0e57cZdG 17dehhaIjUwi8051RJEbxbzZTrJI4tZd2uSxJn5ayp+L4TIQpPdVpc7capk26t9iY7EYkZMziJx HNB/TNJwD8vYMMtMD+Atw8lHnFph4J8fJ+Dh7LEyv7RK86B95cKj6N0VwDXTOF6nLUv1K8bedZf DGfMvyIzRCzweNd4EnP6U/A== X-Google-Smtp-Source: AGHT+IHlgmEk3lhAL2fitfb3dr5idsLcP5oz9EnfLvaJ7slKGPk4coF5sss3P7z/Z2QFRUCKQNTsBQ== X-Received: by 2002:a17:90b:5385:b0:312:e51c:af67 with SMTP id 98e67ed59e1d1-31c9f3ef43cmr28364310a91.1.1753160139584; Mon, 21 Jul 2025 21:55:39 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Gustavo Romero , Mahmoud Mandour , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Manos Pitsidianakis , rowan Hart , Alexandre Iooss , Richard Henderson , Peter Maydell , Pierrick Bouvier , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Subject: [PATCH v3 4/6] contrib/plugins/uftrace: add timestamp-based-on-real-time option Date: Mon, 21 Jul 2025 21:55:25 -0700 Message-ID: <20250722045527.1164751-5-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250722045527.1164751-1-pierrick.bouvier@linaro.org> References: <20250722045527.1164751-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: 1753160314517116600 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=1753160273; cv=none; d=zohomail.com; s=zohoarc; b=cNOwla95XojjEQxIfto+BVew2zeVSWdx4W7KeAfY0RZPT1MavErObCzifdiGcfUTfv+AE7qTZNbt/MccVMBE7Wp5Q9uIlfSlIKUy6lbWPlvg5x5n6k6vj9N3++SQZEGNr5yw+gLzkkOnoumnDTH2IDnkiLEV3peAYPIg2COHzAA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753160273; 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=HzzxmLVkmTRSeZ2DsJAw8DZHcPsSS+NsC39tS2bc0MDnDe87RCrniuwUj3Ynfuo2gAncvLkryEF4gU+5pMoa/2uB5Ad5NrRLBSTVR2MyDE3BNdKTZeYf5sSi1htDFwvlzGK0WknoqRBjqowr9PvPWJXOv+5+FaTNDzvgZJXETvA= 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 175316027312712.500046462411433; Mon, 21 Jul 2025 21:57:53 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ue52s-0005nu-94; Tue, 22 Jul 2025 00:56:06 -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 1ue52b-0005df-UH for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:55 -0400 Received: from mail-pg1-x536.google.com ([2607:f8b0:4864:20::536]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ue52U-0004H8-FB for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:46 -0400 Received: by mail-pg1-x536.google.com with SMTP id 41be03b00d2f7-b31e076f714so4280971a12.0 for ; Mon, 21 Jul 2025 21:55:41 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31cc3e5b038sm7065311a91.10.2025.07.21.21.55.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Jul 2025 21:55:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753160141; x=1753764941; 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=oM1DzS0m5WQNiwpd9HfmGnqJ2ZDUuue86MfRRKro4Zi8BygBuYRLBiLigIwcnwJqG3 iY10Jykg9MSX4w/MLXysx7etSM/2+xJpT6Z8W2Dcex6lFdaSJO806mCBvrPL2qrvtfVU rz2VvPyzBXXANoHmyXnigEZZXu53MGXNZ3YOb/4UehU+udWOvFWaYyHpGrsXziQ1gxmu 0MfDoqEL6QozjzZ/C8d1hdDC5fwMSE95tUzXMydoH8DcrTDLODah1MDLHDtReFbEhIpf 8gMj/RZ9TfguLPuXNdyhq2WoHGt50xxLBjOF8oUhKossuOJWKrG6gZJMFo5msyBUg1m+ UQAA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753160141; x=1753764941; 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=v9qdqZn3uX+fuQzR70WPDzK0wNiH5qFMce1ETsx9VOsjkp7JD36MmEWsd39E2ZErkI VNu+U7YmHkNuiFVi1nH1f6AgQWo/Nc4NoIq5jt0VQRurGxiIiJywjlkvZJ1AbkS2prM5 Ql0DQ01g8YHpjKQOlGLwKQvG2AkMiroCqfP522SAywHAOqYwRSv1ppWcEUdoLRRsWueH 7tA43FiW7ykNfrDknHms86lP4iHpZDH/t//g0NYqdqrecDmzZSNngc14rX1ecInJ+kRv 847y8+gRYbeID7Tamvo4ZMgG4gn+RUpyCH8NKfV4EWThDhvHOfMAxqOiUIR407WJdmWb OlaA== X-Gm-Message-State: AOJu0YxU5GUum45u3jAhO0J+wgGiqPGFGzuIGd2naN3CGlqxKFW6HGvA kvzxRd23dKI9VUGTzKEPQhNzbq7vIiK3iY6ZShy+qm91q5uBtQTo6u9qC15lEokVl2WYIvAiG1V 2/PDC X-Gm-Gg: ASbGncvpD6ybpGECP0WrtukRb9CdqPrelH2q+k+kGlGZBilWb5DeobyfXu/t9iEZ5jJ P3Pq6ajIcTRT/G8U6tmw2Dey0nvoBjpbYOT1h6ZYan/7vvIZkR1cKRxhQIZ0/Oq5TFLrZtGxioe GS5mqI5o/i7EnQkng3fO6VP3KlLCKrQyytDuoiPYMSv4wdeqxEwf8XZqDqMQlE5meU7xSV993Av VOVVSTd71lBDS4fN0BMyGfmDTaTwmess/Ui4z3hbFoDZvnQg7zq5i7K5+MdEQEhixMb5z3a4A6g wDXvjgcN6Ypm+1X7GORfACjhGZXAPFKMya9fPBCUwkWqTO293dMO6NiKeS3JVzhqjeFr9DBpJQN SQL+k4sR6k/HUMcd1Q6n+Bg== X-Google-Smtp-Source: AGHT+IEDCuBSbX9XZo4DiCbxY3XxmlhDLekqSzM/TK6YiYZV6Et4cux2UWWGmnX0l+YhE5oG5n9vng== X-Received: by 2002:a17:90b:3910:b0:311:83d3:fd9c with SMTP id 98e67ed59e1d1-31e3dfcfbb0mr3110650a91.0.1753160140513; Mon, 21 Jul 2025 21:55:40 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Gustavo Romero , Mahmoud Mandour , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Manos Pitsidianakis , rowan Hart , Alexandre Iooss , Richard Henderson , Peter Maydell , Pierrick Bouvier , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Subject: [PATCH v3 5/6] contrib/plugins/uftrace_symbols.py Date: Mon, 21 Jul 2025 21:55:26 -0700 Message-ID: <20250722045527.1164751-6-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250722045527.1164751-1-pierrick.bouvier@linaro.org> References: <20250722045527.1164751-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::536; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pg1-x536.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: 1753160274205116600 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 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=1753160273; cv=none; d=zohomail.com; s=zohoarc; b=hyVkYA+UmIuGZQnOBrfdv40XN4yITLlyDcyNLPUBzof7EP+Qvp9tdxoDWziNACmMjgOFk2vbnbuiUR9vjGK1Kd+PIy15jK4oywFVT3hdCP35ofS4cOFLHYvo9qAE3XVp5RhbiWb8Xg1ZQdfMgtIiK6aqIPjIJw3wJPABnYKRSmk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1753160273; 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=cbUhOZQq2YRB6b9AKgGZx5bPDc2l0pdrzRuQYRnj13c=; b=YoQZNMkgAvl9nfBLSUOJH85UsE44y2yDu7HlofCAufPsQd8lroIWhytj0jQ6ithCwUGtFvzNw/rTJQlokBP22Ox5bAr+gi+U/g7E59LlPcTkvFE6nyIDGC22to5weS2MuTdyuVGtfham1XwVtWMj+C0t8PjujPfcIfTIeIDlrJs= 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 1753160273318374.5380528982381; Mon, 21 Jul 2025 21:57:53 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ue52s-0005ot-U1; Tue, 22 Jul 2025 00:56:07 -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 1ue52b-0005de-UE for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:55 -0400 Received: from mail-pg1-x531.google.com ([2607:f8b0:4864:20::531]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ue52W-0004Hf-1N for qemu-devel@nongnu.org; Tue, 22 Jul 2025 00:55:47 -0400 Received: by mail-pg1-x531.google.com with SMTP id 41be03b00d2f7-b39011e5f8eso4324124a12.0 for ; Mon, 21 Jul 2025 21:55:42 -0700 (PDT) Received: from pc.. ([38.41.223.211]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-31cc3e5b038sm7065311a91.10.2025.07.21.21.55.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Jul 2025 21:55:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1753160141; x=1753764941; 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=cbUhOZQq2YRB6b9AKgGZx5bPDc2l0pdrzRuQYRnj13c=; b=r2cwkfv82yXgLx8WtR9kyAhovUZXZZYBRD9BBSSXtGva5PeVCSALagNThsCAI/1Ari JinAUr7B6yshlzIMgoyfYaghuHYa9soLmVvxEufcVbRMjfen0m2VkZFS7d1hk3LuMM4b K6dO2oKAsZwg/EId5jZB2AELs7gMsO5vORohV9fSiFuiYUvhUFjK+jTA6cZYD7JSScpx ZdJzsXldQA3ARoaP+dXNhOaBsUi567Aa3fJ+ph7budp5mh9hs9C9VJFZ/E1Qltrxt2kh jJbMEWQo3ivAKnLbeh1OqPP/JZwLZWtUg1pgVEwV/y5wPFGkLfi+O+49c0/2j8grTTKP s6ww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753160141; x=1753764941; 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=cbUhOZQq2YRB6b9AKgGZx5bPDc2l0pdrzRuQYRnj13c=; b=isurdi9Rf1eHrnxfnONs+wdIBpw2v2/WoQEu3eG1o+enDX/8EqE50IbcnKsGVJ4e8F T42Ux5X+EHvs8QdaeqvSjRCRQT0rb2524O3R87DJxMys6WiB7KFzlAxCoE77hLJRkTzp qrbhccn8wax6r4hZpFB1k8y7PlMyEHIcXepAHZimAB3zjoFYffGfDshuZMHYd4xCbb6l kdyLGqKpvCmERrUQK5TfJe8kOJ+CXDV0ZcC9dam0FEtkDS74M9tOG7EJmxpw0VWUpLmd qfOudE62se9BkSRpQiNElK+9gWWhcPLEd4tHybcaSpSC/oI7esS5sr4N1sXeCVBrcpy4 QZnQ== X-Gm-Message-State: AOJu0YyBn8yQgBFIO+YMwIoyr4eb7Tth/skQCBl9Xj7aNzOC2c4XTgfN yAw3y/0rcBVPz9wr7dl/jCZacm4k8coax/kmRCJv9+YcGiAoIDQhF46o2T+Xqap3PEin+/sussA 635s8 X-Gm-Gg: ASbGncu46BlLuf36w2OEuxDcYFnsE3PikLXefP3wJxZIXc4bJsaYomcV608EQAxigs6 hlIjrUZlNTa4ORD8qG8IaVhOJLecF6V7LX/CThFGw4+5HtWP6yl5zKl+VqnvVkGLUQPAMOGsLIz lXrcz83IBxPcrp9ieycVKt+LfWK6TIuLNkb+s9zHM+Cvnc94UTC8LdUm05A4DvIXigUOcHc+cN0 v5nS+7kNAst99ZjcpNDdHkS4B4ygqikwnwAudqkxB/nzUOo+viO38np7RUu8O0K1w1+zRFn6aKY C8NFPu6f2c1naw/2PUNS9EVK1NxBPtpOOYr62spze71Aa0nKbRX9QhXmb9n4qk0XEBZlxLSoEWO 3MJYGysoyeLUzl1tE+LSdBg== X-Google-Smtp-Source: AGHT+IGE4oCrog8W3Enl2weKP7FuaxvuzVSwJieCzi1FUL7PIsU7jAl45fOMfVfcHD66U1Vip967dQ== X-Received: by 2002:a17:90b:3901:b0:311:9c9a:58ca with SMTP id 98e67ed59e1d1-31c9f44a3d5mr35193614a91.8.1753160141416; Mon, 21 Jul 2025 21:55:41 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Gustavo Romero , Mahmoud Mandour , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Manos Pitsidianakis , rowan Hart , Alexandre Iooss , Richard Henderson , Peter Maydell , Pierrick Bouvier , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Subject: [PATCH v3 6/6] contrib/plugins/uftrace: add documentation Date: Mon, 21 Jul 2025 21:55:27 -0700 Message-ID: <20250722045527.1164751-7-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250722045527.1164751-1-pierrick.bouvier@linaro.org> References: <20250722045527.1164751-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::531; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pg1-x531.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: 1753160274250116600 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..059ff7f61c3 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-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/. + +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