diff options
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/Documentation/perf-script.txt | 52 | ||||
-rw-r--r-- | tools/perf/builtin-script.c | 171 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 72 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.h | 2 |
4 files changed, 246 insertions, 51 deletions
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 66f040b30729..86c87e214b11 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -113,13 +113,61 @@ OPTIONS | |||
113 | Do various checks like samples ordering and lost events. | 113 | Do various checks like samples ordering and lost events. |
114 | 114 | ||
115 | -f:: | 115 | -f:: |
116 | --fields | 116 | --fields:: |
117 | Comma separated list of fields to print. Options are: | 117 | Comma separated list of fields to print. Options are: |
118 | comm, tid, pid, time, cpu, event, trace, sym. Field | 118 | comm, tid, pid, time, cpu, event, trace, sym. Field |
119 | list must be prepended with the type, trace, sw or hw, | 119 | list can be prepended with the type, trace, sw or hw, |
120 | to indicate to which event type the field list applies. | 120 | to indicate to which event type the field list applies. |
121 | e.g., -f sw:comm,tid,time,sym and -f trace:time,cpu,trace | 121 | e.g., -f sw:comm,tid,time,sym and -f trace:time,cpu,trace |
122 | 122 | ||
123 | perf script -f <fields> | ||
124 | |||
125 | is equivalent to: | ||
126 | |||
127 | perf script -f trace:<fields> -f sw:<fields> -f hw:<fields> | ||
128 | |||
129 | i.e., the specified fields apply to all event types if the type string | ||
130 | is not given. | ||
131 | |||
132 | The arguments are processed in the order received. A later usage can | ||
133 | reset a prior request. e.g.: | ||
134 | |||
135 | -f trace: -f comm,tid,time,sym | ||
136 | |||
137 | The first -f suppresses trace events (field list is ""), but then the | ||
138 | second invocation sets the fields to comm,tid,time,sym. In this case a | ||
139 | warning is given to the user: | ||
140 | |||
141 | "Overriding previous field request for all events." | ||
142 | |||
143 | Alternativey, consider the order: | ||
144 | |||
145 | -f comm,tid,time,sym -f trace: | ||
146 | |||
147 | The first -f sets the fields for all events and the second -f | ||
148 | suppresses trace events. The user is given a warning message about | ||
149 | the override, and the result of the above is that only S/W and H/W | ||
150 | events are displayed with the given fields. | ||
151 | |||
152 | For the 'wildcard' option if a user selected field is invalid for an | ||
153 | event type, a message is displayed to the user that the option is | ||
154 | ignored for that type. For example: | ||
155 | |||
156 | $ perf script -f comm,tid,trace | ||
157 | 'trace' not valid for hardware events. Ignoring. | ||
158 | 'trace' not valid for software events. Ignoring. | ||
159 | |||
160 | Alternatively, if the type is given an invalid field is specified it | ||
161 | is an error. For example: | ||
162 | |||
163 | perf script -v -f sw:comm,tid,trace | ||
164 | 'trace' not valid for software events. | ||
165 | |||
166 | At this point usage is displayed, and perf-script exits. | ||
167 | |||
168 | Finally, a user may not set fields to none for all event types. | ||
169 | i.e., -f "" is not allowed. | ||
170 | |||
123 | -k:: | 171 | -k:: |
124 | --vmlinux=<file>:: | 172 | --vmlinux=<file>:: |
125 | vmlinux pathname | 173 | vmlinux pathname |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index ac574ea23917..6cf811acc41b 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -49,23 +49,52 @@ struct output_option { | |||
49 | }; | 49 | }; |
50 | 50 | ||
51 | /* default set to maintain compatibility with current format */ | 51 | /* default set to maintain compatibility with current format */ |
52 | static u64 output_fields[PERF_TYPE_MAX] = { | 52 | static struct { |
53 | [PERF_TYPE_HARDWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | 53 | bool user_set; |
54 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | 54 | u64 fields; |
55 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 55 | u64 invalid_fields; |
56 | 56 | } output[PERF_TYPE_MAX] = { | |
57 | [PERF_TYPE_SOFTWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | 57 | |
58 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | 58 | [PERF_TYPE_HARDWARE] = { |
59 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 59 | .user_set = false, |
60 | 60 | ||
61 | [PERF_TYPE_TRACEPOINT] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | 61 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
62 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | 62 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
63 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, | 63 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, |
64 | |||
65 | .invalid_fields = PERF_OUTPUT_TRACE, | ||
66 | }, | ||
67 | |||
68 | [PERF_TYPE_SOFTWARE] = { | ||
69 | .user_set = false, | ||
70 | |||
71 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | ||
72 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | ||
73 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | ||
74 | |||
75 | .invalid_fields = PERF_OUTPUT_TRACE, | ||
76 | }, | ||
77 | |||
78 | [PERF_TYPE_TRACEPOINT] = { | ||
79 | .user_set = false, | ||
80 | |||
81 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | ||
82 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | ||
83 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, | ||
84 | }, | ||
64 | }; | 85 | }; |
65 | 86 | ||
66 | static bool output_set_by_user; | 87 | static bool output_set_by_user(void) |
88 | { | ||
89 | int j; | ||
90 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | ||
91 | if (output[j].user_set) | ||
92 | return true; | ||
93 | } | ||
94 | return false; | ||
95 | } | ||
67 | 96 | ||
68 | #define PRINT_FIELD(x) (output_fields[attr->type] & PERF_OUTPUT_##x) | 97 | #define PRINT_FIELD(x) (output[attr->type].fields & PERF_OUTPUT_##x) |
69 | 98 | ||
70 | static int perf_session__check_attr(struct perf_session *session, | 99 | static int perf_session__check_attr(struct perf_session *session, |
71 | struct perf_event_attr *attr) | 100 | struct perf_event_attr *attr) |
@@ -168,7 +197,7 @@ static void process_event(union perf_event *event __unused, | |||
168 | { | 197 | { |
169 | struct perf_event_attr *attr = &evsel->attr; | 198 | struct perf_event_attr *attr = &evsel->attr; |
170 | 199 | ||
171 | if (output_fields[attr->type] == 0) | 200 | if (output[attr->type].fields == 0) |
172 | return; | 201 | return; |
173 | 202 | ||
174 | if (perf_session__check_attr(session, attr) < 0) | 203 | if (perf_session__check_attr(session, attr) < 0) |
@@ -451,6 +480,7 @@ static int parse_output_fields(const struct option *opt __used, | |||
451 | { | 480 | { |
452 | char *tok; | 481 | char *tok; |
453 | int i, imax = sizeof(all_output_options) / sizeof(struct output_option); | 482 | int i, imax = sizeof(all_output_options) / sizeof(struct output_option); |
483 | int j; | ||
454 | int rc = 0; | 484 | int rc = 0; |
455 | char *str = strdup(arg); | 485 | char *str = strdup(arg); |
456 | int type = -1; | 486 | int type = -1; |
@@ -458,52 +488,95 @@ static int parse_output_fields(const struct option *opt __used, | |||
458 | if (!str) | 488 | if (!str) |
459 | return -ENOMEM; | 489 | return -ENOMEM; |
460 | 490 | ||
461 | tok = strtok(str, ":"); | 491 | /* first word can state for which event type the user is specifying |
462 | if (!tok) { | 492 | * the fields. If no type exists, the specified fields apply to all |
463 | fprintf(stderr, | 493 | * event types found in the file minus the invalid fields for a type. |
464 | "Invalid field string - not prepended with type."); | ||
465 | return -EINVAL; | ||
466 | } | ||
467 | |||
468 | /* first word should state which event type user | ||
469 | * is specifying the fields | ||
470 | */ | 494 | */ |
471 | if (!strcmp(tok, "hw")) | 495 | tok = strchr(str, ':'); |
472 | type = PERF_TYPE_HARDWARE; | 496 | if (tok) { |
473 | else if (!strcmp(tok, "sw")) | 497 | *tok = '\0'; |
474 | type = PERF_TYPE_SOFTWARE; | 498 | tok++; |
475 | else if (!strcmp(tok, "trace")) | 499 | if (!strcmp(str, "hw")) |
476 | type = PERF_TYPE_TRACEPOINT; | 500 | type = PERF_TYPE_HARDWARE; |
477 | else { | 501 | else if (!strcmp(str, "sw")) |
478 | fprintf(stderr, "Invalid event type in field string."); | 502 | type = PERF_TYPE_SOFTWARE; |
479 | return -EINVAL; | 503 | else if (!strcmp(str, "trace")) |
504 | type = PERF_TYPE_TRACEPOINT; | ||
505 | else { | ||
506 | fprintf(stderr, "Invalid event type in field string.\n"); | ||
507 | return -EINVAL; | ||
508 | } | ||
509 | |||
510 | if (output[type].user_set) | ||
511 | pr_warning("Overriding previous field request for %s events.\n", | ||
512 | event_type(type)); | ||
513 | |||
514 | output[type].fields = 0; | ||
515 | output[type].user_set = true; | ||
516 | |||
517 | } else { | ||
518 | tok = str; | ||
519 | if (strlen(str) == 0) { | ||
520 | fprintf(stderr, | ||
521 | "Cannot set fields to 'none' for all event types.\n"); | ||
522 | rc = -EINVAL; | ||
523 | goto out; | ||
524 | } | ||
525 | |||
526 | if (output_set_by_user()) | ||
527 | pr_warning("Overriding previous field request for all events.\n"); | ||
528 | |||
529 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | ||
530 | output[j].fields = 0; | ||
531 | output[j].user_set = true; | ||
532 | } | ||
480 | } | 533 | } |
481 | 534 | ||
482 | output_fields[type] = 0; | 535 | tok = strtok(tok, ","); |
483 | while (1) { | 536 | while (tok) { |
484 | tok = strtok(NULL, ","); | ||
485 | if (!tok) | ||
486 | break; | ||
487 | for (i = 0; i < imax; ++i) { | 537 | for (i = 0; i < imax; ++i) { |
488 | if (strcmp(tok, all_output_options[i].str) == 0) { | 538 | if (strcmp(tok, all_output_options[i].str) == 0) |
489 | output_fields[type] |= all_output_options[i].field; | ||
490 | break; | 539 | break; |
491 | } | ||
492 | } | 540 | } |
493 | if (i == imax) { | 541 | if (i == imax) { |
494 | fprintf(stderr, "Invalid field requested."); | 542 | fprintf(stderr, "Invalid field requested.\n"); |
495 | rc = -EINVAL; | 543 | rc = -EINVAL; |
496 | break; | 544 | goto out; |
497 | } | 545 | } |
498 | } | ||
499 | 546 | ||
500 | if (output_fields[type] == 0) { | 547 | if (type == -1) { |
501 | pr_debug("No fields requested for %s type. " | 548 | /* add user option to all events types for |
502 | "Events will not be displayed\n", event_type(type)); | 549 | * which it is valid |
550 | */ | ||
551 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | ||
552 | if (output[j].invalid_fields & all_output_options[i].field) { | ||
553 | pr_warning("\'%s\' not valid for %s events. Ignoring.\n", | ||
554 | all_output_options[i].str, event_type(j)); | ||
555 | } else | ||
556 | output[j].fields |= all_output_options[i].field; | ||
557 | } | ||
558 | } else { | ||
559 | if (output[type].invalid_fields & all_output_options[i].field) { | ||
560 | fprintf(stderr, "\'%s\' not valid for %s events.\n", | ||
561 | all_output_options[i].str, event_type(type)); | ||
562 | |||
563 | rc = -EINVAL; | ||
564 | goto out; | ||
565 | } | ||
566 | output[type].fields |= all_output_options[i].field; | ||
567 | } | ||
568 | |||
569 | tok = strtok(NULL, ","); | ||
503 | } | 570 | } |
504 | 571 | ||
505 | output_set_by_user = true; | 572 | if (type >= 0) { |
573 | if (output[type].fields == 0) { | ||
574 | pr_debug("No fields requested for %s type. " | ||
575 | "Events will not be displayed.\n", event_type(type)); | ||
576 | } | ||
577 | } | ||
506 | 578 | ||
579 | out: | ||
507 | free(str); | 580 | free(str); |
508 | return rc; | 581 | return rc; |
509 | } | 582 | } |
@@ -1020,7 +1093,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) | |||
1020 | struct stat perf_stat; | 1093 | struct stat perf_stat; |
1021 | int input; | 1094 | int input; |
1022 | 1095 | ||
1023 | if (output_set_by_user) { | 1096 | if (output_set_by_user()) { |
1024 | fprintf(stderr, | 1097 | fprintf(stderr, |
1025 | "custom fields not supported for generated scripts"); | 1098 | "custom fields not supported for generated scripts"); |
1026 | return -1; | 1099 | return -1; |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index b7c85ce466a1..a7c7145a8d4f 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -1471,6 +1471,38 @@ static int find_probe_point_by_func(struct probe_finder *pf) | |||
1471 | return _param.retval; | 1471 | return _param.retval; |
1472 | } | 1472 | } |
1473 | 1473 | ||
1474 | struct pubname_callback_param { | ||
1475 | char *function; | ||
1476 | char *file; | ||
1477 | Dwarf_Die *cu_die; | ||
1478 | Dwarf_Die *sp_die; | ||
1479 | int found; | ||
1480 | }; | ||
1481 | |||
1482 | static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) | ||
1483 | { | ||
1484 | struct pubname_callback_param *param = data; | ||
1485 | |||
1486 | if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) { | ||
1487 | if (dwarf_tag(param->sp_die) != DW_TAG_subprogram) | ||
1488 | return DWARF_CB_OK; | ||
1489 | |||
1490 | if (die_compare_name(param->sp_die, param->function)) { | ||
1491 | if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die)) | ||
1492 | return DWARF_CB_OK; | ||
1493 | |||
1494 | if (param->file && | ||
1495 | strtailcmp(param->file, dwarf_decl_file(param->sp_die))) | ||
1496 | return DWARF_CB_OK; | ||
1497 | |||
1498 | param->found = 1; | ||
1499 | return DWARF_CB_ABORT; | ||
1500 | } | ||
1501 | } | ||
1502 | |||
1503 | return DWARF_CB_OK; | ||
1504 | } | ||
1505 | |||
1474 | /* Find probe points from debuginfo */ | 1506 | /* Find probe points from debuginfo */ |
1475 | static int find_probes(int fd, struct probe_finder *pf) | 1507 | static int find_probes(int fd, struct probe_finder *pf) |
1476 | { | 1508 | { |
@@ -1498,6 +1530,27 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1498 | 1530 | ||
1499 | off = 0; | 1531 | off = 0; |
1500 | line_list__init(&pf->lcache); | 1532 | line_list__init(&pf->lcache); |
1533 | |||
1534 | /* Fastpath: lookup by function name from .debug_pubnames section */ | ||
1535 | if (pp->function) { | ||
1536 | struct pubname_callback_param pubname_param = { | ||
1537 | .function = pp->function, | ||
1538 | .file = pp->file, | ||
1539 | .cu_die = &pf->cu_die, | ||
1540 | .sp_die = &pf->sp_die, | ||
1541 | }; | ||
1542 | struct dwarf_callback_param probe_param = { | ||
1543 | .data = pf, | ||
1544 | }; | ||
1545 | |||
1546 | dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); | ||
1547 | if (pubname_param.found) { | ||
1548 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); | ||
1549 | if (ret) | ||
1550 | goto found; | ||
1551 | } | ||
1552 | } | ||
1553 | |||
1501 | /* Loop on CUs (Compilation Unit) */ | 1554 | /* Loop on CUs (Compilation Unit) */ |
1502 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { | 1555 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
1503 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1556 | /* Get the DIE(Debugging Information Entry) of this CU */ |
@@ -1525,6 +1578,8 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1525 | } | 1578 | } |
1526 | off = noff; | 1579 | off = noff; |
1527 | } | 1580 | } |
1581 | |||
1582 | found: | ||
1528 | line_list__free(&pf->lcache); | 1583 | line_list__free(&pf->lcache); |
1529 | if (dwfl) | 1584 | if (dwfl) |
1530 | dwfl_end(dwfl); | 1585 | dwfl_end(dwfl); |
@@ -1946,6 +2001,22 @@ int find_line_range(int fd, struct line_range *lr) | |||
1946 | return -EBADF; | 2001 | return -EBADF; |
1947 | } | 2002 | } |
1948 | 2003 | ||
2004 | /* Fastpath: lookup by function name from .debug_pubnames section */ | ||
2005 | if (lr->function) { | ||
2006 | struct pubname_callback_param pubname_param = { | ||
2007 | .function = lr->function, .file = lr->file, | ||
2008 | .cu_die = &lf.cu_die, .sp_die = &lf.sp_die, .found = 0}; | ||
2009 | struct dwarf_callback_param line_range_param = { | ||
2010 | .data = (void *)&lf, .retval = 0}; | ||
2011 | |||
2012 | dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); | ||
2013 | if (pubname_param.found) { | ||
2014 | line_range_search_cb(&lf.sp_die, &line_range_param); | ||
2015 | if (lf.found) | ||
2016 | goto found; | ||
2017 | } | ||
2018 | } | ||
2019 | |||
1949 | /* Loop on CUs (Compilation Unit) */ | 2020 | /* Loop on CUs (Compilation Unit) */ |
1950 | while (!lf.found && ret >= 0) { | 2021 | while (!lf.found && ret >= 0) { |
1951 | if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) | 2022 | if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) |
@@ -1974,6 +2045,7 @@ int find_line_range(int fd, struct line_range *lr) | |||
1974 | off = noff; | 2045 | off = noff; |
1975 | } | 2046 | } |
1976 | 2047 | ||
2048 | found: | ||
1977 | /* Store comp_dir */ | 2049 | /* Store comp_dir */ |
1978 | if (lf.found) { | 2050 | if (lf.found) { |
1979 | comp_dir = cu_get_comp_dir(&lf.cu_die); | 2051 | comp_dir = cu_get_comp_dir(&lf.cu_die); |
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index beaefc3c1223..605730a366db 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -49,6 +49,7 @@ struct probe_finder { | |||
49 | Dwarf_Addr addr; /* Address */ | 49 | Dwarf_Addr addr; /* Address */ |
50 | const char *fname; /* Real file name */ | 50 | const char *fname; /* Real file name */ |
51 | Dwarf_Die cu_die; /* Current CU */ | 51 | Dwarf_Die cu_die; /* Current CU */ |
52 | Dwarf_Die sp_die; | ||
52 | struct list_head lcache; /* Line cache for lazy match */ | 53 | struct list_head lcache; /* Line cache for lazy match */ |
53 | 54 | ||
54 | /* For variable searching */ | 55 | /* For variable searching */ |
@@ -83,6 +84,7 @@ struct line_finder { | |||
83 | int lno_s; /* Start line number */ | 84 | int lno_s; /* Start line number */ |
84 | int lno_e; /* End line number */ | 85 | int lno_e; /* End line number */ |
85 | Dwarf_Die cu_die; /* Current CU */ | 86 | Dwarf_Die cu_die; /* Current CU */ |
87 | Dwarf_Die sp_die; | ||
86 | int found; | 88 | int found; |
87 | }; | 89 | }; |
88 | 90 | ||