/* * During evlist__purge the evlist will be removed prior to the * evsel__exit calling evsel__tpebs_close and taking the * tpebs_mtx. Avoid a segfault by ignoring samples in this case.
*/ if (t->evsel->evlist == NULL) returntrue;
mutex_lock(tpebs_mtx_get()); if (tpebs_cmd.pid == 0) { /* Record has terminated. */
mutex_unlock(tpebs_mtx_get()); return 0;
}
t = tpebs_retire_lat__find(evsel); if (!t) {
mutex_unlock(tpebs_mtx_get()); return -EINVAL;
} if (should_ignore_sample(sample, t)) {
mutex_unlock(tpebs_mtx_get()); return 0;
} /* * 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.
*/
t->last = sample->weight3;
update_stats(&t->stats, sample->weight3);
mutex_unlock(tpebs_mtx_get()); return 0;
}
staticint 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;
}
/* Check if the command exited before the send, done with the lock held. */ if (tpebs_cmd.pid == 0) return 0;
/* * Let go of the lock while sending/receiving as blocking can starve the * sample reading thread.
*/
mutex_unlock(tpebs_mtx_get());
/* Send perf record command.*/
len = strlen(msg);
ret = write(control_fd[1], msg, len); if (ret != len) {
pr_err("perf record control write control message '%s' failed\n", msg);
ret = -EPIPE; goto out;
}
if (!strcmp(msg, EVLIST_CTL_CMD_STOP_TAG)) {
ret = 0; goto out;
}
/* Wait for an ack. */
pollfd.fd = 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.
*/
again: if (!poll(&pollfd, 1, 500)) { if (check_if_command_finished(&tpebs_cmd)) {
ret = 0; goto out;
}
if (retries++ < 6) goto again;
pr_err("tpebs failed: perf record ack timeout for '%s'\n", msg);
ret = -ETIMEDOUT; goto out;
}
if (!(pollfd.revents & POLLIN)) { if (check_if_command_finished(&tpebs_cmd)) {
ret = 0; goto out;
}
pr_err("tpebs failed: did not received an ack for '%s'\n", msg);
ret = -EPIPE; goto out;
}
ret = read(ack_fd[0], ack_buf, sizeof(ack_buf)); if (ret > 0)
ret = strcmp(ack_buf, EVLIST_CTL_CMD_ACK_TAG); else
pr_err("tpebs: perf record control ack failed\n");
out: /* Re-take lock as expected by caller. */
mutex_lock(tpebs_mtx_get()); return ret;
}
/* * tpebs_stop - stop the sample data read thread and the perf record process.
*/ staticint tpebs_stop(void) EXCLUSIVE_LOCKS_REQUIRED(tpebs_mtx_get())
{ int ret = 0;
/* Like tpebs_start, we should only run tpebs_end once. */ if (tpebs_cmd.pid != 0) {
tpebs_send_record_cmd(EVLIST_CTL_CMD_STOP_TAG);
tpebs_cmd.pid = 0;
mutex_unlock(tpebs_mtx_get());
pthread_join(tpebs_reader_thread, NULL);
mutex_lock(tpebs_mtx_get());
close(control_fd[0]);
close(control_fd[1]);
close(ack_fd[0]);
close(ack_fd[1]);
close(tpebs_cmd.out);
ret = finish_command(&tpebs_cmd);
tpebs_cmd.pid = 0; if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
ret = 0;
} return ret;
}
/** * evsel__tpebs_event() - Create string event encoding to pass to `perf record`.
*/ staticint evsel__tpebs_event(struct evsel *evsel, char **event)
{ char *name, *modifier; int ret;
name = strdup(evsel->name); if (!name) return -ENOMEM;
modifier = strrchr(name, 'R'); if (!modifier) {
ret = -EINVAL; goto out;
}
*modifier = 'p';
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;
}
/* * 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 ((unsignedlong)t->evsel == num) return t;
} return NULL;
}
/** * evsel__tpebs_prepare - create tpebs data structures ready for opening. * @evsel: retire_latency evsel, all evsels on its list will be prepared.
*/ staticint evsel__tpebs_prepare(struct evsel *evsel)
{ struct evsel *pos; struct tpebs_retire_lat *tpebs_event;
mutex_lock(tpebs_mtx_get());
tpebs_event = tpebs_retire_lat__find(evsel); if (tpebs_event) { /* evsel, or an identically named one, was already prepared. */
mutex_unlock(tpebs_mtx_get()); return 0;
}
tpebs_event = tpebs_retire_lat__new(evsel); if (!tpebs_event) {
mutex_unlock(tpebs_mtx_get()); return -ENOMEM;
}
list_add_tail(&tpebs_event->nd, &tpebs_results);
mutex_unlock(tpebs_mtx_get());
/* * Eagerly prepare all other evsels on the list to try to ensure that by * open they are all known.
*/
evlist__for_each_entry(evsel->evlist, pos) { int ret;
if (pos == evsel || !pos->retire_lat) continue;
ret = evsel__tpebs_prepare(pos); if (ret) return ret;
} return 0;
}
/** * evsel__tpebs_open - starts tpebs execution. * @evsel: retire_latency evsel, all evsels on its list will be selected. Each * evsel is sampled to get the average retire_latency value.
*/ int evsel__tpebs_open(struct evsel *evsel)
{ int ret; bool tpebs_empty;
/* We should only run tpebs_start when tpebs_recording is enabled. */ if (!tpebs_recording) return 0; /* Only start the events once. */ if (tpebs_cmd.pid != 0) { struct tpebs_retire_lat *t; bool valid;
mutex_lock(tpebs_mtx_get());
t = tpebs_retire_lat__find(evsel);
valid = t && t->started;
mutex_unlock(tpebs_mtx_get()); /* May fail as the event wasn't started. */ return valid ? 0 : -EBUSY;
}
ret = evsel__tpebs_prepare(evsel); if (ret) return ret;
mutex_lock(tpebs_mtx_get());
tpebs_empty = list_empty(&tpebs_results); if (!tpebs_empty) { /*Create control and ack fd for --control*/ if (pipe(control_fd) < 0) {
pr_err("tpebs: Failed to create control fifo");
ret = -1; goto out;
} if (pipe(ack_fd) < 0) {
pr_err("tpebs: Failed to create control fifo");
ret = -1; goto out;
}
ret = evsel__tpebs_start_perf_record(evsel); if (ret) goto out;
if (pthread_create(&tpebs_reader_thread, /*attr=*/NULL, __sample_reader, /*arg=*/NULL)) {
kill(tpebs_cmd.pid, SIGTERM);
close(tpebs_cmd.out);
pr_err("Could not create thread to process sample data.\n");
ret = -1; goto out;
}
ret = tpebs_send_record_cmd(EVLIST_CTL_CMD_ENABLE_TAG);
}
out: if (ret) { struct tpebs_retire_lat *t = tpebs_retire_lat__find(evsel);
mutex_lock(tpebs_mtx_get());
t = tpebs_retire_lat__find(evsel); /* * If reading the first tpebs result, send a ping to the record * process. Allow the sample reader a chance to read by releasing and * reacquiring the lock.
*/ if (t && &t->nd == tpebs_results.next) {
ret = tpebs_send_record_cmd(EVLIST_CTL_CMD_PING_TAG);
mutex_unlock(tpebs_mtx_get()); if (ret) return ret;
mutex_lock(tpebs_mtx_get());
} if (t == NULL || t->stats.n == 0) { /* No sample data, use default. */ if (tpebs_recording) {
pr_warning_once( "Using precomputed retirement latency data as no samples\n");
}
val = 0; switch (tpebs_mode) { case TPEBS_MODE__MIN:
val = rint(evsel->retirement_latency.min); break; case TPEBS_MODE__MAX:
val = rint(evsel->retirement_latency.max); break; default: case TPEBS_MODE__LAST: case TPEBS_MODE__MEAN:
val = rint(evsel->retirement_latency.mean); break;
}
} else { switch (tpebs_mode) { case TPEBS_MODE__MIN:
val = t->stats.min; break; case TPEBS_MODE__MAX:
val = t->stats.max; break; case TPEBS_MODE__LAST:
val = t->last; break; default: case TPEBS_MODE__MEAN:
val = rint(t->stats.mean); break;
}
}
mutex_unlock(tpebs_mtx_get());
/** * evsel__tpebs_close() - delete tpebs related data. If the last event, stop the * created thread and process by calling tpebs_stop(). * * This function is called in evsel__close() to be symmetric with * evsel__tpebs_open() being called in evsel__open().
*/ void evsel__tpebs_close(struct evsel *evsel)
{ struct tpebs_retire_lat *t;
mutex_lock(tpebs_mtx_get());
t = tpebs_retire_lat__find(evsel); if (t) {
list_del_init(&t->nd);
tpebs_retire_lat__delete(t);
if (list_empty(&tpebs_results))
tpebs_stop();
}
mutex_unlock(tpebs_mtx_get());
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.