Commit cf230918 authored by Ingo Molnar's avatar Ingo Molnar

Merge branch 'perf/core' into perf/urgent, to pick up the latest fixes

Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 4cdf77a8 4ba96195
...@@ -705,6 +705,7 @@ enum perf_event_type { ...@@ -705,6 +705,7 @@ enum perf_event_type {
* u32 min; * u32 min;
* u64 ino; * u64 ino;
* u64 ino_generation; * u64 ino_generation;
* u32 prot, flags;
* char filename[]; * char filename[];
* struct sample_id sample_id; * struct sample_id sample_id;
* }; * };
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <linux/mm_types.h> #include <linux/mm_types.h>
#include <linux/cgroup.h> #include <linux/cgroup.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mman.h>
#include "internal.h" #include "internal.h"
...@@ -5128,6 +5129,7 @@ struct perf_mmap_event { ...@@ -5128,6 +5129,7 @@ struct perf_mmap_event {
int maj, min; int maj, min;
u64 ino; u64 ino;
u64 ino_generation; u64 ino_generation;
u32 prot, flags;
struct { struct {
struct perf_event_header header; struct perf_event_header header;
...@@ -5169,6 +5171,8 @@ static void perf_event_mmap_output(struct perf_event *event, ...@@ -5169,6 +5171,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->min);
mmap_event->event_id.header.size += sizeof(mmap_event->ino); 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->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); perf_event_header__init_id(&mmap_event->event_id.header, &sample, event);
...@@ -5187,6 +5191,8 @@ static void perf_event_mmap_output(struct perf_event *event, ...@@ -5187,6 +5191,8 @@ static void perf_event_mmap_output(struct perf_event *event,
perf_output_put(&handle, mmap_event->min); perf_output_put(&handle, mmap_event->min);
perf_output_put(&handle, mmap_event->ino); perf_output_put(&handle, mmap_event->ino);
perf_output_put(&handle, mmap_event->ino_generation); 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, __output_copy(&handle, mmap_event->file_name,
...@@ -5205,6 +5211,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) ...@@ -5205,6 +5211,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
struct file *file = vma->vm_file; struct file *file = vma->vm_file;
int maj = 0, min = 0; int maj = 0, min = 0;
u64 ino = 0, gen = 0; u64 ino = 0, gen = 0;
u32 prot = 0, flags = 0;
unsigned int size; unsigned int size;
char tmp[16]; char tmp[16];
char *buf = NULL; char *buf = NULL;
...@@ -5235,6 +5242,28 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) ...@@ -5235,6 +5242,28 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
gen = inode->i_generation; gen = inode->i_generation;
maj = MAJOR(dev); maj = MAJOR(dev);
min = MINOR(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; goto got_name;
} else { } else {
name = (char *)arch_vma_name(vma); name = (char *)arch_vma_name(vma);
...@@ -5275,6 +5304,8 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) ...@@ -5275,6 +5304,8 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
mmap_event->min = min; mmap_event->min = min;
mmap_event->ino = ino; mmap_event->ino = ino;
mmap_event->ino_generation = gen; mmap_event->ino_generation = gen;
mmap_event->prot = prot;
mmap_event->flags = flags;
if (!(vma->vm_flags & VM_EXEC)) if (!(vma->vm_flags & VM_EXEC))
mmap_event->event_id.header.misc |= PERF_RECORD_MISC_MMAP_DATA; mmap_event->event_id.header.misc |= PERF_RECORD_MISC_MMAP_DATA;
...@@ -5315,6 +5346,8 @@ void perf_event_mmap(struct vm_area_struct *vma) ...@@ -5315,6 +5346,8 @@ void perf_event_mmap(struct vm_area_struct *vma)
/* .min (attr_mmap2 only) */ /* .min (attr_mmap2 only) */
/* .ino (attr_mmap2 only) */ /* .ino (attr_mmap2 only) */
/* .ino_generation (attr_mmap2 only) */ /* .ino_generation (attr_mmap2 only) */
/* .prot (attr_mmap2 only) */
/* .flags (attr_mmap2 only) */
}; };
perf_event_mmap_event(&mmap_event); perf_event_mmap_event(&mmap_event);
...@@ -6897,10 +6930,6 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, ...@@ -6897,10 +6930,6 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
if (ret) if (ret)
return -EFAULT; return -EFAULT;
/* disabled for now */
if (attr->mmap2)
return -EINVAL;
if (attr->__reserved_1) if (attr->__reserved_1)
return -EINVAL; return -EINVAL;
......
...@@ -765,6 +765,9 @@ static void free_arg(struct print_arg *arg) ...@@ -765,6 +765,9 @@ static void free_arg(struct print_arg *arg)
case PRINT_BSTRING: case PRINT_BSTRING:
free(arg->string.string); free(arg->string.string);
break; break;
case PRINT_BITMASK:
free(arg->bitmask.bitmask);
break;
case PRINT_DYNAMIC_ARRAY: case PRINT_DYNAMIC_ARRAY:
free(arg->dynarray.index); free(arg->dynarray.index);
break; break;
...@@ -2268,6 +2271,7 @@ static int arg_num_eval(struct print_arg *arg, long long *val) ...@@ -2268,6 +2271,7 @@ static int arg_num_eval(struct print_arg *arg, long long *val)
case PRINT_FIELD ... PRINT_SYMBOL: case PRINT_FIELD ... PRINT_SYMBOL:
case PRINT_STRING: case PRINT_STRING:
case PRINT_BSTRING: case PRINT_BSTRING:
case PRINT_BITMASK:
default: default:
do_warning("invalid eval type %d", arg->type); do_warning("invalid eval type %d", arg->type);
ret = 0; ret = 0;
...@@ -2296,6 +2300,7 @@ static char *arg_eval (struct print_arg *arg) ...@@ -2296,6 +2300,7 @@ static char *arg_eval (struct print_arg *arg)
case PRINT_FIELD ... PRINT_SYMBOL: case PRINT_FIELD ... PRINT_SYMBOL:
case PRINT_STRING: case PRINT_STRING:
case PRINT_BSTRING: case PRINT_BSTRING:
case PRINT_BITMASK:
default: default:
do_warning("invalid eval type %d", arg->type); do_warning("invalid eval type %d", arg->type);
break; break;
...@@ -2683,6 +2688,35 @@ process_str(struct event_format *event __maybe_unused, struct print_arg *arg, ...@@ -2683,6 +2688,35 @@ process_str(struct event_format *event __maybe_unused, struct print_arg *arg,
return EVENT_ERROR; 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 * static struct pevent_function_handler *
find_func_handler(struct pevent *pevent, char *func_name) find_func_handler(struct pevent *pevent, char *func_name)
{ {
...@@ -2797,6 +2831,10 @@ process_function(struct event_format *event, struct print_arg *arg, ...@@ -2797,6 +2831,10 @@ process_function(struct event_format *event, struct print_arg *arg,
free_token(token); free_token(token);
return process_str(event, arg, tok); 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) { if (strcmp(token, "__get_dynamic_array") == 0) {
free_token(token); free_token(token);
return process_dynamic_array(event, arg, tok); 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 ...@@ -3324,6 +3362,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
return eval_type(val, arg, 0); return eval_type(val, arg, 0);
case PRINT_STRING: case PRINT_STRING:
case PRINT_BSTRING: case PRINT_BSTRING:
case PRINT_BITMASK:
return 0; return 0;
case PRINT_FUNC: { case PRINT_FUNC: {
struct trace_seq s; struct trace_seq s;
...@@ -3556,6 +3595,60 @@ static void print_str_to_seq(struct trace_seq *s, const char *format, ...@@ -3556,6 +3595,60 @@ static void print_str_to_seq(struct trace_seq *s, const char *format,
trace_seq_printf(s, format, str); 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, static void print_str_arg(struct trace_seq *s, void *data, int size,
struct event_format *event, const char *format, struct event_format *event, const char *format,
int len_arg, struct print_arg *arg) int len_arg, struct print_arg *arg)
...@@ -3691,6 +3784,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, ...@@ -3691,6 +3784,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
case PRINT_BSTRING: case PRINT_BSTRING:
print_str_to_seq(s, format, len_arg, arg->string.string); print_str_to_seq(s, format, len_arg, arg->string.string);
break; 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: case PRINT_OP:
/* /*
* The only op for string should be ? : * The only op for string should be ? :
...@@ -4822,6 +4932,9 @@ static void print_args(struct print_arg *args) ...@@ -4822,6 +4932,9 @@ static void print_args(struct print_arg *args)
case PRINT_BSTRING: case PRINT_BSTRING:
printf("__get_str(%s)", args->string.string); printf("__get_str(%s)", args->string.string);
break; break;
case PRINT_BITMASK:
printf("__get_bitmask(%s)", args->bitmask.bitmask);
break;
case PRINT_TYPE: case PRINT_TYPE:
printf("(%s)", args->typecast.type); printf("(%s)", args->typecast.type);
print_args(args->typecast.item); print_args(args->typecast.item);
......
...@@ -107,8 +107,8 @@ typedef int (*pevent_event_handler_func)(struct trace_seq *s, ...@@ -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_load_func)(struct pevent *pevent);
typedef int (*pevent_plugin_unload_func)(struct pevent *pevent); typedef int (*pevent_plugin_unload_func)(struct pevent *pevent);
struct plugin_option { struct pevent_plugin_option {
struct plugin_option *next; struct pevent_plugin_option *next;
void *handle; void *handle;
char *file; char *file;
char *name; char *name;
...@@ -135,7 +135,7 @@ struct plugin_option { ...@@ -135,7 +135,7 @@ struct plugin_option {
* PEVENT_PLUGIN_OPTIONS: (optional) * PEVENT_PLUGIN_OPTIONS: (optional)
* Plugin options that can be set before loading * Plugin options that can be set before loading
* *
* struct plugin_option PEVENT_PLUGIN_OPTIONS[] = { * struct pevent_plugin_option PEVENT_PLUGIN_OPTIONS[] = {
* { * {
* .name = "option-name", * .name = "option-name",
* .plugin_alias = "overide-file-name", (optional) * .plugin_alias = "overide-file-name", (optional)
...@@ -208,6 +208,11 @@ struct print_arg_string { ...@@ -208,6 +208,11 @@ struct print_arg_string {
int offset; int offset;
}; };
struct print_arg_bitmask {
char *bitmask;
int offset;
};
struct print_arg_field { struct print_arg_field {
char *name; char *name;
struct format_field *field; struct format_field *field;
...@@ -274,6 +279,7 @@ enum print_arg_type { ...@@ -274,6 +279,7 @@ enum print_arg_type {
PRINT_DYNAMIC_ARRAY, PRINT_DYNAMIC_ARRAY,
PRINT_OP, PRINT_OP,
PRINT_FUNC, PRINT_FUNC,
PRINT_BITMASK,
}; };
struct print_arg { struct print_arg {
...@@ -288,6 +294,7 @@ struct print_arg { ...@@ -288,6 +294,7 @@ struct print_arg {
struct print_arg_hex hex; struct print_arg_hex hex;
struct print_arg_func func; struct print_arg_func func;
struct print_arg_string string; struct print_arg_string string;
struct print_arg_bitmask bitmask;
struct print_arg_op op; struct print_arg_op op;
struct print_arg_dynarray dynarray; struct print_arg_dynarray dynarray;
}; };
...@@ -354,6 +361,8 @@ enum pevent_func_arg_type { ...@@ -354,6 +361,8 @@ enum pevent_func_arg_type {
enum pevent_flag { enum pevent_flag {
PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */
PEVENT_DISABLE_SYS_PLUGINS = 1 << 1,
PEVENT_DISABLE_PLUGINS = 1 << 2,
}; };
#define PEVENT_ERRORS \ #define PEVENT_ERRORS \
...@@ -410,9 +419,19 @@ enum pevent_errno { ...@@ -410,9 +419,19 @@ enum pevent_errno {
struct plugin_list; struct plugin_list;
#define INVALID_PLUGIN_LIST_OPTION ((char **)((unsigned long)-1))
struct plugin_list *traceevent_load_plugins(struct pevent *pevent); struct plugin_list *traceevent_load_plugins(struct pevent *pevent);
void traceevent_unload_plugins(struct plugin_list *plugin_list, void traceevent_unload_plugins(struct plugin_list *plugin_list,
struct pevent *pevent); 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;
struct cmdline_list; struct cmdline_list;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/ */
#include <stdio.h>
#include <string.h> #include <string.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -30,12 +31,207 @@ ...@@ -30,12 +31,207 @@
#define LOCAL_PLUGIN_DIR ".traceevent/plugins" #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 {
struct plugin_list *next; struct plugin_list *next;
char *name; char *name;
void *handle; 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 static void
load_plugin(struct pevent *pevent, const char *path, load_plugin(struct pevent *pevent, const char *path,
const char *file, void *data) const char *file, void *data)
...@@ -148,12 +344,17 @@ load_plugins(struct pevent *pevent, const char *suffix, ...@@ -148,12 +344,17 @@ load_plugins(struct pevent *pevent, const char *suffix,
char *path; char *path;
char *envdir; char *envdir;
if (pevent->flags & PEVENT_DISABLE_PLUGINS)
return;
/* /*
* If a system plugin directory was defined, * If a system plugin directory was defined,
* check that first. * check that first.
*/ */
#ifdef PLUGIN_DIR #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 #endif
/* /*
......
...@@ -33,6 +33,29 @@ static int cpus = -1; ...@@ -33,6 +33,29 @@ static int cpus = -1;
#define STK_BLK 10 #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) static void add_child(struct func_stack *stack, const char *child, int pos)
{ {
int i; int i;
...@@ -119,7 +142,8 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record, ...@@ -119,7 +142,8 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record,
parent = pevent_find_function(pevent, pfunction); 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, ""); trace_seq_printf(s, "%*s", index*3, "");
...@@ -128,11 +152,13 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record, ...@@ -128,11 +152,13 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record,
else else
trace_seq_printf(s, "0x%llx", function); trace_seq_printf(s, "0x%llx", function);
trace_seq_printf(s, " <-- "); if (ftrace_parent->set) {
if (parent) trace_seq_printf(s, " <-- ");
trace_seq_printf(s, "%s", parent); if (parent)
else trace_seq_printf(s, "%s", parent);
trace_seq_printf(s, "0x%llx", pfunction); else
trace_seq_printf(s, "0x%llx", pfunction);
}
return 0; return 0;
} }
...@@ -141,6 +167,9 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) ...@@ -141,6 +167,9 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
{ {
pevent_register_event_handler(pevent, -1, "ftrace", "function", pevent_register_event_handler(pevent, -1, "ftrace", "function",
function_handler, NULL); function_handler, NULL);
traceevent_plugin_add_options("ftrace", plugin_options);
return 0; return 0;
} }
...@@ -157,6 +186,8 @@ void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) ...@@ -157,6 +186,8 @@ void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
free(fstack[i].stack); free(fstack[i].stack);
} }
traceevent_plugin_remove_options(plugin_options);
free(fstack); free(fstack);
fstack = NULL; fstack = NULL;
cpus = -1; cpus = -1;
......
...@@ -117,6 +117,22 @@ OPTIONS ...@@ -117,6 +117,22 @@ OPTIONS
By default, every sort keys not specified in -F will be appended By default, every sort keys not specified in -F will be appended
automatically. 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:: -p::
--parent=<regex>:: --parent=<regex>::
A regex filter to identify parent. The parent is a caller of this A regex filter to identify parent. The parent is a caller of this
...@@ -260,6 +276,13 @@ OPTIONS ...@@ -260,6 +276,13 @@ OPTIONS
Demangle symbol names to human readable form. It's enabled by default, Demangle symbol names to human readable form. It's enabled by default,
disable with --no-demangle. 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:: --percent-limit::
Do not show entries which have an overhead under that percent. Do not show entries which have an overhead under that percent.
(Default: 0). (Default: 0).
......
...@@ -43,27 +43,6 @@ TIMECHART OPTIONS ...@@ -43,27 +43,6 @@ TIMECHART OPTIONS
--symfs=<directory>:: --symfs=<directory>::
Look for files with symbols relative to this directory. Look for files with symbols relative to this directory.
EXAMPLES
--------
$ perf timechart record git pull
[ perf record: Woken up 13 times to write data ]
[ perf record: Captured and wrote 4.253 MB perf.data (~185801 samples) ]
$ perf timechart
Written 10.2 seconds of trace to output.svg.
Record system-wide timechart:
$ perf timechart record
then generate timechart and highlight 'gcc' tasks:
$ perf timechart --highlight gcc
-n:: -n::
--proc-num:: --proc-num::
Print task info for at least given number of tasks. Print task info for at least given number of tasks.
...@@ -88,6 +67,26 @@ RECORD OPTIONS ...@@ -88,6 +67,26 @@ RECORD OPTIONS
--callchain:: --callchain::
Do call-graph (stack chain/backtrace) recording Do call-graph (stack chain/backtrace) recording
EXAMPLES
--------
$ perf timechart record git pull
[ perf record: Woken up 13 times to write data ]
[ perf record: Captured and wrote 4.253 MB perf.data (~185801 samples) ]
$ perf timechart
Written 10.2 seconds of trace to output.svg.
Record system-wide timechart:
$ perf timechart record
then generate timechart and highlight 'gcc' tasks:
$ perf timechart --highlight gcc
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-record[1] linkperf:perf-record[1]
...@@ -819,15 +819,15 @@ TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol ...@@ -819,15 +819,15 @@ TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol
TAG_FILES= ../../include/uapi/linux/perf_event.h TAG_FILES= ../../include/uapi/linux/perf_event.h
TAGS: TAGS:
$(RM) TAGS $(QUIET_GEN)$(RM) TAGS; \
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs etags -a $(TAG_FILES) $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs etags -a $(TAG_FILES)
tags: tags:
$(RM) tags $(QUIET_GEN)$(RM) tags; \
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs ctags -a $(TAG_FILES) $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs ctags -a $(TAG_FILES)
cscope: cscope:
$(RM) cscope* $(QUIET_GEN)$(RM) cscope*; \
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs cscope -b $(TAG_FILES) $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs cscope -b $(TAG_FILES)
### Detect prefix changes ### Detect prefix changes
......
...@@ -72,7 +72,7 @@ static int perf_event__repipe_attr(struct perf_tool *tool, ...@@ -72,7 +72,7 @@ static int perf_event__repipe_attr(struct perf_tool *tool,
if (ret) if (ret)
return ret; return ret;
if (&inject->output.is_pipe) if (!inject->output.is_pipe)
return 0; return 0;
return perf_event__repipe_synth(tool, event); return perf_event__repipe_synth(tool, event);
......
...@@ -288,6 +288,13 @@ static void cleanup_params(void) ...@@ -288,6 +288,13 @@ static void cleanup_params(void)
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
} }
static void pr_err_with_code(const char *msg, int err)
{
pr_err("%s", msg);
pr_debug(" Reason: %s (Code: %d)", strerror(-err), err);
pr_err("\n");
}
static int static int
__cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
{ {
...@@ -379,7 +386,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -379,7 +386,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
} }
ret = parse_probe_event_argv(argc, argv); ret = parse_probe_event_argv(argc, argv);
if (ret < 0) { if (ret < 0) {
pr_err(" Error: Parse Error. (%d)\n", ret); pr_err_with_code(" Error: Command Parse Error.", ret);
return ret; return ret;
} }
} }
...@@ -419,8 +426,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -419,8 +426,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
} }
ret = show_perf_probe_events(); ret = show_perf_probe_events();
if (ret < 0) if (ret < 0)
pr_err(" Error: Failed to show event list. (%d)\n", pr_err_with_code(" Error: Failed to show event list.", ret);
ret);
return ret; return ret;
} }
if (params.show_funcs) { if (params.show_funcs) {
...@@ -445,8 +451,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -445,8 +451,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
strfilter__delete(params.filter); strfilter__delete(params.filter);
params.filter = NULL; params.filter = NULL;
if (ret < 0) if (ret < 0)
pr_err(" Error: Failed to show functions." pr_err_with_code(" Error: Failed to show functions.", ret);
" (%d)\n", ret);
return ret; return ret;
} }
...@@ -464,7 +469,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -464,7 +469,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
ret = show_line_range(&params.line_range, params.target); ret = show_line_range(&params.line_range, params.target);
if (ret < 0) if (ret < 0)
pr_err(" Error: Failed to show lines. (%d)\n", ret); pr_err_with_code(" Error: Failed to show lines.", ret);
return ret; return ret;
} }
if (params.show_vars) { if (params.show_vars) {
...@@ -485,7 +490,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -485,7 +490,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
strfilter__delete(params.filter); strfilter__delete(params.filter);
params.filter = NULL; params.filter = NULL;
if (ret < 0) if (ret < 0)
pr_err(" Error: Failed to show vars. (%d)\n", ret); pr_err_with_code(" Error: Failed to show vars.", ret);
return ret; return ret;
} }
#endif #endif
...@@ -493,7 +498,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -493,7 +498,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
if (params.dellist) { if (params.dellist) {
ret = del_perf_probe_events(params.dellist); ret = del_perf_probe_events(params.dellist);
if (ret < 0) { if (ret < 0) {
pr_err(" Error: Failed to delete events. (%d)\n", ret); pr_err_with_code(" Error: Failed to delete events.", ret);
return ret; return ret;
} }
} }
...@@ -504,7 +509,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -504,7 +509,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
params.target, params.target,
params.force_add); params.force_add);
if (ret < 0) { if (ret < 0) {
pr_err(" Error: Failed to add events. (%d)\n", ret); pr_err_with_code(" Error: Failed to add events.", ret);
return ret; return ret;
} }
} }
......
...@@ -299,7 +299,11 @@ else ...@@ -299,7 +299,11 @@ else
NO_LIBUNWIND := 1 NO_LIBUNWIND := 1
NO_LIBDW_DWARF_UNWIND := 1 NO_LIBDW_DWARF_UNWIND := 1
else else
msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); ifneq ($(filter s% -static%,$(LDFLAGS),),)
msg := $(error No static glibc found, please install glibc-static);
else
msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]);
endif
endif endif
else else
ifndef NO_LIBDW_DWARF_UNWIND ifndef NO_LIBDW_DWARF_UNWIND
......
...@@ -458,6 +458,7 @@ int main(int argc, const char **argv) ...@@ -458,6 +458,7 @@ int main(int argc, const char **argv)
/* The page_size is placed in util object. */ /* The page_size is placed in util object. */
page_size = sysconf(_SC_PAGE_SIZE); page_size = sysconf(_SC_PAGE_SIZE);
cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
cmd = perf_extract_argv0_path(argv[0]); cmd = perf_extract_argv0_path(argv[0]);
if (!cmd) if (!cmd)
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
* *
* Builtin regression testing command: ever growing number of sanity tests * Builtin regression testing command: ever growing number of sanity tests
*/ */
#include <unistd.h>
#include <string.h>
#include "builtin.h" #include "builtin.h"
#include "intlist.h" #include "intlist.h"
#include "tests.h" #include "tests.h"
...@@ -50,9 +52,17 @@ static struct test { ...@@ -50,9 +52,17 @@ static struct test {
.func = test__pmu, .func = test__pmu,
}, },
{ {
.desc = "Test dso data interface", .desc = "Test dso data read",
.func = test__dso_data, .func = test__dso_data,
}, },
{
.desc = "Test dso data cache",
.func = test__dso_data_cache,
},
{
.desc = "Test dso data reopen",
.func = test__dso_data_reopen,
},
{ {
.desc = "roundtrip evsel->name check", .desc = "roundtrip evsel->name check",
.func = test__perf_evsel__roundtrip_name_test, .func = test__perf_evsel__roundtrip_name_test,
...@@ -172,6 +182,34 @@ static bool perf_test__matches(int curr, int argc, const char *argv[]) ...@@ -172,6 +182,34 @@ static bool perf_test__matches(int curr, int argc, const char *argv[])
return false; return false;
} }
static int run_test(struct test *test)
{
int status, err = -1, child = fork();
if (child < 0) {
pr_err("failed to fork test: %s\n", strerror(errno));
return -1;
}
if (!child) {
pr_debug("test child forked, pid %d\n", getpid());
err = test->func();
exit(err);
}
wait(&status);
if (WIFEXITED(status)) {
err = WEXITSTATUS(status);
pr_debug("test child finished with %d\n", err);
} else if (WIFSIGNALED(status)) {
err = -1;
pr_debug("test child interrupted\n");
}
return err;
}
static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
{ {
int i = 0; int i = 0;
...@@ -200,7 +238,7 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) ...@@ -200,7 +238,7 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
} }
pr_debug("\n--- start ---\n"); pr_debug("\n--- start ---\n");
err = tests[curr].func(); err = run_test(&tests[curr]);
pr_debug("---- end ----\n%s:", tests[curr].desc); pr_debug("---- end ----\n%s:", tests[curr].desc);
switch (err) { switch (err) {
......
#include "util.h"
#include <stdlib.h> #include <stdlib.h>
#include <linux/types.h> #include <linux/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <api/fs/fs.h>
#include "util.h"
#include "machine.h" #include "machine.h"
#include "symbol.h" #include "symbol.h"
#include "tests.h" #include "tests.h"
static char *test_file(int size) static char *test_file(int size)
{ {
static char buf_templ[] = "/tmp/test-XXXXXX"; #define TEMPL "/tmp/perf-test-XXXXXX"
static char buf_templ[sizeof(TEMPL)];
char *templ = buf_templ; char *templ = buf_templ;
int fd, i; int fd, i;
unsigned char *buf; unsigned char *buf;
strcpy(buf_templ, TEMPL);
#undef TEMPL
fd = mkstemp(templ); fd = mkstemp(templ);
if (fd < 0) { if (fd < 0) {
perror("mkstemp failed"); perror("mkstemp failed");
...@@ -150,3 +155,204 @@ int test__dso_data(void) ...@@ -150,3 +155,204 @@ int test__dso_data(void)
unlink(file); unlink(file);
return 0; return 0;
} }
static long open_files_cnt(void)
{
char path[PATH_MAX];
struct dirent *dent;
DIR *dir;
long nr = 0;
scnprintf(path, PATH_MAX, "%s/self/fd", procfs__mountpoint());
pr_debug("fd path: %s\n", path);
dir = opendir(path);
TEST_ASSERT_VAL("failed to open fd directory", dir);
while ((dent = readdir(dir)) != NULL) {
if (!strcmp(dent->d_name, ".") ||
!strcmp(dent->d_name, ".."))
continue;
nr++;
}
closedir(dir);
return nr - 1;
}
static struct dso **dsos;
static int dsos__create(int cnt, int size)
{
int i;
dsos = malloc(sizeof(dsos) * cnt);
TEST_ASSERT_VAL("failed to alloc dsos array", dsos);
for (i = 0; i < cnt; i++) {
char *file;
file = test_file(size);
TEST_ASSERT_VAL("failed to get dso file", file);
dsos[i] = dso__new(file);
TEST_ASSERT_VAL("failed to get dso", dsos[i]);
}
return 0;
}
static void dsos__delete(int cnt)
{
int i;
for (i = 0; i < cnt; i++) {
struct dso *dso = dsos[i];
unlink(dso->name);
dso__delete(dso);
}
free(dsos);
}
static int set_fd_limit(int n)
{
struct rlimit rlim;
if (getrlimit(RLIMIT_NOFILE, &rlim))
return -1;
pr_debug("file limit %ld, new %d\n", (long) rlim.rlim_cur, n);
rlim.rlim_cur = n;
return setrlimit(RLIMIT_NOFILE, &rlim);
}
int test__dso_data_cache(void)
{
struct machine machine;
long nr_end, nr = open_files_cnt();
int dso_cnt, limit, i, fd;
memset(&machine, 0, sizeof(machine));
/* set as system limit */
limit = nr * 4;
TEST_ASSERT_VAL("failed to set file limit", !set_fd_limit(limit));
/* and this is now our dso open FDs limit + 1 extra */
dso_cnt = limit / 2 + 1;
TEST_ASSERT_VAL("failed to create dsos\n",
!dsos__create(dso_cnt, TEST_FILE_SIZE));
for (i = 0; i < (dso_cnt - 1); i++) {
struct dso *dso = dsos[i];
/*
* Open dsos via dso__data_fd or dso__data_read_offset.
* Both opens the data file and keep it open.
*/
if (i % 2) {
fd = dso__data_fd(dso, &machine);
TEST_ASSERT_VAL("failed to get fd", fd > 0);
} else {
#define BUFSIZE 10
u8 buf[BUFSIZE];
ssize_t n;
n = dso__data_read_offset(dso, &machine, 0, buf, BUFSIZE);
TEST_ASSERT_VAL("failed to read dso", n == BUFSIZE);
}
}
/* open +1 dso over the allowed limit */
fd = dso__data_fd(dsos[i], &machine);
TEST_ASSERT_VAL("failed to get fd", fd > 0);
/* should force the first one to be closed */
TEST_ASSERT_VAL("failed to close dsos[0]", dsos[0]->data.fd == -1);
/* cleanup everything */
dsos__delete(dso_cnt);
/* Make sure we did not leak any file descriptor. */
nr_end = open_files_cnt();
pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end);
TEST_ASSERT_VAL("failed leadking files", nr == nr_end);
return 0;
}
int test__dso_data_reopen(void)
{
struct machine machine;
long nr_end, nr = open_files_cnt();
int fd, fd_extra;
#define dso_0 (dsos[0])
#define dso_1 (dsos[1])
#define dso_2 (dsos[2])
memset(&machine, 0, sizeof(machine));
/*
* Test scenario:
* - create 3 dso objects
* - set process file descriptor limit to current
* files count + 3
* - test that the first dso gets closed when we
* reach the files count limit
*/
/* Make sure we are able to open 3 fds anyway */
TEST_ASSERT_VAL("failed to set file limit",
!set_fd_limit((nr + 3)));
TEST_ASSERT_VAL("failed to create dsos\n", !dsos__create(3, TEST_FILE_SIZE));
/* open dso_0 */
fd = dso__data_fd(dso_0, &machine);
TEST_ASSERT_VAL("failed to get fd", fd > 0);
/* open dso_1 */
fd = dso__data_fd(dso_1, &machine);
TEST_ASSERT_VAL("failed to get fd", fd > 0);
/*
* open extra file descriptor and we just
* reached the files count limit
*/
fd_extra = open("/dev/null", O_RDONLY);
TEST_ASSERT_VAL("failed to open extra fd", fd_extra > 0);
/* open dso_2 */
fd = dso__data_fd(dso_2, &machine);
TEST_ASSERT_VAL("failed to get fd", fd > 0);
/*
* dso_0 should get closed, because we reached
* the file descriptor limit
*/
TEST_ASSERT_VAL("failed to close dso_0", dso_0->data.fd == -1);
/* open dso_0 */
fd = dso__data_fd(dso_0, &machine);
TEST_ASSERT_VAL("failed to get fd", fd > 0);
/*
* dso_1 should get closed, because we reached
* the file descriptor limit
*/
TEST_ASSERT_VAL("failed to close dso_1", dso_1->data.fd == -1);
/* cleanup everything */
close(fd_extra);
dsos__delete(3);
/* Make sure we did not leak any file descriptor. */
nr_end = open_files_cnt();
pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end);
TEST_ASSERT_VAL("failed leadking files", nr == nr_end);
return 0;
}
...@@ -15,7 +15,7 @@ static int mmap_handler(struct perf_tool *tool __maybe_unused, ...@@ -15,7 +15,7 @@ static int mmap_handler(struct perf_tool *tool __maybe_unused,
struct perf_sample *sample __maybe_unused, struct perf_sample *sample __maybe_unused,
struct machine *machine) 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) static int init_live_machine(struct machine *machine)
......
...@@ -205,8 +205,7 @@ $(run): ...@@ -205,8 +205,7 @@ $(run):
( eval $$cmd ) >> $@ 2>&1; \ ( eval $$cmd ) >> $@ 2>&1; \
echo " test: $(call test,$@)" >> $@ 2>&1; \ echo " test: $(call test,$@)" >> $@ 2>&1; \
$(call test,$@) && \ $(call test,$@) && \
rm -f $@ \ rm -rf $@ $$TMP_DEST || (cat $@ ; false)
rm -rf $$TMP_DEST
$(run_O): $(run_O):
$(call clean) $(call clean)
...@@ -217,9 +216,7 @@ $(run_O): ...@@ -217,9 +216,7 @@ $(run_O):
( eval $$cmd ) >> $@ 2>&1 && \ ( eval $$cmd ) >> $@ 2>&1 && \
echo " test: $(call test_O,$@)" >> $@ 2>&1; \ echo " test: $(call test_O,$@)" >> $@ 2>&1; \
$(call test_O,$@) && \ $(call test_O,$@) && \
rm -f $@ && \ rm -rf $@ $$TMP_O $$TMP_DEST || (cat $@ ; false)
rm -rf $$TMP_O \
rm -rf $$TMP_DEST
tarpkg: tarpkg:
@cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \ @cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \
......
...@@ -28,6 +28,8 @@ int test__syscall_open_tp_fields(void); ...@@ -28,6 +28,8 @@ int test__syscall_open_tp_fields(void);
int test__pmu(void); int test__pmu(void);
int test__attr(void); int test__attr(void);
int test__dso_data(void); int test__dso_data(void);
int test__dso_data_cache(void);
int test__dso_data_reopen(void);
int test__parse_events(void); int test__parse_events(void);
int test__hists_link(void); int test__hists_link(void);
int test__python_use(void); int test__python_use(void);
......
#include <asm/bug.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "symbol.h" #include "symbol.h"
#include "dso.h" #include "dso.h"
#include "machine.h" #include "machine.h"
...@@ -136,7 +139,48 @@ int dso__read_binary_type_filename(const struct dso *dso, ...@@ -136,7 +139,48 @@ int dso__read_binary_type_filename(const struct dso *dso,
return ret; return ret;
} }
static int open_dso(struct dso *dso, struct machine *machine) /*
* Global list of open DSOs and the counter.
*/
static LIST_HEAD(dso__data_open);
static long dso__data_open_cnt;
static void dso__list_add(struct dso *dso)
{
list_add_tail(&dso->data.open_entry, &dso__data_open);
dso__data_open_cnt++;
}
static void dso__list_del(struct dso *dso)
{
list_del(&dso->data.open_entry);
WARN_ONCE(dso__data_open_cnt <= 0,
"DSO data fd counter out of bounds.");
dso__data_open_cnt--;
}
static void close_first_dso(void);
static int do_open(char *name)
{
int fd;
do {
fd = open(name, O_RDONLY);
if (fd >= 0)
return fd;
pr_debug("dso open failed, mmap: %s\n", strerror(errno));
if (!dso__data_open_cnt || errno != EMFILE)
break;
close_first_dso();
} while (1);
return -1;
}
static int __open_dso(struct dso *dso, struct machine *machine)
{ {
int fd; int fd;
char *root_dir = (char *)""; char *root_dir = (char *)"";
...@@ -154,11 +198,130 @@ static int open_dso(struct dso *dso, struct machine *machine) ...@@ -154,11 +198,130 @@ static int open_dso(struct dso *dso, struct machine *machine)
return -EINVAL; return -EINVAL;
} }
fd = open(name, O_RDONLY); fd = do_open(name);
free(name); free(name);
return fd; return fd;
} }
static void check_data_close(void);
/**
* dso_close - Open DSO data file
* @dso: dso object
*
* Open @dso's data file descriptor and updates
* list/count of open DSO objects.
*/
static int open_dso(struct dso *dso, struct machine *machine)
{
int fd = __open_dso(dso, machine);
if (fd > 0) {
dso__list_add(dso);
/*
* Check if we crossed the allowed number
* of opened DSOs and close one if needed.
*/
check_data_close();
}
return fd;
}
static void close_data_fd(struct dso *dso)
{
if (dso->data.fd >= 0) {
close(dso->data.fd);
dso->data.fd = -1;
dso->data.file_size = 0;
dso__list_del(dso);
}
}
/**
* dso_close - Close DSO data file
* @dso: dso object
*
* Close @dso's data file descriptor and updates
* list/count of open DSO objects.
*/
static void close_dso(struct dso *dso)
{
close_data_fd(dso);
}
static void close_first_dso(void)
{
struct dso *dso;
dso = list_first_entry(&dso__data_open, struct dso, data.open_entry);
close_dso(dso);
}
static rlim_t get_fd_limit(void)
{
struct rlimit l;
rlim_t limit = 0;
/* Allow half of the current open fd limit. */
if (getrlimit(RLIMIT_NOFILE, &l) == 0) {
if (l.rlim_cur == RLIM_INFINITY)
limit = l.rlim_cur;
else
limit = l.rlim_cur / 2;
} else {
pr_err("failed to get fd limit\n");
limit = 1;
}
return limit;
}
static bool may_cache_fd(void)
{
static rlim_t limit;
if (!limit)
limit = get_fd_limit();
if (limit == RLIM_INFINITY)
return true;
return limit > (rlim_t) dso__data_open_cnt;
}
/*
* Check and close LRU dso if we crossed allowed limit
* for opened dso file descriptors. The limit is half
* of the RLIMIT_NOFILE files opened.
*/
static void check_data_close(void)
{
bool cache_fd = may_cache_fd();
if (!cache_fd)
close_first_dso();
}
/**
* dso__data_close - Close DSO data file
* @dso: dso object
*
* External interface to close @dso's data file descriptor.
*/
void dso__data_close(struct dso *dso)
{
close_dso(dso);
}
/**
* dso__data_fd - Get dso's data file descriptor
* @dso: dso object
* @machine: machine object
*
* External interface to find dso's file, open it and
* returns file descriptor.
*/
int dso__data_fd(struct dso *dso, struct machine *machine) int dso__data_fd(struct dso *dso, struct machine *machine)
{ {
enum dso_binary_type binary_type_data[] = { enum dso_binary_type binary_type_data[] = {
...@@ -168,8 +331,13 @@ int dso__data_fd(struct dso *dso, struct machine *machine) ...@@ -168,8 +331,13 @@ int dso__data_fd(struct dso *dso, struct machine *machine)
}; };
int i = 0; int i = 0;
if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) if (dso->data.fd >= 0)
return open_dso(dso, machine); return dso->data.fd;
if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) {
dso->data.fd = open_dso(dso, machine);
return dso->data.fd;
}
do { do {
int fd; int fd;
...@@ -178,7 +346,7 @@ int dso__data_fd(struct dso *dso, struct machine *machine) ...@@ -178,7 +346,7 @@ int dso__data_fd(struct dso *dso, struct machine *machine)
fd = open_dso(dso, machine); fd = open_dso(dso, machine);
if (fd >= 0) if (fd >= 0)
return fd; return dso->data.fd = fd;
} while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND); } while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND);
...@@ -260,16 +428,10 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset, ...@@ -260,16 +428,10 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset,
} }
static ssize_t static ssize_t
dso_cache__read(struct dso *dso, struct machine *machine, dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
u64 offset, u8 *data, ssize_t size)
{ {
struct dso_cache *cache; struct dso_cache *cache;
ssize_t ret; ssize_t ret;
int fd;
fd = dso__data_fd(dso, machine);
if (fd < 0)
return -1;
do { do {
u64 cache_offset; u64 cache_offset;
...@@ -283,16 +445,16 @@ dso_cache__read(struct dso *dso, struct machine *machine, ...@@ -283,16 +445,16 @@ dso_cache__read(struct dso *dso, struct machine *machine,
cache_offset = offset & DSO__DATA_CACHE_MASK; cache_offset = offset & DSO__DATA_CACHE_MASK;
ret = -EINVAL; ret = -EINVAL;
if (-1 == lseek(fd, cache_offset, SEEK_SET)) if (-1 == lseek(dso->data.fd, cache_offset, SEEK_SET))
break; break;
ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); ret = read(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE);
if (ret <= 0) if (ret <= 0)
break; break;
cache->offset = cache_offset; cache->offset = cache_offset;
cache->size = ret; cache->size = ret;
dso_cache__insert(&dso->cache, cache); dso_cache__insert(&dso->data.cache, cache);
ret = dso_cache__memcpy(cache, offset, data, size); ret = dso_cache__memcpy(cache, offset, data, size);
...@@ -301,24 +463,27 @@ dso_cache__read(struct dso *dso, struct machine *machine, ...@@ -301,24 +463,27 @@ dso_cache__read(struct dso *dso, struct machine *machine,
if (ret <= 0) if (ret <= 0)
free(cache); free(cache);
close(fd);
return ret; return ret;
} }
static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, static ssize_t dso_cache_read(struct dso *dso, u64 offset,
u64 offset, u8 *data, ssize_t size) u8 *data, ssize_t size)
{ {
struct dso_cache *cache; struct dso_cache *cache;
cache = dso_cache__find(&dso->cache, offset); cache = dso_cache__find(&dso->data.cache, offset);
if (cache) if (cache)
return dso_cache__memcpy(cache, offset, data, size); return dso_cache__memcpy(cache, offset, data, size);
else else
return dso_cache__read(dso, machine, offset, data, size); return dso_cache__read(dso, offset, data, size);
} }
ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, /*
u64 offset, u8 *data, ssize_t size) * Reads and caches dso data DSO__DATA_CACHE_SIZE size chunks
* in the rb_tree. Any read to already cached data is served
* by cached data.
*/
static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
{ {
ssize_t r = 0; ssize_t r = 0;
u8 *p = data; u8 *p = data;
...@@ -326,7 +491,7 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, ...@@ -326,7 +491,7 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
do { do {
ssize_t ret; ssize_t ret;
ret = dso_cache_read(dso, machine, offset, p, size); ret = dso_cache_read(dso, offset, p, size);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -346,6 +511,67 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, ...@@ -346,6 +511,67 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
return r; return r;
} }
static int data_file_size(struct dso *dso)
{
struct stat st;
if (!dso->data.file_size) {
if (fstat(dso->data.fd, &st)) {
pr_err("dso mmap failed, fstat: %s\n", strerror(errno));
return -1;
}
dso->data.file_size = st.st_size;
}
return 0;
}
static ssize_t data_read_offset(struct dso *dso, u64 offset,
u8 *data, ssize_t size)
{
if (data_file_size(dso))
return -1;
/* Check the offset sanity. */
if (offset > dso->data.file_size)
return -1;
if (offset + size < offset)
return -1;
return cached_read(dso, offset, data, size);
}
/**
* dso__data_read_offset - Read data from dso file offset
* @dso: dso object
* @machine: machine object
* @offset: file offset
* @data: buffer to store data
* @size: size of the @data buffer
*
* External interface to read data from dso file offset. Open
* dso data file and use cached_read to get the data.
*/
ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
u64 offset, u8 *data, ssize_t size)
{
if (dso__data_fd(dso, machine) < 0)
return -1;
return data_read_offset(dso, offset, data, size);
}
/**
* dso__data_read_addr - Read data from dso address
* @dso: dso object
* @machine: machine object
* @add: virtual memory address
* @data: buffer to store data
* @size: size of the @data buffer
*
* External interface to read data from dso address.
*/
ssize_t dso__data_read_addr(struct dso *dso, struct map *map, ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
struct machine *machine, u64 addr, struct machine *machine, u64 addr,
u8 *data, ssize_t size) u8 *data, ssize_t size)
...@@ -473,7 +699,8 @@ struct dso *dso__new(const char *name) ...@@ -473,7 +699,8 @@ struct dso *dso__new(const char *name)
dso__set_short_name(dso, dso->name, false); dso__set_short_name(dso, dso->name, false);
for (i = 0; i < MAP__NR_TYPES; ++i) for (i = 0; i < MAP__NR_TYPES; ++i)
dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; dso->symbols[i] = dso->symbol_names[i] = RB_ROOT;
dso->cache = RB_ROOT; dso->data.cache = RB_ROOT;
dso->data.fd = -1;
dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND;
dso->loaded = 0; dso->loaded = 0;
...@@ -485,6 +712,7 @@ struct dso *dso__new(const char *name) ...@@ -485,6 +712,7 @@ struct dso *dso__new(const char *name)
dso->kernel = DSO_TYPE_USER; dso->kernel = DSO_TYPE_USER;
dso->needs_swap = DSO_SWAP__UNSET; dso->needs_swap = DSO_SWAP__UNSET;
INIT_LIST_HEAD(&dso->node); INIT_LIST_HEAD(&dso->node);
INIT_LIST_HEAD(&dso->data.open_entry);
} }
return dso; return dso;
...@@ -506,7 +734,8 @@ void dso__delete(struct dso *dso) ...@@ -506,7 +734,8 @@ void dso__delete(struct dso *dso)
dso->long_name_allocated = false; dso->long_name_allocated = false;
} }
dso_cache__free(&dso->cache); dso__data_close(dso);
dso_cache__free(&dso->data.cache);
dso__free_a2l(dso); dso__free_a2l(dso);
zfree(&dso->symsrc_filename); zfree(&dso->symsrc_filename);
free(dso); free(dso);
......
...@@ -76,7 +76,6 @@ struct dso { ...@@ -76,7 +76,6 @@ struct dso {
struct list_head node; struct list_head node;
struct rb_root symbols[MAP__NR_TYPES]; struct rb_root symbols[MAP__NR_TYPES];
struct rb_root symbol_names[MAP__NR_TYPES]; struct rb_root symbol_names[MAP__NR_TYPES];
struct rb_root cache;
void *a2l; void *a2l;
char *symsrc_filename; char *symsrc_filename;
unsigned int a2l_fails; unsigned int a2l_fails;
...@@ -99,6 +98,15 @@ struct dso { ...@@ -99,6 +98,15 @@ struct dso {
const char *long_name; const char *long_name;
u16 long_name_len; u16 long_name_len;
u16 short_name_len; u16 short_name_len;
/* dso data file */
struct {
struct rb_root cache;
int fd;
size_t file_size;
struct list_head open_entry;
} data;
char name[0]; char name[0];
}; };
...@@ -141,7 +149,47 @@ char dso__symtab_origin(const struct dso *dso); ...@@ -141,7 +149,47 @@ char dso__symtab_origin(const struct dso *dso);
int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type,
char *root_dir, char *filename, size_t size); char *root_dir, char *filename, size_t size);
/*
* The dso__data_* external interface provides following functions:
* dso__data_fd
* dso__data_close
* dso__data_read_offset
* dso__data_read_addr
*
* Please refer to the dso.c object code for each function and
* arguments documentation. Following text tries to explain the
* dso file descriptor caching.
*
* The dso__data* interface allows caching of opened file descriptors
* to speed up the dso data accesses. The idea is to leave the file
* descriptor opened ideally for the whole life of the dso object.
*
* The current usage of the dso__data_* interface is as follows:
*
* Get DSO's fd:
* int fd = dso__data_fd(dso, machine);
* USE 'fd' SOMEHOW
*
* Read DSO's data:
* n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE);
* n = dso__data_read_addr(dso_0, &machine, 0, buf, BUFSIZE);
*
* Eventually close DSO's fd:
* dso__data_close(dso);
*
* It is not necessary to close the DSO object data file. Each time new
* DSO data file is opened, the limit (RLIMIT_NOFILE/2) is checked. Once
* it is crossed, the oldest opened DSO object is closed.
*
* The dso__delete function calls close_dso function to ensure the
* data file descriptor gets closed/unmapped before the dso object
* is freed.
*
* TODO
*/
int dso__data_fd(struct dso *dso, struct machine *machine); int dso__data_fd(struct dso *dso, struct machine *machine);
void dso__data_close(struct dso *dso);
ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
u64 offset, u8 *data, ssize_t size); u64 offset, u8 *data, ssize_t size);
ssize_t dso__data_read_addr(struct dso *dso, struct map *map, ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
......
#include <linux/types.h> #include <linux/types.h>
#include <sys/mman.h>
#include "event.h" #include "event.h"
#include "debug.h" #include "debug.h"
#include "hist.h" #include "hist.h"
...@@ -178,13 +179,14 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, ...@@ -178,13 +179,14 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
return -1; return -1;
} }
event->header.type = PERF_RECORD_MMAP; event->header.type = PERF_RECORD_MMAP2;
while (1) { while (1) {
char bf[BUFSIZ]; char bf[BUFSIZ];
char prot[5]; char prot[5];
char execname[PATH_MAX]; char execname[PATH_MAX];
char anonstr[] = "//anon"; char anonstr[] = "//anon";
unsigned int ino;
size_t size; size_t size;
ssize_t n; ssize_t n;
...@@ -195,15 +197,20 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, ...@@ -195,15 +197,20 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
strcpy(execname, ""); strcpy(execname, "");
/* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n", n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n",
&event->mmap.start, &event->mmap.len, prot, &event->mmap2.start, &event->mmap2.len, prot,
&event->mmap.pgoff, &event->mmap2.pgoff, &event->mmap2.maj,
execname); &event->mmap2.min,
&ino, execname);
/* /*
* Anon maps don't have the execname. * Anon maps don't have the execname.
*/ */
if (n < 4) if (n < 7)
continue; continue;
event->mmap2.ino = (u64)ino;
/* /*
* Just like the kernel, see __perf_event_mmap in kernel/perf_event.c * 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, ...@@ -212,6 +219,21 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
else else
event->header.misc = PERF_RECORD_MISC_GUEST_USER; 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 (prot[2] != 'x') {
if (!mmap_data || prot[0] != 'r') if (!mmap_data || prot[0] != 'r')
continue; continue;
...@@ -223,15 +245,15 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, ...@@ -223,15 +245,15 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
strcpy(execname, anonstr); strcpy(execname, anonstr);
size = strlen(execname) + 1; size = strlen(execname) + 1;
memcpy(event->mmap.filename, execname, size); memcpy(event->mmap2.filename, execname, size);
size = PERF_ALIGN(size, sizeof(u64)); size = PERF_ALIGN(size, sizeof(u64));
event->mmap.len -= event->mmap.start; event->mmap2.len -= event->mmap.start;
event->mmap.header.size = (sizeof(event->mmap) - event->mmap2.header.size = (sizeof(event->mmap2) -
(sizeof(event->mmap.filename) - size)); (sizeof(event->mmap2.filename) - size));
memset(event->mmap.filename + size, 0, machine->id_hdr_size); memset(event->mmap2.filename + size, 0, machine->id_hdr_size);
event->mmap.header.size += machine->id_hdr_size; event->mmap2.header.size += machine->id_hdr_size;
event->mmap.pid = tgid; event->mmap2.pid = tgid;
event->mmap.tid = pid; event->mmap2.tid = pid;
if (process(tool, event, &synth_sample, machine) != 0) { if (process(tool, event, &synth_sample, machine) != 0) {
rc = -1; rc = -1;
...@@ -612,12 +634,15 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) ...@@ -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) size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)
{ {
return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 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.pid, event->mmap2.tid, event->mmap2.start,
event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj,
event->mmap2.min, event->mmap2.ino, event->mmap2.min, event->mmap2.ino,
event->mmap2.ino_generation, 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); event->mmap2.filename);
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "../perf.h" #include "../perf.h"
#include "map.h" #include "map.h"
#include "build-id.h" #include "build-id.h"
#include "perf_regs.h"
struct mmap_event { struct mmap_event {
struct perf_event_header header; struct perf_event_header header;
...@@ -27,6 +28,8 @@ struct mmap2_event { ...@@ -27,6 +28,8 @@ struct mmap2_event {
u32 min; u32 min;
u64 ino; u64 ino;
u64 ino_generation; u64 ino_generation;
u32 prot;
u32 flags;
char filename[PATH_MAX]; char filename[PATH_MAX];
}; };
...@@ -87,6 +90,10 @@ struct regs_dump { ...@@ -87,6 +90,10 @@ struct regs_dump {
u64 abi; u64 abi;
u64 mask; u64 mask;
u64 *regs; u64 *regs;
/* Cached values/mask filled by first register access. */
u64 cache_regs[PERF_REGS_MAX];
u64 cache_mask;
}; };
struct stack_dump { struct stack_dump {
......
...@@ -589,10 +589,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) ...@@ -589,10 +589,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
} }
/* /*
* We default some events to a 1 default interval. But keep * We default some events to have a default interval. But keep
* it a weak assumption overridable by the user. * it a weak assumption overridable by the user.
*/ */
if (!attr->sample_period || (opts->user_freq != UINT_MAX && if (!attr->sample_period || (opts->user_freq != UINT_MAX ||
opts->user_interval != ULLONG_MAX)) { opts->user_interval != ULLONG_MAX)) {
if (opts->freq) { if (opts->freq) {
perf_evsel__set_sample_bit(evsel, PERIOD); perf_evsel__set_sample_bit(evsel, PERIOD);
...@@ -659,6 +659,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) ...@@ -659,6 +659,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
perf_evsel__set_sample_bit(evsel, WEIGHT); perf_evsel__set_sample_bit(evsel, WEIGHT);
attr->mmap = track; attr->mmap = track;
attr->mmap2 = track && !perf_missing_features.mmap2;
attr->comm = track; attr->comm = track;
if (opts->sample_transaction) if (opts->sample_transaction)
......
...@@ -128,6 +128,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) ...@@ -128,6 +128,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
+ unresolved_col_width + 2; + unresolved_col_width + 2;
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
symlen); symlen);
hists__new_col_len(hists, HISTC_MEM_DCACHELINE,
symlen + 1);
} else { } else {
symlen = unresolved_col_width + 4 + 2; symlen = unresolved_col_width + 4 + 2;
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
...@@ -439,9 +441,10 @@ struct hist_entry *__hists__add_entry(struct hists *hists, ...@@ -439,9 +441,10 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
.map = al->map, .map = al->map,
.sym = al->sym, .sym = al->sym,
}, },
.cpu = al->cpu, .cpu = al->cpu,
.ip = al->addr, .cpumode = al->cpumode,
.level = al->level, .ip = al->addr,
.level = al->level,
.stat = { .stat = {
.nr_events = 1, .nr_events = 1,
.period = period, .period = period,
......
...@@ -72,6 +72,7 @@ enum hist_column { ...@@ -72,6 +72,7 @@ enum hist_column {
HISTC_MEM_TLB, HISTC_MEM_TLB,
HISTC_MEM_LVL, HISTC_MEM_LVL,
HISTC_MEM_SNOOP, HISTC_MEM_SNOOP,
HISTC_MEM_DCACHELINE,
HISTC_TRANSACTION, HISTC_TRANSACTION,
HISTC_NR_COLS, /* Last entry */ HISTC_NR_COLS, /* Last entry */
}; };
......
...@@ -1060,6 +1060,8 @@ int machine__process_mmap2_event(struct machine *machine, ...@@ -1060,6 +1060,8 @@ int machine__process_mmap2_event(struct machine *machine,
event->mmap2.pid, event->mmap2.maj, event->mmap2.pid, event->mmap2.maj,
event->mmap2.min, event->mmap2.ino, event->mmap2.min, event->mmap2.ino,
event->mmap2.ino_generation, event->mmap2.ino_generation,
event->mmap2.prot,
event->mmap2.flags,
event->mmap2.filename, type); event->mmap2.filename, type);
if (map == NULL) if (map == NULL)
...@@ -1105,7 +1107,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event ...@@ -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, map = map__new(&machine->user_dsos, event->mmap.start,
event->mmap.len, event->mmap.pgoff, 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, event->mmap.filename,
type); type);
......
...@@ -138,7 +138,7 @@ void map__init(struct map *map, enum map_type 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, 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 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) enum map_type type)
{ {
struct map *map = malloc(sizeof(*map)); struct map *map = malloc(sizeof(*map));
...@@ -157,6 +157,8 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, ...@@ -157,6 +157,8 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
map->min = d_min; map->min = d_min;
map->ino = ino; map->ino = ino;
map->ino_generation = ino_gen; map->ino_generation = ino_gen;
map->prot = prot;
map->flags = flags;
if ((anon || no_dso) && type == MAP__FUNCTION) { if ((anon || no_dso) && type == MAP__FUNCTION) {
snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
......
...@@ -35,6 +35,8 @@ struct map { ...@@ -35,6 +35,8 @@ struct map {
bool referenced; bool referenced;
bool erange_warned; bool erange_warned;
u32 priv; u32 priv;
u32 prot;
u32 flags;
u64 pgoff; u64 pgoff;
u64 reloc; u64 reloc;
u32 maj, min; /* only valid for MMAP2 record */ u32 maj, min; /* only valid for MMAP2 record */
...@@ -118,7 +120,7 @@ void map__init(struct map *map, enum map_type type, ...@@ -118,7 +120,7 @@ void map__init(struct map *map, enum map_type type,
u64 start, u64 end, u64 pgoff, struct dso *dso); u64 start, u64 end, u64 pgoff, struct dso *dso);
struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, 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 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); char *filename, enum map_type type);
struct map *map__new2(u64 start, struct dso *dso, enum map_type type); struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
void map__delete(struct map *map); void map__delete(struct map *map);
......
#include <errno.h> #include <errno.h>
#include "perf_regs.h" #include "perf_regs.h"
#include "event.h"
int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
{ {
int i, idx = 0; int i, idx = 0;
u64 mask = regs->mask; u64 mask = regs->mask;
if (regs->cache_mask & (1 << id))
goto out;
if (!(mask & (1 << id))) if (!(mask & (1 << id)))
return -EINVAL; return -EINVAL;
...@@ -14,6 +18,10 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) ...@@ -14,6 +18,10 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
idx++; idx++;
} }
*valp = regs->regs[idx]; regs->cache_mask |= (1 << id);
regs->cache_regs[id] = regs->regs[idx];
out:
*valp = regs->cache_regs[id];
return 0; return 0;
} }
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
#define __PERF_REGS_H #define __PERF_REGS_H
#include <linux/types.h> #include <linux/types.h>
#include "event.h"
struct regs_dump;
#ifdef HAVE_PERF_REGS_SUPPORT #ifdef HAVE_PERF_REGS_SUPPORT
#include <perf_regs.h> #include <perf_regs.h>
...@@ -11,6 +12,7 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id); ...@@ -11,6 +12,7 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id);
#else #else
#define PERF_REGS_MASK 0 #define PERF_REGS_MASK 0
#define PERF_REGS_MAX 0
static inline const char *perf_reg_name(int id __maybe_unused) static inline const char *perf_reg_name(int id __maybe_unused)
{ {
......
...@@ -628,11 +628,11 @@ static int __show_line_range(struct line_range *lr, const char *module) ...@@ -628,11 +628,11 @@ static int __show_line_range(struct line_range *lr, const char *module)
ret = debuginfo__find_line_range(dinfo, lr); ret = debuginfo__find_line_range(dinfo, lr);
debuginfo__delete(dinfo); debuginfo__delete(dinfo);
if (ret == 0) { if (ret == 0 || ret == -ENOENT) {
pr_warning("Specified source line is not found.\n"); pr_warning("Specified source line is not found.\n");
return -ENOENT; return -ENOENT;
} else if (ret < 0) { } else if (ret < 0) {
pr_warning("Debuginfo analysis failed. (%d)\n", ret); pr_warning("Debuginfo analysis failed.\n");
return ret; return ret;
} }
...@@ -641,7 +641,7 @@ static int __show_line_range(struct line_range *lr, const char *module) ...@@ -641,7 +641,7 @@ static int __show_line_range(struct line_range *lr, const char *module)
ret = get_real_path(tmp, lr->comp_dir, &lr->path); ret = get_real_path(tmp, lr->comp_dir, &lr->path);
free(tmp); /* Free old path */ free(tmp); /* Free old path */
if (ret < 0) { if (ret < 0) {
pr_warning("Failed to find source file. (%d)\n", ret); pr_warning("Failed to find source file path.\n");
return ret; return ret;
} }
...@@ -721,9 +721,14 @@ static int show_available_vars_at(struct debuginfo *dinfo, ...@@ -721,9 +721,14 @@ static int show_available_vars_at(struct debuginfo *dinfo,
ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, ret = debuginfo__find_available_vars_at(dinfo, pev, &vls,
max_vls, externs); max_vls, externs);
if (ret <= 0) { if (ret <= 0) {
pr_err("Failed to find variables at %s (%d)\n", buf, ret); if (ret == 0 || ret == -ENOENT) {
pr_err("Failed to find the address of %s\n", buf);
ret = -ENOENT;
} else
pr_warning("Debuginfo analysis failed.\n");
goto end; goto end;
} }
/* Some variables are found */ /* Some variables are found */
fprintf(stdout, "Available variables at %s\n", buf); fprintf(stdout, "Available variables at %s\n", buf);
for (i = 0; i < ret; i++) { for (i = 0; i < ret; i++) {
......
...@@ -573,14 +573,13 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) ...@@ -573,14 +573,13 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)
if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) { if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) {
/* Search again in global variables */ /* Search again in global variables */
if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die))
pr_warning("Failed to find '%s' in this function.\n",
pf->pvar->var);
ret = -ENOENT; ret = -ENOENT;
} }
if (ret >= 0) if (ret >= 0)
ret = convert_variable(&vr_die, pf); ret = convert_variable(&vr_die, pf);
if (ret < 0)
pr_warning("Failed to find '%s' in this function.\n",
pf->pvar->var);
return ret; return ret;
} }
...@@ -1281,7 +1280,11 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) ...@@ -1281,7 +1280,11 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
return ret; return ret;
} }
/* Find available variables at given probe point */ /*
* Find available variables at given probe point
* Return the number of found probe points. Return 0 if there is no
* matched probe point. Return <0 if an error occurs.
*/
int debuginfo__find_available_vars_at(struct debuginfo *dbg, int debuginfo__find_available_vars_at(struct debuginfo *dbg,
struct perf_probe_event *pev, struct perf_probe_event *pev,
struct variable_list **vls, struct variable_list **vls,
......
...@@ -215,6 +215,7 @@ static void define_event_symbols(struct event_format *event, ...@@ -215,6 +215,7 @@ static void define_event_symbols(struct event_format *event,
case PRINT_BSTRING: case PRINT_BSTRING:
case PRINT_DYNAMIC_ARRAY: case PRINT_DYNAMIC_ARRAY:
case PRINT_STRING: case PRINT_STRING:
case PRINT_BITMASK:
break; break;
case PRINT_TYPE: case PRINT_TYPE:
define_event_symbols(event, ev_name, args->typecast.item); define_event_symbols(event, ev_name, args->typecast.item);
......
...@@ -197,6 +197,7 @@ static void define_event_symbols(struct event_format *event, ...@@ -197,6 +197,7 @@ static void define_event_symbols(struct event_format *event,
case PRINT_BSTRING: case PRINT_BSTRING:
case PRINT_DYNAMIC_ARRAY: case PRINT_DYNAMIC_ARRAY:
case PRINT_FUNC: case PRINT_FUNC:
case PRINT_BITMASK:
/* we should warn... */ /* we should warn... */
return; return;
} }
...@@ -622,6 +623,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) ...@@ -622,6 +623,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile)
fprintf(ofp, "%s=", f->name); fprintf(ofp, "%s=", f->name);
if (f->flags & FIELD_IS_STRING || if (f->flags & FIELD_IS_STRING ||
f->flags & FIELD_IS_FLAG || f->flags & FIELD_IS_FLAG ||
f->flags & FIELD_IS_ARRAY ||
f->flags & FIELD_IS_SYMBOLIC) f->flags & FIELD_IS_SYMBOLIC)
fprintf(ofp, "%%s"); fprintf(ofp, "%%s");
else if (f->flags & FIELD_IS_SIGNED) else if (f->flags & FIELD_IS_SIGNED)
......
#include <sys/mman.h>
#include "sort.h" #include "sort.h"
#include "hist.h" #include "hist.h"
#include "comm.h" #include "comm.h"
...@@ -784,6 +785,104 @@ static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf, ...@@ -784,6 +785,104 @@ static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
return repsep_snprintf(bf, size, "%-*s", width, out); 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 = { struct sort_entry sort_mispredict = {
.se_header = "Branch Mispredicted", .se_header = "Branch Mispredicted",
.se_cmp = sort__mispredict_cmp, .se_cmp = sort__mispredict_cmp,
...@@ -876,6 +975,13 @@ struct sort_entry sort_mem_snoop = { ...@@ -876,6 +975,13 @@ struct sort_entry sort_mem_snoop = {
.se_width_idx = HISTC_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 static int64_t
sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
{ {
...@@ -1043,6 +1149,7 @@ static struct sort_dimension memory_sort_dimensions[] = { ...@@ -1043,6 +1149,7 @@ static struct sort_dimension memory_sort_dimensions[] = {
DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb), DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
DIM(SORT_MEM_LVL, "mem", sort_mem_lvl), DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
}; };
#undef DIM #undef DIM
......
...@@ -89,6 +89,7 @@ struct hist_entry { ...@@ -89,6 +89,7 @@ struct hist_entry {
u64 ip; u64 ip;
u64 transaction; u64 transaction;
s32 cpu; s32 cpu;
u8 cpumode;
struct hist_entry_diff diff; struct hist_entry_diff diff;
...@@ -185,6 +186,7 @@ enum sort_type { ...@@ -185,6 +186,7 @@ enum sort_type {
SORT_MEM_TLB, SORT_MEM_TLB,
SORT_MEM_LVL, SORT_MEM_LVL,
SORT_MEM_SNOOP, SORT_MEM_SNOOP,
SORT_MEM_DCACHELINE,
}; };
/* /*
......
...@@ -250,7 +250,6 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, ...@@ -250,7 +250,6 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,
/* Check the .eh_frame section for unwinding info */ /* Check the .eh_frame section for unwinding info */
offset = elf_section_offset(fd, ".eh_frame_hdr"); offset = elf_section_offset(fd, ".eh_frame_hdr");
close(fd);
if (offset) if (offset)
ret = unwind_spec_ehframe(dso, machine, offset, ret = unwind_spec_ehframe(dso, machine, offset,
...@@ -271,7 +270,6 @@ static int read_unwind_spec_debug_frame(struct dso *dso, ...@@ -271,7 +270,6 @@ static int read_unwind_spec_debug_frame(struct dso *dso,
/* Check the .debug_frame section for unwinding info */ /* Check the .debug_frame section for unwinding info */
*offset = elf_section_offset(fd, ".debug_frame"); *offset = elf_section_offset(fd, ".debug_frame");
close(fd);
if (*offset) if (*offset)
return 0; return 0;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
* XXX We need to find a better place for these things... * XXX We need to find a better place for these things...
*/ */
unsigned int page_size; unsigned int page_size;
int cacheline_size;
bool test_attr__enabled; bool test_attr__enabled;
......
...@@ -304,6 +304,7 @@ char *rtrim(char *s); ...@@ -304,6 +304,7 @@ char *rtrim(char *s);
void dump_stack(void); void dump_stack(void);
extern unsigned int page_size; extern unsigned int page_size;
extern int cacheline_size;
void get_term_dimensions(struct winsize *ws); 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