Merge branch 'perf/urgent' into perf/core
authorIngo Molnar <mingo@elte.hu>
Tue, 19 Apr 2011 05:55:58 +0000 (07:55 +0200)
committerIngo Molnar <mingo@elte.hu>
Tue, 19 Apr 2011 05:56:17 +0000 (07:56 +0200)
Merge reason: we'll be queueing up dependent changes.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/kernel/cpu/perf_event_p4.c
tools/perf/Documentation/perf-script.txt
tools/perf/builtin-script.c
tools/perf/util/probe-finder.c
tools/perf/util/probe-finder.h

index c2520e1..8ff882f 100644 (file)
@@ -468,7 +468,7 @@ static struct p4_event_bind p4_event_bind_map[] = {
                .opcode         = P4_OPCODE(P4_EVENT_MISPRED_BRANCH_RETIRED),
                .escr_msr       = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 },
                .escr_emask     =
-               P4_ESCR_EMASK_BIT(P4_EVENT_MISPRED_BRANCH_RETIRED, NBOGUS),
+                       P4_ESCR_EMASK_BIT(P4_EVENT_MISPRED_BRANCH_RETIRED, NBOGUS),
                .cntr           = { {12, 13, 16}, {14, 15, 17} },
        },
        [P4_EVENT_X87_ASSIST] = {
index 66f040b..86c87e2 100644 (file)
@@ -113,13 +113,61 @@ OPTIONS
         Do various checks like samples ordering and lost events.
 
 -f::
---fields
+--fields::
         Comma separated list of fields to print. Options are:
         comm, tid, pid, time, cpu, event, trace, sym. Field
-        list must be prepended with the type, trace, sw or hw,
+        list can be prepended with the type, trace, sw or hw,
         to indicate to which event type the field list applies.
         e.g., -f sw:comm,tid,time,sym  and -f trace:time,cpu,trace
 
+               perf script -f <fields>
+
+       is equivalent to:
+
+               perf script -f trace:<fields> -f sw:<fields> -f hw:<fields>
+    
+       i.e., the specified fields apply to all event types if the type string
+       is not given.
+    
+       The arguments are processed in the order received. A later usage can
+       reset a prior request. e.g.:
+    
+               -f trace: -f comm,tid,time,sym
+    
+       The first -f suppresses trace events (field list is ""), but then the
+       second invocation sets the fields to comm,tid,time,sym. In this case a
+       warning is given to the user:
+    
+               "Overriding previous field request for all events."
+    
+       Alternativey, consider the order:
+    
+               -f comm,tid,time,sym -f trace:
+    
+       The first -f sets the fields for all events and the second -f
+       suppresses trace events. The user is given a warning message about
+       the override, and the result of the above is that only S/W and H/W
+       events are displayed with the given fields.
+    
+       For the 'wildcard' option if a user selected field is invalid for an
+       event type, a message is displayed to the user that the option is
+       ignored for that type. For example:
+    
+               $ perf script -f comm,tid,trace
+               'trace' not valid for hardware events. Ignoring.
+               'trace' not valid for software events. Ignoring.
+    
+       Alternatively, if the type is given an invalid field is specified it
+       is an error. For example:
+    
+        perf script -v -f sw:comm,tid,trace
+        'trace' not valid for software events.
+    
+       At this point usage is displayed, and perf-script exits.
+    
+       Finally, a user may not set fields to none for all event types.
+       i.e., -f "" is not allowed.
+
 -k::
 --vmlinux=<file>::
         vmlinux pathname
index ac574ea..6cf811a 100644 (file)
@@ -49,23 +49,52 @@ struct output_option {
 };
 
 /* default set to maintain compatibility with current format */
-static u64 output_fields[PERF_TYPE_MAX] = {
-       [PERF_TYPE_HARDWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \
-                              PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \
-                              PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM,
-
-       [PERF_TYPE_SOFTWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \
-                              PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \
-                              PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM,
-
-       [PERF_TYPE_TRACEPOINT] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \
-                                PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \
-                                PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE,
+static struct {
+       bool user_set;
+       u64 fields;
+       u64 invalid_fields;
+} output[PERF_TYPE_MAX] = {
+
+       [PERF_TYPE_HARDWARE] = {
+               .user_set = false,
+
+               .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
+                             PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
+                             PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM,
+
+               .invalid_fields = PERF_OUTPUT_TRACE,
+       },
+
+       [PERF_TYPE_SOFTWARE] = {
+               .user_set = false,
+
+               .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
+                             PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
+                             PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM,
+
+               .invalid_fields = PERF_OUTPUT_TRACE,
+       },
+
+       [PERF_TYPE_TRACEPOINT] = {
+               .user_set = false,
+
+               .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
+                                 PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
+                                 PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE,
+       },
 };
 
-static bool output_set_by_user;
+static bool output_set_by_user(void)
+{
+       int j;
+       for (j = 0; j < PERF_TYPE_MAX; ++j) {
+               if (output[j].user_set)
+                       return true;
+       }
+       return false;
+}
 
-#define PRINT_FIELD(x)  (output_fields[attr->type] & PERF_OUTPUT_##x)
+#define PRINT_FIELD(x)  (output[attr->type].fields & PERF_OUTPUT_##x)
 
 static int perf_session__check_attr(struct perf_session *session,
                                    struct perf_event_attr *attr)
@@ -168,7 +197,7 @@ static void process_event(union perf_event *event __unused,
 {
        struct perf_event_attr *attr = &evsel->attr;
 
-       if (output_fields[attr->type] == 0)
+       if (output[attr->type].fields == 0)
                return;
 
        if (perf_session__check_attr(session, attr) < 0)
@@ -451,6 +480,7 @@ static int parse_output_fields(const struct option *opt __used,
 {
        char *tok;
        int i, imax = sizeof(all_output_options) / sizeof(struct output_option);
+       int j;
        int rc = 0;
        char *str = strdup(arg);
        int type = -1;
@@ -458,52 +488,95 @@ static int parse_output_fields(const struct option *opt __used,
        if (!str)
                return -ENOMEM;
 
-       tok = strtok(str, ":");
-       if (!tok) {
-               fprintf(stderr,
-                       "Invalid field string - not prepended with type.");
-               return -EINVAL;
-       }
-
-       /* first word should state which event type user
-        * is specifying the fields
+       /* first word can state for which event type the user is specifying
+        * the fields. If no type exists, the specified fields apply to all
+        * event types found in the file minus the invalid fields for a type.
         */
-       if (!strcmp(tok, "hw"))
-               type = PERF_TYPE_HARDWARE;
-       else if (!strcmp(tok, "sw"))
-               type = PERF_TYPE_SOFTWARE;
-       else if (!strcmp(tok, "trace"))
-               type = PERF_TYPE_TRACEPOINT;
-       else {
-               fprintf(stderr, "Invalid event type in field string.");
-               return -EINVAL;
+       tok = strchr(str, ':');
+       if (tok) {
+               *tok = '\0';
+               tok++;
+               if (!strcmp(str, "hw"))
+                       type = PERF_TYPE_HARDWARE;
+               else if (!strcmp(str, "sw"))
+                       type = PERF_TYPE_SOFTWARE;
+               else if (!strcmp(str, "trace"))
+                       type = PERF_TYPE_TRACEPOINT;
+               else {
+                       fprintf(stderr, "Invalid event type in field string.\n");
+                       return -EINVAL;
+               }
+
+               if (output[type].user_set)
+                       pr_warning("Overriding previous field request for %s events.\n",
+                                  event_type(type));
+
+               output[type].fields = 0;
+               output[type].user_set = true;
+
+       } else {
+               tok = str;
+               if (strlen(str) == 0) {
+                       fprintf(stderr,
+                               "Cannot set fields to 'none' for all event types.\n");
+                       rc = -EINVAL;
+                       goto out;
+               }
+
+               if (output_set_by_user())
+                       pr_warning("Overriding previous field request for all events.\n");
+
+               for (j = 0; j < PERF_TYPE_MAX; ++j) {
+                       output[j].fields = 0;
+                       output[j].user_set = true;
+               }
        }
 
-       output_fields[type] = 0;
-       while (1) {
-               tok = strtok(NULL, ",");
-               if (!tok)
-                       break;
+       tok = strtok(tok, ",");
+       while (tok) {
                for (i = 0; i < imax; ++i) {
-                       if (strcmp(tok, all_output_options[i].str) == 0) {
-                               output_fields[type] |= all_output_options[i].field;
+                       if (strcmp(tok, all_output_options[i].str) == 0)
                                break;
-                       }
                }
                if (i == imax) {
-                       fprintf(stderr, "Invalid field requested.");
+                       fprintf(stderr, "Invalid field requested.\n");
                        rc = -EINVAL;
-                       break;
+                       goto out;
                }
-       }
 
-       if (output_fields[type] == 0) {
-               pr_debug("No fields requested for %s type. "
-                        "Events will not be displayed\n", event_type(type));
+               if (type == -1) {
+                       /* add user option to all events types for
+                        * which it is valid
+                        */
+                       for (j = 0; j < PERF_TYPE_MAX; ++j) {
+                               if (output[j].invalid_fields & all_output_options[i].field) {
+                                       pr_warning("\'%s\' not valid for %s events. Ignoring.\n",
+                                                  all_output_options[i].str, event_type(j));
+                               } else
+                                       output[j].fields |= all_output_options[i].field;
+                       }
+               } else {
+                       if (output[type].invalid_fields & all_output_options[i].field) {
+                               fprintf(stderr, "\'%s\' not valid for %s events.\n",
+                                        all_output_options[i].str, event_type(type));
+
+                               rc = -EINVAL;
+                               goto out;
+                       }
+                       output[type].fields |= all_output_options[i].field;
+               }
+
+               tok = strtok(NULL, ",");
        }
 
-       output_set_by_user = true;
+       if (type >= 0) {
+               if (output[type].fields == 0) {
+                       pr_debug("No fields requested for %s type. "
+                                "Events will not be displayed.\n", event_type(type));
+               }
+       }
 
+out:
        free(str);
        return rc;
 }
@@ -1020,7 +1093,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
                struct stat perf_stat;
                int input;
 
-               if (output_set_by_user) {
+               if (output_set_by_user()) {
                        fprintf(stderr,
                                "custom fields not supported for generated scripts");
                        return -1;
index b7c85ce..a7c7145 100644 (file)
@@ -1471,6 +1471,38 @@ static int find_probe_point_by_func(struct probe_finder *pf)
        return _param.retval;
 }
 
+struct pubname_callback_param {
+       char *function;
+       char *file;
+       Dwarf_Die *cu_die;
+       Dwarf_Die *sp_die;
+       int found;
+};
+
+static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)
+{
+       struct pubname_callback_param *param = data;
+
+       if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) {
+               if (dwarf_tag(param->sp_die) != DW_TAG_subprogram)
+                       return DWARF_CB_OK;
+
+               if (die_compare_name(param->sp_die, param->function)) {
+                       if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die))
+                               return DWARF_CB_OK;
+
+                       if (param->file &&
+                           strtailcmp(param->file, dwarf_decl_file(param->sp_die)))
+                               return DWARF_CB_OK;
+
+                       param->found = 1;
+                       return DWARF_CB_ABORT;
+               }
+       }
+
+       return DWARF_CB_OK;
+}
+
 /* Find probe points from debuginfo */
 static int find_probes(int fd, struct probe_finder *pf)
 {
@@ -1498,6 +1530,27 @@ static int find_probes(int fd, struct probe_finder *pf)
 
        off = 0;
        line_list__init(&pf->lcache);
+
+       /* Fastpath: lookup by function name from .debug_pubnames section */
+       if (pp->function) {
+               struct pubname_callback_param pubname_param = {
+                       .function = pp->function,
+                       .file     = pp->file,
+                       .cu_die   = &pf->cu_die,
+                       .sp_die   = &pf->sp_die,
+               };
+               struct dwarf_callback_param probe_param = {
+                       .data = pf,
+               };
+
+               dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0);
+               if (pubname_param.found) {
+                       ret = probe_point_search_cb(&pf->sp_die, &probe_param);
+                       if (ret)
+                               goto found;
+               }
+       }
+
        /* Loop on CUs (Compilation Unit) */
        while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) {
                /* Get the DIE(Debugging Information Entry) of this CU */
@@ -1525,6 +1578,8 @@ static int find_probes(int fd, struct probe_finder *pf)
                }
                off = noff;
        }
+
+found:
        line_list__free(&pf->lcache);
        if (dwfl)
                dwfl_end(dwfl);
@@ -1946,6 +2001,22 @@ int find_line_range(int fd, struct line_range *lr)
                return -EBADF;
        }
 
+       /* Fastpath: lookup by function name from .debug_pubnames section */
+       if (lr->function) {
+               struct pubname_callback_param pubname_param = {
+                       .function = lr->function, .file = lr->file,
+                       .cu_die = &lf.cu_die, .sp_die = &lf.sp_die, .found = 0};
+               struct dwarf_callback_param line_range_param = {
+                       .data = (void *)&lf, .retval = 0};
+
+               dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0);
+               if (pubname_param.found) {
+                       line_range_search_cb(&lf.sp_die, &line_range_param);
+                       if (lf.found)
+                               goto found;
+               }
+       }
+
        /* Loop on CUs (Compilation Unit) */
        while (!lf.found && ret >= 0) {
                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)
                off = noff;
        }
 
+found:
        /* Store comp_dir */
        if (lf.found) {
                comp_dir = cu_get_comp_dir(&lf.cu_die);
index beaefc3..605730a 100644 (file)
@@ -49,6 +49,7 @@ struct probe_finder {
        Dwarf_Addr              addr;           /* Address */
        const char              *fname;         /* Real file name */
        Dwarf_Die               cu_die;         /* Current CU */
+       Dwarf_Die               sp_die;
        struct list_head        lcache;         /* Line cache for lazy match */
 
        /* For variable searching */
@@ -83,6 +84,7 @@ struct line_finder {
        int                     lno_s;          /* Start line number */
        int                     lno_e;          /* End line number */
        Dwarf_Die               cu_die;         /* Current CU */
+       Dwarf_Die               sp_die;
        int                     found;
 };