1 /* 2 * builtin-ftrace.c 3 * 4 * Copyright (c) 2013 LG Electronics, Namhyung Kim <namhyung@kernel.org> 5 * 6 * Released under the GPL v2. 7 */ 8 9 #include "builtin.h" 10 #include "perf.h" 11 12 #include <errno.h> 13 #include <unistd.h> 14 #include <signal.h> 15 #include <fcntl.h> 16 #include <poll.h> 17 18 #include "debug.h" 19 #include <subcmd/parse-options.h> 20 #include <api/fs/tracing_path.h> 21 #include "evlist.h" 22 #include "target.h" 23 #include "cpumap.h" 24 #include "thread_map.h" 25 #include "util/config.h" 26 27 28 #define DEFAULT_TRACER "function_graph" 29 30 struct perf_ftrace { 31 struct perf_evlist *evlist; 32 struct target target; 33 const char *tracer; 34 }; 35 36 static bool done; 37 38 static void sig_handler(int sig __maybe_unused) 39 { 40 done = true; 41 } 42 43 /* 44 * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since 45 * we asked by setting its exec_error to the function below, 46 * ftrace__workload_exec_failed_signal. 47 * 48 * XXX We need to handle this more appropriately, emitting an error, etc. 49 */ 50 static void ftrace__workload_exec_failed_signal(int signo __maybe_unused, 51 siginfo_t *info __maybe_unused, 52 void *ucontext __maybe_unused) 53 { 54 /* workload_exec_errno = info->si_value.sival_int; */ 55 done = true; 56 } 57 58 static int __write_tracing_file(const char *name, const char *val, bool append) 59 { 60 char *file; 61 int fd, ret = -1; 62 ssize_t size = strlen(val); 63 int flags = O_WRONLY; 64 65 file = get_tracing_file(name); 66 if (!file) { 67 pr_debug("cannot get tracing file: %s\n", name); 68 return -1; 69 } 70 71 if (append) 72 flags |= O_APPEND; 73 else 74 flags |= O_TRUNC; 75 76 fd = open(file, flags); 77 if (fd < 0) { 78 pr_debug("cannot open tracing file: %s\n", name); 79 goto out; 80 } 81 82 if (write(fd, val, size) == size) 83 ret = 0; 84 else 85 pr_debug("write '%s' to tracing/%s failed\n", val, name); 86 87 close(fd); 88 out: 89 put_tracing_file(file); 90 return ret; 91 } 92 93 static int write_tracing_file(const char *name, const char *val) 94 { 95 return __write_tracing_file(name, val, false); 96 } 97 98 static int append_tracing_file(const char *name, const char *val) 99 { 100 return __write_tracing_file(name, val, true); 101 } 102 103 static int reset_tracing_cpu(void); 104 105 static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused) 106 { 107 if (write_tracing_file("tracing_on", "0") < 0) 108 return -1; 109 110 if (write_tracing_file("current_tracer", "nop") < 0) 111 return -1; 112 113 if (write_tracing_file("set_ftrace_pid", " ") < 0) 114 return -1; 115 116 if (reset_tracing_cpu() < 0) 117 return -1; 118 119 return 0; 120 } 121 122 static int set_tracing_pid(struct perf_ftrace *ftrace) 123 { 124 int i; 125 char buf[16]; 126 127 if (target__has_cpu(&ftrace->target)) 128 return 0; 129 130 for (i = 0; i < thread_map__nr(ftrace->evlist->threads); i++) { 131 scnprintf(buf, sizeof(buf), "%d", 132 ftrace->evlist->threads->map[i]); 133 if (append_tracing_file("set_ftrace_pid", buf) < 0) 134 return -1; 135 } 136 return 0; 137 } 138 139 static int set_tracing_cpumask(struct cpu_map *cpumap) 140 { 141 char *cpumask; 142 size_t mask_size; 143 int ret; 144 int last_cpu; 145 146 last_cpu = cpu_map__cpu(cpumap, cpumap->nr - 1); 147 mask_size = (last_cpu + 3) / 4 + 1; 148 mask_size += last_cpu / 32; /* ',' is needed for every 32th cpus */ 149 150 cpumask = malloc(mask_size); 151 if (cpumask == NULL) { 152 pr_debug("failed to allocate cpu mask\n"); 153 return -1; 154 } 155 156 cpu_map__snprint_mask(cpumap, cpumask, mask_size); 157 158 ret = write_tracing_file("tracing_cpumask", cpumask); 159 160 free(cpumask); 161 return ret; 162 } 163 164 static int set_tracing_cpu(struct perf_ftrace *ftrace) 165 { 166 struct cpu_map *cpumap = ftrace->evlist->cpus; 167 168 if (!target__has_cpu(&ftrace->target)) 169 return 0; 170 171 return set_tracing_cpumask(cpumap); 172 } 173 174 static int reset_tracing_cpu(void) 175 { 176 struct cpu_map *cpumap = cpu_map__new(NULL); 177 int ret; 178 179 ret = set_tracing_cpumask(cpumap); 180 cpu_map__put(cpumap); 181 return ret; 182 } 183 184 static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) 185 { 186 char *trace_file; 187 int trace_fd; 188 char buf[4096]; 189 struct pollfd pollfd = { 190 .events = POLLIN, 191 }; 192 193 if (geteuid() != 0) { 194 pr_err("ftrace only works for root!\n"); 195 return -1; 196 } 197 198 signal(SIGINT, sig_handler); 199 signal(SIGUSR1, sig_handler); 200 signal(SIGCHLD, sig_handler); 201 signal(SIGPIPE, sig_handler); 202 203 if (reset_tracing_files(ftrace) < 0) 204 goto out; 205 206 /* reset ftrace buffer */ 207 if (write_tracing_file("trace", "0") < 0) 208 goto out; 209 210 if (argc && perf_evlist__prepare_workload(ftrace->evlist, 211 &ftrace->target, argv, false, 212 ftrace__workload_exec_failed_signal) < 0) { 213 goto out; 214 } 215 216 if (set_tracing_pid(ftrace) < 0) { 217 pr_err("failed to set ftrace pid\n"); 218 goto out_reset; 219 } 220 221 if (set_tracing_cpu(ftrace) < 0) { 222 pr_err("failed to set tracing cpumask\n"); 223 goto out_reset; 224 } 225 226 if (write_tracing_file("current_tracer", ftrace->tracer) < 0) { 227 pr_err("failed to set current_tracer to %s\n", ftrace->tracer); 228 goto out_reset; 229 } 230 231 trace_file = get_tracing_file("trace_pipe"); 232 if (!trace_file) { 233 pr_err("failed to open trace_pipe\n"); 234 goto out_reset; 235 } 236 237 trace_fd = open(trace_file, O_RDONLY); 238 239 put_tracing_file(trace_file); 240 241 if (trace_fd < 0) { 242 pr_err("failed to open trace_pipe\n"); 243 goto out_reset; 244 } 245 246 fcntl(trace_fd, F_SETFL, O_NONBLOCK); 247 pollfd.fd = trace_fd; 248 249 if (write_tracing_file("tracing_on", "1") < 0) { 250 pr_err("can't enable tracing\n"); 251 goto out_close_fd; 252 } 253 254 setup_pager(); 255 256 perf_evlist__start_workload(ftrace->evlist); 257 258 while (!done) { 259 if (poll(&pollfd, 1, -1) < 0) 260 break; 261 262 if (pollfd.revents & POLLIN) { 263 int n = read(trace_fd, buf, sizeof(buf)); 264 if (n < 0) 265 break; 266 if (fwrite(buf, n, 1, stdout) != 1) 267 break; 268 } 269 } 270 271 write_tracing_file("tracing_on", "0"); 272 273 /* read remaining buffer contents */ 274 while (true) { 275 int n = read(trace_fd, buf, sizeof(buf)); 276 if (n <= 0) 277 break; 278 if (fwrite(buf, n, 1, stdout) != 1) 279 break; 280 } 281 282 out_close_fd: 283 close(trace_fd); 284 out_reset: 285 reset_tracing_files(ftrace); 286 out: 287 return done ? 0 : -1; 288 } 289 290 static int perf_ftrace_config(const char *var, const char *value, void *cb) 291 { 292 struct perf_ftrace *ftrace = cb; 293 294 if (prefixcmp(var, "ftrace.")) 295 return 0; 296 297 if (strcmp(var, "ftrace.tracer")) 298 return -1; 299 300 if (!strcmp(value, "function_graph") || 301 !strcmp(value, "function")) { 302 ftrace->tracer = value; 303 return 0; 304 } 305 306 pr_err("Please select \"function_graph\" (default) or \"function\"\n"); 307 return -1; 308 } 309 310 int cmd_ftrace(int argc, const char **argv) 311 { 312 int ret; 313 struct perf_ftrace ftrace = { 314 .tracer = DEFAULT_TRACER, 315 .target = { .uid = UINT_MAX, }, 316 }; 317 const char * const ftrace_usage[] = { 318 "perf ftrace [<options>] [<command>]", 319 "perf ftrace [<options>] -- <command> [<options>]", 320 NULL 321 }; 322 const struct option ftrace_options[] = { 323 OPT_STRING('t', "tracer", &ftrace.tracer, "tracer", 324 "tracer to use: function_graph(default) or function"), 325 OPT_STRING('p', "pid", &ftrace.target.pid, "pid", 326 "trace on existing process id"), 327 OPT_INCR('v', "verbose", &verbose, 328 "be more verbose"), 329 OPT_BOOLEAN('a', "all-cpus", &ftrace.target.system_wide, 330 "system-wide collection from all CPUs"), 331 OPT_STRING('C', "cpu", &ftrace.target.cpu_list, "cpu", 332 "list of cpus to monitor"), 333 OPT_END() 334 }; 335 336 ret = perf_config(perf_ftrace_config, &ftrace); 337 if (ret < 0) 338 return -1; 339 340 argc = parse_options(argc, argv, ftrace_options, ftrace_usage, 341 PARSE_OPT_STOP_AT_NON_OPTION); 342 if (!argc && target__none(&ftrace.target)) 343 usage_with_options(ftrace_usage, ftrace_options); 344 345 ret = target__validate(&ftrace.target); 346 if (ret) { 347 char errbuf[512]; 348 349 target__strerror(&ftrace.target, ret, errbuf, 512); 350 pr_err("%s\n", errbuf); 351 return -EINVAL; 352 } 353 354 ftrace.evlist = perf_evlist__new(); 355 if (ftrace.evlist == NULL) 356 return -ENOMEM; 357 358 ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target); 359 if (ret < 0) 360 goto out_delete_evlist; 361 362 ret = __cmd_ftrace(&ftrace, argc, argv); 363 364 out_delete_evlist: 365 perf_evlist__delete(ftrace.evlist); 366 367 return ret; 368 } 369