Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <inttypes.h>
0003 #include <sys/types.h>
0004 #include <sys/stat.h>
0005 #include <unistd.h>
0006 #include "builtin.h"
0007 #include "perf.h"
0008 
0009 #include <subcmd/parse-options.h>
0010 #include "util/auxtrace.h"
0011 #include "util/trace-event.h"
0012 #include "util/tool.h"
0013 #include "util/session.h"
0014 #include "util/data.h"
0015 #include "util/map_symbol.h"
0016 #include "util/mem-events.h"
0017 #include "util/debug.h"
0018 #include "util/dso.h"
0019 #include "util/map.h"
0020 #include "util/symbol.h"
0021 #include "util/pmu.h"
0022 #include "util/pmu-hybrid.h"
0023 #include "util/string2.h"
0024 #include <linux/err.h>
0025 
0026 #define MEM_OPERATION_LOAD  0x1
0027 #define MEM_OPERATION_STORE 0x2
0028 
0029 struct perf_mem {
0030     struct perf_tool    tool;
0031     char const      *input_name;
0032     bool            hide_unresolved;
0033     bool            dump_raw;
0034     bool            force;
0035     bool            phys_addr;
0036     bool            data_page_size;
0037     int         operation;
0038     const char      *cpu_list;
0039     DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
0040 };
0041 
0042 static int parse_record_events(const struct option *opt,
0043                    const char *str, int unset __maybe_unused)
0044 {
0045     struct perf_mem *mem = *(struct perf_mem **)opt->value;
0046 
0047     if (!strcmp(str, "list")) {
0048         perf_mem_events__list();
0049         exit(0);
0050     }
0051     if (perf_mem_events__parse(str))
0052         exit(-1);
0053 
0054     mem->operation = 0;
0055     return 0;
0056 }
0057 
0058 static const char * const __usage[] = {
0059     "perf mem record [<options>] [<command>]",
0060     "perf mem record [<options>] -- <command> [<options>]",
0061     NULL
0062 };
0063 
0064 static const char * const *record_mem_usage = __usage;
0065 
0066 static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
0067 {
0068     int rec_argc, i = 0, j, tmp_nr = 0;
0069     int start, end;
0070     const char **rec_argv;
0071     char **rec_tmp;
0072     int ret;
0073     bool all_user = false, all_kernel = false;
0074     struct perf_mem_event *e;
0075     struct option options[] = {
0076     OPT_CALLBACK('e', "event", &mem, "event",
0077              "event selector. use 'perf mem record -e list' to list available events",
0078              parse_record_events),
0079     OPT_UINTEGER(0, "ldlat", &perf_mem_events__loads_ldlat, "mem-loads latency"),
0080     OPT_INCR('v', "verbose", &verbose,
0081          "be more verbose (show counter open errors, etc)"),
0082     OPT_BOOLEAN('U', "all-user", &all_user, "collect only user level data"),
0083     OPT_BOOLEAN('K', "all-kernel", &all_kernel, "collect only kernel level data"),
0084     OPT_END()
0085     };
0086 
0087     if (perf_mem_events__init()) {
0088         pr_err("failed: memory events not supported\n");
0089         return -1;
0090     }
0091 
0092     argc = parse_options(argc, argv, options, record_mem_usage,
0093                  PARSE_OPT_KEEP_UNKNOWN);
0094 
0095     if (!perf_pmu__has_hybrid())
0096         rec_argc = argc + 9; /* max number of arguments */
0097     else
0098         rec_argc = argc + 9 * perf_pmu__hybrid_pmu_num();
0099 
0100     rec_argv = calloc(rec_argc + 1, sizeof(char *));
0101     if (!rec_argv)
0102         return -1;
0103 
0104     /*
0105      * Save the allocated event name strings.
0106      */
0107     rec_tmp = calloc(rec_argc + 1, sizeof(char *));
0108     if (!rec_tmp) {
0109         free(rec_argv);
0110         return -1;
0111     }
0112 
0113     rec_argv[i++] = "record";
0114 
0115     e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD_STORE);
0116 
0117     /*
0118      * The load and store operations are required, use the event
0119      * PERF_MEM_EVENTS__LOAD_STORE if it is supported.
0120      */
0121     if (e->tag &&
0122         (mem->operation & MEM_OPERATION_LOAD) &&
0123         (mem->operation & MEM_OPERATION_STORE)) {
0124         e->record = true;
0125     } else {
0126         if (mem->operation & MEM_OPERATION_LOAD) {
0127             e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD);
0128             e->record = true;
0129         }
0130 
0131         if (mem->operation & MEM_OPERATION_STORE) {
0132             e = perf_mem_events__ptr(PERF_MEM_EVENTS__STORE);
0133             e->record = true;
0134         }
0135     }
0136 
0137     e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD);
0138     if (e->record)
0139         rec_argv[i++] = "-W";
0140 
0141     rec_argv[i++] = "-d";
0142 
0143     if (mem->phys_addr)
0144         rec_argv[i++] = "--phys-data";
0145 
0146     if (mem->data_page_size)
0147         rec_argv[i++] = "--data-page-size";
0148 
0149     start = i;
0150     ret = perf_mem_events__record_args(rec_argv, &i, rec_tmp, &tmp_nr);
0151     if (ret)
0152         goto out;
0153     end = i;
0154 
0155     if (all_user)
0156         rec_argv[i++] = "--all-user";
0157 
0158     if (all_kernel)
0159         rec_argv[i++] = "--all-kernel";
0160 
0161     for (j = 0; j < argc; j++, i++)
0162         rec_argv[i] = argv[j];
0163 
0164     if (verbose > 0) {
0165         pr_debug("calling: record ");
0166 
0167         for (j = start; j < end; j++)
0168             pr_debug("%s ", rec_argv[j]);
0169 
0170         pr_debug("\n");
0171     }
0172 
0173     ret = cmd_record(i, rec_argv);
0174 out:
0175     for (i = 0; i < tmp_nr; i++)
0176         free(rec_tmp[i]);
0177 
0178     free(rec_tmp);
0179     free(rec_argv);
0180     return ret;
0181 }
0182 
0183 static int
0184 dump_raw_samples(struct perf_tool *tool,
0185          union perf_event *event,
0186          struct perf_sample *sample,
0187          struct machine *machine)
0188 {
0189     struct perf_mem *mem = container_of(tool, struct perf_mem, tool);
0190     struct addr_location al;
0191     const char *fmt, *field_sep;
0192     char str[PAGE_SIZE_NAME_LEN];
0193 
0194     if (machine__resolve(machine, &al, sample) < 0) {
0195         fprintf(stderr, "problem processing %d event, skipping it.\n",
0196                 event->header.type);
0197         return -1;
0198     }
0199 
0200     if (al.filtered || (mem->hide_unresolved && al.sym == NULL))
0201         goto out_put;
0202 
0203     if (al.map != NULL)
0204         al.map->dso->hit = 1;
0205 
0206     field_sep = symbol_conf.field_sep;
0207     if (field_sep) {
0208         fmt = "%d%s%d%s0x%"PRIx64"%s0x%"PRIx64"%s";
0209     } else {
0210         fmt = "%5d%s%5d%s0x%016"PRIx64"%s0x016%"PRIx64"%s";
0211         symbol_conf.field_sep = " ";
0212     }
0213     printf(fmt,
0214         sample->pid,
0215         symbol_conf.field_sep,
0216         sample->tid,
0217         symbol_conf.field_sep,
0218         sample->ip,
0219         symbol_conf.field_sep,
0220         sample->addr,
0221         symbol_conf.field_sep);
0222 
0223     if (mem->phys_addr) {
0224         printf("0x%016"PRIx64"%s",
0225             sample->phys_addr,
0226             symbol_conf.field_sep);
0227     }
0228 
0229     if (mem->data_page_size) {
0230         printf("%s%s",
0231             get_page_size_name(sample->data_page_size, str),
0232             symbol_conf.field_sep);
0233     }
0234 
0235     if (field_sep)
0236         fmt = "%"PRIu64"%s0x%"PRIx64"%s%s:%s\n";
0237     else
0238         fmt = "%5"PRIu64"%s0x%06"PRIx64"%s%s:%s\n";
0239 
0240     printf(fmt,
0241         sample->weight,
0242         symbol_conf.field_sep,
0243         sample->data_src,
0244         symbol_conf.field_sep,
0245         al.map ? (al.map->dso ? al.map->dso->long_name : "???") : "???",
0246         al.sym ? al.sym->name : "???");
0247 out_put:
0248     addr_location__put(&al);
0249     return 0;
0250 }
0251 
0252 static int process_sample_event(struct perf_tool *tool,
0253                 union perf_event *event,
0254                 struct perf_sample *sample,
0255                 struct evsel *evsel __maybe_unused,
0256                 struct machine *machine)
0257 {
0258     return dump_raw_samples(tool, event, sample, machine);
0259 }
0260 
0261 static int report_raw_events(struct perf_mem *mem)
0262 {
0263     struct itrace_synth_opts itrace_synth_opts = {
0264         .set = true,
0265         .mem = true,    /* Only enable memory event */
0266         .default_no_sample = true,
0267     };
0268 
0269     struct perf_data data = {
0270         .path  = input_name,
0271         .mode  = PERF_DATA_MODE_READ,
0272         .force = mem->force,
0273     };
0274     int ret;
0275     struct perf_session *session = perf_session__new(&data, &mem->tool);
0276 
0277     if (IS_ERR(session))
0278         return PTR_ERR(session);
0279 
0280     session->itrace_synth_opts = &itrace_synth_opts;
0281 
0282     if (mem->cpu_list) {
0283         ret = perf_session__cpu_bitmap(session, mem->cpu_list,
0284                            mem->cpu_bitmap);
0285         if (ret < 0)
0286             goto out_delete;
0287     }
0288 
0289     ret = symbol__init(&session->header.env);
0290     if (ret < 0)
0291         goto out_delete;
0292 
0293     printf("# PID, TID, IP, ADDR, ");
0294 
0295     if (mem->phys_addr)
0296         printf("PHYS ADDR, ");
0297 
0298     if (mem->data_page_size)
0299         printf("DATA PAGE SIZE, ");
0300 
0301     printf("LOCAL WEIGHT, DSRC, SYMBOL\n");
0302 
0303     ret = perf_session__process_events(session);
0304 
0305 out_delete:
0306     perf_session__delete(session);
0307     return ret;
0308 }
0309 static char *get_sort_order(struct perf_mem *mem)
0310 {
0311     bool has_extra_options = (mem->phys_addr | mem->data_page_size) ? true : false;
0312     char sort[128];
0313 
0314     /*
0315      * there is no weight (cost) associated with stores, so don't print
0316      * the column
0317      */
0318     if (!(mem->operation & MEM_OPERATION_LOAD)) {
0319         strcpy(sort, "--sort=mem,sym,dso,symbol_daddr,"
0320                  "dso_daddr,tlb,locked");
0321     } else if (has_extra_options) {
0322         strcpy(sort, "--sort=local_weight,mem,sym,dso,symbol_daddr,"
0323                  "dso_daddr,snoop,tlb,locked,blocked");
0324     } else
0325         return NULL;
0326 
0327     if (mem->phys_addr)
0328         strcat(sort, ",phys_daddr");
0329 
0330     if (mem->data_page_size)
0331         strcat(sort, ",data_page_size");
0332 
0333     return strdup(sort);
0334 }
0335 
0336 static int report_events(int argc, const char **argv, struct perf_mem *mem)
0337 {
0338     const char **rep_argv;
0339     int ret, i = 0, j, rep_argc;
0340     char *new_sort_order;
0341 
0342     if (mem->dump_raw)
0343         return report_raw_events(mem);
0344 
0345     rep_argc = argc + 3;
0346     rep_argv = calloc(rep_argc + 1, sizeof(char *));
0347     if (!rep_argv)
0348         return -1;
0349 
0350     rep_argv[i++] = "report";
0351     rep_argv[i++] = "--mem-mode";
0352     rep_argv[i++] = "-n"; /* display number of samples */
0353 
0354     new_sort_order = get_sort_order(mem);
0355     if (new_sort_order)
0356         rep_argv[i++] = new_sort_order;
0357 
0358     for (j = 1; j < argc; j++, i++)
0359         rep_argv[i] = argv[j];
0360 
0361     ret = cmd_report(i, rep_argv);
0362     free(rep_argv);
0363     return ret;
0364 }
0365 
0366 struct mem_mode {
0367     const char *name;
0368     int mode;
0369 };
0370 
0371 #define MEM_OPT(n, m) \
0372     { .name = n, .mode = (m) }
0373 
0374 #define MEM_END { .name = NULL }
0375 
0376 static const struct mem_mode mem_modes[]={
0377     MEM_OPT("load", MEM_OPERATION_LOAD),
0378     MEM_OPT("store", MEM_OPERATION_STORE),
0379     MEM_END
0380 };
0381 
0382 static int
0383 parse_mem_ops(const struct option *opt, const char *str, int unset)
0384 {
0385     int *mode = (int *)opt->value;
0386     const struct mem_mode *m;
0387     char *s, *os = NULL, *p;
0388     int ret = -1;
0389 
0390     if (unset)
0391         return 0;
0392 
0393     /* str may be NULL in case no arg is passed to -t */
0394     if (str) {
0395         /* because str is read-only */
0396         s = os = strdup(str);
0397         if (!s)
0398             return -1;
0399 
0400         /* reset mode */
0401         *mode = 0;
0402 
0403         for (;;) {
0404             p = strchr(s, ',');
0405             if (p)
0406                 *p = '\0';
0407 
0408             for (m = mem_modes; m->name; m++) {
0409                 if (!strcasecmp(s, m->name))
0410                     break;
0411             }
0412             if (!m->name) {
0413                 fprintf(stderr, "unknown sampling op %s,"
0414                         " check man page\n", s);
0415                 goto error;
0416             }
0417 
0418             *mode |= m->mode;
0419 
0420             if (!p)
0421                 break;
0422 
0423             s = p + 1;
0424         }
0425     }
0426     ret = 0;
0427 
0428     if (*mode == 0)
0429         *mode = MEM_OPERATION_LOAD;
0430 error:
0431     free(os);
0432     return ret;
0433 }
0434 
0435 int cmd_mem(int argc, const char **argv)
0436 {
0437     struct stat st;
0438     struct perf_mem mem = {
0439         .tool = {
0440             .sample     = process_sample_event,
0441             .mmap       = perf_event__process_mmap,
0442             .mmap2      = perf_event__process_mmap2,
0443             .comm       = perf_event__process_comm,
0444             .lost       = perf_event__process_lost,
0445             .fork       = perf_event__process_fork,
0446             .attr       = perf_event__process_attr,
0447             .build_id   = perf_event__process_build_id,
0448             .namespaces = perf_event__process_namespaces,
0449             .auxtrace_info  = perf_event__process_auxtrace_info,
0450             .auxtrace       = perf_event__process_auxtrace,
0451             .auxtrace_error = perf_event__process_auxtrace_error,
0452             .ordered_events = true,
0453         },
0454         .input_name      = "perf.data",
0455         /*
0456          * default to both load an store sampling
0457          */
0458         .operation       = MEM_OPERATION_LOAD | MEM_OPERATION_STORE,
0459     };
0460     const struct option mem_options[] = {
0461     OPT_CALLBACK('t', "type", &mem.operation,
0462            "type", "memory operations(load,store) Default load,store",
0463             parse_mem_ops),
0464     OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw,
0465             "dump raw samples in ASCII"),
0466     OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved,
0467             "Only display entries resolved to a symbol"),
0468     OPT_STRING('i', "input", &input_name, "file",
0469            "input file name"),
0470     OPT_STRING('C', "cpu", &mem.cpu_list, "cpu",
0471            "list of cpus to profile"),
0472     OPT_STRING_NOEMPTY('x', "field-separator", &symbol_conf.field_sep,
0473            "separator",
0474            "separator for columns, no spaces will be added"
0475            " between columns '.' is reserved."),
0476     OPT_BOOLEAN('f', "force", &mem.force, "don't complain, do it"),
0477     OPT_BOOLEAN('p', "phys-data", &mem.phys_addr, "Record/Report sample physical addresses"),
0478     OPT_BOOLEAN(0, "data-page-size", &mem.data_page_size, "Record/Report sample data address page size"),
0479     OPT_END()
0480     };
0481     const char *const mem_subcommands[] = { "record", "report", NULL };
0482     const char *mem_usage[] = {
0483         NULL,
0484         NULL
0485     };
0486 
0487     argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands,
0488                     mem_usage, PARSE_OPT_KEEP_UNKNOWN);
0489 
0490     if (!argc || !(strncmp(argv[0], "rec", 3) || mem.operation))
0491         usage_with_options(mem_usage, mem_options);
0492 
0493     if (!mem.input_name || !strlen(mem.input_name)) {
0494         if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
0495             mem.input_name = "-";
0496         else
0497             mem.input_name = "perf.data";
0498     }
0499 
0500     if (strlen(argv[0]) > 2 && strstarts("record", argv[0]))
0501         return __cmd_record(argc, argv, &mem);
0502     else if (strlen(argv[0]) > 2 && strstarts("report", argv[0]))
0503         return report_events(argc, argv, &mem);
0504     else
0505         usage_with_options(mem_usage, mem_options);
0506 
0507     return 0;
0508 }