|
@@ -6,6 +6,46 @@
|
|
#include "util/thread.h"
|
|
#include "util/thread.h"
|
|
#include "util/header.h"
|
|
#include "util/header.h"
|
|
|
|
|
|
|
|
+static char const *script_name;
|
|
|
|
+static char const *generate_script_lang;
|
|
|
|
+
|
|
|
|
+static int default_start_script(const char *script __attribute((unused)))
|
|
|
|
+{
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int default_stop_script(void)
|
|
|
|
+{
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int default_generate_script(const char *outfile __attribute ((unused)))
|
|
|
|
+{
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct scripting_ops default_scripting_ops = {
|
|
|
|
+ .start_script = default_start_script,
|
|
|
|
+ .stop_script = default_stop_script,
|
|
|
|
+ .process_event = print_event,
|
|
|
|
+ .generate_script = default_generate_script,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static struct scripting_ops *scripting_ops;
|
|
|
|
+
|
|
|
|
+static void setup_scripting(void)
|
|
|
|
+{
|
|
|
|
+ /* make sure PERF_EXEC_PATH is set for scripts */
|
|
|
|
+ perf_set_argv_exec_path(perf_exec_path());
|
|
|
|
+
|
|
|
|
+ scripting_ops = &default_scripting_ops;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int cleanup_scripting(void)
|
|
|
|
+{
|
|
|
|
+ return scripting_ops->stop_script();
|
|
|
|
+}
|
|
|
|
+
|
|
#include "util/parse-options.h"
|
|
#include "util/parse-options.h"
|
|
|
|
|
|
#include "perf.h"
|
|
#include "perf.h"
|
|
@@ -13,11 +53,12 @@
|
|
|
|
|
|
#include "util/trace-event.h"
|
|
#include "util/trace-event.h"
|
|
#include "util/data_map.h"
|
|
#include "util/data_map.h"
|
|
|
|
+#include "util/exec_cmd.h"
|
|
|
|
|
|
-static char const *input_name = "perf.data";
|
|
|
|
|
|
+static char const *input_name = "perf.data";
|
|
|
|
|
|
-static struct perf_header *header;
|
|
|
|
-static u64 sample_type;
|
|
|
|
|
|
+static struct perf_header *header;
|
|
|
|
+static u64 sample_type;
|
|
|
|
|
|
static int process_sample_event(event_t *event)
|
|
static int process_sample_event(event_t *event)
|
|
{
|
|
{
|
|
@@ -69,7 +110,8 @@ static int process_sample_event(event_t *event)
|
|
* field, although it should be the same than this perf
|
|
* field, although it should be the same than this perf
|
|
* event pid
|
|
* event pid
|
|
*/
|
|
*/
|
|
- print_event(cpu, raw->data, raw->size, timestamp, thread->comm);
|
|
|
|
|
|
+ scripting_ops->process_event(cpu, raw->data, raw->size,
|
|
|
|
+ timestamp, thread->comm);
|
|
}
|
|
}
|
|
event__stats.total += period;
|
|
event__stats.total += period;
|
|
|
|
|
|
@@ -105,6 +147,154 @@ static int __cmd_trace(void)
|
|
0, 0, &event__cwdlen, &event__cwd);
|
|
0, 0, &event__cwdlen, &event__cwd);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+struct script_spec {
|
|
|
|
+ struct list_head node;
|
|
|
|
+ struct scripting_ops *ops;
|
|
|
|
+ char spec[0];
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+LIST_HEAD(script_specs);
|
|
|
|
+
|
|
|
|
+static struct script_spec *script_spec__new(const char *spec,
|
|
|
|
+ struct scripting_ops *ops)
|
|
|
|
+{
|
|
|
|
+ struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1);
|
|
|
|
+
|
|
|
|
+ if (s != NULL) {
|
|
|
|
+ strcpy(s->spec, spec);
|
|
|
|
+ s->ops = ops;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return s;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void script_spec__delete(struct script_spec *s)
|
|
|
|
+{
|
|
|
|
+ free(s->spec);
|
|
|
|
+ free(s);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void script_spec__add(struct script_spec *s)
|
|
|
|
+{
|
|
|
|
+ list_add_tail(&s->node, &script_specs);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct script_spec *script_spec__find(const char *spec)
|
|
|
|
+{
|
|
|
|
+ struct script_spec *s;
|
|
|
|
+
|
|
|
|
+ list_for_each_entry(s, &script_specs, node)
|
|
|
|
+ if (strcasecmp(s->spec, spec) == 0)
|
|
|
|
+ return s;
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct script_spec *script_spec__findnew(const char *spec,
|
|
|
|
+ struct scripting_ops *ops)
|
|
|
|
+{
|
|
|
|
+ struct script_spec *s = script_spec__find(spec);
|
|
|
|
+
|
|
|
|
+ if (s)
|
|
|
|
+ return s;
|
|
|
|
+
|
|
|
|
+ s = script_spec__new(spec, ops);
|
|
|
|
+ if (!s)
|
|
|
|
+ goto out_delete_spec;
|
|
|
|
+
|
|
|
|
+ script_spec__add(s);
|
|
|
|
+
|
|
|
|
+ return s;
|
|
|
|
+
|
|
|
|
+out_delete_spec:
|
|
|
|
+ script_spec__delete(s);
|
|
|
|
+
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int script_spec_register(const char *spec, struct scripting_ops *ops)
|
|
|
|
+{
|
|
|
|
+ struct script_spec *s;
|
|
|
|
+
|
|
|
|
+ s = script_spec__find(spec);
|
|
|
|
+ if (s)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ s = script_spec__findnew(spec, ops);
|
|
|
|
+ if (!s)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct scripting_ops *script_spec__lookup(const char *spec)
|
|
|
|
+{
|
|
|
|
+ struct script_spec *s = script_spec__find(spec);
|
|
|
|
+ if (!s)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ return s->ops;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void list_available_languages(void)
|
|
|
|
+{
|
|
|
|
+ struct script_spec *s;
|
|
|
|
+
|
|
|
|
+ fprintf(stderr, "\n");
|
|
|
|
+ fprintf(stderr, "Scripting language extensions (used in "
|
|
|
|
+ "perf trace -s [spec:]script.[spec]):\n\n");
|
|
|
|
+
|
|
|
|
+ list_for_each_entry(s, &script_specs, node)
|
|
|
|
+ fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name);
|
|
|
|
+
|
|
|
|
+ fprintf(stderr, "\n");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int parse_scriptname(const struct option *opt __used,
|
|
|
|
+ const char *str, int unset __used)
|
|
|
|
+{
|
|
|
|
+ char spec[PATH_MAX];
|
|
|
|
+ const char *script, *ext;
|
|
|
|
+ int len;
|
|
|
|
+
|
|
|
|
+ if (strcmp(str, "list") == 0) {
|
|
|
|
+ list_available_languages();
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ script = strchr(str, ':');
|
|
|
|
+ if (script) {
|
|
|
|
+ len = script - str;
|
|
|
|
+ if (len >= PATH_MAX) {
|
|
|
|
+ fprintf(stderr, "invalid language specifier");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ strncpy(spec, str, len);
|
|
|
|
+ spec[len] = '\0';
|
|
|
|
+ scripting_ops = script_spec__lookup(spec);
|
|
|
|
+ if (!scripting_ops) {
|
|
|
|
+ fprintf(stderr, "invalid language specifier");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ script++;
|
|
|
|
+ } else {
|
|
|
|
+ script = str;
|
|
|
|
+ ext = strchr(script, '.');
|
|
|
|
+ if (!ext) {
|
|
|
|
+ fprintf(stderr, "invalid script extension");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ scripting_ops = script_spec__lookup(++ext);
|
|
|
|
+ if (!scripting_ops) {
|
|
|
|
+ fprintf(stderr, "invalid script extension");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ script_name = strdup(script);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static const char * const annotate_usage[] = {
|
|
static const char * const annotate_usage[] = {
|
|
"perf trace [<options>] <command>",
|
|
"perf trace [<options>] <command>",
|
|
NULL
|
|
NULL
|
|
@@ -117,13 +307,23 @@ static const struct option options[] = {
|
|
"be more verbose (show symbol address, etc)"),
|
|
"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)"),
|
|
"show latency attributes (irqs/preemption disabled, etc)"),
|
|
|
|
+ OPT_CALLBACK('s', "script", NULL, "name",
|
|
|
|
+ "script file name (lang:script name, script name, or *)",
|
|
|
|
+ parse_scriptname),
|
|
|
|
+ OPT_STRING('g', "gen-script", &generate_script_lang, "lang",
|
|
|
|
+ "generate perf-trace.xx script in specified language"),
|
|
|
|
+
|
|
OPT_END()
|
|
OPT_END()
|
|
};
|
|
};
|
|
|
|
|
|
int cmd_trace(int argc, const char **argv, const char *prefix __used)
|
|
int cmd_trace(int argc, const char **argv, const char *prefix __used)
|
|
{
|
|
{
|
|
|
|
+ int err;
|
|
|
|
+
|
|
symbol__init(0);
|
|
symbol__init(0);
|
|
|
|
|
|
|
|
+ setup_scripting();
|
|
|
|
+
|
|
argc = parse_options(argc, argv, options, annotate_usage, 0);
|
|
argc = parse_options(argc, argv, options, annotate_usage, 0);
|
|
if (argc) {
|
|
if (argc) {
|
|
/*
|
|
/*
|
|
@@ -136,5 +336,50 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
|
|
|
|
|
|
setup_pager();
|
|
setup_pager();
|
|
|
|
|
|
- return __cmd_trace();
|
|
|
|
|
|
+ if (generate_script_lang) {
|
|
|
|
+ struct stat perf_stat;
|
|
|
|
+
|
|
|
|
+ int input = open(input_name, O_RDONLY);
|
|
|
|
+ if (input < 0) {
|
|
|
|
+ perror("failed to open file");
|
|
|
|
+ exit(-1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = fstat(input, &perf_stat);
|
|
|
|
+ if (err < 0) {
|
|
|
|
+ perror("failed to stat file");
|
|
|
|
+ exit(-1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!perf_stat.st_size) {
|
|
|
|
+ fprintf(stderr, "zero-sized file, nothing to do!\n");
|
|
|
|
+ exit(0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ scripting_ops = script_spec__lookup(generate_script_lang);
|
|
|
|
+ if (!scripting_ops) {
|
|
|
|
+ fprintf(stderr, "invalid language specifier");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ header = perf_header__new();
|
|
|
|
+ if (header == NULL)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ perf_header__read(header, input);
|
|
|
|
+ err = scripting_ops->generate_script("perf-trace");
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (script_name) {
|
|
|
|
+ err = scripting_ops->start_script(script_name);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = __cmd_trace();
|
|
|
|
+
|
|
|
|
+ cleanup_scripting();
|
|
|
|
+out:
|
|
|
|
+ return err;
|
|
}
|
|
}
|