0001
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;
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
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
0119
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,
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
0316
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";
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
0394 if (str) {
0395
0396 s = os = strdup(str);
0397 if (!s)
0398 return -1;
0399
0400
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
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 }