perf callchain: Per-event type selection support
authorKan Liang <kan.liang@intel.com>
Tue, 11 Aug 2015 10:30:47 +0000 (06:30 -0400)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 12 Aug 2015 16:20:27 +0000 (13:20 -0300)
This patchkit adds the ability to set callgraph mode (fp, dwarf, lbr) per
event. This in term can reduce sampling overhead and the size of the
perf.data.

Here is an example.

  perf record -e 'cpu/cpu-cycles,period=1000,call-graph=fp,time=1/,cpu/instructions,call-graph=lbr/' sleep 1

 perf evlist -v
 cpu/cpu-cycles,period=1000,call-graph=fp,time=1/: type: 4, size: 112,
 config: 0x3c, { sample_period, sample_freq }: 1000, sample_type:
 IP|TID|TIME|CALLCHAIN|PERIOD|IDENTIFIER, read_format: ID, disabled: 1,
 inherit: 1, mmap: 1, comm: 1, enable_on_exec: 1, task: 1, sample_id_all:
 1, exclude_guest: 1, mmap2: 1, comm_exec: 1
 cpu/instructions,call-graph=lbr/: type: 4, size: 112, config: 0xc0, {
 sample_period, sample_freq }: 4000, sample_type:
 IP|TID|TIME|CALLCHAIN|PERIOD|BRANCH_STACK|IDENTIFIER, read_format: ID,
 disabled: 1, inherit: 1, freq: 1, enable_on_exec: 1, sample_id_all: 1,
 exclude_guest: 1

Signed-off-by: Kan Liang <kan.liang@intel.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/r/1439289050-40510-1-git-send-email-kan.liang@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Documentation/perf-record.txt
tools/perf/util/evsel.c
tools/perf/util/evsel.h
tools/perf/util/parse-events.c
tools/perf/util/parse-events.h
tools/perf/util/parse-events.l
tools/perf/util/pmu.c

index afbe45ef7e3ec00b164036ea15d5d1f6319cb161..7f82dec2b5415405baf88fef1aa545f8d391aa5f 100644 (file)
@@ -53,6 +53,9 @@ OPTIONS
          - 'time': Disable/enable time stamping. Acceptable values are 1 for
                    enabling time stamping. 0 for disabling time stamping.
                    The default is 1.
+         - 'call-graph': Disable/enable callgraph. Acceptable str are "fp" for
+                        FP mode, "dwarf" for DWARF mode, "lbr" for LBR mode.
+         - 'stack-size': user stack size for dwarf mode
          Note: If user explicitly sets options which conflict with the params,
          the value set by the params will be overridden.
 
index 04fddddc6b6fc93879eb5ec13d946117e537ef1f..6647925d5f289c2ccf84bf426fa61a577fce43a2 100644 (file)
@@ -588,11 +588,36 @@ perf_evsel__config_callgraph(struct perf_evsel *evsel,
        }
 }
 
-static void apply_config_terms(struct perf_evsel *evsel)
+static void
+perf_evsel__reset_callgraph(struct perf_evsel *evsel,
+                           struct callchain_param *param)
+{
+       struct perf_event_attr *attr = &evsel->attr;
+
+       perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
+       if (param->record_mode == CALLCHAIN_LBR) {
+               perf_evsel__reset_sample_bit(evsel, BRANCH_STACK);
+               attr->branch_sample_type &= ~(PERF_SAMPLE_BRANCH_USER |
+                                             PERF_SAMPLE_BRANCH_CALL_STACK);
+       }
+       if (param->record_mode == CALLCHAIN_DWARF) {
+               perf_evsel__reset_sample_bit(evsel, REGS_USER);
+               perf_evsel__reset_sample_bit(evsel, STACK_USER);
+       }
+}
+
+static void apply_config_terms(struct perf_evsel *evsel,
+                              struct record_opts *opts)
 {
        struct perf_evsel_config_term *term;
        struct list_head *config_terms = &evsel->config_terms;
        struct perf_event_attr *attr = &evsel->attr;
+       struct callchain_param param;
+       u32 dump_size = 0;
+       char *callgraph_buf = NULL;
+
+       /* callgraph default */
+       param.record_mode = callchain_param.record_mode;
 
        list_for_each_entry(term, config_terms, list) {
                switch (term->type) {
@@ -610,10 +635,43 @@ static void apply_config_terms(struct perf_evsel *evsel)
                        else
                                perf_evsel__reset_sample_bit(evsel, TIME);
                        break;
+               case PERF_EVSEL__CONFIG_TERM_CALLGRAPH:
+                       callgraph_buf = term->val.callgraph;
+                       break;
+               case PERF_EVSEL__CONFIG_TERM_STACK_USER:
+                       dump_size = term->val.stack_user;
+                       break;
                default:
                        break;
                }
        }
+
+       /* User explicitly set per-event callgraph, clear the old setting and reset. */
+       if ((callgraph_buf != NULL) || (dump_size > 0)) {
+
+               /* parse callgraph parameters */
+               if (callgraph_buf != NULL) {
+                       param.enabled = true;
+                       if (parse_callchain_record(callgraph_buf, &param)) {
+                               pr_err("per-event callgraph setting for %s failed. "
+                                      "Apply callgraph global setting for it\n",
+                                      evsel->name);
+                               return;
+                       }
+               }
+               if (dump_size > 0) {
+                       dump_size = round_up(dump_size, sizeof(u64));
+                       param.dump_size = dump_size;
+               }
+
+               /* If global callgraph set, clear it */
+               if (callchain_param.enabled)
+                       perf_evsel__reset_callgraph(evsel, &callchain_param);
+
+               /* set perf-event callgraph */
+               if (param.enabled)
+                       perf_evsel__config_callgraph(evsel, opts, &param);
+       }
 }
 
 /*
@@ -812,7 +870,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
         * Apply event specific term settings,
         * it overloads any global configuration.
         */
-       apply_config_terms(evsel);
+       apply_config_terms(evsel, opts);
 }
 
 static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
index fdf2674ab339785c4217df7eb68bf6632066d540..93ac6b128149af68ba1ed0feec510aa5c30a0410 100644 (file)
@@ -41,6 +41,8 @@ enum {
        PERF_EVSEL__CONFIG_TERM_PERIOD,
        PERF_EVSEL__CONFIG_TERM_FREQ,
        PERF_EVSEL__CONFIG_TERM_TIME,
+       PERF_EVSEL__CONFIG_TERM_CALLGRAPH,
+       PERF_EVSEL__CONFIG_TERM_STACK_USER,
        PERF_EVSEL__CONFIG_TERM_MAX,
 };
 
@@ -51,6 +53,8 @@ struct perf_evsel_config_term {
                u64     period;
                u64     freq;
                bool    time;
+               char    *callgraph;
+               u64     stack_user;
        } val;
 };
 
index dbf315df422016c48952cd072eb8d2a2c1056dfb..d826e6f515db12a3f75517bf93437eb9d0043829 100644 (file)
@@ -614,6 +614,12 @@ do {                                                                          \
                        return -EINVAL;
                }
                break;
+       case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
+               CHECK_TYPE_VAL(STR);
+               break;
+       case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
+               CHECK_TYPE_VAL(NUM);
+               break;
        case PARSE_EVENTS__TERM_TYPE_NAME:
                CHECK_TYPE_VAL(STR);
                break;
@@ -668,6 +674,12 @@ do {                                                               \
                case PARSE_EVENTS__TERM_TYPE_TIME:
                        ADD_CONFIG_TERM(TIME, time, term->val.num);
                        break;
+               case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
+                       ADD_CONFIG_TERM(CALLGRAPH, callgraph, term->val.str);
+                       break;
+               case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
+                       ADD_CONFIG_TERM(STACK_USER, stack_user, term->val.num);
+                       break;
                default:
                        break;
                }
index ce2d13a16226e8fe73c02aaccb34a95c9b45cc09..a09b0e2109973395b5375832ec99a65c707ac630 100644 (file)
@@ -65,6 +65,8 @@ enum {
        PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ,
        PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
        PARSE_EVENTS__TERM_TYPE_TIME,
+       PARSE_EVENTS__TERM_TYPE_CALLGRAPH,
+       PARSE_EVENTS__TERM_TYPE_STACKSIZE,
 };
 
 struct parse_events_term {
index 4306f5ad75c756b74ad7350773ec273b6043988b..936d566f48d8df39062848fbca9630eadbee3ca0 100644 (file)
@@ -185,6 +185,8 @@ period                      { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
 freq                   { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ); }
 branch_type            { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
 time                   { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_TIME); }
+call-graph             { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CALLGRAPH); }
+stack-size             { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_STACKSIZE); }
 ,                      { return ','; }
 "/"                    { BEGIN(INITIAL); return '/'; }
 {name_minus}           { return str(yyscanner, PE_NAME); }
index d85f11b8cacf00cefeb7b97c6b3aa42da69ed4bc..84cad054d6f798bcc166402c62ebc89f29b4acc0 100644 (file)
@@ -634,7 +634,9 @@ static char *formats_error_string(struct list_head *formats)
 {
        struct perf_pmu_format *format;
        char *err, *str;
-       static const char *static_terms = "config,config1,config2,name,period,freq,branch_type,time\n";
+       static const char *static_terms = "config,config1,config2,name,"
+                                         "period,freq,branch_type,time,"
+                                         "call-graph,stack-size\n";
        unsigned i = 0;
 
        if (!asprintf(&str, "valid terms:"))