From fc8ed7be738ffb1b3b0140ed2de6def38b9a7101 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Aug 2011 12:42:26 -0300 Subject: perf top browser: Remove spurious helpline update It will be immediately replaced in perf_top_browser__run. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-q7e2jzb44elqpkvdllk94x0i@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ui/browsers/top.c | 1 - 1 file changed, 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c index 5a06538532af..88403cf8396a 100644 --- a/tools/perf/util/ui/browsers/top.c +++ b/tools/perf/util/ui/browsers/top.c @@ -208,6 +208,5 @@ int perf_top__tui_browser(struct perf_top *top) }, }; - ui_helpline__push("Press <- or ESC to exit"); return perf_top_browser__run(&browser); } -- cgit v1.2.3 From f57b05ed532ccf3b3e22878a5678ca10de50ad29 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 1 Jun 2011 21:43:46 +0200 Subject: perf report: Use properly build_id kernel binaries If we bring the recorded perf data together with kernel binary from another machine using: on server A: perf archive on server B: tar xjvf perf.data.tar.bz2 -C ~/.debug the build_id kernel dso is not properly recognized during the "perf report" command on server B. The reason is, that build_id dsos are added during the session initialization, while the kernel maps are created during the sample event processing. The machine__create_kernel_maps functions ends up creating new dso object for kernel, but it does not check if we already have one added by build_id processing. Also the build_id reading ABI quirk added in commit: - commit b25114817a73bbd2b84ce9dba02ee1ef8989a947 perf build-id: Add quirk to deal with perf.data file format breakage populates the "struct build_id_event::pid" with 0, which is later interpreted as DEFAULT_GUEST_KERNEL_ID. This is not always correct, so it's better to guess the pid value based on the "struct build_id_event::header::misc" value. - Tested with data generated on x86 kernel version v2.6.34 and reported back on x86_64 current kernel. - Not tested for guest kernel case. Note the problem stays for PERF_RECORD_MMAP events recorded by perf that does not use proper pid (HOST_KERNEL_ID/DEFAULT_GUEST_KERNEL_ID). They are misinterpreted within the current perf code. Probably there's not much we can do about that. Cc: Avi Kivity Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Yanmin Zhang Link: http://lkml.kernel.org/r/20110601194346.GB1934@jolsa.brq.redhat.com Signed-off-by: Jiri Olsa Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 11 +++++++++- tools/perf/util/symbol.c | 57 +++++++++++++++++++++++++++--------------------- tools/perf/util/symbol.h | 1 - 3 files changed, 42 insertions(+), 27 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index d4f3101773db..b6c1ad123ca9 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -726,7 +726,16 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, return -1; bev.header = old_bev.header; - bev.pid = 0; + + /* + * As the pid is the missing value, we need to fill + * it properly. The header.misc value give us nice hint. + */ + bev.pid = HOST_KERNEL_ID; + if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER || + bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL) + bev.pid = DEFAULT_GUEST_KERNEL_ID; + memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); __event_process_build_id(&bev, filename, session); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index a8b53714542a..e142c21ae9a5 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2181,27 +2181,22 @@ size_t machines__fprintf_dsos_buildid(struct rb_root *machines, return ret; } -struct dso *dso__new_kernel(const char *name) +static struct dso* +dso__kernel_findnew(struct machine *machine, const char *name, + const char *short_name, int dso_type) { - struct dso *dso = dso__new(name ?: "[kernel.kallsyms]"); - - if (dso != NULL) { - dso__set_short_name(dso, "[kernel]"); - dso->kernel = DSO_TYPE_KERNEL; - } - - return dso; -} + /* + * The kernel dso could be created by build_id processing. + */ + struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name); -static struct dso *dso__new_guest_kernel(struct machine *machine, - const char *name) -{ - char bf[PATH_MAX]; - struct dso *dso = dso__new(name ?: machine__mmap_name(machine, bf, - sizeof(bf))); + /* + * We need to run this in all cases, since during the build_id + * processing we had no idea this was the kernel dso. + */ if (dso != NULL) { - dso__set_short_name(dso, "[guest.kernel]"); - dso->kernel = DSO_TYPE_GUEST_KERNEL; + dso__set_short_name(dso, short_name); + dso->kernel = dso_type; } return dso; @@ -2219,24 +2214,36 @@ void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) dso->has_build_id = true; } -static struct dso *machine__create_kernel(struct machine *machine) +static struct dso *machine__get_kernel(struct machine *machine) { const char *vmlinux_name = NULL; struct dso *kernel; if (machine__is_host(machine)) { vmlinux_name = symbol_conf.vmlinux_name; - kernel = dso__new_kernel(vmlinux_name); + if (!vmlinux_name) + vmlinux_name = "[kernel.kallsyms]"; + + kernel = dso__kernel_findnew(machine, vmlinux_name, + "[kernel]", + DSO_TYPE_KERNEL); } else { + char bf[PATH_MAX]; + if (machine__is_default_guest(machine)) vmlinux_name = symbol_conf.default_guest_vmlinux_name; - kernel = dso__new_guest_kernel(machine, vmlinux_name); + if (!vmlinux_name) + vmlinux_name = machine__mmap_name(machine, bf, + sizeof(bf)); + + kernel = dso__kernel_findnew(machine, vmlinux_name, + "[guest.kernel]", + DSO_TYPE_GUEST_KERNEL); } - if (kernel != NULL) { + if (kernel != NULL && (!kernel->has_build_id)) dso__read_running_kernel_build_id(kernel, machine); - dsos__add(&machine->kernel_dsos, kernel); - } + return kernel; } @@ -2340,7 +2347,7 @@ void machine__destroy_kernel_maps(struct machine *machine) int machine__create_kernel_maps(struct machine *machine) { - struct dso *kernel = machine__create_kernel(machine); + struct dso *kernel = machine__get_kernel(machine); if (kernel == NULL || __machine__create_kernel_maps(machine, kernel) < 0) diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 325ee36a9d29..4f377d92e75a 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -155,7 +155,6 @@ struct dso { }; struct dso *dso__new(const char *name); -struct dso *dso__new_kernel(const char *name); void dso__delete(struct dso *dso); int dso__name_len(const struct dso *dso); -- cgit v1.2.3 From e9b52ef2228cd0bed7a4465c693a39489e2c338d Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 12 Aug 2011 00:55:37 +0400 Subject: perf: fix temporary file ownership check A file in /tmp/ might be a symlink, so lstat() should be used instead of stat(). Acked-by: Pekka Enberg Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20110811205537.GA22864@albatros Signed-off-by: Vasiliy Kulikov Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e142c21ae9a5..469c0264ed29 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1506,7 +1506,7 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { struct stat st; - if (stat(dso->name, &st) < 0) + if (lstat(dso->name, &st) < 0) return -1; if (st.st_uid && (st.st_uid != geteuid())) { -- cgit v1.2.3 From 8afa2a707d3d1320df5d35966729ac5262da737d Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 11 Aug 2011 20:02:29 +0900 Subject: perf probe: Fix a memory leak for scopes array Fix a memory leak for scopes array when it finds a variable in the global scope. Reviewed-by: Pekka Enberg Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20110811110229.19900.63019.stgit@fedora15 Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 3e44a3e36519..573c72363223 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -660,6 +660,7 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) else { /* Search upper class */ nscopes = dwarf_getscopes_die(sp_die, &scopes); + ret = -ENOENT; while (nscopes-- > 1) { pr_debug("Searching variables in %s\n", dwarf_diename(&scopes[nscopes])); @@ -668,14 +669,12 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) pf->pvar->var, 0, &vr_die)) { ret = convert_variable(&vr_die, pf); - goto found; + break; } } if (scopes) free(scopes); - ret = -ENOENT; } -found: if (ret < 0) pr_warning("Failed to find '%s' in this function.\n", pf->pvar->var); -- cgit v1.2.3 From a128405c6b40371c59c34b00cc66ed06285b9551 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 11 Aug 2011 20:02:35 +0900 Subject: perf probe: Fix line walker to check CU correctly Fix line walker to check whether a given DIE is CU or not. Actually this function accepts CU, subprogram and inlined_subroutine DIEs. Without this fix, perf probe always fails to analyze lines on inlined functions; $ perf probe -L pre_schedule Debuginfo analysis failed. (-2) Error: Failed to show lines. (-2) This fixes that bug, as below. $ perf probe -L pre_schedule 0 static inline void pre_schedule(struct rq *rq, struct task_struct *prev { 2 if (prev->sched_class->pre_schedule) 3 prev->sched_class->pre_schedule(rq, prev); } /* rq->lock is NOT held, but preemption is disabled */ Changes from v1: - Update against current tip tree.(Fix dwarf-aux.c) Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20110811110235.19900.20614.stgit@fedora15 Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dwarf-aux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index fddf40f30d3e..d35b454f98b8 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -439,7 +439,7 @@ static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) /** * die_walk_lines - Walk on lines inside given DIE - * @rt_die: a root DIE (CU or subprogram) + * @rt_die: a root DIE (CU, subprogram or inlined_subroutine) * @callback: callback routine * @data: user data * @@ -460,12 +460,12 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) size_t nlines, i; /* Get the CU die */ - if (dwarf_tag(rt_die) == DW_TAG_subprogram) + if (dwarf_tag(rt_die) != DW_TAG_compile_unit) cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL); else cu_die = rt_die; if (!cu_die) { - pr_debug2("Failed to get CU from subprogram\n"); + pr_debug2("Failed to get CU from given DIE.\n"); return -EINVAL; } -- cgit v1.2.3 From b0e9cb2802d4bf50955cca8a7d87cf94ebf750a5 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 11 Aug 2011 20:02:41 +0900 Subject: perf probe: Fix to search nested inlined functions in CU Fix perf probe to walk through the lines of all nested inlined function call sites and declared lines when a whole CU is passed to the line walker. The die_walk_lines() can have two different type of DIEs, subprogram (or inlined-subroutine) DIE and CU DIE. If a caller passes a subprogram DIE, this means that the walker walk on lines of given subprogram. In this case, it just needs to search on direct children of DIE tree for finding call-site information of inlined function which directly called from given subprogram. On the other hand, if a caller passes a CU DIE to the walker, this means that the walker have to walk on all lines in the source files included in given CU DIE. In this case, it has to search whole DIE trees of all subprograms to find the call-site information of all nested inlined functions. Without this patch: $ perf probe --line kernel/cpu.c:151-157 static int cpu_notify(unsigned long val, void *v) { 154 return __cpu_notify(val, v, -1, NULL); } With this: $ perf probe --line kernel/cpu.c:151-157 152 static int cpu_notify(unsigned long val, void *v) { 154 return __cpu_notify(val, v, -1, NULL); } As you can see, --line option with source line range shows the declared lines as probe-able. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20110811110241.19900.34994.stgit@fedora15 Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dwarf-aux.c | 91 +++++++++++++++++++++++++++++++++++++++------ tools/perf/util/dwarf-aux.h | 3 ++ 2 files changed, 82 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index d35b454f98b8..d9b8ad098498 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -198,6 +198,19 @@ static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, return 0; } +/* Get attribute and translate it as a sdata */ +static int die_get_attr_sdata(Dwarf_Die *tp_die, unsigned int attr_name, + Dwarf_Sword *result) +{ + Dwarf_Attribute attr; + + if (dwarf_attr(tp_die, attr_name, &attr) == NULL || + dwarf_formsdata(&attr, result) != 0) + return -ENOENT; + + return 0; +} + /** * die_is_signed_type - Check whether a type DIE is signed or not * @tp_die: a DIE of a type @@ -250,6 +263,39 @@ int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) return 0; } +/* Get the call file index number in CU DIE */ +static int die_get_call_fileno(Dwarf_Die *in_die) +{ + Dwarf_Sword idx; + + if (die_get_attr_sdata(in_die, DW_AT_call_file, &idx) == 0) + return (int)idx; + else + return -ENOENT; +} + +/** + * die_get_call_file - Get callsite file name of inlined function instance + * @in_die: a DIE of an inlined function instance + * + * Get call-site file name of @in_die. This means from which file the inline + * function is called. + */ +const char *die_get_call_file(Dwarf_Die *in_die) +{ + Dwarf_Die cu_die; + Dwarf_Files *files; + int idx; + + idx = die_get_call_fileno(in_die); + if (idx < 0 || !dwarf_diecu(in_die, &cu_die, NULL, NULL) || + dwarf_getsrcfiles(&cu_die, &files, NULL) != 0) + return NULL; + + return dwarf_filesrc(files, idx, NULL, NULL); +} + + /** * die_find_child - Generic DIE search function in DIE tree * @rt_die: a root DIE @@ -376,7 +422,7 @@ Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, /* Line walker internal parameters */ struct __line_walk_param { - const char *fname; + bool recursive; line_walk_callback_t callback; void *data; int retval; @@ -385,39 +431,56 @@ struct __line_walk_param { static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) { struct __line_walk_param *lw = data; - Dwarf_Addr addr; + Dwarf_Addr addr = 0; + const char *fname; int lineno; if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { + fname = die_get_call_file(in_die); lineno = die_get_call_lineno(in_die); - if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { - lw->retval = lw->callback(lw->fname, lineno, addr, - lw->data); + if (fname && lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { + lw->retval = lw->callback(fname, lineno, addr, lw->data); if (lw->retval != 0) return DIE_FIND_CB_END; } } - return DIE_FIND_CB_SIBLING; + if (!lw->recursive) + /* Don't need to search recursively */ + return DIE_FIND_CB_SIBLING; + + if (addr) { + fname = dwarf_decl_file(in_die); + if (fname && dwarf_decl_line(in_die, &lineno) == 0) { + lw->retval = lw->callback(fname, lineno, addr, lw->data); + if (lw->retval != 0) + return DIE_FIND_CB_END; + } + } + + /* Continue to search nested inlined function call-sites */ + return DIE_FIND_CB_CONTINUE; } /* Walk on lines of blocks included in given DIE */ -static int __die_walk_funclines(Dwarf_Die *sp_die, +static int __die_walk_funclines(Dwarf_Die *sp_die, bool recursive, line_walk_callback_t callback, void *data) { struct __line_walk_param lw = { + .recursive = recursive, .callback = callback, .data = data, .retval = 0, }; Dwarf_Die die_mem; Dwarf_Addr addr; + const char *fname; int lineno; /* Handle function declaration line */ - lw.fname = dwarf_decl_file(sp_die); - if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 && + fname = dwarf_decl_file(sp_die); + if (fname && dwarf_decl_line(sp_die, &lineno) == 0 && dwarf_entrypc(sp_die, &addr) == 0) { - lw.retval = callback(lw.fname, lineno, addr, data); + lw.retval = callback(fname, lineno, addr, data); if (lw.retval != 0) goto done; } @@ -430,7 +493,7 @@ static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) { struct __line_walk_param *lw = data; - lw->retval = __die_walk_funclines(sp_die, lw->callback, lw->data); + lw->retval = __die_walk_funclines(sp_die, true, lw->callback, lw->data); if (lw->retval != 0) return DWARF_CB_ABORT; @@ -509,7 +572,11 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) * subroutines. We have to check functions list or given function. */ if (rt_die != cu_die) - ret = __die_walk_funclines(rt_die, callback, data); + /* + * Don't need walk functions recursively, because nested + * inlined functions don't have lines of the specified DIE. + */ + ret = __die_walk_funclines(rt_die, false, callback, data); else { struct __line_walk_param param = { .callback = callback, diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index bc3b21167e70..c8e491bc133f 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -40,6 +40,9 @@ extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); /* Get callsite line number of inline-function instance */ extern int die_get_call_lineno(Dwarf_Die *in_die); +/* Get callsite file name of inlined function instance */ +extern const char *die_get_call_file(Dwarf_Die *in_die); + /* Get type die */ extern Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); -- cgit v1.2.3 From 36c0c588b9ea979b619d6ddced410f9551e4c5fa Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 11 Aug 2011 20:02:47 +0900 Subject: perf probe: Fix to walk all inline instances Fix line-range collector to walk all instances of inlined function, because some execution paths can be optimized out depending on the function argument of instances. E.g.) inline_func (arg) { if (arg) do_something; else do_another; } func_A() { inline_func(1) } func_B() { inline_func(0) } In this case, func_A may have only do_something code and func_B may have only do_another. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20110811110247.19900.93702.stgit@fedora15 Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 573c72363223..d6d57682473a 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -1393,7 +1393,13 @@ static int line_range_inline_cb(Dwarf_Die *in_die, void *data) struct dwarf_callback_param *param = data; param->retval = find_line_range_by_line(in_die, param->data); - return DWARF_CB_ABORT; /* No need to find other instances */ + + /* + * We have to check all instances of inlined function, because + * some execution paths can be optimized out depends on the + * function argument of instances + */ + return DWARF_CB_OK; } /* Search function from function name */ -- cgit v1.2.3 From 13e27d7686c457c625242fc2c20be30eef942408 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 11 Aug 2011 20:02:53 +0900 Subject: perf probe: Warn when more than one line are given Check multiple --lines option and print warning informing that only the first specified --line option is valid. Changes from the 1st post: - Accept only the first option instead of the last. - Fix warning message according to David's comment. - Mark as a bugfix. Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20110811110253.19900.96192.stgit@fedora15 Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-probe.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 5f2a5c7046df..710ae3d0a489 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -134,10 +134,18 @@ static int opt_show_lines(const struct option *opt __used, { int ret = 0; - if (str) - ret = parse_line_range_desc(str, ¶ms.line_range); - INIT_LIST_HEAD(¶ms.line_range.line_list); + if (!str) + return 0; + + if (params.show_lines) { + pr_warning("Warning: more than one --line options are" + " detected. Only the first one is valid.\n"); + return 0; + } + params.show_lines = true; + ret = parse_line_range_desc(str, ¶ms.line_range); + INIT_LIST_HEAD(¶ms.line_range.line_list); return ret; } -- cgit v1.2.3 From 221d061182b8ff5507d5768aeeecbc74f01c5dfa Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 11 Aug 2011 20:02:59 +0900 Subject: perf probe: Fix to search local variables in appropriate scope Fix perf probe to search local variables in appropriate local inlined function scope. For example, pre_schedule() has only 2 local variables, as below; $ perf probe -L pre_schedule 0 static inline void pre_schedule(struct rq *rq, struct task_struct *prev) { 2 if (prev->sched_class->pre_schedule) 3 prev->sched_class->pre_schedule(rq, prev); } However, current perf probe shows 4 local variables on pre_schedule(), because it searches variables in the caller(schedule()) scope. $ perf probe -V pre_schedule Available variables at pre_schedule @ int cpu long unsigned int* switch_count struct rq* rq struct task_struct* prev This patch fixes this issue by searching variables in the local scope of the instance of inlined function. Here is the result. $ perf probe -V pre_schedule Available variables at pre_schedule @ struct rq* rq struct task_struct* prev Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20110811110259.19900.85664.stgit@fedora15 Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dwarf-aux.c | 33 +++++++++++ tools/perf/util/dwarf-aux.h | 4 ++ tools/perf/util/probe-finder.c | 132 +++++++++++++++++++++++++++++++++-------- tools/perf/util/probe-finder.h | 2 +- 4 files changed, 144 insertions(+), 27 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index d9b8ad098498..425703a58638 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -96,6 +96,39 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr, return *lineno ?: -ENOENT; } +static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data); + +/** + * cu_walk_functions_at - Walk on function DIEs at given address + * @cu_die: A CU DIE + * @addr: An address + * @callback: A callback which called with found DIEs + * @data: A user data + * + * Walk on function DIEs at given @addr in @cu_die. Passed DIEs + * should be subprogram or inlined-subroutines. + */ +int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, + int (*callback)(Dwarf_Die *, void *), void *data) +{ + Dwarf_Die die_mem; + Dwarf_Die *sc_die; + int ret = -ENOENT; + + /* Inlined function could be recursive. Trace it until fail */ + for (sc_die = die_find_realfunc(cu_die, addr, &die_mem); + sc_die != NULL; + sc_die = die_find_child(sc_die, __die_find_inline_cb, &addr, + &die_mem)) { + ret = callback(sc_die, data); + if (ret) + break; + } + + return ret; + +} + /** * die_compare_name - Compare diename and tname * @dw_die: a DIE diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index c8e491bc133f..6f46106fd1d5 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -34,6 +34,10 @@ extern const char *cu_get_comp_dir(Dwarf_Die *cu_die); extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, const char **fname, int *lineno); +/* Walk on funcitons at given address */ +extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, + int (*callback)(Dwarf_Die *, void *), void *data); + /* Compare diename and tname */ extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index d6d57682473a..5c83b7d3d8ef 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -612,8 +612,8 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) return ret; } -/* Find a variable in a subprogram die */ -static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) +/* Find a variable in a scope DIE */ +static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) { Dwarf_Die vr_die, *scopes; char buf[32], *ptr; @@ -655,11 +655,11 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) pr_debug("Searching '%s' variable in context.\n", pf->pvar->var); /* Search child die for local variables and parameters. */ - if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die)) + if (die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) ret = convert_variable(&vr_die, pf); else { /* Search upper class */ - nscopes = dwarf_getscopes_die(sp_die, &scopes); + nscopes = dwarf_getscopes_die(sc_die, &scopes); ret = -ENOENT; while (nscopes-- > 1) { pr_debug("Searching variables in %s\n", @@ -717,26 +717,30 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, return 0; } -/* Call probe_finder callback with real subprogram DIE */ -static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) +/* Call probe_finder callback with scope DIE */ +static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) { - Dwarf_Die die_mem; Dwarf_Attribute fb_attr; size_t nops; int ret; - /* If no real subprogram, find a real one */ - if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { - sp_die = die_find_realfunc(&pf->cu_die, pf->addr, &die_mem); - if (!sp_die) { + if (!sc_die) { + pr_err("Caller must pass a scope DIE. Program error.\n"); + return -EINVAL; + } + + /* If not a real subprogram, find a real one */ + if (dwarf_tag(sc_die) != DW_TAG_subprogram) { + if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { pr_warning("Failed to find probe point in any " "functions.\n"); return -ENOENT; } - } + } else + memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); - /* Get the frame base attribute/ops */ - dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); + /* Get the frame base attribute/ops from subprogram */ + dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); if (ret <= 0 || nops == 0) { pf->fb_ops = NULL; @@ -754,7 +758,7 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) } /* Call finder's callback handler */ - ret = pf->callback(sp_die, pf); + ret = pf->callback(sc_die, pf); /* *pf->fb_ops will be cached in libdw. Don't free it. */ pf->fb_ops = NULL; @@ -762,17 +766,82 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) return ret; } +struct find_scope_param { + const char *function; + const char *file; + int line; + int diff; + Dwarf_Die *die_mem; + bool found; +}; + +static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) +{ + struct find_scope_param *fsp = data; + const char *file; + int lno; + + /* Skip if declared file name does not match */ + if (fsp->file) { + file = dwarf_decl_file(fn_die); + if (!file || strcmp(fsp->file, file) != 0) + return 0; + } + /* If the function name is given, that's what user expects */ + if (fsp->function) { + if (die_compare_name(fn_die, fsp->function)) { + memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); + fsp->found = true; + return 1; + } + } else { + /* With the line number, find the nearest declared DIE */ + dwarf_decl_line(fn_die, &lno); + if (lno < fsp->line && fsp->diff > fsp->line - lno) { + /* Keep a candidate and continue */ + fsp->diff = fsp->line - lno; + memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); + fsp->found = true; + } + } + return 0; +} + +/* Find an appropriate scope fits to given conditions */ +static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) +{ + struct find_scope_param fsp = { + .function = pf->pev->point.function, + .file = pf->fname, + .line = pf->lno, + .diff = INT_MAX, + .die_mem = die_mem, + .found = false, + }; + + cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp); + + return fsp.found ? die_mem : NULL; +} + static int probe_point_line_walker(const char *fname, int lineno, Dwarf_Addr addr, void *data) { struct probe_finder *pf = data; + Dwarf_Die *sc_die, die_mem; int ret; if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) return 0; pf->addr = addr; - ret = call_probe_finder(NULL, pf); + sc_die = find_best_scope(pf, &die_mem); + if (!sc_die) { + pr_warning("Failed to find scope of probe point.\n"); + return -ENOENT; + } + + ret = call_probe_finder(sc_die, pf); /* Continue if no error, because the line will be in inline function */ return ret < 0 ? ret : 0; @@ -826,6 +895,7 @@ static int probe_point_lazy_walker(const char *fname, int lineno, Dwarf_Addr addr, void *data) { struct probe_finder *pf = data; + Dwarf_Die *sc_die, die_mem; int ret; if (!line_list__has_line(&pf->lcache, lineno) || @@ -835,7 +905,14 @@ static int probe_point_lazy_walker(const char *fname, int lineno, pr_debug("Probe line found: line:%d addr:0x%llx\n", lineno, (unsigned long long)addr); pf->addr = addr; - ret = call_probe_finder(NULL, pf); + pf->lno = lineno; + sc_die = find_best_scope(pf, &die_mem); + if (!sc_die) { + pr_warning("Failed to find scope of probe point.\n"); + return -ENOENT; + } + + ret = call_probe_finder(sc_die, pf); /* * Continue if no error, because the lazy pattern will match @@ -1059,7 +1136,7 @@ found: } /* Add a found probe point into trace event list */ -static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) +static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) { struct trace_event_finder *tf = container_of(pf, struct trace_event_finder, pf); @@ -1074,8 +1151,9 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) } tev = &tf->tevs[tf->ntevs++]; - ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe, - &tev->point); + /* Trace point should be converted from subprogram DIE */ + ret = convert_to_trace_point(&pf->sp_die, pf->addr, + pf->pev->point.retprobe, &tev->point); if (ret < 0) return ret; @@ -1090,7 +1168,8 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) for (i = 0; i < pf->pev->nargs; i++) { pf->pvar = &pf->pev->args[i]; pf->tvar = &tev->args[i]; - ret = find_variable(sp_die, pf); + /* Variable should be found from scope DIE */ + ret = find_variable(sc_die, pf); if (ret != 0) return ret; } @@ -1158,7 +1237,7 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) } /* Add a found vars into available variables list */ -static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) +static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) { struct available_var_finder *af = container_of(pf, struct available_var_finder, pf); @@ -1173,8 +1252,9 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) } vl = &af->vls[af->nvls++]; - ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe, - &vl->point); + /* Trace point should be converted from subprogram DIE */ + ret = convert_to_trace_point(&pf->sp_die, pf->addr, + pf->pev->point.retprobe, &vl->point); if (ret < 0) return ret; @@ -1186,14 +1266,14 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) if (vl->vars == NULL) return -ENOMEM; af->child = true; - die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem); + die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); /* Find external variables */ if (!af->externs) goto out; /* Don't need to search child DIE for externs. */ af->child = false; - nscopes = dwarf_getscopes_die(sp_die, &scopes); + nscopes = dwarf_getscopes_die(sc_die, &scopes); while (nscopes-- > 1) die_find_child(&scopes[nscopes], collect_variables_cb, (void *)af, &die_mem); diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index c478b42a2473..1132c8f0ce89 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -57,7 +57,7 @@ struct probe_finder { struct perf_probe_event *pev; /* Target probe event */ /* Callback when a probe point is found */ - int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf); + int (*callback)(Dwarf_Die *sc_die, struct probe_finder *pf); /* For function searching */ int lno; /* Line number */ -- cgit v1.2.3 From f182e3e13ca71b64b40fab1aef31fa6a78271648 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 11 Aug 2011 20:03:05 +0900 Subject: perf probe: Avoid searching variables in intermediate scopes Fix variable searching logic to search one in inner than local scope or global(CU) scope. In the other words, skip searching in intermediate scopes. e.g., in the following code, int var1; void inline infunc(int i) { i++; <--- [A] } void func(void) { int var1, var2; infunc(var2); } At [A], "var1" should point the global variable "var1", however, if user mis-typed as "var2", variable search should be failed. However, current logic searches variable infunc() scope, global scope, and then func() scope. Thus, it can find "var2" variable in func() scope. This may not be what user expects. So, it would better not search outer scopes except outermost (compile unit) scope which contains only global variables, when it failed to find given variable in local scope. E.g. Without this: $ perf probe -V pre_schedule --externs > without.vars With this: $ perf probe -V pre_schedule --externs > with.vars Check the diff: $ diff without.vars with.vars 88d87 < int cpu 133d131 < long unsigned int* switch_count These vars are actually in the scope of schedule(), the caller of pre_schedule(). Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20110811110305.19900.94374.stgit@fedora15 Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 44 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 31 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 5c83b7d3d8ef..114542a5a99c 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -615,9 +615,9 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) /* Find a variable in a scope DIE */ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) { - Dwarf_Die vr_die, *scopes; + Dwarf_Die vr_die; char buf[32], *ptr; - int ret, nscopes; + int ret = 0; if (!is_c_varname(pf->pvar->var)) { /* Copy raw parameters */ @@ -652,29 +652,16 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) if (pf->tvar->name == NULL) return -ENOMEM; - pr_debug("Searching '%s' variable in context.\n", - pf->pvar->var); + pr_debug("Searching '%s' variable in context.\n", pf->pvar->var); /* Search child die for local variables and parameters. */ - if (die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) - ret = convert_variable(&vr_die, pf); - else { - /* Search upper class */ - nscopes = dwarf_getscopes_die(sc_die, &scopes); - ret = -ENOENT; - while (nscopes-- > 1) { - pr_debug("Searching variables in %s\n", - dwarf_diename(&scopes[nscopes])); - /* We should check this scope, so give dummy address */ - if (die_find_variable_at(&scopes[nscopes], - pf->pvar->var, 0, - &vr_die)) { - ret = convert_variable(&vr_die, pf); - break; - } - } - if (scopes) - free(scopes); + if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) { + /* Search again in global variables */ + if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) + ret = -ENOENT; } + if (ret == 0) + ret = convert_variable(&vr_die, pf); + if (ret < 0) pr_warning("Failed to find '%s' in this function.\n", pf->pvar->var); @@ -1242,8 +1229,8 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) struct available_var_finder *af = container_of(pf, struct available_var_finder, pf); struct variable_list *vl; - Dwarf_Die die_mem, *scopes = NULL; - int ret, nscopes; + Dwarf_Die die_mem; + int ret; /* Check number of tevs */ if (af->nvls == af->max_vls) { @@ -1273,12 +1260,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) goto out; /* Don't need to search child DIE for externs. */ af->child = false; - nscopes = dwarf_getscopes_die(sc_die, &scopes); - while (nscopes-- > 1) - die_find_child(&scopes[nscopes], collect_variables_cb, - (void *)af, &die_mem); - if (scopes) - free(scopes); + die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem); out: if (strlist__empty(vl->vars)) { -- cgit v1.2.3 From db0d2c6420eeb8fd669bac84d72f1ab828bbaa64 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 11 Aug 2011 20:03:11 +0900 Subject: perf probe: Search concrete out-of-line instances gcc 4.6 generates a concrete out-of-line instance when there is a function which is implicitly inlined somewhere but also has its own instance. The concrete out-of-line instance means that it has an abstract origin of the function which is referred by not only inlined-subroutines but also a concrete subprogram. Since current dwarf_func_inline_instances() can find only instances of inlined-subroutines, this introduces new die_walk_instances() to find both of subprogram and inlined-subroutines. e.g. without this, Available variables at sched_group_rt_period @ struct task_group* tg perf probe failed to find actual subprogram instance of sched_group_rt_period(). With this, Available variables at sched_group_rt_period @ struct task_group* tg @ struct task_group* tg Now it found the sched_group_rt_period() itself. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20110811110311.19900.63997.stgit@fedora15 Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dwarf-aux.c | 58 ++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/dwarf-aux.h | 4 +++ tools/perf/util/probe-finder.c | 56 +++++++++++++++------------------------- 3 files changed, 83 insertions(+), 35 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 425703a58638..d0f4048b634f 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -453,6 +453,64 @@ Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, return die_mem; } +struct __instance_walk_param { + void *addr; + int (*callback)(Dwarf_Die *, void *); + void *data; + int retval; +}; + +static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) +{ + struct __instance_walk_param *iwp = data; + Dwarf_Attribute attr_mem; + Dwarf_Die origin_mem; + Dwarf_Attribute *attr; + Dwarf_Die *origin; + + attr = dwarf_attr(inst, DW_AT_abstract_origin, &attr_mem); + if (attr == NULL) + return DIE_FIND_CB_CONTINUE; + + origin = dwarf_formref_die(attr, &origin_mem); + if (origin == NULL || origin->addr != iwp->addr) + return DIE_FIND_CB_CONTINUE; + + iwp->retval = iwp->callback(inst, iwp->data); + + return (iwp->retval) ? DIE_FIND_CB_END : DIE_FIND_CB_CONTINUE; +} + +/** + * die_walk_instances - Walk on instances of given DIE + * @or_die: an abstract original DIE + * @callback: a callback function which is called with instance DIE + * @data: user data + * + * Walk on the instances of give @in_die. @in_die must be an inlined function + * declartion. This returns the return value of @callback if it returns + * non-zero value, or -ENOENT if there is no instance. + */ +int die_walk_instances(Dwarf_Die *or_die, int (*callback)(Dwarf_Die *, void *), + void *data) +{ + Dwarf_Die cu_die; + Dwarf_Die die_mem; + struct __instance_walk_param iwp = { + .addr = or_die->addr, + .callback = callback, + .data = data, + .retval = -ENOENT, + }; + + if (dwarf_diecu(or_die, &cu_die, NULL, NULL) == NULL) + return -ENOENT; + + die_find_child(&cu_die, __die_walk_instances_cb, &iwp, &die_mem); + + return iwp.retval; +} + /* Line walker internal parameters */ struct __line_walk_param { bool recursive; diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index 6f46106fd1d5..6ce1717784b7 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -80,6 +80,10 @@ extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, Dwarf_Die *die_mem); +/* Walk on the instances of given DIE */ +extern int die_walk_instances(Dwarf_Die *in_die, + int (*callback)(Dwarf_Die *, void *), void *data); + /* Walker on lines (Note: line number will not be sorted) */ typedef int (* line_walk_callback_t) (const char *fname, int lineno, Dwarf_Addr addr, void *data); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 114542a5a99c..555fc3864b90 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -924,42 +924,39 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) return die_walk_lines(sp_die, probe_point_lazy_walker, pf); } -/* Callback parameter with return value */ -struct dwarf_callback_param { - void *data; - int retval; -}; - static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) { - struct dwarf_callback_param *param = data; - struct probe_finder *pf = param->data; + struct probe_finder *pf = data; struct perf_probe_point *pp = &pf->pev->point; Dwarf_Addr addr; + int ret; if (pp->lazy_line) - param->retval = find_probe_point_lazy(in_die, pf); + ret = find_probe_point_lazy(in_die, pf); else { /* Get probe address */ if (dwarf_entrypc(in_die, &addr) != 0) { pr_warning("Failed to get entry address of %s.\n", dwarf_diename(in_die)); - param->retval = -ENOENT; - return DWARF_CB_ABORT; + return -ENOENT; } pf->addr = addr; pf->addr += pp->offset; pr_debug("found inline addr: 0x%jx\n", (uintmax_t)pf->addr); - param->retval = call_probe_finder(in_die, pf); - if (param->retval < 0) - return DWARF_CB_ABORT; + ret = call_probe_finder(in_die, pf); } - return DWARF_CB_OK; + return ret; } +/* Callback parameter with return value for libdw */ +struct dwarf_callback_param { + void *data; + int retval; +}; + /* Search function from function name */ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) { @@ -996,14 +993,10 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) /* TODO: Check the address in this function */ param->retval = call_probe_finder(sp_die, pf); } - } else { - struct dwarf_callback_param _param = {.data = (void *)pf, - .retval = 0}; + } else /* Inlined function: search instances */ - dwarf_func_inline_instances(sp_die, probe_point_inline_cb, - &_param); - param->retval = _param.retval; - } + param->retval = die_walk_instances(sp_die, + probe_point_inline_cb, (void *)pf); return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ } @@ -1452,16 +1445,14 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) static int line_range_inline_cb(Dwarf_Die *in_die, void *data) { - struct dwarf_callback_param *param = data; - - param->retval = find_line_range_by_line(in_die, param->data); + find_line_range_by_line(in_die, data); /* * We have to check all instances of inlined function, because * some execution paths can be optimized out depends on the * function argument of instances */ - return DWARF_CB_OK; + return 0; } /* Search function from function name */ @@ -1489,15 +1480,10 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); lr->start = lf->lno_s; lr->end = lf->lno_e; - if (dwarf_func_inline(sp_die)) { - struct dwarf_callback_param _param; - _param.data = (void *)lf; - _param.retval = 0; - dwarf_func_inline_instances(sp_die, - line_range_inline_cb, - &_param); - param->retval = _param.retval; - } else + if (dwarf_func_inline(sp_die)) + param->retval = die_walk_instances(sp_die, + line_range_inline_cb, lf); + else param->retval = find_line_range_by_line(sp_die, lf); return DWARF_CB_ABORT; } -- cgit v1.2.3 From 3f4460a28fb2f73df6c32c3a305797abc01c0f9c Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 11 Aug 2011 20:03:18 +0900 Subject: perf probe: Filter out redundant inline-instances With gcc4.6, some instances of concrete inlined function looks redundant and broken, because it appears inside of a concrete instance and its call_file and call_line are same as the original abstruct's decl_file and decl_line respectively. e.g. [ d1aa] subprogram external (flag) Yes name (strp) "add_timer" decl_file (data1) 2 ;here is original decl_line (data2) 847 ;line and file prototyped (flag) Yes inline (data1) inlined (1) sibling (ref4) [ d1c6] ... [ 11d84] subprogram abstract_origin (ref4) [ d1aa] ; concrete instance low_pc (addr) .text+0x000000000000246f high_pc (addr) .text+0x000000000000248b frame_base (block1) [ 0] call_frame_cfa sibling (ref4) [ 11dd9] [ 11d9f] formal_parameter abstract_origin (ref4) [ d1b9] location (data4) location list [ 701b] [ 11da8] inlined_subroutine abstract_origin (ref4) [ d1aa] ; redundant instance low_pc (addr) .text+0x000000000000247e high_pc (addr) .text+0x0000000000002480 call_file (data1) 2 ; call line and file call_line (data2) 847 ; are same as above Those redundant instances leads unwilling results; e.g. find probe points inside of functions even if we specify a function entry as below; $ perf probe -V add_timer Available variables at add_timer @ struct timer_list* timer @ (No matched variables) So, this filters out those redundant instances based on call-site and decl-site information. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20110811110317.19900.59525.stgit@fedora15 Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dwarf-aux.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index d0f4048b634f..ee51e9b4dc09 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -307,6 +307,17 @@ static int die_get_call_fileno(Dwarf_Die *in_die) return -ENOENT; } +/* Get the declared file index number in CU DIE */ +static int die_get_decl_fileno(Dwarf_Die *pdie) +{ + Dwarf_Sword idx; + + if (die_get_attr_sdata(pdie, DW_AT_decl_file, &idx) == 0) + return (int)idx; + else + return -ENOENT; +} + /** * die_get_call_file - Get callsite file name of inlined function instance * @in_die: a DIE of an inlined function instance @@ -467,6 +478,7 @@ static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) Dwarf_Die origin_mem; Dwarf_Attribute *attr; Dwarf_Die *origin; + int tmp; attr = dwarf_attr(inst, DW_AT_abstract_origin, &attr_mem); if (attr == NULL) @@ -476,6 +488,16 @@ static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) if (origin == NULL || origin->addr != iwp->addr) return DIE_FIND_CB_CONTINUE; + /* Ignore redundant instances */ + if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { + dwarf_decl_line(origin, &tmp); + if (die_get_call_lineno(inst) == tmp) { + tmp = die_get_decl_fileno(origin); + if (die_get_call_fileno(inst) == tmp) + return DIE_FIND_CB_CONTINUE; + } + } + iwp->retval = iwp->callback(inst, iwp->data); return (iwp->retval) ? DIE_FIND_CB_END : DIE_FIND_CB_CONTINUE; -- cgit v1.2.3 From 0ac8e58f3818795d02ac309bd57b4d93ec283a77 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 9 Aug 2011 12:24:17 +0100 Subject: ARM: fix perf build with uclibc toolchains libio.h is not provided by uClibc, in order to be able to test the definition of __UCLIBC__ we need to include stdlib.h, which also includes stddef.h, providing the definition of 'NULL'. Signed-off-by: Florian Fainelli Signed-off-by: Will Deacon --- tools/perf/arch/arm/util/dwarf-regs.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools') diff --git a/tools/perf/arch/arm/util/dwarf-regs.c b/tools/perf/arch/arm/util/dwarf-regs.c index fff6450c8c99..e8d5c551c69c 100644 --- a/tools/perf/arch/arm/util/dwarf-regs.c +++ b/tools/perf/arch/arm/util/dwarf-regs.c @@ -8,7 +8,10 @@ * published by the Free Software Foundation. */ +#include +#ifndef __UCLIBC__ #include +#endif #include struct pt_regs_dwarfnum { -- cgit v1.2.3 From 75f25bd31d9315ab57e4fb5eba3340452febc48d Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Wed, 3 Aug 2011 13:17:01 +0800 Subject: cpupower: avoid using symlinks Reference the source directly, don't create symlinks. Signed-off-by: WANG Cong Signed-off-by: Dominik Brodowski --- tools/power/cpupower/debug/x86_64/Makefile | 8 ++++---- tools/power/cpupower/debug/x86_64/centrino-decode.c | 1 - tools/power/cpupower/debug/x86_64/powernow-k8-decode.c | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) delete mode 120000 tools/power/cpupower/debug/x86_64/centrino-decode.c delete mode 120000 tools/power/cpupower/debug/x86_64/powernow-k8-decode.c (limited to 'tools') diff --git a/tools/power/cpupower/debug/x86_64/Makefile b/tools/power/cpupower/debug/x86_64/Makefile index dbf13998462a..3326217dd311 100644 --- a/tools/power/cpupower/debug/x86_64/Makefile +++ b/tools/power/cpupower/debug/x86_64/Makefile @@ -1,10 +1,10 @@ default: all -centrino-decode: centrino-decode.c - $(CC) $(CFLAGS) -o centrino-decode centrino-decode.c +centrino-decode: ../i386/centrino-decode.c + $(CC) $(CFLAGS) -o $@ $< -powernow-k8-decode: powernow-k8-decode.c - $(CC) $(CFLAGS) -o powernow-k8-decode powernow-k8-decode.c +powernow-k8-decode: ../i386/powernow-k8-decode.c + $(CC) $(CFLAGS) -o $@ $< all: centrino-decode powernow-k8-decode diff --git a/tools/power/cpupower/debug/x86_64/centrino-decode.c b/tools/power/cpupower/debug/x86_64/centrino-decode.c deleted file mode 120000 index 26fb3f1d8fc7..000000000000 --- a/tools/power/cpupower/debug/x86_64/centrino-decode.c +++ /dev/null @@ -1 +0,0 @@ -../i386/centrino-decode.c \ No newline at end of file diff --git a/tools/power/cpupower/debug/x86_64/powernow-k8-decode.c b/tools/power/cpupower/debug/x86_64/powernow-k8-decode.c deleted file mode 120000 index eb30c79cf9df..000000000000 --- a/tools/power/cpupower/debug/x86_64/powernow-k8-decode.c +++ /dev/null @@ -1 +0,0 @@ -../i386/powernow-k8-decode.c \ No newline at end of file -- cgit v1.2.3 From 2dfc818b35cbea59188cc86e86e0a0efce2b0dbe Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Fri, 12 Aug 2011 01:11:35 +0200 Subject: cpupower: mperf monitor - Use TSC to calculate max frequency if possible Which makes the implementation independent from cpufreq drivers. Therefore this would also work on a Xen kernel where the hypervisor is doing frequency switching and idle entering. Signed-off-by: Thomas Renninger Signed-off-by: Dominik Brodowski --- tools/power/cpupower/Makefile | 2 +- .../cpupower/utils/idle_monitor/mperf_monitor.c | 177 +++++++++++++++------ 2 files changed, 131 insertions(+), 48 deletions(-) (limited to 'tools') diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index 94c2cf0a98b8..11521d2f0a4c 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -24,7 +24,7 @@ # Set the following to `true' to make a unstripped, unoptimized # binary. Leave this set to `false' for production use. -DEBUG ?= false +DEBUG ?= true # make the build silent. Set this to something else to make it noisy again. V ?= false diff --git a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c index 63ca87a05e5f..5650ab5a2c20 100644 --- a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c @@ -22,12 +22,15 @@ #define MSR_TSC 0x10 +#define MSR_AMD_HWCR 0xc0010015 + enum mperf_id { C0 = 0, Cx, AVG_FREQ, MPERF_CSTATE_COUNT }; static int mperf_get_count_percent(unsigned int self_id, double *percent, unsigned int cpu); static int mperf_get_count_freq(unsigned int id, unsigned long long *count, unsigned int cpu); +static struct timespec time_start, time_end; static cstate_t mperf_cstates[MPERF_CSTATE_COUNT] = { { @@ -54,19 +57,33 @@ static cstate_t mperf_cstates[MPERF_CSTATE_COUNT] = { }, }; +enum MAX_FREQ_MODE { MAX_FREQ_SYSFS, MAX_FREQ_TSC_REF }; +static int max_freq_mode; +/* + * The max frequency mperf is ticking at (in C0), either retrieved via: + * 1) calculated after measurements if we know TSC ticks at mperf/P0 frequency + * 2) cpufreq /sys/devices/.../cpu0/cpufreq/cpuinfo_max_freq at init time + * 1. Is preferred as it also works without cpufreq subsystem (e.g. on Xen) + */ +static unsigned long max_frequency; + static unsigned long long tsc_at_measure_start; static unsigned long long tsc_at_measure_end; -static unsigned long max_frequency; static unsigned long long *mperf_previous_count; static unsigned long long *aperf_previous_count; static unsigned long long *mperf_current_count; static unsigned long long *aperf_current_count; + /* valid flag for all CPUs. If a MSR read failed it will be zero */ static int *is_valid; static int mperf_get_tsc(unsigned long long *tsc) { - return read_msr(0, MSR_TSC, tsc); + int ret; + ret = read_msr(0, MSR_TSC, tsc); + if (ret) + dprint("Reading TSC MSR failed, returning %llu\n", *tsc); + return ret; } static int mperf_init_stats(unsigned int cpu) @@ -97,36 +114,11 @@ static int mperf_measure_stats(unsigned int cpu) return 0; } -/* - * get_average_perf() - * - * Returns the average performance (also considers boosted frequencies) - * - * Input: - * aperf_diff: Difference of the aperf register over a time period - * mperf_diff: Difference of the mperf register over the same time period - * max_freq: Maximum frequency (P0) - * - * Returns: - * Average performance over the time period - */ -static unsigned long get_average_perf(unsigned long long aperf_diff, - unsigned long long mperf_diff) -{ - unsigned int perf_percent = 0; - if (((unsigned long)(-1) / 100) < aperf_diff) { - int shift_count = 7; - aperf_diff >>= shift_count; - mperf_diff >>= shift_count; - } - perf_percent = (aperf_diff * 100) / mperf_diff; - return (max_frequency * perf_percent) / 100; -} - static int mperf_get_count_percent(unsigned int id, double *percent, unsigned int cpu) { unsigned long long aperf_diff, mperf_diff, tsc_diff; + unsigned long long timediff; if (!is_valid[cpu]) return -1; @@ -136,11 +128,19 @@ static int mperf_get_count_percent(unsigned int id, double *percent, mperf_diff = mperf_current_count[cpu] - mperf_previous_count[cpu]; aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu]; - tsc_diff = tsc_at_measure_end - tsc_at_measure_start; - *percent = 100.0 * mperf_diff / tsc_diff; - dprint("%s: mperf_diff: %llu, tsc_diff: %llu\n", - mperf_cstates[id].name, mperf_diff, tsc_diff); + if (max_freq_mode == MAX_FREQ_TSC_REF) { + tsc_diff = tsc_at_measure_end - tsc_at_measure_start; + *percent = 100.0 * mperf_diff / tsc_diff; + dprint("%s: TSC Ref - mperf_diff: %llu, tsc_diff: %llu\n", + mperf_cstates[id].name, mperf_diff, tsc_diff); + } else if (max_freq_mode == MAX_FREQ_SYSFS) { + timediff = timespec_diff_us(time_start, time_end); + *percent = 100.0 * mperf_diff / timediff; + dprint("%s: MAXFREQ - mperf_diff: %llu, time_diff: %llu\n", + mperf_cstates[id].name, mperf_diff, timediff); + } else + return -1; if (id == Cx) *percent = 100.0 - *percent; @@ -154,7 +154,7 @@ static int mperf_get_count_percent(unsigned int id, double *percent, static int mperf_get_count_freq(unsigned int id, unsigned long long *count, unsigned int cpu) { - unsigned long long aperf_diff, mperf_diff; + unsigned long long aperf_diff, mperf_diff, time_diff, tsc_diff; if (id != AVG_FREQ) return 1; @@ -165,11 +165,21 @@ static int mperf_get_count_freq(unsigned int id, unsigned long long *count, mperf_diff = mperf_current_count[cpu] - mperf_previous_count[cpu]; aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu]; - /* Return MHz for now, might want to return KHz if column width is more - generic */ - *count = get_average_perf(aperf_diff, mperf_diff) / 1000; - dprint("%s: %llu\n", mperf_cstates[id].name, *count); + if (max_freq_mode == MAX_FREQ_TSC_REF) { + /* Calculate max_freq from TSC count */ + tsc_diff = tsc_at_measure_end - tsc_at_measure_start; + time_diff = timespec_diff_us(time_start, time_end); + max_frequency = tsc_diff / time_diff; + } + *count = max_frequency * ((double)aperf_diff / mperf_diff); + dprint("%s: Average freq based on %s maximum frequency:\n", + mperf_cstates[id].name, + (max_freq_mode == MAX_FREQ_TSC_REF) ? "TSC calculated" : "sysfs read"); + dprint("%max_frequency: %lu", max_frequency); + dprint("aperf_diff: %llu\n", aperf_diff); + dprint("mperf_diff: %llu\n", mperf_diff); + dprint("avg freq: %llu\n", *count); return 0; } @@ -178,6 +188,7 @@ static int mperf_start(void) int cpu; unsigned long long dbg; + clock_gettime(CLOCK_REALTIME, &time_start); mperf_get_tsc(&tsc_at_measure_start); for (cpu = 0; cpu < cpu_count; cpu++) @@ -193,32 +204,104 @@ static int mperf_stop(void) unsigned long long dbg; int cpu; - mperf_get_tsc(&tsc_at_measure_end); - for (cpu = 0; cpu < cpu_count; cpu++) mperf_measure_stats(cpu); + mperf_get_tsc(&tsc_at_measure_end); + clock_gettime(CLOCK_REALTIME, &time_end); + mperf_get_tsc(&dbg); dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end); return 0; } -struct cpuidle_monitor mperf_monitor; - -struct cpuidle_monitor *mperf_register(void) +/* + * Mperf register is defined to tick at P0 (maximum) frequency + * + * Instead of reading out P0 which can be tricky to read out from HW, + * we use TSC counter if it reliably ticks at P0/mperf frequency. + * + * Still try to fall back to: + * /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq + * on older Intel HW without invariant TSC feature. + * Or on AMD machines where TSC does not tick at P0 (do not exist yet, but + * it's still double checked (MSR_AMD_HWCR)). + * + * On these machines the user would still get useful mperf + * stats when acpi-cpufreq driver is loaded. + */ +static int init_maxfreq_mode(void) { + int ret; + unsigned long long hwcr; unsigned long min; - if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF)) - return NULL; - - /* Assume min/max all the same on all cores */ + if (!cpupower_cpu_info.caps & CPUPOWER_CAP_INV_TSC) + goto use_sysfs; + + if (cpupower_cpu_info.vendor == X86_VENDOR_AMD) { + /* MSR_AMD_HWCR tells us whether TSC runs at P0/mperf + * freq. + * A test whether hwcr is accessable/available would be: + * (cpupower_cpu_info.family > 0x10 || + * cpupower_cpu_info.family == 0x10 && + * cpupower_cpu_info.model >= 0x2)) + * This should be the case for all aperf/mperf + * capable AMD machines and is therefore safe to test here. + * Compare with Linus kernel git commit: acf01734b1747b1ec4 + */ + ret = read_msr(0, MSR_AMD_HWCR, &hwcr); + /* + * If the MSR read failed, assume a Xen system that did + * not explicitly provide access to it and assume TSC works + */ + if (ret != 0) { + dprint("TSC read 0x%x failed - assume TSC working\n", + MSR_AMD_HWCR); + return 0; + } else if (1 & (hwcr >> 24)) { + max_freq_mode = MAX_FREQ_TSC_REF; + return 0; + } else { /* Use sysfs max frequency if available */ } + } else if (cpupower_cpu_info.vendor == X86_VENDOR_INTEL) { + /* + * On Intel we assume mperf (in C0) is ticking at same + * rate than TSC + */ + max_freq_mode = MAX_FREQ_TSC_REF; + return 0; + } +use_sysfs: if (cpufreq_get_hardware_limits(0, &min, &max_frequency)) { dprint("Cannot retrieve max freq from cpufreq kernel " "subsystem\n"); - return NULL; + return -1; } + max_freq_mode = MAX_FREQ_SYSFS; + return 0; +} + +/* + * This monitor provides: + * + * 1) Average frequency a CPU resided in + * This always works if the CPU has aperf/mperf capabilities + * + * 2) C0 and Cx (any sleep state) time a CPU resided in + * Works if mperf timer stops ticking in sleep states which + * seem to be the case on all current HW. + * Both is directly retrieved from HW registers and is independent + * from kernel statistics. + */ +struct cpuidle_monitor mperf_monitor; +struct cpuidle_monitor *mperf_register(void) +{ + if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF)) + return NULL; + + if (init_maxfreq_mode()) + return NULL; /* Free this at program termination */ is_valid = calloc(cpu_count, sizeof(int)); -- cgit v1.2.3 From 88f984e0e235f82a5d34f4a99244eeb14e1413e0 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Fri, 12 Aug 2011 01:11:36 +0200 Subject: cpupower: Do not show an empty Idle_Stats monitor if no idle driver is available By taking error values of: sysfs_get_idlestate_count(..); into account. Signed-off-by: Thomas Renninger Signed-off-by: Dominik Brodowski --- tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c b/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c index d048b96a6155..bcd22a1a3970 100644 --- a/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c +++ b/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c @@ -134,7 +134,7 @@ static struct cpuidle_monitor *cpuidle_register(void) /* Assume idle state count is the same for all CPUs */ cpuidle_sysfs_monitor.hw_states_num = sysfs_get_idlestate_count(0); - if (cpuidle_sysfs_monitor.hw_states_num == 0) + if (cpuidle_sysfs_monitor.hw_states_num <= 0) return NULL; for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) { -- cgit v1.2.3 From 7c74d2bc5a9d43d33d6f16c1e706147162e2bc52 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Fri, 12 Aug 2011 01:11:37 +0200 Subject: cpupower: Better detect offlined CPUs Before, checking for offlined CPUs was done dirty and it was checked whether topology parsing returned -1 values. But this is a valid case on a Xen (and possibly other) kernels. Do proper online/offline checking, also take CONFIG_HOTPLUG_CPU option into account (no /sys/devices/../cpuX/online file). Signed-off-by: Thomas Renninger Signed-off-by: Dominik Brodowski --- tools/power/cpupower/utils/helpers/helpers.h | 3 ++ tools/power/cpupower/utils/helpers/sysfs.c | 50 ++++++++++++++++++++++ tools/power/cpupower/utils/helpers/sysfs.h | 2 + tools/power/cpupower/utils/helpers/topology.c | 5 ++- .../cpupower/utils/idle_monitor/cpupower-monitor.c | 10 +++-- 5 files changed, 66 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 592ee362b877..7a83022733b2 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -96,6 +96,9 @@ struct cpupower_topology { int pkg; int core; int cpu; + + /* flags */ + unsigned int is_online:1; } *core_info; }; diff --git a/tools/power/cpupower/utils/helpers/sysfs.c b/tools/power/cpupower/utils/helpers/sysfs.c index 55e2466674c6..c6343024a611 100644 --- a/tools/power/cpupower/utils/helpers/sysfs.c +++ b/tools/power/cpupower/utils/helpers/sysfs.c @@ -56,6 +56,56 @@ static unsigned int sysfs_write_file(const char *path, return (unsigned int) numwrite; } +/* + * Detect whether a CPU is online + * + * Returns: + * 1 -> if CPU is online + * 0 -> if CPU is offline + * negative errno values in error case + */ +int sysfs_is_cpu_online(unsigned int cpu) +{ + char path[SYSFS_PATH_MAX]; + int fd; + ssize_t numread; + unsigned long long value; + char linebuf[MAX_LINE_LEN]; + char *endp; + struct stat statbuf; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu); + + if (stat(path, &statbuf) != 0) + return 0; + + /* + * kernel without CONFIG_HOTPLUG_CPU + * -> cpuX directory exists, but not cpuX/online file + */ + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu); + if (stat(path, &statbuf) != 0) + return 1; + + fd = open(path, O_RDONLY); + if (fd == -1) + return -errno; + + numread = read(fd, linebuf, MAX_LINE_LEN - 1); + if (numread < 1) { + close(fd); + return -EIO; + } + linebuf[numread] = '\0'; + close(fd); + + value = strtoull(linebuf, &endp, 0); + if (value > 1 || value < 0) + return -EINVAL; + + return value; +} + /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ /* diff --git a/tools/power/cpupower/utils/helpers/sysfs.h b/tools/power/cpupower/utils/helpers/sysfs.h index f9373e090637..8cb797bbceb0 100644 --- a/tools/power/cpupower/utils/helpers/sysfs.h +++ b/tools/power/cpupower/utils/helpers/sysfs.h @@ -7,6 +7,8 @@ extern unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen); +extern int sysfs_is_cpu_online(unsigned int cpu); + extern unsigned long sysfs_get_idlestate_latency(unsigned int cpu, unsigned int idlestate); extern unsigned long sysfs_get_idlestate_usage(unsigned int cpu, diff --git a/tools/power/cpupower/utils/helpers/topology.c b/tools/power/cpupower/utils/helpers/topology.c index 385ee5c7570c..4eae2c47ba48 100644 --- a/tools/power/cpupower/utils/helpers/topology.c +++ b/tools/power/cpupower/utils/helpers/topology.c @@ -41,6 +41,8 @@ struct cpuid_core_info { unsigned int pkg; unsigned int thread; unsigned int cpu; + /* flags */ + unsigned int is_online:1; }; static int __compare(const void *t1, const void *t2) @@ -78,6 +80,8 @@ int get_cpu_topology(struct cpupower_topology *cpu_top) return -ENOMEM; cpu_top->pkgs = cpu_top->cores = 0; for (cpu = 0; cpu < cpus; cpu++) { + cpu_top->core_info[cpu].cpu = cpu; + cpu_top->core_info[cpu].is_online = sysfs_is_cpu_online(cpu); cpu_top->core_info[cpu].pkg = sysfs_topology_read_file(cpu, "physical_package_id"); if ((int)cpu_top->core_info[cpu].pkg != -1 && @@ -85,7 +89,6 @@ int get_cpu_topology(struct cpupower_topology *cpu_top) cpu_top->pkgs = cpu_top->core_info[cpu].pkg; cpu_top->core_info[cpu].core = sysfs_topology_read_file(cpu, "core_id"); - cpu_top->core_info[cpu].cpu = cpu; } cpu_top->pkgs++; diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c index ba4bf068380d..dd8e1ea6e6f2 100644 --- a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c @@ -190,9 +190,13 @@ void print_results(int topology_depth, int cpu) } } } - /* cpu offline */ - if (cpu_top.core_info[cpu].pkg == -1 || - cpu_top.core_info[cpu].core == -1) { + /* + * The monitor could still provide useful data, for example + * AMD HW counters partly sit in PCI config space. + * It's up to the monitor plug-in to check .is_online, this one + * is just for additional info. + */ + if (!cpu_top.core_info[cpu].is_online) { printf(_(" *is offline\n")); return; } else -- cgit v1.2.3 From 9ee31f618a3c8209b2bd4bedd71fd5f2be7786bd Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Fri, 12 Aug 2011 01:11:38 +0200 Subject: cpupower: Make monitor command -c/--cpu aware This allows for example: cpupower -c 2-4,6 monitor -m Mperf |Mperf PKG |CORE|CPU | C0 | Cx | Freq 0| 8| 4| 2.42| 97.58| 1353 0| 16| 2| 14.38| 85.62| 1928 0| 24| 6| 1.76| 98.24| 1442 1| 16| 3| 15.53| 84.47| 1650 CPUs always get resorted for package, core then cpu id if it could get read out (or however you name these topology levels...). Still this is a nice way to keep the overview if a test binary is bound to a specific CPU or if one wants to show all CPUs inside a package or similar. Still missing: Do not measure not available cores to reduce the overhead and achieve better results. Signed-off-by: Thomas Renninger Signed-off-by: Dominik Brodowski --- tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tools') diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c index dd8e1ea6e6f2..6cb8d9e6bb6b 100644 --- a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c @@ -149,6 +149,10 @@ void print_results(int topology_depth, int cpu) unsigned long long result; cstate_t s; + /* Be careful CPUs may got resorted for pkg value do not just use cpu */ + if (!bitmask_isbitset(cpus_chosen, cpu_top.core_info[cpu].cpu)) + return; + if (topology_depth > 2) printf("%4d|", cpu_top.core_info[cpu].pkg); if (topology_depth > 1) @@ -389,6 +393,10 @@ int cmd_monitor(int argc, char **argv) return EXIT_FAILURE; } + /* Default is: monitor all CPUs */ + if (bitmask_isallclear(cpus_chosen)) + bitmask_setall(cpus_chosen); + dprint("System has up to %d CPU cores\n", cpu_count); for (num = 0; all_monitors[num]; num++) { -- cgit v1.2.3 From 77e57297b4ff3f602ba5105398d342a4b4a54774 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Mon, 23 May 2011 14:39:17 +0200 Subject: perf list: Fix exit value This patch fixes an issue with the exit value of perf list: $ perf list; echo $? 129 perf list returns an error exit code even though there is no error. There was a stray exit(129) in print_events(). This patch removes this exit(). $ perf list; echo $? 0 $ perf list hw sw cpu-cycles OR cycles [Hardware event] stalled-cycles-frontend OR idle-cycles-frontend [Hardware event] stalled-cycles-backend OR idle-cycles-backend [Hardware event] instructions [Hardware event] cache-references [Hardware event] cache-misses [Hardware event] branch-instructions OR branches [Hardware event] branch-misses [Hardware event] bus-cycles [Hardware event] cpu-clock [Software event] task-clock [Software event] page-faults OR faults [Software event] minor-faults [Software event] major-faults [Software event] context-switches OR cs [Software event] cpu-migrations OR migrations [Software event] alignment-faults [Software event] emulation-faults [Software event] $ echo $? 0 Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20110523123917.GA31060@quad Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-events.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 4ea7e19f5251..d93f3cea93c7 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1097,6 +1097,4 @@ void print_events(const char *event_glob) printf("\n"); print_tracepoint_events(NULL, NULL); - - exit(129); } -- cgit v1.2.3 From cc2d86b04d9ac28a6be6cb05da6ea8f014fd5aa0 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Tue, 7 Jun 2011 18:19:36 +0200 Subject: perf evlist: Fix missing event name init for default event When no event is given to perf record, perf top, a default event is initialized (cycles). However, perf_evlist__add_default() was not setting the symbolic name for the event. Perf top worked simply because it was reconstructing the name from the event code. But it should not have to do this. This patch initializes the evsel->name field properly. This second version improves the code flow on the non error path. Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20110607161936.GA8163@quad Signed-off-by: Stephane Eranian [committer note: Use perf_evsel__delete() instead of plain free()] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index e03e7bc8205e..c12bd476c6f7 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -85,10 +85,19 @@ int perf_evlist__add_default(struct perf_evlist *evlist) struct perf_evsel *evsel = perf_evsel__new(&attr, 0); if (evsel == NULL) - return -ENOMEM; + goto error; + + /* use strdup() because free(evsel) assumes name is allocated */ + evsel->name = strdup("cycles"); + if (!evsel->name) + goto error_free; perf_evlist__add(evlist, evsel); return 0; +error_free: + perf_evsel__delete(evsel); +error: + return -ENOMEM; } void perf_evlist__disable(struct perf_evlist *evlist) -- cgit v1.2.3 From 777d1d71db622a5e1ff703495741c3d257b532e5 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Sat, 23 Jul 2011 04:10:43 +0200 Subject: perf tools: Fix error handling of unknown events There was a problem with the parse_events() code not printing the correct event name when an event was unknown and starting with an 'r'. The source of the problem was the way raw notation was parsed. Without the patch: $ perf stat -e retired_foo invalid event modifier: 'tired_foo' With the patch: $ perf stat -e retired_foo invalid or unsupported event: 'retired_foo' This also covers the case where the name of the event was not printed at all when perf was linked with libpfm4. Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20110723021043.GA20178@quad Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-events.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index d93f3cea93c7..928918b796b2 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -697,7 +697,11 @@ parse_raw_event(const char **strp, struct perf_event_attr *attr) return EVT_FAILED; n = hex2u64(str + 1, &config); if (n > 0) { - *strp = str + n + 1; + const char *end = str + n + 1; + if (*end != '\0' && *end != ',' && *end != ':') + return EVT_FAILED; + + *strp = end; attr->type = PERF_TYPE_RAW; attr->config = config; return EVT_HANDLED; -- cgit v1.2.3 From 195bcbf5078d74c8e00d68f04eb8695196fb31e8 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Thu, 18 Aug 2011 07:37:21 -0400 Subject: perf tools: Fix build against newer glibc Upstream glibc commit 295e904 added a definition for __attribute_const__ to cdefs.h. This causes the following error when building perf: util/include/linux/compiler.h:8:0: error: "__attribute_const__" redefined [-Werror] /usr/include/sys/cdefs.h:226:0: note: this is the location of the previous definition Wrap __attribute_const__ in #ifndef as we do for __always_inline. Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20110818113720.GL2227@zod.bos.redhat.com Signed-off-by: Josh Boyer Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/include/linux/compiler.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index 791f9dd27ebf..547628e97f3d 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h @@ -5,7 +5,9 @@ #define __always_inline inline #endif #define __user +#ifndef __attribute_const__ #define __attribute_const__ +#endif #define __used __attribute__((__unused__)) -- cgit v1.2.3 From 43bece79796c2a39ec98998fd3f1071f04f3d8c3 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Wed, 17 Aug 2011 18:42:07 +0800 Subject: perf tools: Add group event scheduling option to perf record/stat Group event scheduling command line option is missing in perf record/stat. Add it to perf record/stat, which is same as in perf top. Reported-by: Andi Kleen Cc: Andi Kleen Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1313577727.2754.5.camel@hp6530s Signed-off-by: Lin Ming Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 4 +++- tools/perf/builtin-stat.c | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f6426b496f4a..6b0519f885e4 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -45,7 +45,7 @@ static int freq = 1000; static int output; static int pipe_output = 0; static const char *output_name = NULL; -static int group = 0; +static bool group = false; static int realtime_prio = 0; static bool nodelay = false; static bool raw_samples = false; @@ -753,6 +753,8 @@ const struct option record_options[] = { "child tasks do not inherit counters"), OPT_UINTEGER('F', "freq", &user_freq, "profile at this frequency"), OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), + OPT_BOOLEAN(0, "group", &group, + "put the counters into a counter group"), OPT_BOOLEAN('g', "call-graph", &call_graph, "do call-graph (stack chain/backtrace) recording"), OPT_INCR('v', "verbose", &verbose, diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 1ad04ce29c34..5deb17d9e795 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -193,6 +193,7 @@ static int big_num_opt = -1; static const char *cpu_list; static const char *csv_sep = NULL; static bool csv_output = false; +static bool group = false; static volatile int done = 0; @@ -280,14 +281,14 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) attr->inherit = !no_inherit; if (system_wide) - return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, false); + return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, group); if (target_pid == -1 && target_tid == -1) { attr->disabled = 1; attr->enable_on_exec = 1; } - return perf_evsel__open_per_thread(evsel, evsel_list->threads, false); + return perf_evsel__open_per_thread(evsel, evsel_list->threads, group); } /* @@ -1043,6 +1044,8 @@ static const struct option options[] = { "stat events on existing thread id"), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), + OPT_BOOLEAN('g', "group", &group, + "put the counters into a counter group"), OPT_BOOLEAN('c', "scale", &scale, "scale/normalize counters"), OPT_INCR('v', "verbose", &verbose, -- cgit v1.2.3 From 63b37de12889b7b96463b7d6de6d3f3704486b91 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Tue, 16 Aug 2011 15:36:21 -0400 Subject: cpupower: fix Makefile typo Signed-off-by: Dave Jones Signed-off-by: Dominik Brodowski --- tools/power/cpupower/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index 11521d2f0a4c..edb021c5f8cd 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -35,7 +35,7 @@ NLS ?= true # Set the following to 'true' to build/install the # cpufreq-bench benchmarking tool -CPUFRQ_BENCH ?= true +CPUFREQ_BENCH ?= true # Prefix to the directories we're installing to DESTDIR ?= @@ -139,7 +139,7 @@ ifeq ($(strip $(NLS)),true) COMPILE_NLS += create-gmo endif -ifeq ($(strip $(CPUFRQ_BENCH)),true) +ifeq ($(strip $(CPUFREQ_BENCH)),true) INSTALL_BENCH += install-bench COMPILE_BENCH += compile-bench endif -- cgit v1.2.3 From 47c336307a3680cfdf4adbe718d79f3fe66702ea Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Fri, 19 Aug 2011 17:00:02 +0200 Subject: cpupower: make NLS truly optional Loosely based on a patch for cpufrequtils, submittted by Sergey Dryabzhinsky and signed-off-by: Matt Turner Signed-off-by: Dominik Brodowski --- tools/power/cpupower/Makefile | 1 + tools/power/cpupower/utils/helpers/helpers.h | 9 +++++++++ 2 files changed, 10 insertions(+) (limited to 'tools') diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index edb021c5f8cd..e8a03aceceb1 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -137,6 +137,7 @@ CFLAGS += -pipe ifeq ($(strip $(NLS)),true) INSTALL_NLS += install-gmo COMPILE_NLS += create-gmo + CFLAGS += -DNLS endif ifeq ($(strip $(CPUFREQ_BENCH)),true) diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 7a83022733b2..2747e738efb0 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -16,11 +16,20 @@ #include "helpers/bitmask.h" /* Internationalization ****************************/ +#ifdef NLS + #define _(String) gettext(String) #ifndef gettext_noop #define gettext_noop(String) String #endif #define N_(String) gettext_noop(String) + +#else /* !NLS */ + +#define _(String) String +#define N_(String) String + +#endif /* Internationalization ****************************/ extern int run_as_root; -- cgit v1.2.3 From 498ca793d90aef8ad38a852a969c257f62832738 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 6 Aug 2011 18:11:43 +0200 Subject: cpupower: use man(1) when calling "cpupower help subcommand" Instead of printing something non-formatted to stdout, call man(1) to show the man page for the proper subcommand. Signed-off-by: Dominik Brodowski --- tools/power/cpupower/man/cpupower-frequency-info.1 | 6 +- tools/power/cpupower/man/cpupower-frequency-set.1 | 8 +- tools/power/cpupower/man/cpupower.1 | 14 ++-- tools/power/cpupower/utils/builtin.h | 7 -- tools/power/cpupower/utils/cpufreq-info.c | 42 +--------- tools/power/cpupower/utils/cpufreq-set.c | 29 +------ tools/power/cpupower/utils/cpuidle-info.c | 24 +----- tools/power/cpupower/utils/cpupower-info.c | 20 +---- tools/power/cpupower/utils/cpupower-set.c | 25 +----- tools/power/cpupower/utils/cpupower.c | 91 ++++++++++++---------- .../cpupower/utils/idle_monitor/cpupower-monitor.c | 48 ++++-------- 11 files changed, 86 insertions(+), 228 deletions(-) (limited to 'tools') diff --git a/tools/power/cpupower/man/cpupower-frequency-info.1 b/tools/power/cpupower/man/cpupower-frequency-info.1 index 3194811d58f5..bb60a8d1e45a 100644 --- a/tools/power/cpupower/man/cpupower-frequency-info.1 +++ b/tools/power/cpupower/man/cpupower-frequency-info.1 @@ -1,10 +1,10 @@ -.TH "cpufreq-info" "1" "0.1" "Mattia Dongili" "" +.TH "cpupower-frequency-info" "1" "0.1" "Mattia Dongili" "" .SH "NAME" .LP -cpufreq\-info \- Utility to retrieve cpufreq kernel information +cpupower frequency\-info \- Utility to retrieve cpufreq kernel information .SH "SYNTAX" .LP -cpufreq\-info [\fIoptions\fP] +cpupower [ \-c cpulist ] frequency\-info [\fIoptions\fP] .SH "DESCRIPTION" .LP A small tool which prints out cpufreq information helpful to developers and interested users. diff --git a/tools/power/cpupower/man/cpupower-frequency-set.1 b/tools/power/cpupower/man/cpupower-frequency-set.1 index 26e3e13eee3b..685f469093ad 100644 --- a/tools/power/cpupower/man/cpupower-frequency-set.1 +++ b/tools/power/cpupower/man/cpupower-frequency-set.1 @@ -1,13 +1,13 @@ -.TH "cpufreq-set" "1" "0.1" "Mattia Dongili" "" +.TH "cpupower-freqency-set" "1" "0.1" "Mattia Dongili" "" .SH "NAME" .LP -cpufreq\-set \- A small tool which allows to modify cpufreq settings. +cpupower frequency\-set \- A small tool which allows to modify cpufreq settings. .SH "SYNTAX" .LP -cpufreq\-set [\fIoptions\fP] +cpupower [ \-c cpu ] frequency\-set [\fIoptions\fP] .SH "DESCRIPTION" .LP -cpufreq\-set allows you to modify cpufreq settings without having to type e.g. "/sys/devices/system/cpu/cpu0/cpufreq/scaling_set_speed" all the time. +cpupower frequency\-set allows you to modify cpufreq settings without having to type e.g. "/sys/devices/system/cpu/cpu0/cpufreq/scaling_set_speed" all the time. .SH "OPTIONS" .LP .TP diff --git a/tools/power/cpupower/man/cpupower.1 b/tools/power/cpupower/man/cpupower.1 index 78c20feab85c..baf741d06e82 100644 --- a/tools/power/cpupower/man/cpupower.1 +++ b/tools/power/cpupower/man/cpupower.1 @@ -3,7 +3,7 @@ cpupower \- Shows and sets processor power related values .SH SYNOPSIS .ft B -.B cpupower [ \-c cpulist ] subcommand [ARGS] +.B cpupower [ \-c cpulist ] [ARGS] .B cpupower \-v|\-\-version @@ -13,24 +13,24 @@ cpupower \- Shows and sets processor power related values \fBcpupower \fP is a collection of tools to examine and tune power saving related features of your processor. -The manpages of the subcommands (cpupower\-(1)) provide detailed +The manpages of the commands (cpupower\-(1)) provide detailed descriptions of supported features. Run \fBcpupower help\fP to get an overview -of supported subcommands. +of supported commands. .SH Options .PP \-\-help, \-h .RS 4 -Shows supported subcommands and general usage. +Shows supported commands and general usage. .RE .PP \-\-cpu cpulist, \-c cpulist .RS 4 Only show or set values for specific cores. -This option is not supported by all subcommands, details can be found in the -manpages of the subcommands. +This option is not supported by all commands, details can be found in the +manpages of the commands. -Some subcommands access all cores (typically the *\-set commands), some only +Some commands access all cores (typically the *\-set commands), some only the first core (typically the *\-info commands) by default. The syntax for is based on how the kernel exports CPU bitmasks via diff --git a/tools/power/cpupower/utils/builtin.h b/tools/power/cpupower/utils/builtin.h index c870ffba5219..c10496fbe3c6 100644 --- a/tools/power/cpupower/utils/builtin.h +++ b/tools/power/cpupower/utils/builtin.h @@ -8,11 +8,4 @@ extern int cmd_freq_info(int argc, const char **argv); extern int cmd_idle_info(int argc, const char **argv); extern int cmd_monitor(int argc, const char **argv); -extern void set_help(void); -extern void info_help(void); -extern void freq_set_help(void); -extern void freq_info_help(void); -extern void idle_info_help(void); -extern void monitor_help(void); - #endif diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index 5a1d25f056b3..28953c9a7bd5 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -510,37 +510,6 @@ static int get_latency(unsigned int cpu, unsigned int human) return 0; } -void freq_info_help(void) -{ - printf(_("Usage: cpupower freqinfo [options]\n")); - printf(_("Options:\n")); - printf(_(" -e, --debug Prints out debug information [default]\n")); - printf(_(" -f, --freq Get frequency the CPU currently runs at, according\n" - " to the cpufreq core *\n")); - printf(_(" -w, --hwfreq Get frequency the CPU currently runs at, by reading\n" - " it from hardware (only available to root) *\n")); - printf(_(" -l, --hwlimits Determine the minimum and maximum CPU frequency allowed *\n")); - printf(_(" -d, --driver Determines the used cpufreq kernel driver *\n")); - printf(_(" -p, --policy Gets the currently used cpufreq policy *\n")); - printf(_(" -g, --governors Determines available cpufreq governors *\n")); - printf(_(" -r, --related-cpus Determines which CPUs run at the same hardware frequency *\n")); - printf(_(" -a, --affected-cpus Determines which CPUs need to have their frequency\n" - " coordinated by software *\n")); - printf(_(" -s, --stats Shows cpufreq statistics if available\n")); - printf(_(" -y, --latency Determines the maximum latency on CPU frequency changes *\n")); - printf(_(" -b, --boost Checks for turbo or boost modes *\n")); - printf(_(" -o, --proc Prints out information like provided by the /proc/cpufreq\n" - " interface in 2.4. and early 2.6. kernels\n")); - printf(_(" -m, --human human-readable output for the -f, -w, -s and -y parameters\n")); - printf(_(" -h, --help Prints out this screen\n")); - - printf("\n"); - printf(_("If no argument is given, full output about\n" - "cpufreq is printed which is useful e.g. for reporting bugs.\n\n")); - printf(_("By default info of CPU 0 is shown which can be overridden\n" - "with the cpupower --cpu main command option.\n")); -} - static struct option info_opts[] = { { .name = "debug", .has_arg = no_argument, .flag = NULL, .val = 'e'}, { .name = "boost", .has_arg = no_argument, .flag = NULL, .val = 'b'}, @@ -556,7 +525,6 @@ static struct option info_opts[] = { { .name = "latency", .has_arg = no_argument, .flag = NULL, .val = 'y'}, { .name = "proc", .has_arg = no_argument, .flag = NULL, .val = 'o'}, { .name = "human", .has_arg = no_argument, .flag = NULL, .val = 'm'}, - { .name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'}, { }, }; @@ -570,16 +538,12 @@ int cmd_freq_info(int argc, char **argv) int output_param = 0; do { - ret = getopt_long(argc, argv, "hoefwldpgrasmyb", info_opts, NULL); + ret = getopt_long(argc, argv, "oefwldpgrasmyb", info_opts, NULL); switch (ret) { case '?': output_param = '?'; cont = 0; break; - case 'h': - output_param = 'h'; - cont = 0; - break; case -1: cont = 0; break; @@ -642,11 +606,7 @@ int cmd_freq_info(int argc, char **argv) return -EINVAL; case '?': printf(_("invalid or unknown argument\n")); - freq_info_help(); return -EINVAL; - case 'h': - freq_info_help(); - return EXIT_SUCCESS; case 'o': proc_cpufreq_output(); return EXIT_SUCCESS; diff --git a/tools/power/cpupower/utils/cpufreq-set.c b/tools/power/cpupower/utils/cpufreq-set.c index 5f783622bf31..dd1539eb8c63 100644 --- a/tools/power/cpupower/utils/cpufreq-set.c +++ b/tools/power/cpupower/utils/cpufreq-set.c @@ -20,34 +20,11 @@ #define NORM_FREQ_LEN 32 -void freq_set_help(void) -{ - printf(_("Usage: cpupower frequency-set [options]\n")); - printf(_("Options:\n")); - printf(_(" -d FREQ, --min FREQ new minimum CPU frequency the governor may select\n")); - printf(_(" -u FREQ, --max FREQ new maximum CPU frequency the governor may select\n")); - printf(_(" -g GOV, --governor GOV new cpufreq governor\n")); - printf(_(" -f FREQ, --freq FREQ specific frequency to be set. Requires userspace\n" - " governor to be available and loaded\n")); - printf(_(" -r, --related Switches all hardware-related CPUs\n")); - printf(_(" -h, --help Prints out this screen\n")); - printf("\n"); - printf(_("Notes:\n" - "1. Omitting the -c or --cpu argument is equivalent to setting it to \"all\"\n")); - printf(_("2. The -f FREQ, --freq FREQ parameter cannot be combined with any other parameter\n" - " except the -c CPU, --cpu CPU parameter\n" - "3. FREQuencies can be passed in Hz, kHz (default), MHz, GHz, or THz\n" - " by postfixing the value with the wanted unit name, without any space\n" - " (FREQuency in kHz =^ Hz * 0.001 =^ MHz * 1000 =^ GHz * 1000000).\n")); - -} - static struct option set_opts[] = { { .name = "min", .has_arg = required_argument, .flag = NULL, .val = 'd'}, { .name = "max", .has_arg = required_argument, .flag = NULL, .val = 'u'}, { .name = "governor", .has_arg = required_argument, .flag = NULL, .val = 'g'}, { .name = "freq", .has_arg = required_argument, .flag = NULL, .val = 'f'}, - { .name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'}, { .name = "related", .has_arg = no_argument, .flag = NULL, .val='r'}, { }, }; @@ -80,7 +57,6 @@ const struct freq_units def_units[] = { static void print_unknown_arg(void) { printf(_("invalid or unknown argument\n")); - freq_set_help(); } static unsigned long string_to_frequency(const char *str) @@ -231,14 +207,11 @@ int cmd_freq_set(int argc, char **argv) /* parameter parsing */ do { - ret = getopt_long(argc, argv, "d:u:g:f:hr", set_opts, NULL); + ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL); switch (ret) { case '?': print_unknown_arg(); return -EINVAL; - case 'h': - freq_set_help(); - return 0; case -1: cont = 0; break; diff --git a/tools/power/cpupower/utils/cpuidle-info.c b/tools/power/cpupower/utils/cpuidle-info.c index 70da3574f1e9..b028267c1376 100644 --- a/tools/power/cpupower/utils/cpuidle-info.c +++ b/tools/power/cpupower/utils/cpuidle-info.c @@ -139,30 +139,14 @@ static void proc_cpuidle_cpu_output(unsigned int cpu) } } -/* --freq / -f */ - -void idle_info_help(void) -{ - printf(_ ("Usage: cpupower idleinfo [options]\n")); - printf(_ ("Options:\n")); - printf(_ (" -s, --silent Only show general C-state information\n")); - printf(_ (" -o, --proc Prints out information like provided by the /proc/acpi/processor/*/power\n" - " interface in older kernels\n")); - printf(_ (" -h, --help Prints out this screen\n")); - - printf("\n"); -} - static struct option info_opts[] = { { .name = "silent", .has_arg = no_argument, .flag = NULL, .val = 's'}, { .name = "proc", .has_arg = no_argument, .flag = NULL, .val = 'o'}, - { .name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'}, { }, }; static inline void cpuidle_exit(int fail) { - idle_info_help(); exit(EXIT_FAILURE); } @@ -174,7 +158,7 @@ int cmd_idle_info(int argc, char **argv) unsigned int cpu = 0; do { - ret = getopt_long(argc, argv, "hos", info_opts, NULL); + ret = getopt_long(argc, argv, "os", info_opts, NULL); if (ret == -1) break; switch (ret) { @@ -182,10 +166,6 @@ int cmd_idle_info(int argc, char **argv) output_param = '?'; cont = 0; break; - case 'h': - output_param = 'h'; - cont = 0; - break; case 's': verbose = 0; break; @@ -211,8 +191,6 @@ int cmd_idle_info(int argc, char **argv) case '?': printf(_("invalid or unknown argument\n")); cpuidle_exit(EXIT_FAILURE); - case 'h': - cpuidle_exit(EXIT_SUCCESS); } /* Default is: show output of CPU 0 only */ diff --git a/tools/power/cpupower/utils/cpupower-info.c b/tools/power/cpupower/utils/cpupower-info.c index 85253cb7600e..3f68632c28c7 100644 --- a/tools/power/cpupower/utils/cpupower-info.c +++ b/tools/power/cpupower/utils/cpupower-info.c @@ -16,31 +16,16 @@ #include "helpers/helpers.h" #include "helpers/sysfs.h" -void info_help(void) -{ - printf(_("Usage: cpupower info [ -b ] [ -m ] [ -s ]\n")); - printf(_("Options:\n")); - printf(_(" -b, --perf-bias Gets CPU's power vs performance policy on some\n" - " Intel models [0-15], see manpage for details\n")); - printf(_(" -m, --sched-mc Gets the kernel's multi core scheduler policy.\n")); - printf(_(" -s, --sched-smt Gets the kernel's thread sibling scheduler policy.\n")); - printf(_(" -h, --help Prints out this screen\n")); - printf(_("\nPassing no option will show all info, by default only on core 0\n")); - printf("\n"); -} - static struct option set_opts[] = { { .name = "perf-bias", .has_arg = optional_argument, .flag = NULL, .val = 'b'}, { .name = "sched-mc", .has_arg = optional_argument, .flag = NULL, .val = 'm'}, { .name = "sched-smt", .has_arg = optional_argument, .flag = NULL, .val = 's'}, - { .name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'}, { }, }; static void print_wrong_arg_exit(void) { printf(_("invalid or unknown argument\n")); - info_help(); exit(EXIT_FAILURE); } @@ -64,11 +49,8 @@ int cmd_info(int argc, char **argv) textdomain(PACKAGE); /* parameter parsing */ - while ((ret = getopt_long(argc, argv, "msbh", set_opts, NULL)) != -1) { + while ((ret = getopt_long(argc, argv, "msb", set_opts, NULL)) != -1) { switch (ret) { - case 'h': - info_help(); - return 0; case 'b': if (params.perf_bias) print_wrong_arg_exit(); diff --git a/tools/power/cpupower/utils/cpupower-set.c b/tools/power/cpupower/utils/cpupower-set.c index bc1b391e46f0..dc4de3762111 100644 --- a/tools/power/cpupower/utils/cpupower-set.c +++ b/tools/power/cpupower/utils/cpupower-set.c @@ -17,30 +17,16 @@ #include "helpers/sysfs.h" #include "helpers/bitmask.h" -void set_help(void) -{ - printf(_("Usage: cpupower set [ -b val ] [ -m val ] [ -s val ]\n")); - printf(_("Options:\n")); - printf(_(" -b, --perf-bias [VAL] Sets CPU's power vs performance policy on some\n" - " Intel models [0-15], see manpage for details\n")); - printf(_(" -m, --sched-mc [VAL] Sets the kernel's multi core scheduler policy.\n")); - printf(_(" -s, --sched-smt [VAL] Sets the kernel's thread sibling scheduler policy.\n")); - printf(_(" -h, --help Prints out this screen\n")); - printf("\n"); -} - static struct option set_opts[] = { { .name = "perf-bias", .has_arg = optional_argument, .flag = NULL, .val = 'b'}, { .name = "sched-mc", .has_arg = optional_argument, .flag = NULL, .val = 'm'}, { .name = "sched-smt", .has_arg = optional_argument, .flag = NULL, .val = 's'}, - { .name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'}, { }, }; static void print_wrong_arg_exit(void) { printf(_("invalid or unknown argument\n")); - set_help(); exit(EXIT_FAILURE); } @@ -66,12 +52,9 @@ int cmd_set(int argc, char **argv) params.params = 0; /* parameter parsing */ - while ((ret = getopt_long(argc, argv, "m:s:b:h", + while ((ret = getopt_long(argc, argv, "m:s:b:", set_opts, NULL)) != -1) { switch (ret) { - case 'h': - set_help(); - return 0; case 'b': if (params.perf_bias) print_wrong_arg_exit(); @@ -110,10 +93,8 @@ int cmd_set(int argc, char **argv) } }; - if (!params.params) { - set_help(); - return -EINVAL; - } + if (!params.params) + print_wrong_arg_exit(); if (params.sched_mc) { ret = sysfs_set_sched("mc", sched_mc); diff --git a/tools/power/cpupower/utils/cpupower.c b/tools/power/cpupower/utils/cpupower.c index 5844ae0f786f..52bee591c1c5 100644 --- a/tools/power/cpupower/utils/cpupower.c +++ b/tools/power/cpupower/utils/cpupower.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "builtin.h" #include "helpers/helpers.h" @@ -19,13 +20,12 @@ struct cmd_struct { const char *cmd; int (*main)(int, const char **); - void (*usage)(void); int needs_root; }; #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) -int cmd_help(int argc, const char **argv); +static int cmd_help(int argc, const char **argv); /* Global cpu_info object available for all binaries * Info only retrieved from CPU 0 @@ -44,55 +44,66 @@ int be_verbose; static void print_help(void); static struct cmd_struct commands[] = { - { "frequency-info", cmd_freq_info, freq_info_help, 0 }, - { "frequency-set", cmd_freq_set, freq_set_help, 1 }, - { "idle-info", cmd_idle_info, idle_info_help, 0 }, - { "set", cmd_set, set_help, 1 }, - { "info", cmd_info, info_help, 0 }, - { "monitor", cmd_monitor, monitor_help, 0 }, - { "help", cmd_help, print_help, 0 }, - /* { "bench", cmd_bench, NULL, 1 }, */ + { "frequency-info", cmd_freq_info, 0 }, + { "frequency-set", cmd_freq_set, 1 }, + { "idle-info", cmd_idle_info, 0 }, + { "set", cmd_set, 1 }, + { "info", cmd_info, 0 }, + { "monitor", cmd_monitor, 0 }, + { "help", cmd_help, 0 }, + /* { "bench", cmd_bench, 1 }, */ }; -int cmd_help(int argc, const char **argv) -{ - unsigned int i; - - if (argc > 1) { - for (i = 0; i < ARRAY_SIZE(commands); i++) { - struct cmd_struct *p = commands + i; - if (strcmp(p->cmd, argv[1])) - continue; - if (p->usage) { - p->usage(); - return EXIT_SUCCESS; - } - } - } - print_help(); - if (argc == 1) - return EXIT_SUCCESS; /* cpupower help */ - return EXIT_FAILURE; -} - static void print_help(void) { unsigned int i; #ifdef DEBUG - printf(_("cpupower [ -d ][ -c cpulist ] subcommand [ARGS]\n")); - printf(_(" -d, --debug May increase output (stderr) on some subcommands\n")); + printf(_("Usage:\tcpupower [-d|--debug] [-c|--cpu cpulist ] []\n")); #else - printf(_("cpupower [ -c cpulist ] subcommand [ARGS]\n")); + printf(_("Usage:\tcpupower [-c|--cpu cpulist ] []\n")); #endif - printf(_("cpupower --version\n")); - printf(_("Supported subcommands are:\n")); + printf(_("Supported commands are:\n")); for (i = 0; i < ARRAY_SIZE(commands); i++) printf("\t%s\n", commands[i].cmd); - printf(_("\nSome subcommands can make use of the -c cpulist option.\n")); - printf(_("Look at the general cpupower manpage how to use it\n")); - printf(_("and read up the subcommand's manpage whether it is supported.\n")); - printf(_("\nUse cpupower help subcommand for getting help for above subcommands.\n")); + printf(_("\nNot all commands can make use of the -c cpulist option.\n")); + printf(_("\nUse 'cpupower help ' for getting help for above commands.\n")); +} + +static int print_man_page(const char *subpage) +{ + int len; + char *page; + + len = 10; /* enough for "cpupower-" */ + if (subpage != NULL) + len += strlen(subpage); + + page = malloc(len); + if (!page) + return -ENOMEM; + + sprintf(page, "cpupower"); + if ((subpage != NULL) && strcmp(subpage, "help")) { + strcat(page, "-"); + strcat(page, subpage); + } + + execlp("man", "man", page, NULL); + + /* should not be reached */ + return -EINVAL; +} + +static int cmd_help(int argc, const char **argv) +{ + if (argc > 1) { + print_man_page(argv[1]); /* exits within execlp() */ + return EXIT_FAILURE; + } + + print_help(); + return EXIT_SUCCESS; } static void print_version(void) diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c index 6cb8d9e6bb6b..0d6571e418db 100644 --- a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c @@ -43,6 +43,12 @@ static struct cpupower_topology cpu_top; /* ToDo: Document this in the manpage */ static char range_abbr[RANGE_MAX] = { 'T', 'C', 'P', 'M', }; +static void print_wrong_arg_exit(void) +{ + printf(_("invalid or unknown argument\n")); + exit(EXIT_FAILURE); +} + long long timespec_diff_us(struct timespec start, struct timespec end) { struct timespec temp; @@ -56,21 +62,6 @@ long long timespec_diff_us(struct timespec start, struct timespec end) return (temp.tv_sec * 1000000) + (temp.tv_nsec / 1000); } -void monitor_help(void) -{ - printf(_("cpupower monitor: [-m ,[],.. ] command\n")); - printf(_("cpupower monitor: [-m ,[],.. ] [ -i interval_sec ]\n")); - printf(_("cpupower monitor: -l\n")); - printf(_("\t command: pass an arbitrary command to measure specific workload\n")); - printf(_("\t -i: time intervall to measure for in seconds (default 1)\n")); - printf(_("\t -l: list available CPU sleep monitors (for use with -m)\n")); - printf(_("\t -m: show specific CPU sleep monitors only (in same order)\n")); - printf(_("\t -h: print this help\n")); - printf("\n"); - printf(_("only one of: -l, -m are allowed\nIf none of them is passed,")); - printf(_(" all supported monitors are shown\n")); -} - void print_n_spaces(int n) { int x; @@ -246,7 +237,6 @@ static void parse_monitor_param(char *param) if (hits == 0) { printf(_("No matching monitor found in %s, " "try -l option\n"), param); - monitor_help(); exit(EXIT_FAILURE); } /* Override detected/registerd monitors array with requested one */ @@ -343,37 +333,27 @@ static void cmdline(int argc, char *argv[]) int opt; progname = basename(argv[0]); - while ((opt = getopt(argc, argv, "+hli:m:")) != -1) { + while ((opt = getopt(argc, argv, "+li:m:")) != -1) { switch (opt) { - case 'h': - monitor_help(); - exit(EXIT_SUCCESS); case 'l': - if (mode) { - monitor_help(); - exit(EXIT_FAILURE); - } + if (mode) + print_wrong_arg_exit(); mode = list; break; case 'i': /* only allow -i with -m or no option */ - if (mode && mode != show) { - monitor_help(); - exit(EXIT_FAILURE); - } + if (mode && mode != show) + print_wrong_arg_exit(); interval = atoi(optarg); break; case 'm': - if (mode) { - monitor_help(); - exit(EXIT_FAILURE); - } + if (mode) + print_wrong_arg_exit(); mode = show; show_monitors_param = optarg; break; default: - monitor_help(); - exit(EXIT_FAILURE); + print_wrong_arg_exit(); } } if (!mode) -- cgit v1.2.3