linux/tools/perf/builtin-mem.c
<<
>>
Prefs
   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
  26struct 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        int                     operation;
  34        const char              *cpu_list;
  35        DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
  36};
  37
  38static int parse_record_events(const struct option *opt,
  39                               const char *str, int unset __maybe_unused)
  40{
  41        struct perf_mem *mem = *(struct perf_mem **)opt->value;
  42
  43        if (!strcmp(str, "list")) {
  44                perf_mem_events__list();
  45                exit(0);
  46        }
  47        if (perf_mem_events__parse(str))
  48                exit(-1);
  49
  50        mem->operation = 0;
  51        return 0;
  52}
  53
  54static const char * const __usage[] = {
  55        "perf mem record [<options>] [<command>]",
  56        "perf mem record [<options>] -- <command> [<options>]",
  57        NULL
  58};
  59
  60static const char * const *record_mem_usage = __usage;
  61
  62static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
  63{
  64        int rec_argc, i = 0, j;
  65        const char **rec_argv;
  66        int ret;
  67        bool all_user = false, all_kernel = false;
  68        struct perf_mem_event *e;
  69        struct option options[] = {
  70        OPT_CALLBACK('e', "event", &mem, "event",
  71                     "event selector. use 'perf mem record -e list' to list available events",
  72                     parse_record_events),
  73        OPT_UINTEGER(0, "ldlat", &perf_mem_events__loads_ldlat, "mem-loads latency"),
  74        OPT_INCR('v', "verbose", &verbose,
  75                 "be more verbose (show counter open errors, etc)"),
  76        OPT_BOOLEAN('U', "all-user", &all_user, "collect only user level data"),
  77        OPT_BOOLEAN('K', "all-kernel", &all_kernel, "collect only kernel level data"),
  78        OPT_END()
  79        };
  80
  81        if (perf_mem_events__init()) {
  82                pr_err("failed: memory events not supported\n");
  83                return -1;
  84        }
  85
  86        argc = parse_options(argc, argv, options, record_mem_usage,
  87                             PARSE_OPT_KEEP_UNKNOWN);
  88
  89        rec_argc = argc + 9; /* max number of arguments */
  90        rec_argv = calloc(rec_argc + 1, sizeof(char *));
  91        if (!rec_argv)
  92                return -1;
  93
  94        rec_argv[i++] = "record";
  95
  96        e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD_STORE);
  97
  98        /*
  99         * The load and store operations are required, use the event
 100         * PERF_MEM_EVENTS__LOAD_STORE if it is supported.
 101         */
 102        if (e->tag &&
 103            (mem->operation & MEM_OPERATION_LOAD) &&
 104            (mem->operation & MEM_OPERATION_STORE)) {
 105                e->record = true;
 106        } else {
 107                if (mem->operation & MEM_OPERATION_LOAD) {
 108                        e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD);
 109                        e->record = true;
 110                }
 111
 112                if (mem->operation & MEM_OPERATION_STORE) {
 113                        e = perf_mem_events__ptr(PERF_MEM_EVENTS__STORE);
 114                        e->record = true;
 115                }
 116        }
 117
 118        e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD);
 119        if (e->record)
 120                rec_argv[i++] = "-W";
 121
 122        rec_argv[i++] = "-d";
 123
 124        if (mem->phys_addr)
 125                rec_argv[i++] = "--phys-data";
 126
 127        for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
 128                e = perf_mem_events__ptr(j);
 129                if (!e->record)
 130                        continue;
 131
 132                if (!e->supported) {
 133                        pr_err("failed: event '%s' not supported\n",
 134                               perf_mem_events__name(j));
 135                        free(rec_argv);
 136                        return -1;
 137                }
 138
 139                rec_argv[i++] = "-e";
 140                rec_argv[i++] = perf_mem_events__name(j);
 141        }
 142
 143        if (all_user)
 144                rec_argv[i++] = "--all-user";
 145
 146        if (all_kernel)
 147                rec_argv[i++] = "--all-kernel";
 148
 149        for (j = 0; j < argc; j++, i++)
 150                rec_argv[i] = argv[j];
 151
 152        if (verbose > 0) {
 153                pr_debug("calling: record ");
 154
 155                while (rec_argv[j]) {
 156                        pr_debug("%s ", rec_argv[j]);
 157                        j++;
 158                }
 159                pr_debug("\n");
 160        }
 161
 162        ret = cmd_record(i, rec_argv);
 163        free(rec_argv);
 164        return ret;
 165}
 166
 167static int
 168dump_raw_samples(struct perf_tool *tool,
 169                 union perf_event *event,
 170                 struct perf_sample *sample,
 171                 struct machine *machine)
 172{
 173        struct perf_mem *mem = container_of(tool, struct perf_mem, tool);
 174        struct addr_location al;
 175        const char *fmt;
 176
 177        if (machine__resolve(machine, &al, sample) < 0) {
 178                fprintf(stderr, "problem processing %d event, skipping it.\n",
 179                                event->header.type);
 180                return -1;
 181        }
 182
 183        if (al.filtered || (mem->hide_unresolved && al.sym == NULL))
 184                goto out_put;
 185
 186        if (al.map != NULL)
 187                al.map->dso->hit = 1;
 188
 189        if (mem->phys_addr) {
 190                if (symbol_conf.field_sep) {
 191                        fmt = "%d%s%d%s0x%"PRIx64"%s0x%"PRIx64"%s0x%016"PRIx64
 192                              "%s%"PRIu64"%s0x%"PRIx64"%s%s:%s\n";
 193                } else {
 194                        fmt = "%5d%s%5d%s0x%016"PRIx64"%s0x016%"PRIx64
 195                              "%s0x%016"PRIx64"%s%5"PRIu64"%s0x%06"PRIx64
 196                              "%s%s:%s\n";
 197                        symbol_conf.field_sep = " ";
 198                }
 199
 200                printf(fmt,
 201                        sample->pid,
 202                        symbol_conf.field_sep,
 203                        sample->tid,
 204                        symbol_conf.field_sep,
 205                        sample->ip,
 206                        symbol_conf.field_sep,
 207                        sample->addr,
 208                        symbol_conf.field_sep,
 209                        sample->phys_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        } else {
 218                if (symbol_conf.field_sep) {
 219                        fmt = "%d%s%d%s0x%"PRIx64"%s0x%"PRIx64"%s%"PRIu64
 220                              "%s0x%"PRIx64"%s%s:%s\n";
 221                } else {
 222                        fmt = "%5d%s%5d%s0x%016"PRIx64"%s0x016%"PRIx64
 223                              "%s%5"PRIu64"%s0x%06"PRIx64"%s%s:%s\n";
 224                        symbol_conf.field_sep = " ";
 225                }
 226
 227                printf(fmt,
 228                        sample->pid,
 229                        symbol_conf.field_sep,
 230                        sample->tid,
 231                        symbol_conf.field_sep,
 232                        sample->ip,
 233                        symbol_conf.field_sep,
 234                        sample->addr,
 235                        symbol_conf.field_sep,
 236                        sample->weight,
 237                        symbol_conf.field_sep,
 238                        sample->data_src,
 239                        symbol_conf.field_sep,
 240                        al.map ? (al.map->dso ? al.map->dso->long_name : "???") : "???",
 241                        al.sym ? al.sym->name : "???");
 242        }
 243out_put:
 244        addr_location__put(&al);
 245        return 0;
 246}
 247
 248static int process_sample_event(struct perf_tool *tool,
 249                                union perf_event *event,
 250                                struct perf_sample *sample,
 251                                struct evsel *evsel __maybe_unused,
 252                                struct machine *machine)
 253{
 254        return dump_raw_samples(tool, event, sample, machine);
 255}
 256
 257static int report_raw_events(struct perf_mem *mem)
 258{
 259        struct itrace_synth_opts itrace_synth_opts = {
 260                .set = true,
 261                .mem = true,    /* Only enable memory event */
 262                .default_no_sample = true,
 263        };
 264
 265        struct perf_data data = {
 266                .path  = input_name,
 267                .mode  = PERF_DATA_MODE_READ,
 268                .force = mem->force,
 269        };
 270        int ret;
 271        struct perf_session *session = perf_session__new(&data, false,
 272                                                         &mem->tool);
 273
 274        if (IS_ERR(session))
 275                return PTR_ERR(session);
 276
 277        session->itrace_synth_opts = &itrace_synth_opts;
 278
 279        if (mem->cpu_list) {
 280                ret = perf_session__cpu_bitmap(session, mem->cpu_list,
 281                                               mem->cpu_bitmap);
 282                if (ret < 0)
 283                        goto out_delete;
 284        }
 285
 286        ret = symbol__init(&session->header.env);
 287        if (ret < 0)
 288                goto out_delete;
 289
 290        if (mem->phys_addr)
 291                printf("# PID, TID, IP, ADDR, PHYS ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n");
 292        else
 293                printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n");
 294
 295        ret = perf_session__process_events(session);
 296
 297out_delete:
 298        perf_session__delete(session);
 299        return ret;
 300}
 301static char *get_sort_order(struct perf_mem *mem)
 302{
 303        bool has_extra_options = mem->phys_addr ? true : false;
 304        char sort[128];
 305
 306        /*
 307         * there is no weight (cost) associated with stores, so don't print
 308         * the column
 309         */
 310        if (!(mem->operation & MEM_OPERATION_LOAD)) {
 311                strcpy(sort, "--sort=mem,sym,dso,symbol_daddr,"
 312                             "dso_daddr,tlb,locked");
 313        } else if (has_extra_options) {
 314                strcpy(sort, "--sort=local_weight,mem,sym,dso,symbol_daddr,"
 315                             "dso_daddr,snoop,tlb,locked");
 316        } else
 317                return NULL;
 318
 319        if (mem->phys_addr)
 320                strcat(sort, ",phys_daddr");
 321
 322        return strdup(sort);
 323}
 324
 325static 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
 355struct 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
 365static 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
 371static int
 372parse_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;
 419error:
 420        free(os);
 421        return ret;
 422}
 423
 424int 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_END()
 468        };
 469        const char *const mem_subcommands[] = { "record", "report", NULL };
 470        const char *mem_usage[] = {
 471                NULL,
 472                NULL
 473        };
 474
 475        argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands,
 476                                        mem_usage, PARSE_OPT_KEEP_UNKNOWN);
 477
 478        if (!argc || !(strncmp(argv[0], "rec", 3) || mem.operation))
 479                usage_with_options(mem_usage, mem_options);
 480
 481        if (!mem.input_name || !strlen(mem.input_name)) {
 482                if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
 483                        mem.input_name = "-";
 484                else
 485                        mem.input_name = "perf.data";
 486        }
 487
 488        if (!strncmp(argv[0], "rec", 3))
 489                return __cmd_record(argc, argv, &mem);
 490        else if (!strncmp(argv[0], "rep", 3))
 491                return report_events(argc, argv, &mem);
 492        else
 493                usage_with_options(mem_usage, mem_options);
 494
 495        return 0;
 496}
 497