perf/core improvements and fixes:
. 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: Jiri Olsa <jolsa@kernel.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTlxAaAAoJEPZqUSBWB3s93+4P/1ZRHu7vjlsm4vjUQzjAsNh7 PxEyT0WVXq9Hh77jgtFk/b8TQ7bTwoYJkX3DR69TFmb13qTn7fvxiSdsixjY2Ju+ CGw11BKh/AleSDP279R7LxcJY5UD5Uzmo5dTOxLX+vejyDIN3mitM8L2UKd8MMKe XD1qSb+DXLHCI/0iRdgIRt8k0wMMTv3id/JHwwsh10PEr+0Vk2qIekj7YyJAh1rR o3qBaohYdDugWGLUzO/jMJu63+KQCngiHnMFaDcMOc34DM2JqrN2iEb2MnplPbVG zJIsGhHIHAkJ/3N8n3G6JC+uzOBtGt5als5foR1celsueUMMMUtuP3ze5oTKLJiA d2LDcMIoSy1n9OuaJWhkJEwurAY2rfJrUMuQnDf3i8qlo3ParirxX6BH1P5HVeEJ pPtGGCkpNf487wTTeM7Uv+ruaxS5IQnNYzLwUY4m3qgJlcfCom2lykmH/eXZzt23 7vuGh9nKrmYg3IwsISa9E29ZG5WeFTcrBT2y4GqDDNauvSrvAQyHslLKT0VqWNdM GMIjGnueqe0u8lCjV6XMSmJzAVyiCG4rSEz3JiH7k4wwYLmucp3xAF9a0zCbEeup sxNl61oLBAkW0WOaPtOpipba+wDGB7wjO1ITmNac98+J7Gia2tfFhsaSrebrUbwQ CnJIGjvjmaGxp/qrRH/3 =7Osi -----END PGP SIGNATURE----- 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: Jiri Olsa <jolsa@kernel.org> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
94eb153130
@ -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 = ®istered_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)
|
||||
|
@ -1,4 +1,5 @@
|
||||
#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)
|
||||
|
@ -1,3 +1,4 @@
|
||||
#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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user