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