mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
perf kmem: Print gfp flags in human readable string
Save libtraceevent output and print it in the header. # perf kmem stat --page --caller # # GFP flags # --------- # 00000010: NI: GFP_NOIO # 000000d0: K: GFP_KERNEL # 00000200: NWR: GFP_NOWARN # 000084d0: K|R|Z: GFP_KERNEL|GFP_REPEAT|GFP_ZERO # 000200d2: HU: GFP_HIGHUSER # 000200da: HUM: GFP_HIGHUSER_MOVABLE # 000280da: HUM|Z: GFP_HIGHUSER_MOVABLE|GFP_ZERO # 002084d0: K|R|Z|NT: GFP_KERNEL|GFP_REPEAT|GFP_ZERO|GFP_NOTRACK # 0102005a: NF|HW|M: GFP_NOFS|GFP_HARDWALL|GFP_MOVABLE --------------------------------------------------------------------------------------------------------- Total alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite --------------------------------------------------------------------------------------------------------- 60 | 15 | 0 | UNMOVABL | K|R|Z|NT | pte_alloc_one 40 | 10 | 0 | MOVABLE | HUM|Z | handle_mm_fault 24 | 6 | 0 | MOVABLE | HUM | do_wp_page 24 | 6 | 0 | UNMOVABL | K | __pollwait ... Requested-by: Joonsoo Kim <js1304@gmail.com> Suggested-by: Minchan Kim <minchan@kernel.org> Signed-off-by: Namhyung Kim <namhyung@kernel.org> Acked-by: Pekka Enberg <penberg@kernel.org> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: David Ahern <dsahern@gmail.com> Cc: Joonsoo Kim <js1304@gmail.com> Cc: Minchan Kim <minchan@kernel.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: linux-mm@kvack.org Link: http://lkml.kernel.org/r/1429592107-1807-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
2a7ef02c9c
commit
0e11115644
1 changed files with 209 additions and 13 deletions
|
@ -581,6 +581,176 @@ static bool valid_page(u64 pfn_or_page)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct gfp_flag {
|
||||||
|
unsigned int flags;
|
||||||
|
char *compact_str;
|
||||||
|
char *human_readable;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct gfp_flag *gfps;
|
||||||
|
static int nr_gfps;
|
||||||
|
|
||||||
|
static int gfpcmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct gfp_flag *fa = a;
|
||||||
|
const struct gfp_flag *fb = b;
|
||||||
|
|
||||||
|
return fa->flags - fb->flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* see include/trace/events/gfpflags.h */
|
||||||
|
static const struct {
|
||||||
|
const char *original;
|
||||||
|
const char *compact;
|
||||||
|
} gfp_compact_table[] = {
|
||||||
|
{ "GFP_TRANSHUGE", "THP" },
|
||||||
|
{ "GFP_HIGHUSER_MOVABLE", "HUM" },
|
||||||
|
{ "GFP_HIGHUSER", "HU" },
|
||||||
|
{ "GFP_USER", "U" },
|
||||||
|
{ "GFP_TEMPORARY", "TMP" },
|
||||||
|
{ "GFP_KERNEL", "K" },
|
||||||
|
{ "GFP_NOFS", "NF" },
|
||||||
|
{ "GFP_ATOMIC", "A" },
|
||||||
|
{ "GFP_NOIO", "NI" },
|
||||||
|
{ "GFP_HIGH", "H" },
|
||||||
|
{ "GFP_WAIT", "W" },
|
||||||
|
{ "GFP_IO", "I" },
|
||||||
|
{ "GFP_COLD", "CO" },
|
||||||
|
{ "GFP_NOWARN", "NWR" },
|
||||||
|
{ "GFP_REPEAT", "R" },
|
||||||
|
{ "GFP_NOFAIL", "NF" },
|
||||||
|
{ "GFP_NORETRY", "NR" },
|
||||||
|
{ "GFP_COMP", "C" },
|
||||||
|
{ "GFP_ZERO", "Z" },
|
||||||
|
{ "GFP_NOMEMALLOC", "NMA" },
|
||||||
|
{ "GFP_MEMALLOC", "MA" },
|
||||||
|
{ "GFP_HARDWALL", "HW" },
|
||||||
|
{ "GFP_THISNODE", "TN" },
|
||||||
|
{ "GFP_RECLAIMABLE", "RC" },
|
||||||
|
{ "GFP_MOVABLE", "M" },
|
||||||
|
{ "GFP_NOTRACK", "NT" },
|
||||||
|
{ "GFP_NO_KSWAPD", "NK" },
|
||||||
|
{ "GFP_OTHER_NODE", "ON" },
|
||||||
|
{ "GFP_NOWAIT", "NW" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t max_gfp_len;
|
||||||
|
|
||||||
|
static char *compact_gfp_flags(char *gfp_flags)
|
||||||
|
{
|
||||||
|
char *orig_flags = strdup(gfp_flags);
|
||||||
|
char *new_flags = NULL;
|
||||||
|
char *str, *pos;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
if (orig_flags == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
str = strtok_r(orig_flags, "|", &pos);
|
||||||
|
while (str) {
|
||||||
|
size_t i;
|
||||||
|
char *new;
|
||||||
|
const char *cpt;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(gfp_compact_table); i++) {
|
||||||
|
if (strcmp(gfp_compact_table[i].original, str))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cpt = gfp_compact_table[i].compact;
|
||||||
|
new = realloc(new_flags, len + strlen(cpt) + 2);
|
||||||
|
if (new == NULL) {
|
||||||
|
free(new_flags);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_flags = new;
|
||||||
|
|
||||||
|
if (!len) {
|
||||||
|
strcpy(new_flags, cpt);
|
||||||
|
} else {
|
||||||
|
strcat(new_flags, "|");
|
||||||
|
strcat(new_flags, cpt);
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += strlen(cpt);
|
||||||
|
}
|
||||||
|
|
||||||
|
str = strtok_r(NULL, "|", &pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_gfp_len < len)
|
||||||
|
max_gfp_len = len;
|
||||||
|
|
||||||
|
free(orig_flags);
|
||||||
|
return new_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *compact_gfp_string(unsigned long gfp_flags)
|
||||||
|
{
|
||||||
|
struct gfp_flag key = {
|
||||||
|
.flags = gfp_flags,
|
||||||
|
};
|
||||||
|
struct gfp_flag *gfp;
|
||||||
|
|
||||||
|
gfp = bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp);
|
||||||
|
if (gfp)
|
||||||
|
return gfp->compact_str;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_gfp_flags(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||||
|
unsigned int gfp_flags)
|
||||||
|
{
|
||||||
|
struct pevent_record record = {
|
||||||
|
.cpu = sample->cpu,
|
||||||
|
.data = sample->raw_data,
|
||||||
|
.size = sample->raw_size,
|
||||||
|
};
|
||||||
|
struct trace_seq seq;
|
||||||
|
char *str, *pos;
|
||||||
|
|
||||||
|
if (nr_gfps) {
|
||||||
|
struct gfp_flag key = {
|
||||||
|
.flags = gfp_flags,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_seq_init(&seq);
|
||||||
|
pevent_event_info(&seq, evsel->tp_format, &record);
|
||||||
|
|
||||||
|
str = strtok_r(seq.buffer, " ", &pos);
|
||||||
|
while (str) {
|
||||||
|
if (!strncmp(str, "gfp_flags=", 10)) {
|
||||||
|
struct gfp_flag *new;
|
||||||
|
|
||||||
|
new = realloc(gfps, (nr_gfps + 1) * sizeof(*gfps));
|
||||||
|
if (new == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
gfps = new;
|
||||||
|
new += nr_gfps++;
|
||||||
|
|
||||||
|
new->flags = gfp_flags;
|
||||||
|
new->human_readable = strdup(str + 10);
|
||||||
|
new->compact_str = compact_gfp_flags(str + 10);
|
||||||
|
if (!new->human_readable || !new->compact_str)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
qsort(gfps, nr_gfps, sizeof(*gfps), gfpcmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
str = strtok_r(NULL, " ", &pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_seq_destroy(&seq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
|
static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
|
||||||
struct perf_sample *sample)
|
struct perf_sample *sample)
|
||||||
{
|
{
|
||||||
|
@ -613,6 +783,9 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parse_gfp_flags(evsel, sample, gfp_flags) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
callsite = find_callsite(evsel, sample);
|
callsite = find_callsite(evsel, sample);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -832,16 +1005,18 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)
|
||||||
struct rb_node *next = rb_first(&page_alloc_sorted);
|
struct rb_node *next = rb_first(&page_alloc_sorted);
|
||||||
struct machine *machine = &session->machines.host;
|
struct machine *machine = &session->machines.host;
|
||||||
const char *format;
|
const char *format;
|
||||||
|
int gfp_len = max(strlen("GFP flags"), max_gfp_len);
|
||||||
|
|
||||||
printf("\n%.105s\n", graph_dotted_line);
|
printf("\n%.105s\n", graph_dotted_line);
|
||||||
printf(" %-16s | %5s alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n",
|
printf(" %-16s | %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n",
|
||||||
use_pfn ? "PFN" : "Page", live_page ? "Live" : "Total");
|
use_pfn ? "PFN" : "Page", live_page ? "Live" : "Total",
|
||||||
|
gfp_len, "GFP flags");
|
||||||
printf("%.105s\n", graph_dotted_line);
|
printf("%.105s\n", graph_dotted_line);
|
||||||
|
|
||||||
if (use_pfn)
|
if (use_pfn)
|
||||||
format = " %16llu | %'16llu | %'9d | %5d | %8s | %08lx | %s\n";
|
format = " %16llu | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
|
||||||
else
|
else
|
||||||
format = " %016llx | %'16llu | %'9d | %5d | %8s | %08lx | %s\n";
|
format = " %016llx | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
|
||||||
|
|
||||||
while (next && n_lines--) {
|
while (next && n_lines--) {
|
||||||
struct page_stat *data;
|
struct page_stat *data;
|
||||||
|
@ -862,13 +1037,15 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)
|
||||||
(unsigned long long)data->alloc_bytes / 1024,
|
(unsigned long long)data->alloc_bytes / 1024,
|
||||||
data->nr_alloc, data->order,
|
data->nr_alloc, data->order,
|
||||||
migrate_type_str[data->migrate_type],
|
migrate_type_str[data->migrate_type],
|
||||||
(unsigned long)data->gfp_flags, caller);
|
gfp_len, compact_gfp_string(data->gfp_flags), caller);
|
||||||
|
|
||||||
next = rb_next(next);
|
next = rb_next(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n_lines == -1)
|
if (n_lines == -1) {
|
||||||
printf(" ... | ... | ... | ... | ... | ... | ...\n");
|
printf(" ... | ... | ... | ... | ... | %-*s | ...\n",
|
||||||
|
gfp_len, "...");
|
||||||
|
}
|
||||||
|
|
||||||
printf("%.105s\n", graph_dotted_line);
|
printf("%.105s\n", graph_dotted_line);
|
||||||
}
|
}
|
||||||
|
@ -877,10 +1054,11 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines
|
||||||
{
|
{
|
||||||
struct rb_node *next = rb_first(&page_caller_sorted);
|
struct rb_node *next = rb_first(&page_caller_sorted);
|
||||||
struct machine *machine = &session->machines.host;
|
struct machine *machine = &session->machines.host;
|
||||||
|
int gfp_len = max(strlen("GFP flags"), max_gfp_len);
|
||||||
|
|
||||||
printf("\n%.105s\n", graph_dotted_line);
|
printf("\n%.105s\n", graph_dotted_line);
|
||||||
printf(" %5s alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n",
|
printf(" %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n",
|
||||||
live_page ? "Live" : "Total");
|
live_page ? "Live" : "Total", gfp_len, "GFP flags");
|
||||||
printf("%.105s\n", graph_dotted_line);
|
printf("%.105s\n", graph_dotted_line);
|
||||||
|
|
||||||
while (next && n_lines--) {
|
while (next && n_lines--) {
|
||||||
|
@ -898,21 +1076,37 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines
|
||||||
else
|
else
|
||||||
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
|
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
|
||||||
|
|
||||||
printf(" %'16llu | %'9d | %5d | %8s | %08lx | %s\n",
|
printf(" %'16llu | %'9d | %5d | %8s | %-*s | %s\n",
|
||||||
(unsigned long long)data->alloc_bytes / 1024,
|
(unsigned long long)data->alloc_bytes / 1024,
|
||||||
data->nr_alloc, data->order,
|
data->nr_alloc, data->order,
|
||||||
migrate_type_str[data->migrate_type],
|
migrate_type_str[data->migrate_type],
|
||||||
(unsigned long)data->gfp_flags, caller);
|
gfp_len, compact_gfp_string(data->gfp_flags), caller);
|
||||||
|
|
||||||
next = rb_next(next);
|
next = rb_next(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n_lines == -1)
|
if (n_lines == -1) {
|
||||||
printf(" ... | ... | ... | ... | ... | ...\n");
|
printf(" ... | ... | ... | ... | %-*s | ...\n",
|
||||||
|
gfp_len, "...");
|
||||||
|
}
|
||||||
|
|
||||||
printf("%.105s\n", graph_dotted_line);
|
printf("%.105s\n", graph_dotted_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void print_gfp_flags(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printf("#\n");
|
||||||
|
printf("# GFP flags\n");
|
||||||
|
printf("# ---------\n");
|
||||||
|
for (i = 0; i < nr_gfps; i++) {
|
||||||
|
printf("# %08x: %*s: %s\n", gfps[i].flags,
|
||||||
|
(int) max_gfp_len, gfps[i].compact_str,
|
||||||
|
gfps[i].human_readable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void print_slab_summary(void)
|
static void print_slab_summary(void)
|
||||||
{
|
{
|
||||||
printf("\nSUMMARY (SLAB allocator)");
|
printf("\nSUMMARY (SLAB allocator)");
|
||||||
|
@ -982,6 +1176,8 @@ static void print_slab_result(struct perf_session *session)
|
||||||
|
|
||||||
static void print_page_result(struct perf_session *session)
|
static void print_page_result(struct perf_session *session)
|
||||||
{
|
{
|
||||||
|
if (caller_flag || alloc_flag)
|
||||||
|
print_gfp_flags();
|
||||||
if (caller_flag)
|
if (caller_flag)
|
||||||
__print_page_caller_result(session, caller_lines);
|
__print_page_caller_result(session, caller_lines);
|
||||||
if (alloc_flag)
|
if (alloc_flag)
|
||||||
|
|
Loading…
Reference in a new issue