From nobody Wed Dec 17 10:44:59 2025 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2991B2233B; Fri, 12 Jul 2024 19:09:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1720811387; cv=none; b=WF0jDkX2wMslN5i/s/Un4r3J+UzzX0wkks0Cmgk1mb8LFr0q7i1LX8uCG6fSMMYSsGOMD2EV3DnLSPSP1oixWGj1ercENOkNIAxO280xRLpI7Ocmngi0TyVC57F2IsdKo15wqYzScIEc/XlgJ8eYo4V7ttjLs0ibNDEvfaeznqk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1720811387; c=relaxed/simple; bh=RrdEr8ikzNDKuLE2n6TILUjmX2xJpTkIICHWolwlCVU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TJmljrXy12ZUQ4xnw+anCh5Qo1FkyqEI5lsU3b9JiwOXsqFIwICBu9tatMqMaxZneKROeAzgM6r+TwZNCOvPcepoAQ+FY2DQXFabdti5yd9IpfCCWTI2B5K3k7QRCCNhhcJKcICglCK1PPBYp3aOFF7wfEk/t0cxw6CRJAIrnfs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=b8FvcVlH; arc=none smtp.client-ip=192.198.163.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="b8FvcVlH" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1720811385; x=1752347385; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=RrdEr8ikzNDKuLE2n6TILUjmX2xJpTkIICHWolwlCVU=; b=b8FvcVlH63vI+xpie7GN8GzsT1msLjnZn3KRS5+JGiy7exAJoscR3CVr sHp3t9TL+XwQ2Zq7e9q+lBtYOlHQucDXU2mUd9qEbAdUiFswqZCeVrW2T kADqpFhy4JZ0I8JDH78UPnBfAADWBR0zOsjoq7Tf9f6leNYQOfvA6/xdt NsaKMma8PWPJfhsm5UnxYQMa4dxby1nVgA6yOB2Y7jpW7zD7a3h6d62Up eJQNBOoTO46E5RsaYMudLhZFcbnWM6c6FslkwLN/zMtW8iHkVHSk/B4Uo fyf7qPYn6xuGmcWQV46qhs0+z7KKCwAd83tr9tdjLdzPzzKImqKXNFjnq Q==; X-CSE-ConnectionGUID: 73eK2O6OQ1ioQ/7LPMyHRA== X-CSE-MsgGUID: 4H+XQEZLQvCPOcjxkJ7Wpw== X-IronPort-AV: E=McAfee;i="6700,10204,11131"; a="43683024" X-IronPort-AV: E=Sophos;i="6.09,203,1716274800"; d="scan'208";a="43683024" Received: from orviesa004.jf.intel.com ([10.64.159.144]) by fmvoesa101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Jul 2024 12:09:42 -0700 X-CSE-ConnectionGUID: zEUe4GYJTaWRB0eiuNBBsQ== X-CSE-MsgGUID: HSicS+gzTHGf8GN1f/qhgQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.09,203,1716274800"; d="scan'208";a="54187436" Received: from fl31ca102ks0602.deacluster.intel.com (HELO gnr-bkc.deacluster.intel.com) ([10.75.133.163]) by orviesa004.jf.intel.com with ESMTP; 12 Jul 2024 12:09:42 -0700 From: weilin.wang@intel.com To: weilin.wang@intel.com, Namhyung Kim , Ian Rogers , Arnaldo Carvalho de Melo , Peter Zijlstra , Ingo Molnar , Alexander Shishkin , Jiri Olsa , Adrian Hunter , Kan Liang Cc: linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, Perry Taylor , Samantha Alt , Caleb Biggers Subject: [RFC PATCH v17 3/8] perf stat: Fork and launch perf record when perf stat needs to get retire latency value for a metric. Date: Fri, 12 Jul 2024 15:09:25 -0400 Message-ID: <20240712190932.417531-4-weilin.wang@intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240712190932.417531-1-weilin.wang@intel.com> References: <20240712190932.417531-1-weilin.wang@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Weilin Wang When retire_latency value is used in a metric formula, evsel would fork a p= erf record process with "-e" and "-W" options. Perf record will collect required retire_latency values in parallel while perf stat is collecting counting va= lues. At the point of time that perf stat stops counting, evsel would stop perf r= ecord by sending sigterm signal to perf record process. Sampled data will be proc= ess to get retire latency value. Another thread is required to synchronize betw= een perf stat and perf record when we pass data through pipe. Retire_latency evsel is not opened for perf stat so that there is no counter wasted on it. This commit includes code suggested by Namhyung to adjust rea= ding size for groups that include retire_latency evsels. Signed-off-by: Weilin Wang --- tools/perf/builtin-stat.c | 4 + tools/perf/util/Build | 1 + tools/perf/util/evlist.c | 2 + tools/perf/util/evsel.c | 66 +++++- tools/perf/util/intel-tpebs.c | 430 ++++++++++++++++++++++++++++++++++ tools/perf/util/intel-tpebs.h | 35 +++ 6 files changed, 536 insertions(+), 2 deletions(-) create mode 100644 tools/perf/util/intel-tpebs.c create mode 100644 tools/perf/util/intel-tpebs.h diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 661832756a24..68125bd75b37 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -70,6 +70,7 @@ #include "util/bpf_counter.h" #include "util/iostat.h" #include "util/util.h" +#include "util/intel-tpebs.h" #include "asm/bug.h" =20 #include @@ -683,6 +684,9 @@ static enum counter_recovery stat_handle_error(struct e= vsel *counter) =20 if (child_pid !=3D -1) kill(child_pid, SIGTERM); + + tpebs_delete(); + return COUNTER_FATAL; } =20 diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 0f18fe81ef0b..25cae5c613ad 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -154,6 +154,7 @@ perf-util-y +=3D clockid.o perf-util-y +=3D list_sort.o perf-util-y +=3D mutex.o perf-util-y +=3D sharded_mutex.o +perf-util-$(CONFIG_X86_64) +=3D intel-tpebs.o =20 perf-util-$(CONFIG_LIBBPF) +=3D bpf_map.o perf-util-$(CONFIG_PERF_BPF_SKEL) +=3D bpf_counter.o diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 3a719edafc7a..78ce80f227aa 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -33,6 +33,7 @@ #include "util/bpf-filter.h" #include "util/stat.h" #include "util/util.h" +#include "util/intel-tpebs.h" #include #include #include @@ -179,6 +180,7 @@ void evlist__delete(struct evlist *evlist) if (evlist =3D=3D NULL) return; =20 + tpebs_delete(); evlist__free_stats(evlist); evlist__munmap(evlist); evlist__close(evlist); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index bc603193c477..1d000fa2c6cf 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1554,6 +1554,60 @@ static void evsel__set_count(struct evsel *counter, = int cpu_map_idx, int thread, perf_counts__set_loaded(counter->counts, cpu_map_idx, thread, true); } =20 +static bool evsel__group_has_tpebs(struct evsel *leader) +{ + struct evsel *evsel; + + for_each_group_evsel(evsel, leader) { + if (evsel__is_retire_lat(evsel)) + return true; + } + return false; +} + +static u64 evsel__group_read_nr_members(struct evsel *leader) +{ + u64 nr =3D leader->core.nr_members; + struct evsel *evsel; + + for_each_group_evsel(evsel, leader) { + if (evsel__is_retire_lat(evsel)) + nr--; + } + return nr; +} + +static u64 evsel__group_read_size(struct evsel *leader) +{ + u64 read_format =3D leader->core.attr.read_format; + int entry =3D sizeof(u64); /* value */ + int size =3D 0; + int nr =3D 1; + + if (!evsel__group_has_tpebs(leader)) + return perf_evsel__read_size(&leader->core); + + if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + size +=3D sizeof(u64); + + if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + size +=3D sizeof(u64); + + if (read_format & PERF_FORMAT_ID) + entry +=3D sizeof(u64); + + if (read_format & PERF_FORMAT_LOST) + entry +=3D sizeof(u64); + + if (read_format & PERF_FORMAT_GROUP) { + nr =3D evsel__group_read_nr_members(leader); + size +=3D sizeof(u64); + } + + size +=3D entry * nr; + return size; +} + static int evsel__process_group_data(struct evsel *leader, int cpu_map_idx= , int thread, u64 *data) { u64 read_format =3D leader->core.attr.read_format; @@ -1562,7 +1616,7 @@ static int evsel__process_group_data(struct evsel *le= ader, int cpu_map_idx, int =20 nr =3D *data++; =20 - if (nr !=3D (u64) leader->core.nr_members) + if (nr !=3D evsel__group_read_nr_members(leader)) return -EINVAL; =20 if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) @@ -1592,7 +1646,7 @@ static int evsel__read_group(struct evsel *leader, in= t cpu_map_idx, int thread) { struct perf_stat_evsel *ps =3D leader->stats; u64 read_format =3D leader->core.attr.read_format; - int size =3D perf_evsel__read_size(&leader->core); + int size =3D evsel__group_read_size(leader); u64 *data =3D ps->group_data; =20 if (!(read_format & PERF_FORMAT_ID)) @@ -2200,6 +2254,9 @@ static int evsel__open_cpu(struct evsel *evsel, struc= t perf_cpu_map *cpus, return 0; } =20 + if (evsel__is_retire_lat(evsel)) + return tpebs_start(evsel->evlist); + err =3D __evsel__prepare_open(evsel, cpus, threads); if (err) return err; @@ -2392,6 +2449,8 @@ int evsel__open(struct evsel *evsel, struct perf_cpu_= map *cpus, =20 void evsel__close(struct evsel *evsel) { + if (evsel__is_retire_lat(evsel)) + tpebs_delete(); perf_evsel__close(&evsel->core); perf_evsel__free_id(&evsel->core); } @@ -3357,6 +3416,9 @@ static int store_evsel_ids(struct evsel *evsel, struc= t evlist *evlist) { int cpu_map_idx, thread; =20 + if (evsel__is_retire_lat(evsel)) + return 0; + for (cpu_map_idx =3D 0; cpu_map_idx < xyarray__max_x(evsel->core.fd); cpu= _map_idx++) { for (thread =3D 0; thread < xyarray__max_y(evsel->core.fd); thread++) { diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c new file mode 100644 index 000000000000..6680ef887961 --- /dev/null +++ b/tools/perf/util/intel-tpebs.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * intel_tpebs.c: Intel TPEBS support + */ + + +#include +#include +#include +#include "intel-tpebs.h" +#include +#include +#include +#include "sample.h" +#include "debug.h" +#include "evlist.h" +#include "evsel.h" +#include "session.h" +#include "tool.h" +#include "cpumap.h" +#include "metricgroup.h" +#include +#include +#include +#include + +#define PERF_DATA "-" + +bool tpebs_recording; +static pid_t tpebs_pid =3D -1; +static size_t tpebs_event_size; +static LIST_HEAD(tpebs_results); +static pthread_t tpebs_reader_thread; +static struct child_process *tpebs_cmd; + +struct tpebs_retire_lat { + struct list_head nd; + /* Event name */ + const char *name; + /* Event name with the TPEBS modifier R */ + const char *tpebs_name; + /* Count of retire_latency values found in sample data */ + size_t count; + /* Sum of all the retire_latency values in sample data */ + int sum; + /* Average of retire_latency, val =3D sum / count */ + double val; +}; + +static int get_perf_record_args(const char **record_argv, char buf[], + const char *cpumap_buf) +{ + struct tpebs_retire_lat *e; + int i =3D 0; + + pr_debug("tpebs: Prepare perf record for retire_latency\n"); + + record_argv[i++] =3D "perf"; + record_argv[i++] =3D "record"; + record_argv[i++] =3D "-W"; + record_argv[i++] =3D "--synth=3Dno"; + record_argv[i++] =3D buf; + + if (!cpumap_buf) { + pr_err("tpebs: Require cpumap list to run sampling\n"); + return -ECANCELED; + } + /* Use -C when cpumap_buf is not "-1" */ + if (strcmp(cpumap_buf, "-1")) { + record_argv[i++] =3D "-C"; + record_argv[i++] =3D cpumap_buf; + } + + list_for_each_entry(e, &tpebs_results, nd) { + record_argv[i++] =3D "-e"; + record_argv[i++] =3D e->name; + } + + record_argv[i++] =3D "-o"; + record_argv[i++] =3D PERF_DATA; + + return 0; +} + +static int prepare_run_command(const char **argv) +{ + tpebs_cmd =3D zalloc(sizeof(struct child_process)); + if (!tpebs_cmd) + return -ENOMEM; + tpebs_cmd->argv =3D argv; + tpebs_cmd->out =3D -1; + return 0; +} + +static int start_perf_record(int control_fd[], int ack_fd[], + const char *cpumap_buf) +{ + const char **record_argv; + int ret; + char buf[32]; + + scnprintf(buf, sizeof(buf), "--control=3Dfd:%d,%d", control_fd[0], ack_fd= [1]); + + record_argv =3D calloc(12 + 2 * tpebs_event_size, sizeof(char *)); + if (!record_argv) + return -ENOMEM; + + ret =3D get_perf_record_args(record_argv, buf, cpumap_buf); + if (ret) + goto out; + + ret =3D prepare_run_command(record_argv); + if (ret) + goto out; + ret =3D start_command(tpebs_cmd); +out: + free(record_argv); + return ret; +} + +static int process_sample_event(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_sample *sample, + struct evsel *evsel, + struct machine *machine __maybe_unused) +{ + int ret =3D 0; + const char *evname; + struct tpebs_retire_lat *t; + + evname =3D evsel__name(evsel); + + /* + * Need to handle per core results? We are assuming average retire + * latency value will be used. Save the number of samples and the sum of + * retire latency value for each event. + */ + list_for_each_entry(t, &tpebs_results, nd) { + if (!strcmp(evname, t->name)) { + t->count +=3D 1; + t->sum +=3D sample->retire_lat; + t->val =3D (double) t->sum / t->count; + break; + } + } + + return ret; +} + +static int process_feature_event(struct perf_session *session, + union perf_event *event) +{ + if (event->feat.feat_id < HEADER_LAST_FEATURE) + return perf_event__process_feature(session, event); + return 0; +} + +static void *__sample_reader(void *arg) +{ + struct child_process *child =3D arg; + struct perf_session *session; + struct perf_data data =3D { + .mode =3D PERF_DATA_MODE_READ, + .path =3D PERF_DATA, + .file.fd =3D child->out, + }; + struct perf_tool tool =3D { + .sample =3D process_sample_event, + .feature =3D process_feature_event, + .attr =3D perf_event__process_attr, + }; + + session =3D perf_session__new(&data, &tool); + if (IS_ERR(session)) + return NULL; + perf_session__process_events(session); + perf_session__delete(session); + + return NULL; +} + +/* + * tpebs_stop - stop the sample data read thread and the perf record proce= ss. + */ +static int tpebs_stop(void) +{ + int ret =3D 0; + + /* Like tpebs_start, we should only run tpebs_end once. */ + if (tpebs_pid !=3D -1) { + kill(tpebs_cmd->pid, SIGTERM); + tpebs_pid =3D -1; + pthread_join(tpebs_reader_thread, NULL); + close(tpebs_cmd->out); + ret =3D finish_command(tpebs_cmd); + if (ret =3D=3D -ERR_RUN_COMMAND_WAITPID_SIGNAL) + ret =3D 0; + } + return ret; +} + +/* + * tpebs_start - start tpebs execution. + * @evsel_list: retire_latency evsels in this list will be selected and sa= mpled + * to get the average retire_latency value. + * + * This function will be called from evlist level later when evlist__open(= ) is + * called consistently. + */ +int tpebs_start(struct evlist *evsel_list) +{ + int ret =3D 0; + struct evsel *evsel; + char cpumap_buf[50]; + + /* + * We should only run tpebs_start when tpebs_recording is enabled. + * And we should only run it once with all the required events. + */ + if (tpebs_pid !=3D -1 || !tpebs_recording) + return 0; + + cpu_map__snprint(evsel_list->core.user_requested_cpus, cpumap_buf, sizeof= (cpumap_buf)); + /* + * Prepare perf record for sampling event retire_latency before fork and + * prepare workload + */ + evlist__for_each_entry(evsel_list, evsel) { + int i; + char *name; + struct tpebs_retire_lat *new; + + if (!evsel->retire_lat) + continue; + + pr_debug("tpebs: Retire_latency of event %s is required\n", evsel->name); + for (i =3D strlen(evsel->name) - 1; i > 0; i--) { + if (evsel->name[i] =3D=3D 'R') + break; + } + if (i <=3D 0 || evsel->name[i] !=3D 'R') { + ret =3D -1; + goto err; + } + + name =3D strdup(evsel->name); + if (!name) { + ret =3D -ENOMEM; + goto err; + } + name[i] =3D 'p'; + + new =3D zalloc(sizeof(*new)); + if (!new) { + ret =3D -1; + zfree(name); + goto err; + } + new->name =3D name; + new->tpebs_name =3D evsel->name; + list_add_tail(&new->nd, &tpebs_results); + tpebs_event_size +=3D 1; + } + + if (tpebs_event_size > 0) { + struct pollfd pollfd =3D { .events =3D POLLIN, }; + int control_fd[2], ack_fd[2], len; + char ack_buf[8]; + + /*Create control and ack fd for --control*/ + if (pipe(control_fd) < 0) { + pr_err("tpebs: Failed to create control fifo"); + ret =3D -1; + goto out; + } + if (pipe(ack_fd) < 0) { + pr_err("tpebs: Failed to create control fifo"); + ret =3D -1; + goto out; + } + + ret =3D start_perf_record(control_fd, ack_fd, cpumap_buf); + if (ret) + goto out; + tpebs_pid =3D tpebs_cmd->pid; + if (pthread_create(&tpebs_reader_thread, NULL, __sample_reader, tpebs_cm= d)) { + kill(tpebs_cmd->pid, SIGTERM); + close(tpebs_cmd->out); + pr_err("Could not create thread to process sample data.\n"); + ret =3D -1; + goto out; + } + /* Wait for perf record initialization.*/ + len =3D strlen("enable"); + ret =3D write(control_fd[1], "enable", len); + if (ret !=3D len) { + pr_err("perf record control write control message failed\n"); + goto out; + } + + /* wait for an ack */ + pollfd.fd =3D ack_fd[0]; + + /* + * We need this poll to ensure the ack_fd PIPE will not hang + * when perf record failed for any reason. The timeout value + * 3000ms is an empirical selection. + */ + if (!poll(&pollfd, 1, 3000)) { + pr_err("tpebs failed: perf record ack timeout\n"); + ret =3D -1; + goto out; + } + + if (!(pollfd.revents & POLLIN)) { + pr_err("tpebs failed: did not received an ack\n"); + ret =3D -1; + goto out; + } + + ret =3D read(ack_fd[0], ack_buf, sizeof(ack_buf)); + if (ret > 0) + ret =3D strcmp(ack_buf, "ack\n"); + else { + pr_err("tpebs: perf record control ack failed\n"); + goto out; + } +out: + close(control_fd[0]); + close(control_fd[1]); + close(ack_fd[0]); + close(ack_fd[1]); + } +err: + if (ret) + tpebs_delete(); + return ret; +} + + +int tpebs_set_evsel(struct evsel *evsel, int cpu_map_idx, int thread) +{ + __u64 val; + bool found =3D false; + struct tpebs_retire_lat *t; + struct perf_counts_values *count; + + /* Non reitre_latency evsel should never enter this function. */ + if (!evsel__is_retire_lat(evsel)) + return -1; + + /* + * Need to stop the forked record to ensure get sampled data from the + * PIPE to process and get non-zero retire_lat value for hybrid. + */ + tpebs_stop(); + count =3D perf_counts(evsel->counts, cpu_map_idx, thread); + + list_for_each_entry(t, &tpebs_results, nd) { + if (t->tpebs_name =3D=3D evsel->name || (evsel->metric_id && !strcmp(t->= tpebs_name, evsel->metric_id))) { + found =3D true; + break; + } + } + + /* Set ena and run to non-zero */ + count->ena =3D count->run =3D 1; + count->lost =3D 0; + + if (!found) { + /* + * Set default value or 0 when retire_latency for this event is + * not found from sampling data (record_tpebs not set or 0 + * sample recorded). + */ + count->val =3D 0; + return 0; + } + + /* + * Only set retire_latency value to the first CPU and thread. + */ + if (cpu_map_idx =3D=3D 0 && thread =3D=3D 0) + val =3D rint(t->val); + else + val =3D 0; + + count->val =3D val; + return 0; +} + +static void tpebs_retire_lat__delete(struct tpebs_retire_lat *r) +{ + zfree(&r->name); + free(r); +} + + +/* + * tpebs_delete - delete tpebs related data and stop the created thread and + * process by calling tpebs_stop(). + * + * This function is called from evlist_delete() and also from builtin-stat + * stat_handle_error(). If tpebs_start() is called from places other then = perf + * stat, need to ensure tpebs_delete() is also called to safely free mem a= nd + * close the data read thread and the forked perf record process. + * + * This function is also called in evsel__close() to be symmetric with + * tpebs_start() being called in evsel__open(). We will update this call s= ite + * when move tpebs_start() to evlist level. + */ +void tpebs_delete(void) +{ + struct tpebs_retire_lat *r, *rtmp; + + if (tpebs_pid =3D=3D -1) + return; + + tpebs_stop(); + + list_for_each_entry_safe(r, rtmp, &tpebs_results, nd) { + list_del_init(&r->nd); + tpebs_retire_lat__delete(r); + } + + if (tpebs_cmd) { + free(tpebs_cmd); + tpebs_cmd =3D NULL; + } +} diff --git a/tools/perf/util/intel-tpebs.h b/tools/perf/util/intel-tpebs.h new file mode 100644 index 000000000000..766b3fbd79f1 --- /dev/null +++ b/tools/perf/util/intel-tpebs.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * intel_tpebs.h: Intel TEPBS support + */ +#ifndef INCLUDE__PERF_INTEL_TPEBS_H__ +#define INCLUDE__PERF_INTEL_TPEBS_H__ + +#include "stat.h" +#include "evsel.h" + +#ifdef HAVE_ARCH_X86_64_SUPPORT + +extern bool tpebs_recording; +int tpebs_start(struct evlist *evsel_list); +void tpebs_delete(void); +int tpebs_set_evsel(struct evsel *evsel, int cpu_map_idx, int thread); + +#else + +static inline int tpebs_start(struct evlist *evsel_list __maybe_unused) +{ + return 0; +} + +static inline void tpebs_delete(void) {}; + +static inline int tpebs_set_evsel(struct evsel *evsel __maybe_unused, + int cpu_map_idx __maybe_unused, + int thread __maybe_unused) +{ + return 0; +} + +#endif +#endif --=20 2.43.0