Commit 94eb1531 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf into perf/core

Pull perf/core improvements and fixes from Jiri Olsa:

  * Bitmask handling and plugin updates (Steven Rostedt)

  * Fix pipe check regression in attr event callback (Jiri Olsa)

  * Prettify the tags/TAGS/cscope targets output (Jiri Olsa)

  * Print array argument as string (Namhyung Kim)

  * Pass protection and flags bits through mmap2 interface (Peter Zijlstra)

  * Update perf tool mmap2 interface with protection and flag bits (Don Zickus)

  * Re-enable mmap interface (Don Zickus)

  * Add mem-mode documentation to report command (Don Zickus)

  * Add sort on dcacheline (Don Zickus)
Signed-off-by: default avatarJiri Olsa <jolsa@kernel.org>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 82b89778 9b32ba71
......@@ -705,6 +705,7 @@ enum perf_event_type {
* u32 min;
* u64 ino;
* u64 ino_generation;
* u32 prot, flags;
* char filename[];
* struct sample_id sample_id;
* };
......
......@@ -40,6 +40,7 @@
#include <linux/mm_types.h>
#include <linux/cgroup.h>
#include <linux/module.h>
#include <linux/mman.h>
#include "internal.h"
......@@ -5127,6 +5128,7 @@ struct perf_mmap_event {
int maj, min;
u64 ino;
u64 ino_generation;
u32 prot, flags;
struct {
struct perf_event_header header;
......@@ -5168,6 +5170,8 @@ static void perf_event_mmap_output(struct perf_event *event,
mmap_event->event_id.header.size += sizeof(mmap_event->min);
mmap_event->event_id.header.size += sizeof(mmap_event->ino);
mmap_event->event_id.header.size += sizeof(mmap_event->ino_generation);
mmap_event->event_id.header.size += sizeof(mmap_event->prot);
mmap_event->event_id.header.size += sizeof(mmap_event->flags);
}
perf_event_header__init_id(&mmap_event->event_id.header, &sample, event);
......@@ -5186,6 +5190,8 @@ static void perf_event_mmap_output(struct perf_event *event,
perf_output_put(&handle, mmap_event->min);
perf_output_put(&handle, mmap_event->ino);
perf_output_put(&handle, mmap_event->ino_generation);
perf_output_put(&handle, mmap_event->prot);
perf_output_put(&handle, mmap_event->flags);
}
__output_copy(&handle, mmap_event->file_name,
......@@ -5204,6 +5210,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
struct file *file = vma->vm_file;
int maj = 0, min = 0;
u64 ino = 0, gen = 0;
u32 prot = 0, flags = 0;
unsigned int size;
char tmp[16];
char *buf = NULL;
......@@ -5234,6 +5241,28 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
gen = inode->i_generation;
maj = MAJOR(dev);
min = MINOR(dev);
if (vma->vm_flags & VM_READ)
prot |= PROT_READ;
if (vma->vm_flags & VM_WRITE)
prot |= PROT_WRITE;
if (vma->vm_flags & VM_EXEC)
prot |= PROT_EXEC;
if (vma->vm_flags & VM_MAYSHARE)
flags = MAP_SHARED;
else
flags = MAP_PRIVATE;
if (vma->vm_flags & VM_DENYWRITE)
flags |= MAP_DENYWRITE;
if (vma->vm_flags & VM_MAYEXEC)
flags |= MAP_EXECUTABLE;
if (vma->vm_flags & VM_LOCKED)
flags |= MAP_LOCKED;
if (vma->vm_flags & VM_HUGETLB)
flags |= MAP_HUGETLB;
goto got_name;
} else {
name = (char *)arch_vma_name(vma);
......@@ -5274,6 +5303,8 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
mmap_event->min = min;
mmap_event->ino = ino;
mmap_event->ino_generation = gen;
mmap_event->prot = prot;
mmap_event->flags = flags;
if (!(vma->vm_flags & VM_EXEC))
mmap_event->event_id.header.misc |= PERF_RECORD_MISC_MMAP_DATA;
......@@ -5314,6 +5345,8 @@ void perf_event_mmap(struct vm_area_struct *vma)
/* .min (attr_mmap2 only) */
/* .ino (attr_mmap2 only) */
/* .ino_generation (attr_mmap2 only) */
/* .prot (attr_mmap2 only) */
/* .flags (attr_mmap2 only) */
};
perf_event_mmap_event(&mmap_event);
......@@ -6896,10 +6929,6 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
if (ret)
return -EFAULT;
/* disabled for now */
if (attr->mmap2)
return -EINVAL;
if (attr->__reserved_1)
return -EINVAL;
......
......@@ -765,6 +765,9 @@ static void free_arg(struct print_arg *arg)
case PRINT_BSTRING:
free(arg->string.string);
break;
case PRINT_BITMASK:
free(arg->bitmask.bitmask);
break;
case PRINT_DYNAMIC_ARRAY:
free(arg->dynarray.index);
break;
......@@ -2268,6 +2271,7 @@ static int arg_num_eval(struct print_arg *arg, long long *val)
case PRINT_FIELD ... PRINT_SYMBOL:
case PRINT_STRING:
case PRINT_BSTRING:
case PRINT_BITMASK:
default:
do_warning("invalid eval type %d", arg->type);
ret = 0;
......@@ -2296,6 +2300,7 @@ static char *arg_eval (struct print_arg *arg)
case PRINT_FIELD ... PRINT_SYMBOL:
case PRINT_STRING:
case PRINT_BSTRING:
case PRINT_BITMASK:
default:
do_warning("invalid eval type %d", arg->type);
break;
......@@ -2683,6 +2688,35 @@ process_str(struct event_format *event __maybe_unused, struct print_arg *arg,
return EVENT_ERROR;
}
static enum event_type
process_bitmask(struct event_format *event __maybe_unused, struct print_arg *arg,
char **tok)
{
enum event_type type;
char *token;
if (read_expect_type(EVENT_ITEM, &token) < 0)
goto out_free;
arg->type = PRINT_BITMASK;
arg->bitmask.bitmask = token;
arg->bitmask.offset = -1;
if (read_expected(EVENT_DELIM, ")") < 0)
goto out_err;
type = read_token(&token);
*tok = token;
return type;
out_free:
free_token(token);
out_err:
*tok = NULL;
return EVENT_ERROR;
}
static struct pevent_function_handler *
find_func_handler(struct pevent *pevent, char *func_name)
{
......@@ -2797,6 +2831,10 @@ process_function(struct event_format *event, struct print_arg *arg,
free_token(token);
return process_str(event, arg, tok);
}
if (strcmp(token, "__get_bitmask") == 0) {
free_token(token);
return process_bitmask(event, arg, tok);
}
if (strcmp(token, "__get_dynamic_array") == 0) {
free_token(token);
return process_dynamic_array(event, arg, tok);
......@@ -3324,6 +3362,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
return eval_type(val, arg, 0);
case PRINT_STRING:
case PRINT_BSTRING:
case PRINT_BITMASK:
return 0;
case PRINT_FUNC: {
struct trace_seq s;
......@@ -3556,6 +3595,60 @@ static void print_str_to_seq(struct trace_seq *s, const char *format,
trace_seq_printf(s, format, str);
}
static void print_bitmask_to_seq(struct pevent *pevent,
struct trace_seq *s, const char *format,
int len_arg, const void *data, int size)
{
int nr_bits = size * 8;
int str_size = (nr_bits + 3) / 4;
int len = 0;
char buf[3];
char *str;
int index;
int i;
/*
* The kernel likes to put in commas every 32 bits, we
* can do the same.
*/
str_size += (nr_bits - 1) / 32;
str = malloc(str_size + 1);
if (!str) {
do_warning("%s: not enough memory!", __func__);
return;
}
str[str_size] = 0;
/* Start out with -2 for the two chars per byte */
for (i = str_size - 2; i >= 0; i -= 2) {
/*
* data points to a bit mask of size bytes.
* In the kernel, this is an array of long words, thus
* endianess is very important.
*/
if (pevent->file_bigendian)
index = size - (len + 1);
else
index = len;
snprintf(buf, 3, "%02x", *((unsigned char *)data + index));
memcpy(str + i, buf, 2);
len++;
if (!(len & 3) && i > 0) {
i--;
str[i] = ',';
}
}
if (len_arg >= 0)
trace_seq_printf(s, format, len_arg, str);
else
trace_seq_printf(s, format, str);
free(str);
}
static void print_str_arg(struct trace_seq *s, void *data, int size,
struct event_format *event, const char *format,
int len_arg, struct print_arg *arg)
......@@ -3691,6 +3784,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
case PRINT_BSTRING:
print_str_to_seq(s, format, len_arg, arg->string.string);
break;
case PRINT_BITMASK: {
int bitmask_offset;
int bitmask_size;
if (arg->bitmask.offset == -1) {
struct format_field *f;
f = pevent_find_any_field(event, arg->bitmask.bitmask);
arg->bitmask.offset = f->offset;
}
bitmask_offset = data2host4(pevent, data + arg->bitmask.offset);
bitmask_size = bitmask_offset >> 16;
bitmask_offset &= 0xffff;
print_bitmask_to_seq(pevent, s, format, len_arg,
data + bitmask_offset, bitmask_size);
break;
}
case PRINT_OP:
/*
* The only op for string should be ? :
......@@ -4822,6 +4932,9 @@ static void print_args(struct print_arg *args)
case PRINT_BSTRING:
printf("__get_str(%s)", args->string.string);
break;
case PRINT_BITMASK:
printf("__get_bitmask(%s)", args->bitmask.bitmask);
break;
case PRINT_TYPE:
printf("(%s)", args->typecast.type);
print_args(args->typecast.item);
......
......@@ -107,8 +107,8 @@ typedef int (*pevent_event_handler_func)(struct trace_seq *s,
typedef int (*pevent_plugin_load_func)(struct pevent *pevent);
typedef int (*pevent_plugin_unload_func)(struct pevent *pevent);
struct plugin_option {
struct plugin_option *next;
struct pevent_plugin_option {
struct pevent_plugin_option *next;
void *handle;
char *file;
char *name;
......@@ -135,7 +135,7 @@ struct plugin_option {
* PEVENT_PLUGIN_OPTIONS: (optional)
* Plugin options that can be set before loading
*
* struct plugin_option PEVENT_PLUGIN_OPTIONS[] = {
* struct pevent_plugin_option PEVENT_PLUGIN_OPTIONS[] = {
* {
* .name = "option-name",
* .plugin_alias = "overide-file-name", (optional)
......@@ -208,6 +208,11 @@ struct print_arg_string {
int offset;
};
struct print_arg_bitmask {
char *bitmask;
int offset;
};
struct print_arg_field {
char *name;
struct format_field *field;
......@@ -274,6 +279,7 @@ enum print_arg_type {
PRINT_DYNAMIC_ARRAY,
PRINT_OP,
PRINT_FUNC,
PRINT_BITMASK,
};
struct print_arg {
......@@ -288,6 +294,7 @@ struct print_arg {
struct print_arg_hex hex;
struct print_arg_func func;
struct print_arg_string string;
struct print_arg_bitmask bitmask;
struct print_arg_op op;
struct print_arg_dynarray dynarray;
};
......@@ -354,6 +361,8 @@ enum pevent_func_arg_type {
enum pevent_flag {
PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */
PEVENT_DISABLE_SYS_PLUGINS = 1 << 1,
PEVENT_DISABLE_PLUGINS = 1 << 2,
};
#define PEVENT_ERRORS \
......@@ -410,9 +419,19 @@ enum pevent_errno {
struct plugin_list;
#define INVALID_PLUGIN_LIST_OPTION ((char **)((unsigned long)-1))
struct plugin_list *traceevent_load_plugins(struct pevent *pevent);
void traceevent_unload_plugins(struct plugin_list *plugin_list,
struct pevent *pevent);
char **traceevent_plugin_list_options(void);
void traceevent_plugin_free_options_list(char **list);
int traceevent_plugin_add_options(const char *name,
struct pevent_plugin_option *options);
void traceevent_plugin_remove_options(struct pevent_plugin_option *options);
void traceevent_print_plugins(struct trace_seq *s,
const char *prefix, const char *suffix,
const struct plugin_list *list);
struct cmdline;
struct cmdline_list;
......
......@@ -18,6 +18,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
......@@ -30,12 +31,207 @@
#define LOCAL_PLUGIN_DIR ".traceevent/plugins"
static struct registered_plugin_options {
struct registered_plugin_options *next;
struct pevent_plugin_option *options;
} *registered_options;
static struct trace_plugin_options {
struct trace_plugin_options *next;
char *plugin;
char *option;
char *value;
} *trace_plugin_options;
struct plugin_list {
struct plugin_list *next;
char *name;
void *handle;
};
/**
* traceevent_plugin_list_options - get list of plugin options
*
* Returns an array of char strings that list the currently registered
* plugin options in the format of <plugin>:<option>. This list can be
* used by toggling the option.
*
* Returns NULL if there's no options registered. On error it returns
* INVALID_PLUGIN_LIST_OPTION
*
* Must be freed with traceevent_plugin_free_options_list().
*/
char **traceevent_plugin_list_options(void)
{
struct registered_plugin_options *reg;
struct pevent_plugin_option *op;
char **list = NULL;
char *name;
int count = 0;
for (reg = registered_options; reg; reg = reg->next) {
for (op = reg->options; op->name; op++) {
char *alias = op->plugin_alias ? op->plugin_alias : op->file;
char **temp = list;
name = malloc(strlen(op->name) + strlen(alias) + 2);
if (!name)
goto err;
sprintf(name, "%s:%s", alias, op->name);
list = realloc(list, count + 2);
if (!list) {
list = temp;
free(name);
goto err;
}
list[count++] = name;
list[count] = NULL;
}
}
return list;
err:
while (--count >= 0)
free(list[count]);
free(list);
return INVALID_PLUGIN_LIST_OPTION;
}
void traceevent_plugin_free_options_list(char **list)
{
int i;
if (!list)
return;
if (list == INVALID_PLUGIN_LIST_OPTION)
return;
for (i = 0; list[i]; i++)
free(list[i]);
free(list);
}
static int
update_option(const char *file, struct pevent_plugin_option *option)
{
struct trace_plugin_options *op;
char *plugin;
if (option->plugin_alias) {
plugin = strdup(option->plugin_alias);
if (!plugin)
return -1;
} else {
char *p;
plugin = strdup(file);
if (!plugin)
return -1;
p = strstr(plugin, ".");
if (p)
*p = '\0';
}
/* first look for named options */
for (op = trace_plugin_options; op; op = op->next) {
if (!op->plugin)
continue;
if (strcmp(op->plugin, plugin) != 0)
continue;
if (strcmp(op->option, option->name) != 0)
continue;
option->value = op->value;
option->set ^= 1;
goto out;
}
/* first look for unnamed options */
for (op = trace_plugin_options; op; op = op->next) {
if (op->plugin)
continue;
if (strcmp(op->option, option->name) != 0)
continue;
option->value = op->value;
option->set ^= 1;
break;
}
out:
free(plugin);
return 0;
}
/**
* traceevent_plugin_add_options - Add a set of options by a plugin
* @name: The name of the plugin adding the options
* @options: The set of options being loaded
*
* Sets the options with the values that have been added by user.
*/
int traceevent_plugin_add_options(const char *name,
struct pevent_plugin_option *options)
{
struct registered_plugin_options *reg;
reg = malloc(sizeof(*reg));
if (!reg)
return -1;
reg->next = registered_options;
reg->options = options;
registered_options = reg;
while (options->name) {
update_option(name, options);
options++;
}
return 0;
}
/**
* traceevent_plugin_remove_options - remove plugin options that were registered
* @options: Options to removed that were registered with traceevent_plugin_add_options
*/
void traceevent_plugin_remove_options(struct pevent_plugin_option *options)
{
struct registered_plugin_options **last;
struct registered_plugin_options *reg;
for (last = &registered_options; *last; last = &(*last)->next) {
if ((*last)->options == options) {
reg = *last;
*last = reg->next;
free(reg);
return;
}
}
}
/**
* traceevent_print_plugins - print out the list of plugins loaded
* @s: the trace_seq descripter to write to
* @prefix: The prefix string to add before listing the option name
* @suffix: The suffix string ot append after the option name
* @list: The list of plugins (usually returned by traceevent_load_plugins()
*
* Writes to the trace_seq @s the list of plugins (files) that is
* returned by traceevent_load_plugins(). Use @prefix and @suffix for formating:
* @prefix = " ", @suffix = "\n".
*/
void traceevent_print_plugins(struct trace_seq *s,
const char *prefix, const char *suffix,
const struct plugin_list *list)
{
while (list) {
trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix);
list = list->next;
}
}
static void
load_plugin(struct pevent *pevent, const char *path,
const char *file, void *data)
......@@ -148,12 +344,17 @@ load_plugins(struct pevent *pevent, const char *suffix,
char *path;
char *envdir;
if (pevent->flags & PEVENT_DISABLE_PLUGINS)
return;
/*
* If a system plugin directory was defined,
* check that first.
*/
#ifdef PLUGIN_DIR
load_plugins_dir(pevent, suffix, PLUGIN_DIR, load_plugin, data);
if (!(pevent->flags & PEVENT_DISABLE_SYS_PLUGINS))
load_plugins_dir(pevent, suffix, PLUGIN_DIR,
load_plugin, data);
#endif
/*
......
......@@ -33,6 +33,29 @@ static int cpus = -1;
#define STK_BLK 10
struct pevent_plugin_option plugin_options[] =
{
{
.name = "parent",
.plugin_alias = "ftrace",
.description =
"Print parent of functions for function events",
},
{
.name = "indent",
.plugin_alias = "ftrace",
.description =
"Try to show function call indents, based on parents",
.set = 1,
},
{
.name = NULL,
}
};
static struct pevent_plugin_option *ftrace_parent = &plugin_options[0];
static struct pevent_plugin_option *ftrace_indent = &plugin_options[1];
static void add_child(struct func_stack *stack, const char *child, int pos)
{
int i;
......@@ -119,7 +142,8 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record,
parent = pevent_find_function(pevent, pfunction);
index = add_and_get_index(parent, func, record->cpu);
if (parent && ftrace_indent->set)
index = add_and_get_index(parent, func, record->cpu);
trace_seq_printf(s, "%*s", index*3, "");
......@@ -128,11 +152,13 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record,
else
trace_seq_printf(s, "0x%llx", function);
trace_seq_printf(s, " <-- ");
if (parent)
trace_seq_printf(s, "%s", parent);
else
trace_seq_printf(s, "0x%llx", pfunction);
if (ftrace_parent->set) {
trace_seq_printf(s, " <-- ");
if (parent)
trace_seq_printf(s, "%s", parent);
else
trace_seq_printf(s, "0x%llx", pfunction);
}
return 0;
}
......@@ -141,6 +167,9 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
{
pevent_register_event_handler(pevent, -1, "ftrace", "function",
function_handler, NULL);
traceevent_plugin_add_options("ftrace", plugin_options);
return 0;
}
......@@ -157,6 +186,8 @@ void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
free(fstack[i].stack);
}
traceevent_plugin_remove_options(plugin_options);
free(fstack);
fstack = NULL;
cpus = -1;
......
......@@ -117,6 +117,22 @@ OPTIONS
By default, every sort keys not specified in -F will be appended
automatically.
If --mem-mode option is used, following sort keys are also available
(incompatible with --branch-stack):
symbol_daddr, dso_daddr, locked, tlb, mem, snoop, dcacheline.
- symbol_daddr: name of data symbol being executed on at the time of sample
- dso_daddr: name of library or module containing the data being executed
on at the time of sample
- locked: whether the bus was locked at the time of sample
- tlb: type of tlb access for the data at the time of sample
- mem: type of memory access for the data at the time of sample
- snoop: type of snoop (if any) for the data at the time of sample
- dcacheline: the cacheline the data address is on at the time of sample
And default sort keys are changed to local_weight, mem, sym, dso,
symbol_daddr, dso_daddr, snoop, tlb, locked, see '--mem-mode'.
-p::
--parent=<regex>::
A regex filter to identify parent. The parent is a caller of this
......@@ -260,6 +276,13 @@ OPTIONS
Demangle symbol names to human readable form. It's enabled by default,
disable with --no-demangle.
--mem-mode::
Use the data addresses of samples in addition to instruction addresses
to build the histograms. To generate meaningful output, the perf.data
file must have been obtained using perf record -d -W and using a
special event -e cpu/mem-loads/ or -e cpu/mem-stores/. See
'perf mem' for simpler access.
--percent-limit::
Do not show entries which have an overhead under that percent.
(Default: 0).
......
......@@ -819,15 +819,15 @@ TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol
TAG_FILES= ../../include/uapi/linux/perf_event.h
TAGS:
$(RM) TAGS
$(QUIET_GEN)$(RM) TAGS; \
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs etags -a $(TAG_FILES)
tags:
$(RM) tags
$(QUIET_GEN)$(RM) tags; \
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs ctags -a $(TAG_FILES)
cscope:
$(RM) cscope*
$(QUIET_GEN)$(RM) cscope*; \
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs cscope -b $(TAG_FILES)
### Detect prefix changes
......
......@@ -72,7 +72,7 @@ static int perf_event__repipe_attr(struct perf_tool *tool,
if (ret)
return ret;
if (&inject->output.is_pipe)
if (!inject->output.is_pipe)
return 0;
return perf_event__repipe_synth(tool, event);
......
......@@ -458,6 +458,7 @@ int main(int argc, const char **argv)
/* The page_size is placed in util object. */
page_size = sysconf(_SC_PAGE_SIZE);
cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
cmd = perf_extract_argv0_path(argv[0]);
if (!cmd)
......
......@@ -15,7 +15,7 @@ static int mmap_handler(struct perf_tool *tool __maybe_unused,
struct perf_sample *sample __maybe_unused,
struct machine *machine)
{
return machine__process_mmap_event(machine, event, NULL);
return machine__process_mmap2_event(machine, event, NULL);
}
static int init_live_machine(struct machine *machine)
......
#include <linux/types.h>
#include <sys/mman.h>
#include "event.h"
#include "debug.h"
#include "hist.h"
......@@ -178,13 +179,14 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
return -1;
}
event->header.type = PERF_RECORD_MMAP;
event->header.type = PERF_RECORD_MMAP2;
while (1) {
char bf[BUFSIZ];
char prot[5];
char execname[PATH_MAX];
char anonstr[] = "//anon";
unsigned int ino;
size_t size;
ssize_t n;
......@@ -195,15 +197,20 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
strcpy(execname, "");
/* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n",
&event->mmap.start, &event->mmap.len, prot,
&event->mmap.pgoff,
execname);
n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n",
&event->mmap2.start, &event->mmap2.len, prot,
&event->mmap2.pgoff, &event->mmap2.maj,
&event->mmap2.min,
&ino, execname);
/*
* Anon maps don't have the execname.
*/
if (n < 4)
if (n < 7)
continue;
event->mmap2.ino = (u64)ino;
/*
* Just like the kernel, see __perf_event_mmap in kernel/perf_event.c
*/
......@@ -212,6 +219,21 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
else
event->header.misc = PERF_RECORD_MISC_GUEST_USER;
/* map protection and flags bits */
event->mmap2.prot = 0;
event->mmap2.flags = 0;
if (prot[0] == 'r')
event->mmap2.prot |= PROT_READ;
if (prot[1] == 'w')
event->mmap2.prot |= PROT_WRITE;
if (prot[2] == 'x')
event->mmap2.prot |= PROT_EXEC;
if (prot[3] == 's')
event->mmap2.flags |= MAP_SHARED;
else
event->mmap2.flags |= MAP_PRIVATE;
if (prot[2] != 'x') {
if (!mmap_data || prot[0] != 'r')
continue;
......@@ -223,15 +245,15 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
strcpy(execname, anonstr);
size = strlen(execname) + 1;
memcpy(event->mmap.filename, execname, size);
memcpy(event->mmap2.filename, execname, size);
size = PERF_ALIGN(size, sizeof(u64));
event->mmap.len -= event->mmap.start;
event->mmap.header.size = (sizeof(event->mmap) -
(sizeof(event->mmap.filename) - size));
memset(event->mmap.filename + size, 0, machine->id_hdr_size);
event->mmap.header.size += machine->id_hdr_size;
event->mmap.pid = tgid;
event->mmap.tid = pid;
event->mmap2.len -= event->mmap.start;
event->mmap2.header.size = (sizeof(event->mmap2) -
(sizeof(event->mmap2.filename) - size));
memset(event->mmap2.filename + size, 0, machine->id_hdr_size);
event->mmap2.header.size += machine->id_hdr_size;
event->mmap2.pid = tgid;
event->mmap2.tid = pid;
if (process(tool, event, &synth_sample, machine) != 0) {
rc = -1;
......@@ -612,12 +634,15 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)
{
return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64
" %02x:%02x %"PRIu64" %"PRIu64"]: %c %s\n",
" %02x:%02x %"PRIu64" %"PRIu64"]: %c%c%c%c %s\n",
event->mmap2.pid, event->mmap2.tid, event->mmap2.start,
event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj,
event->mmap2.min, event->mmap2.ino,
event->mmap2.ino_generation,
(event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x',
(event->mmap2.prot & PROT_READ) ? 'r' : '-',
(event->mmap2.prot & PROT_WRITE) ? 'w' : '-',
(event->mmap2.prot & PROT_EXEC) ? 'x' : '-',
(event->mmap2.flags & MAP_SHARED) ? 's' : 'p',
event->mmap2.filename);
}
......
......@@ -27,6 +27,8 @@ struct mmap2_event {
u32 min;
u64 ino;
u64 ino_generation;
u32 prot;
u32 flags;
char filename[PATH_MAX];
};
......
......@@ -659,6 +659,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
perf_evsel__set_sample_bit(evsel, WEIGHT);
attr->mmap = track;
attr->mmap2 = track && !perf_missing_features.mmap2;
attr->comm = track;
if (opts->sample_transaction)
......
......@@ -128,6 +128,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
+ unresolved_col_width + 2;
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
symlen);
hists__new_col_len(hists, HISTC_MEM_DCACHELINE,
symlen + 1);
} else {
symlen = unresolved_col_width + 4 + 2;
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
......@@ -439,9 +441,10 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
.map = al->map,
.sym = al->sym,
},
.cpu = al->cpu,
.ip = al->addr,
.level = al->level,
.cpu = al->cpu,
.cpumode = al->cpumode,
.ip = al->addr,
.level = al->level,
.stat = {
.nr_events = 1,
.period = period,
......
......@@ -72,6 +72,7 @@ enum hist_column {
HISTC_MEM_TLB,
HISTC_MEM_LVL,
HISTC_MEM_SNOOP,
HISTC_MEM_DCACHELINE,
HISTC_TRANSACTION,
HISTC_NR_COLS, /* Last entry */
};
......
......@@ -1060,6 +1060,8 @@ int machine__process_mmap2_event(struct machine *machine,
event->mmap2.pid, event->mmap2.maj,
event->mmap2.min, event->mmap2.ino,
event->mmap2.ino_generation,
event->mmap2.prot,
event->mmap2.flags,
event->mmap2.filename, type);
if (map == NULL)
......@@ -1105,7 +1107,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
map = map__new(&machine->user_dsos, event->mmap.start,
event->mmap.len, event->mmap.pgoff,
event->mmap.pid, 0, 0, 0, 0,
event->mmap.pid, 0, 0, 0, 0, 0, 0,
event->mmap.filename,
type);
......
......@@ -138,7 +138,7 @@ void map__init(struct map *map, enum map_type type,
struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
u64 ino_gen, char *filename,
u64 ino_gen, u32 prot, u32 flags, char *filename,
enum map_type type)
{
struct map *map = malloc(sizeof(*map));
......@@ -157,6 +157,8 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
map->min = d_min;
map->ino = ino;
map->ino_generation = ino_gen;
map->prot = prot;
map->flags = flags;
if ((anon || no_dso) && type == MAP__FUNCTION) {
snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
......
......@@ -35,6 +35,8 @@ struct map {
bool referenced;
bool erange_warned;
u32 priv;
u32 prot;
u32 flags;
u64 pgoff;
u64 reloc;
u32 maj, min; /* only valid for MMAP2 record */
......@@ -118,7 +120,7 @@ void map__init(struct map *map, enum map_type type,
u64 start, u64 end, u64 pgoff, struct dso *dso);
struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
u64 ino_gen,
u64 ino_gen, u32 prot, u32 flags,
char *filename, enum map_type type);
struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
void map__delete(struct map *map);
......
......@@ -215,6 +215,7 @@ static void define_event_symbols(struct event_format *event,
case PRINT_BSTRING:
case PRINT_DYNAMIC_ARRAY:
case PRINT_STRING:
case PRINT_BITMASK:
break;
case PRINT_TYPE:
define_event_symbols(event, ev_name, args->typecast.item);
......
......@@ -197,6 +197,7 @@ static void define_event_symbols(struct event_format *event,
case PRINT_BSTRING:
case PRINT_DYNAMIC_ARRAY:
case PRINT_FUNC:
case PRINT_BITMASK:
/* we should warn... */
return;
}
......@@ -622,6 +623,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile)
fprintf(ofp, "%s=", f->name);
if (f->flags & FIELD_IS_STRING ||
f->flags & FIELD_IS_FLAG ||
f->flags & FIELD_IS_ARRAY ||
f->flags & FIELD_IS_SYMBOLIC)
fprintf(ofp, "%%s");
else if (f->flags & FIELD_IS_SIGNED)
......
#include <sys/mman.h>
#include "sort.h"
#include "hist.h"
#include "comm.h"
......@@ -784,6 +785,104 @@ static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
return repsep_snprintf(bf, size, "%-*s", width, out);
}
static inline u64 cl_address(u64 address)
{
/* return the cacheline of the address */
return (address & ~(cacheline_size - 1));
}
static int64_t
sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
{
u64 l, r;
struct map *l_map, *r_map;
if (!left->mem_info) return -1;
if (!right->mem_info) return 1;
/* group event types together */
if (left->cpumode > right->cpumode) return -1;
if (left->cpumode < right->cpumode) return 1;
l_map = left->mem_info->daddr.map;
r_map = right->mem_info->daddr.map;
/* if both are NULL, jump to sort on al_addr instead */
if (!l_map && !r_map)
goto addr;
if (!l_map) return -1;
if (!r_map) return 1;
if (l_map->maj > r_map->maj) return -1;
if (l_map->maj < r_map->maj) return 1;
if (l_map->min > r_map->min) return -1;
if (l_map->min < r_map->min) return 1;
if (l_map->ino > r_map->ino) return -1;
if (l_map->ino < r_map->ino) return 1;
if (l_map->ino_generation > r_map->ino_generation) return -1;
if (l_map->ino_generation < r_map->ino_generation) return 1;
/*
* Addresses with no major/minor numbers are assumed to be
* anonymous in userspace. Sort those on pid then address.
*
* The kernel and non-zero major/minor mapped areas are
* assumed to be unity mapped. Sort those on address.
*/
if ((left->cpumode != PERF_RECORD_MISC_KERNEL) &&
(!(l_map->flags & MAP_SHARED)) &&
!l_map->maj && !l_map->min && !l_map->ino &&
!l_map->ino_generation) {
/* userspace anonymous */
if (left->thread->pid_ > right->thread->pid_) return -1;
if (left->thread->pid_ < right->thread->pid_) return 1;
}
addr:
/* al_addr does all the right addr - start + offset calculations */
l = cl_address(left->mem_info->daddr.al_addr);
r = cl_address(right->mem_info->daddr.al_addr);
if (l > r) return -1;
if (l < r) return 1;
return 0;
}
static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
uint64_t addr = 0;
struct map *map = NULL;
struct symbol *sym = NULL;
char level = he->level;
if (he->mem_info) {
addr = cl_address(he->mem_info->daddr.al_addr);
map = he->mem_info->daddr.map;
sym = he->mem_info->daddr.sym;
/* print [s] for shared data mmaps */
if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
map && (map->type == MAP__VARIABLE) &&
(map->flags & MAP_SHARED) &&
(map->maj || map->min || map->ino ||
map->ino_generation))
level = 's';
else if (!map)
level = 'X';
}
return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size,
width);
}
struct sort_entry sort_mispredict = {
.se_header = "Branch Mispredicted",
.se_cmp = sort__mispredict_cmp,
......@@ -876,6 +975,13 @@ struct sort_entry sort_mem_snoop = {
.se_width_idx = HISTC_MEM_SNOOP,
};
struct sort_entry sort_mem_dcacheline = {
.se_header = "Data Cacheline",
.se_cmp = sort__dcacheline_cmp,
.se_snprintf = hist_entry__dcacheline_snprintf,
.se_width_idx = HISTC_MEM_DCACHELINE,
};
static int64_t
sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
{
......@@ -1043,6 +1149,7 @@ static struct sort_dimension memory_sort_dimensions[] = {
DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
};
#undef DIM
......
......@@ -89,6 +89,7 @@ struct hist_entry {
u64 ip;
u64 transaction;
s32 cpu;
u8 cpumode;
struct hist_entry_diff diff;
......@@ -185,6 +186,7 @@ enum sort_type {
SORT_MEM_TLB,
SORT_MEM_LVL,
SORT_MEM_SNOOP,
SORT_MEM_DCACHELINE,
};
/*
......
......@@ -17,6 +17,7 @@
* XXX We need to find a better place for these things...
*/
unsigned int page_size;
int cacheline_size;
bool test_attr__enabled;
......
......@@ -304,6 +304,7 @@ char *rtrim(char *s);
void dump_stack(void);
extern unsigned int page_size;
extern int cacheline_size;
void get_term_dimensions(struct winsize *ws);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment