1#include "builtin.h"
2
3#include "perf.h"
4#include "util/cache.h"
5#include "util/debug.h"
6#include "util/exec_cmd.h"
7#include "util/header.h"
8#include "util/parse-options.h"
9#include "util/session.h"
10#include "util/symbol.h"
11#include "util/thread.h"
12#include "util/trace-event.h"
13#include "util/util.h"
14
15static char const		*script_name;
16static char const		*generate_script_lang;
17static bool			debug_mode;
18static u64			last_timestamp;
19static u64			nr_unordered;
20
21static int default_start_script(const char *script __unused,
22				int argc __unused,
23				const char **argv __unused)
24{
25	return 0;
26}
27
28static int default_stop_script(void)
29{
30	return 0;
31}
32
33static int default_generate_script(const char *outfile __unused)
34{
35	return 0;
36}
37
38static struct scripting_ops default_scripting_ops = {
39	.start_script		= default_start_script,
40	.stop_script		= default_stop_script,
41	.process_event		= print_event,
42	.generate_script	= default_generate_script,
43};
44
45static struct scripting_ops	*scripting_ops;
46
47static void setup_scripting(void)
48{
49	/* make sure PERF_EXEC_PATH is set for scripts */
50	perf_set_argv_exec_path(perf_exec_path());
51
52	setup_perl_scripting();
53	setup_python_scripting();
54
55	scripting_ops = &default_scripting_ops;
56}
57
58static int cleanup_scripting(void)
59{
60	pr_debug("\nperf trace script stopped\n");
61
62	return scripting_ops->stop_script();
63}
64
65static char const		*input_name = "perf.data";
66
67static int process_sample_event(event_t *event, struct perf_session *session)
68{
69	struct sample_data data;
70	struct thread *thread;
71
72	memset(&data, 0, sizeof(data));
73	data.time = -1;
74	data.cpu = -1;
75	data.period = 1;
76
77	event__parse_sample(event, session->sample_type, &data);
78
79	dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc,
80		    data.pid, data.tid, data.ip, data.period);
81
82	thread = perf_session__findnew(session, event->ip.pid);
83	if (thread == NULL) {
84		pr_debug("problem processing %d event, skipping it.\n",
85			 event->header.type);
86		return -1;
87	}
88
89	if (session->sample_type & PERF_SAMPLE_RAW) {
90		if (debug_mode) {
91			if (data.time < last_timestamp) {
92				pr_err("Samples misordered, previous: %llu "
93					"this: %llu\n", last_timestamp,
94					data.time);
95				nr_unordered++;
96			}
97			last_timestamp = data.time;
98			return 0;
99		}
100		scripting_ops->process_event(data.cpu, data.raw_data,
101					     data.raw_size,
102					     data.time, thread->comm);
103	}
104
105	session->hists.stats.total_period += data.period;
106	return 0;
107}
108
109static u64 nr_lost;
110
111static int process_lost_event(event_t *event, struct perf_session *session __used)
112{
113	nr_lost += event->lost.lost;
114
115	return 0;
116}
117
118static struct perf_event_ops event_ops = {
119	.sample	= process_sample_event,
120	.comm	= event__process_comm,
121	.attr	= event__process_attr,
122	.event_type = event__process_event_type,
123	.tracing_data = event__process_tracing_data,
124	.build_id = event__process_build_id,
125	.lost = process_lost_event,
126	.ordered_samples = true,
127};
128
129extern volatile int session_done;
130
131static void sig_handler(int sig __unused)
132{
133	session_done = 1;
134}
135
136static int __cmd_trace(struct perf_session *session)
137{
138	int ret;
139
140	signal(SIGINT, sig_handler);
141
142	ret = perf_session__process_events(session, &event_ops);
143
144	if (debug_mode) {
145		pr_err("Misordered timestamps: %llu\n", nr_unordered);
146		pr_err("Lost events: %llu\n", nr_lost);
147	}
148
149	return ret;
150}
151
152struct script_spec {
153	struct list_head	node;
154	struct scripting_ops	*ops;
155	char			spec[0];
156};
157
158LIST_HEAD(script_specs);
159
160static struct script_spec *script_spec__new(const char *spec,
161					    struct scripting_ops *ops)
162{
163	struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1);
164
165	if (s != NULL) {
166		strcpy(s->spec, spec);
167		s->ops = ops;
168	}
169
170	return s;
171}
172
173static void script_spec__delete(struct script_spec *s)
174{
175	free(s->spec);
176	free(s);
177}
178
179static void script_spec__add(struct script_spec *s)
180{
181	list_add_tail(&s->node, &script_specs);
182}
183
184static struct script_spec *script_spec__find(const char *spec)
185{
186	struct script_spec *s;
187
188	list_for_each_entry(s, &script_specs, node)
189		if (strcasecmp(s->spec, spec) == 0)
190			return s;
191	return NULL;
192}
193
194static struct script_spec *script_spec__findnew(const char *spec,
195						struct scripting_ops *ops)
196{
197	struct script_spec *s = script_spec__find(spec);
198
199	if (s)
200		return s;
201
202	s = script_spec__new(spec, ops);
203	if (!s)
204		goto out_delete_spec;
205
206	script_spec__add(s);
207
208	return s;
209
210out_delete_spec:
211	script_spec__delete(s);
212
213	return NULL;
214}
215
216int script_spec_register(const char *spec, struct scripting_ops *ops)
217{
218	struct script_spec *s;
219
220	s = script_spec__find(spec);
221	if (s)
222		return -1;
223
224	s = script_spec__findnew(spec, ops);
225	if (!s)
226		return -1;
227
228	return 0;
229}
230
231static struct scripting_ops *script_spec__lookup(const char *spec)
232{
233	struct script_spec *s = script_spec__find(spec);
234	if (!s)
235		return NULL;
236
237	return s->ops;
238}
239
240static void list_available_languages(void)
241{
242	struct script_spec *s;
243
244	fprintf(stderr, "\n");
245	fprintf(stderr, "Scripting language extensions (used in "
246		"perf trace -s [spec:]script.[spec]):\n\n");
247
248	list_for_each_entry(s, &script_specs, node)
249		fprintf(stderr, "  %-42s [%s]\n", s->spec, s->ops->name);
250
251	fprintf(stderr, "\n");
252}
253
254static int parse_scriptname(const struct option *opt __used,
255			    const char *str, int unset __used)
256{
257	char spec[PATH_MAX];
258	const char *script, *ext;
259	int len;
260
261	if (strcmp(str, "lang") == 0) {
262		list_available_languages();
263		exit(0);
264	}
265
266	script = strchr(str, ':');
267	if (script) {
268		len = script - str;
269		if (len >= PATH_MAX) {
270			fprintf(stderr, "invalid language specifier");
271			return -1;
272		}
273		strncpy(spec, str, len);
274		spec[len] = '\0';
275		scripting_ops = script_spec__lookup(spec);
276		if (!scripting_ops) {
277			fprintf(stderr, "invalid language specifier");
278			return -1;
279		}
280		script++;
281	} else {
282		script = str;
283		ext = strchr(script, '.');
284		if (!ext) {
285			fprintf(stderr, "invalid script extension");
286			return -1;
287		}
288		scripting_ops = script_spec__lookup(++ext);
289		if (!scripting_ops) {
290			fprintf(stderr, "invalid script extension");
291			return -1;
292		}
293	}
294
295	script_name = strdup(script);
296
297	return 0;
298}
299
300#define for_each_lang(scripts_dir, lang_dirent, lang_next)		\
301	while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) &&	\
302	       lang_next)						\
303		if (lang_dirent.d_type == DT_DIR &&			\
304		    (strcmp(lang_dirent.d_name, ".")) &&		\
305		    (strcmp(lang_dirent.d_name, "..")))
306
307#define for_each_script(lang_dir, script_dirent, script_next)		\
308	while (!readdir_r(lang_dir, &script_dirent, &script_next) &&	\
309	       script_next)						\
310		if (script_dirent.d_type != DT_DIR)
311
312
313#define RECORD_SUFFIX			"-record"
314#define REPORT_SUFFIX			"-report"
315
316struct script_desc {
317	struct list_head	node;
318	char			*name;
319	char			*half_liner;
320	char			*args;
321};
322
323LIST_HEAD(script_descs);
324
325static struct script_desc *script_desc__new(const char *name)
326{
327	struct script_desc *s = zalloc(sizeof(*s));
328
329	if (s != NULL)
330		s->name = strdup(name);
331
332	return s;
333}
334
335static void script_desc__delete(struct script_desc *s)
336{
337	free(s->name);
338	free(s);
339}
340
341static void script_desc__add(struct script_desc *s)
342{
343	list_add_tail(&s->node, &script_descs);
344}
345
346static struct script_desc *script_desc__find(const char *name)
347{
348	struct script_desc *s;
349
350	list_for_each_entry(s, &script_descs, node)
351		if (strcasecmp(s->name, name) == 0)
352			return s;
353	return NULL;
354}
355
356static struct script_desc *script_desc__findnew(const char *name)
357{
358	struct script_desc *s = script_desc__find(name);
359
360	if (s)
361		return s;
362
363	s = script_desc__new(name);
364	if (!s)
365		goto out_delete_desc;
366
367	script_desc__add(s);
368
369	return s;
370
371out_delete_desc:
372	script_desc__delete(s);
373
374	return NULL;
375}
376
377static char *ends_with(char *str, const char *suffix)
378{
379	size_t suffix_len = strlen(suffix);
380	char *p = str;
381
382	if (strlen(str) > suffix_len) {
383		p = str + strlen(str) - suffix_len;
384		if (!strncmp(p, suffix, suffix_len))
385			return p;
386	}
387
388	return NULL;
389}
390
391static char *ltrim(char *str)
392{
393	int len = strlen(str);
394
395	while (len && isspace(*str)) {
396		len--;
397		str++;
398	}
399
400	return str;
401}
402
403static int read_script_info(struct script_desc *desc, const char *filename)
404{
405	char line[BUFSIZ], *p;
406	FILE *fp;
407
408	fp = fopen(filename, "r");
409	if (!fp)
410		return -1;
411
412	while (fgets(line, sizeof(line), fp)) {
413		p = ltrim(line);
414		if (strlen(p) == 0)
415			continue;
416		if (*p != '#')
417			continue;
418		p++;
419		if (strlen(p) && *p == '!')
420			continue;
421
422		p = ltrim(p);
423		if (strlen(p) && p[strlen(p) - 1] == '\n')
424			p[strlen(p) - 1] = '\0';
425
426		if (!strncmp(p, "description:", strlen("description:"))) {
427			p += strlen("description:");
428			desc->half_liner = strdup(ltrim(p));
429			continue;
430		}
431
432		if (!strncmp(p, "args:", strlen("args:"))) {
433			p += strlen("args:");
434			desc->args = strdup(ltrim(p));
435			continue;
436		}
437	}
438
439	fclose(fp);
440
441	return 0;
442}
443
444static int list_available_scripts(const struct option *opt __used,
445				  const char *s __used, int unset __used)
446{
447	struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
448	char scripts_path[MAXPATHLEN];
449	DIR *scripts_dir, *lang_dir;
450	char script_path[MAXPATHLEN];
451	char lang_path[MAXPATHLEN];
452	struct script_desc *desc;
453	char first_half[BUFSIZ];
454	char *script_root;
455	char *str;
456
457	snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
458
459	scripts_dir = opendir(scripts_path);
460	if (!scripts_dir)
461		return -1;
462
463	for_each_lang(scripts_dir, lang_dirent, lang_next) {
464		snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
465			 lang_dirent.d_name);
466		lang_dir = opendir(lang_path);
467		if (!lang_dir)
468			continue;
469
470		for_each_script(lang_dir, script_dirent, script_next) {
471			script_root = strdup(script_dirent.d_name);
472			str = ends_with(script_root, REPORT_SUFFIX);
473			if (str) {
474				*str = '\0';
475				desc = script_desc__findnew(script_root);
476				snprintf(script_path, MAXPATHLEN, "%s/%s",
477					 lang_path, script_dirent.d_name);
478				read_script_info(desc, script_path);
479			}
480			free(script_root);
481		}
482	}
483
484	fprintf(stdout, "List of available trace scripts:\n");
485	list_for_each_entry(desc, &script_descs, node) {
486		sprintf(first_half, "%s %s", desc->name,
487			desc->args ? desc->args : "");
488		fprintf(stdout, "  %-36s %s\n", first_half,
489			desc->half_liner ? desc->half_liner : "");
490	}
491
492	exit(0);
493}
494
495static char *get_script_path(const char *script_root, const char *suffix)
496{
497	struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
498	char scripts_path[MAXPATHLEN];
499	char script_path[MAXPATHLEN];
500	DIR *scripts_dir, *lang_dir;
501	char lang_path[MAXPATHLEN];
502	char *str, *__script_root;
503	char *path = NULL;
504
505	snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
506
507	scripts_dir = opendir(scripts_path);
508	if (!scripts_dir)
509		return NULL;
510
511	for_each_lang(scripts_dir, lang_dirent, lang_next) {
512		snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
513			 lang_dirent.d_name);
514		lang_dir = opendir(lang_path);
515		if (!lang_dir)
516			continue;
517
518		for_each_script(lang_dir, script_dirent, script_next) {
519			__script_root = strdup(script_dirent.d_name);
520			str = ends_with(__script_root, suffix);
521			if (str) {
522				*str = '\0';
523				if (strcmp(__script_root, script_root))
524					continue;
525				snprintf(script_path, MAXPATHLEN, "%s/%s",
526					 lang_path, script_dirent.d_name);
527				path = strdup(script_path);
528				free(__script_root);
529				break;
530			}
531			free(__script_root);
532		}
533	}
534
535	return path;
536}
537
538static const char * const trace_usage[] = {
539	"perf trace [<options>] <command>",
540	NULL
541};
542
543static const struct option options[] = {
544	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
545		    "dump raw trace in ASCII"),
546	OPT_INCR('v', "verbose", &verbose,
547		    "be more verbose (show symbol address, etc)"),
548	OPT_BOOLEAN('L', "Latency", &latency_format,
549		    "show latency attributes (irqs/preemption disabled, etc)"),
550	OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts",
551			   list_available_scripts),
552	OPT_CALLBACK('s', "script", NULL, "name",
553		     "script file name (lang:script name, script name, or *)",
554		     parse_scriptname),
555	OPT_STRING('g', "gen-script", &generate_script_lang, "lang",
556		   "generate perf-trace.xx script in specified language"),
557	OPT_STRING('i', "input", &input_name, "file",
558		    "input file name"),
559	OPT_BOOLEAN('d', "debug-mode", &debug_mode,
560		   "do various checks like samples ordering and lost events"),
561
562	OPT_END()
563};
564
565int cmd_trace(int argc, const char **argv, const char *prefix __used)
566{
567	struct perf_session *session;
568	const char *suffix = NULL;
569	const char **__argv;
570	char *script_path;
571	int i, err;
572
573	if (argc >= 2 && strncmp(argv[1], "rec", strlen("rec")) == 0) {
574		if (argc < 3) {
575			fprintf(stderr,
576				"Please specify a record script\n");
577			return -1;
578		}
579		suffix = RECORD_SUFFIX;
580	}
581
582	if (argc >= 2 && strncmp(argv[1], "rep", strlen("rep")) == 0) {
583		if (argc < 3) {
584			fprintf(stderr,
585				"Please specify a report script\n");
586			return -1;
587		}
588		suffix = REPORT_SUFFIX;
589	}
590
591	if (!suffix && argc >= 2 && strncmp(argv[1], "-", strlen("-")) != 0) {
592		char *record_script_path, *report_script_path;
593		int live_pipe[2];
594		pid_t pid;
595
596		record_script_path = get_script_path(argv[1], RECORD_SUFFIX);
597		if (!record_script_path) {
598			fprintf(stderr, "record script not found\n");
599			return -1;
600		}
601
602		report_script_path = get_script_path(argv[1], REPORT_SUFFIX);
603		if (!report_script_path) {
604			fprintf(stderr, "report script not found\n");
605			return -1;
606		}
607
608		if (pipe(live_pipe) < 0) {
609			perror("failed to create pipe");
610			exit(-1);
611		}
612
613		pid = fork();
614		if (pid < 0) {
615			perror("failed to fork");
616			exit(-1);
617		}
618
619		if (!pid) {
620			dup2(live_pipe[1], 1);
621			close(live_pipe[0]);
622
623			__argv = malloc(5 * sizeof(const char *));
624			__argv[0] = "/bin/sh";
625			__argv[1] = record_script_path;
626			__argv[2] = "-o";
627			__argv[3] = "-";
628			__argv[4] = NULL;
629
630			execvp("/bin/sh", (char **)__argv);
631			exit(-1);
632		}
633
634		dup2(live_pipe[0], 0);
635		close(live_pipe[1]);
636
637		__argv = malloc((argc + 3) * sizeof(const char *));
638		__argv[0] = "/bin/sh";
639		__argv[1] = report_script_path;
640		for (i = 2; i < argc; i++)
641			__argv[i] = argv[i];
642		__argv[i++] = "-i";
643		__argv[i++] = "-";
644		__argv[i++] = NULL;
645
646		execvp("/bin/sh", (char **)__argv);
647		exit(-1);
648	}
649
650	if (suffix) {
651		script_path = get_script_path(argv[2], suffix);
652		if (!script_path) {
653			fprintf(stderr, "script not found\n");
654			return -1;
655		}
656
657		__argv = malloc((argc + 1) * sizeof(const char *));
658		__argv[0] = "/bin/sh";
659		__argv[1] = script_path;
660		for (i = 3; i < argc; i++)
661			__argv[i - 1] = argv[i];
662		__argv[argc - 1] = NULL;
663
664		execvp("/bin/sh", (char **)__argv);
665		exit(-1);
666	}
667
668	setup_scripting();
669
670	argc = parse_options(argc, argv, options, trace_usage,
671			     PARSE_OPT_STOP_AT_NON_OPTION);
672
673	if (symbol__init() < 0)
674		return -1;
675	if (!script_name)
676		setup_pager();
677
678	session = perf_session__new(input_name, O_RDONLY, 0, false);
679	if (session == NULL)
680		return -ENOMEM;
681
682	if (strcmp(input_name, "-") &&
683	    !perf_session__has_traces(session, "record -R"))
684		return -EINVAL;
685
686	if (generate_script_lang) {
687		struct stat perf_stat;
688
689		int input = open(input_name, O_RDONLY);
690		if (input < 0) {
691			perror("failed to open file");
692			exit(-1);
693		}
694
695		err = fstat(input, &perf_stat);
696		if (err < 0) {
697			perror("failed to stat file");
698			exit(-1);
699		}
700
701		if (!perf_stat.st_size) {
702			fprintf(stderr, "zero-sized file, nothing to do!\n");
703			exit(0);
704		}
705
706		scripting_ops = script_spec__lookup(generate_script_lang);
707		if (!scripting_ops) {
708			fprintf(stderr, "invalid language specifier");
709			return -1;
710		}
711
712		err = scripting_ops->generate_script("perf-trace");
713		goto out;
714	}
715
716	if (script_name) {
717		err = scripting_ops->start_script(script_name, argc, argv);
718		if (err)
719			goto out;
720		pr_debug("perf trace started with script %s\n\n", script_name);
721	}
722
723	err = __cmd_trace(session);
724
725	perf_session__delete(session);
726	cleanup_scripting();
727out:
728	return err;
729}
730