1 // SPDX-License-Identifier: GPL-2.0 2 #include <inttypes.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <unistd.h> 6 #include "builtin.h" 7 #include "perf.h" 8 9 #include <subcmd/parse-options.h> 10 #include "util/auxtrace.h" 11 #include "util/trace-event.h" 12 #include "util/tool.h" 13 #include "util/session.h" 14 #include "util/data.h" 15 #include "util/map_symbol.h" 16 #include "util/mem-events.h" 17 #include "util/debug.h" 18 #include "util/dso.h" 19 #include "util/map.h" 20 #include "util/symbol.h" 21 #include <linux/err.h> 22 23 #define MEM_OPERATION_LOAD 0x1 24 #define MEM_OPERATION_STORE 0x2 25 26 struct perf_mem { 27 struct perf_tool tool; 28 char const *input_name; 29 bool hide_unresolved; 30 bool dump_raw; 31 bool force; 32 bool phys_addr; 33 bool data_page_size; 34 int operation; 35 const char *cpu_list; 36 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); 37 }; 38 39 static int parse_record_events(const struct option *opt, 40 const char *str, int unset __maybe_unused) 41 { 42 struct perf_mem *mem = *(struct perf_mem **)opt->value; 43 44 if (!strcmp(str, "list")) { 45 perf_mem_events__list(); 46 exit(0); 47 } 48 if (perf_mem_events__parse(str)) 49 exit(-1); 50 51 mem->operation = 0; 52 return 0; 53 } 54 55 static const char * const __usage[] = { 56 "perf mem record [<options>] [<command>]", 57 "perf mem record [<options>] -- <command> [<options>]", 58 NULL 59 }; 60 61 static const char * const *record_mem_usage = __usage; 62 63 static int __cmd_record(int argc, const char **argv, struct perf_mem *mem) 64 { 65 int rec_argc, i = 0, j; 66 const char **rec_argv; 67 int ret; 68 bool all_user = false, all_kernel = false; 69 struct perf_mem_event *e; 70 struct option options[] = { 71 OPT_CALLBACK('e', "event", &mem, "event", 72 "event selector. use 'perf mem record -e list' to list available events", 73 parse_record_events), 74 OPT_UINTEGER(0, "ldlat", &perf_mem_events__loads_ldlat, "mem-loads latency"), 75 OPT_INCR('v', "verbose", &verbose, 76 "be more verbose (show counter open errors, etc)"), 77 OPT_BOOLEAN('U', "all-user", &all_user, "collect only user level data"), 78 OPT_BOOLEAN('K', "all-kernel", &all_kernel, "collect only kernel level data"), 79 OPT_END() 80 }; 81 82 if (perf_mem_events__init()) { 83 pr_err("failed: memory events not supported\n"); 84 return -1; 85 } 86 87 argc = parse_options(argc, argv, options, record_mem_usage, 88 PARSE_OPT_KEEP_UNKNOWN); 89 90 rec_argc = argc + 9; /* max number of arguments */ 91 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 92 if (!rec_argv) 93 return -1; 94 95 rec_argv[i++] = "record"; 96 97 e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD_STORE); 98 99 /* 100 * The load and store operations are required, use the event 101 * PERF_MEM_EVENTS__LOAD_STORE if it is supported. 102 */ 103 if (e->tag && 104 (mem->operation & MEM_OPERATION_LOAD) && 105 (mem->operation & MEM_OPERATION_STORE)) { 106 e->record = true; 107 } else { 108 if (mem->operation & MEM_OPERATION_LOAD) { 109 e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD); 110 e->record = true; 111 } 112 113 if (mem->operation & MEM_OPERATION_STORE) { 114 e = perf_mem_events__ptr(PERF_MEM_EVENTS__STORE); 115 e->record = true; 116 } 117 } 118 119 e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD); 120 if (e->record) 121 rec_argv[i++] = "-W"; 122 123 rec_argv[i++] = "-d"; 124 125 if (mem->phys_addr) 126 rec_argv[i++] = "--phys-data"; 127 128 if (mem->data_page_size) 129 rec_argv[i++] = "--data-page-size"; 130 131 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) { 132 e = perf_mem_events__ptr(j); 133 if (!e->record) 134 continue; 135 136 if (!e->supported) { 137 pr_err("failed: event '%s' not supported\n", 138 perf_mem_events__name(j)); 139 free(rec_argv); 140 return -1; 141 } 142 143 rec_argv[i++] = "-e"; 144 rec_argv[i++] = perf_mem_events__name(j); 145 } 146 147 if (all_user) 148 rec_argv[i++] = "--all-user"; 149 150 if (all_kernel) 151 rec_argv[i++] = "--all-kernel"; 152 153 for (j = 0; j < argc; j++, i++) 154 rec_argv[i] = argv[j]; 155 156 if (verbose > 0) { 157 pr_debug("calling: record "); 158 159 while (rec_argv[j]) { 160 pr_debug("%s ", rec_argv[j]); 161 j++; 162 } 163 pr_debug("\n"); 164 } 165 166 ret = cmd_record(i, rec_argv); 167 free(rec_argv); 168 return ret; 169 } 170 171 static int 172 dump_raw_samples(struct perf_tool *tool, 173 union perf_event *event, 174 struct perf_sample *sample, 175 struct machine *machine) 176 { 177 struct perf_mem *mem = container_of(tool, struct perf_mem, tool); 178 struct addr_location al; 179 const char *fmt, *field_sep; 180 char str[PAGE_SIZE_NAME_LEN]; 181 182 if (machine__resolve(machine, &al, sample) < 0) { 183 fprintf(stderr, "problem processing %d event, skipping it.\n", 184 event->header.type); 185 return -1; 186 } 187 188 if (al.filtered || (mem->hide_unresolved && al.sym == NULL)) 189 goto out_put; 190 191 if (al.map != NULL) 192 al.map->dso->hit = 1; 193 194 field_sep = symbol_conf.field_sep; 195 if (field_sep) { 196 fmt = "%d%s%d%s0x%"PRIx64"%s0x%"PRIx64"%s"; 197 } else { 198 fmt = "%5d%s%5d%s0x%016"PRIx64"%s0x016%"PRIx64"%s"; 199 symbol_conf.field_sep = " "; 200 } 201 printf(fmt, 202 sample->pid, 203 symbol_conf.field_sep, 204 sample->tid, 205 symbol_conf.field_sep, 206 sample->ip, 207 symbol_conf.field_sep, 208 sample->addr, 209 symbol_conf.field_sep); 210 211 if (mem->phys_addr) { 212 printf("0x%016"PRIx64"%s", 213 sample->phys_addr, 214 symbol_conf.field_sep); 215 } 216 217 if (mem->data_page_size) { 218 printf("%s%s", 219 get_page_size_name(sample->data_page_size, str), 220 symbol_conf.field_sep); 221 } 222 223 if (field_sep) 224 fmt = "%"PRIu64"%s0x%"PRIx64"%s%s:%s\n"; 225 else 226 fmt = "%5"PRIu64"%s0x%06"PRIx64"%s%s:%s\n"; 227 228 printf(fmt, 229 sample->weight, 230 symbol_conf.field_sep, 231 sample->data_src, 232 symbol_conf.field_sep, 233 al.map ? (al.map->dso ? al.map->dso->long_name : "???") : "???", 234 al.sym ? al.sym->name : "???"); 235 out_put: 236 addr_location__put(&al); 237 return 0; 238 } 239 240 static int process_sample_event(struct perf_tool *tool, 241 union perf_event *event, 242 struct perf_sample *sample, 243 struct evsel *evsel __maybe_unused, 244 struct machine *machine) 245 { 246 return dump_raw_samples(tool, event, sample, machine); 247 } 248 249 static int report_raw_events(struct perf_mem *mem) 250 { 251 struct itrace_synth_opts itrace_synth_opts = { 252 .set = true, 253 .mem = true, /* Only enable memory event */ 254 .default_no_sample = true, 255 }; 256 257 struct perf_data data = { 258 .path = input_name, 259 .mode = PERF_DATA_MODE_READ, 260 .force = mem->force, 261 }; 262 int ret; 263 struct perf_session *session = perf_session__new(&data, false, 264 &mem->tool); 265 266 if (IS_ERR(session)) 267 return PTR_ERR(session); 268 269 session->itrace_synth_opts = &itrace_synth_opts; 270 271 if (mem->cpu_list) { 272 ret = perf_session__cpu_bitmap(session, mem->cpu_list, 273 mem->cpu_bitmap); 274 if (ret < 0) 275 goto out_delete; 276 } 277 278 ret = symbol__init(&session->header.env); 279 if (ret < 0) 280 goto out_delete; 281 282 printf("# PID, TID, IP, ADDR, "); 283 284 if (mem->phys_addr) 285 printf("PHYS ADDR, "); 286 287 if (mem->data_page_size) 288 printf("DATA PAGE SIZE, "); 289 290 printf("LOCAL WEIGHT, DSRC, SYMBOL\n"); 291 292 ret = perf_session__process_events(session); 293 294 out_delete: 295 perf_session__delete(session); 296 return ret; 297 } 298 static char *get_sort_order(struct perf_mem *mem) 299 { 300 bool has_extra_options = (mem->phys_addr | mem->data_page_size) ? true : false; 301 char sort[128]; 302 303 /* 304 * there is no weight (cost) associated with stores, so don't print 305 * the column 306 */ 307 if (!(mem->operation & MEM_OPERATION_LOAD)) { 308 strcpy(sort, "--sort=mem,sym,dso,symbol_daddr," 309 "dso_daddr,tlb,locked"); 310 } else if (has_extra_options) { 311 strcpy(sort, "--sort=local_weight,mem,sym,dso,symbol_daddr," 312 "dso_daddr,snoop,tlb,locked,blocked"); 313 } else 314 return NULL; 315 316 if (mem->phys_addr) 317 strcat(sort, ",phys_daddr"); 318 319 if (mem->data_page_size) 320 strcat(sort, ",data_page_size"); 321 322 return strdup(sort); 323 } 324 325 static int report_events(int argc, const char **argv, struct perf_mem *mem) 326 { 327 const char **rep_argv; 328 int ret, i = 0, j, rep_argc; 329 char *new_sort_order; 330 331 if (mem->dump_raw) 332 return report_raw_events(mem); 333 334 rep_argc = argc + 3; 335 rep_argv = calloc(rep_argc + 1, sizeof(char *)); 336 if (!rep_argv) 337 return -1; 338 339 rep_argv[i++] = "report"; 340 rep_argv[i++] = "--mem-mode"; 341 rep_argv[i++] = "-n"; /* display number of samples */ 342 343 new_sort_order = get_sort_order(mem); 344 if (new_sort_order) 345 rep_argv[i++] = new_sort_order; 346 347 for (j = 1; j < argc; j++, i++) 348 rep_argv[i] = argv[j]; 349 350 ret = cmd_report(i, rep_argv); 351 free(rep_argv); 352 return ret; 353 } 354 355 struct mem_mode { 356 const char *name; 357 int mode; 358 }; 359 360 #define MEM_OPT(n, m) \ 361 { .name = n, .mode = (m) } 362 363 #define MEM_END { .name = NULL } 364 365 static const struct mem_mode mem_modes[]={ 366 MEM_OPT("load", MEM_OPERATION_LOAD), 367 MEM_OPT("store", MEM_OPERATION_STORE), 368 MEM_END 369 }; 370 371 static int 372 parse_mem_ops(const struct option *opt, const char *str, int unset) 373 { 374 int *mode = (int *)opt->value; 375 const struct mem_mode *m; 376 char *s, *os = NULL, *p; 377 int ret = -1; 378 379 if (unset) 380 return 0; 381 382 /* str may be NULL in case no arg is passed to -t */ 383 if (str) { 384 /* because str is read-only */ 385 s = os = strdup(str); 386 if (!s) 387 return -1; 388 389 /* reset mode */ 390 *mode = 0; 391 392 for (;;) { 393 p = strchr(s, ','); 394 if (p) 395 *p = '\0'; 396 397 for (m = mem_modes; m->name; m++) { 398 if (!strcasecmp(s, m->name)) 399 break; 400 } 401 if (!m->name) { 402 fprintf(stderr, "unknown sampling op %s," 403 " check man page\n", s); 404 goto error; 405 } 406 407 *mode |= m->mode; 408 409 if (!p) 410 break; 411 412 s = p + 1; 413 } 414 } 415 ret = 0; 416 417 if (*mode == 0) 418 *mode = MEM_OPERATION_LOAD; 419 error: 420 free(os); 421 return ret; 422 } 423 424 int cmd_mem(int argc, const char **argv) 425 { 426 struct stat st; 427 struct perf_mem mem = { 428 .tool = { 429 .sample = process_sample_event, 430 .mmap = perf_event__process_mmap, 431 .mmap2 = perf_event__process_mmap2, 432 .comm = perf_event__process_comm, 433 .lost = perf_event__process_lost, 434 .fork = perf_event__process_fork, 435 .attr = perf_event__process_attr, 436 .build_id = perf_event__process_build_id, 437 .namespaces = perf_event__process_namespaces, 438 .auxtrace_info = perf_event__process_auxtrace_info, 439 .auxtrace = perf_event__process_auxtrace, 440 .auxtrace_error = perf_event__process_auxtrace_error, 441 .ordered_events = true, 442 }, 443 .input_name = "perf.data", 444 /* 445 * default to both load an store sampling 446 */ 447 .operation = MEM_OPERATION_LOAD | MEM_OPERATION_STORE, 448 }; 449 const struct option mem_options[] = { 450 OPT_CALLBACK('t', "type", &mem.operation, 451 "type", "memory operations(load,store) Default load,store", 452 parse_mem_ops), 453 OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw, 454 "dump raw samples in ASCII"), 455 OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved, 456 "Only display entries resolved to a symbol"), 457 OPT_STRING('i', "input", &input_name, "file", 458 "input file name"), 459 OPT_STRING('C', "cpu", &mem.cpu_list, "cpu", 460 "list of cpus to profile"), 461 OPT_STRING_NOEMPTY('x', "field-separator", &symbol_conf.field_sep, 462 "separator", 463 "separator for columns, no spaces will be added" 464 " between columns '.' is reserved."), 465 OPT_BOOLEAN('f', "force", &mem.force, "don't complain, do it"), 466 OPT_BOOLEAN('p', "phys-data", &mem.phys_addr, "Record/Report sample physical addresses"), 467 OPT_BOOLEAN(0, "data-page-size", &mem.data_page_size, "Record/Report sample data address page size"), 468 OPT_END() 469 }; 470 const char *const mem_subcommands[] = { "record", "report", NULL }; 471 const char *mem_usage[] = { 472 NULL, 473 NULL 474 }; 475 476 argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands, 477 mem_usage, PARSE_OPT_KEEP_UNKNOWN); 478 479 if (!argc || !(strncmp(argv[0], "rec", 3) || mem.operation)) 480 usage_with_options(mem_usage, mem_options); 481 482 if (!mem.input_name || !strlen(mem.input_name)) { 483 if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) 484 mem.input_name = "-"; 485 else 486 mem.input_name = "perf.data"; 487 } 488 489 if (!strncmp(argv[0], "rec", 3)) 490 return __cmd_record(argc, argv, &mem); 491 else if (!strncmp(argv[0], "rep", 3)) 492 return report_events(argc, argv, &mem); 493 else 494 usage_with_options(mem_usage, mem_options); 495 496 return 0; 497 } 498