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