Developer stuff:
. Improve callchain processing by removing unnecessary work. (Frederic Weisbecker) . Fix comm override error handling (Frederic Weisbecker) . Improve 'perf probe' exit path, release resources (Masami Hiramatsu) . Improve libtraceevent plugins exit path, allowing the registering of an unregister handler to be called at exit time (Namhyung Kim) . Add an alias to the build test makefile (make -C tools/perf build-test) (Namhyung Kim) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.15 (GNU/Linux) iQIcBAABAgAGBQJS2UPMAAoJENZQFvNTUqpAH8IP/RNIWdfG729wY4RLw+OHIqFZ 02RwiZTDY1CI3DUC2FbbH1QmvN9tGaEy3eo35f4U+5yCPp0dGG2r0vxu3KdNcryd KHfpMtQUio3rBPyul4UW5CMlmMSs3eaUuqSVdXrZKCE4/SlqrGQ2SWpqDZhnR4AY 9uP1WMjLZJwoyEyx4c/23UcocW21O+JzzLBeVdr/OjkKgRcFISTDflEaxMe+l4vf DfVCWAd7BhqiCHLnebsLsBuWnGqLzFzYFx4khjwmTOmof5DCArEgR1IcdQmrRv8P amQlxy4kdOMa/p1BLvORCj8zx/dGsNrjIO3SOYVKct2SMAODMgx/G9wGxOOIJCBp jJtQOoOrii4ajbT0tDoGccni5/uGIYZRNV2WvQM2F3xC1oyGyT1/7K3lpuRFsi1Z avNsqmnmAdS2SF0Q1xRaFVYRmhpBSwysmSx0nmSzb0NaOYMBVcTnuppMBUvZY0gn Q+A2Q3UxzCzH4PgmMZgd+edkbNPYGWQGV19Vb0M3U9SV/ryqj6I/xp6VGiGUx5JD XXF8xUnXG/gV3qx05qvZKgHQWAenzo2phrQFhFvgHDjzt1RZUhFdhIzIHPBF1PNR 1Qlx0hESExZFcxbzC3/TUZMD1/tSNr4pFeZQM/I7aAFlIEBVCZSxRgnlor1zuH7X gZH4IMxCF6VwSSiiBGfh =JnA5 -----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 improvements and fixes from Arnaldo Carvalho de Melo: Infrastructure changes: * Improve callchain processing by removing unnecessary work. (Frederic Weisbecker) * Fix comm override error handling (Frederic Weisbecker) * Improve 'perf probe' exit path, release resources (Masami Hiramatsu) * Improve libtraceevent plugins exit path, allowing the registering of an unregister handler to be called at exit time (Namhyung Kim) * Add an alias to the build test makefile (make -C tools/perf build-test) (Namhyung Kim) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
45e6af0636
|
@ -5560,6 +5560,52 @@ int pevent_register_print_function(struct pevent *pevent,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* pevent_unregister_print_function - unregister a helper function
|
||||
* @pevent: the handle to the pevent
|
||||
* @func: the function to process the helper function
|
||||
* @name: the name of the helper function
|
||||
*
|
||||
* This function removes existing print handler for function @name.
|
||||
*
|
||||
* Returns 0 if the handler was removed successully, -1 otherwise.
|
||||
*/
|
||||
int pevent_unregister_print_function(struct pevent *pevent,
|
||||
pevent_func_handler func, char *name)
|
||||
{
|
||||
struct pevent_function_handler *func_handle;
|
||||
|
||||
func_handle = find_func_handler(pevent, name);
|
||||
if (func_handle && func_handle->func == func) {
|
||||
remove_func_handler(pevent, name);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct event_format *pevent_search_event(struct pevent *pevent, int id,
|
||||
const char *sys_name,
|
||||
const char *event_name)
|
||||
{
|
||||
struct event_format *event;
|
||||
|
||||
if (id >= 0) {
|
||||
/* search by id */
|
||||
event = pevent_find_event(pevent, id);
|
||||
if (!event)
|
||||
return NULL;
|
||||
if (event_name && (strcmp(event_name, event->name) != 0))
|
||||
return NULL;
|
||||
if (sys_name && (strcmp(sys_name, event->system) != 0))
|
||||
return NULL;
|
||||
} else {
|
||||
event = pevent_find_event_by_name(pevent, sys_name, event_name);
|
||||
if (!event)
|
||||
return NULL;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* pevent_register_event_handler - register a way to parse an event
|
||||
* @pevent: the handle to the pevent
|
||||
|
@ -5584,20 +5630,9 @@ int pevent_register_event_handler(struct pevent *pevent, int id,
|
|||
struct event_format *event;
|
||||
struct event_handler *handle;
|
||||
|
||||
if (id >= 0) {
|
||||
/* search by id */
|
||||
event = pevent_find_event(pevent, id);
|
||||
if (!event)
|
||||
goto not_found;
|
||||
if (event_name && (strcmp(event_name, event->name) != 0))
|
||||
goto not_found;
|
||||
if (sys_name && (strcmp(sys_name, event->system) != 0))
|
||||
goto not_found;
|
||||
} else {
|
||||
event = pevent_find_event_by_name(pevent, sys_name, event_name);
|
||||
if (!event)
|
||||
goto not_found;
|
||||
}
|
||||
event = pevent_search_event(pevent, id, sys_name, event_name);
|
||||
if (event == NULL)
|
||||
goto not_found;
|
||||
|
||||
pr_stat("overriding event (%d) %s:%s with new print handler",
|
||||
event->id, event->system, event->name);
|
||||
|
@ -5637,6 +5672,79 @@ int pevent_register_event_handler(struct pevent *pevent, int id,
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int handle_matches(struct event_handler *handler, int id,
|
||||
const char *sys_name, const char *event_name,
|
||||
pevent_event_handler_func func, void *context)
|
||||
{
|
||||
if (id >= 0 && id != handler->id)
|
||||
return 0;
|
||||
|
||||
if (event_name && (strcmp(event_name, handler->event_name) != 0))
|
||||
return 0;
|
||||
|
||||
if (sys_name && (strcmp(sys_name, handler->sys_name) != 0))
|
||||
return 0;
|
||||
|
||||
if (func != handler->func || context != handler->context)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* pevent_unregister_event_handler - unregister an existing event handler
|
||||
* @pevent: the handle to the pevent
|
||||
* @id: the id of the event to unregister
|
||||
* @sys_name: the system name the handler belongs to
|
||||
* @event_name: the name of the event handler
|
||||
* @func: the function to call to parse the event information
|
||||
* @context: the data to be passed to @func
|
||||
*
|
||||
* This function removes existing event handler (parser).
|
||||
*
|
||||
* If @id is >= 0, then it is used to find the event.
|
||||
* else @sys_name and @event_name are used.
|
||||
*
|
||||
* Returns 0 if handler was removed successfully, -1 if event was not found.
|
||||
*/
|
||||
int pevent_unregister_event_handler(struct pevent *pevent, int id,
|
||||
const char *sys_name, const char *event_name,
|
||||
pevent_event_handler_func func, void *context)
|
||||
{
|
||||
struct event_format *event;
|
||||
struct event_handler *handle;
|
||||
struct event_handler **next;
|
||||
|
||||
event = pevent_search_event(pevent, id, sys_name, event_name);
|
||||
if (event == NULL)
|
||||
goto not_found;
|
||||
|
||||
if (event->handler == func && event->context == context) {
|
||||
pr_stat("removing override handler for event (%d) %s:%s. Going back to default handler.",
|
||||
event->id, event->system, event->name);
|
||||
|
||||
event->handler = NULL;
|
||||
event->context = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
not_found:
|
||||
for (next = &pevent->handlers; *next; next = &(*next)->next) {
|
||||
handle = *next;
|
||||
if (handle_matches(handle, id, sys_name, event_name,
|
||||
func, context))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(*next))
|
||||
return -1;
|
||||
|
||||
*next = handle->next;
|
||||
free_handler(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pevent_alloc - create a pevent handle
|
||||
*/
|
||||
|
|
|
@ -624,10 +624,15 @@ int pevent_print_func_field(struct trace_seq *s, const char *fmt,
|
|||
int pevent_register_event_handler(struct pevent *pevent, int id,
|
||||
const char *sys_name, const char *event_name,
|
||||
pevent_event_handler_func func, void *context);
|
||||
int pevent_unregister_event_handler(struct pevent *pevent, int id,
|
||||
const char *sys_name, const char *event_name,
|
||||
pevent_event_handler_func func, void *context);
|
||||
int pevent_register_print_function(struct pevent *pevent,
|
||||
pevent_func_handler func,
|
||||
enum pevent_func_arg_type ret_type,
|
||||
char *name, ...);
|
||||
int pevent_unregister_print_function(struct pevent *pevent,
|
||||
pevent_func_handler func, char *name);
|
||||
|
||||
struct format_field *pevent_find_common_field(struct event_format *event, const char *name);
|
||||
struct format_field *pevent_find_field(struct event_format *event, const char *name);
|
||||
|
|
|
@ -22,3 +22,9 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
|
|||
PEVENT_FUNC_ARG_VOID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
|
||||
{
|
||||
pevent_unregister_print_function(pevent, process___le16_to_cpup,
|
||||
"__le16_to_cpup");
|
||||
}
|
||||
|
|
|
@ -148,6 +148,9 @@ void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
|
|||
{
|
||||
int i, x;
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "ftrace", "function",
|
||||
function_handler, NULL);
|
||||
|
||||
for (i = 0; i <= cpus; i++) {
|
||||
for (x = 0; x < fstack[i].size && fstack[i].stack[x]; x++)
|
||||
free(fstack[i].stack[x]);
|
||||
|
|
|
@ -76,3 +76,13 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
|
|||
timer_start_handler, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
|
||||
{
|
||||
pevent_unregister_event_handler(pevent, -1,
|
||||
"timer", "hrtimer_expire_entry",
|
||||
timer_expire_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "timer", "hrtimer_start",
|
||||
timer_start_handler, NULL);
|
||||
}
|
||||
|
|
|
@ -66,3 +66,12 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
|
|||
PEVENT_FUNC_ARG_VOID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
|
||||
{
|
||||
pevent_unregister_print_function(pevent, process_jbd2_dev_to_name,
|
||||
"jbd2_dev_to_name");
|
||||
|
||||
pevent_unregister_print_function(pevent, process_jiffies_to_msecs,
|
||||
"jiffies_to_msecs");
|
||||
}
|
||||
|
|
|
@ -70,3 +70,25 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
|
|||
call_site_handler, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
|
||||
{
|
||||
pevent_unregister_event_handler(pevent, -1, "kmem", "kfree",
|
||||
call_site_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "kmem", "kmalloc",
|
||||
call_site_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "kmem", "kmalloc_node",
|
||||
call_site_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "kmem", "kmem_cache_alloc",
|
||||
call_site_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "kmem",
|
||||
"kmem_cache_alloc_node",
|
||||
call_site_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "kmem", "kmem_cache_free",
|
||||
call_site_handler, NULL);
|
||||
}
|
||||
|
|
|
@ -434,3 +434,32 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
|
|||
PEVENT_FUNC_ARG_VOID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
|
||||
{
|
||||
pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_exit",
|
||||
kvm_exit_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_emulate_insn",
|
||||
kvm_emulate_insn_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_get_page",
|
||||
kvm_mmu_get_page_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_sync_page",
|
||||
kvm_mmu_print_role, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1,
|
||||
"kvmmmu", "kvm_mmu_unsync_page",
|
||||
kvm_mmu_print_role, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_zap_page",
|
||||
kvm_mmu_print_role, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "kvmmmu",
|
||||
"kvm_mmu_prepare_zap_page", kvm_mmu_print_role,
|
||||
NULL);
|
||||
|
||||
pevent_unregister_print_function(pevent, process_is_writable_pte,
|
||||
"is_writable_pte");
|
||||
}
|
||||
|
|
|
@ -93,3 +93,10 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
|
|||
drv_bss_info_changed, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
|
||||
{
|
||||
pevent_unregister_event_handler(pevent, -1, "mac80211",
|
||||
"drv_bss_info_changed",
|
||||
drv_bss_info_changed, NULL);
|
||||
}
|
||||
|
|
|
@ -146,3 +146,15 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
|
|||
sched_wakeup_handler, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
|
||||
{
|
||||
pevent_unregister_event_handler(pevent, -1, "sched", "sched_switch",
|
||||
sched_switch_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "sched", "sched_wakeup",
|
||||
sched_wakeup_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "sched", "sched_wakeup_new",
|
||||
sched_wakeup_handler, NULL);
|
||||
}
|
||||
|
|
|
@ -421,3 +421,9 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
|
|||
PEVENT_FUNC_ARG_VOID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
|
||||
{
|
||||
pevent_unregister_print_function(pevent, process_scsi_trace_parse_cdb,
|
||||
"scsi_trace_parse_cdb");
|
||||
}
|
||||
|
|
|
@ -128,3 +128,9 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
|
|||
PEVENT_FUNC_ARG_VOID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
|
||||
{
|
||||
pevent_unregister_print_function(pevent, process_xen_hypercall_name,
|
||||
"xen_hypercall_name");
|
||||
}
|
||||
|
|
|
@ -74,6 +74,12 @@ all tags TAGS:
|
|||
clean:
|
||||
$(make)
|
||||
|
||||
#
|
||||
# The build-test target is not really parallel, don't print the jobs info:
|
||||
#
|
||||
build-test:
|
||||
@$(MAKE) -f tests/make --no-print-directory
|
||||
|
||||
#
|
||||
# All other targets get passed through:
|
||||
#
|
||||
|
|
|
@ -59,7 +59,7 @@ static struct {
|
|||
struct perf_probe_event events[MAX_PROBES];
|
||||
struct strlist *dellist;
|
||||
struct line_range line_range;
|
||||
const char *target;
|
||||
char *target;
|
||||
int max_probe_points;
|
||||
struct strfilter *filter;
|
||||
} params;
|
||||
|
@ -98,7 +98,10 @@ static int set_target(const char *ptr)
|
|||
* short module name.
|
||||
*/
|
||||
if (!params.target && ptr && *ptr == '/') {
|
||||
params.target = ptr;
|
||||
params.target = strdup(ptr);
|
||||
if (!params.target)
|
||||
return -ENOMEM;
|
||||
|
||||
found = 1;
|
||||
buf = ptr + (strlen(ptr) - 3);
|
||||
|
||||
|
@ -116,6 +119,9 @@ static int parse_probe_event_argv(int argc, const char **argv)
|
|||
char *buf;
|
||||
|
||||
found_target = set_target(argv[0]);
|
||||
if (found_target < 0)
|
||||
return found_target;
|
||||
|
||||
if (found_target && argc == 1)
|
||||
return 0;
|
||||
|
||||
|
@ -217,7 +223,6 @@ static int opt_show_lines(const struct option *opt __maybe_unused,
|
|||
|
||||
params.show_lines = true;
|
||||
ret = parse_line_range_desc(str, ¶ms.line_range);
|
||||
INIT_LIST_HEAD(¶ms.line_range.line_list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -263,7 +268,28 @@ static int opt_set_filter(const struct option *opt __maybe_unused,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
static void init_params(void)
|
||||
{
|
||||
line_range__init(¶ms.line_range);
|
||||
}
|
||||
|
||||
static void cleanup_params(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < params.nevents; i++)
|
||||
clear_perf_probe_event(params.events + i);
|
||||
if (params.dellist)
|
||||
strlist__delete(params.dellist);
|
||||
line_range__clear(¶ms.line_range);
|
||||
free(params.target);
|
||||
if (params.filter)
|
||||
strfilter__delete(params.filter);
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
}
|
||||
|
||||
static int
|
||||
__cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
const char * const probe_usage[] = {
|
||||
"perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
|
||||
|
@ -417,6 +443,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
ret = show_available_funcs(params.target, params.filter,
|
||||
params.uprobes);
|
||||
strfilter__delete(params.filter);
|
||||
params.filter = NULL;
|
||||
if (ret < 0)
|
||||
pr_err(" Error: Failed to show functions."
|
||||
" (%d)\n", ret);
|
||||
|
@ -456,6 +483,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
params.filter,
|
||||
params.show_ext_vars);
|
||||
strfilter__delete(params.filter);
|
||||
params.filter = NULL;
|
||||
if (ret < 0)
|
||||
pr_err(" Error: Failed to show vars. (%d)\n", ret);
|
||||
return ret;
|
||||
|
@ -464,7 +492,6 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
|
||||
if (params.dellist) {
|
||||
ret = del_perf_probe_events(params.dellist);
|
||||
strlist__delete(params.dellist);
|
||||
if (ret < 0) {
|
||||
pr_err(" Error: Failed to delete events. (%d)\n", ret);
|
||||
return ret;
|
||||
|
@ -483,3 +510,14 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_probe(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int ret;
|
||||
|
||||
init_params();
|
||||
ret = __cmd_probe(argc, argv, prefix);
|
||||
cleanup_params();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "asm/bug.h"
|
||||
|
||||
#include "hist.h"
|
||||
#include "util.h"
|
||||
#include "sort.h"
|
||||
|
@ -358,19 +360,14 @@ append_chain_children(struct callchain_node *root,
|
|||
/* lookup in childrens */
|
||||
while (*p) {
|
||||
s64 ret;
|
||||
struct callchain_list *cnode;
|
||||
|
||||
parent = *p;
|
||||
rnode = rb_entry(parent, struct callchain_node, rb_node_in);
|
||||
cnode = list_first_entry(&rnode->val, struct callchain_list,
|
||||
list);
|
||||
|
||||
/* just check first entry */
|
||||
ret = match_chain(node, cnode);
|
||||
if (ret == 0) {
|
||||
append_chain(rnode, cursor, period);
|
||||
/* If at least first entry matches, rely to children */
|
||||
ret = append_chain(rnode, cursor, period);
|
||||
if (ret == 0)
|
||||
goto inc_children_hit;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
p = &parent->rb_left;
|
||||
|
@ -391,11 +388,11 @@ append_chain(struct callchain_node *root,
|
|||
struct callchain_cursor *cursor,
|
||||
u64 period)
|
||||
{
|
||||
struct callchain_cursor_node *curr_snap = cursor->curr;
|
||||
struct callchain_list *cnode;
|
||||
u64 start = cursor->pos;
|
||||
bool found = false;
|
||||
u64 matches;
|
||||
int cmp = 0;
|
||||
|
||||
/*
|
||||
* Lookup in the current node
|
||||
|
@ -410,7 +407,8 @@ append_chain(struct callchain_node *root,
|
|||
if (!node)
|
||||
break;
|
||||
|
||||
if (match_chain(node, cnode) != 0)
|
||||
cmp = match_chain(node, cnode);
|
||||
if (cmp)
|
||||
break;
|
||||
|
||||
found = true;
|
||||
|
@ -420,9 +418,8 @@ append_chain(struct callchain_node *root,
|
|||
|
||||
/* matches not, relay no the parent */
|
||||
if (!found) {
|
||||
cursor->curr = curr_snap;
|
||||
cursor->pos = start;
|
||||
return -1;
|
||||
WARN_ONCE(!cmp, "Chain comparison error\n");
|
||||
return cmp;
|
||||
}
|
||||
|
||||
matches = cursor->pos - start;
|
||||
|
|
|
@ -94,19 +94,20 @@ struct comm *comm__new(const char *str, u64 timestamp)
|
|||
return comm;
|
||||
}
|
||||
|
||||
void comm__override(struct comm *comm, const char *str, u64 timestamp)
|
||||
int comm__override(struct comm *comm, const char *str, u64 timestamp)
|
||||
{
|
||||
struct comm_str *old = comm->comm_str;
|
||||
struct comm_str *new, *old = comm->comm_str;
|
||||
|
||||
comm->comm_str = comm_str__findnew(str, &comm_str_root);
|
||||
if (!comm->comm_str) {
|
||||
comm->comm_str = old;
|
||||
return;
|
||||
}
|
||||
new = comm_str__findnew(str, &comm_str_root);
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
comm->start = timestamp;
|
||||
comm_str__get(comm->comm_str);
|
||||
comm_str__get(new);
|
||||
comm_str__put(old);
|
||||
comm->comm_str = new;
|
||||
comm->start = timestamp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void comm__free(struct comm *comm)
|
||||
|
|
|
@ -16,6 +16,6 @@ struct comm {
|
|||
void comm__free(struct comm *comm);
|
||||
struct comm *comm__new(const char *str, u64 timestamp);
|
||||
const char *comm__str(const struct comm *comm);
|
||||
void comm__override(struct comm *comm, const char *str, u64 timestamp);
|
||||
int comm__override(struct comm *comm, const char *str, u64 timestamp);
|
||||
|
||||
#endif /* __PERF_COMM_H */
|
||||
|
|
|
@ -72,6 +72,7 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
|
|||
static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
|
||||
static int convert_name_to_addr(struct perf_probe_event *pev,
|
||||
const char *exec);
|
||||
static void clear_probe_trace_event(struct probe_trace_event *tev);
|
||||
static struct machine machine;
|
||||
|
||||
/* Initialize symbol maps and path of vmlinux/modules */
|
||||
|
@ -172,54 +173,6 @@ const char *kernel_get_module_path(const char *module)
|
|||
return (dso) ? dso->long_name : NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DWARF_SUPPORT
|
||||
/* Copied from unwind.c */
|
||||
static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
|
||||
GElf_Shdr *shp, const char *name)
|
||||
{
|
||||
Elf_Scn *sec = NULL;
|
||||
|
||||
while ((sec = elf_nextscn(elf, sec)) != NULL) {
|
||||
char *str;
|
||||
|
||||
gelf_getshdr(sec, shp);
|
||||
str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
|
||||
if (!strcmp(name, str))
|
||||
break;
|
||||
}
|
||||
|
||||
return sec;
|
||||
}
|
||||
|
||||
static int get_text_start_address(const char *exec, unsigned long *address)
|
||||
{
|
||||
Elf *elf;
|
||||
GElf_Ehdr ehdr;
|
||||
GElf_Shdr shdr;
|
||||
int fd, ret = -ENOENT;
|
||||
|
||||
fd = open(exec, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
|
||||
if (elf == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (gelf_getehdr(elf, &ehdr) == NULL)
|
||||
goto out;
|
||||
|
||||
if (!elf_section_by_name(elf, &ehdr, &shdr, ".text"))
|
||||
goto out;
|
||||
|
||||
*address = shdr.sh_addr - shdr.sh_offset;
|
||||
ret = 0;
|
||||
out:
|
||||
elf_end(elf);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int init_user_exec(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -340,6 +293,34 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int get_text_start_address(const char *exec, unsigned long *address)
|
||||
{
|
||||
Elf *elf;
|
||||
GElf_Ehdr ehdr;
|
||||
GElf_Shdr shdr;
|
||||
int fd, ret = -ENOENT;
|
||||
|
||||
fd = open(exec, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
|
||||
if (elf == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (gelf_getehdr(elf, &ehdr) == NULL)
|
||||
goto out;
|
||||
|
||||
if (!elf_section_by_name(elf, &ehdr, &shdr, ".text", NULL))
|
||||
goto out;
|
||||
|
||||
*address = shdr.sh_addr - shdr.sh_offset;
|
||||
ret = 0;
|
||||
out:
|
||||
elf_end(elf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
|
||||
int ntevs, const char *exec)
|
||||
{
|
||||
|
@ -407,6 +388,14 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ntevs; i++)
|
||||
clear_probe_trace_event(tevs + i);
|
||||
}
|
||||
|
||||
/* Try to find perf_probe_event with debuginfo */
|
||||
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
|
||||
struct probe_trace_event **tevs,
|
||||
|
@ -442,6 +431,10 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
|
|||
ret = add_module_to_probe_trace_events(*tevs,
|
||||
ntevs, target);
|
||||
}
|
||||
if (ret < 0) {
|
||||
clear_probe_trace_events(*tevs, ntevs);
|
||||
zfree(tevs);
|
||||
}
|
||||
return ret < 0 ? ret : ntevs;
|
||||
}
|
||||
|
||||
|
@ -781,6 +774,28 @@ int show_available_vars(struct perf_probe_event *pevs __maybe_unused,
|
|||
}
|
||||
#endif
|
||||
|
||||
void line_range__clear(struct line_range *lr)
|
||||
{
|
||||
struct line_node *ln;
|
||||
|
||||
free(lr->function);
|
||||
free(lr->file);
|
||||
free(lr->path);
|
||||
free(lr->comp_dir);
|
||||
while (!list_empty(&lr->line_list)) {
|
||||
ln = list_first_entry(&lr->line_list, struct line_node, list);
|
||||
list_del(&ln->list);
|
||||
free(ln);
|
||||
}
|
||||
memset(lr, 0, sizeof(*lr));
|
||||
}
|
||||
|
||||
void line_range__init(struct line_range *lr)
|
||||
{
|
||||
memset(lr, 0, sizeof(*lr));
|
||||
INIT_LIST_HEAD(&lr->line_list);
|
||||
}
|
||||
|
||||
static int parse_line_num(char **ptr, int *val, const char *what)
|
||||
{
|
||||
const char *start = *ptr;
|
||||
|
|
|
@ -120,6 +120,12 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev);
|
|||
/* Command string to line-range */
|
||||
extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
|
||||
|
||||
/* Release line range members */
|
||||
extern void line_range__clear(struct line_range *lr);
|
||||
|
||||
/* Initialize line range */
|
||||
extern void line_range__init(struct line_range *lr);
|
||||
|
||||
/* Internal use: Return kernel/module path */
|
||||
extern const char *kernel_get_module_path(const char *module);
|
||||
|
||||
|
|
|
@ -136,9 +136,8 @@ static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
|
||||
GElf_Shdr *shp, const char *name,
|
||||
size_t *idx)
|
||||
Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
|
||||
GElf_Shdr *shp, const char *name, size_t *idx)
|
||||
{
|
||||
Elf_Scn *sec = NULL;
|
||||
size_t cnt = 1;
|
||||
|
|
|
@ -52,6 +52,11 @@ static inline char *bfd_demangle(void __maybe_unused *v,
|
|||
# define PERF_ELF_C_READ_MMAP ELF_C_READ
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBELF_SUPPORT
|
||||
extern Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
|
||||
GElf_Shdr *shp, const char *name, size_t *idx);
|
||||
#endif
|
||||
|
||||
#ifndef DMGL_PARAMS
|
||||
#define DMGL_PARAMS (1 << 0) /* Include function args */
|
||||
#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
|
||||
|
|
|
@ -66,10 +66,13 @@ struct comm *thread__comm(const struct thread *thread)
|
|||
int thread__set_comm(struct thread *thread, const char *str, u64 timestamp)
|
||||
{
|
||||
struct comm *new, *curr = thread__comm(thread);
|
||||
int err;
|
||||
|
||||
/* Override latest entry if it had no specific time coverage */
|
||||
if (!curr->start) {
|
||||
comm__override(curr, str, timestamp);
|
||||
err = comm__override(curr, str, timestamp);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
new = comm__new(str, timestamp);
|
||||
if (!new)
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "session.h"
|
||||
#include "perf_regs.h"
|
||||
#include "unwind.h"
|
||||
#include "symbol.h"
|
||||
#include "util.h"
|
||||
|
||||
extern int
|
||||
|
@ -158,23 +159,6 @@ static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
|
|||
__v; \
|
||||
})
|
||||
|
||||
static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
|
||||
GElf_Shdr *shp, const char *name)
|
||||
{
|
||||
Elf_Scn *sec = NULL;
|
||||
|
||||
while ((sec = elf_nextscn(elf, sec)) != NULL) {
|
||||
char *str;
|
||||
|
||||
gelf_getshdr(sec, shp);
|
||||
str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
|
||||
if (!strcmp(name, str))
|
||||
break;
|
||||
}
|
||||
|
||||
return sec;
|
||||
}
|
||||
|
||||
static u64 elf_section_offset(int fd, const char *name)
|
||||
{
|
||||
Elf *elf;
|
||||
|
@ -190,7 +174,7 @@ static u64 elf_section_offset(int fd, const char *name)
|
|||
if (gelf_getehdr(elf, &ehdr) == NULL)
|
||||
break;
|
||||
|
||||
if (!elf_section_by_name(elf, &ehdr, &shdr, name))
|
||||
if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
|
||||
break;
|
||||
|
||||
offset = shdr.sh_offset;
|
||||
|
|
Loading…
Reference in a new issue