|
@@ -10,6 +10,7 @@
|
|
|
#include "util/symbol.h"
|
|
|
#include "util/thread.h"
|
|
|
#include "util/trace-event.h"
|
|
|
+#include "util/parse-options.h"
|
|
|
#include "util/util.h"
|
|
|
|
|
|
static char const *script_name;
|
|
@@ -17,6 +18,7 @@ static char const *generate_script_lang;
|
|
|
static bool debug_mode;
|
|
|
static u64 last_timestamp;
|
|
|
static u64 nr_unordered;
|
|
|
+extern const struct option record_options[];
|
|
|
|
|
|
static int default_start_script(const char *script __unused,
|
|
|
int argc __unused,
|
|
@@ -328,7 +330,7 @@ static struct script_desc *script_desc__new(const char *name)
|
|
|
{
|
|
|
struct script_desc *s = zalloc(sizeof(*s));
|
|
|
|
|
|
- if (s != NULL)
|
|
|
+ if (s != NULL && name)
|
|
|
s->name = strdup(name);
|
|
|
|
|
|
return s;
|
|
@@ -337,6 +339,8 @@ static struct script_desc *script_desc__new(const char *name)
|
|
|
static void script_desc__delete(struct script_desc *s)
|
|
|
{
|
|
|
free(s->name);
|
|
|
+ free(s->half_liner);
|
|
|
+ free(s->args);
|
|
|
free(s);
|
|
|
}
|
|
|
|
|
@@ -537,8 +541,40 @@ static char *get_script_path(const char *script_root, const char *suffix)
|
|
|
return path;
|
|
|
}
|
|
|
|
|
|
+static bool is_top_script(const char *script_path)
|
|
|
+{
|
|
|
+ return ends_with((char *)script_path, "top") == NULL ? false : true;
|
|
|
+}
|
|
|
+
|
|
|
+static int has_required_arg(char *script_path)
|
|
|
+{
|
|
|
+ struct script_desc *desc;
|
|
|
+ int n_args = 0;
|
|
|
+ char *p;
|
|
|
+
|
|
|
+ desc = script_desc__new(NULL);
|
|
|
+
|
|
|
+ if (read_script_info(desc, script_path))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (!desc->args)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ for (p = desc->args; *p; p++)
|
|
|
+ if (*p == '<')
|
|
|
+ n_args++;
|
|
|
+out:
|
|
|
+ script_desc__delete(desc);
|
|
|
+
|
|
|
+ return n_args;
|
|
|
+}
|
|
|
+
|
|
|
static const char * const trace_usage[] = {
|
|
|
- "perf trace [<options>] <command>",
|
|
|
+ "perf trace [<options>]",
|
|
|
+ "perf trace [<options>] record <script> [<record-options>] <command>",
|
|
|
+ "perf trace [<options>] report <script> [script-args]",
|
|
|
+ "perf trace [<options>] <script> [<record-options>] <command>",
|
|
|
+ "perf trace [<options>] <top-script> [script-args]",
|
|
|
NULL
|
|
|
};
|
|
|
|
|
@@ -564,50 +600,81 @@ static const struct option options[] = {
|
|
|
OPT_END()
|
|
|
};
|
|
|
|
|
|
+static bool have_cmd(int argc, const char **argv)
|
|
|
+{
|
|
|
+ char **__argv = malloc(sizeof(const char *) * argc);
|
|
|
+
|
|
|
+ if (!__argv)
|
|
|
+ die("malloc");
|
|
|
+ memcpy(__argv, argv, sizeof(const char *) * argc);
|
|
|
+ argc = parse_options(argc, (const char **)__argv, record_options,
|
|
|
+ NULL, PARSE_OPT_STOP_AT_NON_OPTION);
|
|
|
+ free(__argv);
|
|
|
+
|
|
|
+ return argc != 0;
|
|
|
+}
|
|
|
+
|
|
|
int cmd_trace(int argc, const char **argv, const char *prefix __used)
|
|
|
{
|
|
|
+ char *rec_script_path = NULL;
|
|
|
+ char *rep_script_path = NULL;
|
|
|
struct perf_session *session;
|
|
|
- const char *suffix = NULL;
|
|
|
+ char *script_path = NULL;
|
|
|
const char **__argv;
|
|
|
- char *script_path;
|
|
|
- int i, err;
|
|
|
+ bool system_wide;
|
|
|
+ int i, j, err;
|
|
|
|
|
|
- if (argc >= 2 && strncmp(argv[1], "rec", strlen("rec")) == 0) {
|
|
|
- if (argc < 3) {
|
|
|
- fprintf(stderr,
|
|
|
- "Please specify a record script\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
- suffix = RECORD_SUFFIX;
|
|
|
+ setup_scripting();
|
|
|
+
|
|
|
+ argc = parse_options(argc, argv, options, trace_usage,
|
|
|
+ PARSE_OPT_STOP_AT_NON_OPTION);
|
|
|
+
|
|
|
+ if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) {
|
|
|
+ rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);
|
|
|
+ if (!rec_script_path)
|
|
|
+ return cmd_record(argc, argv, NULL);
|
|
|
}
|
|
|
|
|
|
- if (argc >= 2 && strncmp(argv[1], "rep", strlen("rep")) == 0) {
|
|
|
- if (argc < 3) {
|
|
|
+ if (argc > 1 && !strncmp(argv[0], "rep", strlen("rep"))) {
|
|
|
+ rep_script_path = get_script_path(argv[1], REPORT_SUFFIX);
|
|
|
+ if (!rep_script_path) {
|
|
|
fprintf(stderr,
|
|
|
- "Please specify a report script\n");
|
|
|
+ "Please specify a valid report script"
|
|
|
+ "(see 'perf trace -l' for listing)\n");
|
|
|
return -1;
|
|
|
}
|
|
|
- suffix = REPORT_SUFFIX;
|
|
|
}
|
|
|
|
|
|
/* make sure PERF_EXEC_PATH is set for scripts */
|
|
|
perf_set_argv_exec_path(perf_exec_path());
|
|
|
|
|
|
- if (!suffix && argc >= 2 && strncmp(argv[1], "-", strlen("-")) != 0) {
|
|
|
- char *record_script_path, *report_script_path;
|
|
|
+ if (argc && !script_name && !rec_script_path && !rep_script_path) {
|
|
|
int live_pipe[2];
|
|
|
+ int rep_args;
|
|
|
pid_t pid;
|
|
|
|
|
|
- record_script_path = get_script_path(argv[1], RECORD_SUFFIX);
|
|
|
- if (!record_script_path) {
|
|
|
- fprintf(stderr, "record script not found\n");
|
|
|
- return -1;
|
|
|
+ rec_script_path = get_script_path(argv[0], RECORD_SUFFIX);
|
|
|
+ rep_script_path = get_script_path(argv[0], REPORT_SUFFIX);
|
|
|
+
|
|
|
+ if (!rec_script_path && !rep_script_path) {
|
|
|
+ fprintf(stderr, " Couldn't find script %s\n\n See perf"
|
|
|
+ " trace -l for available scripts.\n", argv[0]);
|
|
|
+ usage_with_options(trace_usage, options);
|
|
|
}
|
|
|
|
|
|
- report_script_path = get_script_path(argv[1], REPORT_SUFFIX);
|
|
|
- if (!report_script_path) {
|
|
|
- fprintf(stderr, "report script not found\n");
|
|
|
- return -1;
|
|
|
+ if (is_top_script(argv[0])) {
|
|
|
+ rep_args = argc - 1;
|
|
|
+ } else {
|
|
|
+ int rec_args;
|
|
|
+
|
|
|
+ rep_args = has_required_arg(rep_script_path);
|
|
|
+ rec_args = (argc - 1) - rep_args;
|
|
|
+ if (rec_args < 0) {
|
|
|
+ fprintf(stderr, " %s script requires options."
|
|
|
+ "\n\n See perf trace -l for available "
|
|
|
+ "scripts and options.\n", argv[0]);
|
|
|
+ usage_with_options(trace_usage, options);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (pipe(live_pipe) < 0) {
|
|
@@ -622,60 +689,84 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
|
|
|
}
|
|
|
|
|
|
if (!pid) {
|
|
|
+ system_wide = true;
|
|
|
+ j = 0;
|
|
|
+
|
|
|
dup2(live_pipe[1], 1);
|
|
|
close(live_pipe[0]);
|
|
|
|
|
|
- __argv = malloc(6 * sizeof(const char *));
|
|
|
- __argv[0] = "/bin/sh";
|
|
|
- __argv[1] = record_script_path;
|
|
|
- __argv[2] = "-q";
|
|
|
- __argv[3] = "-o";
|
|
|
- __argv[4] = "-";
|
|
|
- __argv[5] = NULL;
|
|
|
+ if (!is_top_script(argv[0]))
|
|
|
+ system_wide = !have_cmd(argc - rep_args,
|
|
|
+ &argv[rep_args]);
|
|
|
+
|
|
|
+ __argv = malloc((argc + 6) * sizeof(const char *));
|
|
|
+ if (!__argv)
|
|
|
+ die("malloc");
|
|
|
+
|
|
|
+ __argv[j++] = "/bin/sh";
|
|
|
+ __argv[j++] = rec_script_path;
|
|
|
+ if (system_wide)
|
|
|
+ __argv[j++] = "-a";
|
|
|
+ __argv[j++] = "-q";
|
|
|
+ __argv[j++] = "-o";
|
|
|
+ __argv[j++] = "-";
|
|
|
+ for (i = rep_args + 1; i < argc; i++)
|
|
|
+ __argv[j++] = argv[i];
|
|
|
+ __argv[j++] = NULL;
|
|
|
|
|
|
execvp("/bin/sh", (char **)__argv);
|
|
|
+ free(__argv);
|
|
|
exit(-1);
|
|
|
}
|
|
|
|
|
|
dup2(live_pipe[0], 0);
|
|
|
close(live_pipe[1]);
|
|
|
|
|
|
- __argv = malloc((argc + 3) * sizeof(const char *));
|
|
|
- __argv[0] = "/bin/sh";
|
|
|
- __argv[1] = report_script_path;
|
|
|
- for (i = 2; i < argc; i++)
|
|
|
- __argv[i] = argv[i];
|
|
|
- __argv[i++] = "-i";
|
|
|
- __argv[i++] = "-";
|
|
|
- __argv[i++] = NULL;
|
|
|
+ __argv = malloc((argc + 4) * sizeof(const char *));
|
|
|
+ if (!__argv)
|
|
|
+ die("malloc");
|
|
|
+ j = 0;
|
|
|
+ __argv[j++] = "/bin/sh";
|
|
|
+ __argv[j++] = rep_script_path;
|
|
|
+ for (i = 1; i < rep_args + 1; i++)
|
|
|
+ __argv[j++] = argv[i];
|
|
|
+ __argv[j++] = "-i";
|
|
|
+ __argv[j++] = "-";
|
|
|
+ __argv[j++] = NULL;
|
|
|
|
|
|
execvp("/bin/sh", (char **)__argv);
|
|
|
+ free(__argv);
|
|
|
exit(-1);
|
|
|
}
|
|
|
|
|
|
- if (suffix) {
|
|
|
- script_path = get_script_path(argv[2], suffix);
|
|
|
- if (!script_path) {
|
|
|
- fprintf(stderr, "script not found\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- __argv = malloc((argc + 1) * sizeof(const char *));
|
|
|
- __argv[0] = "/bin/sh";
|
|
|
- __argv[1] = script_path;
|
|
|
- for (i = 3; i < argc; i++)
|
|
|
- __argv[i - 1] = argv[i];
|
|
|
- __argv[argc - 1] = NULL;
|
|
|
+ if (rec_script_path)
|
|
|
+ script_path = rec_script_path;
|
|
|
+ if (rep_script_path)
|
|
|
+ script_path = rep_script_path;
|
|
|
+
|
|
|
+ if (script_path) {
|
|
|
+ system_wide = false;
|
|
|
+ j = 0;
|
|
|
+
|
|
|
+ if (rec_script_path)
|
|
|
+ system_wide = !have_cmd(argc - 1, &argv[1]);
|
|
|
+
|
|
|
+ __argv = malloc((argc + 2) * sizeof(const char *));
|
|
|
+ if (!__argv)
|
|
|
+ die("malloc");
|
|
|
+ __argv[j++] = "/bin/sh";
|
|
|
+ __argv[j++] = script_path;
|
|
|
+ if (system_wide)
|
|
|
+ __argv[j++] = "-a";
|
|
|
+ for (i = 2; i < argc; i++)
|
|
|
+ __argv[j++] = argv[i];
|
|
|
+ __argv[j++] = NULL;
|
|
|
|
|
|
execvp("/bin/sh", (char **)__argv);
|
|
|
+ free(__argv);
|
|
|
exit(-1);
|
|
|
}
|
|
|
|
|
|
- setup_scripting();
|
|
|
-
|
|
|
- argc = parse_options(argc, argv, options, trace_usage,
|
|
|
- PARSE_OPT_STOP_AT_NON_OPTION);
|
|
|
-
|
|
|
if (symbol__init() < 0)
|
|
|
return -1;
|
|
|
if (!script_name)
|