diff options
Diffstat (limited to 'tools/perf/builtin-ftrace.c')
-rw-r--r-- | tools/perf/builtin-ftrace.c | 172 |
1 files changed, 139 insertions, 33 deletions
diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index cfd770ec7286..bba36ebc2aa7 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -19,6 +19,7 @@ #include <ctype.h> #include <linux/capability.h> #include <linux/string.h> +#include <sys/stat.h> #include "debug.h" #include <subcmd/pager.h> @@ -45,6 +46,8 @@ static volatile sig_atomic_t done; static struct stats latency_stats; /* for tracepoints */ +static char tracing_instance[PATH_MAX]; /* Trace instance directory */ + static void sig_handler(int sig __maybe_unused) { done = true; @@ -100,6 +103,34 @@ static bool is_ftrace_supported(void) return supported; } +/* + * Wrapper to test if a file in directory .../tracing/instances/XXX + * exists. If so return the .../tracing/instances/XXX file for use. + * Otherwise the file exists only in directory .../tracing and + * is applicable to all instances, for example file available_filter_functions. + * Return that file name in this case. + * + * This functions works similar to get_tracing_file() and expects its caller + * to free the returned file name. + * + * The global variable tracing_instance is set in init_tracing_instance() + * called at the beginning to a process specific tracing subdirectory. + */ +static char *get_tracing_instance_file(const char *name) +{ + char *file; + + if (asprintf(&file, "%s/%s", tracing_instance, name) < 0) + return NULL; + + if (!access(file, F_OK)) + return file; + + free(file); + file = get_tracing_file(name); + return file; +} + static int __write_tracing_file(const char *name, const char *val, bool append) { char *file; @@ -109,7 +140,7 @@ static int __write_tracing_file(const char *name, const char *val, bool append) char errbuf[512]; char *val_copy; - file = get_tracing_file(name); + file = get_tracing_instance_file(name); if (!file) { pr_debug("cannot get tracing file: %s\n", name); return -1; @@ -167,7 +198,7 @@ static int read_tracing_file_to_stdout(const char *name) int fd; int ret = -1; - file = get_tracing_file(name); + file = get_tracing_instance_file(name); if (!file) { pr_debug("cannot get tracing file: %s\n", name); return -1; @@ -209,7 +240,7 @@ static int read_tracing_file_by_line(const char *name, char *file; FILE *fp; - file = get_tracing_file(name); + file = get_tracing_instance_file(name); if (!file) { pr_debug("cannot get tracing file: %s\n", name); return -1; @@ -299,6 +330,39 @@ static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused) return 0; } +/* Remove .../tracing/instances/XXX subdirectory created with + * init_tracing_instance(). + */ +static void exit_tracing_instance(void) +{ + if (rmdir(tracing_instance)) + pr_err("failed to delete tracing/instances directory\n"); +} + +/* Create subdirectory within .../tracing/instances/XXX to have session + * or process specific setup. To delete this setup, simply remove the + * subdirectory. + */ +static int init_tracing_instance(void) +{ + char dirname[] = "instances/perf-ftrace-XXXXXX"; + char *path; + + path = get_tracing_file(dirname); + if (!path) + goto error; + strncpy(tracing_instance, path, sizeof(tracing_instance) - 1); + put_tracing_file(path); + path = mkdtemp(tracing_instance); + if (!path) + goto error; + return 0; + +error: + pr_err("failed to create tracing/instances directory\n"); + return -1; +} + static int set_tracing_pid(struct perf_ftrace *ftrace) { int i; @@ -629,14 +693,17 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace) select_tracer(ftrace); + if (init_tracing_instance() < 0) + goto out; + if (reset_tracing_files(ftrace) < 0) { pr_err("failed to reset ftrace\n"); - goto out; + goto out_reset; } /* reset ftrace buffer */ if (write_tracing_file("trace", "0") < 0) - goto out; + goto out_reset; if (set_tracing_options(ftrace) < 0) goto out_reset; @@ -648,7 +715,7 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace) setup_pager(); - trace_file = get_tracing_file("trace_pipe"); + trace_file = get_tracing_instance_file("trace_pipe"); if (!trace_file) { pr_err("failed to open trace_pipe\n"); goto out_reset; @@ -723,7 +790,7 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace) out_close_fd: close(trace_fd); out_reset: - reset_tracing_files(ftrace); + exit_tracing_instance(); out: return (done && !workload_exec_errno) ? 0 : -1; } @@ -733,6 +800,7 @@ static void make_histogram(struct perf_ftrace *ftrace, int buckets[], { int min_latency = ftrace->min_latency; int max_latency = ftrace->max_latency; + unsigned int bucket_num = ftrace->bucket_num; char *p, *q; char *unit; double num; @@ -797,10 +865,10 @@ static void make_histogram(struct perf_ftrace *ftrace, int buckets[], if (num > 0) // 1st entry: [ 1 unit .. bucket_range units ] i = num / ftrace->bucket_range + 1; if (num >= max_latency - min_latency) - i = NUM_BUCKET -1; + i = bucket_num -1; } - if (i >= NUM_BUCKET) - i = NUM_BUCKET - 1; + if ((unsigned)i >= bucket_num) + i = bucket_num - 1; num += min_latency; do_inc: @@ -820,13 +888,14 @@ static void display_histogram(struct perf_ftrace *ftrace, int buckets[]) { int min_latency = ftrace->min_latency; bool use_nsec = ftrace->use_nsec; - int i; + unsigned int bucket_num = ftrace->bucket_num; + unsigned int i; int total = 0; int bar_total = 46; /* to fit in 80 column */ char bar[] = "###############################################"; int bar_len; - for (i = 0; i < NUM_BUCKET; i++) + for (i = 0; i < bucket_num; i++) total += buckets[i]; if (total == 0) { @@ -839,14 +908,17 @@ static void display_histogram(struct perf_ftrace *ftrace, int buckets[]) bar_len = buckets[0] * bar_total / total; - printf(" %4d - %4d %s | %10d | %.*s%*s |\n", - 0, min_latency ?: 1, use_nsec ? "ns" : "us", - buckets[0], bar_len, bar, bar_total - bar_len, ""); + if (!ftrace->hide_empty || buckets[0]) + printf(" %4d - %4d %s | %10d | %.*s%*s |\n", + 0, min_latency ?: 1, use_nsec ? "ns" : "us", + buckets[0], bar_len, bar, bar_total - bar_len, ""); - for (i = 1; i < NUM_BUCKET - 1; i++) { + for (i = 1; i < bucket_num - 1; i++) { unsigned int start, stop; const char *unit = use_nsec ? "ns" : "us"; + if (ftrace->hide_empty && !buckets[i]) + continue; if (!ftrace->bucket_range) { start = (1 << (i - 1)); stop = 1 << i; @@ -881,11 +953,13 @@ print_bucket_info: bar_total - bar_len, ""); } - bar_len = buckets[NUM_BUCKET - 1] * bar_total / total; + bar_len = buckets[bucket_num - 1] * bar_total / total; + if (ftrace->hide_empty && !buckets[bucket_num - 1]) + goto print_stats; if (!ftrace->bucket_range) { printf(" %4d - %-4s %s", 1, "...", use_nsec ? "ms" : "s "); } else { - unsigned int upper_outlier = (NUM_BUCKET - 2) * ftrace->bucket_range + min_latency; + unsigned int upper_outlier = (bucket_num - 2) * ftrace->bucket_range + min_latency; if (upper_outlier > ftrace->max_latency) upper_outlier = ftrace->max_latency; @@ -897,9 +971,10 @@ print_bucket_info: printf(" %4d - %4s %s", upper_outlier, "...", use_nsec ? "ns" : "us"); } } - printf(" | %10d | %.*s%*s |\n", buckets[NUM_BUCKET - 1], + printf(" | %10d | %.*s%*s |\n", buckets[bucket_num - 1], bar_len, bar, bar_total - bar_len, ""); +print_stats: printf("\n# statistics (in %s)\n", ftrace->use_nsec ? "nsec" : "usec"); printf(" total time: %20.0f\n", latency_stats.mean * latency_stats.n); printf(" avg time: %20.0f\n", latency_stats.mean); @@ -916,6 +991,9 @@ static int prepare_func_latency(struct perf_ftrace *ftrace) if (ftrace->target.use_bpf) return perf_ftrace__latency_prepare_bpf(ftrace); + if (init_tracing_instance() < 0) + return -1; + if (reset_tracing_files(ftrace) < 0) { pr_err("failed to reset ftrace\n"); return -1; @@ -934,7 +1012,7 @@ static int prepare_func_latency(struct perf_ftrace *ftrace) return -1; } - trace_file = get_tracing_file("trace_pipe"); + trace_file = get_tracing_instance_file("trace_pipe"); if (!trace_file) { pr_err("failed to open trace_pipe\n"); return -1; @@ -985,7 +1063,7 @@ static int cleanup_func_latency(struct perf_ftrace *ftrace) if (ftrace->target.use_bpf) return perf_ftrace__latency_cleanup_bpf(ftrace); - reset_tracing_files(ftrace); + exit_tracing_instance(); return 0; } @@ -997,7 +1075,7 @@ static int __cmd_latency(struct perf_ftrace *ftrace) struct pollfd pollfd = { .events = POLLIN, }; - int buckets[NUM_BUCKET] = { }; + int *buckets; trace_fd = prepare_func_latency(ftrace); if (trace_fd < 0) @@ -1011,6 +1089,12 @@ static int __cmd_latency(struct perf_ftrace *ftrace) evlist__start_workload(ftrace->evlist); + buckets = calloc(ftrace->bucket_num, sizeof(*buckets)); + if (buckets == NULL) { + pr_err("failed to allocate memory for the buckets\n"); + goto out; + } + line[0] = '\0'; while (!done) { if (poll(&pollfd, 1, -1) < 0) @@ -1030,7 +1114,7 @@ static int __cmd_latency(struct perf_ftrace *ftrace) if (workload_exec_errno) { const char *emsg = str_error_r(workload_exec_errno, buf, sizeof(buf)); pr_err("workload failed: %s\n", emsg); - goto out; + goto out_free_buckets; } /* read remaining buffer contents */ @@ -1045,6 +1129,8 @@ static int __cmd_latency(struct perf_ftrace *ftrace) display_histogram(ftrace, buckets); +out_free_buckets: + free(buckets); out: close(trace_fd); cleanup_func_latency(ftrace); @@ -1288,17 +1374,20 @@ static int __cmd_profile(struct perf_ftrace *ftrace) goto out; } + if (init_tracing_instance() < 0) + goto out; + if (reset_tracing_files(ftrace) < 0) { pr_err("failed to reset ftrace\n"); - goto out; + goto out_reset; } /* reset ftrace buffer */ if (write_tracing_file("trace", "0") < 0) - goto out; + goto out_reset; if (set_tracing_options(ftrace) < 0) - return -1; + goto out_reset; if (write_tracing_file("current_tracer", ftrace->tracer) < 0) { pr_err("failed to set current_tracer to %s\n", ftrace->tracer); @@ -1307,7 +1396,7 @@ static int __cmd_profile(struct perf_ftrace *ftrace) setup_pager(); - trace_file = get_tracing_file("trace_pipe"); + trace_file = get_tracing_instance_file("trace_pipe"); if (!trace_file) { pr_err("failed to open trace_pipe\n"); goto out_reset; @@ -1369,7 +1458,7 @@ out_free_line: out_close_fd: close(trace_fd); out_reset: - reset_tracing_files(ftrace); + exit_tracing_instance(); out: return (done && !workload_exec_errno) ? 0 : -1; } @@ -1634,7 +1723,9 @@ int cmd_ftrace(int argc, const char **argv) OPT_UINTEGER(0, "min-latency", &ftrace.min_latency, "Minimum latency (1st bucket). Works only with --bucket-range."), OPT_UINTEGER(0, "max-latency", &ftrace.max_latency, - "Maximum latency (last bucket). Works only with --bucket-range and total buckets less than 22."), + "Maximum latency (last bucket). Works only with --bucket-range."), + OPT_BOOLEAN(0, "hide-empty", &ftrace.hide_empty, + "Hide empty buckets in the histogram"), OPT_PARENT(common_options), }; const struct option profile_options[] = { @@ -1751,10 +1842,25 @@ int cmd_ftrace(int argc, const char **argv) ret = -EINVAL; goto out_delete_filters; } - if (ftrace.bucket_range && !ftrace.max_latency) { - /* default max latency should depend on bucket range and num_buckets */ - ftrace.max_latency = (NUM_BUCKET - 2) * ftrace.bucket_range + - ftrace.min_latency; + if (ftrace.bucket_range && ftrace.max_latency && + ftrace.max_latency < ftrace.min_latency + ftrace.bucket_range) { + /* we need at least 1 bucket excluding min and max buckets */ + pr_err("--max-latency must be larger than min-latency + bucket-range\n"); + parse_options_usage(ftrace_usage, options, + "max-latency", /*short_opt=*/false); + ret = -EINVAL; + goto out_delete_filters; + } + /* set default unless max_latency is set and valid */ + ftrace.bucket_num = NUM_BUCKET; + if (ftrace.bucket_range) { + if (ftrace.max_latency) + ftrace.bucket_num = (ftrace.max_latency - ftrace.min_latency) / + ftrace.bucket_range + 2; + else + /* default max latency should depend on bucket range and num_buckets */ + ftrace.max_latency = (NUM_BUCKET - 2) * ftrace.bucket_range + + ftrace.min_latency; } cmd_func = __cmd_latency; break; |