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