perf mem: Enable sampling loads and stores simultaneously

This patch modifies perf mem to default to sampling loads and stores
simultaneously. It could only do one or the other before yet there was
no hardware restriction preventing simultaneous collection. With this
patch, one run is sufficient to collect both.

It is still possible to sample only loads or stores by using the
-t option:
 $ perf mem -t load rec
 $ perf mem -t load rep
Or
 $ perf mem -t store rec
 $ perf mem -t store rep

The perf report TUI will show one event at a time. The store output will
contain a Weight column which will be empty.

In V2, we updated the man pages to reflect the change and also simplify
the initialization of the argv vector passed to the cmd_*() functions as
per LKML feedback.

In V3, we fixed typos in the changelog.

Signed-off-by: Stephane Eranian <eranian@google.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Don Zickus <dzickus@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Joe Mario <jmario@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Richard Fowles <rfowles@redhat.com>
Link: http://lkml.kernel.org/r/20141217152355.GA10053@thinkpad
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Stephane Eranian 2014-12-17 16:23:55 +01:00 committed by Arnaldo Carvalho de Melo
parent 25dd9171f5
commit 67121f85e4
2 changed files with 105 additions and 27 deletions

View File

@ -12,11 +12,12 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
----------- -----------
"perf mem -t <TYPE> record" runs a command and gathers memory operation data "perf mem record" runs a command and gathers memory operation data
from it, into perf.data. Perf record options are accepted and are passed through. from it, into perf.data. Perf record options are accepted and are passed through.
"perf mem -t <TYPE> report" displays the result. It invokes perf report with the "perf mem report" displays the result. It invokes perf report with the
right set of options to display a memory access profile. right set of options to display a memory access profile. By default, loads
and stores are sampled. Use the -t option to limit to loads or stores.
Note that on Intel systems the memory latency reported is the use-latency, Note that on Intel systems the memory latency reported is the use-latency,
not the pure load (or store latency). Use latency includes any pipeline not the pure load (or store latency). Use latency includes any pipeline
@ -29,7 +30,7 @@ OPTIONS
-t:: -t::
--type=:: --type=::
Select the memory operation type: load or store (default: load) Select the memory operation type: load or store (default: load,store)
-D:: -D::
--dump-raw-samples=:: --dump-raw-samples=::

View File

@ -7,10 +7,13 @@
#include "util/session.h" #include "util/session.h"
#include "util/data.h" #include "util/data.h"
#define MEM_OPERATION_LOAD "load" #define MEM_OPERATION_LOAD 0x1
#define MEM_OPERATION_STORE "store" #define MEM_OPERATION_STORE 0x2
static const char *mem_operation = MEM_OPERATION_LOAD; /*
* default to both load an store sampling
*/
static int mem_operation = MEM_OPERATION_LOAD | MEM_OPERATION_STORE;
struct perf_mem { struct perf_mem {
struct perf_tool tool; struct perf_tool tool;
@ -25,26 +28,30 @@ static int __cmd_record(int argc, const char **argv)
{ {
int rec_argc, i = 0, j; int rec_argc, i = 0, j;
const char **rec_argv; const char **rec_argv;
char event[64];
int ret; int ret;
rec_argc = argc + 4; rec_argc = argc + 7; /* max number of arguments */
rec_argv = calloc(rec_argc + 1, sizeof(char *)); rec_argv = calloc(rec_argc + 1, sizeof(char *));
if (!rec_argv) if (!rec_argv)
return -1; return -1;
rec_argv[i++] = strdup("record"); rec_argv[i++] = "record";
if (!strcmp(mem_operation, MEM_OPERATION_LOAD))
rec_argv[i++] = strdup("-W");
rec_argv[i++] = strdup("-d");
rec_argv[i++] = strdup("-e");
if (strcmp(mem_operation, MEM_OPERATION_LOAD)) if (mem_operation & MEM_OPERATION_LOAD)
sprintf(event, "cpu/mem-stores/pp"); rec_argv[i++] = "-W";
else
sprintf(event, "cpu/mem-loads/pp"); rec_argv[i++] = "-d";
if (mem_operation & MEM_OPERATION_LOAD) {
rec_argv[i++] = "-e";
rec_argv[i++] = "cpu/mem-loads/pp";
}
if (mem_operation & MEM_OPERATION_STORE) {
rec_argv[i++] = "-e";
rec_argv[i++] = "cpu/mem-stores/pp";
}
rec_argv[i++] = strdup(event);
for (j = 1; j < argc; j++, i++) for (j = 1; j < argc; j++, i++)
rec_argv[i] = argv[j]; rec_argv[i] = argv[j];
@ -162,17 +169,17 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem)
if (!rep_argv) if (!rep_argv)
return -1; return -1;
rep_argv[i++] = strdup("report"); rep_argv[i++] = "report";
rep_argv[i++] = strdup("--mem-mode"); rep_argv[i++] = "--mem-mode";
rep_argv[i++] = strdup("-n"); /* display number of samples */ rep_argv[i++] = "-n"; /* display number of samples */
/* /*
* there is no weight (cost) associated with stores, so don't print * there is no weight (cost) associated with stores, so don't print
* the column * the column
*/ */
if (strcmp(mem_operation, MEM_OPERATION_LOAD)) if (!(mem_operation & MEM_OPERATION_LOAD))
rep_argv[i++] = strdup("--sort=mem,sym,dso,symbol_daddr," rep_argv[i++] = "--sort=mem,sym,dso,symbol_daddr,"
"dso_daddr,tlb,locked"); "dso_daddr,tlb,locked";
for (j = 1; j < argc; j++, i++) for (j = 1; j < argc; j++, i++)
rep_argv[i] = argv[j]; rep_argv[i] = argv[j];
@ -182,6 +189,75 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem)
return ret; return ret;
} }
struct mem_mode {
const char *name;
int mode;
};
#define MEM_OPT(n, m) \
{ .name = n, .mode = (m) }
#define MEM_END { .name = NULL }
static const struct mem_mode mem_modes[]={
MEM_OPT("load", MEM_OPERATION_LOAD),
MEM_OPT("store", MEM_OPERATION_STORE),
MEM_END
};
static int
parse_mem_ops(const struct option *opt, const char *str, int unset)
{
int *mode = (int *)opt->value;
const struct mem_mode *m;
char *s, *os = NULL, *p;
int ret = -1;
if (unset)
return 0;
/* str may be NULL in case no arg is passed to -t */
if (str) {
/* because str is read-only */
s = os = strdup(str);
if (!s)
return -1;
/* reset mode */
*mode = 0;
for (;;) {
p = strchr(s, ',');
if (p)
*p = '\0';
for (m = mem_modes; m->name; m++) {
if (!strcasecmp(s, m->name))
break;
}
if (!m->name) {
fprintf(stderr, "unknown sampling op %s,"
" check man page\n", s);
goto error;
}
*mode |= m->mode;
if (!p)
break;
s = p + 1;
}
}
ret = 0;
if (*mode == 0)
*mode = MEM_OPERATION_LOAD;
error:
free(os);
return ret;
}
int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
{ {
struct stat st; struct stat st;
@ -199,8 +275,9 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
.input_name = "perf.data", .input_name = "perf.data",
}; };
const struct option mem_options[] = { const struct option mem_options[] = {
OPT_STRING('t', "type", &mem_operation, OPT_CALLBACK('t', "type", &mem_operation,
"type", "memory operations(load/store)"), "type", "memory operations(load,store) Default load,store",
parse_mem_ops),
OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw, OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw,
"dump raw samples in ASCII"), "dump raw samples in ASCII"),
OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved, OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved,