Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull perf fixes from Ingo Molnar:
 "This is larger than usual: the main reason are the ARM symbol lookup
  speedups that came in late and were hard to resist.

  There's also a kprobes fix and various tooling fixes, plus the minimal
  re-enablement of the mmap2 support interface"

* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (36 commits)
  x86/kprobes: Fix build errors and blacklist context_track_user
  perf tests: Add test for closing dso objects on EMFILE error
  perf tests: Add test for caching dso file descriptors
  perf tests: Allow reuse of test_file function
  perf tests: Spawn child for each test
  perf tools: Add dso__data_* interface descriptons
  perf tools: Allow to close dso fd in case of open failure
  perf tools: Add file size check and factor dso__data_read_offset
  perf tools: Cache dso data file descriptor
  perf tools: Add global count of opened dso objects
  perf tools: Add global list of opened dso objects
  perf tools: Add data_fd into dso object
  perf tools: Separate dso data related variables
  perf tools: Cache register accesses for unwind processing
  perf record: Fix to honor user freq/interval properly
  perf timechart: Reflow documentation
  perf probe: Improve error messages in --line option
  perf probe: Improve an error message of perf probe --vars mode
  perf probe: Show error code and description in verbose mode
  perf probe: Improve error message for unknown member of data structure
  ...
This commit is contained in:
Linus Torvalds
2014-06-21 07:07:17 -10:00
41 changed files with 1249 additions and 126 deletions
+4 -3
View File
@@ -343,6 +343,7 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code)
if (poke_int3_handler(regs))
return;
prev_state = exception_enter();
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
SIGTRAP) == NOTIFY_STOP)
@@ -351,9 +352,8 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code)
#ifdef CONFIG_KPROBES
if (kprobe_int3_handler(regs))
return;
goto exit;
#endif
prev_state = exception_enter();
if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
SIGTRAP) == NOTIFY_STOP)
@@ -433,6 +433,8 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
unsigned long dr6;
int si_code;
prev_state = exception_enter();
get_debugreg(dr6, 6);
/* Filter out all the reserved bits which are preset to 1 */
@@ -465,7 +467,6 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
if (kprobe_debug_handler(regs))
goto exit;
#endif
prev_state = exception_enter();
if (notify_die(DIE_DEBUG, "debug", regs, (long)&dr6, error_code,
SIGTRAP) == NOTIFY_STOP)
+1
View File
@@ -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;
* };
+3
View File
@@ -19,6 +19,7 @@
#include <linux/sched.h>
#include <linux/hardirq.h>
#include <linux/export.h>
#include <linux/kprobes.h>
#define CREATE_TRACE_POINTS
#include <trace/events/context_tracking.h>
@@ -104,6 +105,7 @@ void context_tracking_user_enter(void)
}
local_irq_restore(flags);
}
NOKPROBE_SYMBOL(context_tracking_user_enter);
#ifdef CONFIG_PREEMPT
/**
@@ -181,6 +183,7 @@ void context_tracking_user_exit(void)
}
local_irq_restore(flags);
}
NOKPROBE_SYMBOL(context_tracking_user_exit);
/**
* __context_tracking_task_switch - context switch the syscall callbacks
+33 -4
View File
@@ -40,6 +40,7 @@
#include <linux/mm_types.h>
#include <linux/cgroup.h>
#include <linux/module.h>
#include <linux/mman.h>
#include "internal.h"
@@ -5128,6 +5129,7 @@ struct perf_mmap_event {
int maj, min;
u64 ino;
u64 ino_generation;
u32 prot, flags;
struct {
struct perf_event_header header;
@@ -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->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);
@@ -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->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,
@@ -5205,6 +5211,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;
@@ -5235,6 +5242,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);
@@ -5275,6 +5304,8 @@ got_name:
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;
@@ -5315,6 +5346,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);
@@ -6897,10 +6930,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;
+113
View File
@@ -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);
+22 -3
View File
@@ -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;
+202 -1
View File
@@ -18,6 +18,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
@@ -30,12 +31,207 @@
#define LOCAL_PLUGIN_DIR ".traceevent/plugins"
static struct registered_plugin_options {
struct registered_plugin_options *next;
struct pevent_plugin_option *options;
} *registered_options;
static struct trace_plugin_options {
struct trace_plugin_options *next;
char *plugin;
char *option;
char *value;
} *trace_plugin_options;
struct plugin_list {
struct plugin_list *next;
char *name;
void *handle;
};
/**
* traceevent_plugin_list_options - get list of plugin options
*
* Returns an array of char strings that list the currently registered
* plugin options in the format of <plugin>:<option>. This list can be
* used by toggling the option.
*
* Returns NULL if there's no options registered. On error it returns
* INVALID_PLUGIN_LIST_OPTION
*
* Must be freed with traceevent_plugin_free_options_list().
*/
char **traceevent_plugin_list_options(void)
{
struct registered_plugin_options *reg;
struct pevent_plugin_option *op;
char **list = NULL;
char *name;
int count = 0;
for (reg = registered_options; reg; reg = reg->next) {
for (op = reg->options; op->name; op++) {
char *alias = op->plugin_alias ? op->plugin_alias : op->file;
char **temp = list;
name = malloc(strlen(op->name) + strlen(alias) + 2);
if (!name)
goto err;
sprintf(name, "%s:%s", alias, op->name);
list = realloc(list, count + 2);
if (!list) {
list = temp;
free(name);
goto err;
}
list[count++] = name;
list[count] = NULL;
}
}
return list;
err:
while (--count >= 0)
free(list[count]);
free(list);
return INVALID_PLUGIN_LIST_OPTION;
}
void traceevent_plugin_free_options_list(char **list)
{
int i;
if (!list)
return;
if (list == INVALID_PLUGIN_LIST_OPTION)
return;
for (i = 0; list[i]; i++)
free(list[i]);
free(list);
}
static int
update_option(const char *file, struct pevent_plugin_option *option)
{
struct trace_plugin_options *op;
char *plugin;
if (option->plugin_alias) {
plugin = strdup(option->plugin_alias);
if (!plugin)
return -1;
} else {
char *p;
plugin = strdup(file);
if (!plugin)
return -1;
p = strstr(plugin, ".");
if (p)
*p = '\0';
}
/* first look for named options */
for (op = trace_plugin_options; op; op = op->next) {
if (!op->plugin)
continue;
if (strcmp(op->plugin, plugin) != 0)
continue;
if (strcmp(op->option, option->name) != 0)
continue;
option->value = op->value;
option->set ^= 1;
goto out;
}
/* first look for unnamed options */
for (op = trace_plugin_options; op; op = op->next) {
if (op->plugin)
continue;
if (strcmp(op->option, option->name) != 0)
continue;
option->value = op->value;
option->set ^= 1;
break;
}
out:
free(plugin);
return 0;
}
/**
* traceevent_plugin_add_options - Add a set of options by a plugin
* @name: The name of the plugin adding the options
* @options: The set of options being loaded
*
* Sets the options with the values that have been added by user.
*/
int traceevent_plugin_add_options(const char *name,
struct pevent_plugin_option *options)
{
struct registered_plugin_options *reg;
reg = malloc(sizeof(*reg));
if (!reg)
return -1;
reg->next = registered_options;
reg->options = options;
registered_options = reg;
while (options->name) {
update_option(name, options);
options++;
}
return 0;
}
/**
* traceevent_plugin_remove_options - remove plugin options that were registered
* @options: Options to removed that were registered with traceevent_plugin_add_options
*/
void traceevent_plugin_remove_options(struct pevent_plugin_option *options)
{
struct registered_plugin_options **last;
struct registered_plugin_options *reg;
for (last = &registered_options; *last; last = &(*last)->next) {
if ((*last)->options == options) {
reg = *last;
*last = reg->next;
free(reg);
return;
}
}
}
/**
* traceevent_print_plugins - print out the list of plugins loaded
* @s: the trace_seq descripter to write to
* @prefix: The prefix string to add before listing the option name
* @suffix: The suffix string ot append after the option name
* @list: The list of plugins (usually returned by traceevent_load_plugins()
*
* Writes to the trace_seq @s the list of plugins (files) that is
* returned by traceevent_load_plugins(). Use @prefix and @suffix for formating:
* @prefix = " ", @suffix = "\n".
*/
void traceevent_print_plugins(struct trace_seq *s,
const char *prefix, const char *suffix,
const struct plugin_list *list)
{
while (list) {
trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix);
list = list->next;
}
}
static void
load_plugin(struct pevent *pevent, const char *path,
const char *file, void *data)
@@ -148,12 +344,17 @@ load_plugins(struct pevent *pevent, const char *suffix,
char *path;
char *envdir;
if (pevent->flags & PEVENT_DISABLE_PLUGINS)
return;
/*
* If a system plugin directory was defined,
* check that first.
*/
#ifdef PLUGIN_DIR
load_plugins_dir(pevent, suffix, PLUGIN_DIR, load_plugin, data);
if (!(pevent->flags & PEVENT_DISABLE_SYS_PLUGINS))
load_plugins_dir(pevent, suffix, PLUGIN_DIR,
load_plugin, data);
#endif
/*
+37 -6
View File
@@ -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;
+23
View File
@@ -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).
+20 -21
View File
@@ -43,27 +43,6 @@ TIMECHART OPTIONS
--symfs=<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::
--proc-num::
Print task info for at least given number of tasks.
@@ -88,6 +67,26 @@ RECORD OPTIONS
--callchain::
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
--------
linkperf:perf-record[1]
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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);
+14 -9
View File
@@ -288,6 +288,13 @@ static void cleanup_params(void)
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
__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);
if (ret < 0) {
pr_err(" Error: Parse Error. (%d)\n", ret);
pr_err_with_code(" Error: Command Parse Error.", ret);
return ret;
}
}
@@ -419,8 +426,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
}
ret = show_perf_probe_events();
if (ret < 0)
pr_err(" Error: Failed to show event list. (%d)\n",
ret);
pr_err_with_code(" Error: Failed to show event list.", ret);
return ret;
}
if (params.show_funcs) {
@@ -445,8 +451,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
strfilter__delete(params.filter);
params.filter = NULL;
if (ret < 0)
pr_err(" Error: Failed to show functions."
" (%d)\n", ret);
pr_err_with_code(" Error: Failed to show functions.", ret);
return ret;
}
@@ -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);
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;
}
if (params.show_vars) {
@@ -485,7 +490,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
strfilter__delete(params.filter);
params.filter = NULL;
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;
}
#endif
@@ -493,7 +498,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
if (params.dellist) {
ret = del_perf_probe_events(params.dellist);
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;
}
}
@@ -504,7 +509,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
params.target,
params.force_add);
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;
}
}
+5 -1
View File
@@ -299,7 +299,11 @@ else
NO_LIBUNWIND := 1
NO_LIBDW_DWARF_UNWIND := 1
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
else
ifndef NO_LIBDW_DWARF_UNWIND
+1
View File
@@ -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)
+40 -2
View File
@@ -3,6 +3,8 @@
*
* Builtin regression testing command: ever growing number of sanity tests
*/
#include <unistd.h>
#include <string.h>
#include "builtin.h"
#include "intlist.h"
#include "tests.h"
@@ -50,9 +52,17 @@ static struct test {
.func = test__pmu,
},
{
.desc = "Test dso data interface",
.desc = "Test dso data read",
.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",
.func = test__perf_evsel__roundtrip_name_test,
@@ -172,6 +182,34 @@ static bool perf_test__matches(int curr, int argc, const char *argv[])
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)
{
int i = 0;
@@ -200,7 +238,7 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
}
pr_debug("\n--- start ---\n");
err = tests[curr].func();
err = run_test(&tests[curr]);
pr_debug("---- end ----\n%s:", tests[curr].desc);
switch (err) {
+210 -4
View File
@@ -1,22 +1,27 @@
#include "util.h"
#include <stdlib.h>
#include <linux/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <api/fs/fs.h>
#include "util.h"
#include "machine.h"
#include "symbol.h"
#include "tests.h"
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;
int fd, i;
unsigned char *buf;
strcpy(buf_templ, TEMPL);
#undef TEMPL
fd = mkstemp(templ);
if (fd < 0) {
perror("mkstemp failed");
@@ -150,3 +155,204 @@ int test__dso_data(void)
unlink(file);
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;
}
+1 -1
View File
@@ -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)
+2 -5
View File
@@ -205,8 +205,7 @@ $(run):
( eval $$cmd ) >> $@ 2>&1; \
echo " test: $(call test,$@)" >> $@ 2>&1; \
$(call test,$@) && \
rm -f $@ \
rm -rf $$TMP_DEST
rm -rf $@ $$TMP_DEST || (cat $@ ; false)
$(run_O):
$(call clean)
@@ -217,9 +216,7 @@ $(run_O):
( eval $$cmd ) >> $@ 2>&1 && \
echo " test: $(call test_O,$@)" >> $@ 2>&1; \
$(call test_O,$@) && \
rm -f $@ && \
rm -rf $$TMP_O \
rm -rf $$TMP_DEST
rm -rf $@ $$TMP_O $$TMP_DEST || (cat $@ ; false)
tarpkg:
@cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \
+2
View File
@@ -28,6 +28,8 @@ int test__syscall_open_tp_fields(void);
int test__pmu(void);
int test__attr(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__hists_link(void);
int test__python_use(void);

Some files were not shown because too many files have changed in this diff Show More