diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/perf/builtin-stat.c | 2 | ||||
-rw-r--r-- | tools/perf/util/evlist.c | 1 | ||||
-rw-r--r-- | tools/perf/util/evsel.c | 2 | ||||
-rw-r--r-- | tools/perf/util/intel-tpebs.c | 150 | ||||
-rw-r--r-- | tools/perf/util/intel-tpebs.h | 2 |
5 files changed, 93 insertions, 64 deletions
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 68ea7589c143..80e491bd775b 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -681,8 +681,6 @@ static enum counter_recovery stat_handle_error(struct evsel *counter) if (child_pid != -1) kill(child_pid, SIGTERM); - tpebs_delete(); - return COUNTER_FATAL; } diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index c1a04141aed0..0a21da4f990f 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -183,7 +183,6 @@ void evlist__delete(struct evlist *evlist) if (evlist == NULL) return; - 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 c29b93151158..c2e0ed7815b0 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -2737,7 +2737,7 @@ int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus, void evsel__close(struct evsel *evsel) { if (evsel__is_retire_lat(evsel)) - tpebs_delete(); + evsel__tpebs_close(evsel); perf_evsel__close(&evsel->core); perf_evsel__free_id(&evsel->core); } diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c index e42f3ec39a64..b48f3692c798 100644 --- a/tools/perf/util/intel-tpebs.c +++ b/tools/perf/util/intel-tpebs.c @@ -35,10 +35,10 @@ static struct child_process tpebs_cmd; struct tpebs_retire_lat { struct list_head nd; - /* Event name */ - char *name; - /* Event name with the TPEBS modifier R */ - const char *tpebs_name; + /** @evsel: The evsel that opened the retire_lat event. */ + struct evsel *evsel; + /** @event: Event passed to perf record. */ + char *event; /* Count of retire_latency values found in sample data */ size_t count; /* Sum of all the retire_latency values in sample data */ @@ -49,6 +49,8 @@ struct tpebs_retire_lat { bool started; }; +static struct tpebs_retire_lat *tpebs_retire_lat__find(struct evsel *evsel); + static int evsel__tpebs_start_perf_record(struct evsel *evsel, int control_fd[], int ack_fd[]) { const char **record_argv; @@ -85,7 +87,7 @@ static int evsel__tpebs_start_perf_record(struct evsel *evsel, int control_fd[], list_for_each_entry(t, &tpebs_results, nd) { record_argv[i++] = "-e"; - record_argv[i++] = t->name; + record_argv[i++] = t->event; } record_argv[i++] = NULL; assert(i == 10 + 2 * tpebs_event_size || i == 8 + 2 * tpebs_event_size); @@ -108,27 +110,20 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, struct evsel *evsel, struct machine *machine __maybe_unused) { - int ret = 0; - const char *evname; struct tpebs_retire_lat *t; - evname = evsel__name(evsel); - + t = tpebs_retire_lat__find(evsel); + if (!t) + return -EINVAL; /* * 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 += 1; - t->sum += sample->retire_lat; - t->val = (double) t->sum / t->count; - break; - } - } - - return ret; + t->count += 1; + t->sum += sample->retire_lat; + t->val = (double) t->sum / t->count; + return 0; } static int process_feature_event(struct perf_session *session, @@ -183,50 +178,98 @@ static int tpebs_stop(void) return ret; } -static char *evsel__tpebs_name(struct evsel *evsel) +/** + * evsel__tpebs_event() - Create string event encoding to pass to `perf record`. + */ +static int evsel__tpebs_event(struct evsel *evsel, char **event) { char *name, *modifier; + int ret; name = strdup(evsel->name); if (!name) - return NULL; + return -ENOMEM; modifier = strrchr(name, 'R'); if (!modifier) { - pr_err("Tpebs event missing modifier '%s'\n", name); - free(name); - return NULL; + ret = -EINVAL; + goto out; } - *modifier = 'p'; - return name; + modifier = strchr(name, ':'); + if (!modifier) + modifier = strrchr(name, '/'); + if (!modifier) { + ret = -EINVAL; + goto out; + } + *modifier = '\0'; + if (asprintf(event, "%s/name=tpebs_event_%p/%s", name, evsel, modifier + 1) > 0) + ret = 0; + else + ret = -ENOMEM; +out: + if (ret) + pr_err("Tpebs event modifier broken '%s'\n", evsel->name); + free(name); + return ret; } static struct tpebs_retire_lat *tpebs_retire_lat__new(struct evsel *evsel) { struct tpebs_retire_lat *result = zalloc(sizeof(*result)); + int ret; if (!result) return NULL; - result->tpebs_name = evsel->name; - result->name = evsel__tpebs_name(evsel); - if (!result->name) { + ret = evsel__tpebs_event(evsel, &result->event); + if (ret) { free(result); return NULL; } + result->evsel = evsel; list_add_tail(&result->nd, &tpebs_results); return result; } +static void tpebs_retire_lat__delete(struct tpebs_retire_lat *r) +{ + zfree(&r->event); + free(r); +} + static struct tpebs_retire_lat *tpebs_retire_lat__find(struct evsel *evsel) { struct tpebs_retire_lat *t; + unsigned long num; + const char *evsel_name; + /* + * Evsels will match for evlist with the retirement latency event. The + * name with "tpebs_event_" prefix will be present on events being read + * from `perf record`. + */ + if (evsel__is_retire_lat(evsel)) { + list_for_each_entry(t, &tpebs_results, nd) { + if (t->evsel == evsel) + return t; + } + return NULL; + } + evsel_name = strstr(evsel->name, "tpebs_event_"); + if (!evsel_name) { + /* Unexpected that the perf record should have other events. */ + return NULL; + } + errno = 0; + num = strtoull(evsel_name + 12, NULL, 16); + if (errno) { + pr_err("Bad evsel for tpebs find '%s'\n", evsel->name); + return NULL; + } list_for_each_entry(t, &tpebs_results, nd) { - if (t->tpebs_name == evsel->name || - !strcmp(t->tpebs_name, evsel->name) || - (evsel->metric_id && !strcmp(t->tpebs_name, evsel->metric_id))) + if ((unsigned long)t->evsel == num) return t; } return NULL; @@ -363,8 +406,12 @@ out: close(ack_fd[0]); close(ack_fd[1]); } - if (ret) - tpebs_delete(); + if (ret) { + struct tpebs_retire_lat *t = tpebs_retire_lat__find(evsel); + + list_del_init(&t->nd); + tpebs_retire_lat__delete(t); + } return ret; } @@ -414,34 +461,19 @@ int tpebs_set_evsel(struct evsel *evsel, int cpu_map_idx, int thread) 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 and - * close the data read thread and the forked perf record process. +/** + * evsel__tpebs_close() - delete tpebs related data. If the last event, stop the + * created thread and process by calling tpebs_stop(). * - * This function is also called in evsel__close() to be symmetric with - * tpebs_start() being called in evsel__open(). We will update this call site - * when move tpebs_start() to evlist level. + * This function is called in evsel__close() to be symmetric with + * evsel__tpebs_open() being called in evsel__open(). */ -void tpebs_delete(void) +void evsel__tpebs_close(struct evsel *evsel) { - struct tpebs_retire_lat *r, *rtmp; + struct tpebs_retire_lat *t = tpebs_retire_lat__find(evsel); - tpebs_stop(); + tpebs_retire_lat__delete(t); - list_for_each_entry_safe(r, rtmp, &tpebs_results, nd) { - list_del_init(&r->nd); - tpebs_retire_lat__delete(r); - } + if (list_empty(&tpebs_results)) + tpebs_stop(); } diff --git a/tools/perf/util/intel-tpebs.h b/tools/perf/util/intel-tpebs.h index cc98203719c8..5c671181ec60 100644 --- a/tools/perf/util/intel-tpebs.h +++ b/tools/perf/util/intel-tpebs.h @@ -11,7 +11,7 @@ struct evsel; extern bool tpebs_recording; int evsel__tpebs_open(struct evsel *evsel); -void tpebs_delete(void); +void evsel__tpebs_close(struct evsel *evsel); int tpebs_set_evsel(struct evsel *evsel, int cpu_map_idx, int thread); #endif /* __INTEL_TPEBS_H */ |