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 */
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;
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);
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,
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;
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;
}
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();
}