Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b9c0c59 authored by Tom Zanussi's avatar Tom Zanussi Committed by Ingo Molnar
Browse files

perf trace/scripting: List available scripts



Lists the available perf trace scripts, one per line e.g.:

root@tropicana:~# perf trace -l
List of available trace scripts:
  workqueue-stats                      workqueue stats (ins/exe/create/destroy)
  wakeup-latency                       system-wide min/max/avg wakeup latency
  rw-by-file <comm>                    r/w activity for a program, by file
  check-perf-trace                     useless but exhaustive test script
  rw-by-pid                            system-wide r/w activity

To be consistent with the other listing options in perf, the
current latency trace option was changed to '-L', and '-l' is
now used to access the script listing as:

To create the list, it searches each scripts/*/bin directory for
files ending with "-report" and reads information found in
certain comment lines contained in those shell scripts:

  - if the comment line starts with "description:", the rest of the
    line is used as a 'half-line' description.  To keep each line in
    the list to a single line, the description should be limited to 40
    characters (the rest of the line contains the script name and
    args)

  - if the comment line starts with "args:", the rest of the line
    names the args the script supports.  Required args should be
    surrounded by <> brackets, optional args by [] brackets.

The current scripts in scripts/perl/bin have also been updated
with description: and args: comments.

Signed-off-by: default avatarTom Zanussi <tzanussi@gmail.com>
Cc: fweisbec@gmail.com
Cc: rostedt@goodmis.org
LKML-Reference: <1260867220-15699-5-git-send-email-tzanussi@gmail.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 8f11d85a
Loading
Loading
Loading
Loading
+198 −1
Original line number Diff line number Diff line
@@ -274,6 +274,201 @@ static int parse_scriptname(const struct option *opt __used,
	return 0;
}

#define for_each_lang(scripts_dir, lang_dirent, lang_next)		\
	while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) &&	\
	       lang_next)						\
		if (lang_dirent.d_type == DT_DIR &&			\
		    (strcmp(lang_dirent.d_name, ".")) &&		\
		    (strcmp(lang_dirent.d_name, "..")))

#define for_each_script(lang_dir, script_dirent, script_next)		\
	while (!readdir_r(lang_dir, &script_dirent, &script_next) &&	\
	       script_next)						\
		if (script_dirent.d_type != DT_DIR)


#define RECORD_SUFFIX			"-record"
#define REPORT_SUFFIX			"-report"

struct script_desc {
	struct list_head	node;
	char			*name;
	char			*half_liner;
	char			*args;
};

LIST_HEAD(script_descs);

static struct script_desc *script_desc__new(const char *name)
{
	struct script_desc *s = zalloc(sizeof(*s));

	if (s != NULL)
		s->name = strdup(name);

	return s;
}

static void script_desc__delete(struct script_desc *s)
{
	free(s->name);
	free(s);
}

static void script_desc__add(struct script_desc *s)
{
	list_add_tail(&s->node, &script_descs);
}

static struct script_desc *script_desc__find(const char *name)
{
	struct script_desc *s;

	list_for_each_entry(s, &script_descs, node)
		if (strcasecmp(s->name, name) == 0)
			return s;
	return NULL;
}

static struct script_desc *script_desc__findnew(const char *name)
{
	struct script_desc *s = script_desc__find(name);

	if (s)
		return s;

	s = script_desc__new(name);
	if (!s)
		goto out_delete_desc;

	script_desc__add(s);

	return s;

out_delete_desc:
	script_desc__delete(s);

	return NULL;
}

static char *ends_with(char *str, const char *suffix)
{
	size_t suffix_len = strlen(suffix);
	char *p = str;

	if (strlen(str) > suffix_len) {
		p = str + strlen(str) - suffix_len;
		if (!strncmp(p, suffix, suffix_len))
			return p;
	}

	return NULL;
}

static char *ltrim(char *str)
{
	int len = strlen(str);

	while (len && isspace(*str)) {
		len--;
		str++;
	}

	return str;
}

static int read_script_info(struct script_desc *desc, const char *filename)
{
	char line[BUFSIZ], *p;
	FILE *fp;

	fp = fopen(filename, "r");
	if (!fp)
		return -1;

	while (fgets(line, sizeof(line), fp)) {
		p = ltrim(line);
		if (strlen(p) == 0)
			continue;
		if (*p != '#')
			continue;
		p++;
		if (strlen(p) && *p == '!')
			continue;

		p = ltrim(p);
		if (strlen(p) && p[strlen(p) - 1] == '\n')
			p[strlen(p) - 1] = '\0';

		if (!strncmp(p, "description:", strlen("description:"))) {
			p += strlen("description:");
			desc->half_liner = strdup(ltrim(p));
			continue;
		}

		if (!strncmp(p, "args:", strlen("args:"))) {
			p += strlen("args:");
			desc->args = strdup(ltrim(p));
			continue;
		}
	}

	fclose(fp);

	return 0;
}

static int list_available_scripts(const struct option *opt __used,
				  const char *s __used, int unset __used)
{
	struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
	char scripts_path[MAXPATHLEN];
	DIR *scripts_dir, *lang_dir;
	char script_path[MAXPATHLEN];
	char lang_path[MAXPATHLEN];
	struct script_desc *desc;
	char first_half[BUFSIZ];
	char *script_root;
	char *str;

	snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());

	scripts_dir = opendir(scripts_path);
	if (!scripts_dir)
		return -1;

	for_each_lang(scripts_dir, lang_dirent, lang_next) {
		snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
			 lang_dirent.d_name);
		lang_dir = opendir(lang_path);
		if (!lang_dir)
			continue;

		for_each_script(lang_dir, script_dirent, script_next) {
			script_root = strdup(script_dirent.d_name);
			str = ends_with(script_root, REPORT_SUFFIX);
			if (str) {
				*str = '\0';
				desc = script_desc__findnew(script_root);
				snprintf(script_path, MAXPATHLEN, "%s/%s",
					 lang_path, script_dirent.d_name);
				read_script_info(desc, script_path);
			}
			free(script_root);
		}
	}

	fprintf(stdout, "List of available trace scripts:\n");
	list_for_each_entry(desc, &script_descs, node) {
		sprintf(first_half, "%s %s", desc->name,
			desc->args ? desc->args : "");
		fprintf(stdout, "  %-36s %s\n", first_half,
			desc->half_liner ? desc->half_liner : "");
	}

	exit(0);
}

static const char * const annotate_usage[] = {
	"perf trace [<options>] <command>",
	NULL
@@ -284,8 +479,10 @@ static const struct option options[] = {
		    "dump raw trace in ASCII"),
	OPT_BOOLEAN('v', "verbose", &verbose,
		    "be more verbose (show symbol address, etc)"),
	OPT_BOOLEAN('l', "latency", &latency_format,
	OPT_BOOLEAN('L', "Latency", &latency_format,
		    "show latency attributes (irqs/preemption disabled, etc)"),
	OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts",
			   list_available_scripts),
	OPT_CALLBACK('s', "script", NULL, "name",
		     "script file name (lang:script name, script name, or *)",
		     parse_scriptname),
+1 −0
Original line number Diff line number Diff line
#!/bin/bash
# description: useless but exhaustive test script
perf trace -s ~/libexec/perf-core/scripts/perl/check-perf-trace.pl


+2 −0
Original line number Diff line number Diff line
#!/bin/bash
# description: r/w activity for a program, by file
# args: <comm>
perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $1


+1 −0
Original line number Diff line number Diff line
#!/bin/bash
# description: system-wide r/w activity
perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl


+1 −0
Original line number Diff line number Diff line
#!/bin/bash
# description: system-wide min/max/avg wakeup latency
perf trace -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl


Loading