Commit 9327ca73 authored by Ingo Molnar's avatar Ingo Molnar

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

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

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

User visible changes:

  - Allow callchain order (caller, callee) to the libdw and libunwind based DWARF
    unwinders (Jiri Olsa)

  - Add missing parent_val initialization in the callchain code, fixing a
    SEGFAULT when using callchains with 'perf top' (Jiri Olsa)

  - Add initial 'perf config' command, for now just with a --list command to the
    contents of the configuration file in use and a basic man page describing
    its format, commands for doing edits and detailed documentation are being
    reviewed and proof-read. (Taeung Song)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents b7883a1c 646a6e84
perf-y += builtin-bench.o perf-y += builtin-bench.o
perf-y += builtin-annotate.o perf-y += builtin-annotate.o
perf-y += builtin-config.o
perf-y += builtin-diff.o perf-y += builtin-diff.o
perf-y += builtin-evlist.o perf-y += builtin-evlist.o
perf-y += builtin-help.o perf-y += builtin-help.o
......
perf-config(1)
==============
NAME
----
perf-config - Get and set variables in a configuration file.
SYNOPSIS
--------
[verse]
'perf config' -l | --list
DESCRIPTION
-----------
You can manage variables in a configuration file with this command.
OPTIONS
-------
-l::
--list::
Show current config variables, name and value, for all sections.
CONFIGURATION FILE
------------------
The perf configuration file contains many variables to change various
aspects of each of its tools, including output, disk usage, etc.
The '$HOME/.perfconfig' file is used to store a per-user configuration.
The file '$(sysconfdir)/perfconfig' can be used to
store a system-wide default configuration.
Syntax
~~~~~~
The file consist of sections. A section starts with its name
surrounded by square brackets and continues till the next section
begins. Each variable must be in a section, and have the form
'name = value', for example:
[section]
name1 = value1
name2 = value2
Section names are case sensitive and can contain any characters except
newline (double quote `"` and backslash have to be escaped as `\"` and `\\`,
respectively). Section headers can't span multiple lines.
Example
~~~~~~~
Given a $HOME/.perfconfig like this:
#
# This is the config file, and
# a '#' and ';' character indicates a comment
#
[colors]
# Color variables
top = red, default
medium = green, default
normal = lightgray, default
selected = white, lightgray
code = blue, default
addr = magenta, default
root = white, blue
[tui]
# Defaults if linked with libslang
report = on
annotate = on
top = on
[buildid]
# Default, disable using /dev/null
dir = ~/.debug
[annotate]
# Defaults
hide_src_code = false
use_offset = true
jump_arrows = true
show_nr_jumps = false
[help]
# Format can be man, info, web or html
format = man
autocorrect = 0
[ui]
show-headers = true
[call-graph]
# fp (framepointer), dwarf
record-mode = fp
print-type = graph
order = caller
sort-key = function
SEE ALSO
--------
linkperf:perf[1]
/*
* builtin-config.c
*
* Copyright (C) 2015, Taeung Song <treeze.taeung@gmail.com>
*
*/
#include "builtin.h"
#include "perf.h"
#include "util/cache.h"
#include "util/parse-options.h"
#include "util/util.h"
#include "util/debug.h"
static const char * const config_usage[] = {
"perf config [options]",
NULL
};
enum actions {
ACTION_LIST = 1
} actions;
static struct option config_options[] = {
OPT_SET_UINT('l', "list", &actions,
"show current config variables", ACTION_LIST),
OPT_END()
};
static int show_config(const char *key, const char *value,
void *cb __maybe_unused)
{
if (value)
printf("%s=%s\n", key, value);
else
printf("%s\n", key);
return 0;
}
int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused)
{
int ret = 0;
argc = parse_options(argc, argv, config_options, config_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
switch (actions) {
case ACTION_LIST:
if (argc) {
pr_err("Error: takes no arguments\n");
parse_options_usage(config_usage, config_options, "l", 1);
} else {
ret = perf_config(show_config, NULL);
if (ret < 0)
pr_err("Nothing configured, "
"please check your ~/.perfconfig file\n");
}
break;
default:
usage_with_options(config_usage, config_options);
}
return ret;
}
...@@ -17,6 +17,7 @@ extern int cmd_annotate(int argc, const char **argv, const char *prefix); ...@@ -17,6 +17,7 @@ extern int cmd_annotate(int argc, const char **argv, const char *prefix);
extern int cmd_bench(int argc, const char **argv, const char *prefix); extern int cmd_bench(int argc, const char **argv, const char *prefix);
extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix); extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix);
extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); extern int cmd_buildid_list(int argc, const char **argv, const char *prefix);
extern int cmd_config(int argc, const char **argv, const char *prefix);
extern int cmd_diff(int argc, const char **argv, const char *prefix); extern int cmd_diff(int argc, const char **argv, const char *prefix);
extern int cmd_evlist(int argc, const char **argv, const char *prefix); extern int cmd_evlist(int argc, const char **argv, const char *prefix);
extern int cmd_help(int argc, const char **argv, const char *prefix); extern int cmd_help(int argc, const char **argv, const char *prefix);
......
...@@ -9,6 +9,7 @@ perf-buildid-cache mainporcelain common ...@@ -9,6 +9,7 @@ perf-buildid-cache mainporcelain common
perf-buildid-list mainporcelain common perf-buildid-list mainporcelain common
perf-data mainporcelain common perf-data mainporcelain common
perf-diff mainporcelain common perf-diff mainporcelain common
perf-config mainporcelain common
perf-evlist mainporcelain common perf-evlist mainporcelain common
perf-inject mainporcelain common perf-inject mainporcelain common
perf-kmem mainporcelain common perf-kmem mainporcelain common
......
...@@ -39,6 +39,7 @@ struct cmd_struct { ...@@ -39,6 +39,7 @@ struct cmd_struct {
static struct cmd_struct commands[] = { static struct cmd_struct commands[] = {
{ "buildid-cache", cmd_buildid_cache, 0 }, { "buildid-cache", cmd_buildid_cache, 0 },
{ "buildid-list", cmd_buildid_list, 0 }, { "buildid-list", cmd_buildid_list, 0 },
{ "config", cmd_config, 0 },
{ "diff", cmd_diff, 0 }, { "diff", cmd_diff, 0 },
{ "evlist", cmd_evlist, 0 }, { "evlist", cmd_evlist, 0 },
{ "help", cmd_help, 0 }, { "help", cmd_help, 0 },
......
...@@ -51,6 +51,12 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) ...@@ -51,6 +51,12 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
"krava_1", "krava_1",
"test__dwarf_unwind" "test__dwarf_unwind"
}; };
/*
* The funcs[MAX_STACK] array index, based on the
* callchain order setup.
*/
int idx = callchain_param.order == ORDER_CALLER ?
MAX_STACK - *cnt - 1 : *cnt;
if (*cnt >= MAX_STACK) { if (*cnt >= MAX_STACK) {
pr_debug("failed: crossed the max stack value %d\n", MAX_STACK); pr_debug("failed: crossed the max stack value %d\n", MAX_STACK);
...@@ -63,8 +69,10 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) ...@@ -63,8 +69,10 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
return -1; return -1;
} }
pr_debug("got: %s 0x%" PRIx64 "\n", symbol, entry->ip); (*cnt)++;
return strcmp((const char *) symbol, funcs[(*cnt)++]); pr_debug("got: %s 0x%" PRIx64 ", expecting %s\n",
symbol, entry->ip, funcs[idx]);
return strcmp((const char *) symbol, funcs[idx]);
} }
__attribute__ ((noinline)) __attribute__ ((noinline))
...@@ -105,8 +113,16 @@ static int compare(void *p1, void *p2) ...@@ -105,8 +113,16 @@ static int compare(void *p1, void *p2)
/* Any possible value should be 'thread' */ /* Any possible value should be 'thread' */
struct thread *thread = *(struct thread **)p1; struct thread *thread = *(struct thread **)p1;
if (global_unwind_retval == -INT_MAX) if (global_unwind_retval == -INT_MAX) {
/* Call unwinder twice for both callchain orders. */
callchain_param.order = ORDER_CALLER;
global_unwind_retval = unwind_thread(thread); global_unwind_retval = unwind_thread(thread);
if (!global_unwind_retval) {
callchain_param.order = ORDER_CALLEE;
global_unwind_retval = unwind_thread(thread);
}
}
return p1 - p2; return p1 - p2;
} }
......
...@@ -143,6 +143,7 @@ extern __thread struct callchain_cursor callchain_cursor; ...@@ -143,6 +143,7 @@ extern __thread struct callchain_cursor callchain_cursor;
static inline void callchain_init(struct callchain_root *root) static inline void callchain_init(struct callchain_root *root)
{ {
INIT_LIST_HEAD(&root->node.val); INIT_LIST_HEAD(&root->node.val);
INIT_LIST_HEAD(&root->node.parent_val);
root->node.parent = NULL; root->node.parent = NULL;
root->node.hit = 0; root->node.hit = 0;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/types.h> #include <linux/types.h>
#include "event.h" #include "event.h"
#include "perf_regs.h" #include "perf_regs.h"
#include "callchain.h"
static char *debuginfo_path; static char *debuginfo_path;
...@@ -52,25 +53,28 @@ static int report_module(u64 ip, struct unwind_info *ui) ...@@ -52,25 +53,28 @@ static int report_module(u64 ip, struct unwind_info *ui)
return __report_module(&al, ip, ui); return __report_module(&al, ip, ui);
} }
/*
* Store all entries within entries array,
* we will process it after we finish unwind.
*/
static int entry(u64 ip, struct unwind_info *ui) static int entry(u64 ip, struct unwind_info *ui)
{ {
struct unwind_entry e; struct unwind_entry *e = &ui->entries[ui->idx++];
struct addr_location al; struct addr_location al;
if (__report_module(&al, ip, ui)) if (__report_module(&al, ip, ui))
return -1; return -1;
e.ip = ip; e->ip = ip;
e.map = al.map; e->map = al.map;
e.sym = al.sym; e->sym = al.sym;
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
al.sym ? al.sym->name : "''", al.sym ? al.sym->name : "''",
ip, ip,
al.map ? al.map->map_ip(al.map, ip) : (u64) 0); al.map ? al.map->map_ip(al.map, ip) : (u64) 0);
return 0;
return ui->cb(&e, ui->arg);
} }
static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp) static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp)
...@@ -168,7 +172,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, ...@@ -168,7 +172,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct perf_sample *data, struct perf_sample *data,
int max_stack) int max_stack)
{ {
struct unwind_info ui = { struct unwind_info *ui, ui_buf = {
.sample = data, .sample = data,
.thread = thread, .thread = thread,
.machine = thread->mg->machine, .machine = thread->mg->machine,
...@@ -177,35 +181,54 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, ...@@ -177,35 +181,54 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
.max_stack = max_stack, .max_stack = max_stack,
}; };
Dwarf_Word ip; Dwarf_Word ip;
int err = -EINVAL; int err = -EINVAL, i;
if (!data->user_regs.regs) if (!data->user_regs.regs)
return -EINVAL; return -EINVAL;
ui.dwfl = dwfl_begin(&offline_callbacks); ui = zalloc(sizeof(ui_buf) + sizeof(ui_buf.entries[0]) * max_stack);
if (!ui.dwfl) if (!ui)
return -ENOMEM;
*ui = ui_buf;
ui->dwfl = dwfl_begin(&offline_callbacks);
if (!ui->dwfl)
goto out; goto out;
err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP);
if (err) if (err)
goto out; goto out;
err = report_module(ip, &ui); err = report_module(ip, ui);
if (err) if (err)
goto out; goto out;
if (!dwfl_attach_state(ui.dwfl, EM_NONE, thread->tid, &callbacks, &ui)) if (!dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui))
goto out; goto out;
err = dwfl_getthread_frames(ui.dwfl, thread->tid, frame_callback, &ui); err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui);
if (err && !ui.max_stack) if (err && !ui->max_stack)
err = 0; err = 0;
/*
* Display what we got based on the order setup.
*/
for (i = 0; i < ui->idx && !err; i++) {
int j = i;
if (callchain_param.order == ORDER_CALLER)
j = ui->idx - i - 1;
err = ui->entries[j].ip ? ui->cb(&ui->entries[j], ui->arg) : 0;
}
out: out:
if (err) if (err)
pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1)); pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1));
dwfl_end(ui.dwfl); dwfl_end(ui->dwfl);
free(ui);
return 0; return 0;
} }
...@@ -16,6 +16,8 @@ struct unwind_info { ...@@ -16,6 +16,8 @@ struct unwind_info {
unwind_entry_cb_t cb; unwind_entry_cb_t cb;
void *arg; void *arg;
int max_stack; int max_stack;
int idx;
struct unwind_entry entries[];
}; };
#endif /* __PERF_UNWIND_LIBDW_H */ #endif /* __PERF_UNWIND_LIBDW_H */
...@@ -614,23 +614,48 @@ void unwind__finish_access(struct thread *thread) ...@@ -614,23 +614,48 @@ void unwind__finish_access(struct thread *thread)
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
void *arg, int max_stack) void *arg, int max_stack)
{ {
u64 val;
unw_word_t ips[max_stack];
unw_addr_space_t addr_space; unw_addr_space_t addr_space;
unw_cursor_t c; unw_cursor_t c;
int ret; int ret, i = 0;
addr_space = thread__priv(ui->thread);
if (addr_space == NULL)
return -1;
ret = unw_init_remote(&c, addr_space, ui); ret = perf_reg_value(&val, &ui->sample->user_regs, PERF_REG_IP);
if (ret) if (ret)
display_error(ret); return ret;
while (!ret && (unw_step(&c) > 0) && max_stack--) { ips[i++] = (unw_word_t) val;
unw_word_t ip;
unw_get_reg(&c, UNW_REG_IP, &ip); /*
ret = ip ? entry(ip, ui->thread, cb, arg) : 0; * If we need more than one entry, do the DWARF
* unwind itself.
*/
if (max_stack - 1 > 0) {
addr_space = thread__priv(ui->thread);
if (addr_space == NULL)
return -1;
ret = unw_init_remote(&c, addr_space, ui);
if (ret)
display_error(ret);
while (!ret && (unw_step(&c) > 0) && i < max_stack) {
unw_get_reg(&c, UNW_REG_IP, &ips[i]);
++i;
}
max_stack = i;
}
/*
* Display what we got based on the order setup.
*/
for (i = 0; i < max_stack && !ret; i++) {
int j = i;
if (callchain_param.order == ORDER_CALLER)
j = max_stack - i - 1;
ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0;
} }
return ret; return ret;
...@@ -640,24 +665,17 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, ...@@ -640,24 +665,17 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread, struct thread *thread,
struct perf_sample *data, int max_stack) struct perf_sample *data, int max_stack)
{ {
u64 ip;
struct unwind_info ui = { struct unwind_info ui = {
.sample = data, .sample = data,
.thread = thread, .thread = thread,
.machine = thread->mg->machine, .machine = thread->mg->machine,
}; };
int ret;
if (!data->user_regs.regs) if (!data->user_regs.regs)
return -EINVAL; return -EINVAL;
ret = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); if (max_stack <= 0)
if (ret) return -EINVAL;
return ret;
ret = entry(ip, thread, cb, arg);
if (ret)
return -ENOMEM;
return --max_stack > 0 ? get_entries(&ui, cb, arg, max_stack) : 0; return get_entries(&ui, cb, arg, max_stack);
} }
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