mirror of https://gitee.com/openkylin/linux.git
perf/core improvements and fixes:
User visible: - 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: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWU4omAAoJENZQFvNTUqpAltYP/A/Z6HZyTpLgGG2IjUqF9cpP z7R2wz62MMeqMgub1lnWMUDi2rvnb0Bm5i72LVy2RprmLU6xjym8451uuu5t0CUa EErta0gTbUK6+SnND+HstxGigHZ9ZNppL8MGFjwvsCvLDtOeZEsyNBPOtkeNmzHW qjBUCkbusGsLa7TuYSJaTGukpUnEoTQHO5FutIOnI+jx1HhbxmDA4VKIiVw2HtJc LlxlinKRFj+JQklB5oL2hELm4IDyoeLZ36RVmnnRxASFuU+1BCyuRwS6D2Q/mDoY KtY6s3dHG/fNCgmK64QD+/KxoBIuEJ/BMCfabl6Xs7+BBBu6HhUtH6CPIbvANr7g 2N9luv7/dYFsgWmBUar0f0o8oVhPWD6uMHMiy55Jo+34gqYtdJQg9q0pUY4dz/BS QzluaGmPM78bAiCGZf7+MXuODIthBJi4Gec5Tfk5KdY8pIf+NZaR1PN/GD6i48Mx FV83Nuk3tNqF5tBOc9Zi7it9anqRfnPjhM+UgVh2XGBl5SO99tg0UKBnLdVZFYL1 LCyP8xRUao4EkuXmKJEfyA/ofIQGJJsUfdDGA0LRPxGaIfgIqm3fgAeyvKjfIeb+ +RzKSDkUs0M9H9isafwRt6TA7zx4wlo+2WlFlAxFZBRL6UOgrbl2C0SY6ldpbXKo p6Jr4gWSmyQzgOiUtDsJ =7CW1 -----END PGP SIGNATURE----- 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: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
9327ca7347
|
@ -1,5 +1,6 @@
|
|||
perf-y += builtin-bench.o
|
||||
perf-y += builtin-annotate.o
|
||||
perf-y += builtin-config.o
|
||||
perf-y += builtin-diff.o
|
||||
perf-y += builtin-evlist.o
|
||||
perf-y += builtin-help.o
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
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]
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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);
|
|||
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_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_evlist(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
|
|||
perf-buildid-list mainporcelain common
|
||||
perf-data mainporcelain common
|
||||
perf-diff mainporcelain common
|
||||
perf-config mainporcelain common
|
||||
perf-evlist mainporcelain common
|
||||
perf-inject mainporcelain common
|
||||
perf-kmem mainporcelain common
|
||||
|
|
|
@ -39,6 +39,7 @@ struct cmd_struct {
|
|||
static struct cmd_struct commands[] = {
|
||||
{ "buildid-cache", cmd_buildid_cache, 0 },
|
||||
{ "buildid-list", cmd_buildid_list, 0 },
|
||||
{ "config", cmd_config, 0 },
|
||||
{ "diff", cmd_diff, 0 },
|
||||
{ "evlist", cmd_evlist, 0 },
|
||||
{ "help", cmd_help, 0 },
|
||||
|
|
|
@ -51,6 +51,12 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
|
|||
"krava_1",
|
||||
"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) {
|
||||
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)
|
|||
return -1;
|
||||
}
|
||||
|
||||
pr_debug("got: %s 0x%" PRIx64 "\n", symbol, entry->ip);
|
||||
return strcmp((const char *) symbol, funcs[(*cnt)++]);
|
||||
(*cnt)++;
|
||||
pr_debug("got: %s 0x%" PRIx64 ", expecting %s\n",
|
||||
symbol, entry->ip, funcs[idx]);
|
||||
return strcmp((const char *) symbol, funcs[idx]);
|
||||
}
|
||||
|
||||
__attribute__ ((noinline))
|
||||
|
@ -105,8 +113,16 @@ static int compare(void *p1, void *p2)
|
|||
/* Any possible value should be 'thread' */
|
||||
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);
|
||||
if (!global_unwind_retval) {
|
||||
callchain_param.order = ORDER_CALLEE;
|
||||
global_unwind_retval = unwind_thread(thread);
|
||||
}
|
||||
}
|
||||
|
||||
return p1 - p2;
|
||||
}
|
||||
|
|
|
@ -143,6 +143,7 @@ extern __thread struct callchain_cursor callchain_cursor;
|
|||
static inline void callchain_init(struct callchain_root *root)
|
||||
{
|
||||
INIT_LIST_HEAD(&root->node.val);
|
||||
INIT_LIST_HEAD(&root->node.parent_val);
|
||||
|
||||
root->node.parent = NULL;
|
||||
root->node.hit = 0;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/types.h>
|
||||
#include "event.h"
|
||||
#include "perf_regs.h"
|
||||
#include "callchain.h"
|
||||
|
||||
static char *debuginfo_path;
|
||||
|
||||
|
@ -52,25 +53,28 @@ static int report_module(u64 ip, struct unwind_info *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)
|
||||
|
||||
{
|
||||
struct unwind_entry e;
|
||||
struct unwind_entry *e = &ui->entries[ui->idx++];
|
||||
struct addr_location al;
|
||||
|
||||
if (__report_module(&al, ip, ui))
|
||||
return -1;
|
||||
|
||||
e.ip = ip;
|
||||
e.map = al.map;
|
||||
e.sym = al.sym;
|
||||
e->ip = ip;
|
||||
e->map = al.map;
|
||||
e->sym = al.sym;
|
||||
|
||||
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
|
||||
al.sym ? al.sym->name : "''",
|
||||
ip,
|
||||
al.map ? al.map->map_ip(al.map, ip) : (u64) 0);
|
||||
|
||||
return ui->cb(&e, ui->arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
|||
struct perf_sample *data,
|
||||
int max_stack)
|
||||
{
|
||||
struct unwind_info ui = {
|
||||
struct unwind_info *ui, ui_buf = {
|
||||
.sample = data,
|
||||
.thread = thread,
|
||||
.machine = thread->mg->machine,
|
||||
|
@ -177,35 +181,54 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
|
|||
.max_stack = max_stack,
|
||||
};
|
||||
Dwarf_Word ip;
|
||||
int err = -EINVAL;
|
||||
int err = -EINVAL, i;
|
||||
|
||||
if (!data->user_regs.regs)
|
||||
return -EINVAL;
|
||||
|
||||
ui.dwfl = dwfl_begin(&offline_callbacks);
|
||||
if (!ui.dwfl)
|
||||
ui = zalloc(sizeof(ui_buf) + sizeof(ui_buf.entries[0]) * max_stack);
|
||||
if (!ui)
|
||||
return -ENOMEM;
|
||||
|
||||
*ui = ui_buf;
|
||||
|
||||
ui->dwfl = dwfl_begin(&offline_callbacks);
|
||||
if (!ui->dwfl)
|
||||
goto out;
|
||||
|
||||
err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = report_module(ip, &ui);
|
||||
err = report_module(ip, ui);
|
||||
if (err)
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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:
|
||||
if (err)
|
||||
pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1));
|
||||
|
||||
dwfl_end(ui.dwfl);
|
||||
dwfl_end(ui->dwfl);
|
||||
free(ui);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ struct unwind_info {
|
|||
unwind_entry_cb_t cb;
|
||||
void *arg;
|
||||
int max_stack;
|
||||
int idx;
|
||||
struct unwind_entry entries[];
|
||||
};
|
||||
|
||||
#endif /* __PERF_UNWIND_LIBDW_H */
|
||||
|
|
|
@ -614,23 +614,48 @@ void unwind__finish_access(struct thread *thread)
|
|||
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
|
||||
void *arg, int max_stack)
|
||||
{
|
||||
u64 val;
|
||||
unw_word_t ips[max_stack];
|
||||
unw_addr_space_t addr_space;
|
||||
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)
|
||||
display_error(ret);
|
||||
return ret;
|
||||
|
||||
while (!ret && (unw_step(&c) > 0) && max_stack--) {
|
||||
unw_word_t ip;
|
||||
ips[i++] = (unw_word_t) val;
|
||||
|
||||
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;
|
||||
|
@ -640,24 +665,17 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
|
|||
struct thread *thread,
|
||||
struct perf_sample *data, int max_stack)
|
||||
{
|
||||
u64 ip;
|
||||
struct unwind_info ui = {
|
||||
.sample = data,
|
||||
.thread = thread,
|
||||
.machine = thread->mg->machine,
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (!data->user_regs.regs)
|
||||
return -EINVAL;
|
||||
|
||||
ret = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (max_stack <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue