From 761844b9c68b3c67b085265f92ac0675706cc3b3 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Fri, 28 May 2010 12:08:01 +0200 Subject: perf report: Make -D print sampled CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is useful to know on which CPU a sample was captured on. The information is captured with perf record -R but it was not printed out by perf report -D. This patch adds this. When -R is not used, cpu is set to -1to indicate that the CPU is unknown (it is not captured). Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: <4bff964c.e88cd80a.3106.7d31@mx.google.com> Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 5 +++-- tools/perf/util/event.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 35920578296..207da184980 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -157,8 +157,9 @@ static int process_sample_event(event_t *event, struct perf_session *session) event__parse_sample(event, session->sample_type, &data); - dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, - data.pid, data.tid, data.ip, data.period); + dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n", + event->header.misc, data.pid, data.tid, data.ip, + data.period, data.cpu); if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { unsigned int i; diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1f08f008d28..891753255f5 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -765,7 +765,8 @@ int event__parse_sample(event_t *event, u64 type, struct sample_data *data) u32 *p = (u32 *)array; data->cpu = *p; array++; - } + } else + data->cpu = -1; if (type & PERF_SAMPLE_PERIOD) { data->period = *array; -- cgit v1.2.3-18-g5258 From c45c6ea2e5c57960dc67e00294c2b78e9540c007 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Fri, 28 May 2010 12:00:01 +0200 Subject: perf tools: Add the ability to specify list of cpus to monitor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a -C option to stat, record, top to designate a list of CPUs to monitor. CPUs can be specified as a comma-separated list or ranges, no space allowed. Examples: $ perf record -a -C0-1,4-7 sleep 1 $ perf top -C0-4 $ perf stat -a -C1,2,3,4 sleep 1 With perf record in per-thread mode with inherit mode on, samples are collected only when the thread runs on the designated CPUs. The -C option does not turn on system-wide mode automatically. Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: <4bff9496.d345d80a.41fe.7b00@mx.google.com> Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 7 ++++ tools/perf/Documentation/perf-stat.txt | 7 ++++ tools/perf/Documentation/perf-top.txt | 8 +++-- tools/perf/builtin-record.c | 23 ++++++++----- tools/perf/builtin-stat.c | 14 +++++--- tools/perf/builtin-top.c | 16 +++++---- tools/perf/util/cpumap.c | 57 +++++++++++++++++++++++++++++++- tools/perf/util/cpumap.h | 2 +- 8 files changed, 110 insertions(+), 24 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 34e255fc3e2..25576b477c8 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -103,6 +103,13 @@ OPTIONS --raw-samples:: Collect raw sample records from all opened counters (default for tracepoint counters). +-C:: +--cpu:: +Collect samples only on the list of cpus provided. Multiple CPUs can be provided as a +comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. +In per-thread mode with inheritance mode on (default), samples are captured only when +the thread executes on the designated CPUs. Default is to monitor all CPUs. + SEE ALSO -------- linkperf:perf-stat[1], linkperf:perf-list[1] diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 909fa766fa1..4b3a2d46b43 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -46,6 +46,13 @@ OPTIONS -B:: print large numbers with thousands' separators according to locale +-C:: +--cpu=:: +Count only on the list of cpus provided. Multiple CPUs can be provided as a +comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. +In per-thread mode, this option is ignored. The -a option is still necessary +to activate system-wide monitoring. Default is to count on all CPUs. + EXAMPLES -------- diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 785b9fc32a4..1f9687663f2 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -25,9 +25,11 @@ OPTIONS --count=:: Event period to sample. --C :: ---CPU=:: - CPU to profile. +-C :: +--cpu=:: +Monitor only on the list of cpus provided. Multiple CPUs can be provided as a +comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. +Default is to monitor all CPUS. -d :: --delay=:: diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index dc3435e18bd..f28c4bbd801 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -49,7 +49,6 @@ static int group = 0; static int realtime_prio = 0; static bool raw_samples = false; static bool system_wide = false; -static int profile_cpu = -1; static pid_t target_pid = -1; static pid_t target_tid = -1; static pid_t *all_tids = NULL; @@ -74,6 +73,7 @@ static int file_new = 1; static off_t post_processing_offset; static struct perf_session *session; +static const char *cpu_list; struct mmap_data { int counter; @@ -300,7 +300,7 @@ try_again: die("Permission error - are you root?\n" "\t Consider tweaking" " /proc/sys/kernel/perf_event_paranoid.\n"); - else if (err == ENODEV && profile_cpu != -1) { + else if (err == ENODEV && cpu_list) { die("No such device - did you specify" " an out-of-range profile CPU?\n"); } @@ -622,10 +622,15 @@ static int __cmd_record(int argc, const char **argv) close(child_ready_pipe[0]); } - if ((!system_wide && no_inherit) || profile_cpu != -1) { - open_counters(profile_cpu); + nr_cpus = read_cpu_map(cpu_list); + if (nr_cpus < 1) { + perror("failed to collect number of CPUs\n"); + return -1; + } + + if (!system_wide && no_inherit && !cpu_list) { + open_counters(-1); } else { - nr_cpus = read_cpu_map(); for (i = 0; i < nr_cpus; i++) open_counters(cpumap[i]); } @@ -704,7 +709,7 @@ static int __cmd_record(int argc, const char **argv) if (perf_guest) perf_session__process_machines(session, event__synthesize_guest_os); - if (!system_wide && profile_cpu == -1) + if (!system_wide && cpu_list) event__synthesize_thread(target_tid, process_synthesized_event, session); else @@ -794,8 +799,8 @@ static const struct option options[] = { "system-wide collection from all CPUs"), OPT_BOOLEAN('A', "append", &append_file, "append to the output file to do incremental profiling"), - OPT_INTEGER('C', "profile_cpu", &profile_cpu, - "CPU to profile on"), + OPT_STRING('C', "cpu", &cpu_list, "cpu", + "list of cpus to monitor"), OPT_BOOLEAN('f', "force", &force, "overwrite existing data file (deprecated)"), OPT_U64('c', "count", &user_interval, "event period to sample"), @@ -825,7 +830,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) argc = parse_options(argc, argv, options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (!argc && target_pid == -1 && target_tid == -1 && - !system_wide && profile_cpu == -1) + !system_wide && !cpu_list) usage_with_options(record_usage, options); if (force && append_file) { diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 9a39ca3c3ac..a6b4d44f950 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -69,7 +69,7 @@ static struct perf_event_attr default_attrs[] = { }; static bool system_wide = false; -static unsigned int nr_cpus = 0; +static int nr_cpus = 0; static int run_idx = 0; static int run_count = 1; @@ -82,6 +82,7 @@ static int thread_num = 0; static pid_t child_pid = -1; static bool null_run = false; static bool big_num = false; +static const char *cpu_list; static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; @@ -158,7 +159,7 @@ static int create_perf_stat_counter(int counter) PERF_FORMAT_TOTAL_TIME_RUNNING; if (system_wide) { - unsigned int cpu; + int cpu; for (cpu = 0; cpu < nr_cpus; cpu++) { fd[cpu][counter][0] = sys_perf_event_open(attr, @@ -208,7 +209,7 @@ static inline int nsec_counter(int counter) static void read_counter(int counter) { u64 count[3], single_count[3]; - unsigned int cpu; + int cpu; size_t res, nv; int scaled; int i, thread; @@ -542,6 +543,8 @@ static const struct option options[] = { "null run - dont start any counters"), OPT_BOOLEAN('B', "big-num", &big_num, "print large numbers with thousands\' separators"), + OPT_STRING('C', "cpu", &cpu_list, "cpu", + "list of cpus to monitor in system-wide"), OPT_END() }; @@ -566,10 +569,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) } if (system_wide) - nr_cpus = read_cpu_map(); + nr_cpus = read_cpu_map(cpu_list); else nr_cpus = 1; + if (nr_cpus < 1) + usage_with_options(stat_usage, options); + if (target_pid != -1) { target_tid = target_pid; thread_num = find_all_tid(target_pid, &all_tids); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index a66f4272b99..45014ef1105 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -102,6 +102,7 @@ struct sym_entry *sym_filter_entry_sched = NULL; static int sym_pcnt_filter = 5; static int sym_counter = 0; static int display_weighted = -1; +static const char *cpu_list; /* * Symbols @@ -1351,8 +1352,8 @@ static const struct option options[] = { "profile events on existing thread id"), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), - OPT_INTEGER('C', "CPU", &profile_cpu, - "CPU to profile on"), + OPT_STRING('C', "cpu", &cpu_list, "cpu", + "list of cpus to monitor"), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, @@ -1428,10 +1429,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) return -ENOMEM; /* CPU and PID are mutually exclusive */ - if (target_tid > 0 && profile_cpu != -1) { + if (target_tid > 0 && cpu_list) { printf("WARNING: PID switch overriding CPU\n"); sleep(1); - profile_cpu = -1; + cpu_list = NULL; } if (!nr_counters) @@ -1469,10 +1470,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) attrs[counter].sample_period = default_interval; } - if (target_tid != -1 || profile_cpu != -1) + if (target_tid != -1) nr_cpus = 1; else - nr_cpus = read_cpu_map(); + nr_cpus = read_cpu_map(cpu_list); + + if (nr_cpus < 1) + usage_with_options(top_usage, options); get_term_dimensions(&winsize); if (print_entries == 0) { diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 4e01490e51e..0f9b8d7a7d7 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -20,7 +20,7 @@ static int default_cpu_map(void) return nr_cpus; } -int read_cpu_map(void) +static int read_all_cpu_map(void) { FILE *onlnf; int nr_cpus = 0; @@ -57,3 +57,58 @@ int read_cpu_map(void) return default_cpu_map(); } + +int read_cpu_map(const char *cpu_list) +{ + unsigned long start_cpu, end_cpu = 0; + char *p = NULL; + int i, nr_cpus = 0; + + if (!cpu_list) + return read_all_cpu_map(); + + if (!isdigit(*cpu_list)) + goto invalid; + + while (isdigit(*cpu_list)) { + p = NULL; + start_cpu = strtoul(cpu_list, &p, 0); + if (start_cpu >= INT_MAX + || (*p != '\0' && *p != ',' && *p != '-')) + goto invalid; + + if (*p == '-') { + cpu_list = ++p; + p = NULL; + end_cpu = strtoul(cpu_list, &p, 0); + + if (end_cpu >= INT_MAX || (*p != '\0' && *p != ',')) + goto invalid; + + if (end_cpu < start_cpu) + goto invalid; + } else { + end_cpu = start_cpu; + } + + for (; start_cpu <= end_cpu; start_cpu++) { + /* check for duplicates */ + for (i = 0; i < nr_cpus; i++) + if (cpumap[i] == (int)start_cpu) + goto invalid; + + assert(nr_cpus < MAX_NR_CPUS); + cpumap[nr_cpus++] = (int)start_cpu; + } + if (*p) + ++p; + + cpu_list = p; + } + if (nr_cpus > 0) + return nr_cpus; + + return default_cpu_map(); +invalid: + return -1; +} diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 86c78bb3309..3e60f56e490 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -1,7 +1,7 @@ #ifndef __PERF_CPUMAP_H #define __PERF_CPUMAP_H -extern int read_cpu_map(void); +extern int read_cpu_map(const char *cpu_list); extern int cpumap[]; #endif /* __PERF_CPUMAP_H */ -- cgit v1.2.3-18-g5258 From 8e5564e6c7554902301543e731354ad2ad58ae53 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 31 May 2010 11:13:21 -0300 Subject: perf tools: Make target to generate self contained source tarball MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Useful for when people want to try some version of the perf tools and don't wants to download the kernel tarball. Here is a session using this new target: [root@emilia linux-2.6-tip]# make help | grep -i perf perf-tar-src-pkg - Build perf-2.6.35-rc1.tar source tarball perf-targz-src-pkg - Build perf-2.6.35-rc1.tar.gz source tarball perf-tarbz2-src-pkg - Build perf-2.6.35-rc1.tar.bz2 source tarball [root@emilia linux-2.6-tip]# make perf-tarbz2-src-pkg TAR [root@emilia linux-2.6-tip]# ls -la perf-2.6.35-rc1.tar.bz2 -rw-r--r-- 1 root root 295731 May 31 11:18 perf-2.6.35-rc1.tar.bz2 [root@emilia linux-2.6-tip]# tar xf perf-2.6.35-rc1.tar.bz2 [root@emilia linux-2.6-tip]# cd perf-2.6.35-rc1 [root@emilia perf-2.6.35-rc1]# ls arch HEAD include lib tools [root@emilia perf-2.6.35-rc1]# cd tools/perf [root@emilia perf]# make -j9 2>&1 | tail CC arch/x86/util/dwarf-regs.o CC util/probe-finder.o CC util/newt.o CC util/scripting-engines/trace-event-perl.o CC scripts/perl/Perf-Trace-Util/Context.o CC perf.o CC builtin-help.o AR libperf.a LINK perf rm .perf.dev.null [root@emilia perf]# ./perf record -a sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.262 MB perf.data (~11457 samples) ] [root@emilia perf]# ./perf report | head -12 # Events: 6K cycles # # Overhead Command Shared Object Symbol # ........ ............... .................. ...... # 4.73% perf [kernel.kallsyms] [k] format_decode 4.49% perf libc-2.12.so [.] _IO_file_underflow_internal 4.38% init [kernel.kallsyms] [k] mwait_idle 3.29% perf [kernel.kallsyms] [k] vsnprintf 2.38% init [kernel.kallsyms] [k] sched_clock_local 2.35% init [kernel.kallsyms] [k] apic_timer_interrupt 1.86% sirq-timer/5 [kernel.kallsyms] [k] find_busiest_group [root@emilia perf]# Acked-by: Michal Marek Acked-by: Sam Ravnborg Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Michal Marek Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Sam Ravnborg Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: <20100528185357.GA28009@ghostprotocols.net> Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/MANIFEST | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tools/perf/MANIFEST (limited to 'tools') diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST new file mode 100644 index 00000000000..8c7fc0c8f0b --- /dev/null +++ b/tools/perf/MANIFEST @@ -0,0 +1,12 @@ +tools/perf +include/linux/perf_event.h +include/linux/rbtree.h +include/linux/list.h +include/linux/hash.h +include/linux/stringify.h +lib/rbtree.c +include/linux/swab.h +arch/*/include/asm/unistd*.h +include/linux/poison.h +include/linux/magic.h +include/linux/hw_breakpoint.h -- cgit v1.2.3-18-g5258 From 45de34bbe3e1b8f4c8bc8ecaf6c915b4b4c545f8 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Tue, 1 Jun 2010 21:25:01 +0200 Subject: perf buildid: add perfconfig option to specify buildid cache dir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the ability to specify an alternate directory to store the buildid cache (buildids, copy of binaries). By default, it is hardcoded to $HOME/.debug. This directory contains immutable data. The layout of the directory is such that no conflicts in filenames are possible. A modification in a file, yields a different buildid and thus a different location in the subdir hierarchy. You may want to put the buildid cache elsewhere because of disk space limitation or simply to share the cache between users. It is also useful for remote collect vs. local analysis of profiles. This patch adds a new config option to the perfconfig file. Under the tag 'buildid', there is a dir option. For instance, if you have: $ cat /etc/perfconfig [buildid] dir = /var/cache/perf-buildid All buildids and binaries are be saved in the directory specified. The perf record, buildid-list, buildid-cache, report, annotate, and archive commands will it to pull information out. The option can be set in the system-wide perfconfig file or in the $HOME/.perfconfig file. Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: <4c055fb7.df0ce30a.5f0d.ffffae52@mx.google.com> Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-buildid-cache.c | 3 +- tools/perf/perf-archive.sh | 20 +++++++++--- tools/perf/perf.c | 2 ++ tools/perf/util/build-id.c | 10 +++--- tools/perf/util/cache.h | 1 + tools/perf/util/config.c | 64 +++++++++++++++++++++++++++++++++++++- tools/perf/util/header.c | 3 +- tools/perf/util/symbol.h | 2 -- tools/perf/util/util.h | 2 ++ 9 files changed, 89 insertions(+), 18 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index f8e3d185202..29ad20e6791 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -78,8 +78,7 @@ static int __cmd_buildid_cache(void) struct str_node *pos; char debugdir[PATH_MAX]; - snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), - DEBUG_CACHE_DIR); + snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); if (add_name_list_str) { list = strlist__new(true, add_name_list_str); diff --git a/tools/perf/perf-archive.sh b/tools/perf/perf-archive.sh index 2e7a4f417e2..677e59d62a8 100644 --- a/tools/perf/perf-archive.sh +++ b/tools/perf/perf-archive.sh @@ -7,7 +7,17 @@ if [ $# -ne 0 ] ; then PERF_DATA=$1 fi -DEBUGDIR=~/.debug/ +# +# PERF_BUILDID_DIR environment variable set by perf +# path to buildid directory, default to $HOME/.debug +# +if [ -z $PERF_BUILDID_DIR ]; then + PERF_BUILDID_DIR=~/.debug/ +else + # append / to make substitutions work + PERF_BUILDID_DIR=$PERF_BUILDID_DIR/ +fi + BUILDIDS=$(mktemp /tmp/perf-archive-buildids.XXXXXX) NOBUILDID=0000000000000000000000000000000000000000 @@ -22,13 +32,13 @@ MANIFEST=$(mktemp /tmp/perf-archive-manifest.XXXXXX) cut -d ' ' -f 1 $BUILDIDS | \ while read build_id ; do - linkname=$DEBUGDIR.build-id/${build_id:0:2}/${build_id:2} + linkname=$PERF_BUILDID_DIR.build-id/${build_id:0:2}/${build_id:2} filename=$(readlink -f $linkname) - echo ${linkname#$DEBUGDIR} >> $MANIFEST - echo ${filename#$DEBUGDIR} >> $MANIFEST + echo ${linkname#$PERF_BUILDID_DIR} >> $MANIFEST + echo ${filename#$PERF_BUILDID_DIR} >> $MANIFEST done -tar cfj $PERF_DATA.tar.bz2 -C $DEBUGDIR -T $MANIFEST +tar cfj $PERF_DATA.tar.bz2 -C $PERF_BUILDID_DIR -T $MANIFEST rm -f $MANIFEST $BUILDIDS echo -e "Now please run:\n" echo -e "$ tar xvf $PERF_DATA.tar.bz2 -C ~/.debug\n" diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 6e487119113..cdd6c03f1e1 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -458,6 +458,8 @@ int main(int argc, const char **argv) handle_options(&argv, &argc, NULL); commit_pager_choice(); set_debugfs_path(); + set_buildid_dir(); + if (argc > 0) { if (!prefixcmp(argv[0], "--")) argv[0] += 2; diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 70c5cf87d02..5c26e2d314a 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -43,19 +43,17 @@ struct perf_event_ops build_id__mark_dso_hit_ops = { char *dso__build_id_filename(struct dso *self, char *bf, size_t size) { char build_id_hex[BUILD_ID_SIZE * 2 + 1]; - const char *home; if (!self->has_build_id) return NULL; build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); - home = getenv("HOME"); if (bf == NULL) { - if (asprintf(&bf, "%s/%s/.build-id/%.2s/%s", home, - DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2) < 0) + if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir, + build_id_hex, build_id_hex + 2) < 0) return NULL; } else - snprintf(bf, size, "%s/%s/.build-id/%.2s/%s", home, - DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2); + snprintf(bf, size, "%s/.build-id/%.2s/%s", buildid_dir, + build_id_hex, build_id_hex + 2); return bf; } diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 65fe664fddf..27e9ebe4076 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -23,6 +23,7 @@ extern int perf_config(config_fn_t fn, void *); extern int perf_config_int(const char *, const char *); extern int perf_config_bool(const char *, const char *); extern int config_error_nonbool(const char *); +extern const char *perf_config_dirname(const char *, const char *); /* pager.c */ extern void setup_pager(void); diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index dabe892d0e5..e02d78cae70 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -11,6 +11,11 @@ #define MAXNAME (256) +#define DEBUG_CACHE_DIR ".debug" + + +char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */ + static FILE *config_file; static const char *config_file_name; static int config_linenr; @@ -127,7 +132,7 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) break; if (!iskeychar(c)) break; - name[len++] = tolower(c); + name[len++] = c; if (len >= MAXNAME) return -1; } @@ -327,6 +332,13 @@ int perf_config_bool(const char *name, const char *value) return !!perf_config_bool_or_int(name, value, &discard); } +const char *perf_config_dirname(const char *name, const char *value) +{ + if (!name) + return NULL; + return value; +} + static int perf_default_core_config(const char *var __used, const char *value __used) { /* Add other config variables here and to Documentation/config.txt. */ @@ -428,3 +440,53 @@ int config_error_nonbool(const char *var) { return error("Missing value for '%s'", var); } + +struct buildid_dir_config { + char *dir; +}; + +static int buildid_dir_command_config(const char *var, const char *value, + void *data) +{ + struct buildid_dir_config *c = data; + const char *v; + + /* same dir for all commands */ + if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) { + v = perf_config_dirname(var, value); + if (!v) + return -1; + strncpy(c->dir, v, MAXPATHLEN-1); + c->dir[MAXPATHLEN-1] = '\0'; + } + return 0; +} + +static void check_buildid_dir_config(void) +{ + struct buildid_dir_config c; + c.dir = buildid_dir; + perf_config(buildid_dir_command_config, &c); +} + +void set_buildid_dir(void) +{ + buildid_dir[0] = '\0'; + + /* try config file */ + check_buildid_dir_config(); + + /* default to $HOME/.debug */ + if (buildid_dir[0] == '\0') { + char *v = getenv("HOME"); + if (v) { + snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", + v, DEBUG_CACHE_DIR); + } else { + strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); + } + buildid_dir[MAXPATHLEN-1] = '\0'; + } + /* for communicating with external commands */ + setenv("PERF_BUILDID_DIR", buildid_dir, 1); +} diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 1f62435f96c..4a6a4b3a4ab 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -385,8 +385,7 @@ static int perf_session__cache_build_ids(struct perf_session *self) int ret; char debugdir[PATH_MAX]; - snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), - DEBUG_CACHE_DIR); + snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) return -1; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 5e02d2c1715..34760a2fc60 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -9,8 +9,6 @@ #include #include -#define DEBUG_CACHE_DIR ".debug" - #ifdef HAVE_CPLUS_DEMANGLE extern char *cplus_demangle(const char *, int); diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 4e8b6b0c551..de61441b6dd 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -89,6 +89,7 @@ extern const char *graph_line; extern const char *graph_dotted_line; +extern char buildid_dir[]; /* On most systems would have given us this, but * not on some systems (e.g. GNU/Hurd). @@ -152,6 +153,7 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))) extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); extern int prefixcmp(const char *str, const char *prefix); +extern void set_buildid_dir(void); static inline const char *skip_prefix(const char *str, const char *prefix) { -- cgit v1.2.3-18-g5258 From 45d8e8025a2b2a6996be92d769fb6763bfb3cbae Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 3 Jun 2010 15:50:01 +0200 Subject: perf annotate: Ask objdump to demangle symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Perf report is demangling symbols but not annotate. The former uses internal demangling via libbdf or libiberty. The latter executes objdump which by default does not demangle symbols. This patch adds the -C option to the objdump cmdline to enable symbol demangling. Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: <4c07b323.2126e30a.6245.0e1e@mx.google.com> Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 07f89b66b31..9e6baad92c4 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1037,7 +1037,7 @@ fallback: dso, dso->long_name, sym, sym->name); snprintf(command, sizeof(command), - "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", + "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", map__rip_2objdump(map, sym->start), map__rip_2objdump(map, sym->end), filename, filename); -- cgit v1.2.3-18-g5258 From 41a37e20178b081193b08b228030d8f562bfee62 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 4 Jun 2010 08:02:07 -0300 Subject: perf tools: Make event__preprocess_sample parse the sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplifying the tools that were using both in sequence and allowing upcoming simplifications, such as Arun's patch to sort by cpus. Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 6 ++---- tools/perf/builtin-diff.c | 7 +------ tools/perf/builtin-report.c | 26 +------------------------- tools/perf/builtin-top.c | 4 +++- tools/perf/util/callchain.c | 2 +- tools/perf/util/callchain.h | 2 +- tools/perf/util/event.c | 33 +++++++++++++++++++++++++++++---- tools/perf/util/event.h | 5 +++-- 8 files changed, 41 insertions(+), 44 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 96db5248e99..fd20670ce98 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -61,11 +61,9 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) static int process_sample_event(event_t *event, struct perf_session *session) { struct addr_location al; + struct sample_data data; - dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, - event->ip.pid, event->ip.ip); - - if (event__preprocess_sample(event, session, &al, NULL) < 0) { + if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { pr_warning("problem processing %d event, skipping it.\n", event->header.type); return -1; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index a6e2fdc7a04..39e6627ebb9 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -35,10 +35,7 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi struct addr_location al; struct sample_data data = { .period = 1, }; - dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, - event->ip.pid, event->ip.ip); - - if (event__preprocess_sample(event, session, &al, NULL) < 0) { + if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { pr_warning("problem processing %d event, skipping it.\n", event->header.type); return -1; @@ -47,8 +44,6 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi if (al.filtered || al.sym == NULL) return 0; - event__parse_sample(event, session->sample_type, &data); - if (hists__add_entry(&session->hists, &al, data.period)) { pr_warning("problem incrementing symbol period, skipping event\n"); return -1; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 207da184980..371a3c99580 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -155,31 +155,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) struct addr_location al; struct perf_event_attr *attr; - event__parse_sample(event, session->sample_type, &data); - - dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n", - event->header.misc, data.pid, data.tid, data.ip, - data.period, data.cpu); - - if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { - unsigned int i; - - dump_printf("... chain: nr:%Lu\n", data.callchain->nr); - - if (!ip_callchain__valid(data.callchain, event)) { - pr_debug("call-chain problem with event, " - "skipping it.\n"); - return 0; - } - - if (dump_trace) { - for (i = 0; i < data.callchain->nr; i++) - dump_printf("..... %2d: %016Lx\n", - i, data.callchain->ips[i]); - } - } - - if (event__preprocess_sample(event, session, &al, NULL) < 0) { + if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { fprintf(stderr, "problem processing %d event, skipping it.\n", event->header.type); return -1; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 45014ef1105..1e8e92e317b 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -983,6 +983,7 @@ static void event__process_sample(const event_t *self, u64 ip = self->ip.ip; struct sym_entry *syme; struct addr_location al; + struct sample_data data; struct machine *machine; u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; @@ -1025,7 +1026,8 @@ static void event__process_sample(const event_t *self, if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) exact_samples++; - if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || + if (event__preprocess_sample(self, session, &al, &data, + symbol_filter) < 0 || al.filtered) return; diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 62b69ad4aa7..e63c997d6c1 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -18,7 +18,7 @@ #include "util.h" #include "callchain.h" -bool ip_callchain__valid(struct ip_callchain *chain, event_t *event) +bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) { unsigned int chain_size = event->header.size; chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 1ca73e4a272..809850fb75f 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -60,5 +60,5 @@ int register_callchain_param(struct callchain_param *param); int append_chain(struct callchain_node *root, struct ip_callchain *chain, struct map_symbol *syms); -bool ip_callchain__valid(struct ip_callchain *chain, event_t *event); +bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 891753255f5..ed3e14ff6df 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -655,11 +655,36 @@ static void dso__calc_col_width(struct dso *self) } int event__preprocess_sample(const event_t *self, struct perf_session *session, - struct addr_location *al, symbol_filter_t filter) + struct addr_location *al, struct sample_data *data, + symbol_filter_t filter) { u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - struct thread *thread = perf_session__findnew(session, self->ip.pid); + struct thread *thread; + + event__parse_sample(self, session->sample_type, data); + + dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n", + self->header.misc, data->pid, data->tid, data->ip, + data->period, data->cpu); + + if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { + unsigned int i; + + dump_printf("... chain: nr:%Lu\n", data->callchain->nr); + if (!ip_callchain__valid(data->callchain, self)) { + pr_debug("call-chain problem with event, " + "skipping it.\n"); + goto out_filtered; + } + + if (dump_trace) { + for (i = 0; i < data->callchain->nr; i++) + dump_printf("..... %2d: %016Lx\n", + i, data->callchain->ips[i]); + } + } + thread = perf_session__findnew(session, self->ip.pid); if (thread == NULL) return -1; @@ -724,9 +749,9 @@ out_filtered: return 0; } -int event__parse_sample(event_t *event, u64 type, struct sample_data *data) +int event__parse_sample(const event_t *event, u64 type, struct sample_data *data) { - u64 *array = event->sample.array; + const u64 *array = event->sample.array; if (type & PERF_SAMPLE_IP) { data->ip = event->ip.ip; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 8577085db06..887ee63bbb6 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -157,8 +157,9 @@ int event__process_task(event_t *self, struct perf_session *session); struct addr_location; int event__preprocess_sample(const event_t *self, struct perf_session *session, - struct addr_location *al, symbol_filter_t filter); -int event__parse_sample(event_t *event, u64 type, struct sample_data *data); + struct addr_location *al, struct sample_data *data, + symbol_filter_t filter); +int event__parse_sample(const event_t *event, u64 type, struct sample_data *data); extern const char *event__name[]; -- cgit v1.2.3-18-g5258 From f60f359383edf2a0ec3aa32cf8be98ad815bdf65 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Fri, 4 Jun 2010 11:27:10 -0300 Subject: perf report: Implement --sort cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a shared multi-core environment, users want to analyze why their program was slow. In particular, if the code ran slower only on certain CPUs due to interference from other programs or kernel threads, the user should be able to notice that. Sample usage: perf record -f -a -- sleep 3 perf report --sort cpu,comm Workload: program is running on 16 CPUs Experiencing interference from an antagonist only on 4 CPUs. Samples: 106218177676 cycles Overhead CPU Command ........ ... ............... 6.25% 2 program 6.24% 6 program 6.24% 11 program 6.24% 5 program 6.24% 9 program 6.24% 10 program 6.23% 15 program 6.23% 7 program 6.23% 3 program 6.23% 14 program 6.22% 1 program 6.20% 13 program 3.17% 12 program 3.15% 8 program 3.14% 0 program 3.13% 4 program 3.11% 4 antagonist 3.11% 0 antagonist 3.10% 8 antagonist 3.07% 12 antagonist Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: <20100505181612.GA5091@sharma-home.net> Signed-off-by: Arun Sharma Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 3 +++ tools/perf/util/event.c | 1 + tools/perf/util/hist.c | 1 + tools/perf/util/sort.c | 27 +++++++++++++++++++++++++++ tools/perf/util/sort.h | 6 +++++- tools/perf/util/symbol.h | 3 ++- 6 files changed, 39 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f28c4bbd801..5e5c6403a31 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -274,6 +274,9 @@ static void create_counter(int counter, int cpu) if (call_graph) attr->sample_type |= PERF_SAMPLE_CALLCHAIN; + if (system_wide) + attr->sample_type |= PERF_SAMPLE_CPU; + if (raw_samples) { attr->sample_type |= PERF_SAMPLE_TIME; attr->sample_type |= PERF_SAMPLE_RAW; diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index ed3e14ff6df..a7460868124 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -710,6 +710,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, al->map ? al->map->dso->long_name : al->level == 'H' ? "[hypervisor]" : ""); al->sym = NULL; + al->cpu = data->cpu; if (al->map) { if (symbol_conf.dso_list && diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9e6baad92c4..68d288c975d 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -70,6 +70,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, .map = al->map, .sym = al->sym, }, + .cpu = al->cpu, .ip = al->addr, .level = al->level, .period = period, diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 2316cb5a411..c27b4b03fbc 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -13,6 +13,7 @@ enum sort_type sort__first_dimension; unsigned int dsos__col_width; unsigned int comms__col_width; unsigned int threads__col_width; +unsigned int cpus__col_width; static unsigned int parent_symbol__col_width; char * field_sep; @@ -28,6 +29,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, size_t size, unsigned int width); static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, size_t size, unsigned int width); +static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); struct sort_entry sort_thread = { .se_header = "Command: Pid", @@ -63,6 +66,13 @@ struct sort_entry sort_parent = { .se_snprintf = hist_entry__parent_snprintf, .se_width = &parent_symbol__col_width, }; + +struct sort_entry sort_cpu = { + .se_header = "CPU", + .se_cmp = sort__cpu_cmp, + .se_snprintf = hist_entry__cpu_snprintf, + .se_width = &cpus__col_width, +}; struct sort_dimension { const char *name; @@ -76,6 +86,7 @@ static struct sort_dimension sort_dimensions[] = { { .name = "dso", .entry = &sort_dso, }, { .name = "symbol", .entry = &sort_sym, }, { .name = "parent", .entry = &sort_parent, }, + { .name = "cpu", .entry = &sort_cpu, }, }; int64_t cmp_null(void *l, void *r) @@ -242,6 +253,20 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, self->parent ? self->parent->name : "[other]"); } +/* --sort cpu */ + +int64_t +sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return right->cpu - left->cpu; +} + +static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + return repsep_snprintf(bf, size, "%-*d", width, self->cpu); +} + int sort_dimension__add(const char *tok) { unsigned int i; @@ -281,6 +306,8 @@ int sort_dimension__add(const char *tok) sort__first_dimension = SORT_SYM; else if (!strcmp(sd->name, "parent")) sort__first_dimension = SORT_PARENT; + else if (!strcmp(sd->name, "cpu")) + sort__first_dimension = SORT_CPU; } list_add_tail(&sd->entry->list, &hist_entry__sort_list); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 0d61c4082f4..560c855417e 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -39,6 +39,7 @@ extern struct sort_entry sort_parent; extern unsigned int dsos__col_width; extern unsigned int comms__col_width; extern unsigned int threads__col_width; +extern unsigned int cpus__col_width; extern enum sort_type sort__first_dimension; struct hist_entry { @@ -51,6 +52,7 @@ struct hist_entry { struct map_symbol ms; struct thread *thread; u64 ip; + s32 cpu; u32 nr_events; char level; u8 filtered; @@ -68,7 +70,8 @@ enum sort_type { SORT_COMM, SORT_DSO, SORT_SYM, - SORT_PARENT + SORT_PARENT, + SORT_CPU, }; /* @@ -104,6 +107,7 @@ extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *); extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); +int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right); extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); extern int sort_dimension__add(const char *); void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 34760a2fc60..10b7ff859ce 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -110,7 +110,8 @@ struct addr_location { u64 addr; char level; bool filtered; - unsigned int cpumode; + u8 cpumode; + s32 cpu; }; enum dso_kernel_type { -- cgit v1.2.3-18-g5258 From 3af9e859281bda7eb7c20b51879cf43aa788ac2e Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Tue, 18 May 2010 15:30:49 +0100 Subject: perf: Add non-exec mmap() tracking Add the capacility to track data mmap()s. This can be used together with PERF_SAMPLE_ADDR for data profiling. Signed-off-by: Anton Blanchard [Updated code for stable perf ABI] Signed-off-by: Eric B Munson Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Mike Galbraith Cc: Steven Rostedt LKML-Reference: <1274193049-25997-1-git-send-email-ebmunson@us.ibm.com> Signed-off-by: Ingo Molnar --- tools/perf/builtin-record.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 5e5c6403a31..39c7247bc54 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -268,8 +268,10 @@ static void create_counter(int counter, int cpu) if (inherit_stat) attr->inherit_stat = 1; - if (sample_address) + if (sample_address) { attr->sample_type |= PERF_SAMPLE_ADDR; + attr->mmap_data = track; + } if (call_graph) attr->sample_type |= PERF_SAMPLE_CALLCHAIN; -- cgit v1.2.3-18-g5258 From f9af3a4c1f59753bdd5a49e3a34263005f96972e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 9 Jun 2010 16:57:39 -0300 Subject: perf tools: Reorganize the Makefile feature tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moving the tests to a separate file, feature-tests.mak and using a try-cc function similar to the try-run in Kbuild. This also makes the output more quiet as we can stop using the INTERMEDIATE target to remove the .perf.dev.null file needed for some gcc versions where /dev/null can't be used as the output file name. As the tests get shorter by uninlining the source code used to test for features, we can more properly use identation. The feature tests itself can be made more clear and reused, like when trying to see what is needed to have bfd_demangle. We also get a bit closer to reusing scripts/Kbuild.include, reducing the distance from the kernel build system. Tests performed: [root@emilia perf]# make -j9 O=/tmp/perf PERF_VERSION = 0.0.2.PERF GEN /tmp/perf/common-cmds.h * new build flags or prefix GEN perf-archive CC /tmp/perf/builtin-annotate.o CC /tmp/perf/bench/sched-messaging.o CC /tmp/perf/builtin-diff.o CC /tmp/perf/scripts/python/Perf-Trace-Util/Context.o CC /tmp/perf/perf.o CC /tmp/perf/builtin-help.o AR /tmp/perf/libperf.a LINK /tmp/perf/perf [root@emilia perf]# If we uninstall, for instance newt-devel we get: [root@emilia perf]# rpm -e newt-devel [root@emilia perf]# make -j9 O=/tmp/perf Makefile:564: newt not found, disables TUI support. Please install newt-devel or libnewt-dev * new build flags or prefix GEN perf-archive CC /tmp/perf/perf.o CC /tmp/perf/builtin-annotate.o AR /tmp/perf/libperf.a LINK /tmp/perf/perf [root@emilia perf]# And then binutils-devel: [root@emilia perf]# make -j9 O=/tmp/perf Makefile:564: newt not found, disables TUI support. Please install newt-devel or libnewt-dev Makefile:632: No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling * new build flags or prefix GEN perf-archive CC /tmp/perf/perf.o AR /tmp/perf/libperf.a LINK /tmp/perf/perf [root@emilia perf]# And then strictly required devel packages: [root@emilia perf]# rpm -e elfutils-libelf-devel elfutils-devel [root@emilia perf]# make -j9 O=/tmp/perf Makefile:509: No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev Makefile:542: *** No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel. Stop. [root@emilia perf]# After installing everything back on: [root@emilia perf]# yum install elfutils-devel binutils-devel newt-devel Installed: binutils-devel.x86_64 0:2.20.51.0.2-5.11.el6 elfutils-devel.x86_64 0:0.147-1.el6 elfutils-libelf-devel.x86_64 0:0.147-1.el6 newt-devel.x86_64 0:0.52.11-1.el6 Complete! [root@emilia perf]# make -j9 PERF_VERSION = 0.0.2.PERF GEN common-cmds.h * new build flags or prefix GEN perf-archive CC builtin-annotate.o AR libperf.a LINK perf [root@emilia perf]# make -j9 [root@emilia perf]# Thanks to Sam for pointing me to try-run. Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Michal Marek Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Sam Ravnborg Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 109 ++++++++++++++++++++------------------- tools/perf/feature-tests.mak | 119 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 53 deletions(-) create mode 100644 tools/perf/feature-tests.mak (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 3d8f31ed771..6aa2fe323db 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -285,14 +285,10 @@ else QUIET_STDERR = ">/dev/null 2>&1" endif -BITBUCKET = "/dev/null" +-include feature-tests.mak -ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { return puts(\"hi\"); }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) "$(QUIET_STDERR)" && echo y"), y) - BITBUCKET = .perf.dev.null -endif - -ifeq ($(shell sh -c "echo 'int foo(void) {char X[2]; return 3;}' | $(CC) -x c -c -Werror -fstack-protector-all - -o $(BITBUCKET) "$(QUIET_STDERR)" && echo y"), y) - CFLAGS := $(CFLAGS) -fstack-protector-all +ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y) + CFLAGS := $(CFLAGS) -fstack-protector-all endif @@ -508,7 +504,8 @@ PERFLIBS = $(LIB_FILE) -include config.mak ifndef NO_DWARF -ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo '\#include '; echo '\#ifndef _ELFUTILS_PREREQ'; echo '\#error'; echo '\#endif'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) +FLAGS_DWARF=$(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS) +ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y) msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); NO_DWARF := 1 endif # Dwarf support @@ -536,16 +533,18 @@ ifneq ($(OUTPUT),) BASIC_CFLAGS += -I$(OUTPUT) endif -ifeq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) -ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { const char * version = gnu_get_libc_version(); return (long)version; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) - msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); +FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) +ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y) + FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS) + ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y) + msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); + else + msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel); + endif endif - ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ_MMAP, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) - BASIC_CFLAGS += -DLIBELF_NO_MMAP - endif -else - msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]); +ifneq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y) + BASIC_CFLAGS += -DLIBELF_NO_MMAP endif ifndef NO_DWARF @@ -561,41 +560,47 @@ endif # NO_DWARF ifdef NO_NEWT BASIC_CFLAGS += -DNO_NEWT_SUPPORT else -ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtInit(); newtCls(); return newtFinished(); }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -lnewt -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) - msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); - BASIC_CFLAGS += -DNO_NEWT_SUPPORT -else - # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h - BASIC_CFLAGS += -I/usr/include/slang - EXTLIBS += -lnewt -lslang - LIB_OBJS += $(OUTPUT)util/newt.o -endif -endif # NO_NEWT - -ifndef NO_LIBPERL -PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null` -PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` + FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt + ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y) + msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); + BASIC_CFLAGS += -DNO_NEWT_SUPPORT + else + # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h + BASIC_CFLAGS += -I/usr/include/slang + EXTLIBS += -lnewt -lslang + LIB_OBJS += $(OUTPUT)util/newt.o + endif endif -ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo 'int main(void) { perl_alloc(); return 0; }') | $(CC) -x c - $(PERL_EMBED_CCOPTS) -o $(BITBUCKET) $(PERL_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y) +ifdef NO_LIBPERL BASIC_CFLAGS += -DNO_LIBPERL else - ALL_LDFLAGS += $(PERL_EMBED_LDOPTS) - LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o - LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o -endif + PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null` + PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` + PERL_EMBED_FLAGS=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) -ifndef NO_LIBPYTHON -PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null` -PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` + ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y) + BASIC_CFLAGS += -DNO_LIBPERL + else + ALL_LDFLAGS += $(PERL_EMBED_LDOPTS) + LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o + LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o + endif endif -ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { Py_Initialize(); return 0; }') | $(CC) -x c - $(PYTHON_EMBED_CCOPTS) -o $(BITBUCKET) $(PYTHON_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y) +ifdef NO_LIBPYTHON BASIC_CFLAGS += -DNO_LIBPYTHON else - ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS) - LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o - LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o + PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null` + PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` + FLAGS_PYTHON_EMBED=$(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) + ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y) + BASIC_CFLAGS += -DNO_LIBPYTHON + else + ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS) + LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o + LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o + endif endif ifdef NO_DEMANGLE @@ -604,20 +609,23 @@ else ifdef HAVE_CPLUS_DEMANGLE EXTLIBS += -liberty BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE else - has_bfd := $(shell sh -c "(echo '\#include '; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd "$(QUIET_STDERR)" && echo y") - + FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd + has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD)) ifeq ($(has_bfd),y) EXTLIBS += -lbfd else - has_bfd_iberty := $(shell sh -c "(echo '\#include '; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd -liberty "$(QUIET_STDERR)" && echo y") + FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty + has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY)) ifeq ($(has_bfd_iberty),y) EXTLIBS += -lbfd -liberty else - has_bfd_iberty_z := $(shell sh -c "(echo '\#include '; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd -liberty -lz "$(QUIET_STDERR)" && echo y") + FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz + has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z)) ifeq ($(has_bfd_iberty_z),y) EXTLIBS += -lbfd -liberty -lz else - has_cplus_demangle := $(shell sh -c "(echo 'extern char *cplus_demangle(const char *, int);'; echo 'int main(void) { cplus_demangle(0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -liberty "$(QUIET_STDERR)" && echo y") + FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty + has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE)) ifeq ($(has_cplus_demangle),y) EXTLIBS += -liberty BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE @@ -865,7 +873,7 @@ export TAR INSTALL DESTDIR SHELL_PATH SHELL = $(SHELL_PATH) -all:: .perf.dev.null shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS +all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS ifneq (,$X) $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';) endif @@ -1195,11 +1203,6 @@ clean: .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS .PHONY: .FORCE-PERF-BUILD-OPTIONS -.perf.dev.null: - touch .perf.dev.null - -.INTERMEDIATE: .perf.dev.null - ### Make sure built-ins do not have dups and listed in perf.c # check-builtins:: diff --git a/tools/perf/feature-tests.mak b/tools/perf/feature-tests.mak new file mode 100644 index 00000000000..ddb68e601f0 --- /dev/null +++ b/tools/perf/feature-tests.mak @@ -0,0 +1,119 @@ +define SOURCE_HELLO +#include +int main(void) +{ + return puts(\"hi\"); +} +endef + +ifndef NO_DWARF +define SOURCE_DWARF +#include +#include +#include +#ifndef _ELFUTILS_PREREQ +#error +#endif + +int main(void) +{ + Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); + return (long)dbg; +} +endef +endif + +define SOURCE_LIBELF +#include + +int main(void) +{ + Elf *elf = elf_begin(0, ELF_C_READ, 0); + return (long)elf; +} +endef + +define SOURCE_GLIBC +#include + +int main(void) +{ + const char *version = gnu_get_libc_version(); + return (long)version; +} +endef + +define SOURCE_ELF_MMAP +#include +int main(void) +{ + Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0); + return (long)elf; +} +endef + +ifndef NO_NEWT +define SOURCE_NEWT +#include + +int main(void) +{ + newtInit(); + newtCls(); + return newtFinished(); +} +endef +endif + +ifndef NO_LIBPERL +define SOURCE_PERL_EMBED +#include +#include + +int main(void) +{ +perl_alloc(); +return 0; +} +endef +endif + +ifndef NO_LIBPYTHON +define SOURCE_PYTHON_EMBED +#include + +int main(void) +{ + Py_Initialize(); + return 0; +} +endef +endif + +define SOURCE_BFD +#include + +int main(void) +{ + bfd_demangle(0, 0, 0); + return 0; +} +endef + +define SOURCE_CPLUS_DEMANGLE +extern char *cplus_demangle(const char *, int); + +int main(void) +{ + cplus_demangle(0, 0); + return 0; +} +endef + +# try-cc +# Usage: option = $(call try-cc, source-to-build, cc-options) +try-cc = $(shell sh -c \ + 'TMP="$(TMPOUT).$$$$"; \ + echo "$(1)" | \ + $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \ + rm -f "$$TMP"') -- cgit v1.2.3-18-g5258 From cf103a14dd2ab23f847e998c8881ea4a5f8090bf Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Wed, 16 Jun 2010 20:59:01 +0200 Subject: perf record: Avoid synthesizing mmap() for all processes in per-thread mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bug was introduced by commit c45c6ea2e5c57960dc67e00294c2b78e9540c007. Perf record was scanning /proc/PID to create synthetic PERF_RECOR_MMAP entries even though it was running in per-thread mode. There was a bogus check to select what mmaps to synthesize. We only need all processes in system-wide mode. Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: <4c192107.4f1ee30a.4316.fffff98e@mx.google.com> Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 39c7247bc54..5efc3fc68a3 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -714,7 +714,7 @@ static int __cmd_record(int argc, const char **argv) if (perf_guest) perf_session__process_machines(session, event__synthesize_guest_os); - if (!system_wide && cpu_list) + if (!system_wide) event__synthesize_thread(target_tid, process_synthesized_event, session); else -- cgit v1.2.3-18-g5258 From 70c3856b2f1304e0abc65f1b96a8c60ddfc0fb9e Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Mon, 14 Jun 2010 14:56:33 +0100 Subject: perf symbols: Function descriptor symbol lookup Currently symbol resolution does not work for 64-bit programs on architectures that use function descriptors such as ppc64. The problem is that a symbol doesn't point to a text address, it points to a data area that contains (amongst other things) a pointer to the text address. We look for a section called ".opd" which is the function descriptor area. To create the full symbol table, when we see a symbol in the function descriptor section we load the first pointer and use that as the text address. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: <1276523793-15422-1-git-send-email-ebmunson@us.ibm.com> Signed-off-by: Anton Blanchard Signed-off-by: Eric B Munson Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b63e5713849..971d0a05d6b 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -933,6 +933,25 @@ static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type } } +static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) +{ + Elf_Scn *sec = NULL; + GElf_Shdr shdr; + size_t cnt = 1; + + while ((sec = elf_nextscn(elf, sec)) != NULL) { + gelf_getshdr(sec, &shdr); + + if ((addr >= shdr.sh_addr) && + (addr < (shdr.sh_addr + shdr.sh_size))) + return cnt; + + ++cnt; + } + + return -1; +} + static int dso__load_sym(struct dso *self, struct map *map, const char *name, int fd, symbol_filter_t filter, int kmodule) { @@ -944,12 +963,13 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, int err = -1; uint32_t idx; GElf_Ehdr ehdr; - GElf_Shdr shdr; - Elf_Data *syms; + GElf_Shdr shdr, opdshdr; + Elf_Data *syms, *opddata = NULL; GElf_Sym sym; - Elf_Scn *sec, *sec_strndx; + Elf_Scn *sec, *sec_strndx, *opdsec; Elf *elf; int nr = 0; + size_t opdidx = 0; elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); if (elf == NULL) { @@ -969,6 +989,10 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, goto out_elf_end; } + opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); + if (opdsec) + opddata = elf_rawdata(opdsec, NULL); + syms = elf_getdata(sec, NULL); if (syms == NULL) goto out_elf_end; @@ -1013,6 +1037,13 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, if (!is_label && !elf_sym__is_a(&sym, map->type)) continue; + if (opdsec && sym.st_shndx == opdidx) { + u32 offset = sym.st_value - opdshdr.sh_addr; + u64 *opd = opddata->d_buf + offset; + sym.st_value = *opd; + sym.st_shndx = elf_addr_to_index(elf, sym.st_value); + } + sec = elf_getscn(elf, sym.st_shndx); if (!sec) goto out_elf_end; -- cgit v1.2.3-18-g5258 From a1ac1d3c085420ea8c809ebbee3bb212ed3616bd Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 17 Jun 2010 11:39:01 +0200 Subject: perf record: Add option to avoid updating buildid cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are situations where there is enough information in the perf.data to process the samples. Updating the buildid cache may add unecessary overhead in terms of disk space and time (copying large elf images). A persistent option to do this already exists via the perfconfig file, simply do: [buildid] dir = /dev/null This patch provides a way to suppress builid cache updates on a per-run basis. It addds a new option, -N, to perf record. Buildids are still generated in the perf.data file. Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: <4c19ef89.93ecd80a.40dc.fffff8e9@mx.google.com> Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 6 ++++++ tools/perf/builtin-record.c | 5 +++++ tools/perf/util/header.c | 10 +++++++++- tools/perf/util/util.h | 1 + 4 files changed, 21 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 25576b477c8..3ee27dccfde 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -110,6 +110,12 @@ comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2 In per-thread mode with inheritance mode on (default), samples are captured only when the thread executes on the designated CPUs. Default is to monitor all CPUs. +-N:: +--no-buildid-cache:: +Do not update the builid cache. This saves some overhead in situations +where the information in the perf.data file (which includes buildids) +is sufficient. + SEE ALSO -------- linkperf:perf-stat[1], linkperf:perf-list[1] diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 5efc3fc68a3..86b1c3b6264 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -60,6 +60,7 @@ static bool call_graph = false; static bool inherit_stat = false; static bool no_samples = false; static bool sample_address = false; +static bool no_buildid = false; static long samples = 0; static u64 bytes_written = 0; @@ -825,6 +826,8 @@ static const struct option options[] = { "Sample addresses"), OPT_BOOLEAN('n', "no-samples", &no_samples, "don't sample"), + OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid, + "do not update the buildid cache"), OPT_END() }; @@ -849,6 +852,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) } symbol__init(); + if (no_buildid) + disable_buildid_cache(); if (!nr_counters) { nr_counters = 1; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 4a6a4b3a4ab..d7e67b167ea 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -16,6 +16,8 @@ #include "symbol.h" #include "debug.h" +static bool no_buildid_cache = false; + /* * Create new perf.data header attribute: */ @@ -470,7 +472,8 @@ static int perf_header__adds_write(struct perf_header *self, int fd) } buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset; - perf_session__cache_build_ids(session); + if (!no_buildid_cache) + perf_session__cache_build_ids(session); } lseek(fd, sec_start, SEEK_SET); @@ -1189,3 +1192,8 @@ int event__process_build_id(event_t *self, session); return 0; } + +void disable_buildid_cache(void) +{ + no_buildid_cache = true; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index de61441b6dd..f380fed7435 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -154,6 +154,7 @@ extern void set_die_routine(void (*routine)(const char *err, va_list params) NOR extern int prefixcmp(const char *str, const char *prefix); extern void set_buildid_dir(void); +extern void disable_buildid_cache(void); static inline const char *skip_prefix(const char *str, const char *prefix) { -- cgit v1.2.3-18-g5258 From cfc21cc641dae17a4ebda29ed093134358449577 Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Mon, 14 Jun 2010 16:00:47 +0400 Subject: perf tools: .gitignore += config.make config.make.autogen These are local-configuration files and should be ignored. LKML-Reference: <1276516847-25817-1-git-send-email-kirr@landau.phys.spbu.ru> Signed-off-by: Kirill Smelkov Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/.gitignore | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools') diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index e1d60d78078..cb43289e447 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore @@ -18,3 +18,5 @@ perf-archive tags TAGS cscope* +config.mak +config.mak.autogen -- cgit v1.2.3-18-g5258 From 9ed7e1b85cd55dc46cb9410a23086bdaa2ff3eb9 Mon Sep 17 00:00:00 2001 From: Chase Douglas Date: Mon, 14 Jun 2010 15:26:30 -0400 Subject: perf probe: Add kernel source path option The probe plugin requires access to the source code for some operations. The source code must be in the exact same location as specified by the DWARF tags, but sometimes the location is an absolute path that cannot be replicated by a normal user. This change adds the -s|--source option to allow the user to specify the root of the kernel source tree. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Masami Hiramatsu LKML-Reference: <1276543590-10486-1-git-send-email-chase.douglas@canonical.com> Signed-off-by: Chase Douglas Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-probe.txt | 4 +++ tools/perf/builtin-probe.c | 2 ++ tools/perf/util/probe-finder.c | 58 +++++++++++++++++++++++++++++++-- tools/perf/util/symbol.h | 1 + 4 files changed, 62 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 94a258c96a4..ea531d9d975 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -31,6 +31,10 @@ OPTIONS --vmlinux=PATH:: Specify vmlinux path which has debuginfo (Dwarf binary). +-s:: +--source=PATH:: + Specify path to kernel source. + -v:: --verbose:: Be more verbose (show parsed arguments, etc). diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index e4a4da32a56..54551867e7e 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -182,6 +182,8 @@ static const struct option options[] = { "Show source code lines.", opt_show_lines), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), + OPT_STRING('s', "source", &symbol_conf.source_prefix, + "directory", "path to kernel source"), #endif OPT__DRY_RUN(&probe_event_dry_run), OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index d964cb199c6..baf66538349 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -37,6 +37,7 @@ #include "event.h" #include "debug.h" #include "util.h" +#include "symbol.h" #include "probe-finder.h" /* Kprobe tracer basic type is up to u64 */ @@ -57,6 +58,55 @@ static int strtailcmp(const char *s1, const char *s2) return 0; } +/* + * Find a src file from a DWARF tag path. Prepend optional source path prefix + * and chop off leading directories that do not exist. Result is passed back as + * a newly allocated path on success. + * Return 0 if file was found and readable, -errno otherwise. + */ +static int get_real_path(const char *raw_path, char **new_path) +{ + if (!symbol_conf.source_prefix) { + if (access(raw_path, R_OK) == 0) { + *new_path = strdup(raw_path); + return 0; + } else + return -errno; + } + + *new_path = malloc((strlen(symbol_conf.source_prefix) + + strlen(raw_path) + 2)); + if (!*new_path) + return -ENOMEM; + + for (;;) { + sprintf(*new_path, "%s/%s", symbol_conf.source_prefix, + raw_path); + + if (access(*new_path, R_OK) == 0) + return 0; + + switch (errno) { + case ENAMETOOLONG: + case ENOENT: + case EROFS: + case EFAULT: + raw_path = strchr(++raw_path, '/'); + if (!raw_path) { + free(*new_path); + *new_path = NULL; + return -ENOENT; + } + continue; + + default: + free(*new_path); + *new_path = NULL; + return -errno; + } + } +} + /* Line number list operations */ /* Add a line to line number list */ @@ -1096,11 +1146,13 @@ end: static int line_range_add_line(const char *src, unsigned int lineno, struct line_range *lr) { + int ret; + /* Copy real path */ if (!lr->path) { - lr->path = strdup(src); - if (lr->path == NULL) - return -ENOMEM; + ret = get_real_path(src, &lr->path); + if (ret != 0) + return ret; } return line_list__add_line(&lr->line_list, lineno); } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 10b7ff859ce..80e569bbdec 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -71,6 +71,7 @@ struct symbol_conf { full_paths, show_cpu_utilization; const char *vmlinux_name, + *source_prefix, *field_sep; const char *default_guest_vmlinux_name, *default_guest_kallsyms, -- cgit v1.2.3-18-g5258 From 84c104ad429c8a474b93dd374815d1c238032fa8 Mon Sep 17 00:00:00 2001 From: Andy Isaacson Date: Fri, 11 Jun 2010 19:44:04 -0700 Subject: perf debug: fix hex dump partial final line The loop counter math in trace_event was much more complicated than necessary, resulting in incorrectly decoding the human-readable portion of the partial last line of hexdump in "perf trace -D" output: . 0020: 00 00 00 00 00 00 00 00 2f 73 62 69 6e 2f 69 6e ......../sbin/i . 0030: 69 74 00 00 00 00 00 00 /sbin/i With this fixed (and simpler!) code, we get the correct output: . 0020: 00 00 00 00 00 00 00 00 2f 73 62 69 6e 2f 69 6e ......../sbin/in . 0030: 69 74 00 00 00 00 00 00 it...... Cc: Ingo Molnar LPU-Reference: <20100612024404.GA24469@hexapodia.org> Signed-off-by: Andy Isaacson Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/debug.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 6cddff2bc97..318dab15d17 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -86,12 +86,10 @@ void trace_event(event_t *event) dump_printf_color(" ", color); for (j = 0; j < 15-(i & 15); j++) dump_printf_color(" ", color); - for (j = 0; j < (i & 15); j++) { - if (isprint(raw_event[i-15+j])) - dump_printf_color("%c", color, - raw_event[i-15+j]); - else - dump_printf_color(".", color); + for (j = i & ~15; j <= i; j++) { + dump_printf_color("%c", color, + isprint(raw_event[j]) ? + raw_event[j] : '.'); } dump_printf_color("\n", color); } -- cgit v1.2.3-18-g5258 From 0f2c3de2ba110626515234d5d584fb1b0c0749a2 Mon Sep 17 00:00:00 2001 From: Andy Isaacson Date: Fri, 11 Jun 2010 20:36:15 -0700 Subject: perf session: fix error message on failure to open perf.data If we cannot open our data file, print strerror(errno) for a more comprehensible error message; and only suggest 'perf record' on ENOENT. In particular, this fixes the nonsensical advice when: % sudo perf record sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.009 MB perf.data (~381 samples) ] % perf trace failed to open file: perf.data (try 'perf record' first) % Cc: Ingo Molnar LPU-Reference: <20100612033615.GA24731@hexapodia.org> Signed-off-by: Andy Isaacson Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 8f83a183576..0564a5cfb12 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -27,8 +27,10 @@ static int perf_session__open(struct perf_session *self, bool force) self->fd = open(self->filename, O_RDONLY); if (self->fd < 0) { - pr_err("failed to open file: %s", self->filename); - if (!strcmp(self->filename, "perf.data")) + int err = errno; + + pr_err("failed to open %s: %s", self->filename, strerror(err)); + if (err == ENOENT && !strcmp(self->filename, "perf.data")) pr_err(" (try 'perf record' first)"); pr_err("\n"); return -errno; -- cgit v1.2.3-18-g5258 From bfde744863eab22a3a400c9003f4f555c903f61d Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 17 Jun 2010 23:40:06 -0500 Subject: perf scripts perl: Makefile fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a typo introduced by recent Makefile changes, in f9af3a4. Without it, Perl scripting support won't get compiled in. Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Stephane Eranian LKML-Reference: <1276836006.7762.15.camel@tropicana> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 6aa2fe323db..17a3692397c 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -577,7 +577,7 @@ ifdef NO_LIBPERL else PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null` PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` - PERL_EMBED_FLAGS=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) + FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y) BASIC_CFLAGS += -DNO_LIBPERL -- cgit v1.2.3-18-g5258 From 8c694d2559acf09bfd8b71fe1795e740063ad803 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 21 Jun 2010 12:44:42 -0300 Subject: perf ui: Introduce routine ui_browser__is_current_entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Will be used in more places in the new tree widget. Cc: Frédéric Weisbecker Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index cf182ca132f..1e774e7f228 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -270,6 +270,11 @@ struct ui_browser { u32 nr_entries; }; +static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) +{ + return (self->first_visible_entry_idx + row) == self->index; +} + static void ui_browser__refresh_dimensions(struct ui_browser *self) { int cols, rows; @@ -286,8 +291,8 @@ static void ui_browser__refresh_dimensions(struct ui_browser *self) static void ui_browser__reset_index(struct ui_browser *self) { - self->index = self->first_visible_entry_idx = 0; - self->first_visible_entry = NULL; + self->index = self->first_visible_entry_idx = 0; + self->first_visible_entry = NULL; } static int objdump_line__show(struct objdump_line *self, struct list_head *head, @@ -353,7 +358,7 @@ static int ui_browser__refresh_entries(struct ui_browser *self) pos = list_entry(self->first_visible_entry, struct objdump_line, node); list_for_each_entry_from(pos, head, node) { - bool current_entry = (self->first_visible_entry_idx + row) == self->index; + bool current_entry = ui_browser__is_current_entry(self, row); SLsmg_gotorc(self->top + row, self->left); objdump_line__show(pos, head, self->width, he, len, current_entry); -- cgit v1.2.3-18-g5258 From 46b0a07a45b07ed5ca8053bbb6ec522982d1a1dd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 21 Jun 2010 13:36:20 -0300 Subject: perf ui: Introduce ui_browser->seek to support multiple list structures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that we can use the ui_browser on things like an rb_tree, etc. Cc: Frédéric Weisbecker Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 69 +++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 26 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 1e774e7f228..0ffc8281363 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -267,9 +267,42 @@ struct ui_browser { void *first_visible_entry, *entries; u16 top, left, width, height; void *priv; + void (*seek)(struct ui_browser *self, + off_t offset, int whence); u32 nr_entries; }; +static void ui_browser__list_head_seek(struct ui_browser *self, + off_t offset, int whence) +{ + struct list_head *head = self->entries; + struct list_head *pos; + + switch (whence) { + case SEEK_SET: + pos = head->next; + break; + case SEEK_CUR: + pos = self->first_visible_entry; + break; + case SEEK_END: + pos = head->prev; + break; + default: + return; + } + + if (offset > 0) { + while (offset-- != 0) + pos = pos->next; + } else { + while (offset++ != 0) + pos = pos->prev; + } + + self->first_visible_entry = pos; +} + static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) { return (self->first_visible_entry_idx + row) == self->index; @@ -292,7 +325,7 @@ static void ui_browser__refresh_dimensions(struct ui_browser *self) static void ui_browser__reset_index(struct ui_browser *self) { self->index = self->first_visible_entry_idx = 0; - self->first_visible_entry = NULL; + self->seek(self, 0, SEEK_SET); } static int objdump_line__show(struct objdump_line *self, struct list_head *head, @@ -408,7 +441,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title, newtFormAddComponent(self->form, self->sb); while (1) { - unsigned int offset; + off_t offset; newtFormRun(self->form, es); @@ -422,9 +455,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title, break; ++self->index; if (self->index == self->first_visible_entry_idx + self->height) { - struct list_head *pos = self->first_visible_entry; ++self->first_visible_entry_idx; - self->first_visible_entry = pos->next; + self->seek(self, +1, SEEK_CUR); } break; case NEWT_KEY_UP: @@ -432,9 +464,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title, break; --self->index; if (self->index < self->first_visible_entry_idx) { - struct list_head *pos = self->first_visible_entry; --self->first_visible_entry_idx; - self->first_visible_entry = pos->prev; + self->seek(self, -1, SEEK_CUR); } break; case NEWT_KEY_PGDN: @@ -447,12 +478,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title, offset = self->nr_entries - 1 - self->index; self->index += offset; self->first_visible_entry_idx += offset; - - while (offset--) { - struct list_head *pos = self->first_visible_entry; - self->first_visible_entry = pos->next; - } - + self->seek(self, +offset, SEEK_CUR); break; case NEWT_KEY_PGUP: if (self->first_visible_entry_idx == 0) @@ -465,29 +491,19 @@ static int ui_browser__run(struct ui_browser *self, const char *title, self->index -= offset; self->first_visible_entry_idx -= offset; - - while (offset--) { - struct list_head *pos = self->first_visible_entry; - self->first_visible_entry = pos->prev; - } + self->seek(self, -offset, SEEK_CUR); break; case NEWT_KEY_HOME: ui_browser__reset_index(self); break; - case NEWT_KEY_END: { - struct list_head *head = self->entries; + case NEWT_KEY_END: offset = self->height - 1; if (offset > self->nr_entries) offset = self->nr_entries; self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; - self->first_visible_entry = head->prev; - while (offset-- != 0) { - struct list_head *pos = self->first_visible_entry; - self->first_visible_entry = pos->prev; - } - } + self->seek(self, -offset, SEEK_END); break; case NEWT_KEY_RIGHT: case NEWT_KEY_LEFT: @@ -706,7 +722,8 @@ int hist_entry__tui_annotate(struct hist_entry *self) ui_helpline__push("Press <- or ESC to exit"); memset(&browser, 0, sizeof(browser)); - browser.entries = &head; + browser.entries = &head; + browser.seek = ui_browser__list_head_seek; browser.priv = self; list_for_each_entry(pos, &head, node) { size_t line_len = strlen(pos->line); -- cgit v1.2.3-18-g5258 From 13f499f076c67675e6e3022973729b5d906a84e9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 21 Jun 2010 18:04:02 -0300 Subject: perf ui: Separate showing the entries from running the browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Another patch eroding the changes I had to move to a tree widget that doesn't requires adding all entries in an existing list/tree structure to a generic tree widget, but instead allows traversing just the entries that should appear on the screen on a given moment. Cc: Frédéric Weisbecker Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 60 ++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 0ffc8281363..9fa5b20d409 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -328,6 +328,32 @@ static void ui_browser__reset_index(struct ui_browser *self) self->seek(self, 0, SEEK_SET); } +static int ui_browser__show(struct ui_browser *self, const char *title) +{ + if (self->form != NULL) + return 0; + ui_browser__refresh_dimensions(self); + newtCenteredWindow(self->width + 2, self->height, title); + self->form = newt_form__new(); + if (self->form == NULL) + return -1; + + self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, + HE_COLORSET_NORMAL, + HE_COLORSET_SELECTED); + if (self->sb == NULL) + return -1; + + newtFormAddHotKey(self->form, NEWT_KEY_UP); + newtFormAddHotKey(self->form, NEWT_KEY_DOWN); + newtFormAddHotKey(self->form, NEWT_KEY_PGUP); + newtFormAddHotKey(self->form, NEWT_KEY_PGDN); + newtFormAddHotKey(self->form, NEWT_KEY_HOME); + newtFormAddHotKey(self->form, NEWT_KEY_END); + newtFormAddComponent(self->form, self->sb); + return 0; +} + static int objdump_line__show(struct objdump_line *self, struct list_head *head, int width, struct hist_entry *he, int len, bool current_entry) @@ -406,39 +432,10 @@ static int ui_browser__refresh_entries(struct ui_browser *self) return 0; } -static int ui_browser__run(struct ui_browser *self, const char *title, - struct newtExitStruct *es) +static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) { - if (self->form) { - newtFormDestroy(self->form); - newtPopWindow(); - } - - ui_browser__refresh_dimensions(self); - newtCenteredWindow(self->width + 2, self->height, title); - self->form = newt_form__new(); - if (self->form == NULL) - return -1; - - self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, - HE_COLORSET_NORMAL, - HE_COLORSET_SELECTED); - if (self->sb == NULL) - return -1; - - newtFormAddHotKey(self->form, NEWT_KEY_UP); - newtFormAddHotKey(self->form, NEWT_KEY_DOWN); - newtFormAddHotKey(self->form, NEWT_KEY_PGUP); - newtFormAddHotKey(self->form, NEWT_KEY_PGDN); - newtFormAddHotKey(self->form, ' '); - newtFormAddHotKey(self->form, NEWT_KEY_HOME); - newtFormAddHotKey(self->form, NEWT_KEY_END); - newtFormAddHotKey(self->form, NEWT_KEY_TAB); - newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); - if (ui_browser__refresh_entries(self) < 0) return -1; - newtFormAddComponent(self->form, self->sb); while (1) { off_t offset; @@ -733,7 +730,8 @@ int hist_entry__tui_annotate(struct hist_entry *self) } browser.width += 18; /* Percentage */ - ret = ui_browser__run(&browser, self->ms.sym->name, &es); + ui_browser__show(&browser, self->ms.sym->name); + ui_browser__run(&browser, &es); newtFormDestroy(browser.form); newtPopWindow(); list_for_each_entry_safe(pos, n, &head, node) { -- cgit v1.2.3-18-g5258 From 9f61d85fd0fe91f9a17a9f304d67e55f8ae38b2e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 21 Jun 2010 19:38:33 -0300 Subject: perf ui: Move objdump_line specific stuff out of ui_browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By adding a ui_browser->refresh_entries() pure virtual member. Cc: Frédéric Weisbecker Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 9fa5b20d409..7bdbfd3e24d 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -267,6 +267,7 @@ struct ui_browser { void *first_visible_entry, *entries; u16 top, left, width, height; void *priv; + unsigned int (*refresh_entries)(struct ui_browser *self); void (*seek)(struct ui_browser *self, off_t offset, int whence); u32 nr_entries; @@ -405,26 +406,10 @@ static int objdump_line__show(struct objdump_line *self, struct list_head *head, static int ui_browser__refresh_entries(struct ui_browser *self) { - struct objdump_line *pos; - struct list_head *head = self->entries; - struct hist_entry *he = self->priv; - int row = 0; - int len = he->ms.sym->end - he->ms.sym->start; - - if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) - self->first_visible_entry = head->next; - - pos = list_entry(self->first_visible_entry, struct objdump_line, node); - - list_for_each_entry_from(pos, head, node) { - bool current_entry = ui_browser__is_current_entry(self, row); - SLsmg_gotorc(self->top + row, self->left); - objdump_line__show(pos, head, self->width, - he, len, current_entry); - if (++row == self->height) - break; - } + int row; + newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); + row = self->refresh_entries(self); SLsmg_set_color(HE_COLORSET_NORMAL); SLsmg_fill_region(self->top + row, self->left, self->height - row, self->width, ' '); @@ -557,6 +542,31 @@ static char *callchain_list__sym_name(struct callchain_list *self, return bf; } +static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self) +{ + struct objdump_line *pos; + struct list_head *head = self->entries; + struct hist_entry *he = self->priv; + int row = 0; + int len = he->ms.sym->end - he->ms.sym->start; + + if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) + self->first_visible_entry = head->next; + + pos = list_entry(self->first_visible_entry, struct objdump_line, node); + + list_for_each_entry_from(pos, head, node) { + bool current_entry = ui_browser__is_current_entry(self, row); + SLsmg_gotorc(self->top + row, self->left); + objdump_line__show(pos, head, self->width, + he, len, current_entry); + if (++row == self->height) + break; + } + + return row; +} + static void __callchain__append_graph_browser(struct callchain_node *self, newtComponent tree, u64 total, int *indexes, int depth) @@ -720,6 +730,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) memset(&browser, 0, sizeof(browser)); browser.entries = &head; + browser.refresh_entries = hist_entry__annotate_browser_refresh; browser.seek = ui_browser__list_head_seek; browser.priv = self; list_for_each_entry(pos, &head, node) { -- cgit v1.2.3-18-g5258 From aa59a48596d8358a908bfb458300b5625cd47785 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 24 Jun 2010 21:36:19 +0200 Subject: perf: Don't use 4 bytes as a default instruction breakpoint length 4 bytes is fine as a default access for data breakpoints. But instruction breakpoints should take the native pointer length, otherwise we get a -EINVAL in x86-64. Signed-off-by: Frederic Weisbecker Cc: Will Deacon Cc: Prasad Cc: Mahesh Salgaonkar Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Jason Wessel --- tools/perf/util/parse-events.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 9bf0f402ca7..4af5bd59cfd 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -602,8 +602,15 @@ parse_breakpoint_event(const char **strp, struct perf_event_attr *attr) return EVT_FAILED; } - /* We should find a nice way to override the access type */ - attr->bp_len = HW_BREAKPOINT_LEN_4; + /* + * We should find a nice way to override the access length + * Provide some defaults for now + */ + if (attr->bp_type == HW_BREAKPOINT_X) + attr->bp_len = sizeof(long); + else + attr->bp_len = HW_BREAKPOINT_LEN_4; + attr->type = PERF_TYPE_BREAKPOINT; return EVT_HANDLED; -- cgit v1.2.3-18-g5258 From 6fcf7ddbb73d677b3bb7b16f0fff1419cb8349e9 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 27 May 2010 15:46:25 +0200 Subject: perf: Don't print traces when debugging ordering Errors due to ordering bugs are easily lost in the middle of traces. When we are in this mode, don't print the traces so that we don't miss the debugging messages. But display a comforting message if we didn't encounter any ordering problem. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras --- tools/perf/builtin-trace.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index dddf3f01b5a..83df8db4f35 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -13,6 +13,7 @@ static char const *script_name; static char const *generate_script_lang; static bool debug_ordering; static u64 last_timestamp; +static u64 nr_unordered; static int default_start_script(const char *script __unused, int argc __unused, @@ -96,8 +97,10 @@ static int process_sample_event(event_t *event, struct perf_session *session) pr_err("Samples misordered, previous: %llu " "this: %llu\n", last_timestamp, data.time); + nr_unordered++; } last_timestamp = data.time; + return 0; } /* * FIXME: better resolve from pid from the struct trace_entry @@ -132,9 +135,16 @@ static void sig_handler(int sig __unused) static int __cmd_trace(struct perf_session *session) { + int ret; + signal(SIGINT, sig_handler); - return perf_session__process_events(session, &event_ops); + ret = perf_session__process_events(session, &event_ops); + + if (debug_ordering) + pr_err("Misordered timestamps: %llu\n", nr_unordered); + + return ret; } struct script_spec { -- cgit v1.2.3-18-g5258 From ffabd99e051e73344efe4e53d58f11643f180512 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 27 May 2010 16:27:47 +0200 Subject: perf: Report lost events in perf trace debug mode Account and report lost events in perf trace debugging mode, useful to check the reliability of the traces. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Masami Hiramatsu Cc: Tom Zanussi --- tools/perf/builtin-trace.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 83df8db4f35..294da725a57 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -11,7 +11,7 @@ static char const *script_name; static char const *generate_script_lang; -static bool debug_ordering; +static bool debug_mode; static u64 last_timestamp; static u64 nr_unordered; @@ -92,7 +92,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) } if (session->sample_type & PERF_SAMPLE_RAW) { - if (debug_ordering) { + if (debug_mode) { if (data.time < last_timestamp) { pr_err("Samples misordered, previous: %llu " "this: %llu\n", last_timestamp, @@ -116,6 +116,15 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; } +static u64 nr_lost; + +static int process_lost_event(event_t *event, struct perf_session *session __used) +{ + nr_lost += event->lost.lost; + + return 0; +} + static struct perf_event_ops event_ops = { .sample = process_sample_event, .comm = event__process_comm, @@ -123,6 +132,7 @@ static struct perf_event_ops event_ops = { .event_type = event__process_event_type, .tracing_data = event__process_tracing_data, .build_id = event__process_build_id, + .lost = process_lost_event, .ordered_samples = true, }; @@ -141,8 +151,10 @@ static int __cmd_trace(struct perf_session *session) ret = perf_session__process_events(session, &event_ops); - if (debug_ordering) + if (debug_mode) { pr_err("Misordered timestamps: %llu\n", nr_unordered); + pr_err("Lost events: %llu\n", nr_lost); + } return ret; } @@ -554,8 +566,8 @@ static const struct option options[] = { "generate perf-trace.xx script in specified language"), OPT_STRING('i', "input", &input_name, "file", "input file name"), - OPT_BOOLEAN('d', "debug-ordering", &debug_ordering, - "check that samples time ordering is monotonic"), + OPT_BOOLEAN('d', "debug-mode", &debug_mode, + "do various checks like samples ordering and lost events"), OPT_END() }; -- cgit v1.2.3-18-g5258 From 830f4c803196eec181e209110885c4ac130f3805 Mon Sep 17 00:00:00 2001 From: Gui Jianfeng Date: Fri, 25 Jun 2010 10:15:28 +0800 Subject: perf kvm: Get rid of unused guest_kallsyms guest_kallsyms is redundant here, remove it. Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Yanmin Zhang LKML-Reference: <4C241140.9090008@cn.fujitsu.com> Signed-off-by: Gui Jianfeng Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 86b1c3b6264..0df64088135 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -445,8 +445,6 @@ static void atexit_header(void) static void event__synthesize_guest_os(struct machine *machine, void *data) { int err; - char *guest_kallsyms; - char path[PATH_MAX]; struct perf_session *psession = data; if (machine__is_host(machine)) @@ -466,13 +464,6 @@ static void event__synthesize_guest_os(struct machine *machine, void *data) pr_err("Couldn't record guest kernel [%d]'s reference" " relocation symbol.\n", machine->pid); - if (machine__is_default_guest(machine)) - guest_kallsyms = (char *) symbol_conf.default_guest_kallsyms; - else { - sprintf(path, "%s/proc/kallsyms", machine->root_dir); - guest_kallsyms = path; - } - /* * We use _stext for guest kernel because guest kernel's /proc/kallsyms * have no _text sometimes. -- cgit v1.2.3-18-g5258 From 0879b100f3c187257729f36cba33d96ec2875766 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Tue, 29 Jun 2010 23:02:26 +0530 Subject: perf: Fix hist_entry__tui_annotate() build failure When compiling perf on latest tip/master I see the following error: cc1: warnings being treated as errors util/newt.c: In function 'hist_entry__tui_annotate': util/newt.c:764: warning: 'ret' is used uninitialized in this function make: *** [util/newt.o] Error 1 I think the problem was introduced by commit 13f499f076c67675e6e3022973729b5d906a84e9 Below is a patch that fixes the problem. Signed-off-by: Srikar Dronamraju Cc: Arnaldo Carvalho de Melo LKML-Reference: <20100629173226.GC23231@linux.vnet.ibm.com> Signed-off-by: Ingo Molnar --- tools/perf/util/newt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 89c52fc9b22..06f248fde5c 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -753,7 +753,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) browser.width += 18; /* Percentage */ ui_browser__show(&browser, self->ms.sym->name); - ui_browser__run(&browser, &es); + ret = ui_browser__run(&browser, &es); newtFormDestroy(browser.form); newtPopWindow(); list_for_each_entry_safe(pos, n, &head, node) { -- cgit v1.2.3-18-g5258 From 167a58f10d9cd1bdf6a911aa1eecbdff596de156 Mon Sep 17 00:00:00 2001 From: Conny Seidel Date: Thu, 1 Jul 2010 15:19:26 +0200 Subject: perf tools: Fix fallback to cplus_demangle() when bfd_demangle() is not available make version 3.80 doesn't support "else ifdef" on the same line, also it doesn't support unindented nested constructs. Build fails with: Makefile:608: Extraneous text after `else' directive Makefile:611: *** only one `else' per conditional. Stop. This patch fixes the build for make 3.80. Cc: Borislav Petkov LKML-Reference: <1277990366-1462-1-git-send-email-conny.seidel@amd.com> Signed-off-by: Conny Seidel Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 17a3692397c..26f626d45a9 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -605,33 +605,35 @@ endif ifdef NO_DEMANGLE BASIC_CFLAGS += -DNO_DEMANGLE -else ifdef HAVE_CPLUS_DEMANGLE - EXTLIBS += -liberty - BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE else - FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd - has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD)) - ifeq ($(has_bfd),y) - EXTLIBS += -lbfd - else - FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty - has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY)) - ifeq ($(has_bfd_iberty),y) - EXTLIBS += -lbfd -liberty + ifdef HAVE_CPLUS_DEMANGLE + EXTLIBS += -liberty + BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE + else + FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd + has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD)) + ifeq ($(has_bfd),y) + EXTLIBS += -lbfd else - FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz - has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z)) - ifeq ($(has_bfd_iberty_z),y) - EXTLIBS += -lbfd -liberty -lz + FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty + has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY)) + ifeq ($(has_bfd_iberty),y) + EXTLIBS += -lbfd -liberty else - FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty - has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE)) - ifeq ($(has_cplus_demangle),y) - EXTLIBS += -liberty - BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE + FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz + has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z)) + ifeq ($(has_bfd_iberty_z),y) + EXTLIBS += -lbfd -liberty -lz else - msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling) - BASIC_CFLAGS += -DNO_DEMANGLE + FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty + has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE)) + ifeq ($(has_cplus_demangle),y) + EXTLIBS += -liberty + BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE + else + msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling) + BASIC_CFLAGS += -DNO_DEMANGLE + endif endif endif endif -- cgit v1.2.3-18-g5258 From 73317b954041031249e8968d2e9023ff4e960d99 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 19 May 2010 15:57:35 -0400 Subject: perf probe: Support "string" type Support string type casting to event argument. If perf-probe finds an argument casted as string, it ensures the target variable is "(unsigned/signed) char *(or []). perf-probe also adds dereference if the target is a pointer. So, both of 'char buf[10];' and 'char *buf;' can be accessed by 'buf:string' Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100519195734.2885.1666.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-probe.txt | 2 +- tools/perf/util/probe-finder.c | 59 +++++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index ea531d9d975..394016d33ce 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -95,7 +95,7 @@ Each probe argument follows below syntax. [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE] 'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.) -'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. +'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type. LINE SYNTAX ----------- diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index baf66538349..aaea16b1c60 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -464,18 +464,61 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf) } static int convert_variable_type(Dwarf_Die *vr_die, - struct kprobe_trace_arg *targ) + struct kprobe_trace_arg *tvar, + const char *cast) { + struct kprobe_trace_arg_ref **ref_ptr = &tvar->ref; Dwarf_Die type; char buf[16]; int ret; + /* TODO: check all types */ + if (cast && strcmp(cast, "string") != 0) { + /* Non string type is OK */ + tvar->type = strdup(cast); + return (tvar->type == NULL) ? -ENOMEM : 0; + } + if (die_get_real_type(vr_die, &type) == NULL) { pr_warning("Failed to get a type information of %s.\n", dwarf_diename(vr_die)); return -ENOENT; } + if (cast && strcmp(cast, "string") == 0) { /* String type */ + ret = dwarf_tag(&type); + if (ret != DW_TAG_pointer_type && + ret != DW_TAG_array_type) { + pr_warning("Failed to cast into string: " + "%s(%s) is not a pointer nor array.", + dwarf_diename(vr_die), dwarf_diename(&type)); + return -EINVAL; + } + if (ret == DW_TAG_pointer_type) { + if (die_get_real_type(&type, &type) == NULL) { + pr_warning("Failed to get a type information."); + return -ENOENT; + } + while (*ref_ptr) + ref_ptr = &(*ref_ptr)->next; + /* Add new reference with offset +0 */ + *ref_ptr = zalloc(sizeof(struct kprobe_trace_arg_ref)); + if (*ref_ptr == NULL) { + pr_warning("Out of memory error\n"); + return -ENOMEM; + } + } + if (die_compare_name(&type, "char") != 0 && + die_compare_name(&type, "unsigned char") != 0) { + pr_warning("Failed to cast into string: " + "%s is not (unsigned) char *.", + dwarf_diename(vr_die)); + return -EINVAL; + } + tvar->type = strdup(cast); + return (tvar->type == NULL) ? -ENOMEM : 0; + } + ret = die_get_byte_size(&type) * 8; if (ret) { /* Check the bitwidth */ @@ -495,8 +538,8 @@ static int convert_variable_type(Dwarf_Die *vr_die, strerror(-ret)); return ret; } - targ->type = strdup(buf); - if (targ->type == NULL) + tvar->type = strdup(buf); + if (tvar->type == NULL) return -ENOMEM; } return 0; @@ -606,14 +649,8 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) &die_mem); vr_die = &die_mem; } - if (ret == 0) { - if (pf->pvar->type) { - pf->tvar->type = strdup(pf->pvar->type); - if (pf->tvar->type == NULL) - ret = -ENOMEM; - } else - ret = convert_variable_type(vr_die, pf->tvar); - } + if (ret == 0) + ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type); /* *expr will be cached in libdw. Don't free it. */ return ret; error: -- cgit v1.2.3-18-g5258 From b2a3c12b7442247c440f7083d48ef05716753ec1 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 19 May 2010 15:57:42 -0400 Subject: perf probe: Support tracing an entry of array Add array-entry tracing support to perf probe. This enables to trace an entry of array which is indexed by constant value, e.g. array[0]. For example: $ perf probe -a 'bio_split bi->bi_io_vec[0]' Cc: Paul Mackerras Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100519195742.2885.5344.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-probe.txt | 2 +- tools/perf/util/probe-event.c | 56 +++++++++++++++++++++++---------- tools/perf/util/probe-event.h | 1 + tools/perf/util/probe-finder.c | 48 ++++++++++++++++++++++++---- 4 files changed, 83 insertions(+), 24 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 394016d33ce..27d52dae5a4 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -94,7 +94,7 @@ Each probe argument follows below syntax. [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE] -'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.) +'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), local array with fixed index (e.g. array[1], var->array[0], var->pointer[2]), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.) 'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type. LINE SYNTAX diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 914c67095d9..351baa9a369 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -557,7 +557,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) /* Parse perf-probe event argument */ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) { - char *tmp; + char *tmp, *goodname; struct perf_probe_arg_field **fieldp; pr_debug("parsing arg: %s into ", str); @@ -580,7 +580,7 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) pr_debug("type:%s ", arg->type); } - tmp = strpbrk(str, "-."); + tmp = strpbrk(str, "-.["); if (!is_c_varname(str) || !tmp) { /* A variable, register, symbol or special value */ arg->var = strdup(str); @@ -590,10 +590,11 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) return 0; } - /* Structure fields */ + /* Structure fields or array element */ arg->var = strndup(str, tmp - str); if (arg->var == NULL) return -ENOMEM; + goodname = arg->var; pr_debug("%s, ", arg->var); fieldp = &arg->field; @@ -601,22 +602,38 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); if (*fieldp == NULL) return -ENOMEM; - if (*tmp == '.') { - str = tmp + 1; - (*fieldp)->ref = false; - } else if (tmp[1] == '>') { - str = tmp + 2; + if (*tmp == '[') { /* Array */ + str = tmp; + (*fieldp)->index = strtol(str + 1, &tmp, 0); (*fieldp)->ref = true; - } else { - semantic_error("Argument parse error: %s\n", str); - return -EINVAL; + if (*tmp != ']' || tmp == str + 1) { + semantic_error("Array index must be a" + " number.\n"); + return -EINVAL; + } + tmp++; + if (*tmp == '\0') + tmp = NULL; + } else { /* Structure */ + if (*tmp == '.') { + str = tmp + 1; + (*fieldp)->ref = false; + } else if (tmp[1] == '>') { + str = tmp + 2; + (*fieldp)->ref = true; + } else { + semantic_error("Argument parse error: %s\n", + str); + return -EINVAL; + } + tmp = strpbrk(str, "-.["); } - - tmp = strpbrk(str, "-."); if (tmp) { (*fieldp)->name = strndup(str, tmp - str); if ((*fieldp)->name == NULL) return -ENOMEM; + if (*str != '[') + goodname = (*fieldp)->name; pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); fieldp = &(*fieldp)->next; } @@ -624,11 +641,13 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) (*fieldp)->name = strdup(str); if ((*fieldp)->name == NULL) return -ENOMEM; + if (*str != '[') + goodname = (*fieldp)->name; pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); - /* If no name is specified, set the last field name */ + /* If no name is specified, set the last field name (not array index)*/ if (!arg->name) { - arg->name = strdup((*fieldp)->name); + arg->name = strdup(goodname); if (arg->name == NULL) return -ENOMEM; } @@ -776,8 +795,11 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) len -= ret; while (field) { - ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".", - field->name); + if (field->name[0] == '[') + ret = e_snprintf(tmp, len, "%s", field->name); + else + ret = e_snprintf(tmp, len, "%s%s", + field->ref ? "->" : ".", field->name); if (ret <= 0) goto error; tmp += ret; diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index e9db1a214ca..bc06d3e8baf 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -50,6 +50,7 @@ struct perf_probe_point { struct perf_probe_arg_field { struct perf_probe_arg_field *next; /* Next field */ char *name; /* Name of the field */ + long index; /* Array index number */ bool ref; /* Referencing flag */ }; diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index aaea16b1c60..308664deb85 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -485,6 +485,9 @@ static int convert_variable_type(Dwarf_Die *vr_die, return -ENOENT; } + pr_debug("%s type is %s.\n", + dwarf_diename(vr_die), dwarf_diename(&type)); + if (cast && strcmp(cast, "string") == 0) { /* String type */ ret = dwarf_tag(&type); if (ret != DW_TAG_pointer_type && @@ -553,16 +556,44 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, struct kprobe_trace_arg_ref *ref = *ref_ptr; Dwarf_Die type; Dwarf_Word offs; - int ret; + int ret, tag; pr_debug("converting %s in %s\n", field->name, varname); if (die_get_real_type(vr_die, &type) == NULL) { pr_warning("Failed to get the type of %s.\n", varname); return -ENOENT; } - - /* Check the pointer and dereference */ - if (dwarf_tag(&type) == DW_TAG_pointer_type) { + pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type)); + tag = dwarf_tag(&type); + + if (field->name[0] == '[' && + (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { + if (field->next) + /* Save original type for next field */ + memcpy(die_mem, &type, sizeof(*die_mem)); + /* Get the type of this array */ + if (die_get_real_type(&type, &type) == NULL) { + pr_warning("Failed to get the type of %s.\n", varname); + return -ENOENT; + } + pr_debug2("Array real type: (%x)\n", + (unsigned)dwarf_dieoffset(&type)); + if (tag == DW_TAG_pointer_type) { + ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); + if (ref == NULL) + return -ENOMEM; + if (*ref_ptr) + (*ref_ptr)->next = ref; + else + *ref_ptr = ref; + } + ref->offset += die_get_byte_size(&type) * field->index; + if (!field->next) + /* Save vr_die for converting types */ + memcpy(die_mem, vr_die, sizeof(*die_mem)); + goto next; + } else if (tag == DW_TAG_pointer_type) { + /* Check the pointer and dereference */ if (!field->ref) { pr_err("Semantic error: %s must be referred by '->'\n", field->name); @@ -588,10 +619,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, *ref_ptr = ref; } else { /* Verify it is a data structure */ - if (dwarf_tag(&type) != DW_TAG_structure_type) { + if (tag != DW_TAG_structure_type) { pr_warning("%s is not a data structure.\n", varname); return -EINVAL; } + if (field->name[0] == '[') { + pr_err("Semantic error: %s is not a pointor nor array.", + varname); + return -EINVAL; + } if (field->ref) { pr_err("Semantic error: %s must be referred by '.'\n", field->name); @@ -618,6 +654,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, } ref->offset += (long)offs; +next: /* Converting next field */ if (field->next) return convert_variable_fields(die_mem, field->name, @@ -667,7 +704,6 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) char buf[32], *ptr; int ret; - /* TODO: Support arrays */ if (pf->pvar->name) pf->tvar->name = strdup(pf->pvar->name); else { -- cgit v1.2.3-18-g5258 From b7dcb857cc3eb89136111fefe89780129c1af1d7 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 19 May 2010 15:57:49 -0400 Subject: perf probe: Support static and global variables Add static and global variables support to perf probe. This allows user to trace non-local variables (and structure members) at probe points. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100519195749.2885.17451.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 15 ++++++-- tools/perf/util/probe-finder.c | 85 ++++++++++++++++++++++++++++++------------ 2 files changed, 73 insertions(+), 27 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 351baa9a369..09cf5465e10 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -926,6 +926,7 @@ out: static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, char *buf, size_t buflen) { + struct kprobe_trace_arg_ref *ref = arg->ref; int ret, depth = 0; char *tmp = buf; @@ -939,16 +940,24 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, buf += ret; buflen -= ret; + /* Special case: @XXX */ + if (arg->value[0] == '@' && arg->ref) + ref = ref->next; + /* Dereferencing arguments */ - if (arg->ref) { - depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf, + if (ref) { + depth = __synthesize_kprobe_trace_arg_ref(ref, &buf, &buflen, 1); if (depth < 0) return depth; } /* Print argument value */ - ret = e_snprintf(buf, buflen, "%s", arg->value); + if (arg->value[0] == '@' && arg->ref) + ret = e_snprintf(buf, buflen, "%s%+ld", arg->value, + arg->ref->offset); + else + ret = e_snprintf(buf, buflen, "%s", arg->value); if (ret < 0) return ret; buf += ret; diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 308664deb85..3e64e1fa105 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -406,14 +406,50 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, * Probe finder related functions */ +static struct kprobe_trace_arg_ref *alloc_trace_arg_ref(long offs) +{ + struct kprobe_trace_arg_ref *ref; + ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); + if (ref != NULL) + ref->offset = offs; + return ref; +} + /* Show a location */ -static int convert_location(Dwarf_Op *op, struct probe_finder *pf) +static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) { + Dwarf_Attribute attr; + Dwarf_Op *op; + size_t nops; unsigned int regn; Dwarf_Word offs = 0; bool ref = false; const char *regs; struct kprobe_trace_arg *tvar = pf->tvar; + int ret; + + /* TODO: handle more than 1 exprs */ + if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || + dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 || + nops == 0) { + /* TODO: Support const_value */ + pr_err("Failed to find the location of %s at this address.\n" + " Perhaps, it has been optimized out.\n", pf->pvar->var); + return -ENOENT; + } + + if (op->atom == DW_OP_addr) { + /* Static variables on memory (not stack), make @varname */ + ret = strlen(dwarf_diename(vr_die)); + tvar->value = zalloc(ret + 2); + if (tvar->value == NULL) + return -ENOMEM; + snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); + tvar->ref = alloc_trace_arg_ref((long)offs); + if (tvar->ref == NULL) + return -ENOMEM; + return 0; + } /* If this is based on frame buffer, set the offset */ if (op->atom == DW_OP_fbreg) { @@ -455,10 +491,9 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf) return -ENOMEM; if (ref) { - tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); + tvar->ref = alloc_trace_arg_ref((long)offs); if (tvar->ref == NULL) return -ENOMEM; - tvar->ref->offset = (long)offs; } return 0; } @@ -666,20 +701,13 @@ next: /* Show a variables in kprobe event format */ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { - Dwarf_Attribute attr; Dwarf_Die die_mem; - Dwarf_Op *expr; - size_t nexpr; int ret; - if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) - goto error; - /* TODO: handle more than 1 exprs */ - ret = dwarf_getlocation_addr(&attr, pf->addr, &expr, &nexpr, 1); - if (ret <= 0 || nexpr == 0) - goto error; + pr_debug("Converting variable %s into trace event.\n", + dwarf_diename(vr_die)); - ret = convert_location(expr, pf); + ret = convert_variable_location(vr_die, pf); if (ret == 0 && pf->pvar->field) { ret = convert_variable_fields(vr_die, pf->pvar->var, pf->pvar->field, &pf->tvar->ref, @@ -690,19 +718,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type); /* *expr will be cached in libdw. Don't free it. */ return ret; -error: - /* TODO: Support const_value */ - pr_err("Failed to find the location of %s at this address.\n" - " Perhaps, it has been optimized out.\n", pf->pvar->var); - return -ENOENT; } /* Find a variable in a subprogram die */ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { - Dwarf_Die vr_die; + Dwarf_Die vr_die, *scopes; char buf[32], *ptr; - int ret; + int ret, nscopes; if (pf->pvar->name) pf->tvar->name = strdup(pf->pvar->name); @@ -730,12 +753,26 @@ 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(sp_die, pf->pvar->var, &vr_die)) { + if (die_find_variable(sp_die, pf->pvar->var, &vr_die)) + ret = convert_variable(&vr_die, pf); + else { + /* Search upper class */ + nscopes = dwarf_getscopes_die(sp_die, &scopes); + if (nscopes > 0) { + ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var, + 0, NULL, 0, 0, &vr_die); + if (ret >= 0) + ret = convert_variable(&vr_die, pf); + else + ret = -ENOENT; + free(scopes); + } else + ret = -ENOENT; + } + if (ret < 0) pr_warning("Failed to find '%s' in this function.\n", pf->pvar->var); - return -ENOENT; - } - return convert_variable(&vr_die, pf); + return ret; } /* Show a probe point to output buffer */ -- cgit v1.2.3-18-g5258 From 0dd9ac63ce26ec87b080ca9c3e6efed33c23ace6 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sat, 10 Jul 2010 16:10:39 +0100 Subject: perf tools: Add DWARF register lookup for SH Implement get_arch_regstr() for SH so that, given a DWARF register number, the corresponding symbolic name of that register can be looked up. Acked-by: Masami Hiramatsu Cc: Masami Hiramatsu Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Ian Munsie LKML-Reference: Signed-off-by: Matt Fleming Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/sh/Makefile | 4 +++ tools/perf/arch/sh/util/dwarf-regs.c | 55 ++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 tools/perf/arch/sh/Makefile create mode 100644 tools/perf/arch/sh/util/dwarf-regs.c (limited to 'tools') diff --git a/tools/perf/arch/sh/Makefile b/tools/perf/arch/sh/Makefile new file mode 100644 index 00000000000..15130b50dfe --- /dev/null +++ b/tools/perf/arch/sh/Makefile @@ -0,0 +1,4 @@ +ifndef NO_DWARF +PERF_HAVE_DWARF_REGS := 1 +LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o +endif diff --git a/tools/perf/arch/sh/util/dwarf-regs.c b/tools/perf/arch/sh/util/dwarf-regs.c new file mode 100644 index 00000000000..a11edb007a6 --- /dev/null +++ b/tools/perf/arch/sh/util/dwarf-regs.c @@ -0,0 +1,55 @@ +/* + * Mapping of DWARF debug register numbers into register names. + * + * Copyright (C) 2010 Matt Fleming + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +/* + * Generic dwarf analysis helpers + */ + +#define SH_MAX_REGS 18 +const char *sh_regs_table[SH_MAX_REGS] = { + "r0", + "r1", + "r2", + "r3", + "r4", + "r5", + "r6", + "r7", + "r8", + "r9", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", + "pc", + "pr", +}; + +/* Return architecture dependent register string (for kprobe-tracer) */ +const char *get_arch_regstr(unsigned int n) +{ + return (n <= SH_MAX_REGS) ? sh_regs_table[n] : NULL; +} -- cgit v1.2.3-18-g5258 From 7cf0b79e6ffd04bba5d4e625a0fe2e30a5b383e5 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 9 Jul 2010 18:28:59 +0900 Subject: perf probe: Fix error message if get_real_path() failed Perf probe -L shows incorrect error message (Dwarf error) if it fails to find source file. This can confuse users. # ./perf probe -s /nowhere -L vfs_read Debuginfo analysis failed. (-2) Error: Failed to show lines. (-2) With this patch, it shows correct message. # ./perf probe -s /nowhere -L vfs_read Failed to find source file. (-2) Error: Failed to show lines. (-2) LKML-Reference: <4C36EBDB.4020308@hitachi.com> Cc: Chase Douglas Cc: Arnaldo Carvalho de Melo Cc: Ingo Molnar Acked-by: Chase Douglas Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 59 ++++++++++++++++++++++++++++++++++++++++ tools/perf/util/probe-finder.c | 61 ++++-------------------------------------- 2 files changed, 64 insertions(+), 56 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 09cf5465e10..8d08e75b2dd 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -195,6 +195,55 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, return ntevs; } +/* + * Find a src file from a DWARF tag path. Prepend optional source path prefix + * and chop off leading directories that do not exist. Result is passed back as + * a newly allocated path on success. + * Return 0 if file was found and readable, -errno otherwise. + */ +static int get_real_path(const char *raw_path, char **new_path) +{ + if (!symbol_conf.source_prefix) { + if (access(raw_path, R_OK) == 0) { + *new_path = strdup(raw_path); + return 0; + } else + return -errno; + } + + *new_path = malloc((strlen(symbol_conf.source_prefix) + + strlen(raw_path) + 2)); + if (!*new_path) + return -ENOMEM; + + for (;;) { + sprintf(*new_path, "%s/%s", symbol_conf.source_prefix, + raw_path); + + if (access(*new_path, R_OK) == 0) + return 0; + + switch (errno) { + case ENAMETOOLONG: + case ENOENT: + case EROFS: + case EFAULT: + raw_path = strchr(++raw_path, '/'); + if (!raw_path) { + free(*new_path); + *new_path = NULL; + return -ENOENT; + } + continue; + + default: + free(*new_path); + *new_path = NULL; + return -errno; + } + } +} + #define LINEBUF_SIZE 256 #define NR_ADDITIONAL_LINES 2 @@ -244,6 +293,7 @@ int show_line_range(struct line_range *lr) struct line_node *ln; FILE *fp; int fd, ret; + char *tmp; /* Search a line range */ ret = init_vmlinux(); @@ -266,6 +316,15 @@ int show_line_range(struct line_range *lr) return ret; } + /* Convert source file path */ + tmp = lr->path; + ret = get_real_path(tmp, &lr->path); + free(tmp); /* Free old path */ + if (ret < 0) { + pr_warning("Failed to find source file. (%d)\n", ret); + return ret; + } + setup_pager(); if (lr->function) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 3e64e1fa105..a934a364c30 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -58,55 +58,6 @@ static int strtailcmp(const char *s1, const char *s2) return 0; } -/* - * Find a src file from a DWARF tag path. Prepend optional source path prefix - * and chop off leading directories that do not exist. Result is passed back as - * a newly allocated path on success. - * Return 0 if file was found and readable, -errno otherwise. - */ -static int get_real_path(const char *raw_path, char **new_path) -{ - if (!symbol_conf.source_prefix) { - if (access(raw_path, R_OK) == 0) { - *new_path = strdup(raw_path); - return 0; - } else - return -errno; - } - - *new_path = malloc((strlen(symbol_conf.source_prefix) + - strlen(raw_path) + 2)); - if (!*new_path) - return -ENOMEM; - - for (;;) { - sprintf(*new_path, "%s/%s", symbol_conf.source_prefix, - raw_path); - - if (access(*new_path, R_OK) == 0) - return 0; - - switch (errno) { - case ENAMETOOLONG: - case ENOENT: - case EROFS: - case EFAULT: - raw_path = strchr(++raw_path, '/'); - if (!raw_path) { - free(*new_path); - *new_path = NULL; - return -ENOENT; - } - continue; - - default: - free(*new_path); - *new_path = NULL; - return -errno; - } - } -} - /* Line number list operations */ /* Add a line to line number list */ @@ -1256,13 +1207,11 @@ end: static int line_range_add_line(const char *src, unsigned int lineno, struct line_range *lr) { - int ret; - - /* Copy real path */ + /* Copy source path */ if (!lr->path) { - ret = get_real_path(src, &lr->path); - if (ret != 0) - return ret; + lr->path = strdup(src); + if (lr->path == NULL) + return -ENOMEM; } return line_list__add_line(&lr->line_list, lineno); } @@ -1460,7 +1409,7 @@ int find_line_range(int fd, struct line_range *lr) } off = noff; } - pr_debug("path: %lx\n", (unsigned long)lr->path); + pr_debug("path: %s\n", lr->path); dwarf_end(dbg); return (ret < 0) ? ret : lf.found; -- cgit v1.2.3-18-g5258 From 6a330a3c8a648916b3c6bda79a78c38ac093af17 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 9 Jul 2010 18:29:11 +0900 Subject: perf probe: Support comp_dir to find an absolute source path Gcc generates DW_AT_comp_dir and stores relative source path if building kernel without O= option. In that case, perf probe --line sometimes doesn't work without --source option, because it tries to access relative source path. This adds DW_AT_comp_dir support to perf probe for finding an absolute source path when no --source option. LKML-Reference: <4C36EBE7.3060802@hitachi.com> Cc: Ingo Molnar Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 34 ++++++++++++++++++++++------------ tools/perf/util/probe-event.h | 1 + tools/perf/util/probe-finder.c | 21 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 8d08e75b2dd..4445a1e7052 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -201,28 +201,38 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, * a newly allocated path on success. * Return 0 if file was found and readable, -errno otherwise. */ -static int get_real_path(const char *raw_path, char **new_path) +static int get_real_path(const char *raw_path, const char *comp_dir, + char **new_path) { - if (!symbol_conf.source_prefix) { - if (access(raw_path, R_OK) == 0) { - *new_path = strdup(raw_path); - return 0; - } else - return -errno; + const char *prefix = symbol_conf.source_prefix; + + if (!prefix) { + if (raw_path[0] != '/' && comp_dir) + /* If not an absolute path, try to use comp_dir */ + prefix = comp_dir; + else { + if (access(raw_path, R_OK) == 0) { + *new_path = strdup(raw_path); + return 0; + } else + return -errno; + } } - *new_path = malloc((strlen(symbol_conf.source_prefix) + - strlen(raw_path) + 2)); + *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2)); if (!*new_path) return -ENOMEM; for (;;) { - sprintf(*new_path, "%s/%s", symbol_conf.source_prefix, - raw_path); + sprintf(*new_path, "%s/%s", prefix, raw_path); if (access(*new_path, R_OK) == 0) return 0; + if (!symbol_conf.source_prefix) + /* In case of searching comp_dir, don't retry */ + return -errno; + switch (errno) { case ENAMETOOLONG: case ENOENT: @@ -318,7 +328,7 @@ int show_line_range(struct line_range *lr) /* Convert source file path */ tmp = lr->path; - ret = get_real_path(tmp, &lr->path); + ret = get_real_path(tmp, lr->comp_dir, &lr->path); free(tmp); /* Free old path */ if (ret < 0) { pr_warning("Failed to find source file. (%d)\n", ret); diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index bc06d3e8baf..ed362acff4b 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -86,6 +86,7 @@ struct line_range { int end; /* End line number */ int offset; /* Start line offset */ char *path; /* Real path name */ + char *comp_dir; /* Compile directory */ struct list_head line_list; /* Visible lines */ }; diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index a934a364c30..37dcdb651a6 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -144,6 +144,15 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) return src; } +/* Get DW_AT_comp_dir (should be NULL with older gcc) */ +static const char *cu_get_comp_dir(Dwarf_Die *cu_die) +{ + Dwarf_Attribute attr; + if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) + return NULL; + return dwarf_formstring(&attr); +} + /* Compare diename and tname */ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) { @@ -1374,6 +1383,7 @@ int find_line_range(int fd, struct line_range *lr) size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; + const char *comp_dir; dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) { @@ -1409,6 +1419,17 @@ int find_line_range(int fd, struct line_range *lr) } off = noff; } + + /* Store comp_dir */ + if (lf.found) { + comp_dir = cu_get_comp_dir(&lf.cu_die); + if (comp_dir) { + lr->comp_dir = strdup(comp_dir); + if (!lr->comp_dir) + ret = -ENOMEM; + } + } + pr_debug("path: %s\n", lr->path); dwarf_end(dbg); -- cgit v1.2.3-18-g5258 From 8217563359878d11ef03cc76bc935ada89d73efd Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 9 Jul 2010 18:29:17 +0900 Subject: perf probe: Fix the logic of die_compare_name Invert the return value of die_compare_name(), because it returns a 'bool' result which should be expeced true if the die's name is same as compared string. LKML-Reference: <4C36EBED.1000006@hitachi.com> Cc: Arnaldo Carvalho de Melo Cc: Ingo Molnar Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 37dcdb651a6..f88070ea5b9 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -158,7 +158,7 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) { const char *name; name = dwarf_diename(dw_die); - return name ? strcmp(tname, name) : -1; + return name ? (strcmp(tname, name) == 0) : false; } /* Get type die, but skip qualifiers and typedef */ @@ -329,7 +329,7 @@ static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) tag = dwarf_tag(die_mem); if ((tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) && - (die_compare_name(die_mem, name) == 0)) + die_compare_name(die_mem, name)) return DIE_FIND_CB_FOUND; return DIE_FIND_CB_CONTINUE; @@ -348,7 +348,7 @@ static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) const char *name = data; if ((dwarf_tag(die_mem) == DW_TAG_member) && - (die_compare_name(die_mem, name) == 0)) + die_compare_name(die_mem, name)) return DIE_FIND_CB_FOUND; return DIE_FIND_CB_SIBLING; @@ -506,8 +506,8 @@ static int convert_variable_type(Dwarf_Die *vr_die, return -ENOMEM; } } - if (die_compare_name(&type, "char") != 0 && - die_compare_name(&type, "unsigned char") != 0) { + if (!die_compare_name(&type, "char") && + !die_compare_name(&type, "unsigned char")) { pr_warning("Failed to cast into string: " "%s is not (unsigned) char *.", dwarf_diename(vr_die)); @@ -1017,7 +1017,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) /* Check tag and diename */ if (dwarf_tag(sp_die) != DW_TAG_subprogram || - die_compare_name(sp_die, pp->function) != 0) + !die_compare_name(sp_die, pp->function)) return DWARF_CB_OK; pf->fname = dwarf_decl_file(sp_die); @@ -1340,7 +1340,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) struct line_range *lr = lf->lr; if (dwarf_tag(sp_die) == DW_TAG_subprogram && - die_compare_name(sp_die, lr->function) == 0) { + die_compare_name(sp_die, lr->function)) { lf->fname = dwarf_decl_file(sp_die); dwarf_decl_line(sp_die, &lr->offset); pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); -- cgit v1.2.3-18-g5258 From 63f20e744a595444f7ab1d47a29c5b74830feb47 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 15 Jul 2010 07:21:07 -0300 Subject: perf ui: Make END go to the last entry, not the top of the last page Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 06f248fde5c..932f12468c3 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -491,11 +491,11 @@ static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) break; case NEWT_KEY_END: offset = self->height - 1; + if (offset >= self->nr_entries) + offset = self->nr_entries - 1; - if (offset > self->nr_entries) - offset = self->nr_entries; - - self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; + self->index = self->nr_entries - 1; + self->first_visible_entry_idx = self->index - offset; self->seek(self, -offset, SEEK_END); break; case NEWT_KEY_RIGHT: -- cgit v1.2.3-18-g5258 From b66ecd97c1866f9a869643a5b69a984d5ce8f8f1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 15 Jul 2010 07:24:30 -0300 Subject: perf ui: Make ui_browser__run exit on unhandled hot keys Right now ENTER doesn't always exits the newt tree widget, as it is used for expanding/collapsing branches, but with the new tree widget being developed we need to regain control to handle it, expanding/collapsing branches. In fact its really up to the ui_browser user to state what extra keys should stop ui_browser__run, and it should handle just the ones needed for basic browsing. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 932f12468c3..7979003adea 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -498,12 +498,8 @@ static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) self->first_visible_entry_idx = self->index - offset; self->seek(self, -offset, SEEK_END); break; - case NEWT_KEY_RIGHT: - case NEWT_KEY_LEFT: - case NEWT_KEY_TAB: - return es->u.key; default: - continue; + return es->u.key; } if (ui_browser__refresh_entries(self) < 0) return -1; -- cgit v1.2.3-18-g5258 From cc5edb0eb9ce892b530e34a5d110382483587942 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 16 Jul 2010 12:35:07 -0300 Subject: perf hists: Factor out duplicated code Introducing hists__remove_entry_filter. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 68d288c975d..7b5848ce150 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -795,6 +795,21 @@ enum hist_filter { HIST_FILTER__THREAD, }; +static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, + enum hist_filter filter) +{ + h->filtered &= ~(1 << filter); + if (h->filtered) + return; + + ++self->nr_entries; + self->stats.total_period += h->period; + self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; + + if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) + self->max_sym_namelen = h->ms.sym->namelen; +} + void hists__filter_by_dso(struct hists *self, const struct dso *dso) { struct rb_node *nd; @@ -814,15 +829,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) continue; } - h->filtered &= ~(1 << HIST_FILTER__DSO); - if (!h->filtered) { - ++self->nr_entries; - self->stats.total_period += h->period; - self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; - if (h->ms.sym && - self->max_sym_namelen < h->ms.sym->namelen) - self->max_sym_namelen = h->ms.sym->namelen; - } + hists__remove_entry_filter(self, h, HIST_FILTER__DSO); } } @@ -841,15 +848,8 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) h->filtered |= (1 << HIST_FILTER__THREAD); continue; } - h->filtered &= ~(1 << HIST_FILTER__THREAD); - if (!h->filtered) { - ++self->nr_entries; - self->stats.total_period += h->period; - self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; - if (h->ms.sym && - self->max_sym_namelen < h->ms.sym->namelen) - self->max_sym_namelen = h->ms.sym->namelen; - } + + hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); } } -- cgit v1.2.3-18-g5258 From 4c21adf26f8fcf86a755b9b9f55c2e9fd241e1fb Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Tue, 20 Jul 2010 16:59:34 -0700 Subject: x86 cpufreq, perf: Make trace_power_frequency cpufreq driver independent and fix the broken case if a core's frequency depends on others. trace_power_frequency was only implemented in a rather ungeneric way in acpi-cpufreq driver's target() function only. -> Move the call to trace_power_frequency to cpufreq.c:cpufreq_notify_transition() where CPUFREQ_POSTCHANGE notifier is triggered. This will support power frequency tracing by all cpufreq drivers. trace_power_frequency did not trace frequency changes correctly when the userspace governor was used or when CPU cores' frequency depend on each other. -> Moving this into the CPUFREQ_POSTCHANGE notifier and pass the cpu which gets switched automatically fixes this. Robert Schoene provided some important fixes on top of my initial quick shot version which are integrated in this patch: - Forgot some changes in power_end trace (TP_printk/variable names) - Variable dummy in power_end must now be cpu_id - Use static 64 bit variable instead of unsigned int for cpu_id [akpm@linux-foundation.org: build fix] Signed-off-by: Thomas Renninger Cc: davej@codemonkey.org.uk Signed-off-by: Ingo Molnar Cc: Dave Jones Acked-by: Arjan van de Ven Cc: Robert Schoene Tested-by: Robert Schoene Signed-off-by: Andrew Morton --- tools/perf/builtin-timechart.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 5a52ed9fc10..5161619d471 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -300,8 +300,9 @@ struct trace_entry { struct power_entry { struct trace_entry te; - s64 type; - s64 value; + u64 type; + u64 value; + u64 cpu_id; }; #define TASK_COMM_LEN 16 @@ -498,13 +499,13 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; if (strcmp(event_str, "power:power_start") == 0) - c_state_start(data.cpu, data.time, pe->value); + c_state_start(pe->cpu_id, data.time, pe->value); if (strcmp(event_str, "power:power_end") == 0) - c_state_end(data.cpu, data.time); + c_state_end(pe->cpu_id, data.time); if (strcmp(event_str, "power:power_frequency") == 0) - p_state_change(data.cpu, data.time, pe->value); + p_state_change(pe->cpu_id, data.time, pe->value); if (strcmp(event_str, "sched:sched_wakeup") == 0) sched_wakeup(data.cpu, data.time, data.pid, te); -- cgit v1.2.3-18-g5258 From 7a007ca90b7c465137de06795ef4d5faa10f459e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 21 Jul 2010 09:19:41 -0300 Subject: perf hists: Mark entries filtered by parent And don't consider them in hists__inc_nr_entries. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 7b5848ce150..d998d1d706e 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -5,6 +5,12 @@ #include "sort.h" #include +enum hist_filter { + HIST_FILTER__DSO, + HIST_FILTER__THREAD, + HIST_FILTER__PARENT, +}; + struct callchain_param callchain_param = { .mode = CHAIN_GRAPH_REL, .min_percent = 0.5 @@ -52,11 +58,20 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) { + if (entry->filtered) + return; if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) self->max_sym_namelen = entry->ms.sym->namelen; ++self->nr_entries; } +static u8 symbol__parent_filter(const struct symbol *parent) +{ + if (symbol_conf.exclude_other && parent == NULL) + return 1 << HIST_FILTER__PARENT; + return 0; +} + struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, struct symbol *sym_parent, u64 period) @@ -75,6 +90,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, .level = al->level, .period = period, .parent = sym_parent, + .filtered = symbol__parent_filter(sym_parent), }; int cmp; @@ -790,11 +806,6 @@ print_entries: return ret; } -enum hist_filter { - HIST_FILTER__DSO, - HIST_FILTER__THREAD, -}; - static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, enum hist_filter filter) { -- cgit v1.2.3-18-g5258 From 8a6c5b261c1188379469807d84bfb1365d0f6823 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 20 Jul 2010 14:42:52 -0300 Subject: perf sort: Make column width code per hists instance They were globals, and since we support multiple hists and sessions at the same time, it doesn't make sense to calculate those values considereing all symbols in all sessions. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 34 +++++++------- tools/perf/util/hist.c | 115 ++++++++++++++++++++++++++++++++--------------- tools/perf/util/hist.h | 27 ++++++++--- tools/perf/util/newt.c | 9 ++-- tools/perf/util/sort.c | 17 +++---- tools/perf/util/sort.h | 6 +-- tools/perf/util/symbol.c | 9 ++++ tools/perf/util/symbol.h | 2 + 8 files changed, 140 insertions(+), 79 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index d7f21d71eb6..121339f4360 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -340,30 +340,29 @@ int event__synthesize_kernel_mmap(event__handler_t process, return process(&ev, session); } -static void thread__comm_adjust(struct thread *self) +static void thread__comm_adjust(struct thread *self, struct hists *hists) { char *comm = self->comm; if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && (!symbol_conf.comm_list || strlist__has_entry(symbol_conf.comm_list, comm))) { - unsigned int slen = strlen(comm); + u16 slen = strlen(comm); - if (slen > comms__col_width) { - comms__col_width = slen; - threads__col_width = slen + 6; - } + if (hists__new_col_len(hists, HISTC_COMM, slen)) + hists__set_col_len(hists, HISTC_THREAD, slen + 6); } } -static int thread__set_comm_adjust(struct thread *self, const char *comm) +static int thread__set_comm_adjust(struct thread *self, const char *comm, + struct hists *hists) { int ret = thread__set_comm(self, comm); if (ret) return ret; - thread__comm_adjust(self); + thread__comm_adjust(self, hists); return 0; } @@ -374,7 +373,8 @@ int event__process_comm(event_t *self, struct perf_session *session) dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); - if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { + if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm, + &session->hists)) { dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); return -1; } @@ -641,16 +641,13 @@ void thread__find_addr_location(struct thread *self, al->sym = NULL; } -static void dso__calc_col_width(struct dso *self) +static void dso__calc_col_width(struct dso *self, struct hists *hists) { if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && (!symbol_conf.dso_list || strlist__has_entry(symbol_conf.dso_list, self->name))) { - u16 slen = self->short_name_len; - if (verbose) - slen = self->long_name_len; - if (dsos__col_width < slen) - dsos__col_width = slen; + u16 slen = dso__name_len(self); + hists__new_col_len(hists, HISTC_DSO, slen); } self->slen_calculated = 1; @@ -729,16 +726,17 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, * sampled. */ if (!sort_dso.elide && !al->map->dso->slen_calculated) - dso__calc_col_width(al->map->dso); + dso__calc_col_width(al->map->dso, &session->hists); al->sym = map__find_symbol(al->map, al->addr, filter); } else { const unsigned int unresolved_col_width = BITS_PER_LONG / 4; - if (dsos__col_width < unresolved_col_width && + if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width && !symbol_conf.col_width_list_str && !symbol_conf.field_sep && !symbol_conf.dso_list) - dsos__col_width = unresolved_col_width; + hists__set_col_len(&session->hists, HISTC_DSO, + unresolved_col_width); } if (symbol_conf.sym_list && al->sym && diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index d998d1d706e..0bc67900352 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -16,6 +16,50 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; +u16 hists__col_len(struct hists *self, enum hist_column col) +{ + return self->col_len[col]; +} + +void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) +{ + self->col_len[col] = len; +} + +bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) +{ + if (len > hists__col_len(self, col)) { + hists__set_col_len(self, col, len); + return true; + } + return false; +} + +static void hists__reset_col_len(struct hists *self) +{ + enum hist_column col; + + for (col = 0; col < HISTC_NR_COLS; ++col) + hists__set_col_len(self, col, 0); +} + +static void hists__calc_col_len(struct hists *self, struct hist_entry *h) +{ + u16 len; + + if (h->ms.sym) + hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); + + len = thread__comm_len(h->thread); + if (hists__new_col_len(self, HISTC_COMM, len)) + hists__set_col_len(self, HISTC_THREAD, len + 6); + + if (h->ms.map) { + len = dso__name_len(h->ms.map->dso); + hists__new_col_len(self, HISTC_DSO, len); + } +} + static void hist_entry__add_cpumode_period(struct hist_entry *self, unsigned int cpumode, u64 period) { @@ -56,13 +100,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) return self; } -static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) +static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) { - if (entry->filtered) - return; - if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) - self->max_sym_namelen = entry->ms.sym->namelen; - ++self->nr_entries; + if (!h->filtered) { + hists__calc_col_len(self, h); + ++self->nr_entries; + } } static u8 symbol__parent_filter(const struct symbol *parent) @@ -208,7 +251,7 @@ void hists__collapse_resort(struct hists *self) tmp = RB_ROOT; next = rb_first(&self->entries); self->nr_entries = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); while (next) { n = rb_entry(next, struct hist_entry, rb_node); @@ -265,7 +308,7 @@ void hists__output_resort(struct hists *self) next = rb_first(&self->entries); self->nr_entries = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); while (next) { n = rb_entry(next, struct hist_entry, rb_node); @@ -532,8 +575,9 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, } int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, - struct hists *pair_hists, bool show_displacement, - long displacement, bool color, u64 session_total) + struct hists *hists, struct hists *pair_hists, + bool show_displacement, long displacement, + bool color, u64 session_total) { struct sort_entry *se; u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; @@ -637,24 +681,25 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); ret += se->se_snprintf(self, s + ret, size - ret, - se->se_width ? *se->se_width : 0); + hists__col_len(hists, se->se_width_idx)); } return ret; } -int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, - bool show_displacement, long displacement, FILE *fp, - u64 session_total) +int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, + struct hists *pair_hists, bool show_displacement, + long displacement, FILE *fp, u64 session_total) { char bf[512]; - hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, + hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, show_displacement, displacement, true, session_total); return fprintf(fp, "%s\n", bf); } -static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, +static size_t hist_entry__fprintf_callchain(struct hist_entry *self, + struct hists *hists, FILE *fp, u64 session_total) { int left_margin = 0; @@ -662,7 +707,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, if (sort__first_dimension == SORT_COMM) { struct sort_entry *se = list_first_entry(&hist_entry__sort_list, typeof(*se), list); - left_margin = se->se_width ? *se->se_width : 0; + left_margin = hists__col_len(hists, se->se_width_idx); left_margin -= thread__comm_len(self->thread); } @@ -733,17 +778,17 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, continue; } width = strlen(se->se_header); - if (se->se_width) { - if (symbol_conf.col_width_list_str) { - if (col_width) { - *se->se_width = atoi(col_width); - col_width = strchr(col_width, ','); - if (col_width) - ++col_width; - } + if (symbol_conf.col_width_list_str) { + if (col_width) { + hists__set_col_len(self, se->se_width_idx, + atoi(col_width)); + col_width = strchr(col_width, ','); + if (col_width) + ++col_width; } - width = *se->se_width = max(*se->se_width, width); } + if (!hists__new_col_len(self, se->se_width_idx, width)) + width = hists__col_len(self, se->se_width_idx); fprintf(fp, " %*s", width, se->se_header); } fprintf(fp, "\n"); @@ -766,9 +811,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, continue; fprintf(fp, " "); - if (se->se_width) - width = *se->se_width; - else + width = hists__col_len(self, se->se_width_idx); + if (width == 0) width = strlen(se->se_header); for (i = 0; i < width; i++) fprintf(fp, "."); @@ -788,12 +832,12 @@ print_entries: displacement = 0; ++position; } - ret += hist_entry__fprintf(h, pair, show_displacement, + ret += hist_entry__fprintf(h, self, pair, show_displacement, displacement, fp, self->stats.total_period); if (symbol_conf.use_callchain) - ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); - + ret += hist_entry__fprintf_callchain(h, self, fp, + self->stats.total_period); if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, MAP__FUNCTION, verbose, fp); @@ -817,8 +861,7 @@ static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, self->stats.total_period += h->period; self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; - if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) - self->max_sym_namelen = h->ms.sym->namelen; + hists__calc_col_len(self, h); } void hists__filter_by_dso(struct hists *self, const struct dso *dso) @@ -827,7 +870,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) self->nr_entries = self->stats.total_period = 0; self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); @@ -850,7 +893,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) self->nr_entries = self->stats.total_period = 0; self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 83fa33a7b38..92962b2f579 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -56,6 +56,16 @@ struct events_stats { u32 nr_unknown_events; }; +enum hist_column { + HISTC_SYMBOL, + HISTC_DSO, + HISTC_THREAD, + HISTC_COMM, + HISTC_PARENT, + HISTC_CPU, + HISTC_NR_COLS, /* Last entry */ +}; + struct hists { struct rb_node rb_node; struct rb_root entries; @@ -64,7 +74,7 @@ struct hists { u64 config; u64 event_stream; u32 type; - u32 max_sym_namelen; + u16 col_len[HISTC_NR_COLS]; }; struct hist_entry *__hists__add_entry(struct hists *self, @@ -72,12 +82,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, struct symbol *parent, u64 period); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); -int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, - bool show_displacement, long displacement, FILE *fp, - u64 total); +int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, + struct hists *pair_hists, bool show_displacement, + long displacement, FILE *fp, u64 total); int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, - struct hists *pair_hists, bool show_displacement, - long displacement, bool color, u64 total); + struct hists *hists, struct hists *pair_hists, + bool show_displacement, long displacement, + bool color, u64 total); void hist_entry__free(struct hist_entry *); void hists__output_resort(struct hists *self); @@ -95,6 +106,10 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head); void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); +u16 hists__col_len(struct hists *self, enum hist_column col); +void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); +bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); + #ifdef NO_NEWT_SUPPORT static inline int hists__browse(struct hists *self __used, const char *helpline __used, diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 7979003adea..ab6eb368cbf 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -692,7 +692,8 @@ static void hist_entry__append_callchain_browser(struct hist_entry *self, } static size_t hist_entry__append_browser(struct hist_entry *self, - newtComponent tree, u64 total) + newtComponent tree, + struct hists *hists) { char s[256]; size_t ret; @@ -700,8 +701,8 @@ static size_t hist_entry__append_browser(struct hist_entry *self, if (symbol_conf.exclude_other && !self->parent) return 0; - ret = hist_entry__snprintf(self, s, sizeof(s), NULL, - false, 0, false, total); + ret = hist_entry__snprintf(self, s, sizeof(s), hists, NULL, + false, 0, false, hists->stats.total_period); if (symbol_conf.use_callchain) { int indexes[2]; @@ -842,7 +843,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists if (h->filtered) continue; - len = hist_entry__append_browser(h, self->tree, hists->stats.total_period); + len = hist_entry__append_browser(h, self->tree, hists); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index c27b4b03fbc..1c61a4f4aa8 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,4 +1,5 @@ #include "sort.h" +#include "hist.h" regex_t parent_regex; const char default_parent_pattern[] = "^sys_|^do_page_fault"; @@ -10,11 +11,6 @@ int sort__has_parent = 0; enum sort_type sort__first_dimension; -unsigned int dsos__col_width; -unsigned int comms__col_width; -unsigned int threads__col_width; -unsigned int cpus__col_width; -static unsigned int parent_symbol__col_width; char * field_sep; LIST_HEAD(hist_entry__sort_list); @@ -36,7 +32,7 @@ struct sort_entry sort_thread = { .se_header = "Command: Pid", .se_cmp = sort__thread_cmp, .se_snprintf = hist_entry__thread_snprintf, - .se_width = &threads__col_width, + .se_width_idx = HISTC_THREAD, }; struct sort_entry sort_comm = { @@ -44,34 +40,35 @@ struct sort_entry sort_comm = { .se_cmp = sort__comm_cmp, .se_collapse = sort__comm_collapse, .se_snprintf = hist_entry__comm_snprintf, - .se_width = &comms__col_width, + .se_width_idx = HISTC_COMM, }; struct sort_entry sort_dso = { .se_header = "Shared Object", .se_cmp = sort__dso_cmp, .se_snprintf = hist_entry__dso_snprintf, - .se_width = &dsos__col_width, + .se_width_idx = HISTC_DSO, }; struct sort_entry sort_sym = { .se_header = "Symbol", .se_cmp = sort__sym_cmp, .se_snprintf = hist_entry__sym_snprintf, + .se_width_idx = HISTC_SYMBOL, }; struct sort_entry sort_parent = { .se_header = "Parent symbol", .se_cmp = sort__parent_cmp, .se_snprintf = hist_entry__parent_snprintf, - .se_width = &parent_symbol__col_width, + .se_width_idx = HISTC_PARENT, }; struct sort_entry sort_cpu = { .se_header = "CPU", .se_cmp = sort__cpu_cmp, .se_snprintf = hist_entry__cpu_snprintf, - .se_width = &cpus__col_width, + .se_width_idx = HISTC_CPU, }; struct sort_dimension { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 560c855417e..03a1722e6d0 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -36,10 +36,6 @@ extern struct sort_entry sort_comm; extern struct sort_entry sort_dso; extern struct sort_entry sort_sym; extern struct sort_entry sort_parent; -extern unsigned int dsos__col_width; -extern unsigned int comms__col_width; -extern unsigned int threads__col_width; -extern unsigned int cpus__col_width; extern enum sort_type sort__first_dimension; struct hist_entry { @@ -87,7 +83,7 @@ struct sort_entry { int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, unsigned int width); - unsigned int *se_width; + u8 se_width_idx; bool elide; }; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 971d0a05d6b..bc6e7e8c480 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -12,6 +12,7 @@ #include #include #include "build-id.h" +#include "debug.h" #include "symbol.h" #include "strlist.h" @@ -40,6 +41,14 @@ struct symbol_conf symbol_conf = { .try_vmlinux_path = true, }; +int dso__name_len(const struct dso *self) +{ + if (verbose) + return self->long_name_len; + + return self->short_name_len; +} + bool dso__loaded(const struct dso *self, enum map_type type) { return self->loaded & (1 << type); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 80e569bbdec..d436ee3d3a7 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -146,6 +146,8 @@ struct dso *dso__new(const char *name); struct dso *dso__new_kernel(const char *name); void dso__delete(struct dso *self); +int dso__name_len(const struct dso *self); + bool dso__loaded(const struct dso *self, enum map_type type); bool dso__sorted_by_name(const struct dso *self, enum map_type type); -- cgit v1.2.3-18-g5258 From 9f6d3c4b76314c40c866a935d78c80fd284768bd Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:58:05 +0200 Subject: tools/firewire: add userspace front-end of nosy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds nosy-dump, the userspace part of nosy, the IEEE 1394 traffic sniffer for Texas Instruments PCILynx/ PCILynx2 based cards. Author is Kristian Høgsberg. The files added here are taken from git://anongit.freedesktop.org/~krh/nosy commit ee29be97 (2009-11-10) with the following changes by Stefan Richter: - Parts pertaining to the kernel module removed from Makefile. - dist target removed from the Makefile. - Mentioned nosy-dump in the Kconfig help to nosy's kernel component. - Add copyright notice to nosy-dump.c. This is a duplicate of the respective notice in the kernel component nosy.c except for a time span of 2002 - 2006, according to Kristian's git log. "git shortlog decode-fcp.c list.h nosy-dump.[ch]" from nosy's git repository: Jonathan Woithe (1): Save logs on Ctrl-C Kristian Høgsberg (11): Pull over nosy from mercurial repo. Remove some fields from default view, add logging feature. Use infinite time out for poll(), mark more detail fields. Fix byte ordering macro. Add decoding of iso data and lock packets. Add flag to indicate data length field. Add cycle start packet decoding, add --iso and --cycle-start flags. Distinguish between phy-packets and 0-length iso data. Fix transaction and stats view. Add simple AV/C decoder. Don't break down on big payloads. Signed-off-by: Stefan Richter Acked-by: Kristian Høgsberg --- tools/firewire/Makefile | 19 + tools/firewire/decode-fcp.c | 239 ++++++++++ tools/firewire/list.h | 62 +++ tools/firewire/nosy-dump.c | 1037 +++++++++++++++++++++++++++++++++++++++++++ tools/firewire/nosy-dump.h | 292 ++++++++++++ 5 files changed, 1649 insertions(+) create mode 100644 tools/firewire/Makefile create mode 100644 tools/firewire/decode-fcp.c create mode 100644 tools/firewire/list.h create mode 100644 tools/firewire/nosy-dump.c create mode 100644 tools/firewire/nosy-dump.h (limited to 'tools') diff --git a/tools/firewire/Makefile b/tools/firewire/Makefile new file mode 100644 index 00000000000..6afcdc21c7c --- /dev/null +++ b/tools/firewire/Makefile @@ -0,0 +1,19 @@ +version = 0.3 +prefix = /usr + +CC = gcc + +all : nosy-dump + +nosy-dump : CFLAGS = -Wall -O2 -g +nosy-dump : CPPFLAGS = -DVERSION=\"$(version)\" -I../../drivers/firewire +nosy-dump : LDFLAGS = -g +nosy-dump : LDLIBS = -lpopt + +nosy-dump : nosy-dump.o decode-fcp.o + +clean : + rm -rf *.o nosy-dump + +install : + install nosy-dump $(prefix)/bin/nosy-dump diff --git a/tools/firewire/decode-fcp.c b/tools/firewire/decode-fcp.c new file mode 100644 index 00000000000..cb9a31b3753 --- /dev/null +++ b/tools/firewire/decode-fcp.c @@ -0,0 +1,239 @@ +#include +#include +#include "list.h" +#include "nosy-dump.h" + +#define CSR_FCP_COMMAND 0xfffff0000b00ull +#define CSR_FCP_RESPONSE 0xfffff0000d00ull + +static const char * const ctype_names[16] = { + "control", + "status", + "specific inquiry", + "notify", + "general inquiry", + "(reserved 0x05)", + "(reserved 0x06)", + "(reserved 0x07)", + "not implemented", + "accepted", + "rejected", + "in transition", + "stable", + "changed", + "(reserved 0x0e)", + "interim" +}; + +static const char * const subunit_type_names[32] = { + "monitor", + "audio", + "printer", + "disc", + "tape recorder/player", + "tuner", + "ca", + "camera", + "(reserved 0x08)", + "panel", + "bulletin board", + "camera storage", + "(reserved 0x0c)", + "(reserved 0x0d)", + "(reserved 0x0e)", + "(reserved 0x0f)", + "(reserved 0x10)", + "(reserved 0x11)", + "(reserved 0x12)", + "(reserved 0x13)", + "(reserved 0x14)", + "(reserved 0x15)", + "(reserved 0x16)", + "(reserved 0x17)", + "(reserved 0x18)", + "(reserved 0x19)", + "(reserved 0x1a)", + "(reserved 0x1b)", + "vendor unique", + "all subunit types", + "subunit_type extended to next byte", + "unit" +}; + +struct avc_enum { + int value; + const char *name; +}; + +struct avc_field { + const char *name; /* Short name for field. */ + int offset; /* Location of field, specified in bits. + * Negative means from end of packet */ + int width; /* Width of field, 0 means use data_length. */ + struct avc_enum *names; +}; + +struct avc_opcode_info { + const char *name; + struct avc_field fields[8]; +}; + +struct avc_enum power_field_names[] = { + { 0x70, "on" }, + { 0x60, "off" }, + { } +}; + +static const struct avc_opcode_info opcode_info[256] = { + + /* TA Document 1999026 + * AV/C Digital Interface Command Set General Specification + * Version 4.0 */ + [0xb2] = + { "power", { + { "state", 0, 8, power_field_names } + } + }, + [0x30] = + { "unit info", { + { "foo", 0, 8 }, + { "unit_type", 8, 5 }, + { "unit", 13, 3 }, + { "company id", 16, 24 }, + } + }, + [0x31] = { "subunit info" }, + [0x01] = { "reserve" }, + [0xb0] = { "version" }, + [0x00] = { "vendor dependent" }, + + [0x02] = { "plug info" }, + [0x12] = { "channel usage" }, + [0x24] = { "connect" }, + [0x20] = { "connect av" }, + [0x22] = { "connections" }, + [0x11] = { "digital input" }, + [0x10] = { "digital output" }, + [0x25] = { "disconnect" }, + [0x21] = { "disconnect av" }, + [0x19] = { "input plug signal format" }, + [0x18] = { "output plug signal format" }, + [0x1f] = { "general bus setup" }, + + /* TA Document 1999025 + * AV/C Descriptor Mechanism Specification Version 1.0 */ + [0x0c] = { "create descriptor" }, + [0x08] = { "open descriptor" }, + [0x09] = { "read descriptor" }, + [0x0a] = { "write descriptor" }, + [0x05] = { "open info block" }, + [0x06] = { "read info block" }, + [0x07] = { "write info block" }, + [0x0b] = { "search descriptor" }, + [0x0d] = { "object number select" }, + + /* TA Document 1999015 + * AV/C Command Set for Rate Control of Isochronous Data Flow 1.0 */ + [0xb3] = { "rate", { + { "subfunction", 0, 8 }, + { "result", 8, 8 }, + { "plug_type", 16, 8 }, + { "plug_id", 16, 8 }, + } + }, + + /* TA Document 1999008 + * AV/C Audio Subunit Specification 1.0 */ + [0xb8] = { "function block" }, + + /* TA Document 2001001 + * AV/C Panel Subunit Specification 1.1 */ + [0x7d] = { "gui update" }, + [0x7e] = { "push gui data" }, + [0x7f] = { "user action" }, + [0x7c] = { "pass through" }, + + /* */ + [0x26] = { "asynchronous connection" }, +}; + +struct avc_frame { + unsigned int operand0 : 8; + unsigned int opcode : 8; + unsigned int subunit_id : 3; + unsigned int subunit_type : 5; + unsigned int ctype : 4; + unsigned int cts : 4; +}; + +static void +decode_avc(struct link_transaction *t) +{ + struct avc_frame *frame = (struct avc_frame *) t->request->packet.write_block.data; + const struct avc_opcode_info *info; + const char *name; + char buffer[32]; + int i; + + info = &opcode_info[frame->opcode]; + if (info->name == NULL) { + snprintf(buffer, sizeof buffer, "(unknown opcode 0x%02x)", frame->opcode); + name = buffer; + } else { + name = info->name; + } + + printf("av/c %s, subunit_type=%s, subunit_id=%d, opcode=%s", + ctype_names[frame->ctype], subunit_type_names[frame->subunit_type], + frame->subunit_id, name); + + for (i = 0; info->fields[i].name != NULL; i++) { + printf(", %s", info->fields[i].name); + } + + printf("\n"); +} + + +int +decode_fcp(struct link_transaction *t) +{ + struct avc_frame *frame = (struct avc_frame *) t->request->packet.write_block.data; + unsigned long long offset; + + offset = ((unsigned long long) t->request->packet.common.offset_high << 32) | + t->request->packet.common.offset_low; + + if (t->request->packet.common.tcode != TCODE_WRITE_BLOCK) + return 0; + + if (offset == CSR_FCP_COMMAND || offset == CSR_FCP_RESPONSE) { + switch (frame->cts) { + case 0x00: + decode_avc(t); + break; + case 0x01: + printf("cal fcp frame (cts=0x01)\n"); + break; + case 0x02: + printf("ehs fcp frame (cts=0x02)\n"); + break; + case 0x03: + printf("havi fcp frame (cts=0x03)\n"); + break; + case 0x0e: + printf("vendor specific fcp frame (cts=0x0e)\n"); + break; + case 0x0f: + printf("extended cts\n"); + break; + default: + printf("reserved fcp frame (ctx=0x%02x)\n", frame->cts); + break; + } + return 1; + } + + return 0; +} + diff --git a/tools/firewire/list.h b/tools/firewire/list.h new file mode 100644 index 00000000000..fa119dd371f --- /dev/null +++ b/tools/firewire/list.h @@ -0,0 +1,62 @@ +struct list { + struct list *next, *prev; +}; + +static inline void +list_init(struct list *list) +{ + list->next = list; + list->prev = list; +} + +static inline int +list_empty(struct list *list) +{ + return list->next == list; +} + +static inline void +list_insert(struct list *link, struct list *new_link) +{ + new_link->prev = link->prev; + new_link->next = link; + new_link->prev->next = new_link; + new_link->next->prev = new_link; +} + +static inline void +list_append(struct list *list, struct list *new_link) +{ + list_insert((struct list *)list, new_link); +} + +static inline void +list_prepend(struct list *list, struct list *new_link) +{ + list_insert(list->next, new_link); +} + +static inline void +list_remove(struct list *link) +{ + link->prev->next = link->next; + link->next->prev = link->prev; +} + +#define list_entry(link, type, member) \ + ((type *)((char *)(link)-(unsigned long)(&((type *)0)->member))) + +#define list_head(list, type, member) \ + list_entry((list)->next, type, member) + +#define list_tail(list, type, member) \ + list_entry((list)->prev, type, member) + +#define list_next(elm, member) \ + list_entry((elm)->member.next, typeof(*elm), member) + +#define list_for_each_entry(pos, list, member) \ + for (pos = list_head(list, typeof(*pos), member); \ + &pos->member != (list); \ + pos = list_next(pos, member)) + diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c new file mode 100644 index 00000000000..64e5ea43892 --- /dev/null +++ b/tools/firewire/nosy-dump.c @@ -0,0 +1,1037 @@ +/* -*- mode: c; c-basic-offset: 2 -*- */ + +/* + * nosy-dump - Interface to snoop mode driver for TI PCILynx 1394 controllers + * Copyright (C) 2002-2006 Kristian Høgsberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "list.h" +#include "nosy-user.h" +#include "nosy-dump.h" + +enum { + PACKET_FIELD_DETAIL = 0x01, + PACKET_FIELD_DATA_LENGTH = 0x02, + /* Marks the fields we print in transaction view. */ + PACKET_FIELD_TRANSACTION = 0x04 +}; + +static void +print_packet(unsigned long *data, size_t length); +static void +decode_link_packet(struct link_packet *packet, size_t length, + int include_flags, int exclude_flags); + +static int run = 1; +sig_t sys_sigint_handler; + +static char *option_nosy_device = "/dev/nosy"; +static char *option_view = "packet"; +static char *option_output = NULL; +static char *option_input = NULL; +static int option_hex; +static int option_iso; +static int option_cycle_start; +static int option_version; +static int option_verbose; + +enum { + VIEW_TRANSACTION, + VIEW_PACKET, + VIEW_STATS +}; + +static const struct poptOption options[] = { + { + longName: "device", + shortName: 'd', + argInfo: POPT_ARG_STRING, + arg: &option_nosy_device, + descrip: "Path to nosy device.", + argDescrip: "DEVICE" + }, + { + longName: "view", + argInfo: POPT_ARG_STRING, + arg: &option_view, + descrip: "Specify view of bus traffic: packet, transaction or stats.", + argDescrip: "VIEW" + }, + { + longName: "hex", + shortName: 'x', + argInfo: POPT_ARG_NONE, + arg: &option_hex, + descrip: "Print each packet in hex.", + }, + { + longName: "iso", + argInfo: POPT_ARG_NONE, + arg: &option_iso, + descrip: "Print iso packets.", + }, + { + longName: "cycle-start", + argInfo: POPT_ARG_NONE, + arg: &option_cycle_start, + descrip: "Print cycle start packets.", + }, + { + longName: "verbose", + shortName: 'v', + argInfo: POPT_ARG_NONE, + arg: &option_verbose, + descrip: "Verbose packet view.", + }, + { + longName: "output", + shortName: 'o', + argInfo: POPT_ARG_STRING, + arg: &option_output, + descrip: "Log to output file.", + argDescrip: "FILENAME" + }, + { + longName: "input", + shortName: 'i', + argInfo: POPT_ARG_STRING, + arg: &option_input, + descrip: "Decode log from file.", + argDescrip: "FILENAME" + }, + { + longName: "version", + argInfo: POPT_ARG_NONE, + arg: &option_version, + descrip: "Specify print version info.", + }, + POPT_AUTOHELP + POPT_TABLEEND +}; + +void +sigint_handler(int signal_num) +{ + if (run == 1) { + run = 0; + /* Allow all Ctrl-C's except the first to interrupt the program in + * the usual way. + */ + signal(SIGINT, SIG_DFL); + } +} + +struct subaction * +subaction_create(unsigned long *data, size_t length) +{ + struct subaction *sa; + + /* we put the ack in the subaction struct for easy access. */ + sa = malloc(sizeof *sa - sizeof sa->packet + length); + sa->ack = data[length / 4 - 1]; + sa->length = length; + memcpy (&sa->packet, data, length); + + return sa; +} + +void +subaction_destroy(struct subaction *sa) +{ + free(sa); +} + +struct list pending_transaction_list = + { &pending_transaction_list, &pending_transaction_list }; + +struct link_transaction * +link_transaction_lookup(int request_node, int response_node, int tlabel) +{ + struct link_transaction *t; + + list_for_each_entry(t, &pending_transaction_list, link) { + if (t->request_node == request_node && + t->response_node == response_node && + t->tlabel == tlabel) + return t; + } + + t = malloc(sizeof *t); + t->request_node = request_node; + t->response_node = response_node; + t->tlabel = tlabel; + list_init(&t->request_list); + list_init(&t->response_list); + + list_append(&pending_transaction_list, &t->link); + + return t; +} + +void +link_transaction_destroy(struct link_transaction *t) +{ + while (!list_empty(&t->request_list)) { + struct subaction *sa = list_head(&t->request_list, struct subaction, link); + list_remove(&sa->link); + subaction_destroy(sa); + } + + while (!list_empty(&t->response_list)) { + struct subaction *sa = list_head(&t->response_list, struct subaction, link); + list_remove(&sa->link); + subaction_destroy(sa); + } + + free(t); +} + +struct protocol_decoder { + const char *name; + int (*decode)(struct link_transaction *t); +}; + +static struct protocol_decoder protocol_decoders[] = { + { "FCP", decode_fcp } +}; + +void +handle_transaction(struct link_transaction *t) +{ + struct subaction *sa; + int i; + + for (i = 0; i < array_length(protocol_decoders); i++) + if (protocol_decoders[i].decode(t)) + break; + + /* HACK: decode only fcp right now. */ + return; + + decode_link_packet(&t->request->packet, t->request->length, + PACKET_FIELD_TRANSACTION, 0); + if (t->response) + decode_link_packet(&t->response->packet, t->request->length, + PACKET_FIELD_TRANSACTION, 0); + else + printf("[no response]"); + + if (option_verbose) { + list_for_each_entry(sa, &t->request_list, link) + print_packet((unsigned long *) &sa->packet, sa->length); + list_for_each_entry(sa, &t->response_list, link) + print_packet((unsigned long *) &sa->packet, sa->length); + } + printf("\r\n"); + + link_transaction_destroy(t); +} + +void +clear_pending_transaction_list(void) +{ + struct link_transaction *t; + + while (!list_empty(&pending_transaction_list)) { + t = list_head(&pending_transaction_list, struct link_transaction, link); + list_remove(&t->link); + link_transaction_destroy(t); + /* print unfinished transactions */ + } +} + +static const char * const tcode_names[] = { + "write_quadlet_request", + "write_block_request", + "write_response", + "reserved", + "read_quadlet_request", + "read_block_request", + "read_quadlet_response", + "read_block_response", + "cycle_start", + "lock_request", + "iso_data", + "lock_response" +}; + +static const char * const ack_names[] = { + "no ack", + "ack_complete", + "ack_pending", + "reserved (0x03)", + "ack_busy_x", + "ack_busy_a", + "ack_busy_b", + "reserved (0x07)", + "reserved (0x08)", + "reserved (0x09)", + "reserved (0x0a)", + "reserved (0x0b)", + "reserved (0x0c)", + "ack_data_error", + "ack_type_error", + "reserved (0x0f)", +}; + +static const char * const rcode_names[] = { + "complete", + "reserved (0x01)", + "reserved (0x02)", + "reserved (0x03)", + "conflict_error", + "data_error", + "type_error", + "address_error", +}; + +static const char * const retry_names[] = { + "retry_1", + "retry_x", + "retry_a", + "retry_b", +}; + +enum { + PACKET_RESERVED, + PACKET_REQUEST, + PACKET_RESPONSE, + PACKET_OTHER, +}; + +struct packet_info { + const char *name; + int type; + int response_tcode; + struct packet_field *fields; + int field_count; +}; + +struct packet_field { + const char *name; /* Short name for field. */ + int offset; /* Location of field, specified in bits. + * Negative means from end of packet */ + int width; /* Width of field, 0 means use data_length. */ + int flags; /* Show options. */ + const char * const *value_names; +}; + +#define COMMON_REQUEST_FIELDS \ + { "dest", 0, 16, PACKET_FIELD_TRANSACTION }, \ + { "tl", 16, 6 }, \ + { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \ + { "tcode", 24, 4, PACKET_FIELD_TRANSACTION, tcode_names }, \ + { "pri", 28, 4, PACKET_FIELD_DETAIL }, \ + { "src", 32, 16, PACKET_FIELD_TRANSACTION }, \ + { "offs", 48, 48, PACKET_FIELD_TRANSACTION } + +#define COMMON_RESPONSE_FIELDS \ + { "dest", 0, 16 }, \ + { "tl", 16, 6 }, \ + { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \ + { "tcode", 24, 4, 0, tcode_names }, \ + { "pri", 28, 4, PACKET_FIELD_DETAIL }, \ + { "src", 32, 16 }, \ + { "rcode", 48, 4, PACKET_FIELD_TRANSACTION, rcode_names } + +struct packet_field read_quadlet_request_fields[] = { + COMMON_REQUEST_FIELDS, + { "crc", 96, 32, PACKET_FIELD_DETAIL }, + { "ack", 156, 4, 0, ack_names } +}; + +struct packet_field read_quadlet_response_fields[] = { + COMMON_RESPONSE_FIELDS, + { "data", 96, 32, PACKET_FIELD_TRANSACTION }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "ack", 188, 4, 0, ack_names } +}; + +struct packet_field read_block_request_fields[] = { + COMMON_REQUEST_FIELDS, + { "data_length", 96, 16, PACKET_FIELD_TRANSACTION }, + { "extended_tcode", 112, 16 }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "ack", 188, 4, 0, ack_names }, +}; + +struct packet_field block_response_fields[] = { + COMMON_RESPONSE_FIELDS, + { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH }, + { "extended_tcode", 112, 16 }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "data", 160, 0, PACKET_FIELD_TRANSACTION }, + { "crc", -64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names } +}; + +struct packet_field write_quadlet_request_fields[] = { + COMMON_REQUEST_FIELDS, + { "data", 96, 32, PACKET_FIELD_TRANSACTION }, + { "ack", -4, 4, 0, ack_names } +}; + +struct packet_field block_request_fields[] = { + COMMON_REQUEST_FIELDS, + { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH | PACKET_FIELD_TRANSACTION }, + { "extended_tcode", 112, 16, PACKET_FIELD_TRANSACTION }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "data", 160, 0, PACKET_FIELD_TRANSACTION }, + { "crc", -64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names } +}; + +struct packet_field write_response_fields[] = { + COMMON_RESPONSE_FIELDS, + { "reserved", 64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names } +}; + +struct packet_field iso_data_fields[] = { + { "data_length", 0, 16, PACKET_FIELD_DATA_LENGTH }, + { "tag", 16, 2 }, + { "channel", 18, 6 }, + { "tcode", 24, 4, 0, tcode_names }, + { "sy", 28, 4 }, + { "crc", 32, 32, PACKET_FIELD_DETAIL }, + { "data", 64, 0 }, + { "crc", -64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names } +}; + +static struct packet_info packet_info[] = { + { + .name = "write_quadlet_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_WRITE_RESPONSE, + .fields = write_quadlet_request_fields, + .field_count = array_length(write_quadlet_request_fields) + }, + { + .name = "write_block_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_WRITE_RESPONSE, + .fields = block_request_fields, + .field_count = array_length(block_request_fields) + }, + { + .name = "write_response", + .type = PACKET_RESPONSE, + .fields = write_response_fields, + .field_count = array_length(write_response_fields) + }, + { + .name = "reserved", + .type = PACKET_RESERVED, + }, + { + .name = "read_quadlet_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_READ_QUADLET_RESPONSE, + .fields = read_quadlet_request_fields, + .field_count = array_length(read_quadlet_request_fields) + }, + { + .name = "read_block_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_READ_BLOCK_RESPONSE, + .fields = read_block_request_fields, + .field_count = array_length(read_block_request_fields) + }, + { + .name = "read_quadlet_response", + .type = PACKET_RESPONSE, + .fields = read_quadlet_response_fields, + .field_count = array_length(read_quadlet_response_fields) + }, + { + .name = "read_block_response", + .type = PACKET_RESPONSE, + .fields = block_response_fields, + .field_count = array_length(block_response_fields) + }, + { + .name = "cycle_start", + .type = PACKET_OTHER, + .fields = write_quadlet_request_fields, + .field_count = array_length(write_quadlet_request_fields) + }, + { + .name = "lock_request", + .type = PACKET_REQUEST, + .fields = block_request_fields, + .field_count = array_length(block_request_fields) + }, + { + .name = "iso_data", + .type = PACKET_OTHER, + .fields = iso_data_fields, + .field_count = array_length(iso_data_fields) + }, + { + .name = "lock_response", + .type = PACKET_RESPONSE, + .fields = block_response_fields, + .field_count = array_length(block_response_fields) + } +}; + +int +handle_packet(unsigned long *data, size_t length) +{ + if (length == 0) { + printf("bus reset\r\n"); + clear_pending_transaction_list(); + } + else if (length > sizeof(struct phy_packet)) { + struct link_packet *p = (struct link_packet *) data; + struct subaction *sa, *prev; + struct link_transaction *t; + + switch (packet_info[p->common.tcode].type) { + case PACKET_REQUEST: + t = link_transaction_lookup(p->common.source, p->common.destination, + p->common.tlabel); + sa = subaction_create(data, length); + t->request = sa; + + if (!list_empty(&t->request_list)) { + prev = list_tail(&t->request_list, struct subaction, link); + + if (!ACK_BUSY(prev->ack)) { + /* error, we should only see ack_busy_* before the + * ack_pending/ack_complete -- this is an ack_pending + * instead (ack_complete would have finished the + * transaction). */ + } + + if (prev->packet.common.tcode != sa->packet.common.tcode || + prev->packet.common.tlabel != sa->packet.common.tlabel) + /* memcmp() ? */ + /* error, these should match for retries. */; + } + + list_append(&t->request_list, &sa->link); + + switch (sa->ack) { + case ACK_COMPLETE: + if (p->common.tcode != TCODE_WRITE_QUADLET && + p->common.tcode != TCODE_WRITE_BLOCK) + /* error, unified transactions only allowed for write */; + list_remove(&t->link); + handle_transaction(t); + break; + + case ACK_NO_ACK: + case ACK_DATA_ERROR: + case ACK_TYPE_ERROR: + list_remove(&t->link); + handle_transaction(t); + break; + + case ACK_PENDING: + /* request subaction phase over, wait for response. */ + break; + + case ACK_BUSY_X: + case ACK_BUSY_A: + case ACK_BUSY_B: + /* ok, wait for retry. */ + /* check that retry protocol is respected. */ + break; + } + break; + + case PACKET_RESPONSE: + t = link_transaction_lookup(p->common.destination, p->common.source, + p->common.tlabel); + if (list_empty(&t->request_list)) { + /* unsolicited response */ + } + + sa = subaction_create(data, length); + t->response = sa; + + if (!list_empty(&t->response_list)) { + prev = list_tail(&t->response_list, struct subaction, link); + + if (!ACK_BUSY(prev->ack)) + /* error, we should only see ack_busy_* before the + * ack_pending/ack_complete */; + + if (prev->packet.common.tcode != sa->packet.common.tcode || + prev->packet.common.tlabel != sa->packet.common.tlabel) + /* use memcmp() instead? */ + /* error, these should match for retries. */; + } + else { + prev = list_tail(&t->request_list, struct subaction, link); + if (prev->ack != ACK_PENDING) { + /* error, should not get response unless last request got + * ack_pending. */ + } + + if (packet_info[prev->packet.common.tcode].response_tcode != + sa->packet.common.tcode) { + /* error, tcode mismatch */ + } + } + + list_append(&t->response_list, &sa->link); + + switch (sa->ack) { + case ACK_COMPLETE: + case ACK_NO_ACK: + case ACK_DATA_ERROR: + case ACK_TYPE_ERROR: + list_remove(&t->link); + handle_transaction(t); + /* transaction complete, remove t from pending list. */ + break; + + case ACK_PENDING: + /* error for responses. */ + break; + + case ACK_BUSY_X: + case ACK_BUSY_A: + case ACK_BUSY_B: + /* no problem, wait for next retry */ + break; + } + + break; + + case PACKET_OTHER: + case PACKET_RESERVED: + return 0; + } + } + + return 1; +} + +unsigned int get_bits(struct link_packet *packet, int offset, int width) +{ + unsigned long *data = (unsigned long *) packet; + unsigned long index, shift, mask; + + index = offset / 32 + 1; + shift = 32 - (offset & 31) - width; + mask = width == 32 ? ~0 : (1 << width) - 1; + + return (data[index] >> shift) & mask; +} + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define byte_index(i) ((i) ^ 3) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define byte_index(i) (i) +#else +#error unsupported byte order. +#endif + +void dump_data(unsigned char *data, int length) +{ + int i, print_length; + + if (length > 128) + print_length = 128; + else + print_length = length; + + for (i = 0; i < print_length; i++) + printf("%s%02hhx", + (i % 4 == 0 && i != 0) ? " " : "", + data[byte_index(i)]); + + if (print_length < length) + printf(" (%d more bytes)", length - print_length); +} + +static void +decode_link_packet(struct link_packet *packet, size_t length, + int include_flags, int exclude_flags) +{ + struct packet_info *pi; + int data_length = 0; + int i; + + pi = &packet_info[packet->common.tcode]; + + for (i = 0; i < pi->field_count; i++) { + struct packet_field *f = &pi->fields[i]; + int offset; + + if (f->flags & exclude_flags) + continue; + if (include_flags && !(f->flags & include_flags)) + continue; + + if (f->offset < 0) + offset = length * 8 + f->offset - 32; + else + offset = f->offset; + + if (f->value_names != NULL) { + unsigned long bits; + + bits = get_bits(packet, offset, f->width); + printf("%s", f->value_names[bits]); + } + else if (f->width == 0) { + printf("%s=[", f->name); + dump_data((unsigned char *) packet + (offset / 8 + 4), data_length); + printf("]"); + } + else { + unsigned long long bits; + int high_width, low_width; + + if ((offset & ~31) != ((offset + f->width - 1) & ~31)) { + /* Bit field spans quadlet boundary. */ + high_width = ((offset + 31) & ~31) - offset; + low_width = f->width - high_width; + + bits = get_bits(packet, offset, high_width); + bits = (bits << low_width) | + get_bits(packet, offset + high_width, low_width); + } + else + bits = get_bits(packet, offset, f->width); + + printf("%s=0x%0*llx", f->name, (f->width + 3) / 4, bits); + + if (f->flags & PACKET_FIELD_DATA_LENGTH) + data_length = bits; + } + + if (i < pi->field_count - 1) + printf(", "); + } +} + +static void +print_packet(unsigned long *data, size_t length) +{ + int i; + + printf("%6lu ", data[0]); + + if (length == 4) + printf("bus reset"); + else if (length < sizeof(struct phy_packet)) { + printf("short packet: "); + for (i = 1; i < length / 4; i++) + printf("%s%08lx", i == 0 ? "[" : " ", data[i]); + printf("]"); + + } + else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) { + struct phy_packet *pp = (struct phy_packet *) data; + + /* phy packet are 3 quadlets: the 1 quadlet payload, + * the bitwise inverse of the payload and the snoop + * mode ack */ + + switch (pp->common.identifier) { + case PHY_PACKET_CONFIGURATION: + if (!pp->phy_config.set_root && !pp->phy_config.set_gap_count) { + printf("ext phy config: phy_id=%02x", pp->phy_config.root_id); + } + else { + printf("phy config:"); + if (pp->phy_config.set_root) + printf(" set_root_id=%02x", pp->phy_config.root_id); + if (pp->phy_config.set_gap_count) + printf(" set_gap_count=%d", pp->phy_config.gap_count); + } + break; + + case PHY_PACKET_LINK_ON: + printf("link-on packet, phy_id=%02x", pp->link_on.phy_id); + break; + + case PHY_PACKET_SELF_ID: + if (pp->self_id.extended) { + printf("extended self id: phy_id=%02x, seq=%d", + pp->ext_self_id.phy_id, pp->ext_self_id.sequence); + } + else { + static const char * const speed_names[] = { + "S100", "S200", "S400", "BETA" + }; + printf("self id: phy_id=%02x, link %s, gap_count=%d, speed=%s%s%s", + pp->self_id.phy_id, + (pp->self_id.link_active ? "active" : "not active"), + pp->self_id.gap_count, + speed_names[pp->self_id.phy_speed], + (pp->self_id.contender ? ", irm contender" : ""), + (pp->self_id.initiated_reset ? ", initiator" : "")); + + } + break; + default: + printf("unknown phy packet: "); + for (i = 1; i < length / 4; i++) + printf("%s%08lx", i == 0 ? "[" : " ", data[i]); + printf("]"); + break; + } + } + else { + struct link_packet *packet = (struct link_packet *) data; + + decode_link_packet(packet, length, 0, + option_verbose ? 0 : PACKET_FIELD_DETAIL); + } + + if (option_hex) { + printf(" ["); + dump_data((unsigned char *) data + 4, length - 4); + printf("]"); + } + + printf("\r\n"); +} + +#define HIDE_CURSOR "\033[?25l" +#define SHOW_CURSOR "\033[?25h" +#define CLEAR "\033[H\033[2J" + +static void +print_stats(unsigned long *data, size_t length) +{ + static int bus_reset_count, short_packet_count, phy_packet_count; + static int tcode_count[16]; + static struct timeval last_update; + struct timeval now; + int i; + + if (length == 0) + bus_reset_count++; + else if (length < sizeof(struct phy_packet)) + short_packet_count++; + else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) + phy_packet_count++; + else { + struct link_packet *packet = (struct link_packet *) data; + tcode_count[packet->common.tcode]++; + } + + gettimeofday(&now, NULL); + if (now.tv_sec <= last_update.tv_sec && + now.tv_usec < last_update.tv_usec + 500000) + return; + + last_update = now; + printf(CLEAR HIDE_CURSOR + " bus resets : %8d\n" + " short packets : %8d\n" + " phy packets : %8d\n", + bus_reset_count, short_packet_count, phy_packet_count); + + for (i = 0; i < array_length(packet_info); i++) + if (packet_info[i].type != PACKET_RESERVED) + printf(" %-24s: %8d\n", packet_info[i].name, tcode_count[i]); + printf(SHOW_CURSOR "\n"); +} + +struct termios saved_attributes; + +void +reset_input_mode (void) +{ + tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes); +} + +void +set_input_mode (void) +{ + struct termios tattr; + + /* Make sure stdin is a terminal. */ + if (!isatty(STDIN_FILENO)) { + fprintf(stderr, "Not a terminal.\n"); + exit(EXIT_FAILURE); + } + + /* Save the terminal attributes so we can restore them later. */ + tcgetattr(STDIN_FILENO, &saved_attributes); + atexit(reset_input_mode); + + /* Set the funny terminal modes. */ + tcgetattr(STDIN_FILENO, &tattr); + tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */ + tattr.c_cc[VMIN] = 1; + tattr.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr); +} + +int main(int argc, const char *argv[]) +{ + int fd = -1; + FILE *output = NULL, *input = NULL; + poptContext con; + int retval; + int view; + char c; + struct pollfd pollfds[2]; + + sys_sigint_handler = signal(SIGINT, sigint_handler); + + con = poptGetContext(NULL, argc, argv, options, 0); + retval = poptGetNextOpt(con); + if (retval < -1) { + poptPrintUsage(con, stdout, 0); + return -1; + } + + if (option_version) { + printf("dump tool for nosy sniffer, version %s\n", VERSION); + return 0; + } + + if (__BYTE_ORDER != __LITTLE_ENDIAN) + fprintf(stderr, "warning: nosy has only been tested on little " + "endian machines\n"); + + if (option_input != NULL) { + input = fopen(option_input, "r"); + if (input == NULL) { + fprintf(stderr, "Could not open %s, %m\n", option_input); + return -1; + } + } + else { + fd = open(option_nosy_device, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Could not open %s, %m\n", option_nosy_device); + return -1; + } + set_input_mode(); + } + + if (strcmp(option_view, "transaction") == 0) + view = VIEW_TRANSACTION; + else if (strcmp(option_view, "stats") == 0) + view = VIEW_STATS; + else + view = VIEW_PACKET; + + if (option_output) { + output = fopen(option_output, "w"); + if (output == NULL) { + fprintf(stderr, "Could not open %s, %m\n", option_output); + return -1; + } + } + + setvbuf(stdout, NULL, _IOLBF, BUFSIZ); + + if (1) { + unsigned long buf[128 * 1024]; + unsigned int filter; + int length; + + filter = ~0; + if (!option_iso) + filter &= ~(1 <> 2) == 0) +#define ACK_BUSY(a) ((a >> 2) == 1) +#define ACK_ERROR(a) ((a >> 2) == 3) + +#define SPEED_100 0x0 +#define SPEED_200 0x1 +#define SPEED_400 0x2 + +struct phy_packet { + unsigned long timestamp; + + union { + struct { + unsigned int zero : 24; + unsigned int phy_id : 6; + unsigned int identifier : 2; + } common, link_on; + struct { + unsigned int zero : 16; + unsigned int gap_count : 6; + unsigned int set_gap_count : 1; + unsigned int set_root : 1; + unsigned int root_id : 6; + unsigned int identifier : 2; + } phy_config; + struct { + unsigned int more_packets : 1; + unsigned int initiated_reset : 1; + unsigned int port2 : 2; + unsigned int port1 : 2; + unsigned int port0 : 2; + unsigned int power_class : 3; + unsigned int contender : 1; + unsigned int phy_delay : 2; + unsigned int phy_speed : 2; + unsigned int gap_count : 6; + unsigned int link_active : 1; + unsigned int extended : 1; + unsigned int phy_id : 6; + unsigned int identifier : 2; + } self_id; + + struct { + unsigned int more_packets : 1; + unsigned int reserved1 : 1; + unsigned int porth : 2; + unsigned int portg : 2; + unsigned int portf : 2; + unsigned int porte : 2; + unsigned int portd : 2; + unsigned int portc : 2; + unsigned int portb : 2; + unsigned int porta : 2; + unsigned int reserved0 : 2; + unsigned int sequence : 3; + unsigned int extended : 1; + unsigned int phy_id : 6; + unsigned int identifier : 2; + } ext_self_id; + }; + + unsigned long inverted; + unsigned long ack; +}; + +#define PHY_PACKET_CONFIGURATION 0x00 +#define PHY_PACKET_LINK_ON 0x01 +#define PHY_PACKET_SELF_ID 0x02 + +struct link_packet { + unsigned long timestamp; + + union { + struct { + unsigned int priority : 4; + unsigned int tcode : 4; + unsigned int rt : 2; + unsigned int tlabel : 6; + unsigned int destination : 16; + + unsigned int offset_high : 16; + unsigned int source : 16; + + unsigned long offset_low; + } common; + + struct { + unsigned int priority : 4; + unsigned int tcode : 4; + unsigned int rt : 2; + unsigned int tlabel : 6; + unsigned int destination : 16; + + unsigned int offset_high : 16; + unsigned int source : 16; + + unsigned long offset_low; + + unsigned long crc; + } read_quadlet; + + struct { + unsigned int priority : 4; + unsigned int tcode : 4; + unsigned int rt : 2; + unsigned int tlabel : 6; + unsigned int destination : 16; + + unsigned int reserved0 : 12; + unsigned int rcode : 4; + unsigned int source : 16; + + unsigned long reserved1; + + unsigned long data; + + unsigned long crc; + } read_quadlet_response; + + struct { + unsigned int priority : 4; + unsigned int tcode : 4; + unsigned int rt : 2; + unsigned int tlabel : 6; + unsigned int destination : 16; + + unsigned int offset_high : 16; + unsigned int source : 16; + + unsigned long offset_low; + + unsigned int extended_tcode : 16; + unsigned int data_length : 16; + + unsigned long crc; + } read_block; + + struct { + unsigned int priority : 4; + unsigned int tcode : 4; + unsigned int rt : 2; + unsigned int tlabel : 6; + unsigned int destination : 16; + + unsigned int reserved0 : 12; + unsigned int rcode : 4; + unsigned int source : 16; + + unsigned long reserved1; + + unsigned int extended_tcode : 16; + unsigned int data_length : 16; + + unsigned long crc; + + unsigned long data[0]; + + /* crc and ack follows. */ + + } read_block_response; + + struct { + unsigned int priority : 4; + unsigned int tcode : 4; + unsigned int rt : 2; + unsigned int tlabel : 6; + unsigned int destination : 16; + + unsigned int offset_high : 16; + unsigned int source : 16; + + unsigned long offset_low; + + unsigned long data; + + unsigned long crc; + + } write_quadlet; + + struct { + unsigned int priority : 4; + unsigned int tcode : 4; + unsigned int rt : 2; + unsigned int tlabel : 6; + unsigned int destination : 16; + + unsigned int offset_high : 16; + unsigned int source : 16; + + unsigned int offset_low : 32; + + unsigned int extended_tcode : 16; + unsigned int data_length : 16; + + unsigned long crc; + unsigned long data[0]; + + /* crc and ack follows. */ + + } write_block; + + struct { + unsigned int priority : 4; + unsigned int tcode : 4; + unsigned int rt : 2; + unsigned int tlabel : 6; + unsigned int destination : 16; + + unsigned int reserved0 : 12; + unsigned int rcode : 4; + unsigned int source : 16; + + unsigned long reserved1; + + unsigned long crc; + } write_response; + + struct { + unsigned int priority : 4; + unsigned int tcode : 4; + unsigned int rt : 2; + unsigned int tlabel : 6; + unsigned int destination : 16; + + unsigned int offset_high : 16; + unsigned int source : 16; + + unsigned long offset_low; + + unsigned long data; + + unsigned long crc; + } cycle_start; + + struct { + unsigned int sy : 4; + unsigned int tcode : 4; + unsigned int channel : 6; + unsigned int tag : 2; + unsigned int data_length : 16; + + unsigned long crc; + } iso_data; + + }; + +}; + +struct subaction { + unsigned long ack; + size_t length; + struct list link; + struct link_packet packet; +}; + +struct link_transaction { + int request_node, response_node, tlabel; + struct subaction *request, *response; + struct list request_list, response_list; + struct list link; +}; + +int decode_fcp(struct link_transaction *t); + +#endif /* __nosy_dump_h__ */ -- cgit v1.2.3-18-g5258 From 1bcc69fb6a48edb3ddf2e8c42f1d1c242196bf74 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:58:05 +0200 Subject: tools/firewire: nosy-dump: fix it on x86-64 Replace 'unsigned long' and the (unaffected) 'unsigned int' by uint32_t if they represent quadlets. Signed-off-by: Stefan Richter --- tools/firewire/decode-fcp.c | 12 +- tools/firewire/nosy-dump.c | 37 +++--- tools/firewire/nosy-dump.h | 272 ++++++++++++++------------------------------ 3 files changed, 112 insertions(+), 209 deletions(-) (limited to 'tools') diff --git a/tools/firewire/decode-fcp.c b/tools/firewire/decode-fcp.c index cb9a31b3753..b6b37253761 100644 --- a/tools/firewire/decode-fcp.c +++ b/tools/firewire/decode-fcp.c @@ -158,12 +158,12 @@ static const struct avc_opcode_info opcode_info[256] = { }; struct avc_frame { - unsigned int operand0 : 8; - unsigned int opcode : 8; - unsigned int subunit_id : 3; - unsigned int subunit_type : 5; - unsigned int ctype : 4; - unsigned int cts : 4; + uint32_t operand0:8; + uint32_t opcode:8; + uint32_t subunit_id:3; + uint32_t subunit_type:5; + uint32_t ctype:4; + uint32_t cts:4; }; static void diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c index 64e5ea43892..0bad0a42d77 100644 --- a/tools/firewire/nosy-dump.c +++ b/tools/firewire/nosy-dump.c @@ -46,7 +46,7 @@ enum { }; static void -print_packet(unsigned long *data, size_t length); +print_packet(uint32_t *data, size_t length); static void decode_link_packet(struct link_packet *packet, size_t length, int include_flags, int exclude_flags); @@ -151,7 +151,7 @@ sigint_handler(int signal_num) } struct subaction * -subaction_create(unsigned long *data, size_t length) +subaction_create(uint32_t *data, size_t length) { struct subaction *sa; @@ -247,9 +247,9 @@ handle_transaction(struct link_transaction *t) if (option_verbose) { list_for_each_entry(sa, &t->request_list, link) - print_packet((unsigned long *) &sa->packet, sa->length); + print_packet((uint32_t *) &sa->packet, sa->length); list_for_each_entry(sa, &t->response_list, link) - print_packet((unsigned long *) &sa->packet, sa->length); + print_packet((uint32_t *) &sa->packet, sa->length); } printf("\r\n"); @@ -506,7 +506,7 @@ static struct packet_info packet_info[] = { }; int -handle_packet(unsigned long *data, size_t length) +handle_packet(uint32_t *data, size_t length) { if (length == 0) { printf("bus reset\r\n"); @@ -642,8 +642,8 @@ handle_packet(unsigned long *data, size_t length) unsigned int get_bits(struct link_packet *packet, int offset, int width) { - unsigned long *data = (unsigned long *) packet; - unsigned long index, shift, mask; + uint32_t *data = (uint32_t *) packet; + uint32_t index, shift, mask; index = offset / 32 + 1; shift = 32 - (offset & 31) - width; @@ -703,7 +703,7 @@ decode_link_packet(struct link_packet *packet, size_t length, offset = f->offset; if (f->value_names != NULL) { - unsigned long bits; + uint32_t bits; bits = get_bits(packet, offset, f->width); printf("%s", f->value_names[bits]); @@ -741,18 +741,18 @@ decode_link_packet(struct link_packet *packet, size_t length, } static void -print_packet(unsigned long *data, size_t length) +print_packet(uint32_t *data, size_t length) { int i; - printf("%6lu ", data[0]); + printf("%6u ", data[0]); if (length == 4) printf("bus reset"); else if (length < sizeof(struct phy_packet)) { printf("short packet: "); for (i = 1; i < length / 4; i++) - printf("%s%08lx", i == 0 ? "[" : " ", data[i]); + printf("%s%08x", i == 0 ? "[" : " ", data[i]); printf("]"); } @@ -803,7 +803,7 @@ print_packet(unsigned long *data, size_t length) default: printf("unknown phy packet: "); for (i = 1; i < length / 4; i++) - printf("%s%08lx", i == 0 ? "[" : " ", data[i]); + printf("%s%08x", i == 0 ? "[" : " ", data[i]); printf("]"); break; } @@ -829,7 +829,7 @@ print_packet(unsigned long *data, size_t length) #define CLEAR "\033[H\033[2J" static void -print_stats(unsigned long *data, size_t length) +print_stats(uint32_t *data, size_t length) { static int bus_reset_count, short_packet_count, phy_packet_count; static int tcode_count[16]; @@ -959,8 +959,8 @@ int main(int argc, const char *argv[]) setvbuf(stdout, NULL, _IOLBF, BUFSIZ); if (1) { - unsigned long buf[128 * 1024]; - unsigned int filter; + uint32_t buf[128 * 1024]; + uint32_t filter; int length; filter = ~0; @@ -968,11 +968,10 @@ int main(int argc, const char *argv[]) filter &= ~(1 < +struct phy_packet { + uint32_t timestamp; union { struct { - unsigned int zero : 24; - unsigned int phy_id : 6; - unsigned int identifier : 2; + uint32_t zero:24; + uint32_t phy_id:6; + uint32_t identifier:2; } common, link_on; + struct { - unsigned int zero : 16; - unsigned int gap_count : 6; - unsigned int set_gap_count : 1; - unsigned int set_root : 1; - unsigned int root_id : 6; - unsigned int identifier : 2; + uint32_t zero:16; + uint32_t gap_count:6; + uint32_t set_gap_count:1; + uint32_t set_root:1; + uint32_t root_id:6; + uint32_t identifier:2; } phy_config; + struct { - unsigned int more_packets : 1; - unsigned int initiated_reset : 1; - unsigned int port2 : 2; - unsigned int port1 : 2; - unsigned int port0 : 2; - unsigned int power_class : 3; - unsigned int contender : 1; - unsigned int phy_delay : 2; - unsigned int phy_speed : 2; - unsigned int gap_count : 6; - unsigned int link_active : 1; - unsigned int extended : 1; - unsigned int phy_id : 6; - unsigned int identifier : 2; + uint32_t more_packets:1; + uint32_t initiated_reset:1; + uint32_t port2:2; + uint32_t port1:2; + uint32_t port0:2; + uint32_t power_class:3; + uint32_t contender:1; + uint32_t phy_delay:2; + uint32_t phy_speed:2; + uint32_t gap_count:6; + uint32_t link_active:1; + uint32_t extended:1; + uint32_t phy_id:6; + uint32_t identifier:2; } self_id; struct { - unsigned int more_packets : 1; - unsigned int reserved1 : 1; - unsigned int porth : 2; - unsigned int portg : 2; - unsigned int portf : 2; - unsigned int porte : 2; - unsigned int portd : 2; - unsigned int portc : 2; - unsigned int portb : 2; - unsigned int porta : 2; - unsigned int reserved0 : 2; - unsigned int sequence : 3; - unsigned int extended : 1; - unsigned int phy_id : 6; - unsigned int identifier : 2; + uint32_t more_packets:1; + uint32_t reserved1:1; + uint32_t porth:2; + uint32_t portg:2; + uint32_t portf:2; + uint32_t porte:2; + uint32_t portd:2; + uint32_t portc:2; + uint32_t portb:2; + uint32_t porta:2; + uint32_t reserved0:2; + uint32_t sequence:3; + uint32_t extended:1; + uint32_t phy_id:6; + uint32_t identifier:2; } ext_self_id; }; - - unsigned long inverted; - unsigned long ack; + uint32_t inverted; + uint32_t ack; }; #define PHY_PACKET_CONFIGURATION 0x00 @@ -95,186 +97,88 @@ struct phy_packet { #define PHY_PACKET_SELF_ID 0x02 struct link_packet { - unsigned long timestamp; - + uint32_t timestamp; union { struct { - unsigned int priority : 4; - unsigned int tcode : 4; - unsigned int rt : 2; - unsigned int tlabel : 6; - unsigned int destination : 16; + uint32_t priority:4; + uint32_t tcode:4; + uint32_t rt:2; + uint32_t tlabel:6; + uint32_t destination:16; - unsigned int offset_high : 16; - unsigned int source : 16; + uint32_t offset_high:16; + uint32_t source:16; - unsigned long offset_low; + uint32_t offset_low; } common; struct { - unsigned int priority : 4; - unsigned int tcode : 4; - unsigned int rt : 2; - unsigned int tlabel : 6; - unsigned int destination : 16; - - unsigned int offset_high : 16; - unsigned int source : 16; - - unsigned long offset_low; - - unsigned long crc; + uint32_t common[3]; + uint32_t crc; } read_quadlet; struct { - unsigned int priority : 4; - unsigned int tcode : 4; - unsigned int rt : 2; - unsigned int tlabel : 6; - unsigned int destination : 16; - - unsigned int reserved0 : 12; - unsigned int rcode : 4; - unsigned int source : 16; - - unsigned long reserved1; - - unsigned long data; - - unsigned long crc; + uint32_t common[3]; + uint32_t data; + uint32_t crc; } read_quadlet_response; struct { - unsigned int priority : 4; - unsigned int tcode : 4; - unsigned int rt : 2; - unsigned int tlabel : 6; - unsigned int destination : 16; - - unsigned int offset_high : 16; - unsigned int source : 16; - - unsigned long offset_low; - - unsigned int extended_tcode : 16; - unsigned int data_length : 16; - - unsigned long crc; + uint32_t common[3]; + uint32_t extended_tcode:16; + uint32_t data_length:16; + uint32_t crc; } read_block; struct { - unsigned int priority : 4; - unsigned int tcode : 4; - unsigned int rt : 2; - unsigned int tlabel : 6; - unsigned int destination : 16; - - unsigned int reserved0 : 12; - unsigned int rcode : 4; - unsigned int source : 16; - - unsigned long reserved1; - - unsigned int extended_tcode : 16; - unsigned int data_length : 16; - - unsigned long crc; - - unsigned long data[0]; - + uint32_t common[3]; + uint32_t extended_tcode:16; + uint32_t data_length:16; + uint32_t crc; + uint32_t data[0]; /* crc and ack follows. */ - } read_block_response; struct { - unsigned int priority : 4; - unsigned int tcode : 4; - unsigned int rt : 2; - unsigned int tlabel : 6; - unsigned int destination : 16; - - unsigned int offset_high : 16; - unsigned int source : 16; - - unsigned long offset_low; - - unsigned long data; - - unsigned long crc; - + uint32_t common[3]; + uint32_t data; + uint32_t crc; } write_quadlet; struct { - unsigned int priority : 4; - unsigned int tcode : 4; - unsigned int rt : 2; - unsigned int tlabel : 6; - unsigned int destination : 16; - - unsigned int offset_high : 16; - unsigned int source : 16; - - unsigned int offset_low : 32; - - unsigned int extended_tcode : 16; - unsigned int data_length : 16; - - unsigned long crc; - unsigned long data[0]; - + uint32_t common[3]; + uint32_t extended_tcode:16; + uint32_t data_length:16; + uint32_t crc; + uint32_t data[0]; /* crc and ack follows. */ - } write_block; struct { - unsigned int priority : 4; - unsigned int tcode : 4; - unsigned int rt : 2; - unsigned int tlabel : 6; - unsigned int destination : 16; - - unsigned int reserved0 : 12; - unsigned int rcode : 4; - unsigned int source : 16; - - unsigned long reserved1; - - unsigned long crc; + uint32_t common[3]; + uint32_t crc; } write_response; struct { - unsigned int priority : 4; - unsigned int tcode : 4; - unsigned int rt : 2; - unsigned int tlabel : 6; - unsigned int destination : 16; - - unsigned int offset_high : 16; - unsigned int source : 16; - - unsigned long offset_low; - - unsigned long data; - - unsigned long crc; + uint32_t common[3]; + uint32_t data; + uint32_t crc; } cycle_start; struct { - unsigned int sy : 4; - unsigned int tcode : 4; - unsigned int channel : 6; - unsigned int tag : 2; - unsigned int data_length : 16; + uint32_t sy:4; + uint32_t tcode:4; + uint32_t channel:6; + uint32_t tag:2; + uint32_t data_length:16; - unsigned long crc; + uint32_t crc; } iso_data; - }; - }; struct subaction { - unsigned long ack; + uint32_t ack; size_t length; struct list link; struct link_packet packet; -- cgit v1.2.3-18-g5258 From a8461c0f3bfffe752fc62ce3960eb827463e90b5 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:58:05 +0200 Subject: tools/firewire: nosy-dump: work around segfault in decode_fcp If I run "nosy-dump --view=transaction" with my camcorder on battery instead of mains, it segfaults very quickly because of !t->request. Perhaps this is because of increased likelyhood of incomplete transactions (ack_busy when host writes to camcorder's FCP_Request) and a bug deeper in nosy-dump's transaction housekeeping. This is a quick workaround to get me going. Signed-off-by: Stefan Richter --- tools/firewire/nosy-dump.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tools') diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c index 0bad0a42d77..1d4dd5439d4 100644 --- a/tools/firewire/nosy-dump.c +++ b/tools/firewire/nosy-dump.c @@ -230,6 +230,11 @@ handle_transaction(struct link_transaction *t) struct subaction *sa; int i; + if (!t->request) { + printf("BUG in handle_transaction\n"); + return; + } + for (i = 0; i < array_length(protocol_decoders); i++) if (protocol_decoders[i].decode(t)) break; -- cgit v1.2.3-18-g5258 From 92c16f7e9c65f9a271c6bd5020279b3e37989672 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:58:05 +0200 Subject: tools/firewire: nosy-dump: change to kernel coding style This changes only - whitespace - C99 initializers - comment style - order of #includes - if { } else { } bracing Signed-off-by: Stefan Richter --- tools/firewire/decode-fcp.c | 364 +++++----- tools/firewire/list.h | 24 +- tools/firewire/nosy-dump.c | 1663 +++++++++++++++++++++---------------------- tools/firewire/nosy-dump.h | 280 ++++---- 4 files changed, 1139 insertions(+), 1192 deletions(-) (limited to 'tools') diff --git a/tools/firewire/decode-fcp.c b/tools/firewire/decode-fcp.c index b6b37253761..9cd1550db24 100644 --- a/tools/firewire/decode-fcp.c +++ b/tools/firewire/decode-fcp.c @@ -6,234 +6,206 @@ #define CSR_FCP_COMMAND 0xfffff0000b00ull #define CSR_FCP_RESPONSE 0xfffff0000d00ull -static const char * const ctype_names[16] = { - "control", - "status", - "specific inquiry", - "notify", - "general inquiry", - "(reserved 0x05)", - "(reserved 0x06)", - "(reserved 0x07)", - "not implemented", - "accepted", - "rejected", - "in transition", - "stable", - "changed", - "(reserved 0x0e)", - "interim" +static const char * const ctype_names[] = { + [0x0] = "control", [0x8] = "not implemented", + [0x1] = "status", [0x9] = "accepted", + [0x2] = "specific inquiry", [0xa] = "rejected", + [0x3] = "notify", [0xb] = "in transition", + [0x4] = "general inquiry", [0xc] = "stable", + [0x5] = "(reserved 0x05)", [0xd] = "changed", + [0x6] = "(reserved 0x06)", [0xe] = "(reserved 0x0e)", + [0x7] = "(reserved 0x07)", [0xf] = "interim", }; -static const char * const subunit_type_names[32] = { - "monitor", - "audio", - "printer", - "disc", - "tape recorder/player", - "tuner", - "ca", - "camera", - "(reserved 0x08)", - "panel", - "bulletin board", - "camera storage", - "(reserved 0x0c)", - "(reserved 0x0d)", - "(reserved 0x0e)", - "(reserved 0x0f)", - "(reserved 0x10)", - "(reserved 0x11)", - "(reserved 0x12)", - "(reserved 0x13)", - "(reserved 0x14)", - "(reserved 0x15)", - "(reserved 0x16)", - "(reserved 0x17)", - "(reserved 0x18)", - "(reserved 0x19)", - "(reserved 0x1a)", - "(reserved 0x1b)", - "vendor unique", - "all subunit types", - "subunit_type extended to next byte", - "unit" +static const char * const subunit_type_names[] = { + [0x00] = "monitor", [0x10] = "(reserved 0x10)", + [0x01] = "audio", [0x11] = "(reserved 0x11)", + [0x02] = "printer", [0x12] = "(reserved 0x12)", + [0x03] = "disc", [0x13] = "(reserved 0x13)", + [0x04] = "tape recorder/player",[0x14] = "(reserved 0x14)", + [0x05] = "tuner", [0x15] = "(reserved 0x15)", + [0x06] = "ca", [0x16] = "(reserved 0x16)", + [0x07] = "camera", [0x17] = "(reserved 0x17)", + [0x08] = "(reserved 0x08)", [0x18] = "(reserved 0x18)", + [0x09] = "panel", [0x19] = "(reserved 0x19)", + [0x0a] = "bulletin board", [0x1a] = "(reserved 0x1a)", + [0x0b] = "camera storage", [0x1b] = "(reserved 0x1b)", + [0x0c] = "(reserved 0x0c)", [0x1c] = "vendor unique", + [0x0d] = "(reserved 0x0d)", [0x1d] = "all subunit types", + [0x0e] = "(reserved 0x0e)", [0x1e] = "subunit_type extended to next byte", + [0x0f] = "(reserved 0x0f)", [0x1f] = "unit", }; struct avc_enum { - int value; - const char *name; + int value; + const char *name; }; struct avc_field { - const char *name; /* Short name for field. */ - int offset; /* Location of field, specified in bits. - * Negative means from end of packet */ - int width; /* Width of field, 0 means use data_length. */ - struct avc_enum *names; + const char *name; /* Short name for field. */ + int offset; /* Location of field, specified in bits; */ + /* negative means from end of packet. */ + int width; /* Width of field, 0 means use data_length. */ + struct avc_enum *names; }; struct avc_opcode_info { - const char *name; - struct avc_field fields[8]; + const char *name; + struct avc_field fields[8]; }; struct avc_enum power_field_names[] = { - { 0x70, "on" }, - { 0x60, "off" }, - { } + { 0x70, "on" }, + { 0x60, "off" }, + { } }; static const struct avc_opcode_info opcode_info[256] = { - /* TA Document 1999026 - * AV/C Digital Interface Command Set General Specification - * Version 4.0 */ - [0xb2] = - { "power", { - { "state", 0, 8, power_field_names } - } - }, - [0x30] = - { "unit info", { - { "foo", 0, 8 }, - { "unit_type", 8, 5 }, - { "unit", 13, 3 }, - { "company id", 16, 24 }, - } - }, - [0x31] = { "subunit info" }, - [0x01] = { "reserve" }, - [0xb0] = { "version" }, - [0x00] = { "vendor dependent" }, - - [0x02] = { "plug info" }, - [0x12] = { "channel usage" }, - [0x24] = { "connect" }, - [0x20] = { "connect av" }, - [0x22] = { "connections" }, - [0x11] = { "digital input" }, - [0x10] = { "digital output" }, - [0x25] = { "disconnect" }, - [0x21] = { "disconnect av" }, - [0x19] = { "input plug signal format" }, - [0x18] = { "output plug signal format" }, - [0x1f] = { "general bus setup" }, - - /* TA Document 1999025 - * AV/C Descriptor Mechanism Specification Version 1.0 */ - [0x0c] = { "create descriptor" }, - [0x08] = { "open descriptor" }, - [0x09] = { "read descriptor" }, - [0x0a] = { "write descriptor" }, - [0x05] = { "open info block" }, - [0x06] = { "read info block" }, - [0x07] = { "write info block" }, - [0x0b] = { "search descriptor" }, - [0x0d] = { "object number select" }, - - /* TA Document 1999015 - * AV/C Command Set for Rate Control of Isochronous Data Flow 1.0 */ - [0xb3] = { "rate", { - { "subfunction", 0, 8 }, - { "result", 8, 8 }, - { "plug_type", 16, 8 }, - { "plug_id", 16, 8 }, - } - }, - - /* TA Document 1999008 - * AV/C Audio Subunit Specification 1.0 */ - [0xb8] = { "function block" }, - - /* TA Document 2001001 - * AV/C Panel Subunit Specification 1.1 */ - [0x7d] = { "gui update" }, - [0x7e] = { "push gui data" }, - [0x7f] = { "user action" }, - [0x7c] = { "pass through" }, - - /* */ - [0x26] = { "asynchronous connection" }, + /* TA Document 1999026 */ + /* AV/C Digital Interface Command Set General Specification 4.0 */ + [0xb2] = { "power", { + { "state", 0, 8, power_field_names } + } + }, + [0x30] = { "unit info", { + { "foo", 0, 8 }, + { "unit_type", 8, 5 }, + { "unit", 13, 3 }, + { "company id", 16, 24 }, + } + }, + [0x31] = { "subunit info" }, + [0x01] = { "reserve" }, + [0xb0] = { "version" }, + [0x00] = { "vendor dependent" }, + [0x02] = { "plug info" }, + [0x12] = { "channel usage" }, + [0x24] = { "connect" }, + [0x20] = { "connect av" }, + [0x22] = { "connections" }, + [0x11] = { "digital input" }, + [0x10] = { "digital output" }, + [0x25] = { "disconnect" }, + [0x21] = { "disconnect av" }, + [0x19] = { "input plug signal format" }, + [0x18] = { "output plug signal format" }, + [0x1f] = { "general bus setup" }, + + /* TA Document 1999025 */ + /* AV/C Descriptor Mechanism Specification Version 1.0 */ + [0x0c] = { "create descriptor" }, + [0x08] = { "open descriptor" }, + [0x09] = { "read descriptor" }, + [0x0a] = { "write descriptor" }, + [0x05] = { "open info block" }, + [0x06] = { "read info block" }, + [0x07] = { "write info block" }, + [0x0b] = { "search descriptor" }, + [0x0d] = { "object number select" }, + + /* TA Document 1999015 */ + /* AV/C Command Set for Rate Control of Isochronous Data Flow 1.0 */ + [0xb3] = { "rate", { + { "subfunction", 0, 8 }, + { "result", 8, 8 }, + { "plug_type", 16, 8 }, + { "plug_id", 16, 8 }, + } + }, + + /* TA Document 1999008 */ + /* AV/C Audio Subunit Specification 1.0 */ + [0xb8] = { "function block" }, + + /* TA Document 2001001 */ + /* AV/C Panel Subunit Specification 1.1 */ + [0x7d] = { "gui update" }, + [0x7e] = { "push gui data" }, + [0x7f] = { "user action" }, + [0x7c] = { "pass through" }, + + /* */ + [0x26] = { "asynchronous connection" }, }; struct avc_frame { - uint32_t operand0:8; - uint32_t opcode:8; - uint32_t subunit_id:3; - uint32_t subunit_type:5; - uint32_t ctype:4; - uint32_t cts:4; + uint32_t operand0:8; + uint32_t opcode:8; + uint32_t subunit_id:3; + uint32_t subunit_type:5; + uint32_t ctype:4; + uint32_t cts:4; }; static void decode_avc(struct link_transaction *t) { - struct avc_frame *frame = (struct avc_frame *) t->request->packet.write_block.data; - const struct avc_opcode_info *info; - const char *name; - char buffer[32]; - int i; - - info = &opcode_info[frame->opcode]; - if (info->name == NULL) { - snprintf(buffer, sizeof buffer, "(unknown opcode 0x%02x)", frame->opcode); - name = buffer; - } else { - name = info->name; - } - - printf("av/c %s, subunit_type=%s, subunit_id=%d, opcode=%s", - ctype_names[frame->ctype], subunit_type_names[frame->subunit_type], - frame->subunit_id, name); - - for (i = 0; info->fields[i].name != NULL; i++) { - printf(", %s", info->fields[i].name); - } - - printf("\n"); -} + struct avc_frame *frame = + (struct avc_frame *) t->request->packet.write_block.data; + const struct avc_opcode_info *info; + const char *name; + char buffer[32]; + int i; + + info = &opcode_info[frame->opcode]; + if (info->name == NULL) { + snprintf(buffer, sizeof(buffer), + "(unknown opcode 0x%02x)", frame->opcode); + name = buffer; + } else { + name = info->name; + } + printf("av/c %s, subunit_type=%s, subunit_id=%d, opcode=%s", + ctype_names[frame->ctype], subunit_type_names[frame->subunit_type], + frame->subunit_id, name); + + for (i = 0; info->fields[i].name != NULL; i++) + printf(", %s", info->fields[i].name); + + printf("\n"); +} int decode_fcp(struct link_transaction *t) { - struct avc_frame *frame = (struct avc_frame *) t->request->packet.write_block.data; - unsigned long long offset; - - offset = ((unsigned long long) t->request->packet.common.offset_high << 32) | - t->request->packet.common.offset_low; - - if (t->request->packet.common.tcode != TCODE_WRITE_BLOCK) - return 0; - - if (offset == CSR_FCP_COMMAND || offset == CSR_FCP_RESPONSE) { - switch (frame->cts) { - case 0x00: - decode_avc(t); - break; - case 0x01: - printf("cal fcp frame (cts=0x01)\n"); - break; - case 0x02: - printf("ehs fcp frame (cts=0x02)\n"); - break; - case 0x03: - printf("havi fcp frame (cts=0x03)\n"); - break; - case 0x0e: - printf("vendor specific fcp frame (cts=0x0e)\n"); - break; - case 0x0f: - printf("extended cts\n"); - break; - default: - printf("reserved fcp frame (ctx=0x%02x)\n", frame->cts); - break; + struct avc_frame *frame = + (struct avc_frame *) t->request->packet.write_block.data; + unsigned long long offset = + ((unsigned long long) t->request->packet.common.offset_high << 32) | + t->request->packet.common.offset_low; + + if (t->request->packet.common.tcode != TCODE_WRITE_BLOCK) + return 0; + + if (offset == CSR_FCP_COMMAND || offset == CSR_FCP_RESPONSE) { + switch (frame->cts) { + case 0x00: + decode_avc(t); + break; + case 0x01: + printf("cal fcp frame (cts=0x01)\n"); + break; + case 0x02: + printf("ehs fcp frame (cts=0x02)\n"); + break; + case 0x03: + printf("havi fcp frame (cts=0x03)\n"); + break; + case 0x0e: + printf("vendor specific fcp frame (cts=0x0e)\n"); + break; + case 0x0f: + printf("extended cts\n"); + break; + default: + printf("reserved fcp frame (ctx=0x%02x)\n", frame->cts); + break; + } + return 1; } - return 1; - } - return 0; + return 0; } diff --git a/tools/firewire/list.h b/tools/firewire/list.h index fa119dd371f..41f4bdadf63 100644 --- a/tools/firewire/list.h +++ b/tools/firewire/list.h @@ -1,46 +1,46 @@ struct list { - struct list *next, *prev; + struct list *next, *prev; }; static inline void list_init(struct list *list) { - list->next = list; - list->prev = list; + list->next = list; + list->prev = list; } static inline int list_empty(struct list *list) { - return list->next == list; + return list->next == list; } static inline void list_insert(struct list *link, struct list *new_link) { - new_link->prev = link->prev; - new_link->next = link; - new_link->prev->next = new_link; - new_link->next->prev = new_link; + new_link->prev = link->prev; + new_link->next = link; + new_link->prev->next = new_link; + new_link->next->prev = new_link; } static inline void list_append(struct list *list, struct list *new_link) { - list_insert((struct list *)list, new_link); + list_insert((struct list *)list, new_link); } static inline void list_prepend(struct list *list, struct list *new_link) { - list_insert(list->next, new_link); + list_insert(list->next, new_link); } static inline void list_remove(struct list *link) { - link->prev->next = link->next; - link->next->prev = link->prev; + link->prev->next = link->next; + link->next->prev = link->prev; } #define list_entry(link, type, member) \ diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c index 1d4dd5439d4..3d7e1a6dfe9 100644 --- a/tools/firewire/nosy-dump.c +++ b/tools/firewire/nosy-dump.c @@ -1,5 +1,3 @@ -/* -*- mode: c; c-basic-offset: 2 -*- */ - /* * nosy-dump - Interface to snoop mode driver for TI PCILynx 1394 controllers * Copyright (C) 2002-2006 Kristian Høgsberg @@ -19,642 +17,628 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include +#include +#include +#include +#include +#include +#include #include +#include #include -#include -#include #include #include -#include -#include -#include -#include #include - -#include +#include #include "list.h" -#include "nosy-user.h" #include "nosy-dump.h" +#include "nosy-user.h" enum { - PACKET_FIELD_DETAIL = 0x01, - PACKET_FIELD_DATA_LENGTH = 0x02, - /* Marks the fields we print in transaction view. */ - PACKET_FIELD_TRANSACTION = 0x04 + PACKET_FIELD_DETAIL = 0x01, + PACKET_FIELD_DATA_LENGTH = 0x02, + /* Marks the fields we print in transaction view. */ + PACKET_FIELD_TRANSACTION = 0x04, }; -static void -print_packet(uint32_t *data, size_t length); -static void -decode_link_packet(struct link_packet *packet, size_t length, - int include_flags, int exclude_flags); - -static int run = 1; +static void print_packet(uint32_t *data, size_t length); +static void decode_link_packet(struct link_packet *packet, size_t length, + int include_flags, int exclude_flags); +static int run = 1; sig_t sys_sigint_handler; static char *option_nosy_device = "/dev/nosy"; static char *option_view = "packet"; -static char *option_output = NULL; -static char *option_input = NULL; -static int option_hex; -static int option_iso; -static int option_cycle_start; -static int option_version; -static int option_verbose; +static char *option_output; +static char *option_input; +static int option_hex; +static int option_iso; +static int option_cycle_start; +static int option_version; +static int option_verbose; enum { - VIEW_TRANSACTION, - VIEW_PACKET, - VIEW_STATS + VIEW_TRANSACTION, + VIEW_PACKET, + VIEW_STATS, }; static const struct poptOption options[] = { - { - longName: "device", - shortName: 'd', - argInfo: POPT_ARG_STRING, - arg: &option_nosy_device, - descrip: "Path to nosy device.", - argDescrip: "DEVICE" - }, - { - longName: "view", - argInfo: POPT_ARG_STRING, - arg: &option_view, - descrip: "Specify view of bus traffic: packet, transaction or stats.", - argDescrip: "VIEW" - }, - { - longName: "hex", - shortName: 'x', - argInfo: POPT_ARG_NONE, - arg: &option_hex, - descrip: "Print each packet in hex.", - }, - { - longName: "iso", - argInfo: POPT_ARG_NONE, - arg: &option_iso, - descrip: "Print iso packets.", - }, - { - longName: "cycle-start", - argInfo: POPT_ARG_NONE, - arg: &option_cycle_start, - descrip: "Print cycle start packets.", - }, - { - longName: "verbose", - shortName: 'v', - argInfo: POPT_ARG_NONE, - arg: &option_verbose, - descrip: "Verbose packet view.", - }, - { - longName: "output", - shortName: 'o', - argInfo: POPT_ARG_STRING, - arg: &option_output, - descrip: "Log to output file.", - argDescrip: "FILENAME" - }, - { - longName: "input", - shortName: 'i', - argInfo: POPT_ARG_STRING, - arg: &option_input, - descrip: "Decode log from file.", - argDescrip: "FILENAME" - }, - { - longName: "version", - argInfo: POPT_ARG_NONE, - arg: &option_version, - descrip: "Specify print version info.", - }, - POPT_AUTOHELP - POPT_TABLEEND + { + .longName = "device", + .shortName = 'd', + .argInfo = POPT_ARG_STRING, + .arg = &option_nosy_device, + .descrip = "Path to nosy device.", + .argDescrip = "DEVICE" + }, + { + .longName = "view", + .argInfo = POPT_ARG_STRING, + .arg = &option_view, + .descrip = "Specify view of bus traffic: packet, transaction or stats.", + .argDescrip = "VIEW" + }, + { + .longName = "hex", + .shortName = 'x', + .argInfo = POPT_ARG_NONE, + .arg = &option_hex, + .descrip = "Print each packet in hex.", + }, + { + .longName = "iso", + .argInfo = POPT_ARG_NONE, + .arg = &option_iso, + .descrip = "Print iso packets.", + }, + { + .longName = "cycle-start", + .argInfo = POPT_ARG_NONE, + .arg = &option_cycle_start, + .descrip = "Print cycle start packets.", + }, + { + .longName = "verbose", + .shortName = 'v', + .argInfo = POPT_ARG_NONE, + .arg = &option_verbose, + .descrip = "Verbose packet view.", + }, + { + .longName = "output", + .shortName = 'o', + .argInfo = POPT_ARG_STRING, + .arg = &option_output, + .descrip = "Log to output file.", + .argDescrip = "FILENAME" + }, + { + .longName = "input", + .shortName = 'i', + .argInfo = POPT_ARG_STRING, + .arg = &option_input, + .descrip = "Decode log from file.", + .argDescrip = "FILENAME" + }, + { + .longName = "version", + .argInfo = POPT_ARG_NONE, + .arg = &option_version, + .descrip = "Specify print version info.", + }, + POPT_AUTOHELP + POPT_TABLEEND }; -void +/* Allow all ^C except the first to interrupt the program in the usual way. */ +void sigint_handler(int signal_num) { - if (run == 1) { - run = 0; - /* Allow all Ctrl-C's except the first to interrupt the program in - * the usual way. - */ - signal(SIGINT, SIG_DFL); - } + if (run == 1) { + run = 0; + signal(SIGINT, SIG_DFL); + } } struct subaction * subaction_create(uint32_t *data, size_t length) { - struct subaction *sa; + struct subaction *sa; - /* we put the ack in the subaction struct for easy access. */ - sa = malloc(sizeof *sa - sizeof sa->packet + length); - sa->ack = data[length / 4 - 1]; - sa->length = length; - memcpy (&sa->packet, data, length); + /* we put the ack in the subaction struct for easy access. */ + sa = malloc(sizeof *sa - sizeof sa->packet + length); + sa->ack = data[length / 4 - 1]; + sa->length = length; + memcpy(&sa->packet, data, length); - return sa; + return sa; } void subaction_destroy(struct subaction *sa) { - free(sa); + free(sa); } -struct list pending_transaction_list = - { &pending_transaction_list, &pending_transaction_list }; +struct list pending_transaction_list = { + &pending_transaction_list, &pending_transaction_list +}; struct link_transaction * link_transaction_lookup(int request_node, int response_node, int tlabel) { - struct link_transaction *t; - - list_for_each_entry(t, &pending_transaction_list, link) { - if (t->request_node == request_node && - t->response_node == response_node && - t->tlabel == tlabel) - return t; - } - - t = malloc(sizeof *t); - t->request_node = request_node; - t->response_node = response_node; - t->tlabel = tlabel; - list_init(&t->request_list); - list_init(&t->response_list); - - list_append(&pending_transaction_list, &t->link); - - return t; + struct link_transaction *t; + + list_for_each_entry(t, &pending_transaction_list, link) { + if (t->request_node == request_node && + t->response_node == response_node && + t->tlabel == tlabel) + return t; + } + + t = malloc(sizeof *t); + t->request_node = request_node; + t->response_node = response_node; + t->tlabel = tlabel; + list_init(&t->request_list); + list_init(&t->response_list); + + list_append(&pending_transaction_list, &t->link); + + return t; } void link_transaction_destroy(struct link_transaction *t) { - while (!list_empty(&t->request_list)) { - struct subaction *sa = list_head(&t->request_list, struct subaction, link); - list_remove(&sa->link); - subaction_destroy(sa); - } - - while (!list_empty(&t->response_list)) { - struct subaction *sa = list_head(&t->response_list, struct subaction, link); - list_remove(&sa->link); - subaction_destroy(sa); - } - - free(t); + struct subaction *sa; + + while (!list_empty(&t->request_list)) { + sa = list_head(&t->request_list, struct subaction, link); + list_remove(&sa->link); + subaction_destroy(sa); + } + while (!list_empty(&t->response_list)) { + sa = list_head(&t->response_list, struct subaction, link); + list_remove(&sa->link); + subaction_destroy(sa); + } + free(t); } struct protocol_decoder { - const char *name; - int (*decode)(struct link_transaction *t); + const char *name; + int (*decode)(struct link_transaction *t); }; static struct protocol_decoder protocol_decoders[] = { - { "FCP", decode_fcp } + { "FCP", decode_fcp } }; void handle_transaction(struct link_transaction *t) { - struct subaction *sa; - int i; - - if (!t->request) { - printf("BUG in handle_transaction\n"); - return; - } - - for (i = 0; i < array_length(protocol_decoders); i++) - if (protocol_decoders[i].decode(t)) - break; - - /* HACK: decode only fcp right now. */ - return; - - decode_link_packet(&t->request->packet, t->request->length, - PACKET_FIELD_TRANSACTION, 0); - if (t->response) - decode_link_packet(&t->response->packet, t->request->length, - PACKET_FIELD_TRANSACTION, 0); - else - printf("[no response]"); - - if (option_verbose) { - list_for_each_entry(sa, &t->request_list, link) - print_packet((uint32_t *) &sa->packet, sa->length); - list_for_each_entry(sa, &t->response_list, link) - print_packet((uint32_t *) &sa->packet, sa->length); - } - printf("\r\n"); - - link_transaction_destroy(t); + struct subaction *sa; + int i; + + if (!t->request) { + printf("BUG in handle_transaction\n"); + return; + } + + for (i = 0; i < array_length(protocol_decoders); i++) + if (protocol_decoders[i].decode(t)) + break; + + /* HACK: decode only fcp right now. */ + return; + + decode_link_packet(&t->request->packet, t->request->length, + PACKET_FIELD_TRANSACTION, 0); + if (t->response) + decode_link_packet(&t->response->packet, t->request->length, + PACKET_FIELD_TRANSACTION, 0); + else + printf("[no response]"); + + if (option_verbose) { + list_for_each_entry(sa, &t->request_list, link) + print_packet((uint32_t *) &sa->packet, sa->length); + list_for_each_entry(sa, &t->response_list, link) + print_packet((uint32_t *) &sa->packet, sa->length); + } + printf("\r\n"); + + link_transaction_destroy(t); } void clear_pending_transaction_list(void) { - struct link_transaction *t; - - while (!list_empty(&pending_transaction_list)) { - t = list_head(&pending_transaction_list, struct link_transaction, link); - list_remove(&t->link); - link_transaction_destroy(t); - /* print unfinished transactions */ - } + struct link_transaction *t; + + while (!list_empty(&pending_transaction_list)) { + t = list_head(&pending_transaction_list, + struct link_transaction, link); + list_remove(&t->link); + link_transaction_destroy(t); + /* print unfinished transactions */ + } } static const char * const tcode_names[] = { - "write_quadlet_request", - "write_block_request", - "write_response", - "reserved", - "read_quadlet_request", - "read_block_request", - "read_quadlet_response", - "read_block_response", - "cycle_start", - "lock_request", - "iso_data", - "lock_response" + [0x0] = "write_quadlet_request", [0x6] = "read_quadlet_response", + [0x1] = "write_block_request", [0x7] = "read_block_response", + [0x2] = "write_response", [0x8] = "cycle_start", + [0x3] = "reserved", [0x9] = "lock_request", + [0x4] = "read_quadlet_request", [0xa] = "iso_data", + [0x5] = "read_block_request", [0xb] = "lock_response", }; static const char * const ack_names[] = { - "no ack", - "ack_complete", - "ack_pending", - "reserved (0x03)", - "ack_busy_x", - "ack_busy_a", - "ack_busy_b", - "reserved (0x07)", - "reserved (0x08)", - "reserved (0x09)", - "reserved (0x0a)", - "reserved (0x0b)", - "reserved (0x0c)", - "ack_data_error", - "ack_type_error", - "reserved (0x0f)", + [0x0] = "no ack", [0x8] = "reserved (0x08)", + [0x1] = "ack_complete", [0x9] = "reserved (0x09)", + [0x2] = "ack_pending", [0xa] = "reserved (0x0a)", + [0x3] = "reserved (0x03)", [0xb] = "reserved (0x0b)", + [0x4] = "ack_busy_x", [0xc] = "reserved (0x0c)", + [0x5] = "ack_busy_a", [0xd] = "ack_data_error", + [0x6] = "ack_busy_b", [0xe] = "ack_type_error", + [0x7] = "reserved (0x07)", [0xf] = "reserved (0x0f)", }; static const char * const rcode_names[] = { - "complete", - "reserved (0x01)", - "reserved (0x02)", - "reserved (0x03)", - "conflict_error", - "data_error", - "type_error", - "address_error", + [0x0] = "complete", [0x4] = "conflict_error", + [0x1] = "reserved (0x01)", [0x5] = "data_error", + [0x2] = "reserved (0x02)", [0x6] = "type_error", + [0x3] = "reserved (0x03)", [0x7] = "address_error", }; static const char * const retry_names[] = { - "retry_1", - "retry_x", - "retry_a", - "retry_b", + [0x0] = "retry_1", + [0x1] = "retry_x", + [0x2] = "retry_a", + [0x3] = "retry_b", }; enum { - PACKET_RESERVED, - PACKET_REQUEST, - PACKET_RESPONSE, - PACKET_OTHER, + PACKET_RESERVED, + PACKET_REQUEST, + PACKET_RESPONSE, + PACKET_OTHER, }; struct packet_info { - const char *name; - int type; - int response_tcode; - struct packet_field *fields; - int field_count; + const char *name; + int type; + int response_tcode; + struct packet_field *fields; + int field_count; }; struct packet_field { - const char *name; /* Short name for field. */ - int offset; /* Location of field, specified in bits. - * Negative means from end of packet */ - int width; /* Width of field, 0 means use data_length. */ - int flags; /* Show options. */ - const char * const *value_names; + const char *name; /* Short name for field. */ + int offset; /* Location of field, specified in bits; */ + /* negative means from end of packet. */ + int width; /* Width of field, 0 means use data_length. */ + int flags; /* Show options. */ + const char * const *value_names; }; -#define COMMON_REQUEST_FIELDS \ - { "dest", 0, 16, PACKET_FIELD_TRANSACTION }, \ - { "tl", 16, 6 }, \ - { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \ - { "tcode", 24, 4, PACKET_FIELD_TRANSACTION, tcode_names }, \ - { "pri", 28, 4, PACKET_FIELD_DETAIL }, \ - { "src", 32, 16, PACKET_FIELD_TRANSACTION }, \ - { "offs", 48, 48, PACKET_FIELD_TRANSACTION } - -#define COMMON_RESPONSE_FIELDS \ - { "dest", 0, 16 }, \ - { "tl", 16, 6 }, \ - { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \ - { "tcode", 24, 4, 0, tcode_names }, \ - { "pri", 28, 4, PACKET_FIELD_DETAIL }, \ - { "src", 32, 16 }, \ - { "rcode", 48, 4, PACKET_FIELD_TRANSACTION, rcode_names } +#define COMMON_REQUEST_FIELDS \ + { "dest", 0, 16, PACKET_FIELD_TRANSACTION }, \ + { "tl", 16, 6 }, \ + { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \ + { "tcode", 24, 4, PACKET_FIELD_TRANSACTION, tcode_names }, \ + { "pri", 28, 4, PACKET_FIELD_DETAIL }, \ + { "src", 32, 16, PACKET_FIELD_TRANSACTION }, \ + { "offs", 48, 48, PACKET_FIELD_TRANSACTION } + +#define COMMON_RESPONSE_FIELDS \ + { "dest", 0, 16 }, \ + { "tl", 16, 6 }, \ + { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \ + { "tcode", 24, 4, 0, tcode_names }, \ + { "pri", 28, 4, PACKET_FIELD_DETAIL }, \ + { "src", 32, 16 }, \ + { "rcode", 48, 4, PACKET_FIELD_TRANSACTION, rcode_names } struct packet_field read_quadlet_request_fields[] = { - COMMON_REQUEST_FIELDS, - { "crc", 96, 32, PACKET_FIELD_DETAIL }, - { "ack", 156, 4, 0, ack_names } + COMMON_REQUEST_FIELDS, + { "crc", 96, 32, PACKET_FIELD_DETAIL }, + { "ack", 156, 4, 0, ack_names }, }; struct packet_field read_quadlet_response_fields[] = { - COMMON_RESPONSE_FIELDS, - { "data", 96, 32, PACKET_FIELD_TRANSACTION }, - { "crc", 128, 32, PACKET_FIELD_DETAIL }, - { "ack", 188, 4, 0, ack_names } + COMMON_RESPONSE_FIELDS, + { "data", 96, 32, PACKET_FIELD_TRANSACTION }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "ack", 188, 4, 0, ack_names }, }; struct packet_field read_block_request_fields[] = { - COMMON_REQUEST_FIELDS, - { "data_length", 96, 16, PACKET_FIELD_TRANSACTION }, - { "extended_tcode", 112, 16 }, - { "crc", 128, 32, PACKET_FIELD_DETAIL }, - { "ack", 188, 4, 0, ack_names }, + COMMON_REQUEST_FIELDS, + { "data_length", 96, 16, PACKET_FIELD_TRANSACTION }, + { "extended_tcode", 112, 16 }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "ack", 188, 4, 0, ack_names }, }; struct packet_field block_response_fields[] = { - COMMON_RESPONSE_FIELDS, - { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH }, - { "extended_tcode", 112, 16 }, - { "crc", 128, 32, PACKET_FIELD_DETAIL }, - { "data", 160, 0, PACKET_FIELD_TRANSACTION }, - { "crc", -64, 32, PACKET_FIELD_DETAIL }, - { "ack", -4, 4, 0, ack_names } + COMMON_RESPONSE_FIELDS, + { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH }, + { "extended_tcode", 112, 16 }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "data", 160, 0, PACKET_FIELD_TRANSACTION }, + { "crc", -64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names }, }; struct packet_field write_quadlet_request_fields[] = { - COMMON_REQUEST_FIELDS, - { "data", 96, 32, PACKET_FIELD_TRANSACTION }, - { "ack", -4, 4, 0, ack_names } + COMMON_REQUEST_FIELDS, + { "data", 96, 32, PACKET_FIELD_TRANSACTION }, + { "ack", -4, 4, 0, ack_names }, }; struct packet_field block_request_fields[] = { - COMMON_REQUEST_FIELDS, - { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH | PACKET_FIELD_TRANSACTION }, - { "extended_tcode", 112, 16, PACKET_FIELD_TRANSACTION }, - { "crc", 128, 32, PACKET_FIELD_DETAIL }, - { "data", 160, 0, PACKET_FIELD_TRANSACTION }, - { "crc", -64, 32, PACKET_FIELD_DETAIL }, - { "ack", -4, 4, 0, ack_names } + COMMON_REQUEST_FIELDS, + { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH | PACKET_FIELD_TRANSACTION }, + { "extended_tcode", 112, 16, PACKET_FIELD_TRANSACTION }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "data", 160, 0, PACKET_FIELD_TRANSACTION }, + { "crc", -64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names }, }; struct packet_field write_response_fields[] = { - COMMON_RESPONSE_FIELDS, - { "reserved", 64, 32, PACKET_FIELD_DETAIL }, - { "ack", -4, 4, 0, ack_names } + COMMON_RESPONSE_FIELDS, + { "reserved", 64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names }, }; struct packet_field iso_data_fields[] = { - { "data_length", 0, 16, PACKET_FIELD_DATA_LENGTH }, - { "tag", 16, 2 }, - { "channel", 18, 6 }, - { "tcode", 24, 4, 0, tcode_names }, - { "sy", 28, 4 }, - { "crc", 32, 32, PACKET_FIELD_DETAIL }, - { "data", 64, 0 }, - { "crc", -64, 32, PACKET_FIELD_DETAIL }, - { "ack", -4, 4, 0, ack_names } + { "data_length", 0, 16, PACKET_FIELD_DATA_LENGTH }, + { "tag", 16, 2 }, + { "channel", 18, 6 }, + { "tcode", 24, 4, 0, tcode_names }, + { "sy", 28, 4 }, + { "crc", 32, 32, PACKET_FIELD_DETAIL }, + { "data", 64, 0 }, + { "crc", -64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names }, }; static struct packet_info packet_info[] = { - { - .name = "write_quadlet_request", - .type = PACKET_REQUEST, - .response_tcode = TCODE_WRITE_RESPONSE, - .fields = write_quadlet_request_fields, - .field_count = array_length(write_quadlet_request_fields) - }, - { - .name = "write_block_request", - .type = PACKET_REQUEST, - .response_tcode = TCODE_WRITE_RESPONSE, - .fields = block_request_fields, - .field_count = array_length(block_request_fields) - }, - { - .name = "write_response", - .type = PACKET_RESPONSE, - .fields = write_response_fields, - .field_count = array_length(write_response_fields) - }, - { - .name = "reserved", - .type = PACKET_RESERVED, - }, - { - .name = "read_quadlet_request", - .type = PACKET_REQUEST, - .response_tcode = TCODE_READ_QUADLET_RESPONSE, - .fields = read_quadlet_request_fields, - .field_count = array_length(read_quadlet_request_fields) - }, - { - .name = "read_block_request", - .type = PACKET_REQUEST, - .response_tcode = TCODE_READ_BLOCK_RESPONSE, - .fields = read_block_request_fields, - .field_count = array_length(read_block_request_fields) - }, - { - .name = "read_quadlet_response", - .type = PACKET_RESPONSE, - .fields = read_quadlet_response_fields, - .field_count = array_length(read_quadlet_response_fields) - }, - { - .name = "read_block_response", - .type = PACKET_RESPONSE, - .fields = block_response_fields, - .field_count = array_length(block_response_fields) - }, - { - .name = "cycle_start", - .type = PACKET_OTHER, - .fields = write_quadlet_request_fields, - .field_count = array_length(write_quadlet_request_fields) - }, - { - .name = "lock_request", - .type = PACKET_REQUEST, - .fields = block_request_fields, - .field_count = array_length(block_request_fields) - }, - { - .name = "iso_data", - .type = PACKET_OTHER, - .fields = iso_data_fields, - .field_count = array_length(iso_data_fields) - }, - { - .name = "lock_response", - .type = PACKET_RESPONSE, - .fields = block_response_fields, - .field_count = array_length(block_response_fields) - } + { + .name = "write_quadlet_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_WRITE_RESPONSE, + .fields = write_quadlet_request_fields, + .field_count = array_length(write_quadlet_request_fields) + }, + { + .name = "write_block_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_WRITE_RESPONSE, + .fields = block_request_fields, + .field_count = array_length(block_request_fields) + }, + { + .name = "write_response", + .type = PACKET_RESPONSE, + .fields = write_response_fields, + .field_count = array_length(write_response_fields) + }, + { + .name = "reserved", + .type = PACKET_RESERVED, + }, + { + .name = "read_quadlet_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_READ_QUADLET_RESPONSE, + .fields = read_quadlet_request_fields, + .field_count = array_length(read_quadlet_request_fields) + }, + { + .name = "read_block_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_READ_BLOCK_RESPONSE, + .fields = read_block_request_fields, + .field_count = array_length(read_block_request_fields) + }, + { + .name = "read_quadlet_response", + .type = PACKET_RESPONSE, + .fields = read_quadlet_response_fields, + .field_count = array_length(read_quadlet_response_fields) + }, + { + .name = "read_block_response", + .type = PACKET_RESPONSE, + .fields = block_response_fields, + .field_count = array_length(block_response_fields) + }, + { + .name = "cycle_start", + .type = PACKET_OTHER, + .fields = write_quadlet_request_fields, + .field_count = array_length(write_quadlet_request_fields) + }, + { + .name = "lock_request", + .type = PACKET_REQUEST, + .fields = block_request_fields, + .field_count = array_length(block_request_fields) + }, + { + .name = "iso_data", + .type = PACKET_OTHER, + .fields = iso_data_fields, + .field_count = array_length(iso_data_fields) + }, + { + .name = "lock_response", + .type = PACKET_RESPONSE, + .fields = block_response_fields, + .field_count = array_length(block_response_fields) + }, }; int handle_packet(uint32_t *data, size_t length) { - if (length == 0) { - printf("bus reset\r\n"); - clear_pending_transaction_list(); - } - else if (length > sizeof(struct phy_packet)) { - struct link_packet *p = (struct link_packet *) data; - struct subaction *sa, *prev; - struct link_transaction *t; - - switch (packet_info[p->common.tcode].type) { - case PACKET_REQUEST: - t = link_transaction_lookup(p->common.source, p->common.destination, - p->common.tlabel); - sa = subaction_create(data, length); - t->request = sa; - - if (!list_empty(&t->request_list)) { - prev = list_tail(&t->request_list, struct subaction, link); - - if (!ACK_BUSY(prev->ack)) { - /* error, we should only see ack_busy_* before the - * ack_pending/ack_complete -- this is an ack_pending - * instead (ack_complete would have finished the - * transaction). */ + if (length == 0) { + printf("bus reset\r\n"); + clear_pending_transaction_list(); + } else if (length > sizeof(struct phy_packet)) { + struct link_packet *p = (struct link_packet *) data; + struct subaction *sa, *prev; + struct link_transaction *t; + + switch (packet_info[p->common.tcode].type) { + case PACKET_REQUEST: + t = link_transaction_lookup(p->common.source, p->common.destination, + p->common.tlabel); + sa = subaction_create(data, length); + t->request = sa; + + if (!list_empty(&t->request_list)) { + prev = list_tail(&t->request_list, + struct subaction, link); + + if (!ACK_BUSY(prev->ack)) { + /* + * error, we should only see ack_busy_* before the + * ack_pending/ack_complete -- this is an ack_pending + * instead (ack_complete would have finished the + * transaction). + */ + } + + if (prev->packet.common.tcode != sa->packet.common.tcode || + prev->packet.common.tlabel != sa->packet.common.tlabel) { + /* memcmp() ? */ + /* error, these should match for retries. */ + } + } + + list_append(&t->request_list, &sa->link); + + switch (sa->ack) { + case ACK_COMPLETE: + if (p->common.tcode != TCODE_WRITE_QUADLET && + p->common.tcode != TCODE_WRITE_BLOCK) + /* error, unified transactions only allowed for write */; + list_remove(&t->link); + handle_transaction(t); + break; + + case ACK_NO_ACK: + case ACK_DATA_ERROR: + case ACK_TYPE_ERROR: + list_remove(&t->link); + handle_transaction(t); + break; + + case ACK_PENDING: + /* request subaction phase over, wait for response. */ + break; + + case ACK_BUSY_X: + case ACK_BUSY_A: + case ACK_BUSY_B: + /* ok, wait for retry. */ + /* check that retry protocol is respected. */ + break; + } + break; + + case PACKET_RESPONSE: + t = link_transaction_lookup(p->common.destination, p->common.source, + p->common.tlabel); + if (list_empty(&t->request_list)) { + /* unsolicited response */ + } + + sa = subaction_create(data, length); + t->response = sa; + + if (!list_empty(&t->response_list)) { + prev = list_tail(&t->response_list, struct subaction, link); + + if (!ACK_BUSY(prev->ack)) { + /* + * error, we should only see ack_busy_* before the + * ack_pending/ack_complete + */ + } + + if (prev->packet.common.tcode != sa->packet.common.tcode || + prev->packet.common.tlabel != sa->packet.common.tlabel) { + /* use memcmp() instead? */ + /* error, these should match for retries. */ + } + } else { + prev = list_tail(&t->request_list, struct subaction, link); + if (prev->ack != ACK_PENDING) { + /* + * error, should not get response unless last request got + * ack_pending. + */ + } + + if (packet_info[prev->packet.common.tcode].response_tcode != + sa->packet.common.tcode) { + /* error, tcode mismatch */ + } + } + + list_append(&t->response_list, &sa->link); + + switch (sa->ack) { + case ACK_COMPLETE: + case ACK_NO_ACK: + case ACK_DATA_ERROR: + case ACK_TYPE_ERROR: + list_remove(&t->link); + handle_transaction(t); + /* transaction complete, remove t from pending list. */ + break; + + case ACK_PENDING: + /* error for responses. */ + break; + + case ACK_BUSY_X: + case ACK_BUSY_A: + case ACK_BUSY_B: + /* no problem, wait for next retry */ + break; + } + + break; + + case PACKET_OTHER: + case PACKET_RESERVED: + return 0; + } } - - if (prev->packet.common.tcode != sa->packet.common.tcode || - prev->packet.common.tlabel != sa->packet.common.tlabel) - /* memcmp() ? */ - /* error, these should match for retries. */; - } - - list_append(&t->request_list, &sa->link); - - switch (sa->ack) { - case ACK_COMPLETE: - if (p->common.tcode != TCODE_WRITE_QUADLET && - p->common.tcode != TCODE_WRITE_BLOCK) - /* error, unified transactions only allowed for write */; - list_remove(&t->link); - handle_transaction(t); - break; - - case ACK_NO_ACK: - case ACK_DATA_ERROR: - case ACK_TYPE_ERROR: - list_remove(&t->link); - handle_transaction(t); - break; - - case ACK_PENDING: - /* request subaction phase over, wait for response. */ - break; - - case ACK_BUSY_X: - case ACK_BUSY_A: - case ACK_BUSY_B: - /* ok, wait for retry. */ - /* check that retry protocol is respected. */ - break; - } - break; - - case PACKET_RESPONSE: - t = link_transaction_lookup(p->common.destination, p->common.source, - p->common.tlabel); - if (list_empty(&t->request_list)) { - /* unsolicited response */ - } - - sa = subaction_create(data, length); - t->response = sa; - - if (!list_empty(&t->response_list)) { - prev = list_tail(&t->response_list, struct subaction, link); - - if (!ACK_BUSY(prev->ack)) - /* error, we should only see ack_busy_* before the - * ack_pending/ack_complete */; - - if (prev->packet.common.tcode != sa->packet.common.tcode || - prev->packet.common.tlabel != sa->packet.common.tlabel) - /* use memcmp() instead? */ - /* error, these should match for retries. */; - } - else { - prev = list_tail(&t->request_list, struct subaction, link); - if (prev->ack != ACK_PENDING) { - /* error, should not get response unless last request got - * ack_pending. */ - } - - if (packet_info[prev->packet.common.tcode].response_tcode != - sa->packet.common.tcode) { - /* error, tcode mismatch */ - } - } - - list_append(&t->response_list, &sa->link); - - switch (sa->ack) { - case ACK_COMPLETE: - case ACK_NO_ACK: - case ACK_DATA_ERROR: - case ACK_TYPE_ERROR: - list_remove(&t->link); - handle_transaction(t); - /* transaction complete, remove t from pending list. */ - break; - - case ACK_PENDING: - /* error for responses. */ - break; - - case ACK_BUSY_X: - case ACK_BUSY_A: - case ACK_BUSY_B: - /* no problem, wait for next retry */ - break; - } - - break; - - case PACKET_OTHER: - case PACKET_RESERVED: - return 0; - } - } - - return 1; + + return 1; } unsigned int get_bits(struct link_packet *packet, int offset, int width) { - uint32_t *data = (uint32_t *) packet; - uint32_t index, shift, mask; + uint32_t *data = (uint32_t *) packet; + uint32_t index, shift, mask; - index = offset / 32 + 1; - shift = 32 - (offset & 31) - width; - mask = width == 32 ? ~0 : (1 << width) - 1; + index = offset / 32 + 1; + shift = 32 - (offset & 31) - width; + mask = width == 32 ? ~0 : (1 << width) - 1; - return (data[index] >> shift) & mask; + return (data[index] >> shift) & mask; } #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -667,166 +651,159 @@ unsigned int get_bits(struct link_packet *packet, int offset, int width) void dump_data(unsigned char *data, int length) { - int i, print_length; + int i, print_length; - if (length > 128) - print_length = 128; - else - print_length = length; + if (length > 128) + print_length = 128; + else + print_length = length; - for (i = 0; i < print_length; i++) - printf("%s%02hhx", - (i % 4 == 0 && i != 0) ? " " : "", - data[byte_index(i)]); + for (i = 0; i < print_length; i++) + printf("%s%02hhx", + (i % 4 == 0 && i != 0) ? " " : "", + data[byte_index(i)]); - if (print_length < length) - printf(" (%d more bytes)", length - print_length); + if (print_length < length) + printf(" (%d more bytes)", length - print_length); } static void decode_link_packet(struct link_packet *packet, size_t length, int include_flags, int exclude_flags) { - struct packet_info *pi; - int data_length = 0; - int i; - - pi = &packet_info[packet->common.tcode]; - - for (i = 0; i < pi->field_count; i++) { - struct packet_field *f = &pi->fields[i]; - int offset; - - if (f->flags & exclude_flags) - continue; - if (include_flags && !(f->flags & include_flags)) - continue; - - if (f->offset < 0) - offset = length * 8 + f->offset - 32; - else - offset = f->offset; - - if (f->value_names != NULL) { - uint32_t bits; - - bits = get_bits(packet, offset, f->width); - printf("%s", f->value_names[bits]); - } - else if (f->width == 0) { - printf("%s=[", f->name); - dump_data((unsigned char *) packet + (offset / 8 + 4), data_length); - printf("]"); - } - else { - unsigned long long bits; - int high_width, low_width; - - if ((offset & ~31) != ((offset + f->width - 1) & ~31)) { - /* Bit field spans quadlet boundary. */ - high_width = ((offset + 31) & ~31) - offset; - low_width = f->width - high_width; - - bits = get_bits(packet, offset, high_width); - bits = (bits << low_width) | - get_bits(packet, offset + high_width, low_width); - } - else - bits = get_bits(packet, offset, f->width); - - printf("%s=0x%0*llx", f->name, (f->width + 3) / 4, bits); - - if (f->flags & PACKET_FIELD_DATA_LENGTH) - data_length = bits; - } - - if (i < pi->field_count - 1) - printf(", "); - } + struct packet_info *pi; + int data_length = 0; + int i; + + pi = &packet_info[packet->common.tcode]; + + for (i = 0; i < pi->field_count; i++) { + struct packet_field *f = &pi->fields[i]; + int offset; + + if (f->flags & exclude_flags) + continue; + if (include_flags && !(f->flags & include_flags)) + continue; + + if (f->offset < 0) + offset = length * 8 + f->offset - 32; + else + offset = f->offset; + + if (f->value_names != NULL) { + uint32_t bits; + + bits = get_bits(packet, offset, f->width); + printf("%s", f->value_names[bits]); + } else if (f->width == 0) { + printf("%s=[", f->name); + dump_data((unsigned char *) packet + (offset / 8 + 4), data_length); + printf("]"); + } else { + unsigned long long bits; + int high_width, low_width; + + if ((offset & ~31) != ((offset + f->width - 1) & ~31)) { + /* Bit field spans quadlet boundary. */ + high_width = ((offset + 31) & ~31) - offset; + low_width = f->width - high_width; + + bits = get_bits(packet, offset, high_width); + bits = (bits << low_width) | + get_bits(packet, offset + high_width, low_width); + } else { + bits = get_bits(packet, offset, f->width); + } + + printf("%s=0x%0*llx", f->name, (f->width + 3) / 4, bits); + + if (f->flags & PACKET_FIELD_DATA_LENGTH) + data_length = bits; + } + + if (i < pi->field_count - 1) + printf(", "); + } } static void print_packet(uint32_t *data, size_t length) { - int i; - - printf("%6u ", data[0]); - - if (length == 4) - printf("bus reset"); - else if (length < sizeof(struct phy_packet)) { - printf("short packet: "); - for (i = 1; i < length / 4; i++) - printf("%s%08x", i == 0 ? "[" : " ", data[i]); - printf("]"); - - } - else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) { - struct phy_packet *pp = (struct phy_packet *) data; - - /* phy packet are 3 quadlets: the 1 quadlet payload, - * the bitwise inverse of the payload and the snoop - * mode ack */ - - switch (pp->common.identifier) { - case PHY_PACKET_CONFIGURATION: - if (!pp->phy_config.set_root && !pp->phy_config.set_gap_count) { - printf("ext phy config: phy_id=%02x", pp->phy_config.root_id); - } - else { - printf("phy config:"); - if (pp->phy_config.set_root) - printf(" set_root_id=%02x", pp->phy_config.root_id); - if (pp->phy_config.set_gap_count) - printf(" set_gap_count=%d", pp->phy_config.gap_count); - } - break; - - case PHY_PACKET_LINK_ON: - printf("link-on packet, phy_id=%02x", pp->link_on.phy_id); - break; - - case PHY_PACKET_SELF_ID: - if (pp->self_id.extended) { - printf("extended self id: phy_id=%02x, seq=%d", - pp->ext_self_id.phy_id, pp->ext_self_id.sequence); - } - else { - static const char * const speed_names[] = { - "S100", "S200", "S400", "BETA" - }; - printf("self id: phy_id=%02x, link %s, gap_count=%d, speed=%s%s%s", - pp->self_id.phy_id, - (pp->self_id.link_active ? "active" : "not active"), - pp->self_id.gap_count, - speed_names[pp->self_id.phy_speed], - (pp->self_id.contender ? ", irm contender" : ""), - (pp->self_id.initiated_reset ? ", initiator" : "")); - - } - break; - default: - printf("unknown phy packet: "); - for (i = 1; i < length / 4; i++) - printf("%s%08x", i == 0 ? "[" : " ", data[i]); - printf("]"); - break; - } - } - else { - struct link_packet *packet = (struct link_packet *) data; - - decode_link_packet(packet, length, 0, - option_verbose ? 0 : PACKET_FIELD_DETAIL); - } - - if (option_hex) { - printf(" ["); - dump_data((unsigned char *) data + 4, length - 4); - printf("]"); - } - - printf("\r\n"); + int i; + + printf("%6u ", data[0]); + + if (length == 4) { + printf("bus reset"); + } else if (length < sizeof(struct phy_packet)) { + printf("short packet: "); + for (i = 1; i < length / 4; i++) + printf("%s%08x", i == 0 ? "[" : " ", data[i]); + printf("]"); + + } else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) { + struct phy_packet *pp = (struct phy_packet *) data; + + /* phy packet are 3 quadlets: the 1 quadlet payload, + * the bitwise inverse of the payload and the snoop + * mode ack */ + + switch (pp->common.identifier) { + case PHY_PACKET_CONFIGURATION: + if (!pp->phy_config.set_root && !pp->phy_config.set_gap_count) { + printf("ext phy config: phy_id=%02x", pp->phy_config.root_id); + } else { + printf("phy config:"); + if (pp->phy_config.set_root) + printf(" set_root_id=%02x", pp->phy_config.root_id); + if (pp->phy_config.set_gap_count) + printf(" set_gap_count=%d", pp->phy_config.gap_count); + } + break; + + case PHY_PACKET_LINK_ON: + printf("link-on packet, phy_id=%02x", pp->link_on.phy_id); + break; + + case PHY_PACKET_SELF_ID: + if (pp->self_id.extended) { + printf("extended self id: phy_id=%02x, seq=%d", + pp->ext_self_id.phy_id, pp->ext_self_id.sequence); + } else { + static const char * const speed_names[] = { + "S100", "S200", "S400", "BETA" + }; + printf("self id: phy_id=%02x, link %s, gap_count=%d, speed=%s%s%s", + pp->self_id.phy_id, + (pp->self_id.link_active ? "active" : "not active"), + pp->self_id.gap_count, + speed_names[pp->self_id.phy_speed], + (pp->self_id.contender ? ", irm contender" : ""), + (pp->self_id.initiated_reset ? ", initiator" : "")); + } + break; + default: + printf("unknown phy packet: "); + for (i = 1; i < length / 4; i++) + printf("%s%08x", i == 0 ? "[" : " ", data[i]); + printf("]"); + break; + } + } else { + struct link_packet *packet = (struct link_packet *) data; + + decode_link_packet(packet, length, 0, + option_verbose ? 0 : PACKET_FIELD_DETAIL); + } + + if (option_hex) { + printf(" ["); + dump_data((unsigned char *) data + 4, length - 4); + printf("]"); + } + + printf("\r\n"); } #define HIDE_CURSOR "\033[?25l" @@ -836,206 +813,204 @@ print_packet(uint32_t *data, size_t length) static void print_stats(uint32_t *data, size_t length) { - static int bus_reset_count, short_packet_count, phy_packet_count; - static int tcode_count[16]; - static struct timeval last_update; - struct timeval now; - int i; - - if (length == 0) - bus_reset_count++; - else if (length < sizeof(struct phy_packet)) - short_packet_count++; - else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) - phy_packet_count++; - else { - struct link_packet *packet = (struct link_packet *) data; - tcode_count[packet->common.tcode]++; - } - - gettimeofday(&now, NULL); - if (now.tv_sec <= last_update.tv_sec && - now.tv_usec < last_update.tv_usec + 500000) - return; - - last_update = now; - printf(CLEAR HIDE_CURSOR - " bus resets : %8d\n" - " short packets : %8d\n" - " phy packets : %8d\n", - bus_reset_count, short_packet_count, phy_packet_count); - - for (i = 0; i < array_length(packet_info); i++) - if (packet_info[i].type != PACKET_RESERVED) - printf(" %-24s: %8d\n", packet_info[i].name, tcode_count[i]); - printf(SHOW_CURSOR "\n"); + static int bus_reset_count, short_packet_count, phy_packet_count; + static int tcode_count[16]; + static struct timeval last_update; + struct timeval now; + int i; + + if (length == 0) + bus_reset_count++; + else if (length < sizeof(struct phy_packet)) + short_packet_count++; + else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) + phy_packet_count++; + else { + struct link_packet *packet = (struct link_packet *) data; + tcode_count[packet->common.tcode]++; + } + + gettimeofday(&now, NULL); + if (now.tv_sec <= last_update.tv_sec && + now.tv_usec < last_update.tv_usec + 500000) + return; + + last_update = now; + printf(CLEAR HIDE_CURSOR + " bus resets : %8d\n" + " short packets : %8d\n" + " phy packets : %8d\n", + bus_reset_count, short_packet_count, phy_packet_count); + + for (i = 0; i < array_length(packet_info); i++) + if (packet_info[i].type != PACKET_RESERVED) + printf(" %-24s: %8d\n", packet_info[i].name, tcode_count[i]); + printf(SHOW_CURSOR "\n"); } struct termios saved_attributes; void -reset_input_mode (void) +reset_input_mode(void) { - tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes); + tcsetattr(STDIN_FILENO, TCSANOW, &saved_attributes); } void -set_input_mode (void) +set_input_mode(void) { - struct termios tattr; - - /* Make sure stdin is a terminal. */ - if (!isatty(STDIN_FILENO)) { - fprintf(stderr, "Not a terminal.\n"); - exit(EXIT_FAILURE); - } - - /* Save the terminal attributes so we can restore them later. */ - tcgetattr(STDIN_FILENO, &saved_attributes); - atexit(reset_input_mode); - - /* Set the funny terminal modes. */ - tcgetattr(STDIN_FILENO, &tattr); - tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */ - tattr.c_cc[VMIN] = 1; - tattr.c_cc[VTIME] = 0; - tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr); + struct termios tattr; + + /* Make sure stdin is a terminal. */ + if (!isatty(STDIN_FILENO)) { + fprintf(stderr, "Not a terminal.\n"); + exit(EXIT_FAILURE); + } + + /* Save the terminal attributes so we can restore them later. */ + tcgetattr(STDIN_FILENO, &saved_attributes); + atexit(reset_input_mode); + + /* Set the funny terminal modes. */ + tcgetattr(STDIN_FILENO, &tattr); + tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */ + tattr.c_cc[VMIN] = 1; + tattr.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr); } int main(int argc, const char *argv[]) { - int fd = -1; - FILE *output = NULL, *input = NULL; - poptContext con; - int retval; - int view; - char c; - struct pollfd pollfds[2]; - - sys_sigint_handler = signal(SIGINT, sigint_handler); - - con = poptGetContext(NULL, argc, argv, options, 0); - retval = poptGetNextOpt(con); - if (retval < -1) { - poptPrintUsage(con, stdout, 0); - return -1; - } - - if (option_version) { - printf("dump tool for nosy sniffer, version %s\n", VERSION); - return 0; - } - - if (__BYTE_ORDER != __LITTLE_ENDIAN) - fprintf(stderr, "warning: nosy has only been tested on little " - "endian machines\n"); - - if (option_input != NULL) { - input = fopen(option_input, "r"); - if (input == NULL) { - fprintf(stderr, "Could not open %s, %m\n", option_input); - return -1; - } - } - else { - fd = open(option_nosy_device, O_RDWR); - if (fd < 0) { - fprintf(stderr, "Could not open %s, %m\n", option_nosy_device); - return -1; - } - set_input_mode(); - } - - if (strcmp(option_view, "transaction") == 0) - view = VIEW_TRANSACTION; - else if (strcmp(option_view, "stats") == 0) - view = VIEW_STATS; - else - view = VIEW_PACKET; - - if (option_output) { - output = fopen(option_output, "w"); - if (output == NULL) { - fprintf(stderr, "Could not open %s, %m\n", option_output); - return -1; - } - } - - setvbuf(stdout, NULL, _IOLBF, BUFSIZ); - - if (1) { - uint32_t buf[128 * 1024]; - uint32_t filter; - int length; - - filter = ~0; - if (!option_iso) - filter &= ~(1 < struct phy_packet { - uint32_t timestamp; - union { - struct { - uint32_t zero:24; - uint32_t phy_id:6; - uint32_t identifier:2; - } common, link_on; - - struct { - uint32_t zero:16; - uint32_t gap_count:6; - uint32_t set_gap_count:1; - uint32_t set_root:1; - uint32_t root_id:6; - uint32_t identifier:2; - } phy_config; - - struct { - uint32_t more_packets:1; - uint32_t initiated_reset:1; - uint32_t port2:2; - uint32_t port1:2; - uint32_t port0:2; - uint32_t power_class:3; - uint32_t contender:1; - uint32_t phy_delay:2; - uint32_t phy_speed:2; - uint32_t gap_count:6; - uint32_t link_active:1; - uint32_t extended:1; - uint32_t phy_id:6; - uint32_t identifier:2; - } self_id; - - struct { - uint32_t more_packets:1; - uint32_t reserved1:1; - uint32_t porth:2; - uint32_t portg:2; - uint32_t portf:2; - uint32_t porte:2; - uint32_t portd:2; - uint32_t portc:2; - uint32_t portb:2; - uint32_t porta:2; - uint32_t reserved0:2; - uint32_t sequence:3; - uint32_t extended:1; - uint32_t phy_id:6; - uint32_t identifier:2; - } ext_self_id; - }; - uint32_t inverted; - uint32_t ack; + uint32_t timestamp; + union { + struct { + uint32_t zero:24; + uint32_t phy_id:6; + uint32_t identifier:2; + } common, link_on; + + struct { + uint32_t zero:16; + uint32_t gap_count:6; + uint32_t set_gap_count:1; + uint32_t set_root:1; + uint32_t root_id:6; + uint32_t identifier:2; + } phy_config; + + struct { + uint32_t more_packets:1; + uint32_t initiated_reset:1; + uint32_t port2:2; + uint32_t port1:2; + uint32_t port0:2; + uint32_t power_class:3; + uint32_t contender:1; + uint32_t phy_delay:2; + uint32_t phy_speed:2; + uint32_t gap_count:6; + uint32_t link_active:1; + uint32_t extended:1; + uint32_t phy_id:6; + uint32_t identifier:2; + } self_id; + + struct { + uint32_t more_packets:1; + uint32_t reserved1:1; + uint32_t porth:2; + uint32_t portg:2; + uint32_t portf:2; + uint32_t porte:2; + uint32_t portd:2; + uint32_t portc:2; + uint32_t portb:2; + uint32_t porta:2; + uint32_t reserved0:2; + uint32_t sequence:3; + uint32_t extended:1; + uint32_t phy_id:6; + uint32_t identifier:2; + } ext_self_id; + }; + uint32_t inverted; + uint32_t ack; }; #define PHY_PACKET_CONFIGURATION 0x00 @@ -97,98 +97,98 @@ struct phy_packet { #define PHY_PACKET_SELF_ID 0x02 struct link_packet { - uint32_t timestamp; - union { - struct { - uint32_t priority:4; - uint32_t tcode:4; - uint32_t rt:2; - uint32_t tlabel:6; - uint32_t destination:16; - - uint32_t offset_high:16; - uint32_t source:16; - - uint32_t offset_low; - } common; - - struct { - uint32_t common[3]; - uint32_t crc; - } read_quadlet; - - struct { - uint32_t common[3]; - uint32_t data; - uint32_t crc; - } read_quadlet_response; - - struct { - uint32_t common[3]; - uint32_t extended_tcode:16; - uint32_t data_length:16; - uint32_t crc; - } read_block; - - struct { - uint32_t common[3]; - uint32_t extended_tcode:16; - uint32_t data_length:16; - uint32_t crc; - uint32_t data[0]; - /* crc and ack follows. */ - } read_block_response; - - struct { - uint32_t common[3]; - uint32_t data; - uint32_t crc; - } write_quadlet; - - struct { - uint32_t common[3]; - uint32_t extended_tcode:16; - uint32_t data_length:16; - uint32_t crc; - uint32_t data[0]; - /* crc and ack follows. */ - } write_block; - - struct { - uint32_t common[3]; - uint32_t crc; - } write_response; - - struct { - uint32_t common[3]; - uint32_t data; - uint32_t crc; - } cycle_start; - - struct { - uint32_t sy:4; - uint32_t tcode:4; - uint32_t channel:6; - uint32_t tag:2; - uint32_t data_length:16; - - uint32_t crc; - } iso_data; - }; + uint32_t timestamp; + union { + struct { + uint32_t priority:4; + uint32_t tcode:4; + uint32_t rt:2; + uint32_t tlabel:6; + uint32_t destination:16; + + uint32_t offset_high:16; + uint32_t source:16; + + uint32_t offset_low; + } common; + + struct { + uint32_t common[3]; + uint32_t crc; + } read_quadlet; + + struct { + uint32_t common[3]; + uint32_t data; + uint32_t crc; + } read_quadlet_response; + + struct { + uint32_t common[3]; + uint32_t extended_tcode:16; + uint32_t data_length:16; + uint32_t crc; + } read_block; + + struct { + uint32_t common[3]; + uint32_t extended_tcode:16; + uint32_t data_length:16; + uint32_t crc; + uint32_t data[0]; + /* crc and ack follows. */ + } read_block_response; + + struct { + uint32_t common[3]; + uint32_t data; + uint32_t crc; + } write_quadlet; + + struct { + uint32_t common[3]; + uint32_t extended_tcode:16; + uint32_t data_length:16; + uint32_t crc; + uint32_t data[0]; + /* crc and ack follows. */ + } write_block; + + struct { + uint32_t common[3]; + uint32_t crc; + } write_response; + + struct { + uint32_t common[3]; + uint32_t data; + uint32_t crc; + } cycle_start; + + struct { + uint32_t sy:4; + uint32_t tcode:4; + uint32_t channel:6; + uint32_t tag:2; + uint32_t data_length:16; + + uint32_t crc; + } iso_data; + }; }; struct subaction { - uint32_t ack; - size_t length; - struct list link; - struct link_packet packet; + uint32_t ack; + size_t length; + struct list link; + struct link_packet packet; }; struct link_transaction { - int request_node, response_node, tlabel; - struct subaction *request, *response; - struct list request_list, response_list; - struct list link; + int request_node, response_node, tlabel; + struct subaction *request, *response; + struct list request_list, response_list; + struct list link; }; int decode_fcp(struct link_transaction *t); -- cgit v1.2.3-18-g5258 From 468066f771032ad4bef4a906dfe0b0a55593d64d Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:58:05 +0200 Subject: tools/firewire: nosy-dump: make some symbols static or const Signed-off-by: Stefan Richter --- tools/firewire/nosy-dump.c | 56 ++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 27 deletions(-) (limited to 'tools') diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c index 3d7e1a6dfe9..5d1e8923373 100644 --- a/tools/firewire/nosy-dump.c +++ b/tools/firewire/nosy-dump.c @@ -133,7 +133,7 @@ static const struct poptOption options[] = { }; /* Allow all ^C except the first to interrupt the program in the usual way. */ -void +static void sigint_handler(int signal_num) { if (run == 1) { @@ -142,7 +142,7 @@ sigint_handler(int signal_num) } } -struct subaction * +static struct subaction * subaction_create(uint32_t *data, size_t length) { struct subaction *sa; @@ -156,17 +156,17 @@ subaction_create(uint32_t *data, size_t length) return sa; } -void +static void subaction_destroy(struct subaction *sa) { free(sa); } -struct list pending_transaction_list = { +static struct list pending_transaction_list = { &pending_transaction_list, &pending_transaction_list }; -struct link_transaction * +static struct link_transaction * link_transaction_lookup(int request_node, int response_node, int tlabel) { struct link_transaction *t; @@ -190,7 +190,7 @@ link_transaction_lookup(int request_node, int response_node, int tlabel) return t; } -void +static void link_transaction_destroy(struct link_transaction *t) { struct subaction *sa; @@ -213,11 +213,11 @@ struct protocol_decoder { int (*decode)(struct link_transaction *t); }; -static struct protocol_decoder protocol_decoders[] = { +static const struct protocol_decoder protocol_decoders[] = { { "FCP", decode_fcp } }; -void +static void handle_transaction(struct link_transaction *t) { struct subaction *sa; @@ -254,7 +254,7 @@ handle_transaction(struct link_transaction *t) link_transaction_destroy(t); } -void +static void clear_pending_transaction_list(void) { struct link_transaction *t; @@ -313,7 +313,7 @@ struct packet_info { const char *name; int type; int response_tcode; - struct packet_field *fields; + const struct packet_field *fields; int field_count; }; @@ -344,20 +344,20 @@ struct packet_field { { "src", 32, 16 }, \ { "rcode", 48, 4, PACKET_FIELD_TRANSACTION, rcode_names } -struct packet_field read_quadlet_request_fields[] = { +static const struct packet_field read_quadlet_request_fields[] = { COMMON_REQUEST_FIELDS, { "crc", 96, 32, PACKET_FIELD_DETAIL }, { "ack", 156, 4, 0, ack_names }, }; -struct packet_field read_quadlet_response_fields[] = { +static const struct packet_field read_quadlet_response_fields[] = { COMMON_RESPONSE_FIELDS, { "data", 96, 32, PACKET_FIELD_TRANSACTION }, { "crc", 128, 32, PACKET_FIELD_DETAIL }, { "ack", 188, 4, 0, ack_names }, }; -struct packet_field read_block_request_fields[] = { +static const struct packet_field read_block_request_fields[] = { COMMON_REQUEST_FIELDS, { "data_length", 96, 16, PACKET_FIELD_TRANSACTION }, { "extended_tcode", 112, 16 }, @@ -365,7 +365,7 @@ struct packet_field read_block_request_fields[] = { { "ack", 188, 4, 0, ack_names }, }; -struct packet_field block_response_fields[] = { +static const struct packet_field block_response_fields[] = { COMMON_RESPONSE_FIELDS, { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH }, { "extended_tcode", 112, 16 }, @@ -375,13 +375,13 @@ struct packet_field block_response_fields[] = { { "ack", -4, 4, 0, ack_names }, }; -struct packet_field write_quadlet_request_fields[] = { +static const struct packet_field write_quadlet_request_fields[] = { COMMON_REQUEST_FIELDS, { "data", 96, 32, PACKET_FIELD_TRANSACTION }, { "ack", -4, 4, 0, ack_names }, }; -struct packet_field block_request_fields[] = { +static const struct packet_field block_request_fields[] = { COMMON_REQUEST_FIELDS, { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH | PACKET_FIELD_TRANSACTION }, { "extended_tcode", 112, 16, PACKET_FIELD_TRANSACTION }, @@ -391,13 +391,13 @@ struct packet_field block_request_fields[] = { { "ack", -4, 4, 0, ack_names }, }; -struct packet_field write_response_fields[] = { +static const struct packet_field write_response_fields[] = { COMMON_RESPONSE_FIELDS, { "reserved", 64, 32, PACKET_FIELD_DETAIL }, { "ack", -4, 4, 0, ack_names }, }; -struct packet_field iso_data_fields[] = { +static const struct packet_field iso_data_fields[] = { { "data_length", 0, 16, PACKET_FIELD_DATA_LENGTH }, { "tag", 16, 2 }, { "channel", 18, 6 }, @@ -409,7 +409,7 @@ struct packet_field iso_data_fields[] = { { "ack", -4, 4, 0, ack_names }, }; -static struct packet_info packet_info[] = { +static const struct packet_info packet_info[] = { { .name = "write_quadlet_request", .type = PACKET_REQUEST, @@ -486,7 +486,7 @@ static struct packet_info packet_info[] = { }, }; -int +static int handle_packet(uint32_t *data, size_t length) { if (length == 0) { @@ -629,7 +629,8 @@ handle_packet(uint32_t *data, size_t length) return 1; } -unsigned int get_bits(struct link_packet *packet, int offset, int width) +static unsigned int +get_bits(struct link_packet *packet, int offset, int width) { uint32_t *data = (uint32_t *) packet; uint32_t index, shift, mask; @@ -649,7 +650,8 @@ unsigned int get_bits(struct link_packet *packet, int offset, int width) #error unsupported byte order. #endif -void dump_data(unsigned char *data, int length) +static void +dump_data(unsigned char *data, int length) { int i, print_length; @@ -671,14 +673,14 @@ static void decode_link_packet(struct link_packet *packet, size_t length, int include_flags, int exclude_flags) { - struct packet_info *pi; + const struct packet_info *pi; int data_length = 0; int i; pi = &packet_info[packet->common.tcode]; for (i = 0; i < pi->field_count; i++) { - struct packet_field *f = &pi->fields[i]; + const struct packet_field *f = &pi->fields[i]; int offset; if (f->flags & exclude_flags) @@ -848,15 +850,15 @@ print_stats(uint32_t *data, size_t length) printf(SHOW_CURSOR "\n"); } -struct termios saved_attributes; +static struct termios saved_attributes; -void +static void reset_input_mode(void) { tcsetattr(STDIN_FILENO, TCSANOW, &saved_attributes); } -void +static void set_input_mode(void) { struct termios tattr; -- cgit v1.2.3-18-g5258 From 269fe1023191a338736c71ba4f4db13839747772 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:58:05 +0200 Subject: tools/firewire: nosy-dump: break up a deeply nested function Signed-off-by: Stefan Richter --- tools/firewire/nosy-dump.c | 245 ++++++++++++++++++++++++--------------------- 1 file changed, 132 insertions(+), 113 deletions(-) (limited to 'tools') diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c index 5d1e8923373..32c48556170 100644 --- a/tools/firewire/nosy-dump.c +++ b/tools/firewire/nosy-dump.c @@ -487,138 +487,157 @@ static const struct packet_info packet_info[] = { }; static int -handle_packet(uint32_t *data, size_t length) +handle_request_packet(uint32_t *data, size_t length) { - if (length == 0) { - printf("bus reset\r\n"); - clear_pending_transaction_list(); - } else if (length > sizeof(struct phy_packet)) { - struct link_packet *p = (struct link_packet *) data; - struct subaction *sa, *prev; - struct link_transaction *t; + struct link_packet *p = (struct link_packet *) data; + struct subaction *sa, *prev; + struct link_transaction *t; - switch (packet_info[p->common.tcode].type) { - case PACKET_REQUEST: - t = link_transaction_lookup(p->common.source, p->common.destination, - p->common.tlabel); - sa = subaction_create(data, length); - t->request = sa; - - if (!list_empty(&t->request_list)) { - prev = list_tail(&t->request_list, - struct subaction, link); - - if (!ACK_BUSY(prev->ack)) { - /* - * error, we should only see ack_busy_* before the - * ack_pending/ack_complete -- this is an ack_pending - * instead (ack_complete would have finished the - * transaction). - */ - } + t = link_transaction_lookup(p->common.source, p->common.destination, + p->common.tlabel); + sa = subaction_create(data, length); + t->request = sa; + + if (!list_empty(&t->request_list)) { + prev = list_tail(&t->request_list, + struct subaction, link); + + if (!ACK_BUSY(prev->ack)) { + /* + * error, we should only see ack_busy_* before the + * ack_pending/ack_complete -- this is an ack_pending + * instead (ack_complete would have finished the + * transaction). + */ + } - if (prev->packet.common.tcode != sa->packet.common.tcode || - prev->packet.common.tlabel != sa->packet.common.tlabel) { - /* memcmp() ? */ - /* error, these should match for retries. */ - } - } + if (prev->packet.common.tcode != sa->packet.common.tcode || + prev->packet.common.tlabel != sa->packet.common.tlabel) { + /* memcmp() ? */ + /* error, these should match for retries. */ + } + } - list_append(&t->request_list, &sa->link); + list_append(&t->request_list, &sa->link); - switch (sa->ack) { - case ACK_COMPLETE: - if (p->common.tcode != TCODE_WRITE_QUADLET && - p->common.tcode != TCODE_WRITE_BLOCK) - /* error, unified transactions only allowed for write */; - list_remove(&t->link); - handle_transaction(t); - break; + switch (sa->ack) { + case ACK_COMPLETE: + if (p->common.tcode != TCODE_WRITE_QUADLET && + p->common.tcode != TCODE_WRITE_BLOCK) + /* error, unified transactions only allowed for write */; + list_remove(&t->link); + handle_transaction(t); + break; - case ACK_NO_ACK: - case ACK_DATA_ERROR: - case ACK_TYPE_ERROR: - list_remove(&t->link); - handle_transaction(t); - break; + case ACK_NO_ACK: + case ACK_DATA_ERROR: + case ACK_TYPE_ERROR: + list_remove(&t->link); + handle_transaction(t); + break; + + case ACK_PENDING: + /* request subaction phase over, wait for response. */ + break; + + case ACK_BUSY_X: + case ACK_BUSY_A: + case ACK_BUSY_B: + /* ok, wait for retry. */ + /* check that retry protocol is respected. */ + break; + } - case ACK_PENDING: - /* request subaction phase over, wait for response. */ - break; + return 1; +} - case ACK_BUSY_X: - case ACK_BUSY_A: - case ACK_BUSY_B: - /* ok, wait for retry. */ - /* check that retry protocol is respected. */ - break; - } - break; +static int +handle_response_packet(uint32_t *data, size_t length) +{ + struct link_packet *p = (struct link_packet *) data; + struct subaction *sa, *prev; + struct link_transaction *t; - case PACKET_RESPONSE: - t = link_transaction_lookup(p->common.destination, p->common.source, - p->common.tlabel); - if (list_empty(&t->request_list)) { - /* unsolicited response */ - } + t = link_transaction_lookup(p->common.destination, p->common.source, + p->common.tlabel); + if (list_empty(&t->request_list)) { + /* unsolicited response */ + } - sa = subaction_create(data, length); - t->response = sa; + sa = subaction_create(data, length); + t->response = sa; - if (!list_empty(&t->response_list)) { - prev = list_tail(&t->response_list, struct subaction, link); + if (!list_empty(&t->response_list)) { + prev = list_tail(&t->response_list, struct subaction, link); - if (!ACK_BUSY(prev->ack)) { - /* - * error, we should only see ack_busy_* before the - * ack_pending/ack_complete - */ - } + if (!ACK_BUSY(prev->ack)) { + /* + * error, we should only see ack_busy_* before the + * ack_pending/ack_complete + */ + } - if (prev->packet.common.tcode != sa->packet.common.tcode || - prev->packet.common.tlabel != sa->packet.common.tlabel) { - /* use memcmp() instead? */ - /* error, these should match for retries. */ - } - } else { - prev = list_tail(&t->request_list, struct subaction, link); - if (prev->ack != ACK_PENDING) { - /* - * error, should not get response unless last request got - * ack_pending. - */ - } + if (prev->packet.common.tcode != sa->packet.common.tcode || + prev->packet.common.tlabel != sa->packet.common.tlabel) { + /* use memcmp() instead? */ + /* error, these should match for retries. */ + } + } else { + prev = list_tail(&t->request_list, struct subaction, link); + if (prev->ack != ACK_PENDING) { + /* + * error, should not get response unless last request got + * ack_pending. + */ + } - if (packet_info[prev->packet.common.tcode].response_tcode != - sa->packet.common.tcode) { - /* error, tcode mismatch */ - } - } + if (packet_info[prev->packet.common.tcode].response_tcode != + sa->packet.common.tcode) { + /* error, tcode mismatch */ + } + } - list_append(&t->response_list, &sa->link); + list_append(&t->response_list, &sa->link); - switch (sa->ack) { - case ACK_COMPLETE: - case ACK_NO_ACK: - case ACK_DATA_ERROR: - case ACK_TYPE_ERROR: - list_remove(&t->link); - handle_transaction(t); - /* transaction complete, remove t from pending list. */ - break; + switch (sa->ack) { + case ACK_COMPLETE: + case ACK_NO_ACK: + case ACK_DATA_ERROR: + case ACK_TYPE_ERROR: + list_remove(&t->link); + handle_transaction(t); + /* transaction complete, remove t from pending list. */ + break; + + case ACK_PENDING: + /* error for responses. */ + break; + + case ACK_BUSY_X: + case ACK_BUSY_A: + case ACK_BUSY_B: + /* no problem, wait for next retry */ + break; + } - case ACK_PENDING: - /* error for responses. */ - break; + return 1; +} - case ACK_BUSY_X: - case ACK_BUSY_A: - case ACK_BUSY_B: - /* no problem, wait for next retry */ - break; - } +static int +handle_packet(uint32_t *data, size_t length) +{ + if (length == 0) { + printf("bus reset\r\n"); + clear_pending_transaction_list(); + } else if (length > sizeof(struct phy_packet)) { + struct link_packet *p = (struct link_packet *) data; - break; + switch (packet_info[p->common.tcode].type) { + case PACKET_REQUEST: + return handle_request_packet(data, length); + + case PACKET_RESPONSE: + return handle_response_packet(data, length); case PACKET_OTHER: case PACKET_RESERVED: -- cgit v1.2.3-18-g5258 From 83ef7c75939f45f8da46976cdca4db0727276a2d Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:58:05 +0200 Subject: tools/firewire: nosy-dump: use linux/firewire-constants.h Signed-off-by: Stefan Richter --- tools/firewire/decode-fcp.c | 6 ++++-- tools/firewire/nosy-dump.c | 7 ++++--- tools/firewire/nosy-dump.h | 29 +++-------------------------- 3 files changed, 11 insertions(+), 31 deletions(-) (limited to 'tools') diff --git a/tools/firewire/decode-fcp.c b/tools/firewire/decode-fcp.c index 9cd1550db24..e41223b6a4c 100644 --- a/tools/firewire/decode-fcp.c +++ b/tools/firewire/decode-fcp.c @@ -1,5 +1,7 @@ -#include +#include #include +#include + #include "list.h" #include "nosy-dump.h" @@ -176,7 +178,7 @@ decode_fcp(struct link_transaction *t) ((unsigned long long) t->request->packet.common.offset_high << 32) | t->request->packet.common.offset_low; - if (t->request->packet.common.tcode != TCODE_WRITE_BLOCK) + if (t->request->packet.common.tcode != TCODE_WRITE_BLOCK_REQUEST) return 0; if (offset == CSR_FCP_COMMAND || offset == CSR_FCP_RESPONSE) { diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c index 32c48556170..5241cb402d6 100644 --- a/tools/firewire/nosy-dump.c +++ b/tools/firewire/nosy-dump.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -522,8 +523,8 @@ handle_request_packet(uint32_t *data, size_t length) switch (sa->ack) { case ACK_COMPLETE: - if (p->common.tcode != TCODE_WRITE_QUADLET && - p->common.tcode != TCODE_WRITE_BLOCK) + if (p->common.tcode != TCODE_WRITE_QUADLET_REQUEST && + p->common.tcode != TCODE_WRITE_BLOCK_REQUEST) /* error, unified transactions only allowed for write */; list_remove(&t->link); handle_transaction(t); @@ -967,7 +968,7 @@ int main(int argc, const char *argv[]) filter = ~0; if (!option_iso) - filter &= ~(1 << TCODE_ISO_DATA); + filter &= ~(1 << TCODE_STREAM_DATA); if (!option_cycle_start) filter &= ~(1 << TCODE_CYCLE_START); if (view == VIEW_STATS) diff --git a/tools/firewire/nosy-dump.h b/tools/firewire/nosy-dump.h index fe8be630efa..3a4b5b33ba5 100644 --- a/tools/firewire/nosy-dump.h +++ b/tools/firewire/nosy-dump.h @@ -3,36 +3,11 @@ #define array_length(array) (sizeof(array) / sizeof(array[0])) -#define TCODE_WRITE_QUADLET 0x0 -#define TCODE_WRITE_BLOCK 0x1 -#define TCODE_WRITE_RESPONSE 0x2 -#define TCODE_READ_QUADLET 0x4 -#define TCODE_READ_BLOCK 0x5 -#define TCODE_READ_QUADLET_RESPONSE 0x6 -#define TCODE_READ_BLOCK_RESPONSE 0x7 -#define TCODE_CYCLE_START 0x8 -#define TCODE_LOCK_REQUEST 0x9 -#define TCODE_ISO_DATA 0xa -#define TCODE_LOCK_RESPONSE 0xb -#define TCODE_PHY_PACKET 0x10 - -#define ACK_NO_ACK 0x0 -#define ACK_COMPLETE 0x1 -#define ACK_PENDING 0x2 -#define ACK_BUSY_X 0x4 -#define ACK_BUSY_A 0x5 -#define ACK_BUSY_B 0x6 -#define ACK_DATA_ERROR 0xd -#define ACK_TYPE_ERROR 0xe - +#define ACK_NO_ACK 0x0 #define ACK_DONE(a) ((a >> 2) == 0) #define ACK_BUSY(a) ((a >> 2) == 1) #define ACK_ERROR(a) ((a >> 2) == 3) -#define SPEED_100 0x0 -#define SPEED_200 0x1 -#define SPEED_400 0x2 - #include struct phy_packet { @@ -92,6 +67,8 @@ struct phy_packet { uint32_t ack; }; +#define TCODE_PHY_PACKET 0x10 + #define PHY_PACKET_CONFIGURATION 0x00 #define PHY_PACKET_LINK_ON 0x01 #define PHY_PACKET_SELF_ID 0x02 -- cgit v1.2.3-18-g5258 From ddbfe74958ec21c3d58d5a46dd6f4048bc7969a4 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:58:05 +0200 Subject: tools/firewire: nosy-dump: remove unused code Signed-off-by: Stefan Richter --- tools/firewire/nosy-dump.c | 105 +++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 56 deletions(-) (limited to 'tools') diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c index 5241cb402d6..f93b776370b 100644 --- a/tools/firewire/nosy-dump.c +++ b/tools/firewire/nosy-dump.c @@ -903,11 +903,12 @@ set_input_mode(void) int main(int argc, const char *argv[]) { + uint32_t buf[128 * 1024]; + uint32_t filter; + int length, retval, view; int fd = -1; FILE *output = NULL, *input = NULL; poptContext con; - int retval; - int view; char c; struct pollfd pollfds[2]; @@ -961,70 +962,62 @@ int main(int argc, const char *argv[]) setvbuf(stdout, NULL, _IOLBF, BUFSIZ); - if (1) { - uint32_t buf[128 * 1024]; - uint32_t filter; - int length; + filter = ~0; + if (!option_iso) + filter &= ~(1 << TCODE_STREAM_DATA); + if (!option_cycle_start) + filter &= ~(1 << TCODE_CYCLE_START); + if (view == VIEW_STATS) + filter = ~(1 << TCODE_CYCLE_START); - filter = ~0; - if (!option_iso) - filter &= ~(1 << TCODE_STREAM_DATA); - if (!option_cycle_start) - filter &= ~(1 << TCODE_CYCLE_START); - if (view == VIEW_STATS) - filter = ~(1 << TCODE_CYCLE_START); + ioctl(fd, NOSY_IOC_FILTER, filter); - ioctl(fd, NOSY_IOC_FILTER, filter); + ioctl(fd, NOSY_IOC_START); - ioctl(fd, NOSY_IOC_START); + pollfds[0].fd = fd; + pollfds[0].events = POLLIN; + pollfds[1].fd = STDIN_FILENO; + pollfds[1].events = POLLIN; - pollfds[0].fd = fd; - pollfds[0].events = POLLIN; - pollfds[1].fd = STDIN_FILENO; - pollfds[1].events = POLLIN; - - while (run) { - if (input != NULL) { - if (fread(&length, sizeof length, 1, input) != 1) + while (run) { + if (input != NULL) { + if (fread(&length, sizeof length, 1, input) != 1) + return 0; + fread(buf, 1, length, input); + } else { + poll(pollfds, 2, -1); + if (pollfds[1].revents) { + read(STDIN_FILENO, &c, sizeof c); + switch (c) { + case 'q': + if (output != NULL) + fclose(output); return 0; - fread(buf, 1, length, input); - } else { - poll(pollfds, 2, -1); - if (pollfds[1].revents) { - read(STDIN_FILENO, &c, sizeof c); - switch (c) { - case 'q': - if (output != NULL) - fclose(output); - return 0; - } } - - if (pollfds[0].revents) - length = read(fd, buf, sizeof buf); - else - continue; } - if (output != NULL) { - fwrite(&length, sizeof length, 1, output); - fwrite(buf, 1, length, output); - } + if (pollfds[0].revents) + length = read(fd, buf, sizeof buf); + else + continue; + } - switch (view) { - case VIEW_TRANSACTION: - handle_packet(buf, length); - break; - case VIEW_PACKET: - print_packet(buf, length); - break; - case VIEW_STATS: - print_stats(buf, length); - break; - } + if (output != NULL) { + fwrite(&length, sizeof length, 1, output); + fwrite(buf, 1, length, output); + } + + switch (view) { + case VIEW_TRANSACTION: + handle_packet(buf, length); + break; + case VIEW_PACKET: + print_packet(buf, length); + break; + case VIEW_STATS: + print_stats(buf, length); + break; } - } else { - poptPrintUsage(con, stdout, 0); } if (output != NULL) -- cgit v1.2.3-18-g5258 From b20d02e37eeabf34b40c3995c2dbb0af53da3c57 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:58:05 +0200 Subject: tools/firewire: nosy-dump: increment program version Since version 0.3 from Kristian's repository, there should actually be no change in functionality except for the x86-64 fix. Nevertheless, make it distinct from the original nosy-dump --- just in case and also because of potential future changes. Signed-off-by: Stefan Richter --- tools/firewire/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/firewire/Makefile b/tools/firewire/Makefile index 6afcdc21c7c..81767adaae7 100644 --- a/tools/firewire/Makefile +++ b/tools/firewire/Makefile @@ -1,12 +1,12 @@ -version = 0.3 prefix = /usr +nosy-dump-version = 0.4 CC = gcc all : nosy-dump nosy-dump : CFLAGS = -Wall -O2 -g -nosy-dump : CPPFLAGS = -DVERSION=\"$(version)\" -I../../drivers/firewire +nosy-dump : CPPFLAGS = -DVERSION=\"$(nosy-dump-version)\" -I../../drivers/firewire nosy-dump : LDFLAGS = -g nosy-dump : LDLIBS = -lpopt -- cgit v1.2.3-18-g5258 From b61b55ed995fd2765cd4ec0b22f0348dee272070 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 21 Jul 2010 17:55:32 -0300 Subject: perf ui: Restore SPACE as an alias to PGDN in annotate Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index ab6eb368cbf..2f5f7a1878c 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -750,6 +750,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) browser.width += 18; /* Percentage */ ui_browser__show(&browser, self->ms.sym->name); + newtFormAddHotKey(browser.form, ' '); ret = ui_browser__run(&browser, &es); newtFormDestroy(browser.form); newtPopWindow(); -- cgit v1.2.3-18-g5258 From 06daaaba7c211ca6a8227b9a54dbc86dd837f034 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 21 Jul 2010 17:58:25 -0300 Subject: perf hist: Introduce routine to measure lenght of formatted entry Will be used to figure out the window width needed in the new tree widget. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 27 +++++++++++++++++++++++++++ tools/perf/util/hist.h | 3 +++ 2 files changed, 30 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 0bc67900352..f93095ffaab 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -850,6 +850,33 @@ print_entries: return ret; } +/* + * See hists__fprintf to match the column widths + */ +unsigned int hists__sort_list_width(struct hists *self) +{ + struct sort_entry *se; + int ret = 9; /* total % */ + + if (symbol_conf.show_cpu_utilization) { + ret += 7; /* count_sys % */ + ret += 6; /* count_us % */ + if (perf_guest) { + ret += 13; /* count_guest_sys % */ + ret += 12; /* count_guest_us % */ + } + } + + if (symbol_conf.show_nr_samples) + ret += 11; + + list_for_each_entry(se, &hist_entry__sort_list, list) + if (!se->elide) + ret += 2 + hists__col_len(self, se->se_width_idx); + + return ret; +} + static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, enum hist_filter filter) { diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 92962b2f579..65a48db46a2 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -141,4 +141,7 @@ int hist_entry__tui_annotate(struct hist_entry *self); int hists__tui_browse_tree(struct rb_root *self, const char *help); #endif + +unsigned int hists__sort_list_width(struct hists *self); + #endif /* __PERF_HIST_H */ -- cgit v1.2.3-18-g5258 From 63160f73e7baa6618f19d7681bcab5be5c557205 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 26 Jul 2010 13:47:15 -0300 Subject: perf ui: Consider the refreshed dimensions in ui_browser__show When we call ui_browser__show we may have called ui_browser__refresh_dimensions to check if the maximum lenght for the contained entries changed, such as when zooming in and out DSOs or threads in the hist browser. For that to happen we must delete the old form, that will take care of deleting the vertical scrollbar, etc, and then recreate them, with the new dimensions. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 2f5f7a1878c..aed21490ccd 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -342,8 +342,10 @@ static void ui_browser__reset_index(struct ui_browser *self) static int ui_browser__show(struct ui_browser *self, const char *title) { - if (self->form != NULL) - return 0; + if (self->form != NULL) { + newtFormDestroy(self->form); + newtPopWindow(); + } ui_browser__refresh_dimensions(self); newtCenteredWindow(self->width + 2, self->height, title); self->form = newt_form__new(); -- cgit v1.2.3-18-g5258 From 8d8c369f3d697e31e04133ca18b516952549ee33 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 26 Jul 2010 14:08:48 -0300 Subject: perf ui: Show the scroll bar over the left window frame So that we gain two columns and look more like classical (at least in TUIs) scroll bars bars. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index aed21490ccd..cdd958eb68c 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -347,12 +347,12 @@ static int ui_browser__show(struct ui_browser *self, const char *title) newtPopWindow(); } ui_browser__refresh_dimensions(self); - newtCenteredWindow(self->width + 2, self->height, title); + newtCenteredWindow(self->width, self->height, title); self->form = newt_form__new(); if (self->form == NULL) return -1; - self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, + self->sb = newtVerticalScrollbar(self->width, 0, self->height, HE_COLORSET_NORMAL, HE_COLORSET_SELECTED); if (self->sb == NULL) -- cgit v1.2.3-18-g5258 From 0f0cbf7aa3d3460a3eb201a772326739a0c0210a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 26 Jul 2010 17:13:40 -0300 Subject: perf ui: New hists tree widget The stock newt checkbox tree widget we were using was not really suitable for hist entry + callchain browsing. The problems with it were manifold: - We needed to traverse the whole hist_entry rb_tree to add each entry + callchains beforehand. - No control over the colors used for each row So a new tree widget, based mostly on slang, was written. It extends the ui_browser class already used for annotate to allow the user to fold/unfold branches in the callchains tree, using extra fields in the symbol_map class that is embedded in hist_entry and callchain_node instances to store the folding state and when changing this state calculates the number of rows that are produced when showing a particular hist_entry instance. This greatly speeds up browsing as we don't have to upfront touch all the entries and only calculate callchain related operations when some callchain branch is actually unfolded. The memory footprint is also reduced as the data structure is not duplicated, just some extra fields for controling callchain state and to simplify the process of seeking thru entries (nr_rows, row_offset) were added. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 3 + tools/perf/util/newt.c | 972 ++++++++++++++++++++++++++++++++--------------- tools/perf/util/sort.h | 12 + tools/perf/util/symbol.h | 2 + 4 files changed, 678 insertions(+), 311 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f93095ffaab..d0f07f7bdf1 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -885,6 +885,9 @@ static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, return; ++self->nr_entries; + if (h->ms.unfolded) + self->nr_entries += h->nr_rows; + h->row_offset = 0; self->stats.total_period += h->period; self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index cdd958eb68c..28f74eb8fe7 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -509,38 +509,6 @@ static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) return 0; } -/* - * When debugging newt problems it was useful to be able to "unroll" - * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate - * a source file with the sequence of calls to these methods, to then - * tweak the arrays to get the intended results, so I'm keeping this code - * here, may be useful again in the future. - */ -#undef NEWT_DEBUG - -static void newt_checkbox_tree__add(newtComponent tree, const char *str, - void *priv, int *indexes) -{ -#ifdef NEWT_DEBUG - /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */ - int i = 0, len = 40 - strlen(str); - - fprintf(stderr, - "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ", - len, len, " ", str, priv); - while (indexes[i] != NEWT_ARG_LAST) { - if (indexes[i] != NEWT_ARG_APPEND) - fprintf(stderr, " %d,", indexes[i]); - else - fprintf(stderr, " %s,", "NEWT_ARG_APPEND"); - ++i; - } - fprintf(stderr, " %s", " NEWT_ARG_LAST);\n"); - fflush(stderr); -#endif - newtCheckboxTreeAddArray(tree, str, priv, 0, indexes); -} - static char *callchain_list__sym_name(struct callchain_list *self, char *bf, size_t bfsize) { @@ -576,147 +544,6 @@ static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self return row; } -static void __callchain__append_graph_browser(struct callchain_node *self, - newtComponent tree, u64 total, - int *indexes, int depth) -{ - struct rb_node *node; - u64 new_total, remaining; - int idx = 0; - - if (callchain_param.mode == CHAIN_GRAPH_REL) - new_total = self->children_hit; - else - new_total = total; - - remaining = new_total; - node = rb_first(&self->rb_root); - while (node) { - struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); - struct rb_node *next = rb_next(node); - u64 cumul = cumul_hits(child); - struct callchain_list *chain; - int first = true, printed = 0; - int chain_idx = -1; - remaining -= cumul; - - indexes[depth] = NEWT_ARG_APPEND; - indexes[depth + 1] = NEWT_ARG_LAST; - - list_for_each_entry(chain, &child->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], - *alloc_str = NULL; - const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - - if (first) { - double percent = cumul * 100.0 / new_total; - - first = false; - if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) - str = "Not enough memory!"; - else - str = alloc_str; - } else { - indexes[depth] = idx; - indexes[depth + 1] = NEWT_ARG_APPEND; - indexes[depth + 2] = NEWT_ARG_LAST; - ++chain_idx; - } - newt_checkbox_tree__add(tree, str, &chain->ms, indexes); - free(alloc_str); - ++printed; - } - - indexes[depth] = idx; - if (chain_idx != -1) - indexes[depth + 1] = chain_idx; - if (printed != 0) - ++idx; - __callchain__append_graph_browser(child, tree, new_total, indexes, - depth + (chain_idx != -1 ? 2 : 1)); - node = next; - } -} - -static void callchain__append_graph_browser(struct callchain_node *self, - newtComponent tree, u64 total, - int *indexes, int parent_idx) -{ - struct callchain_list *chain; - int i = 0; - - indexes[1] = NEWT_ARG_APPEND; - indexes[2] = NEWT_ARG_LAST; - - list_for_each_entry(chain, &self->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], *str; - - if (chain->ip >= PERF_CONTEXT_MAX) - continue; - - if (!i++ && sort__first_dimension == SORT_SYM) - continue; - - str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - newt_checkbox_tree__add(tree, str, &chain->ms, indexes); - } - - indexes[1] = parent_idx; - indexes[2] = NEWT_ARG_APPEND; - indexes[3] = NEWT_ARG_LAST; - __callchain__append_graph_browser(self, tree, total, indexes, 2); -} - -static void hist_entry__append_callchain_browser(struct hist_entry *self, - newtComponent tree, u64 total, int parent_idx) -{ - struct rb_node *rb_node; - int indexes[1024] = { [0] = parent_idx, }; - int idx = 0; - struct callchain_node *chain; - - rb_node = rb_first(&self->sorted_chain); - while (rb_node) { - chain = rb_entry(rb_node, struct callchain_node, rb_node); - switch (callchain_param.mode) { - case CHAIN_FLAT: - break; - case CHAIN_GRAPH_ABS: /* falldown */ - case CHAIN_GRAPH_REL: - callchain__append_graph_browser(chain, tree, total, indexes, idx++); - break; - case CHAIN_NONE: - default: - break; - } - rb_node = rb_next(rb_node); - } -} - -static size_t hist_entry__append_browser(struct hist_entry *self, - newtComponent tree, - struct hists *hists) -{ - char s[256]; - size_t ret; - - if (symbol_conf.exclude_other && !self->parent) - return 0; - - ret = hist_entry__snprintf(self, s, sizeof(s), hists, NULL, - false, 0, false, hists->stats.total_period); - if (symbol_conf.use_callchain) { - int indexes[2]; - - indexes[0] = NEWT_ARG_APPEND; - indexes[1] = NEWT_ARG_LAST; - newt_checkbox_tree__add(tree, s, &self->ms, indexes); - } else - newtListboxAppendEntry(tree, s, &self->ms); - - return ret; -} - int hist_entry__tui_annotate(struct hist_entry *self) { struct ui_browser browser; @@ -764,157 +591,48 @@ int hist_entry__tui_annotate(struct hist_entry *self) return ret; } -static const void *newt__symbol_tree_get_current(newtComponent self) -{ - if (symbol_conf.use_callchain) - return newtCheckboxTreeGetCurrent(self); - return newtListboxGetCurrent(self); -} - -static void hist_browser__selection(newtComponent self, void *data) -{ - const struct map_symbol **symbol_ptr = data; - *symbol_ptr = newt__symbol_tree_get_current(self); -} - struct hist_browser { - newtComponent form, tree; - const struct map_symbol *selection; + struct ui_browser b; + struct hists *hists; + struct hist_entry *he_selection; + struct map_symbol *selection; }; -static struct hist_browser *hist_browser__new(void) +static void hist_browser__reset(struct hist_browser *self); +static int hist_browser__run(struct hist_browser *self, const char *title, + struct newtExitStruct *es); +static unsigned int hist_browser__refresh_entries(struct ui_browser *self); +static void ui_browser__hists_seek(struct ui_browser *self, + off_t offset, int whence); + +static struct hist_browser *hist_browser__new(struct hists *hists) { - struct hist_browser *self = malloc(sizeof(*self)); + struct hist_browser *self = zalloc(sizeof(*self)); - if (self != NULL) - self->form = NULL; + if (self) { + self->hists = hists; + self->b.refresh_entries = hist_browser__refresh_entries; + self->b.seek = ui_browser__hists_seek; + } return self; } static void hist_browser__delete(struct hist_browser *self) { - newtFormDestroy(self->form); + newtFormDestroy(self->b.form); newtPopWindow(); free(self); } -static int hist_browser__populate(struct hist_browser *self, struct hists *hists, - const char *title) -{ - int max_len = 0, idx, cols, rows; - struct ui_progress *progress; - struct rb_node *nd; - u64 curr_hist = 0; - char seq[] = ".", unit; - char str[256]; - unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; - - if (self->form) { - newtFormDestroy(self->form); - newtPopWindow(); - } - - nr_events = convert_unit(nr_events, &unit); - snprintf(str, sizeof(str), "Events: %lu%c ", - nr_events, unit); - newtDrawRootText(0, 0, str); - - newtGetScreenSize(NULL, &rows); - - if (symbol_conf.use_callchain) - self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, - NEWT_FLAG_SCROLL); - else - self->tree = newtListbox(0, 0, rows - 5, - (NEWT_FLAG_SCROLL | - NEWT_FLAG_RETURNEXIT)); - - newtComponentAddCallback(self->tree, hist_browser__selection, - &self->selection); - - progress = ui_progress__new("Adding entries to the browser...", - hists->nr_entries); - if (progress == NULL) - return -1; - - idx = 0; - for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - int len; - - if (h->filtered) - continue; - - len = hist_entry__append_browser(h, self->tree, hists); - if (len > max_len) - max_len = len; - if (symbol_conf.use_callchain) - hist_entry__append_callchain_browser(h, self->tree, - hists->stats.total_period, idx++); - ++curr_hist; - if (curr_hist % 5) - ui_progress__update(progress, curr_hist); - } - - ui_progress__delete(progress); - - newtGetScreenSize(&cols, &rows); - - if (max_len > cols) - max_len = cols - 3; - - if (!symbol_conf.use_callchain) - newtListboxSetWidth(self->tree, max_len); - - newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), - rows - 5, title); - self->form = newt_form__new(); - if (self->form == NULL) - return -1; - - newtFormAddHotKey(self->form, 'A'); - newtFormAddHotKey(self->form, 'a'); - newtFormAddHotKey(self->form, 'D'); - newtFormAddHotKey(self->form, 'd'); - newtFormAddHotKey(self->form, 'T'); - newtFormAddHotKey(self->form, 't'); - newtFormAddHotKey(self->form, '?'); - newtFormAddHotKey(self->form, 'H'); - newtFormAddHotKey(self->form, 'h'); - newtFormAddHotKey(self->form, NEWT_KEY_F1); - newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); - newtFormAddHotKey(self->form, NEWT_KEY_TAB); - newtFormAddHotKey(self->form, NEWT_KEY_UNTAB); - newtFormAddComponents(self->form, self->tree, NULL); - self->selection = newt__symbol_tree_get_current(self->tree); - - return 0; -} - static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) { - int *indexes; - - if (!symbol_conf.use_callchain) - goto out; - - indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection); - if (indexes) { - bool is_hist_entry = indexes[1] == NEWT_ARG_LAST; - free(indexes); - if (is_hist_entry) - goto out; - } - return NULL; -out: - return container_of(self->selection, struct hist_entry, ms); + return self->he_selection; } static struct thread *hist_browser__selected_thread(struct hist_browser *self) { - struct hist_entry *he = hist_browser__selected_entry(self); - return he ? he->thread : NULL; + return self->he_selection->thread; } static int hist_browser__title(char *bf, size_t size, const char *ev_name, @@ -936,7 +654,7 @@ static int hist_browser__title(char *bf, size_t size, const char *ev_name, int hists__browse(struct hists *self, const char *helpline, const char *ev_name) { - struct hist_browser *browser = hist_browser__new(); + struct hist_browser *browser = hist_browser__new(self); struct pstack *fstack; const struct thread *thread_filter = NULL; const struct dso *dso_filter = NULL; @@ -955,8 +673,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) hist_browser__title(msg, sizeof(msg), ev_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, self, msg) < 0) - goto out_free_stack; while (1) { const struct thread *thread; @@ -965,7 +681,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) int nr_options = 0, choice = 0, i, annotate = -2, zoom_dso = -2, zoom_thread = -2; - newtFormRun(browser->form, &es); + if (hist_browser__run(browser, msg, &es)) + break; thread = hist_browser__selected_thread(browser); dso = browser->selection->map ? browser->selection->map->dso : NULL; @@ -1100,8 +817,7 @@ zoom_out_dso: hists__filter_by_dso(self, dso_filter); hist_browser__title(msg, sizeof(msg), ev_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, self, msg) < 0) - goto out; + hist_browser__reset(browser); } else if (choice == zoom_thread) { zoom_thread: if (thread_filter) { @@ -1119,8 +835,7 @@ zoom_out_thread: hists__filter_by_thread(self, thread_filter); hist_browser__title(msg, sizeof(msg), ev_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, self, msg) < 0) - goto out; + hist_browser__reset(browser); } } out_free_stack: @@ -1207,3 +922,638 @@ void exit_browser(bool wait_for_ok) newtFinished(); } } + +static void hist_browser__refresh_dimensions(struct hist_browser *self) +{ + /* 3 == +/- toggle symbol before actual hist_entry rendering */ + self->b.width = 3 + (hists__sort_list_width(self->hists) + + sizeof("[k]")); +} + +static void hist_browser__reset(struct hist_browser *self) +{ + self->b.nr_entries = self->hists->nr_entries; + hist_browser__refresh_dimensions(self); + ui_browser__reset_index(&self->b); +} + +static char tree__folded_sign(bool unfolded) +{ + return unfolded ? '-' : '+'; +} + +static char map_symbol__folded(const struct map_symbol *self) +{ + return self->has_children ? tree__folded_sign(self->unfolded) : ' '; +} + +static char hist_entry__folded(const struct hist_entry *self) +{ + return map_symbol__folded(&self->ms); +} + +static char callchain_list__folded(const struct callchain_list *self) +{ + return map_symbol__folded(&self->ms); +} + +static bool map_symbol__toggle_fold(struct map_symbol *self) +{ + if (!self->has_children) + return false; + + self->unfolded = !self->unfolded; + return true; +} + +#define LEVEL_OFFSET_STEP 3 + +static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, + struct callchain_node *chain_node, + u64 total, int level, + unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct rb_node *node; + int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; + u64 new_total, remaining; + + if (callchain_param.mode == CHAIN_GRAPH_REL) + new_total = chain_node->children_hit; + else + new_total = total; + + remaining = new_total; + node = rb_first(&chain_node->rb_root); + while (node) { + struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); + struct rb_node *next = rb_next(node); + u64 cumul = cumul_hits(child); + struct callchain_list *chain; + char folded_sign = ' '; + int first = true; + int extra_offset = 0; + + remaining -= cumul; + + list_for_each_entry(chain, &child->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; + const char *str; + int color; + bool was_first = first; + + if (first) { + first = false; + chain->ms.has_children = chain->list.next != &child->val || + rb_first(&child->rb_root) != NULL; + } else { + extra_offset = LEVEL_OFFSET_STEP; + chain->ms.has_children = chain->list.next == &child->val && + rb_first(&child->rb_root) != NULL; + } + + folded_sign = callchain_list__folded(chain); + if (*row_offset != 0) { + --*row_offset; + goto do_next; + } + + alloc_str = NULL; + str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + if (was_first) { + double percent = cumul * 100.0 / new_total; + + if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) + str = "Not enough memory!"; + else + str = alloc_str; + } + + color = HE_COLORSET_NORMAL; + width = self->b.width - (offset + extra_offset + 2); + if (ui_browser__is_current_entry(&self->b, row)) { + self->selection = &chain->ms; + color = HE_COLORSET_SELECTED; + *is_current_entry = true; + } + + SLsmg_set_color(color); + SLsmg_gotorc(self->b.top + row, self->b.left); + slsmg_write_nstring(" ", offset + extra_offset); + slsmg_printf("%c ", folded_sign); + slsmg_write_nstring(str, width); + free(alloc_str); + + if (++row == self->b.height) + goto out; +do_next: + if (folded_sign == '+') + break; + } + + if (folded_sign == '-') { + const int new_level = level + (extra_offset ? 2 : 1); + row += hist_browser__show_callchain_node_rb_tree(self, child, new_total, + new_level, row, row_offset, + is_current_entry); + } + if (row == self->b.height) + goto out; + node = next; + } +out: + return row - first_row; +} + +static int hist_browser__show_callchain_node(struct hist_browser *self, + struct callchain_node *node, + int level, unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct callchain_list *chain; + int first_row = row, + offset = level * LEVEL_OFFSET_STEP, + width = self->b.width - offset; + char folded_sign = ' '; + + list_for_each_entry(chain, &node->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], *s; + int color; + /* + * FIXME: This should be moved to somewhere else, + * probably when the callchain is created, so as not to + * traverse it all over again + */ + chain->ms.has_children = rb_first(&node->rb_root) != NULL; + folded_sign = callchain_list__folded(chain); + + if (*row_offset != 0) { + --*row_offset; + continue; + } + + color = HE_COLORSET_NORMAL; + if (ui_browser__is_current_entry(&self->b, row)) { + self->selection = &chain->ms; + color = HE_COLORSET_SELECTED; + *is_current_entry = true; + } + + s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + SLsmg_gotorc(self->b.top + row, self->b.left); + SLsmg_set_color(color); + slsmg_write_nstring(" ", offset); + slsmg_printf("%c ", folded_sign); + slsmg_write_nstring(s, width - 2); + + if (++row == self->b.height) + goto out; + } + + if (folded_sign == '-') + row += hist_browser__show_callchain_node_rb_tree(self, node, + self->hists->stats.total_period, + level + 1, row, + row_offset, + is_current_entry); +out: + return row - first_row; +} + +static int hist_browser__show_callchain(struct hist_browser *self, + struct rb_root *chain, + int level, unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct rb_node *nd; + int first_row = row; + + for (nd = rb_first(chain); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + + row += hist_browser__show_callchain_node(self, node, level, + row, row_offset, + is_current_entry); + if (row == self->b.height) + break; + } + + return row - first_row; +} + +static int hist_browser__show_entry(struct hist_browser *self, + struct hist_entry *entry, + unsigned short row) +{ + char s[256]; + double percent; + int printed = 0; + int color, width = self->b.width; + char folded_sign = ' '; + bool current_entry = ui_browser__is_current_entry(&self->b, row); + off_t row_offset = entry->row_offset; + + if (current_entry) { + self->he_selection = entry; + self->selection = &entry->ms; + } + + if (symbol_conf.use_callchain) { + entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain); + folded_sign = hist_entry__folded(entry); + } + + if (row_offset == 0) { + hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, + 0, false, self->hists->stats.total_period); + percent = (entry->period * 100.0) / self->hists->stats.total_period; + + color = HE_COLORSET_SELECTED; + if (!current_entry) { + if (percent >= MIN_RED) + color = HE_COLORSET_TOP; + else if (percent >= MIN_GREEN) + color = HE_COLORSET_MEDIUM; + else + color = HE_COLORSET_NORMAL; + } + + SLsmg_set_color(color); + SLsmg_gotorc(self->b.top + row, self->b.left); + if (symbol_conf.use_callchain) { + slsmg_printf("%c ", folded_sign); + width -= 2; + } + slsmg_write_nstring(s, width); + ++row; + ++printed; + } else + --row_offset; + + if (folded_sign == '-' && row != self->b.height) { + printed += hist_browser__show_callchain(self, &entry->sorted_chain, + 1, row, &row_offset, + ¤t_entry); + if (current_entry) + self->he_selection = entry; + } + + return printed; +} + +static unsigned int hist_browser__refresh_entries(struct ui_browser *self) +{ + unsigned row = 0; + struct rb_node *nd; + struct hist_browser *hb = container_of(self, struct hist_browser, b); + + if (self->first_visible_entry == NULL) + self->first_visible_entry = rb_first(&hb->hists->entries); + + for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (h->filtered) + continue; + + row += hist_browser__show_entry(hb, h, row); + if (row == self->height) + break; + } + + return row; +} + +static void callchain_node__init_have_children_rb_tree(struct callchain_node *self) +{ + struct rb_node *nd = rb_first(&self->rb_root); + + for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { + struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); + struct callchain_list *chain; + int first = true; + + list_for_each_entry(chain, &child->val, list) { + if (first) { + first = false; + chain->ms.has_children = chain->list.next != &child->val || + rb_first(&child->rb_root) != NULL; + } else + chain->ms.has_children = chain->list.next == &child->val && + rb_first(&child->rb_root) != NULL; + } + + callchain_node__init_have_children_rb_tree(child); + } +} + +static void callchain_node__init_have_children(struct callchain_node *self) +{ + struct callchain_list *chain; + + list_for_each_entry(chain, &self->val, list) + chain->ms.has_children = rb_first(&self->rb_root) != NULL; + + callchain_node__init_have_children_rb_tree(self); +} + +static void callchain__init_have_children(struct rb_root *self) +{ + struct rb_node *nd; + + for (nd = rb_first(self); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + callchain_node__init_have_children(node); + } +} + +static void hist_entry__init_have_children(struct hist_entry *self) +{ + if (!self->init_have_children) { + callchain__init_have_children(&self->sorted_chain); + self->init_have_children = true; + } +} + +static struct rb_node *hists__filter_entries(struct rb_node *nd) +{ + while (nd != NULL) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + if (!h->filtered) + return nd; + + nd = rb_next(nd); + } + + return NULL; +} + +static struct rb_node *hists__filter_prev_entries(struct rb_node *nd) +{ + while (nd != NULL) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + if (!h->filtered) + return nd; + + nd = rb_prev(nd); + } + + return NULL; +} + +static void ui_browser__hists_seek(struct ui_browser *self, + off_t offset, int whence) +{ + struct hist_entry *h; + struct rb_node *nd; + bool first = true; + + switch (whence) { + case SEEK_SET: + nd = hists__filter_entries(rb_first(self->entries)); + break; + case SEEK_CUR: + nd = self->first_visible_entry; + goto do_offset; + case SEEK_END: + nd = hists__filter_prev_entries(rb_last(self->entries)); + first = false; + break; + default: + return; + } + + /* + * Moves not relative to the first visible entry invalidates its + * row_offset: + */ + h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node); + h->row_offset = 0; + + /* + * Here we have to check if nd is expanded (+), if it is we can't go + * the next top level hist_entry, instead we must compute an offset of + * what _not_ to show and not change the first visible entry. + * + * This offset increments when we are going from top to bottom and + * decreases when we're going from bottom to top. + * + * As we don't have backpointers to the top level in the callchains + * structure, we need to always print the whole hist_entry callchain, + * skipping the first ones that are before the first visible entry + * and stop when we printed enough lines to fill the screen. + */ +do_offset: + if (offset > 0) { + do { + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) { + u16 remaining = h->nr_rows - h->row_offset; + if (offset > remaining) { + offset -= remaining; + h->row_offset = 0; + } else { + h->row_offset += offset; + offset = 0; + self->first_visible_entry = nd; + break; + } + } + nd = hists__filter_entries(rb_next(nd)); + if (nd == NULL) + break; + --offset; + self->first_visible_entry = nd; + } while (offset != 0); + } else if (offset < 0) { + while (1) { + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) { + if (first) { + if (-offset > h->row_offset) { + offset += h->row_offset; + h->row_offset = 0; + } else { + h->row_offset += offset; + offset = 0; + self->first_visible_entry = nd; + break; + } + } else { + if (-offset > h->nr_rows) { + offset += h->nr_rows; + h->row_offset = 0; + } else { + h->row_offset = h->nr_rows + offset; + offset = 0; + self->first_visible_entry = nd; + break; + } + } + } + + nd = hists__filter_prev_entries(rb_prev(nd)); + if (nd == NULL) + break; + ++offset; + self->first_visible_entry = nd; + if (offset == 0) { + /* + * Last unfiltered hist_entry, check if it is + * unfolded, if it is then we should have + * row_offset at its last entry. + */ + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) + h->row_offset = h->nr_rows; + break; + } + first = false; + } + } else { + self->first_visible_entry = nd; + h = rb_entry(nd, struct hist_entry, rb_node); + h->row_offset = 0; + } +} + +static int callchain_node__count_rows_rb_tree(struct callchain_node *self) +{ + int n = 0; + struct rb_node *nd; + + for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { + struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); + struct callchain_list *chain; + char folded_sign = ' '; /* No children */ + + list_for_each_entry(chain, &child->val, list) { + ++n; + /* We need this because we may not have children */ + folded_sign = callchain_list__folded(chain); + if (folded_sign == '+') + break; + } + + if (folded_sign == '-') /* Have children and they're unfolded */ + n += callchain_node__count_rows_rb_tree(child); + } + + return n; +} + +static int callchain_node__count_rows(struct callchain_node *node) +{ + struct callchain_list *chain; + bool unfolded = false; + int n = 0; + + list_for_each_entry(chain, &node->val, list) { + ++n; + unfolded = chain->ms.unfolded; + } + + if (unfolded) + n += callchain_node__count_rows_rb_tree(node); + + return n; +} + +static int callchain__count_rows(struct rb_root *chain) +{ + struct rb_node *nd; + int n = 0; + + for (nd = rb_first(chain); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + n += callchain_node__count_rows(node); + } + + return n; +} + +static bool hist_browser__toggle_fold(struct hist_browser *self) +{ + if (map_symbol__toggle_fold(self->selection)) { + struct hist_entry *he = self->he_selection; + + hist_entry__init_have_children(he); + self->hists->nr_entries -= he->nr_rows; + + if (he->ms.unfolded) + he->nr_rows = callchain__count_rows(&he->sorted_chain); + else + he->nr_rows = 0; + self->hists->nr_entries += he->nr_rows; + self->b.nr_entries = self->hists->nr_entries; + + return true; + } + + /* If it doesn't have children, no toggling performed */ + return false; +} + +static int hist_browser__run(struct hist_browser *self, const char *title, + struct newtExitStruct *es) +{ + char str[256], unit; + unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; + + self->b.entries = &self->hists->entries; + self->b.nr_entries = self->hists->nr_entries; + + hist_browser__refresh_dimensions(self); + + nr_events = convert_unit(nr_events, &unit); + snprintf(str, sizeof(str), "Events: %lu%c ", + nr_events, unit); + newtDrawRootText(0, 0, str); + + if (ui_browser__show(&self->b, title) < 0) + return -1; + + newtFormAddHotKey(self->b.form, 'A'); + newtFormAddHotKey(self->b.form, 'a'); + newtFormAddHotKey(self->b.form, '?'); + newtFormAddHotKey(self->b.form, 'h'); + newtFormAddHotKey(self->b.form, 'H'); + newtFormAddHotKey(self->b.form, 'd'); + + newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); + newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); + + while (1) { + ui_browser__run(&self->b, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + switch (es->u.key) { + case 'd': { /* Debug */ + static int seq; + struct hist_entry *h = rb_entry(self->b.first_visible_entry, + struct hist_entry, rb_node); + ui_helpline__pop(); + ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", + seq++, self->b.nr_entries, + self->hists->nr_entries, + self->b.height, + self->b.index, + self->b.first_visible_entry_idx, + h->row_offset, h->nr_rows); + } + continue; + case NEWT_KEY_ENTER: + if (hist_browser__toggle_fold(self)) + break; + /* fall thru */ + default: + return 0; + } + } + return 0; +} diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 03a1722e6d0..46e531d09e8 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -38,6 +38,12 @@ extern struct sort_entry sort_sym; extern struct sort_entry sort_parent; extern enum sort_type sort__first_dimension; +/** + * struct hist_entry - histogram entry + * + * @row_offset - offset from the first callchain expanded to appear on screen + * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding + */ struct hist_entry { struct rb_node rb_node; u64 period; @@ -50,6 +56,12 @@ struct hist_entry { u64 ip; s32 cpu; u32 nr_events; + + /* XXX These two should move to some tree widget lib */ + u16 row_offset; + u16 nr_rows; + + bool init_have_children; char level; u8 filtered; struct symbol *parent; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index d436ee3d3a7..bb1aab9fa34 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -102,6 +102,8 @@ struct ref_reloc_sym { struct map_symbol { struct map *map; struct symbol *sym; + bool unfolded; + bool has_children; }; struct addr_location { -- cgit v1.2.3-18-g5258 From 361d13462585474267a0c41e956f1a1c19a93f17 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 27 Jul 2010 16:40:02 +0100 Subject: perf report: Don't abbreviate file paths relative to the cwd This avoids around some problems where the full path is executables and DSOs it needed for finding debug symbols on platforms with separated debug symbol files such as Ubuntu. This is simpler than tracking an extra name for each image. The only impact should be that paths in verbose output from the perf tools become absolute, instead of relative to . LKML-Reference: Signed-off-by: Dave Martin Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 2 +- tools/perf/util/map.c | 22 +--------------------- tools/perf/util/map.h | 2 +- 3 files changed, 3 insertions(+), 23 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 121339f4360..5b81bb29a07 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -517,7 +517,7 @@ int event__process_mmap(event_t *self, struct perf_session *session) map = map__new(&machine->user_dsos, self->mmap.start, self->mmap.len, self->mmap.pgoff, self->mmap.pid, self->mmap.filename, - MAP__FUNCTION, session->cwd, session->cwdlen); + MAP__FUNCTION); if (thread == NULL || map == NULL) goto out_problem; diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index e672f2fef65..37cab903853 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -17,16 +17,6 @@ static inline int is_anon_memory(const char *filename) return strcmp(filename, "//anon") == 0; } -static int strcommon(const char *pathname, char *cwd, int cwdlen) -{ - int n = 0; - - while (n < cwdlen && pathname[n] == cwd[n]) - ++n; - - return n; -} - void map__init(struct map *self, enum map_type type, u64 start, u64 end, u64 pgoff, struct dso *dso) { @@ -43,7 +33,7 @@ void map__init(struct map *self, enum map_type type, struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, u64 pgoff, u32 pid, char *filename, - enum map_type type, char *cwd, int cwdlen) + enum map_type type) { struct map *self = malloc(sizeof(*self)); @@ -52,16 +42,6 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, struct dso *dso; int anon; - if (cwd) { - int n = strcommon(filename, cwd, cwdlen); - - if (n == cwdlen) { - snprintf(newfilename, sizeof(newfilename), - ".%s", filename + n); - filename = newfilename; - } - } - anon = is_anon_memory(filename); if (anon) { diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index f3913451282..3b2f706c0ba 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -106,7 +106,7 @@ void map__init(struct map *self, enum map_type type, u64 start, u64 end, u64 pgoff, struct dso *dso); struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, u64 pgoff, u32 pid, char *filename, - enum map_type type, char *cwd, int cwdlen); + enum map_type type); void map__delete(struct map *self); struct map *map__clone(struct map *self); int map__overlap(struct map *l, struct map *r); -- cgit v1.2.3-18-g5258 From 88ca895dd4e0e64ebd942adb7925fa60ca5b2a98 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 27 Jul 2010 11:46:12 -0300 Subject: perf tools: Remove unneeded code for tracking the cwd in perf sessions Tidy-up patch to remove some code and struct perf_session data members which are no longer needed due to the previous patch: "perf tools: Don't abbreviate file paths relative to the cwd". LKML-Reference: Signed-off-by: Dave Martin Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-buildid-list.c | 4 +--- tools/perf/builtin-diff.c | 2 -- tools/perf/builtin-report.c | 2 -- tools/perf/util/session.c | 22 +--------------------- tools/perf/util/symbol.h | 1 - 5 files changed, 2 insertions(+), 29 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 99890728409..44a47e13bd6 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -43,10 +43,8 @@ static int __cmd_buildid_list(void) if (session == NULL) return -1; - if (with_hits) { - symbol_conf.full_paths = true; + if (with_hits) perf_session__process_events(session, &build_id__mark_dso_hit_ops); - } perf_session__fprintf_dsos_buildid(session, stdout, with_hits); diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 39e6627ebb9..fca1d440291 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -180,8 +180,6 @@ static const struct option options[] = { OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, "load module symbols - WARNING: use only with -k and LIVE kernel"), - OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, - "Don't shorten the pathnames taking into account the cwd"), OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", "only consider symbols in these dsos"), OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index ce42bbaa252..2f4b92925b2 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -441,8 +441,6 @@ static const struct option options[] = { "pretty printing style key: normal raw"), OPT_STRING('s', "sort", &sort_order, "key[,key2...]", "sort by key(s): pid, comm, dso, symbol, parent"), - OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, - "Don't shorten the pathnames taking into account the cwd"), OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, "Show sample percentage for different cpu modes"), OPT_STRING('p', "parent", &parent_pattern, "regex", diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 030791870e3..8cbea122e34 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -96,8 +96,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->hists_tree = RB_ROOT; self->last_match = NULL; self->mmap_window = 32; - self->cwd = NULL; - self->cwdlen = 0; self->machines = RB_ROOT; self->repipe = repipe; INIT_LIST_HEAD(&self->ordered_samples.samples_head); @@ -130,7 +128,6 @@ void perf_session__delete(struct perf_session *self) { perf_header__exit(&self->header); close(self->fd); - free(self->cwd); free(self); } @@ -832,23 +829,6 @@ int perf_session__process_events(struct perf_session *self, if (perf_session__register_idle_thread(self) == NULL) return -ENOMEM; - if (!symbol_conf.full_paths) { - char bf[PATH_MAX]; - - if (getcwd(bf, sizeof(bf)) == NULL) { - err = -errno; -out_getcwd_err: - pr_err("failed to get the current directory\n"); - goto out_err; - } - self->cwd = strdup(bf); - if (self->cwd == NULL) { - err = -ENOMEM; - goto out_getcwd_err; - } - self->cwdlen = strlen(self->cwd); - } - if (!self->fd_pipe) err = __perf_session__process_events(self, self->header.data_offset, @@ -856,7 +836,7 @@ out_getcwd_err: self->size, ops); else err = __perf_session__process_pipe_events(self, ops); -out_err: + return err; } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index bb1aab9fa34..6452a07425a 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -68,7 +68,6 @@ struct symbol_conf { show_nr_samples, use_callchain, exclude_other, - full_paths, show_cpu_utilization; const char *vmlinux_name, *source_prefix, -- cgit v1.2.3-18-g5258 From 8c31a1e049a0c26f78558d7cc5a9ab6956c86694 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 28 Jul 2010 11:30:10 -0300 Subject: perf man pages: Fix cut'n'paste error We remove files _from_ the cache. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-buildid-cache.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt index 5d1a9500277..c1057701a7d 100644 --- a/tools/perf/Documentation/perf-buildid-cache.txt +++ b/tools/perf/Documentation/perf-buildid-cache.txt @@ -12,9 +12,9 @@ SYNOPSIS DESCRIPTION ----------- -This command manages the build-id cache. It can add and remove files to the -cache. In the future it should as well purge older entries, set upper limits -for the space used by the cache, etc. +This command manages the build-id cache. It can add and remove files to/from +the cache. In the future it should as well purge older entries, set upper +limits for the space used by the cache, etc. OPTIONS ------- @@ -23,7 +23,7 @@ OPTIONS Add specified file to the cache. -r:: --remove=:: - Remove specified file to the cache. + Remove specified file from the cache. -v:: --verbose:: Be more verbose. -- cgit v1.2.3-18-g5258 From 39d17dacb3c25df878b56aa80a170d6088e041f9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 29 Jul 2010 14:08:55 -0300 Subject: perf record: Release resources at exit So that we can reduce the noise on valgrind when looking for memory leaks. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b93879677cc..5ae0d93d859 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -439,6 +439,7 @@ static void atexit_header(void) process_buildids(); perf_header__write(&session->header, output, true); + perf_session__delete(session); } } @@ -558,12 +559,15 @@ static int __cmd_record(int argc, const char **argv) if (!file_new) { err = perf_header__read(session, output); if (err < 0) - return err; + goto out_delete_session; } if (have_tracepoints(attrs, nr_counters)) perf_header__set_feat(&session->header, HEADER_TRACE_INFO); + /* + * perf_session__delete(session) will be called at atexit_header() + */ atexit(atexit_header); if (forks) { @@ -768,6 +772,10 @@ static int __cmd_record(int argc, const char **argv) bytes_written / 24); return 0; + +out_delete_session: + perf_session__delete(session); + return err; } static const char * const record_usage[] = { @@ -824,7 +832,7 @@ static const struct option options[] = { int cmd_record(int argc, const char **argv, const char *prefix __used) { - int i,j; + int i, j, err = -ENOMEM; argc = parse_options(argc, argv, options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); @@ -873,13 +881,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) for (j = 0; j < MAX_COUNTERS; j++) { fd[i][j] = malloc(sizeof(int)*thread_num); if (!fd[i][j]) - return -ENOMEM; + goto out_free_fd; } } event_array = malloc( sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); if (!event_array) - return -ENOMEM; + goto out_free_fd; if (user_interval != ULLONG_MAX) default_interval = user_interval; @@ -895,8 +903,20 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) default_interval = freq; } else { fprintf(stderr, "frequency and count are zero, aborting\n"); - exit(EXIT_FAILURE); + err = -EINVAL; + goto out_free_event_array; } - return __cmd_record(argc, argv); + err = __cmd_record(argc, argv); + +out_free_event_array: + free(event_array); +out_free_fd: + for (i = 0; i < MAX_NR_CPUS; i++) { + for (j = 0; j < MAX_COUNTERS; j++) + free(fd[i][j]); + } + free(all_tids); + all_tids = NULL; + return err; } -- cgit v1.2.3-18-g5258 From 6e406257b3794009e3b7a6d48b54beb547719565 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 29 Jul 2010 15:11:30 -0300 Subject: perf symbols: Precisely specify if dso->{long,short}_name should be freed Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/symbol.c | 5 ++++- tools/perf/util/symbol.h | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 5b81bb29a07..8151d23664c 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -456,6 +456,7 @@ static int event__process_kernel_mmap(event_t *self, goto out_problem; map->dso->short_name = name; + map->dso->sname_alloc = 1; map->end = map->start + self->mmap.len; } else if (is_kernel_mmap) { const char *symbol_name = (self->mmap.filename + diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index bc6e7e8c480..242d2b216f4 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -224,7 +224,9 @@ void dso__delete(struct dso *self) int i; for (i = 0; i < MAP__NR_TYPES; ++i) symbols__delete(&self->symbols[i]); - if (self->long_name != self->name) + if (self->sname_alloc) + free((char *)self->short_name); + if (self->lname_alloc) free(self->long_name); free(self); } @@ -1530,6 +1532,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, if (long_name == NULL) goto failure; dso__set_long_name(map->dso, long_name); + map->dso->lname_alloc = 1; dso__kernel_module_get_build_id(map->dso, ""); } } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 6452a07425a..f29f73c2080 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -126,12 +126,14 @@ struct dso { struct list_head node; struct rb_root symbols[MAP__NR_TYPES]; struct rb_root symbol_names[MAP__NR_TYPES]; + enum dso_kernel_type kernel; u8 adjust_symbols:1; u8 slen_calculated:1; u8 has_build_id:1; - enum dso_kernel_type kernel; u8 hit:1; u8 annotate_warned:1; + u8 sname_alloc:1; + u8 lname_alloc:1; unsigned char origin; u8 sorted_by_name; u8 loaded; -- cgit v1.2.3-18-g5258 From 21916c380d93ab59d6d07ee198fb31c8f1338e26 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Fri, 30 Jul 2010 09:08:08 -0300 Subject: perf tools: Factor out buildid reading and make it implicit in dso__load If we have a buildid, then we never want to load an image which has no buildid, or which has a different buildid, so it makes sense for the check to be built into dso__load and not done separately. This is fine for old distros which don't use buildid at all since we do no check in that case. This refactoring also alleviates some subtle race condition issues by not opening ELF images twice to check the buildid and then load the symbols, which could lead to weirdness if an image is replaced under our feet. Signed-off-by: Dave Martin LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 80 ++++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 33 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 242d2b216f4..b812ace91c6 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -26,6 +26,8 @@ #define NT_GNU_BUILD_ID 3 #endif +static bool dso__build_id_equal(const struct dso *self, u8 *build_id); +static int elf_read_build_id(Elf *elf, void *bf, size_t size); static void dsos__add(struct list_head *head, struct dso *dso); static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); static int dso__load_kernel_sym(struct dso *self, struct map *map, @@ -993,6 +995,17 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, goto out_elf_end; } + if (self->has_build_id) { + u8 build_id[BUILD_ID_SIZE]; + + if (elf_read_build_id(elf, build_id, + BUILD_ID_SIZE) != BUILD_ID_SIZE) + goto out_elf_end; + + if (!dso__build_id_equal(self, build_id)) + goto out_elf_end; + } + sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); if (sec == NULL) { sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); @@ -1193,37 +1206,26 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits) */ #define NOTE_ALIGN(n) (((n) + 3) & -4U) -int filename__read_build_id(const char *filename, void *bf, size_t size) +static int elf_read_build_id(Elf *elf, void *bf, size_t size) { - int fd, err = -1; + int err = -1; GElf_Ehdr ehdr; GElf_Shdr shdr; Elf_Data *data; Elf_Scn *sec; Elf_Kind ek; void *ptr; - Elf *elf; if (size < BUILD_ID_SIZE) goto out; - fd = open(filename, O_RDONLY); - if (fd < 0) - goto out; - - elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); - if (elf == NULL) { - pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); - goto out_close; - } - ek = elf_kind(elf); if (ek != ELF_K_ELF) - goto out_elf_end; + goto out; if (gelf_getehdr(elf, &ehdr) == NULL) { pr_err("%s: cannot get elf header.\n", __func__); - goto out_elf_end; + goto out; } sec = elf_section_by_name(elf, &ehdr, &shdr, @@ -1232,12 +1234,12 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) sec = elf_section_by_name(elf, &ehdr, &shdr, ".notes", NULL); if (sec == NULL) - goto out_elf_end; + goto out; } data = elf_getdata(sec, NULL); if (data == NULL) - goto out_elf_end; + goto out; ptr = data->d_buf; while (ptr < (data->d_buf + data->d_size)) { @@ -1259,7 +1261,31 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) } ptr += descsz; } -out_elf_end: + +out: + return err; +} + +int filename__read_build_id(const char *filename, void *bf, size_t size) +{ + int fd, err = -1; + Elf *elf; + + if (size < BUILD_ID_SIZE) + goto out; + + fd = open(filename, O_RDONLY); + if (fd < 0) + goto out; + + elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + if (elf == NULL) { + pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); + goto out_close; + } + + err = elf_read_build_id(elf, bf, size); + elf_end(elf); out_close: close(fd); @@ -1335,7 +1361,6 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) { int size = PATH_MAX; char *name; - u8 build_id[BUILD_ID_SIZE]; int ret = -1; int fd; struct machine *machine; @@ -1382,16 +1407,14 @@ more: self->long_name); break; case DSO__ORIG_BUILDID: - if (filename__read_build_id(self->long_name, build_id, - sizeof(build_id))) { + if (self->has_build_id) { char build_id_hex[BUILD_ID_SIZE * 2 + 1]; - build_id__sprintf(build_id, sizeof(build_id), + build_id__sprintf(self->build_id, + sizeof(self->build_id), build_id_hex); snprintf(name, size, "/usr/lib/debug/.build-id/%.2s/%s.debug", build_id_hex, build_id_hex + 2); - if (self->has_build_id) - goto compare_build_id; break; } self->origin++; @@ -1410,15 +1433,6 @@ more: default: goto out; } - - if (self->has_build_id) { - if (filename__read_build_id(name, build_id, - sizeof(build_id)) < 0) - goto more; -compare_build_id: - if (!dso__build_id_equal(self, build_id)) - goto more; - } open_file: fd = open(name, O_RDONLY); } while (fd < 0); -- cgit v1.2.3-18-g5258 From 8b1389ef93b36621c6acdeb623bd85aee3c405c9 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Fri, 30 Jul 2010 09:36:08 -0300 Subject: perf tools: remove extra build-id check factored into dso__load Signed-off-by: Dave Martin LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b812ace91c6..e0d9480dc37 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -986,12 +986,12 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); if (elf == NULL) { - pr_err("%s: cannot read %s ELF file.\n", __func__, name); + pr_debug("%s: cannot read %s ELF file.\n", __func__, name); goto out_close; } if (gelf_getehdr(elf, &ehdr) == NULL) { - pr_err("%s: cannot get elf header.\n", __func__); + pr_debug("%s: cannot get elf header.\n", __func__); goto out_elf_end; } @@ -1710,30 +1710,6 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, { int err = -1, fd; - if (self->has_build_id) { - u8 build_id[BUILD_ID_SIZE]; - - if (filename__read_build_id(vmlinux, build_id, - sizeof(build_id)) < 0) { - pr_debug("No build_id in %s, ignoring it\n", vmlinux); - return -1; - } - if (!dso__build_id_equal(self, build_id)) { - char expected_build_id[BUILD_ID_SIZE * 2 + 1], - vmlinux_build_id[BUILD_ID_SIZE * 2 + 1]; - - build_id__sprintf(self->build_id, - sizeof(self->build_id), - expected_build_id); - build_id__sprintf(build_id, sizeof(build_id), - vmlinux_build_id); - pr_debug("build_id in %s is %s while expected is %s, " - "ignoring it\n", vmlinux, vmlinux_build_id, - expected_build_id); - return -1; - } - } - fd = open(vmlinux, O_RDONLY); if (fd < 0) return -1; -- cgit v1.2.3-18-g5258 From 6da80ce8c43ddda153208cbb46b75290cf566fac Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Fri, 30 Jul 2010 09:50:09 -0300 Subject: perf symbols: Improve debug image search when loading symbols Changes: * Simplification of the main search loop on dso__load() * Replace the search with a 2-pass search: * First, try to find an image with a proper symtab. * Second, repeat the search, accepting dynsym. A second scan should only ever happen when needed debug images are missing from the buildid cache or stale, i.e., when the cache is out of sync. Currently, the second scan also happens when using separated debug images, since the caching logic doesn't currently know how to cache those. Improvements to the cache behaviour ought to solve that. Signed-off-by: Dave Martin LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 96 ++++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 35 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e0d9480dc37..d99497ec52a 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -966,7 +966,8 @@ static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) } static int dso__load_sym(struct dso *self, struct map *map, const char *name, - int fd, symbol_filter_t filter, int kmodule) + int fd, symbol_filter_t filter, int kmodule, + int want_symtab) { struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; struct map *curr_map = map; @@ -995,6 +996,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, goto out_elf_end; } + /* Always reject images with a mismatched build-id: */ if (self->has_build_id) { u8 build_id[BUILD_ID_SIZE]; @@ -1008,6 +1010,9 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); if (sec == NULL) { + if (want_symtab) + goto out_elf_end; + sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); if (sec == NULL) goto out_elf_end; @@ -1365,6 +1370,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) int fd; struct machine *machine; const char *root_dir; + int want_symtab; dso__set_loaded(self, map->type); @@ -1391,13 +1397,18 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) return ret; } - self->origin = DSO__ORIG_BUILD_ID_CACHE; - if (dso__build_id_filename(self, name, size) != NULL) - goto open_file; -more: - do { - self->origin++; + /* Iterate over candidate debug images. + * On the first pass, only load images if they have a full symtab. + * Failing that, do a second pass where we accept .dynsym also + */ + for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1; + self->origin != DSO__ORIG_NOT_FOUND; + self->origin++) { switch (self->origin) { + case DSO__ORIG_BUILD_ID_CACHE: + if (dso__build_id_filename(self, name, size) == NULL) + continue; + break; case DSO__ORIG_FEDORA: snprintf(name, size, "/usr/lib/debug%s.debug", self->long_name); @@ -1406,19 +1417,20 @@ more: snprintf(name, size, "/usr/lib/debug%s", self->long_name); break; - case DSO__ORIG_BUILDID: - if (self->has_build_id) { - char build_id_hex[BUILD_ID_SIZE * 2 + 1]; - build_id__sprintf(self->build_id, - sizeof(self->build_id), - build_id_hex); - snprintf(name, size, - "/usr/lib/debug/.build-id/%.2s/%s.debug", - build_id_hex, build_id_hex + 2); - break; + case DSO__ORIG_BUILDID: { + char build_id_hex[BUILD_ID_SIZE * 2 + 1]; + + if (!self->has_build_id) + continue; + + build_id__sprintf(self->build_id, + sizeof(self->build_id), + build_id_hex); + snprintf(name, size, + "/usr/lib/debug/.build-id/%.2s/%s.debug", + build_id_hex, build_id_hex + 2); } - self->origin++; - /* Fall thru */ + break; case DSO__ORIG_DSO: snprintf(name, size, "%s", self->long_name); break; @@ -1431,27 +1443,41 @@ more: break; default: - goto out; + /* + * If we wanted a full symtab but no image had one, + * relax our requirements and repeat the search. + */ + if (want_symtab) { + want_symtab = 0; + self->origin = DSO__ORIG_BUILD_ID_CACHE; + } else + continue; } -open_file: + + /* Name is now the name of the next image to try */ fd = open(name, O_RDONLY); - } while (fd < 0); + if (fd < 0) + continue; - ret = dso__load_sym(self, map, name, fd, filter, 0); - close(fd); + ret = dso__load_sym(self, map, name, fd, filter, 0, + want_symtab); + close(fd); - /* - * Some people seem to have debuginfo files _WITHOUT_ debug info!?!? - */ - if (!ret) - goto more; + /* + * Some people seem to have debuginfo files _WITHOUT_ debug + * info!?!? + */ + if (!ret) + continue; - if (ret > 0) { - int nr_plt = dso__synthesize_plt_symbols(self, map, filter); - if (nr_plt > 0) - ret += nr_plt; + if (ret > 0) { + int nr_plt = dso__synthesize_plt_symbols(self, map, filter); + if (nr_plt > 0) + ret += nr_plt; + break; + } } -out: + free(name); if (ret < 0 && strstr(self->name, " (deleted)") != NULL) return 0; @@ -1715,7 +1741,7 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, return -1; dso__set_loaded(self, map->type); - err = dso__load_sym(self, map, vmlinux, fd, filter, 0); + err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0); close(fd); if (err > 0) -- cgit v1.2.3-18-g5258 From 73ae8f85fda49410a59d7b532ce69a0b811ef6d5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 30 Jul 2010 10:06:06 -0300 Subject: perf tui: Make CTRL+Z suspend perf Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 28f74eb8fe7..91de99b5844 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -11,6 +11,7 @@ #define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG #endif #include +#include #include #include #include @@ -891,6 +892,13 @@ static struct newtPercentTreeColors { "blue", "lightgray", }; +static void newt_suspend(void *d __used) +{ + newtSuspend(); + raise(SIGTSTP); + newtResume(); +} + void setup_browser(void) { struct newtPercentTreeColors *c = &defaultPercentTreeColors; @@ -904,6 +912,7 @@ void setup_browser(void) use_browser = 1; newtInit(); newtCls(); + newtSetSuspendCallback(newt_suspend, NULL); ui_helpline__puts(" "); sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); -- cgit v1.2.3-18-g5258 From 0e60836bbd392300198c5c2d918c18845428a1fe Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Thu, 29 Jul 2010 19:43:51 +0530 Subject: perf probe: Rename common fields/functions from kprobe to probe. As a precursor for perf to support uprobes, rename fields/functions that had kprobe in their name but can be shared across perf-kprobes and perf-uprobes to probe. Cc: Ananth N Mavinakayanahalli Cc: Andrew Morton Cc: Christoph Hellwig Cc: "Frank Ch. Eigler" Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jim Keniston Cc: Linus Torvalds Cc: Mark Wielaard Cc: Mathieu Desnoyers Cc: Naren A Devaiah Cc: Oleg Nesterov Cc: "Paul E. McKenney" Cc: Peter Zijlstra Cc: Randy Dunlap Cc: Steven Rostedt LKML-Reference: <20100729141351.GG21723@linux.vnet.ibm.com> Signed-off-by: Srikar Dronamraju Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-probe.c | 1 - tools/perf/util/probe-event.c | 135 +++++++++++++++++++++-------------------- tools/perf/util/probe-event.h | 27 +++------ tools/perf/util/probe-finder.c | 34 +++++------ tools/perf/util/probe-finder.h | 10 +-- 5 files changed, 101 insertions(+), 106 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 54551867e7e..199d5e19554 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -267,4 +267,3 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) } return 0; } - diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 4445a1e7052..2e665cb8405 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -1,5 +1,5 @@ /* - * probe-event.c : perf-probe definition to kprobe_events format converter + * probe-event.c : perf-probe definition to probe_events format converter * * Written by Masami Hiramatsu * @@ -120,8 +120,11 @@ static int open_vmlinux(void) return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); } -/* Convert trace point to probe point with debuginfo */ -static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, +/* + * Convert trace point to probe point with debuginfo + * Currently only handles kprobes. + */ +static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, struct perf_probe_point *pp) { struct symbol *sym; @@ -151,8 +154,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, } /* Try to find perf_probe_event with debuginfo */ -static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs, +static int try_to_find_probe_trace_events(struct perf_probe_event *pev, + struct probe_trace_event **tevs, int max_tevs) { bool need_dwarf = perf_probe_event_need_dwarf(pev); @@ -169,11 +172,11 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, } /* Searching trace events corresponding to probe event */ - ntevs = find_kprobe_trace_events(fd, pev, tevs, max_tevs); + ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); close(fd); if (ntevs > 0) { /* Succeeded to find trace events */ - pr_debug("find %d kprobe_trace_events.\n", ntevs); + pr_debug("find %d probe_trace_events.\n", ntevs); return ntevs; } @@ -377,8 +380,8 @@ end: #else /* !DWARF_SUPPORT */ -static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, - struct perf_probe_point *pp) +static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, + struct perf_probe_point *pp) { pp->function = strdup(tp->symbol); if (pp->function == NULL) @@ -389,8 +392,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, return 0; } -static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs __unused, +static int try_to_find_probe_trace_events(struct perf_probe_event *pev, + struct probe_trace_event **tevs __unused, int max_tevs __unused) { if (perf_probe_event_need_dwarf(pev)) { @@ -781,16 +784,17 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) return false; } -/* Parse kprobe_events event into struct probe_point */ -int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) +/* Parse probe_events event into struct probe_point */ +static int parse_probe_trace_command(const char *cmd, + struct probe_trace_event *tev) { - struct kprobe_trace_point *tp = &tev->point; + struct probe_trace_point *tp = &tev->point; char pr; char *p; int ret, i, argc; char **argv; - pr_debug("Parsing kprobe_events: %s\n", cmd); + pr_debug("Parsing probe_events: %s\n", cmd); argv = argv_split(cmd, &argc); if (!argv) { pr_debug("Failed to split arguments.\n"); @@ -822,7 +826,7 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) tp->offset = 0; tev->nargs = argc - 2; - tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); if (tev->args == NULL) { ret = -ENOMEM; goto out; @@ -968,13 +972,13 @@ char *synthesize_perf_probe_command(struct perf_probe_event *pev) } #endif -static int __synthesize_kprobe_trace_arg_ref(struct kprobe_trace_arg_ref *ref, +static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref, char **buf, size_t *buflen, int depth) { int ret; if (ref->next) { - depth = __synthesize_kprobe_trace_arg_ref(ref->next, buf, + depth = __synthesize_probe_trace_arg_ref(ref->next, buf, buflen, depth + 1); if (depth < 0) goto out; @@ -992,10 +996,10 @@ out: } -static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, +static int synthesize_probe_trace_arg(struct probe_trace_arg *arg, char *buf, size_t buflen) { - struct kprobe_trace_arg_ref *ref = arg->ref; + struct probe_trace_arg_ref *ref = arg->ref; int ret, depth = 0; char *tmp = buf; @@ -1015,7 +1019,7 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, /* Dereferencing arguments */ if (ref) { - depth = __synthesize_kprobe_trace_arg_ref(ref, &buf, + depth = __synthesize_probe_trace_arg_ref(ref, &buf, &buflen, 1); if (depth < 0) return depth; @@ -1051,9 +1055,9 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, return buf - tmp; } -char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) +char *synthesize_probe_trace_command(struct probe_trace_event *tev) { - struct kprobe_trace_point *tp = &tev->point; + struct probe_trace_point *tp = &tev->point; char *buf; int i, len, ret; @@ -1069,7 +1073,7 @@ char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) goto error; for (i = 0; i < tev->nargs; i++) { - ret = synthesize_kprobe_trace_arg(&tev->args[i], buf + len, + ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, MAX_CMDLEN - len); if (ret <= 0) goto error; @@ -1082,7 +1086,7 @@ error: return NULL; } -int convert_to_perf_probe_event(struct kprobe_trace_event *tev, +static int convert_to_perf_probe_event(struct probe_trace_event *tev, struct perf_probe_event *pev) { char buf[64] = ""; @@ -1095,7 +1099,7 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev, return -ENOMEM; /* Convert trace_point to probe_point */ - ret = convert_to_perf_probe_point(&tev->point, &pev->point); + ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); if (ret < 0) return ret; @@ -1108,7 +1112,7 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev, if (tev->args[i].name) pev->args[i].name = strdup(tev->args[i].name); else { - ret = synthesize_kprobe_trace_arg(&tev->args[i], + ret = synthesize_probe_trace_arg(&tev->args[i], buf, 64); pev->args[i].name = strdup(buf); } @@ -1159,9 +1163,9 @@ void clear_perf_probe_event(struct perf_probe_event *pev) memset(pev, 0, sizeof(*pev)); } -void clear_kprobe_trace_event(struct kprobe_trace_event *tev) +static void clear_probe_trace_event(struct probe_trace_event *tev) { - struct kprobe_trace_arg_ref *ref, *next; + struct probe_trace_arg_ref *ref, *next; int i; if (tev->event) @@ -1222,7 +1226,7 @@ static int open_kprobe_events(bool readwrite) } /* Get raw string list of current kprobe_events */ -static struct strlist *get_kprobe_trace_command_rawlist(int fd) +static struct strlist *get_probe_trace_command_rawlist(int fd) { int ret, idx; FILE *fp; @@ -1290,7 +1294,7 @@ static int show_perf_probe_event(struct perf_probe_event *pev) int show_perf_probe_events(void) { int fd, ret; - struct kprobe_trace_event tev; + struct probe_trace_event tev; struct perf_probe_event pev; struct strlist *rawlist; struct str_node *ent; @@ -1307,20 +1311,20 @@ int show_perf_probe_events(void) if (fd < 0) return fd; - rawlist = get_kprobe_trace_command_rawlist(fd); + rawlist = get_probe_trace_command_rawlist(fd); close(fd); if (!rawlist) return -ENOENT; strlist__for_each(ent, rawlist) { - ret = parse_kprobe_trace_command(ent->s, &tev); + ret = parse_probe_trace_command(ent->s, &tev); if (ret >= 0) { ret = convert_to_perf_probe_event(&tev, &pev); if (ret >= 0) ret = show_perf_probe_event(&pev); } clear_perf_probe_event(&pev); - clear_kprobe_trace_event(&tev); + clear_probe_trace_event(&tev); if (ret < 0) break; } @@ -1330,20 +1334,19 @@ int show_perf_probe_events(void) } /* Get current perf-probe event names */ -static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) +static struct strlist *get_probe_trace_event_names(int fd, bool include_group) { char buf[128]; struct strlist *sl, *rawlist; struct str_node *ent; - struct kprobe_trace_event tev; + struct probe_trace_event tev; int ret = 0; memset(&tev, 0, sizeof(tev)); - - rawlist = get_kprobe_trace_command_rawlist(fd); + rawlist = get_probe_trace_command_rawlist(fd); sl = strlist__new(true, NULL); strlist__for_each(ent, rawlist) { - ret = parse_kprobe_trace_command(ent->s, &tev); + ret = parse_probe_trace_command(ent->s, &tev); if (ret < 0) break; if (include_group) { @@ -1353,7 +1356,7 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) ret = strlist__add(sl, buf); } else ret = strlist__add(sl, tev.event); - clear_kprobe_trace_event(&tev); + clear_probe_trace_event(&tev); if (ret < 0) break; } @@ -1366,13 +1369,13 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) return sl; } -static int write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) +static int write_probe_trace_event(int fd, struct probe_trace_event *tev) { int ret = 0; - char *buf = synthesize_kprobe_trace_command(tev); + char *buf = synthesize_probe_trace_command(tev); if (!buf) { - pr_debug("Failed to synthesize kprobe trace event.\n"); + pr_debug("Failed to synthesize probe trace event.\n"); return -EINVAL; } @@ -1425,12 +1428,12 @@ static int get_new_event_name(char *buf, size_t len, const char *base, return ret; } -static int __add_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event *tevs, +static int __add_probe_trace_events(struct perf_probe_event *pev, + struct probe_trace_event *tevs, int ntevs, bool allow_suffix) { int i, fd, ret; - struct kprobe_trace_event *tev = NULL; + struct probe_trace_event *tev = NULL; char buf[64]; const char *event, *group; struct strlist *namelist; @@ -1439,7 +1442,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, if (fd < 0) return fd; /* Get current event names */ - namelist = get_kprobe_trace_event_names(fd, false); + namelist = get_probe_trace_event_names(fd, false); if (!namelist) { pr_debug("Failed to get current event list.\n"); return -EIO; @@ -1474,7 +1477,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, ret = -ENOMEM; break; } - ret = write_kprobe_trace_event(fd, tev); + ret = write_probe_trace_event(fd, tev); if (ret < 0) break; /* Add added event name to namelist */ @@ -1511,21 +1514,21 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, return ret; } -static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs, +static int convert_to_probe_trace_events(struct perf_probe_event *pev, + struct probe_trace_event **tevs, int max_tevs) { struct symbol *sym; int ret = 0, i; - struct kprobe_trace_event *tev; + struct probe_trace_event *tev; /* Convert perf_probe_event with debuginfo */ - ret = try_to_find_kprobe_trace_events(pev, tevs, max_tevs); + ret = try_to_find_probe_trace_events(pev, tevs, max_tevs); if (ret != 0) return ret; /* Allocate trace event buffer */ - tev = *tevs = zalloc(sizeof(struct kprobe_trace_event)); + tev = *tevs = zalloc(sizeof(struct probe_trace_event)); if (tev == NULL) return -ENOMEM; @@ -1538,7 +1541,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, tev->point.offset = pev->point.offset; tev->nargs = pev->nargs; if (tev->nargs) { - tev->args = zalloc(sizeof(struct kprobe_trace_arg) + tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); if (tev->args == NULL) { ret = -ENOMEM; @@ -1579,7 +1582,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, return 1; error: - clear_kprobe_trace_event(tev); + clear_probe_trace_event(tev); free(tev); *tevs = NULL; return ret; @@ -1587,7 +1590,7 @@ error: struct __event_package { struct perf_probe_event *pev; - struct kprobe_trace_event *tevs; + struct probe_trace_event *tevs; int ntevs; }; @@ -1610,7 +1613,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, for (i = 0; i < npevs; i++) { pkgs[i].pev = &pevs[i]; /* Convert with or without debuginfo */ - ret = convert_to_kprobe_trace_events(pkgs[i].pev, + ret = convert_to_probe_trace_events(pkgs[i].pev, &pkgs[i].tevs, max_tevs); if (ret < 0) goto end; @@ -1619,24 +1622,24 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, /* Loop 2: add all events */ for (i = 0; i < npevs && ret >= 0; i++) - ret = __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, + ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, pkgs[i].ntevs, force_add); end: /* Loop 3: cleanup trace events */ for (i = 0; i < npevs; i++) for (j = 0; j < pkgs[i].ntevs; j++) - clear_kprobe_trace_event(&pkgs[i].tevs[j]); + clear_probe_trace_event(&pkgs[i].tevs[j]); return ret; } -static int __del_trace_kprobe_event(int fd, struct str_node *ent) +static int __del_trace_probe_event(int fd, struct str_node *ent) { char *p; char buf[128]; int ret; - /* Convert from perf-probe event to trace-kprobe event */ + /* Convert from perf-probe event to trace-probe event */ ret = e_snprintf(buf, 128, "-:%s", ent->s); if (ret < 0) goto error; @@ -1662,7 +1665,7 @@ error: return ret; } -static int del_trace_kprobe_event(int fd, const char *group, +static int del_trace_probe_event(int fd, const char *group, const char *event, struct strlist *namelist) { char buf[128]; @@ -1679,7 +1682,7 @@ static int del_trace_kprobe_event(int fd, const char *group, strlist__for_each_safe(ent, n, namelist) if (strglobmatch(ent->s, buf)) { found++; - ret = __del_trace_kprobe_event(fd, ent); + ret = __del_trace_probe_event(fd, ent); if (ret < 0) break; strlist__remove(namelist, ent); @@ -1688,7 +1691,7 @@ static int del_trace_kprobe_event(int fd, const char *group, ent = strlist__find(namelist, buf); if (ent) { found++; - ret = __del_trace_kprobe_event(fd, ent); + ret = __del_trace_probe_event(fd, ent); if (ret >= 0) strlist__remove(namelist, ent); } @@ -1712,7 +1715,7 @@ int del_perf_probe_events(struct strlist *dellist) return fd; /* Get current event names */ - namelist = get_kprobe_trace_event_names(fd, true); + namelist = get_probe_trace_event_names(fd, true); if (namelist == NULL) return -EINVAL; @@ -1733,7 +1736,7 @@ int del_perf_probe_events(struct strlist *dellist) event = str; } pr_debug("Group: %s, Event: %s\n", group, event); - ret = del_trace_kprobe_event(fd, group, event, namelist); + ret = del_trace_probe_event(fd, group, event, namelist); free(str); if (ret < 0) break; diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index ed362acff4b..5af39243a25 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -7,33 +7,33 @@ extern bool probe_event_dry_run; /* kprobe-tracer tracing point */ -struct kprobe_trace_point { +struct probe_trace_point { char *symbol; /* Base symbol */ unsigned long offset; /* Offset from symbol */ bool retprobe; /* Return probe flag */ }; -/* kprobe-tracer tracing argument referencing offset */ -struct kprobe_trace_arg_ref { - struct kprobe_trace_arg_ref *next; /* Next reference */ +/* probe-tracer tracing argument referencing offset */ +struct probe_trace_arg_ref { + struct probe_trace_arg_ref *next; /* Next reference */ long offset; /* Offset value */ }; /* kprobe-tracer tracing argument */ -struct kprobe_trace_arg { +struct probe_trace_arg { char *name; /* Argument name */ char *value; /* Base value */ char *type; /* Type name */ - struct kprobe_trace_arg_ref *ref; /* Referencing offset */ + struct probe_trace_arg_ref *ref; /* Referencing offset */ }; /* kprobe-tracer tracing event (point + arg) */ -struct kprobe_trace_event { +struct probe_trace_event { char *event; /* Event name */ char *group; /* Group name */ - struct kprobe_trace_point point; /* Trace point */ + struct probe_trace_point point; /* Trace point */ int nargs; /* Number of args */ - struct kprobe_trace_arg *args; /* Arguments */ + struct probe_trace_arg *args; /* Arguments */ }; /* Perf probe probing point */ @@ -93,25 +93,18 @@ struct line_range { /* Command string to events */ extern int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev); -extern int parse_kprobe_trace_command(const char *cmd, - struct kprobe_trace_event *tev); /* Events to command string */ extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); -extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); +extern char *synthesize_probe_trace_command(struct probe_trace_event *tev); extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len); /* Check the perf_probe_event needs debuginfo */ extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); -/* Convert from kprobe_trace_event to perf_probe_event */ -extern int convert_to_perf_probe_event(struct kprobe_trace_event *tev, - struct perf_probe_event *pev); - /* Release event contents */ extern void clear_perf_probe_event(struct perf_probe_event *pev); -extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); /* Command string to line-range */ extern int parse_line_range_desc(const char *cmd, struct line_range *lr); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index f88070ea5b9..840f1aabbb7 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -366,10 +366,10 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, * Probe finder related functions */ -static struct kprobe_trace_arg_ref *alloc_trace_arg_ref(long offs) +static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs) { - struct kprobe_trace_arg_ref *ref; - ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); + struct probe_trace_arg_ref *ref; + ref = zalloc(sizeof(struct probe_trace_arg_ref)); if (ref != NULL) ref->offset = offs; return ref; @@ -385,7 +385,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) Dwarf_Word offs = 0; bool ref = false; const char *regs; - struct kprobe_trace_arg *tvar = pf->tvar; + struct probe_trace_arg *tvar = pf->tvar; int ret; /* TODO: handle more than 1 exprs */ @@ -459,10 +459,10 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) } static int convert_variable_type(Dwarf_Die *vr_die, - struct kprobe_trace_arg *tvar, + struct probe_trace_arg *tvar, const char *cast) { - struct kprobe_trace_arg_ref **ref_ptr = &tvar->ref; + struct probe_trace_arg_ref **ref_ptr = &tvar->ref; Dwarf_Die type; char buf[16]; int ret; @@ -500,7 +500,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, while (*ref_ptr) ref_ptr = &(*ref_ptr)->next; /* Add new reference with offset +0 */ - *ref_ptr = zalloc(sizeof(struct kprobe_trace_arg_ref)); + *ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref)); if (*ref_ptr == NULL) { pr_warning("Out of memory error\n"); return -ENOMEM; @@ -545,10 +545,10 @@ static int convert_variable_type(Dwarf_Die *vr_die, static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, struct perf_probe_arg_field *field, - struct kprobe_trace_arg_ref **ref_ptr, + struct probe_trace_arg_ref **ref_ptr, Dwarf_Die *die_mem) { - struct kprobe_trace_arg_ref *ref = *ref_ptr; + struct probe_trace_arg_ref *ref = *ref_ptr; Dwarf_Die type; Dwarf_Word offs; int ret, tag; @@ -574,7 +574,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, pr_debug2("Array real type: (%x)\n", (unsigned)dwarf_dieoffset(&type)); if (tag == DW_TAG_pointer_type) { - ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); + ref = zalloc(sizeof(struct probe_trace_arg_ref)); if (ref == NULL) return -ENOMEM; if (*ref_ptr) @@ -605,7 +605,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, return -EINVAL; } - ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); + ref = zalloc(sizeof(struct probe_trace_arg_ref)); if (ref == NULL) return -ENOMEM; if (*ref_ptr) @@ -738,7 +738,7 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) /* Show a probe point to output buffer */ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) { - struct kprobe_trace_event *tev; + struct probe_trace_event *tev; Dwarf_Addr eaddr; Dwarf_Die die_mem; const char *name; @@ -803,7 +803,7 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) /* Find each argument */ tev->nargs = pf->pev->nargs; - tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); if (tev->args == NULL) return -ENOMEM; for (i = 0; i < pf->pev->nargs; i++) { @@ -1060,9 +1060,9 @@ static int find_probe_point_by_func(struct probe_finder *pf) return _param.retval; } -/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ -int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, - struct kprobe_trace_event **tevs, int max_tevs) +/* Find probe_trace_events specified by perf_probe_event from debuginfo */ +int find_probe_trace_events(int fd, struct perf_probe_event *pev, + struct probe_trace_event **tevs, int max_tevs) { struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; struct perf_probe_point *pp = &pev->point; @@ -1072,7 +1072,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, Dwarf *dbg; int ret = 0; - pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); + pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); if (pf.tevs == NULL) return -ENOMEM; *tevs = pf.tevs; diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index e1f61dcd18f..4507d519f18 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -16,9 +16,9 @@ static inline int is_c_varname(const char *name) } #ifdef DWARF_SUPPORT -/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ -extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, - struct kprobe_trace_event **tevs, +/* Find probe_trace_events specified by perf_probe_event from debuginfo */ +extern int find_probe_trace_events(int fd, struct perf_probe_event *pev, + struct probe_trace_event **tevs, int max_tevs); /* Find a perf_probe_point from debuginfo */ @@ -33,7 +33,7 @@ extern int find_line_range(int fd, struct line_range *lr); struct probe_finder { struct perf_probe_event *pev; /* Target probe event */ - struct kprobe_trace_event *tevs; /* Result trace events */ + struct probe_trace_event *tevs; /* Result trace events */ int ntevs; /* Number of trace events */ int max_tevs; /* Max number of trace events */ @@ -50,7 +50,7 @@ struct probe_finder { #endif Dwarf_Op *fb_ops; /* Frame base attribute */ struct perf_probe_arg *pvar; /* Current target variable */ - struct kprobe_trace_arg *tvar; /* Current result variable */ + struct probe_trace_arg *tvar; /* Current result variable */ }; struct line_finder { -- cgit v1.2.3-18-g5258 From 591765fdaf7ea1888157f342b67b0461f2e5ed9b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 30 Jul 2010 18:28:42 -0300 Subject: perf tools: Release thread resources on PERF_RECORD_EXIT For long running sessions with many threads with short lifetimes the amount of memory that the buildid process takes is too much. Since we don't have hist_entries that may be pointing to them, we can just release the resources associated with each thread when the exit (PERF_RECORD_EXIT) event is received. For normal processing we need to annotate maps with hits, and thus hist_entries pointing to it and drop the ones that had none. Will be done in a followup patch. Cc: David S. Miller Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 18 ++++++++++++++++++ tools/perf/util/map.c | 33 +++++++++++++++++++++++++++++++++ tools/perf/util/map.h | 1 + tools/perf/util/thread.c | 7 +++++++ tools/perf/util/thread.h | 2 ++ 5 files changed, 61 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 5c26e2d314a..e437edb7241 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -12,6 +12,7 @@ #include "event.h" #include "symbol.h" #include +#include "debug.h" static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) { @@ -34,10 +35,27 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) return 0; } +static int event__exit_del_thread(event_t *self, struct perf_session *session) +{ + struct thread *thread = perf_session__findnew(session, self->fork.tid); + + dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, + self->fork.ppid, self->fork.ptid); + + if (thread) { + rb_erase(&thread->rb_node, &session->threads); + session->last_match = NULL; + thread__delete(thread); + } + + return 0; +} + struct perf_event_ops build_id__mark_dso_hit_ops = { .sample = build_id__mark_dso_hit, .mmap = event__process_mmap, .fork = event__process_task, + .exit = event__exit_del_thread, }; char *dso__build_id_filename(struct dso *self, char *bf, size_t size) diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 37cab903853..2ddbae319de 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -228,6 +228,39 @@ void map_groups__init(struct map_groups *self) self->machine = NULL; } +static void maps__delete(struct rb_root *self) +{ + struct rb_node *next = rb_first(self); + + while (next) { + struct map *pos = rb_entry(next, struct map, rb_node); + + next = rb_next(&pos->rb_node); + rb_erase(&pos->rb_node, self); + map__delete(pos); + } +} + +static void maps__delete_removed(struct list_head *self) +{ + struct map *pos, *n; + + list_for_each_entry_safe(pos, n, self, node) { + list_del(&pos->node); + map__delete(pos); + } +} + +void map_groups__exit(struct map_groups *self) +{ + int i; + + for (i = 0; i < MAP__NR_TYPES; ++i) { + maps__delete(&self->maps[i]); + maps__delete_removed(&self->removed_maps[i]); + } +} + void map_groups__flush(struct map_groups *self) { int type; diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 3b2f706c0ba..20eba420dda 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -127,6 +127,7 @@ size_t __map_groups__fprintf_maps(struct map_groups *self, void maps__insert(struct rb_root *maps, struct map *map); struct map *maps__find(struct rb_root *maps, u64 addr); void map_groups__init(struct map_groups *self); +void map_groups__exit(struct map_groups *self); int map_groups__clone(struct map_groups *self, struct map_groups *parent, enum map_type type); size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 9a448b47400..8c72d888e44 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -62,6 +62,13 @@ static struct thread *thread__new(pid_t pid) return self; } +void thread__delete(struct thread *self) +{ + map_groups__exit(&self->mg); + free(self->comm); + free(self); +} + int thread__set_comm(struct thread *self, const char *comm) { int err; diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index ee6bbcf277c..688500ff826 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -20,6 +20,8 @@ struct thread { struct perf_session; +void thread__delete(struct thread *self); + int find_all_tid(int pid, pid_t ** all_tid); int thread__set_comm(struct thread *self, const char *comm); int thread__comm_len(struct thread *self); -- cgit v1.2.3-18-g5258 From d65a458b348cd458413b3cfec66e43ebd0367646 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 30 Jul 2010 18:31:28 -0300 Subject: perf tools: Release session and symbol resources on exit So that we reduce the noise when looking for leaks using tools such as valgrind. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 5 ++++- tools/perf/util/event.c | 5 +++-- tools/perf/util/map.c | 26 ++++++++++++++++++++++++++ tools/perf/util/map.h | 1 + tools/perf/util/session.c | 26 ++++++++++++++++++++++++++ tools/perf/util/symbol.c | 9 +++++++++ tools/perf/util/symbol.h | 1 + 7 files changed, 70 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 5ae0d93d859..ff77b805de7 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -440,6 +440,7 @@ static void atexit_header(void) process_buildids(); perf_header__write(&session->header, output, true); perf_session__delete(session); + symbol__exit(); } } @@ -871,7 +872,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) } else { all_tids=malloc(sizeof(pid_t)); if (!all_tids) - return -ENOMEM; + goto out_symbol_exit; all_tids[0] = target_tid; thread_num = 1; @@ -918,5 +919,7 @@ out_free_fd: } free(all_tids); all_tids = NULL; +out_symbol_exit: + symbol__exit(); return err; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 8151d23664c..6b0db557792 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -515,12 +515,13 @@ int event__process_mmap(event_t *self, struct perf_session *session) if (machine == NULL) goto out_problem; thread = perf_session__findnew(session, self->mmap.pid); + if (thread == NULL) + goto out_problem; map = map__new(&machine->user_dsos, self->mmap.start, self->mmap.len, self->mmap.pgoff, self->mmap.pid, self->mmap.filename, MAP__FUNCTION); - - if (thread == NULL || map == NULL) + if (map == NULL) goto out_problem; thread__insert_map(thread, map); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 2ddbae319de..15d6a6dd50c 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -539,6 +539,32 @@ int machine__init(struct machine *self, const char *root_dir, pid_t pid) return self->root_dir == NULL ? -ENOMEM : 0; } +static void dsos__delete(struct list_head *self) +{ + struct dso *pos, *n; + + list_for_each_entry_safe(pos, n, self, node) { + list_del(&pos->node); + dso__delete(pos); + } +} + +void machine__exit(struct machine *self) +{ + struct kmap *kmap = map__kmap(self->vmlinux_maps[MAP__FUNCTION]); + + if (kmap->ref_reloc_sym) { + free((char *)kmap->ref_reloc_sym->name); + free(kmap->ref_reloc_sym); + } + + map_groups__exit(&self->kmaps); + dsos__delete(&self->user_dsos); + dsos__delete(&self->kernel_dsos); + free(self->root_dir); + self->root_dir = NULL; +} + struct machine *machines__add(struct rb_root *self, pid_t pid, const char *root_dir) { diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 20eba420dda..0e0984e86fc 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -143,6 +143,7 @@ struct machine *machines__find(struct rb_root *self, pid_t pid); struct machine *machines__findnew(struct rb_root *self, pid_t pid); char *machine__mmap_name(struct machine *self, char *bf, size_t size); int machine__init(struct machine *self, const char *root_dir, pid_t pid); +void machine__exit(struct machine *self); /* * Default guest kernel is defined by parameter --guestkallsyms diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 8cbea122e34..04a3b3db9e9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -124,9 +124,35 @@ out_delete: return NULL; } +static void perf_session__delete_dead_threads(struct perf_session *self) +{ + struct thread *n, *t; + + list_for_each_entry_safe(t, n, &self->dead_threads, node) { + list_del(&t->node); + thread__delete(t); + } +} + +static void perf_session__delete_threads(struct perf_session *self) +{ + struct rb_node *nd = rb_first(&self->threads); + + while (nd) { + struct thread *t = rb_entry(nd, struct thread, rb_node); + + rb_erase(&t->rb_node, &self->threads); + nd = rb_next(nd); + thread__delete(t); + } +} + void perf_session__delete(struct perf_session *self) { perf_header__exit(&self->header); + perf_session__delete_dead_threads(self); + perf_session__delete_threads(self); + machine__exit(&self->host_machine); close(self->fd); free(self); } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index d99497ec52a..94cdf68440c 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2245,6 +2245,15 @@ out_free_comm_list: return -1; } +void symbol__exit(void) +{ + strlist__delete(symbol_conf.sym_list); + strlist__delete(symbol_conf.dso_list); + strlist__delete(symbol_conf.comm_list); + vmlinux_path__exit(); + symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; +} + int machines__create_kernel_maps(struct rb_root *self, pid_t pid) { struct machine *machine = machines__findnew(self, pid); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index f29f73c2080..33d53ce2895 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -219,6 +219,7 @@ int machines__create_kernel_maps(struct rb_root *self, pid_t pid); int machines__create_guest_kernel_maps(struct rb_root *self); int symbol__init(void); +void symbol__exit(void); bool symbol_type__is_a(char symbol_type, enum map_type map_type); size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp); -- cgit v1.2.3-18-g5258 From 880d22f2470af6037715b7f6eb083b6ec5561d92 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 20 Jul 2010 21:55:33 +0200 Subject: perf: New migration tool overview This brings a GUI tool that displays an overview of the load of tasks proportion in each CPUs. The CPUs forward progress is cut in timeslices. A new timeslice is created for every runqueue event: a task gets pushed out or pulled in the runqueue. For each timeslice, every CPUs rectangle is colored with a red power that describes the local load against the total load. This more red is the rectangle, the higher is the given CPU load. This load is the number of tasks running on the CPU, without any distinction against the scheduler policy of the tasks, for now. Also for each timeslice, the event origin is depicted on the CPUs that triggered it using a thin colored line on top of the rectangle timeslice. These events are: * sleep: a task went to sleep and has then been pulled out the runqueue. The origin color in the thin line is dark blue. * wake up: a task woke up and has then been pushed in the runqueue. The origin color is yellow. * wake up new: a new task woke up and has then been pushed in the runqueue. The origin color is green. * migrate in: a task migrated in the runqueue due to a load balancing operation. The origin color is violet. * migrate out: reverse of the previous one. Migrate in events usually have paired migrate out events in another runqueue. The origin color is light blue. Clicking on a timeslice provides the runqueue event details and the runqueue state. The CPU rectangles can be navigated using the usual arrow controls. Horizontal zooming in/out is possible with the "+" and "-" buttons. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Li Zefan Cc: Steven Rostedt Cc: Tom Zanussi Cc: Mike Galbraith Cc: Venkatesh Pallipadi Cc: Pierre Tardy Cc: Nikhil Rao Cc: Li Zefan --- .../perf/scripts/python/bin/sched-migration-record | 2 + .../perf/scripts/python/bin/sched-migration-report | 3 + tools/perf/scripts/python/sched-migration.py | 634 +++++++++++++++++++++ 3 files changed, 639 insertions(+) create mode 100644 tools/perf/scripts/python/bin/sched-migration-record create mode 100644 tools/perf/scripts/python/bin/sched-migration-report create mode 100644 tools/perf/scripts/python/sched-migration.py (limited to 'tools') diff --git a/tools/perf/scripts/python/bin/sched-migration-record b/tools/perf/scripts/python/bin/sched-migration-record new file mode 100644 index 00000000000..17a3e9bd9e8 --- /dev/null +++ b/tools/perf/scripts/python/bin/sched-migration-record @@ -0,0 +1,2 @@ +#!/bin/bash +perf record -m 16384 -a -e sched:sched_wakeup -e sched:sched_wakeup_new -e sched:sched_switch -e sched:sched_migrate_task $@ diff --git a/tools/perf/scripts/python/bin/sched-migration-report b/tools/perf/scripts/python/bin/sched-migration-report new file mode 100644 index 00000000000..61d05f72e44 --- /dev/null +++ b/tools/perf/scripts/python/bin/sched-migration-report @@ -0,0 +1,3 @@ +#!/bin/bash +# description: sched migration overview +perf trace $@ -s ~/libexec/perf-core/scripts/python/sched-migration.py diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py new file mode 100644 index 00000000000..f73e1c736a3 --- /dev/null +++ b/tools/perf/scripts/python/sched-migration.py @@ -0,0 +1,634 @@ +#!/usr/bin/python +# +# Cpu task migration overview toy +# +# Copyright (C) 2010 Frederic Weisbecker +# +# perf trace event handlers have been generated by perf trace -g python +# +# The whole is licensed under the terms of the GNU GPL License version 2 + + +try: + import wx +except ImportError: + raise ImportError, "You need to install the wxpython lib for this script" + +import os +import sys + +from collections import defaultdict +from UserList import UserList + +sys.path.append(os.environ['PERF_EXEC_PATH'] + \ + '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') + +from perf_trace_context import * +from Core import * + +class RootFrame(wx.Frame): + def __init__(self, timeslices, parent = None, id = -1, title = "Migration"): + wx.Frame.__init__(self, parent, id, title) + + (self.screen_width, self.screen_height) = wx.GetDisplaySize() + self.screen_width -= 10 + self.screen_height -= 10 + self.zoom = 0.5 + self.scroll_scale = 20 + self.timeslices = timeslices + (self.ts_start, self.ts_end) = timeslices.interval() + self.update_width_virtual() + + # whole window panel + self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) + + # scrollable container + self.scroll = wx.ScrolledWindow(self.panel) + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, 100 / 10) + self.scroll.EnableScrolling(True, True) + self.scroll.SetFocus() + + # scrollable drawing area + self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width, self.screen_height / 2)) + self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) + self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) + self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) + self.scroll.Bind(wx.EVT_PAINT, self.on_paint) + + self.scroll.Fit() + self.Fit() + + self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, -1, wx.SIZE_USE_EXISTING) + + self.max_cpu = -1 + self.txt = None + + self.Show(True) + + def us_to_px(self, val): + return val / (10 ** 3) * self.zoom + + def px_to_us(self, val): + return (val / self.zoom) * (10 ** 3) + + def scroll_start(self): + (x, y) = self.scroll.GetViewStart() + return (x * self.scroll_scale, y * self.scroll_scale) + + def scroll_start_us(self): + (x, y) = self.scroll_start() + return self.px_to_us(x) + + def update_rectangle_cpu(self, dc, slice, cpu, offset_time): + rq = slice.rqs[cpu] + + if slice.total_load != 0: + load_rate = rq.load() / float(slice.total_load) + else: + load_rate = 0 + + + offset_px = self.us_to_px(slice.start - offset_time) + width_px = self.us_to_px(slice.end - slice.start) + (x, y) = self.scroll_start() + + if width_px == 0: + return + + offset_py = 100 + (cpu * 150) + width_py = 100 + + if cpu in slice.event_cpus: + rgb = rq.event.color() + if rgb is not None: + (r, g, b) = rgb + color = wx.Colour(r, g, b) + brush = wx.Brush(color, wx.SOLID) + dc.SetBrush(brush) + dc.DrawRectangle(offset_px, offset_py, width_px, 5) + width_py -= 5 + offset_py += 5 + + red_power = int(0xff - (0xff * load_rate)) + color = wx.Colour(0xff, red_power, red_power) + brush = wx.Brush(color, wx.SOLID) + dc.SetBrush(brush) + dc.DrawRectangle(offset_px, offset_py, width_px, width_py) + + def update_rectangles(self, dc, start, end): + if len(self.timeslices) == 0: + return + start += self.timeslices[0].start + end += self.timeslices[0].start + + color = wx.Colour(0, 0, 0) + brush = wx.Brush(color, wx.SOLID) + dc.SetBrush(brush) + + i = self.timeslices.find_time_slice(start) + if i == -1: + return + + for i in xrange(i, len(self.timeslices)): + timeslice = self.timeslices[i] + if timeslice.start > end: + return + + for cpu in timeslice.rqs: + self.update_rectangle_cpu(dc, timeslice, cpu, self.timeslices[0].start) + if cpu > self.max_cpu: + self.max_cpu = cpu + + def on_paint(self, event): + color = wx.Colour(0xff, 0xff, 0xff) + brush = wx.Brush(color, wx.SOLID) + dc = wx.PaintDC(self.scroll_panel) + dc.SetBrush(brush) + + width = min(self.width_virtual, self.screen_width) + (x, y) = self.scroll_start() + start = self.px_to_us(x) + end = self.px_to_us(x + width) + self.update_rectangles(dc, start, end) + + def cpu_from_ypixel(self, y): + y -= 100 + cpu = y / 150 + height = y % 150 + + if cpu < 0 or cpu > self.max_cpu or height > 100: + return -1 + + return cpu + + def update_summary(self, cpu, t): + idx = self.timeslices.find_time_slice(t) + if idx == -1: + return + + ts = self.timeslices[idx] + rq = ts.rqs[cpu] + raw = "CPU: %d\n" % cpu + raw += "Last event : %s\n" % rq.event.__repr__() + raw += "Timestamp : %d.%06d\n" % (ts.start / (10 ** 9), (ts.start % (10 ** 9)) / 1000) + raw += "Duration : %6d us\n" % ((ts.end - ts.start) / (10 ** 6)) + raw += "Load = %d\n" % rq.load() + for t in rq.tasks: + raw += "%s \n" % thread_name(t) + + if self.txt: + self.txt.Destroy() + self.txt = wx.StaticText(self.panel, -1, raw, (0, (self.screen_height / 2) + 50)) + + + def on_mouse_down(self, event): + (x, y) = event.GetPositionTuple() + cpu = self.cpu_from_ypixel(y) + if cpu == -1: + return + + t = self.px_to_us(x) + self.timeslices[0].start + + self.update_summary(cpu, t) + + + def update_width_virtual(self): + self.width_virtual = self.us_to_px(self.ts_end - self.ts_start) + + def __zoom(self, x): + self.update_width_virtual() + (xpos, ypos) = self.scroll.GetViewStart() + xpos = self.us_to_px(x) / self.scroll_scale + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, 100 / 10, xpos, ypos) + self.Refresh() + + def zoom_in(self): + x = self.scroll_start_us() + self.zoom *= 2 + self.__zoom(x) + + def zoom_out(self): + x = self.scroll_start_us() + self.zoom /= 2 + self.__zoom(x) + + + def on_key_press(self, event): + key = event.GetRawKeyCode() + if key == ord("+"): + self.zoom_in() + return + if key == ord("-"): + self.zoom_out() + return + + key = event.GetKeyCode() + (x, y) = self.scroll.GetViewStart() + if key == wx.WXK_RIGHT: + self.scroll.Scroll(x + 1, y) + elif key == wx.WXK_LEFT: + self.scroll.Scroll(x -1, y) + + +threads = { 0 : "idle"} + +def thread_name(pid): + return "%s:%d" % (threads[pid], pid) + +class EventHeaders: + def __init__(self, common_cpu, common_secs, common_nsecs, + common_pid, common_comm): + self.cpu = common_cpu + self.secs = common_secs + self.nsecs = common_nsecs + self.pid = common_pid + self.comm = common_comm + + def ts(self): + return (self.secs * (10 ** 9)) + self.nsecs + + def ts_format(self): + return "%d.%d" % (self.secs, int(self.nsecs / 1000)) + + +def taskState(state): + states = { + 0 : "R", + 1 : "S", + 2 : "D", + 64: "DEAD" + } + + if state not in states: + print "Unhandled task state %d" % state + return "" + + return states[state] + + +class RunqueueEventUnknown: + @staticmethod + def color(): + return None + + def __repr__(self): + return "unknown" + +class RunqueueEventSleep: + @staticmethod + def color(): + return (0, 0, 0xff) + + def __init__(self, sleeper): + self.sleeper = sleeper + + def __repr__(self): + return "%s gone to sleep" % thread_name(self.sleeper) + +class RunqueueEventWakeup: + @staticmethod + def color(): + return (0xff, 0xff, 0) + + def __init__(self, wakee): + self.wakee = wakee + + def __repr__(self): + return "%s woke up" % thread_name(self.wakee) + +class RunqueueEventFork: + @staticmethod + def color(): + return (0, 0xff, 0) + + def __init__(self, child): + self.child = child + + def __repr__(self): + return "new forked task %s" % thread_name(self.child) + +class RunqueueMigrateIn: + @staticmethod + def color(): + return (0, 0xf0, 0xff) + + def __init__(self, new): + self.new = new + + def __repr__(self): + return "task migrated in %s" % thread_name(self.new) + +class RunqueueMigrateOut: + @staticmethod + def color(): + return (0xff, 0, 0xff) + + def __init__(self, old): + self.old = old + + def __repr__(self): + return "task migrated out %s" % thread_name(self.old) + +class RunqueueSnapshot: + def __init__(self, tasks = [0], event = RunqueueEventUnknown()): + self.tasks = tuple(tasks) + self.event = event + + def sched_switch(self, prev, prev_state, next): + event = RunqueueEventUnknown() + + if taskState(prev_state) == "R" and next in self.tasks \ + and prev in self.tasks: + return self + + if taskState(prev_state) != "R": + event = RunqueueEventSleep(prev) + + next_tasks = list(self.tasks[:]) + if prev in self.tasks: + if taskState(prev_state) != "R": + next_tasks.remove(prev) + elif taskState(prev_state) == "R": + next_tasks.append(prev) + + if next not in next_tasks: + next_tasks.append(next) + + return RunqueueSnapshot(next_tasks, event) + + def migrate_out(self, old): + if old not in self.tasks: + return self + next_tasks = [task for task in self.tasks if task != old] + + return RunqueueSnapshot(next_tasks, RunqueueMigrateOut(old)) + + def __migrate_in(self, new, event): + if new in self.tasks: + self.event = event + return self + next_tasks = self.tasks[:] + tuple([new]) + + return RunqueueSnapshot(next_tasks, event) + + def migrate_in(self, new): + return self.__migrate_in(new, RunqueueMigrateIn(new)) + + def wake_up(self, new): + return self.__migrate_in(new, RunqueueEventWakeup(new)) + + def wake_up_new(self, new): + return self.__migrate_in(new, RunqueueEventFork(new)) + + def load(self): + """ Provide the number of tasks on the runqueue. + Don't count idle""" + return len(self.tasks) - 1 + + def __repr__(self): + ret = self.tasks.__repr__() + ret += self.origin_tostring() + + return ret + +class TimeSlice: + def __init__(self, start, prev): + self.start = start + self.prev = prev + self.end = start + # cpus that triggered the event + self.event_cpus = [] + if prev is not None: + self.total_load = prev.total_load + self.rqs = prev.rqs.copy() + else: + self.rqs = defaultdict(RunqueueSnapshot) + self.total_load = 0 + + def __update_total_load(self, old_rq, new_rq): + diff = new_rq.load() - old_rq.load() + self.total_load += diff + + def sched_switch(self, ts_list, prev, prev_state, next, cpu): + old_rq = self.prev.rqs[cpu] + new_rq = old_rq.sched_switch(prev, prev_state, next) + + if old_rq is new_rq: + return + + self.rqs[cpu] = new_rq + self.__update_total_load(old_rq, new_rq) + ts_list.append(self) + self.event_cpus = [cpu] + + def migrate(self, ts_list, new, old_cpu, new_cpu): + if old_cpu == new_cpu: + return + old_rq = self.prev.rqs[old_cpu] + out_rq = old_rq.migrate_out(new) + self.rqs[old_cpu] = out_rq + self.__update_total_load(old_rq, out_rq) + + new_rq = self.prev.rqs[new_cpu] + in_rq = new_rq.migrate_in(new) + self.rqs[new_cpu] = in_rq + self.__update_total_load(new_rq, in_rq) + + ts_list.append(self) + self.event_cpus = [old_cpu, new_cpu] + + def wake_up(self, ts_list, pid, cpu, fork): + old_rq = self.prev.rqs[cpu] + if fork: + new_rq = old_rq.wake_up_new(pid) + else: + new_rq = old_rq.wake_up(pid) + + if new_rq is old_rq: + return + self.rqs[cpu] = new_rq + self.__update_total_load(old_rq, new_rq) + ts_list.append(self) + self.event_cpus = [cpu] + + def next(self, t): + self.end = t + return TimeSlice(t, self) + +class TimeSliceList(UserList): + def __init__(self, arg = []): + self.data = arg + + def get_time_slice(self, ts): + if len(self.data) == 0: + slice = TimeSlice(ts, TimeSlice(-1, None)) + else: + slice = self.data[-1].next(ts) + return slice + + def find_time_slice(self, ts): + start = 0 + end = len(self.data) + found = -1 + searching = True + while searching: + if start == end or start == end - 1: + searching = False + + i = (end + start) / 2 + if self.data[i].start <= ts and self.data[i].end >= ts: + found = i + end = i + continue + + if self.data[i].end < ts: + start = i + + elif self.data[i].start > ts: + end = i + + return found + + def interval(self): + if len(self.data) == 0: + return (0, 0) + + return (self.data[0].start, self.data[-1].end) + + +class SchedEventProxy: + def __init__(self): + self.current_tsk = defaultdict(lambda : -1) + self.timeslices = TimeSliceList() + + def sched_switch(self, headers, prev_comm, prev_pid, prev_prio, prev_state, + next_comm, next_pid, next_prio): + """ Ensure the task we sched out this cpu is really the one + we logged. Otherwise we may have missed traces """ + + on_cpu_task = self.current_tsk[headers.cpu] + + if on_cpu_task != -1 and on_cpu_task != prev_pid: + print "Sched switch event rejected ts: %s cpu: %d prev: %s(%d) next: %s(%d)" % \ + (headers.ts_format(), headers.cpu, prev_comm, prev_pid, next_comm, next_pid) + + threads[prev_pid] = prev_comm + threads[next_pid] = next_comm + self.current_tsk[headers.cpu] = next_pid + + ts = self.timeslices.get_time_slice(headers.ts()) + ts.sched_switch(self.timeslices, prev_pid, prev_state, next_pid, headers.cpu) + + def migrate(self, headers, pid, prio, orig_cpu, dest_cpu): + ts = self.timeslices.get_time_slice(headers.ts()) + ts.migrate(self.timeslices, pid, orig_cpu, dest_cpu) + + def wake_up(self, headers, comm, pid, success, target_cpu, fork): + if success == 0: + return + ts = self.timeslices.get_time_slice(headers.ts()) + ts.wake_up(self.timeslices, pid, target_cpu, fork) + + +def trace_begin(): + global parser + parser = SchedEventProxy() + +def trace_end(): + app = wx.App(False) + timeslices = parser.timeslices + frame = RootFrame(timeslices) + app.MainLoop() + +def sched__sched_stat_runtime(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, runtime, vruntime): + pass + +def sched__sched_stat_iowait(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, delay): + pass + +def sched__sched_stat_sleep(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, delay): + pass + +def sched__sched_stat_wait(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, delay): + pass + +def sched__sched_process_fork(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + parent_comm, parent_pid, child_comm, child_pid): + pass + +def sched__sched_process_wait(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio): + pass + +def sched__sched_process_exit(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio): + pass + +def sched__sched_process_free(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio): + pass + +def sched__sched_migrate_task(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio, orig_cpu, + dest_cpu): + headers = EventHeaders(common_cpu, common_secs, common_nsecs, + common_pid, common_comm) + parser.migrate(headers, pid, prio, orig_cpu, dest_cpu) + +def sched__sched_switch(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + prev_comm, prev_pid, prev_prio, prev_state, + next_comm, next_pid, next_prio): + + headers = EventHeaders(common_cpu, common_secs, common_nsecs, + common_pid, common_comm) + parser.sched_switch(headers, prev_comm, prev_pid, prev_prio, prev_state, + next_comm, next_pid, next_prio) + +def sched__sched_wakeup_new(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio, success, + target_cpu): + headers = EventHeaders(common_cpu, common_secs, common_nsecs, + common_pid, common_comm) + parser.wake_up(headers, comm, pid, success, target_cpu, 1) + +def sched__sched_wakeup(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio, success, + target_cpu): + headers = EventHeaders(common_cpu, common_secs, common_nsecs, + common_pid, common_comm) + parser.wake_up(headers, comm, pid, success, target_cpu, 0) + +def sched__sched_wait_task(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio): + pass + +def sched__sched_kthread_stop_ret(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + ret): + pass + +def sched__sched_kthread_stop(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid): + pass + +def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs, + common_pid, common_comm): + pass -- cgit v1.2.3-18-g5258 From 749e507411b17ad686783b6d1183befd846fb81b Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 21 Jul 2010 22:45:51 +0200 Subject: perf, sched migration: Handle ignored migrate out events Migrate out events may happen on tasks that are not in the runqueue, for example this is the case for tasks that are sleeping. In this case, we don't want to log the migrate out event in the source runqueue because the task is not eventually in the runqueue and we have already logged its sleep event. This fixes timeslices that spuriously propagate a sleep event from the previous timeslice. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- tools/perf/scripts/python/sched-migration.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index f73e1c736a3..7304d86c76c 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -435,7 +435,10 @@ class TimeSlice: self.__update_total_load(new_rq, in_rq) ts_list.append(self) - self.event_cpus = [old_cpu, new_cpu] + + if old_rq is not out_rq: + self.event_cpus.append(old_cpu) + self.event_cpus.append(new_cpu) def wake_up(self, ts_list, pid, cpu, fork): old_rq = self.prev.rqs[cpu] -- cgit v1.2.3-18-g5258 From 207f90fc4757adc732d5ac23ad11bb90dd078754 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 21 Jul 2010 23:10:38 +0200 Subject: perf, sched migration: Ignore unhandled task states Stop printing an error message when we don't have the letter for a given task state. All we need to know is if the task is in the TASK_RUNNING state. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- tools/perf/scripts/python/sched-migration.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index 7304d86c76c..e9898c5dbcc 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -260,8 +260,7 @@ def taskState(state): } if state not in states: - print "Unhandled task state %d" % state - return "" + return "Unknown" return states[state] -- cgit v1.2.3-18-g5258 From be6d947691376218e788418e2656fc9a3e43b9bc Mon Sep 17 00:00:00 2001 From: Nikhil Rao Date: Wed, 21 Jul 2010 19:46:11 -0700 Subject: perf, sched migration: Fix key bindings EVT_KEY_DOWN and EVT_LEFT_DOWN events are not bound to the RootFrame event handler. As a result, zoom/scroll via keyboard events do not work. This patch adds the missing bindings. Signed-off-by: Nikhil Rao Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Tom Zanussi Signed-off-by: Frederic Weisbecker --- tools/perf/scripts/python/sched-migration.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index e9898c5dbcc..8b8fb7c6099 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -54,6 +54,8 @@ class RootFrame(wx.Frame): self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) self.scroll.Bind(wx.EVT_PAINT, self.on_paint) + self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press) + self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) self.scroll.Fit() self.Fit() -- cgit v1.2.3-18-g5258 From 0cddf56aa841713b37c10c5ab673d6164fce9833 Mon Sep 17 00:00:00 2001 From: Nikhil Rao Date: Wed, 21 Jul 2010 19:46:27 -0700 Subject: perf, sched migration: Parameterize cpu height and spacing Without vertical zoom, it is not possible to see all CPUs in a trace taken on a larger machine. This patch parameterizes the height and spacing of CPUs so that you can fit more cpus into the screen. Ideally we should dynamically size/space the CPU rectangles with some minimum threshold. Until then, this patch is a stop-gap. Signed-off-by: Nikhil Rao Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Tom Zanussi Signed-off-by: Frederic Weisbecker --- tools/perf/scripts/python/sched-migration.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index 8b8fb7c6099..d9026683027 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -27,6 +27,11 @@ from perf_trace_context import * from Core import * class RootFrame(wx.Frame): + Y_OFFSET = 100 + CPU_HEIGHT = 100 + CPU_SPACE = 50 + EVENT_MARKING_WIDTH = 5 + def __init__(self, timeslices, parent = None, id = -1, title = "Migration"): wx.Frame.__init__(self, parent, id, title) @@ -97,8 +102,8 @@ class RootFrame(wx.Frame): if width_px == 0: return - offset_py = 100 + (cpu * 150) - width_py = 100 + offset_py = RootFrame.Y_OFFSET + (cpu * (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE)) + width_py = RootFrame.CPU_HEIGHT if cpu in slice.event_cpus: rgb = rq.event.color() @@ -107,9 +112,9 @@ class RootFrame(wx.Frame): color = wx.Colour(r, g, b) brush = wx.Brush(color, wx.SOLID) dc.SetBrush(brush) - dc.DrawRectangle(offset_px, offset_py, width_px, 5) - width_py -= 5 - offset_py += 5 + dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH) + width_py -= RootFrame.EVENT_MARKING_WIDTH + offset_py += RootFrame.EVENT_MARKING_WIDTH red_power = int(0xff - (0xff * load_rate)) color = wx.Colour(0xff, red_power, red_power) @@ -154,11 +159,11 @@ class RootFrame(wx.Frame): self.update_rectangles(dc, start, end) def cpu_from_ypixel(self, y): - y -= 100 - cpu = y / 150 - height = y % 150 + y -= RootFrame.Y_OFFSET + cpu = y / (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) + height = y % (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) - if cpu < 0 or cpu > self.max_cpu or height > 100: + if cpu < 0 or cpu > self.max_cpu or height > RootFrame.CPU_HEIGHT: return -1 return cpu -- cgit v1.2.3-18-g5258 From 70d815a3decc57c482e5384a623a859e3371e680 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 25 Jul 2010 23:11:51 +0200 Subject: perf, sched migration: Make it vertically scrollable With scheduler traces covering more than two cpus, rectangles of the CPUs 3 and more are not visibles. This makes the vertical navigation scrollable so that all of the CPUs rectangles are available. We also want to be able to zoom vertically, so that we can fit at best the screen with CPU rectangles, but that's for later. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- tools/perf/scripts/python/sched-migration.py | 29 +++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index d9026683027..9d46377f793 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -43,18 +43,20 @@ class RootFrame(wx.Frame): self.timeslices = timeslices (self.ts_start, self.ts_end) = timeslices.interval() self.update_width_virtual() + self.nr_cpus = timeslices.max_cpu() + 1 + self.height_virtual = RootFrame.Y_OFFSET + (self.nr_cpus * (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE)) # whole window panel self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) # scrollable container self.scroll = wx.ScrolledWindow(self.panel) - self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, 100 / 10) + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale) self.scroll.EnableScrolling(True, True) self.scroll.SetFocus() # scrollable drawing area - self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width, self.screen_height / 2)) + self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width - 15, self.screen_height / 2)) self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) @@ -65,9 +67,8 @@ class RootFrame(wx.Frame): self.scroll.Fit() self.Fit() - self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, -1, wx.SIZE_USE_EXISTING) + self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_virtual, wx.SIZE_USE_EXISTING) - self.max_cpu = -1 self.txt = None self.Show(True) @@ -143,8 +144,6 @@ class RootFrame(wx.Frame): for cpu in timeslice.rqs: self.update_rectangle_cpu(dc, timeslice, cpu, self.timeslices[0].start) - if cpu > self.max_cpu: - self.max_cpu = cpu def on_paint(self, event): color = wx.Colour(0xff, 0xff, 0xff) @@ -163,7 +162,7 @@ class RootFrame(wx.Frame): cpu = y / (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) height = y % (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) - if cpu < 0 or cpu > self.max_cpu or height > RootFrame.CPU_HEIGHT: + if cpu < 0 or cpu > self.nr_cpus - 1 or height > RootFrame.CPU_HEIGHT: return -1 return cpu @@ -206,7 +205,7 @@ class RootFrame(wx.Frame): self.update_width_virtual() (xpos, ypos) = self.scroll.GetViewStart() xpos = self.us_to_px(x) / self.scroll_scale - self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, 100 / 10, xpos, ypos) + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xpos, ypos) self.Refresh() def zoom_in(self): @@ -234,7 +233,11 @@ class RootFrame(wx.Frame): if key == wx.WXK_RIGHT: self.scroll.Scroll(x + 1, y) elif key == wx.WXK_LEFT: - self.scroll.Scroll(x -1, y) + self.scroll.Scroll(x - 1, y) + elif key == wx.WXK_DOWN: + self.scroll.Scroll(x, y + 1) + elif key == wx.WXK_UP: + self.scroll.Scroll(x, y - 1) threads = { 0 : "idle"} @@ -504,6 +507,14 @@ class TimeSliceList(UserList): return (self.data[0].start, self.data[-1].end) + def max_cpu(self): + last_ts = self.data[-1] + max_cpu = 0 + for cpu in last_ts.rqs: + if cpu > max_cpu: + max_cpu = cpu + return max_cpu + class SchedEventProxy: def __init__(self): -- cgit v1.2.3-18-g5258 From 699b6d922c7d07f0c1c9041b489e884b5dd5fee5 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 26 Jul 2010 02:02:39 +0200 Subject: perf, sched migration: Make the GUI class client agnostic Make the perf migration GUI generic so that it can be reused for other kinds of trace painting. No more notion of CPUs or runqueue from the GUI class, it's now used as a library by the trace parser. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- tools/perf/scripts/python/sched-migration.py | 177 ++++++++++++++------------- 1 file changed, 92 insertions(+), 85 deletions(-) (limited to 'tools') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index 9d46377f793..6d7281a7de3 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -28,11 +28,11 @@ from Core import * class RootFrame(wx.Frame): Y_OFFSET = 100 - CPU_HEIGHT = 100 - CPU_SPACE = 50 + RECT_HEIGHT = 100 + RECT_SPACE = 50 EVENT_MARKING_WIDTH = 5 - def __init__(self, timeslices, parent = None, id = -1, title = "Migration"): + def __init__(self, sched_tracer, title, parent = None, id = -1): wx.Frame.__init__(self, parent, id, title) (self.screen_width, self.screen_height) = wx.GetDisplaySize() @@ -40,11 +40,12 @@ class RootFrame(wx.Frame): self.screen_height -= 10 self.zoom = 0.5 self.scroll_scale = 20 - self.timeslices = timeslices - (self.ts_start, self.ts_end) = timeslices.interval() + self.sched_tracer = sched_tracer + self.sched_tracer.set_root_win(self) + (self.ts_start, self.ts_end) = sched_tracer.interval() self.update_width_virtual() - self.nr_cpus = timeslices.max_cpu() + 1 - self.height_virtual = RootFrame.Y_OFFSET + (self.nr_cpus * (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE)) + self.nr_rects = sched_tracer.nr_rectangles() + 1 + self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) # whole window panel self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) @@ -87,69 +88,38 @@ class RootFrame(wx.Frame): (x, y) = self.scroll_start() return self.px_to_us(x) - def update_rectangle_cpu(self, dc, slice, cpu, offset_time): - rq = slice.rqs[cpu] - - if slice.total_load != 0: - load_rate = rq.load() / float(slice.total_load) - else: - load_rate = 0 + def paint_rectangle_zone(self, nr, color, top_color, start, end): + offset_px = self.us_to_px(start - self.ts_start) + width_px = self.us_to_px(end - self.ts_start) + offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) + width_py = RootFrame.RECT_HEIGHT - offset_px = self.us_to_px(slice.start - offset_time) - width_px = self.us_to_px(slice.end - slice.start) - (x, y) = self.scroll_start() + dc = self.dc - if width_px == 0: - return + if top_color is not None: + (r, g, b) = top_color + top_color = wx.Colour(r, g, b) + brush = wx.Brush(top_color, wx.SOLID) + dc.SetBrush(brush) + dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH) + width_py -= RootFrame.EVENT_MARKING_WIDTH + offset_py += RootFrame.EVENT_MARKING_WIDTH - offset_py = RootFrame.Y_OFFSET + (cpu * (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE)) - width_py = RootFrame.CPU_HEIGHT - - if cpu in slice.event_cpus: - rgb = rq.event.color() - if rgb is not None: - (r, g, b) = rgb - color = wx.Colour(r, g, b) - brush = wx.Brush(color, wx.SOLID) - dc.SetBrush(brush) - dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH) - width_py -= RootFrame.EVENT_MARKING_WIDTH - offset_py += RootFrame.EVENT_MARKING_WIDTH - - red_power = int(0xff - (0xff * load_rate)) - color = wx.Colour(0xff, red_power, red_power) + (r ,g, b) = color + color = wx.Colour(r, g, b) brush = wx.Brush(color, wx.SOLID) dc.SetBrush(brush) dc.DrawRectangle(offset_px, offset_py, width_px, width_py) def update_rectangles(self, dc, start, end): - if len(self.timeslices) == 0: - return - start += self.timeslices[0].start - end += self.timeslices[0].start - - color = wx.Colour(0, 0, 0) - brush = wx.Brush(color, wx.SOLID) - dc.SetBrush(brush) - - i = self.timeslices.find_time_slice(start) - if i == -1: - return - - for i in xrange(i, len(self.timeslices)): - timeslice = self.timeslices[i] - if timeslice.start > end: - return - - for cpu in timeslice.rqs: - self.update_rectangle_cpu(dc, timeslice, cpu, self.timeslices[0].start) + start += self.ts_start + end += self.ts_start + self.sched_tracer.fill_zone(start, end) def on_paint(self, event): - color = wx.Colour(0xff, 0xff, 0xff) - brush = wx.Brush(color, wx.SOLID) dc = wx.PaintDC(self.scroll_panel) - dc.SetBrush(brush) + self.dc = dc width = min(self.width_virtual, self.screen_width) (x, y) = self.scroll_start() @@ -157,45 +127,31 @@ class RootFrame(wx.Frame): end = self.px_to_us(x + width) self.update_rectangles(dc, start, end) - def cpu_from_ypixel(self, y): + def rect_from_ypixel(self, y): y -= RootFrame.Y_OFFSET - cpu = y / (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) - height = y % (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) + rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) + height = y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) - if cpu < 0 or cpu > self.nr_cpus - 1 or height > RootFrame.CPU_HEIGHT: + if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGHT: return -1 - return cpu - - def update_summary(self, cpu, t): - idx = self.timeslices.find_time_slice(t) - if idx == -1: - return - - ts = self.timeslices[idx] - rq = ts.rqs[cpu] - raw = "CPU: %d\n" % cpu - raw += "Last event : %s\n" % rq.event.__repr__() - raw += "Timestamp : %d.%06d\n" % (ts.start / (10 ** 9), (ts.start % (10 ** 9)) / 1000) - raw += "Duration : %6d us\n" % ((ts.end - ts.start) / (10 ** 6)) - raw += "Load = %d\n" % rq.load() - for t in rq.tasks: - raw += "%s \n" % thread_name(t) + return rect + def update_summary(self, txt): if self.txt: self.txt.Destroy() - self.txt = wx.StaticText(self.panel, -1, raw, (0, (self.screen_height / 2) + 50)) + self.txt = wx.StaticText(self.panel, -1, txt, (0, (self.screen_height / 2) + 50)) def on_mouse_down(self, event): (x, y) = event.GetPositionTuple() - cpu = self.cpu_from_ypixel(y) - if cpu == -1: + rect = self.rect_from_ypixel(y) + if rect == -1: return - t = self.px_to_us(x) + self.timeslices[0].start + t = self.px_to_us(x) + self.ts_start - self.update_summary(cpu, t) + self.sched_tracer.mouse_down(rect, t) def update_width_virtual(self): @@ -501,13 +457,64 @@ class TimeSliceList(UserList): return found + def set_root_win(self, win): + self.root_win = win + + def mouse_down(self, cpu, t): + idx = self.find_time_slice(t) + if idx == -1: + return + + ts = self[idx] + rq = ts.rqs[cpu] + raw = "CPU: %d\n" % cpu + raw += "Last event : %s\n" % rq.event.__repr__() + raw += "Timestamp : %d.%06d\n" % (ts.start / (10 ** 9), (ts.start % (10 ** 9)) / 1000) + raw += "Duration : %6d us\n" % ((ts.end - ts.start) / (10 ** 6)) + raw += "Load = %d\n" % rq.load() + for t in rq.tasks: + raw += "%s \n" % thread_name(t) + + self.root_win.update_summary(raw) + + def update_rectangle_cpu(self, slice, cpu): + rq = slice.rqs[cpu] + + if slice.total_load != 0: + load_rate = rq.load() / float(slice.total_load) + else: + load_rate = 0 + + red_power = int(0xff - (0xff * load_rate)) + color = (0xff, red_power, red_power) + + top_color = None + + if cpu in slice.event_cpus: + top_color = rq.event.color() + + self.root_win.paint_rectangle_zone(cpu, color, top_color, slice.start, slice.end) + + def fill_zone(self, start, end): + i = self.find_time_slice(start) + if i == -1: + return + + for i in xrange(i, len(self.data)): + timeslice = self.data[i] + if timeslice.start > end: + return + + for cpu in timeslice.rqs: + self.update_rectangle_cpu(timeslice, cpu) + def interval(self): if len(self.data) == 0: return (0, 0) return (self.data[0].start, self.data[-1].end) - def max_cpu(self): + def nr_rectangles(self): last_ts = self.data[-1] max_cpu = 0 for cpu in last_ts.rqs: @@ -557,7 +564,7 @@ def trace_begin(): def trace_end(): app = wx.App(False) timeslices = parser.timeslices - frame = RootFrame(timeslices) + frame = RootFrame(timeslices, "Migration") app.MainLoop() def sched__sched_stat_runtime(event_name, context, common_cpu, -- cgit v1.2.3-18-g5258 From df92b40848616596c50b3b9e6d6ce8252af606ee Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 26 Jul 2010 02:29:44 +0200 Subject: perf, sched migration: Librarize the GUI class Export the GUI facility in the common library path. It is going to be useful for other scheduler views. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- .../Perf-Trace-Util/lib/Perf/Trace/SchedGui.py | 184 +++++++++++++++++++++ tools/perf/scripts/python/sched-migration.py | 180 +------------------- 2 files changed, 189 insertions(+), 175 deletions(-) create mode 100644 tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py (limited to 'tools') diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py new file mode 100644 index 00000000000..ae9a56e43e0 --- /dev/null +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py @@ -0,0 +1,184 @@ +# SchedGui.py - Python extension for perf trace, basic GUI code for +# traces drawing and overview. +# +# Copyright (C) 2010 by Frederic Weisbecker +# +# This software is distributed under the terms of the GNU General +# Public License ("GPL") version 2 as published by the Free Software +# Foundation. + + +try: + import wx +except ImportError: + raise ImportError, "You need to install the wxpython lib for this script" + + +class RootFrame(wx.Frame): + Y_OFFSET = 100 + RECT_HEIGHT = 100 + RECT_SPACE = 50 + EVENT_MARKING_WIDTH = 5 + + def __init__(self, sched_tracer, title, parent = None, id = -1): + wx.Frame.__init__(self, parent, id, title) + + (self.screen_width, self.screen_height) = wx.GetDisplaySize() + self.screen_width -= 10 + self.screen_height -= 10 + self.zoom = 0.5 + self.scroll_scale = 20 + self.sched_tracer = sched_tracer + self.sched_tracer.set_root_win(self) + (self.ts_start, self.ts_end) = sched_tracer.interval() + self.update_width_virtual() + self.nr_rects = sched_tracer.nr_rectangles() + 1 + self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) + + # whole window panel + self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) + + # scrollable container + self.scroll = wx.ScrolledWindow(self.panel) + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale) + self.scroll.EnableScrolling(True, True) + self.scroll.SetFocus() + + # scrollable drawing area + self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width - 15, self.screen_height / 2)) + self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) + self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) + self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) + self.scroll.Bind(wx.EVT_PAINT, self.on_paint) + self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press) + self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) + + self.scroll.Fit() + self.Fit() + + self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_virtual, wx.SIZE_USE_EXISTING) + + self.txt = None + + self.Show(True) + + def us_to_px(self, val): + return val / (10 ** 3) * self.zoom + + def px_to_us(self, val): + return (val / self.zoom) * (10 ** 3) + + def scroll_start(self): + (x, y) = self.scroll.GetViewStart() + return (x * self.scroll_scale, y * self.scroll_scale) + + def scroll_start_us(self): + (x, y) = self.scroll_start() + return self.px_to_us(x) + + def paint_rectangle_zone(self, nr, color, top_color, start, end): + offset_px = self.us_to_px(start - self.ts_start) + width_px = self.us_to_px(end - self.ts_start) + + offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) + width_py = RootFrame.RECT_HEIGHT + + dc = self.dc + + if top_color is not None: + (r, g, b) = top_color + top_color = wx.Colour(r, g, b) + brush = wx.Brush(top_color, wx.SOLID) + dc.SetBrush(brush) + dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH) + width_py -= RootFrame.EVENT_MARKING_WIDTH + offset_py += RootFrame.EVENT_MARKING_WIDTH + + (r ,g, b) = color + color = wx.Colour(r, g, b) + brush = wx.Brush(color, wx.SOLID) + dc.SetBrush(brush) + dc.DrawRectangle(offset_px, offset_py, width_px, width_py) + + def update_rectangles(self, dc, start, end): + start += self.ts_start + end += self.ts_start + self.sched_tracer.fill_zone(start, end) + + def on_paint(self, event): + dc = wx.PaintDC(self.scroll_panel) + self.dc = dc + + width = min(self.width_virtual, self.screen_width) + (x, y) = self.scroll_start() + start = self.px_to_us(x) + end = self.px_to_us(x + width) + self.update_rectangles(dc, start, end) + + def rect_from_ypixel(self, y): + y -= RootFrame.Y_OFFSET + rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) + height = y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) + + if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGHT: + return -1 + + return rect + + def update_summary(self, txt): + if self.txt: + self.txt.Destroy() + self.txt = wx.StaticText(self.panel, -1, txt, (0, (self.screen_height / 2) + 50)) + + + def on_mouse_down(self, event): + (x, y) = event.GetPositionTuple() + rect = self.rect_from_ypixel(y) + if rect == -1: + return + + t = self.px_to_us(x) + self.ts_start + + self.sched_tracer.mouse_down(rect, t) + + + def update_width_virtual(self): + self.width_virtual = self.us_to_px(self.ts_end - self.ts_start) + + def __zoom(self, x): + self.update_width_virtual() + (xpos, ypos) = self.scroll.GetViewStart() + xpos = self.us_to_px(x) / self.scroll_scale + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xpos, ypos) + self.Refresh() + + def zoom_in(self): + x = self.scroll_start_us() + self.zoom *= 2 + self.__zoom(x) + + def zoom_out(self): + x = self.scroll_start_us() + self.zoom /= 2 + self.__zoom(x) + + + def on_key_press(self, event): + key = event.GetRawKeyCode() + if key == ord("+"): + self.zoom_in() + return + if key == ord("-"): + self.zoom_out() + return + + key = event.GetKeyCode() + (x, y) = self.scroll.GetViewStart() + if key == wx.WXK_RIGHT: + self.scroll.Scroll(x + 1, y) + elif key == wx.WXK_LEFT: + self.scroll.Scroll(x - 1, y) + elif key == wx.WXK_DOWN: + self.scroll.Scroll(x, y + 1) + elif key == wx.WXK_UP: + self.scroll.Scroll(x, y - 1) diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index 6d7281a7de3..983463050f0 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -6,14 +6,11 @@ # # perf trace event handlers have been generated by perf trace -g python # -# The whole is licensed under the terms of the GNU GPL License version 2 +# This software is distributed under the terms of the GNU General +# Public License ("GPL") version 2 as published by the Free Software +# Foundation. -try: - import wx -except ImportError: - raise ImportError, "You need to install the wxpython lib for this script" - import os import sys @@ -22,178 +19,11 @@ from UserList import UserList sys.path.append(os.environ['PERF_EXEC_PATH'] + \ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') +sys.path.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace') from perf_trace_context import * from Core import * - -class RootFrame(wx.Frame): - Y_OFFSET = 100 - RECT_HEIGHT = 100 - RECT_SPACE = 50 - EVENT_MARKING_WIDTH = 5 - - def __init__(self, sched_tracer, title, parent = None, id = -1): - wx.Frame.__init__(self, parent, id, title) - - (self.screen_width, self.screen_height) = wx.GetDisplaySize() - self.screen_width -= 10 - self.screen_height -= 10 - self.zoom = 0.5 - self.scroll_scale = 20 - self.sched_tracer = sched_tracer - self.sched_tracer.set_root_win(self) - (self.ts_start, self.ts_end) = sched_tracer.interval() - self.update_width_virtual() - self.nr_rects = sched_tracer.nr_rectangles() + 1 - self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) - - # whole window panel - self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) - - # scrollable container - self.scroll = wx.ScrolledWindow(self.panel) - self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale) - self.scroll.EnableScrolling(True, True) - self.scroll.SetFocus() - - # scrollable drawing area - self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width - 15, self.screen_height / 2)) - self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) - self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) - self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) - self.scroll.Bind(wx.EVT_PAINT, self.on_paint) - self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press) - self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) - - self.scroll.Fit() - self.Fit() - - self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_virtual, wx.SIZE_USE_EXISTING) - - self.txt = None - - self.Show(True) - - def us_to_px(self, val): - return val / (10 ** 3) * self.zoom - - def px_to_us(self, val): - return (val / self.zoom) * (10 ** 3) - - def scroll_start(self): - (x, y) = self.scroll.GetViewStart() - return (x * self.scroll_scale, y * self.scroll_scale) - - def scroll_start_us(self): - (x, y) = self.scroll_start() - return self.px_to_us(x) - - def paint_rectangle_zone(self, nr, color, top_color, start, end): - offset_px = self.us_to_px(start - self.ts_start) - width_px = self.us_to_px(end - self.ts_start) - - offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) - width_py = RootFrame.RECT_HEIGHT - - dc = self.dc - - if top_color is not None: - (r, g, b) = top_color - top_color = wx.Colour(r, g, b) - brush = wx.Brush(top_color, wx.SOLID) - dc.SetBrush(brush) - dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH) - width_py -= RootFrame.EVENT_MARKING_WIDTH - offset_py += RootFrame.EVENT_MARKING_WIDTH - - (r ,g, b) = color - color = wx.Colour(r, g, b) - brush = wx.Brush(color, wx.SOLID) - dc.SetBrush(brush) - dc.DrawRectangle(offset_px, offset_py, width_px, width_py) - - def update_rectangles(self, dc, start, end): - start += self.ts_start - end += self.ts_start - self.sched_tracer.fill_zone(start, end) - - def on_paint(self, event): - dc = wx.PaintDC(self.scroll_panel) - self.dc = dc - - width = min(self.width_virtual, self.screen_width) - (x, y) = self.scroll_start() - start = self.px_to_us(x) - end = self.px_to_us(x + width) - self.update_rectangles(dc, start, end) - - def rect_from_ypixel(self, y): - y -= RootFrame.Y_OFFSET - rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) - height = y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) - - if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGHT: - return -1 - - return rect - - def update_summary(self, txt): - if self.txt: - self.txt.Destroy() - self.txt = wx.StaticText(self.panel, -1, txt, (0, (self.screen_height / 2) + 50)) - - - def on_mouse_down(self, event): - (x, y) = event.GetPositionTuple() - rect = self.rect_from_ypixel(y) - if rect == -1: - return - - t = self.px_to_us(x) + self.ts_start - - self.sched_tracer.mouse_down(rect, t) - - - def update_width_virtual(self): - self.width_virtual = self.us_to_px(self.ts_end - self.ts_start) - - def __zoom(self, x): - self.update_width_virtual() - (xpos, ypos) = self.scroll.GetViewStart() - xpos = self.us_to_px(x) / self.scroll_scale - self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xpos, ypos) - self.Refresh() - - def zoom_in(self): - x = self.scroll_start_us() - self.zoom *= 2 - self.__zoom(x) - - def zoom_out(self): - x = self.scroll_start_us() - self.zoom /= 2 - self.__zoom(x) - - - def on_key_press(self, event): - key = event.GetRawKeyCode() - if key == ord("+"): - self.zoom_in() - return - if key == ord("-"): - self.zoom_out() - return - - key = event.GetKeyCode() - (x, y) = self.scroll.GetViewStart() - if key == wx.WXK_RIGHT: - self.scroll.Scroll(x + 1, y) - elif key == wx.WXK_LEFT: - self.scroll.Scroll(x - 1, y) - elif key == wx.WXK_DOWN: - self.scroll.Scroll(x, y + 1) - elif key == wx.WXK_UP: - self.scroll.Scroll(x, y - 1) +from SchedGui import * threads = { 0 : "idle"} -- cgit v1.2.3-18-g5258 From 1b0ff06e68155de606f86e7e69eb238f14e05ba0 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 1 Aug 2010 14:55:45 +0200 Subject: perf, sched migration: Librarize task states and event headers helpers Librarize the task state and event headers helpers as they can be generally useful. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- .../python/Perf-Trace-Util/lib/Perf/Trace/Core.py | 30 ++++++++++++++++++++++ tools/perf/scripts/python/sched-migration.py | 30 ---------------------- 2 files changed, 30 insertions(+), 30 deletions(-) (limited to 'tools') diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py index 1dc464ee2ca..aad7525bca1 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py @@ -89,3 +89,33 @@ def trace_flag_str(value): value &= ~idx return string + + +def taskState(state): + states = { + 0 : "R", + 1 : "S", + 2 : "D", + 64: "DEAD" + } + + if state not in states: + return "Unknown" + + return states[state] + + +class EventHeaders: + def __init__(self, common_cpu, common_secs, common_nsecs, + common_pid, common_comm): + self.cpu = common_cpu + self.secs = common_secs + self.nsecs = common_nsecs + self.pid = common_pid + self.comm = common_comm + + def ts(self): + return (self.secs * (10 ** 9)) + self.nsecs + + def ts_format(self): + return "%d.%d" % (self.secs, int(self.nsecs / 1000)) diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index 983463050f0..b934383c336 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -31,36 +31,6 @@ threads = { 0 : "idle"} def thread_name(pid): return "%s:%d" % (threads[pid], pid) -class EventHeaders: - def __init__(self, common_cpu, common_secs, common_nsecs, - common_pid, common_comm): - self.cpu = common_cpu - self.secs = common_secs - self.nsecs = common_nsecs - self.pid = common_pid - self.comm = common_comm - - def ts(self): - return (self.secs * (10 ** 9)) + self.nsecs - - def ts_format(self): - return "%d.%d" % (self.secs, int(self.nsecs / 1000)) - - -def taskState(state): - states = { - 0 : "R", - 1 : "S", - 2 : "D", - 64: "DEAD" - } - - if state not in states: - return "Unknown" - - return states[state] - - class RunqueueEventUnknown: @staticmethod def color(): -- cgit v1.2.3-18-g5258 From 076c6e45215aea0de1ed34d3d5079fabeaabf5e1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Aug 2010 18:18:28 -0300 Subject: perf session: Free the ref_reloc_sym memory at the right place Which is at perf_session__destroy_kernel_maps, counterpart to the perf_session__create_kernel_maps where the kmap structure is located, just after the vmlinux_maps. Make it also check if the kernel maps were actually created, which may not be the case if, for instance, perf_session__new can't complete due to permission problems in, for instance, a 'perf report' case, when a segfault will take place, that is how this was noticed. The problem was introduced in d65a458, thus post .35. This also adds code to release guest machines as them are also created in perf_session__create_kernel_maps, so should be deleted on this newly introduced counterpart, perf_session__destroy_kernel_maps. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/map.c | 18 +++++++++++------- tools/perf/util/map.h | 7 +++++++ tools/perf/util/session.c | 7 +++++++ tools/perf/util/symbol.c | 43 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/symbol.h | 2 ++ 5 files changed, 70 insertions(+), 7 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 15d6a6dd50c..801e6962b0a 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -506,6 +506,11 @@ void maps__insert(struct rb_root *maps, struct map *map) rb_insert_color(&map->rb_node, maps); } +void maps__remove(struct rb_root *self, struct map *map) +{ + rb_erase(&map->rb_node, self); +} + struct map *maps__find(struct rb_root *maps, u64 ip) { struct rb_node **p = &maps->rb_node; @@ -551,13 +556,6 @@ static void dsos__delete(struct list_head *self) void machine__exit(struct machine *self) { - struct kmap *kmap = map__kmap(self->vmlinux_maps[MAP__FUNCTION]); - - if (kmap->ref_reloc_sym) { - free((char *)kmap->ref_reloc_sym->name); - free(kmap->ref_reloc_sym); - } - map_groups__exit(&self->kmaps); dsos__delete(&self->user_dsos); dsos__delete(&self->kernel_dsos); @@ -565,6 +563,12 @@ void machine__exit(struct machine *self) self->root_dir = NULL; } +void machine__delete(struct machine *self) +{ + machine__exit(self); + free(self); +} + struct machine *machines__add(struct rb_root *self, pid_t pid, const char *root_dir) { diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 0e0984e86fc..5b51bbd2f73 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -125,6 +125,7 @@ void map__reloc_vmlinux(struct map *self); size_t __map_groups__fprintf_maps(struct map_groups *self, enum map_type type, int verbose, FILE *fp); void maps__insert(struct rb_root *maps, struct map *map); +void maps__remove(struct rb_root *self, struct map *map); struct map *maps__find(struct rb_root *maps, u64 addr); void map_groups__init(struct map_groups *self); void map_groups__exit(struct map_groups *self); @@ -144,6 +145,7 @@ struct machine *machines__findnew(struct rb_root *self, pid_t pid); char *machine__mmap_name(struct machine *self, char *bf, size_t size); int machine__init(struct machine *self, const char *root_dir, pid_t pid); void machine__exit(struct machine *self); +void machine__delete(struct machine *self); /* * Default guest kernel is defined by parameter --guestkallsyms @@ -165,6 +167,11 @@ static inline void map_groups__insert(struct map_groups *self, struct map *map) map->groups = self; } +static inline void map_groups__remove(struct map_groups *self, struct map *map) +{ + maps__remove(&self->maps[map->type], map); +} + static inline struct map *map_groups__find(struct map_groups *self, enum map_type type, u64 addr) { diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 04a3b3db9e9..5d2fd52fe7b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -79,6 +79,12 @@ int perf_session__create_kernel_maps(struct perf_session *self) return ret; } +static void perf_session__destroy_kernel_maps(struct perf_session *self) +{ + machine__destroy_kernel_maps(&self->host_machine); + machines__destroy_guest_kernel_maps(&self->machines); +} + struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) { size_t len = filename ? strlen(filename) + 1 : 0; @@ -150,6 +156,7 @@ static void perf_session__delete_threads(struct perf_session *self) void perf_session__delete(struct perf_session *self) { perf_header__exit(&self->header); + perf_session__destroy_kernel_maps(self); perf_session__delete_dead_threads(self); perf_session__delete_threads(self); machine__exit(&self->host_machine); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 3b8c0050667..6f0dd90c36c 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2107,6 +2107,36 @@ int __machine__create_kernel_maps(struct machine *self, struct dso *kernel) return 0; } +void machine__destroy_kernel_maps(struct machine *self) +{ + enum map_type type; + + for (type = 0; type < MAP__NR_TYPES; ++type) { + struct kmap *kmap; + + if (self->vmlinux_maps[type] == NULL) + continue; + + kmap = map__kmap(self->vmlinux_maps[type]); + map_groups__remove(&self->kmaps, self->vmlinux_maps[type]); + if (kmap->ref_reloc_sym) { + /* + * ref_reloc_sym is shared among all maps, so free just + * on one of them. + */ + if (type == MAP__FUNCTION) { + free((char *)kmap->ref_reloc_sym->name); + kmap->ref_reloc_sym->name = NULL; + free(kmap->ref_reloc_sym); + } + kmap->ref_reloc_sym = NULL; + } + + map__delete(self->vmlinux_maps[type]); + self->vmlinux_maps[type] = NULL; + } +} + int machine__create_kernel_maps(struct machine *self) { struct dso *kernel = machine__create_kernel(self); @@ -2351,6 +2381,19 @@ failure: return ret; } +void machines__destroy_guest_kernel_maps(struct rb_root *self) +{ + struct rb_node *next = rb_first(self); + + while (next) { + struct machine *pos = rb_entry(next, struct machine, rb_node); + + next = rb_next(&pos->rb_node); + rb_erase(&pos->rb_node, self); + machine__delete(pos); + } +} + int machine__load_kallsyms(struct machine *self, const char *filename, enum map_type type, symbol_filter_t filter) { diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 33d53ce2895..906be20011d 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -212,11 +212,13 @@ int kallsyms__parse(const char *filename, void *arg, int (*process_symbol)(void *arg, const char *name, char type, u64 start)); +void machine__destroy_kernel_maps(struct machine *self); int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); int machine__create_kernel_maps(struct machine *self); int machines__create_kernel_maps(struct rb_root *self, pid_t pid); int machines__create_guest_kernel_maps(struct rb_root *self); +void machines__destroy_guest_kernel_maps(struct rb_root *self); int symbol__init(void); void symbol__exit(void); -- cgit v1.2.3-18-g5258 From 70597f21f128b7dd6a2490078bea99d704b6f8c3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Aug 2010 18:59:28 -0300 Subject: perf session: Invalidate last_match when removing threads from rb_tree If we receive two PERF_RECORD_EXIT for the same thread, we can end up reusing session->last_match and trying to remove the thread twice from the rb_tree, causing a segfault, so invalidade last_match in perf_session__remove_thread. Receiving two PERF_RECORD_EXIT for the same thread is a bug, but its a harmless one if we make the tool more robust, like this patch does. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5d2fd52fe7b..fa9d652c2dc 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -166,6 +166,7 @@ void perf_session__delete(struct perf_session *self) void perf_session__remove_thread(struct perf_session *self, struct thread *th) { + self->last_match = NULL; rb_erase(&th->rb_node, &self->threads); /* * We may have references to this thread, for instance in some hist_entry -- cgit v1.2.3-18-g5258 From 0a1eae391d0d92b60cff9f55cdaf3861b4e33922 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Aug 2010 19:45:23 -0300 Subject: perf tools: Don't keep unreferenced maps when unmaps are detected For a file with: [root@emilia linux-2.6-tip]# perf report -D -fi allmodconfig-j32.perf.data | grep events: TOTAL events: 36933 MMAP events: 9056 LOST events: 0 COMM events: 1702 EXIT events: 1887 THROTTLE events: 8 UNTHROTTLE events: 8 FORK events: 1894 READ events: 0 SAMPLE events: 22378 ATTR events: 0 EVENT_TYPE events: 0 TRACING_DATA events: 0 BUILD_ID events: 0 [root@emilia linux-2.6-tip]# Testing with valgrind and making perf_session__delete() a nop, so that we can notice how many maps were actually deleted due to not having any samples on it: ==== HEAP SUMMARY: Before: ==10339== in use at exit: 8,909,997 bytes in 68,690 blocks ==10339== total heap usage: 78,696 allocs, 10,007 frees, 11,925,853 bytes allocated After: ==10506== in use at exit: 8,902,605 bytes in 68,606 blocks ==10506== total heap usage: 78,696 allocs, 10,091 frees, 11,925,853 bytes allocated I.e. just 84 detected unmaps with no hits out of 9056 for this workload, not much, but in some other long running workload this may save more bytes. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 2 ++ tools/perf/util/map.c | 31 +++++++++++++++++++++---------- tools/perf/util/map.h | 3 ++- 3 files changed, 25 insertions(+), 11 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index a6cea2894d1..e7263d49bcf 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -93,6 +93,8 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) if (self != NULL) { *self = *template; self->nr_events = 1; + if (self->ms.map) + self->ms.map->referenced = true; if (symbol_conf.use_callchain) callchain_init(self->callchain); } diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 801e6962b0a..3a7eb6ec0ee 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -29,6 +29,7 @@ void map__init(struct map *self, enum map_type type, self->unmap_ip = map__unmap_ip; RB_CLEAR_NODE(&self->rb_node); self->groups = NULL; + self->referenced = false; } struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, @@ -387,6 +388,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, { struct rb_root *root = &self->maps[map->type]; struct rb_node *next = rb_first(root); + int err = 0; while (next) { struct map *pos = rb_entry(next, struct map, rb_node); @@ -402,12 +404,6 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, } rb_erase(&pos->rb_node, root); - /* - * We may have references to this map, for instance in some - * hist_entry instances, so just move them to a separate - * list. - */ - list_add_tail(&pos->node, &self->removed_maps[map->type]); /* * Now check if we need to create new maps for areas not * overlapped by the new map: @@ -415,8 +411,10 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, if (map->start > pos->start) { struct map *before = map__clone(pos); - if (before == NULL) - return -ENOMEM; + if (before == NULL) { + err = -ENOMEM; + goto move_map; + } before->end = map->start - 1; map_groups__insert(self, before); @@ -427,14 +425,27 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, if (map->end < pos->end) { struct map *after = map__clone(pos); - if (after == NULL) - return -ENOMEM; + if (after == NULL) { + err = -ENOMEM; + goto move_map; + } after->start = map->end + 1; map_groups__insert(self, after); if (verbose >= 2) map__fprintf(after, fp); } +move_map: + /* + * If we have references, just move them to a separate list. + */ + if (pos->referenced) + list_add_tail(&pos->node, &self->removed_maps[map->type]); + else + map__delete(pos); + + if (err) + return err; } return 0; diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 5b51bbd2f73..78575796d5f 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -29,7 +29,8 @@ struct map { }; u64 start; u64 end; - enum map_type type; + u8 /* enum map_type */ type; + bool referenced; u32 priv; u64 pgoff; -- cgit v1.2.3-18-g5258 From b5a6325464b700c4bdac8799c495970516eed41c Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 3 Aug 2010 12:48:35 +0100 Subject: perf events: Fix mmap offset determination Fix buggy-looking code which unnecessarily adjusts the file offset fields read from /proc/*/maps. This may have gone unnoticed since the offset is usually 0 (and the logic in util/symbol.c may work incorrectly for other offset values). Commiter note: This fixes a bug introduced in 4af8b35, there is no need to shift pgoff twice, the show_map_vma routine in fs/proc/task_mmu.c already converts it from the number of pages to the size in bytes, and that is what appears in /proc/PID/map. Cc: Nicolas Pitre Cc: Will Deacon LKML-Reference: <1280836116-6654-2-git-send-email-dave.martin@linaro.org> Signed-off-by: Dave Martin Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 6b0db557792..db8a1d4b4d8 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -151,7 +151,6 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, continue; pbf += n + 3; if (*pbf == 'x') { /* vm_exec */ - u64 vm_pgoff; char *execname = strchr(bf, '/'); /* Catch VDSO */ @@ -162,12 +161,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, continue; pbf += 3; - n = hex2u64(pbf, &vm_pgoff); - /* pgoff is in bytes, not pages */ - if (n >= 0) - ev.mmap.pgoff = vm_pgoff << getpagesize(); - else - ev.mmap.pgoff = 0; + n = hex2u64(pbf, &ev.mmap.pgoff); size = strlen(execname); execname[size - 1] = '\0'; /* Remove \n */ -- cgit v1.2.3-18-g5258 From b83f920e179101a54721e5ab1d6c3edfb9d4bcbb Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Mon, 2 Aug 2010 18:08:51 +0530 Subject: perf: expose event__process function The event__process function is useful in processing /proc//maps. All of the functions that are called from event__process are defined in util/event.c. Though its defined in builtin-top.c, it could be reused for perf probe for uprobes. Hence moving it to util/event.c and exporting the function. LKML-Reference: <20100802123851.GD22812@linux.vnet.ibm.com> Signed-off-by: Srikar Dronamraju Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 20 -------------------- tools/perf/util/event.c | 20 ++++++++++++++++++++ tools/perf/util/event.h | 1 + 3 files changed, 21 insertions(+), 20 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 1e8e92e317b..b513e40974f 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1082,26 +1082,6 @@ static void event__process_sample(const event_t *self, } } -static int event__process(event_t *event, struct perf_session *session) -{ - switch (event->header.type) { - case PERF_RECORD_COMM: - event__process_comm(event, session); - break; - case PERF_RECORD_MMAP: - event__process_mmap(event, session); - break; - case PERF_RECORD_FORK: - case PERF_RECORD_EXIT: - event__process_task(event, session); - break; - default: - break; - } - - return 0; -} - struct mmap_data { int counter; void *base; diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index db8a1d4b4d8..dab9e754a28 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -548,6 +548,26 @@ int event__process_task(event_t *self, struct perf_session *session) return 0; } +int event__process(event_t *event, struct perf_session *session) +{ + switch (event->header.type) { + case PERF_RECORD_COMM: + event__process_comm(event, session); + break; + case PERF_RECORD_MMAP: + event__process_mmap(event, session); + break; + case PERF_RECORD_FORK: + case PERF_RECORD_EXIT: + event__process_task(event, session); + break; + default: + break; + } + + return 0; +} + void thread__find_addr_map(struct thread *self, struct perf_session *session, u8 cpumode, enum map_type type, pid_t pid, u64 addr, diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 887ee63bbb6..8e790dae702 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -154,6 +154,7 @@ int event__process_comm(event_t *self, struct perf_session *session); int event__process_lost(event_t *self, struct perf_session *session); int event__process_mmap(event_t *self, struct perf_session *session); int event__process_task(event_t *self, struct perf_session *session); +int event__process(event_t *event, struct perf_session *session); struct addr_location; int event__preprocess_sample(const event_t *self, struct perf_session *session, -- cgit v1.2.3-18-g5258 From c408fedfc4a1fa16e611ffd6f3280301b38614be Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 12:59:47 -0300 Subject: perf symbols: Store the symbol binding So that tools that wan't to act only on a subset of (weak, global, local) symbols can do so, such as the upcoming uprobes support in 'perf probe'. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Srikar Dronamraju Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 30 ++++++++++++++++++++++-------- tools/perf/util/symbol.h | 1 + 2 files changed, 23 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 6f0dd90c36c..b6f5970f910 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -131,7 +131,8 @@ static void map_groups__fixup_end(struct map_groups *self) __map_groups__fixup_end(self, i); } -static struct symbol *symbol__new(u64 start, u64 len, const char *name) +static struct symbol *symbol__new(u64 start, u64 len, u8 binding, + const char *name) { size_t namelen = strlen(name) + 1; struct symbol *self = calloc(1, (symbol_conf.priv_size + @@ -144,6 +145,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name) self->start = start; self->end = len ? start + len - 1 : start; + self->binding = binding; self->namelen = namelen - 1; pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); @@ -160,8 +162,11 @@ void symbol__delete(struct symbol *self) static size_t symbol__fprintf(struct symbol *self, FILE *fp) { - return fprintf(fp, " %llx-%llx %s\n", - self->start, self->end, self->name); + return fprintf(fp, " %llx-%llx %c %s\n", + self->start, self->end, + self->binding == STB_GLOBAL ? 'g' : + self->binding == STB_LOCAL ? 'l' : 'w', + self->name); } void dso__set_long_name(struct dso *self, char *name) @@ -453,6 +458,14 @@ struct process_kallsyms_args { struct dso *dso; }; +static u8 kallsyms2elf_type(char type) +{ + if (type == 'W') + return STB_WEAK; + + return isupper(type) ? STB_GLOBAL : STB_LOCAL; +} + static int map__process_kallsym_symbol(void *arg, const char *name, char type, u64 start) { @@ -466,7 +479,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name, /* * Will fix up the end later, when we have all symbols sorted. */ - sym = symbol__new(start, 0, name); + sym = symbol__new(start, 0, kallsyms2elf_type(type), name); if (sym == NULL) return -ENOMEM; @@ -661,7 +674,7 @@ static int dso__load_perf_map(struct dso *self, struct map *map, if (len + 2 >= line_len) continue; - sym = symbol__new(start, size, line + len); + sym = symbol__new(start, size, STB_GLOBAL, line + len); if (sym == NULL) goto out_delete_line; @@ -873,7 +886,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map, "%s@plt", elf_sym__name(&sym, symstrs)); f = symbol__new(plt_offset, shdr_plt.sh_entsize, - sympltname); + STB_GLOBAL, sympltname); if (!f) goto out_elf_end; @@ -895,7 +908,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map, "%s@plt", elf_sym__name(&sym, symstrs)); f = symbol__new(plt_offset, shdr_plt.sh_entsize, - sympltname); + STB_GLOBAL, sympltname); if (!f) goto out_elf_end; @@ -1146,7 +1159,8 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, if (demangled != NULL) elf_name = demangled; new_symbol: - f = symbol__new(sym.st_value, sym.st_size, elf_name); + f = symbol__new(sym.st_value, sym.st_size, + GELF_ST_BIND(sym.st_info), elf_name); free(demangled); if (!f) goto out_elf_end; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 906be20011d..b7a8da4af5a 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -53,6 +53,7 @@ struct symbol { u64 start; u64 end; u16 namelen; + u8 binding; char name[0]; }; -- cgit v1.2.3-18-g5258 From 9a725995e88fd3fd79daf99819c51d676ba37ad9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 17:00:42 -0300 Subject: perf ui: Add a map browser Press -> and then "Browse map details" to see the DSO long name as the title and the list of symbols in the DSO used by the map where the current symbol is. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Srikar Dronamraju Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 91de99b5844..fc4a2b3aa65 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -280,6 +281,7 @@ struct ui_browser { u16 top, left, width, height; void *priv; unsigned int (*refresh_entries)(struct ui_browser *self); + void (*write)(struct ui_browser *self, void *entry, int row); void (*seek)(struct ui_browser *self, off_t offset, int whence); u32 nr_entries; @@ -316,6 +318,58 @@ static void ui_browser__list_head_seek(struct ui_browser *self, self->first_visible_entry = pos; } +static void ui_browser__rb_tree_seek(struct ui_browser *self, + off_t offset, int whence) +{ + struct rb_root *root = self->entries; + struct rb_node *nd; + + switch (whence) { + case SEEK_SET: + nd = rb_first(root); + break; + case SEEK_CUR: + nd = self->first_visible_entry; + break; + case SEEK_END: + nd = rb_last(root); + break; + default: + return; + } + + if (offset > 0) { + while (offset-- != 0) + nd = rb_next(nd); + } else { + while (offset++ != 0) + nd = rb_prev(nd); + } + + self->first_visible_entry = nd; +} + +static unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) +{ + struct rb_node *nd; + int row = 0; + + if (self->first_visible_entry == NULL) + self->first_visible_entry = rb_first(self->entries); + + nd = self->first_visible_entry; + + while (nd != NULL) { + SLsmg_gotorc(self->top + row, self->left); + self->write(self, nd, row); + if (++row == self->height) + break; + nd = rb_next(nd); + } + + return row; +} + static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) { return (self->first_visible_entry_idx + row) == self->index; @@ -592,6 +646,70 @@ int hist_entry__tui_annotate(struct hist_entry *self) return ret; } +/* -------------------------------------------------------------------- */ + +struct map_browser { + struct ui_browser b; + struct map *map; + u16 namelen; + u8 addrlen; +}; + +static void map_browser__write(struct ui_browser *self, void *nd, int row) +{ + struct symbol *sym = rb_entry(nd, struct symbol, rb_node); + struct map_browser *mb = container_of(self, struct map_browser, b); + bool current_entry = ui_browser__is_current_entry(self, row); + int color = ui_browser__percent_color(0, current_entry); + + SLsmg_set_color(color); + slsmg_printf("%*llx %*llx %c ", + mb->addrlen, sym->start, mb->addrlen, sym->end, + sym->binding == STB_GLOBAL ? 'g' : + sym->binding == STB_LOCAL ? 'l' : 'w'); + slsmg_write_nstring(sym->name, mb->namelen); +} + +static int map__browse(struct map *self) +{ + struct map_browser mb = { + .b = { + .entries = &self->dso->symbols[self->type], + .refresh_entries = ui_browser__rb_tree_refresh, + .seek = ui_browser__rb_tree_seek, + .write = map_browser__write, + }, + }; + struct newtExitStruct es; + struct rb_node *nd; + char tmp[BITS_PER_LONG / 4]; + u64 maxaddr = 0; + int ret; + + ui_helpline__push("Press <- or ESC to exit"); + + for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) { + struct symbol *pos = rb_entry(nd, struct symbol, rb_node); + + if (mb.namelen < pos->namelen) + mb.namelen = pos->namelen; + if (maxaddr < pos->end) + maxaddr = pos->end; + ++mb.b.nr_entries; + } + + mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); + mb.b.width += mb.addrlen * 2 + 4 + mb.namelen; + ui_browser__show(&mb.b, self->dso->long_name); + ret = ui_browser__run(&mb.b, &es); + newtFormDestroy(mb.b.form); + newtPopWindow(); + ui_helpline__pop(); + return ret; +} + +/* -------------------------------------------------------------------- */ + struct hist_browser { struct ui_browser b; struct hists *hists; @@ -680,7 +798,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) const struct dso *dso; char *options[16]; int nr_options = 0, choice = 0, i, - annotate = -2, zoom_dso = -2, zoom_thread = -2; + annotate = -2, zoom_dso = -2, zoom_thread = -2, + browse_map = -2; if (hist_browser__run(browser, msg, &es)) break; @@ -771,6 +890,10 @@ do_help: (dso->kernel ? "the Kernel" : dso->short_name)) > 0) zoom_dso = nr_options++; + if (browser->selection->map != NULL && + asprintf(&options[nr_options], "Browse map details") > 0) + browse_map = nr_options++; + options[nr_options++] = (char *)"Exit"; choice = popup_menu(nr_options, options); @@ -800,7 +923,9 @@ do_annotate: continue; hist_entry__tui_annotate(he); - } else if (choice == zoom_dso) { + } else if (choice == browse_map) + map__browse(browser->selection->map); + else if (choice == zoom_dso) { zoom_dso: if (dso_filter) { pstack__remove(fstack, &dso_filter); -- cgit v1.2.3-18-g5258 From 76ce93d0b61fa8c61b9cd917d9f7190b40fb29b6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 17:02:54 -0300 Subject: perf ui: Shorten ui_browser->refresh_entries to refresh LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index fc4a2b3aa65..f98a2406dd8 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -280,7 +280,7 @@ struct ui_browser { void *first_visible_entry, *entries; u16 top, left, width, height; void *priv; - unsigned int (*refresh_entries)(struct ui_browser *self); + unsigned int (*refresh)(struct ui_browser *self); void (*write)(struct ui_browser *self, void *entry, int row); void (*seek)(struct ui_browser *self, off_t offset, int whence); @@ -472,12 +472,12 @@ static int objdump_line__show(struct objdump_line *self, struct list_head *head, return 0; } -static int ui_browser__refresh_entries(struct ui_browser *self) +static int ui_browser__refresh(struct ui_browser *self) { int row; newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); - row = self->refresh_entries(self); + row = self->refresh(self); SLsmg_set_color(HE_COLORSET_NORMAL); SLsmg_fill_region(self->top + row, self->left, self->height - row, self->width, ' '); @@ -487,7 +487,7 @@ static int ui_browser__refresh_entries(struct ui_browser *self) static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) { - if (ui_browser__refresh_entries(self) < 0) + if (ui_browser__refresh(self) < 0) return -1; while (1) { @@ -558,7 +558,7 @@ static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) default: return es->u.key; } - if (ui_browser__refresh_entries(self) < 0) + if (ui_browser__refresh(self) < 0) return -1; } return 0; @@ -621,9 +621,9 @@ int hist_entry__tui_annotate(struct hist_entry *self) ui_helpline__push("Press <- or ESC to exit"); memset(&browser, 0, sizeof(browser)); - browser.entries = &head; - browser.refresh_entries = hist_entry__annotate_browser_refresh; - browser.seek = ui_browser__list_head_seek; + browser.entries = &head; + browser.refresh = hist_entry__annotate_browser_refresh; + browser.seek = ui_browser__list_head_seek; browser.priv = self; list_for_each_entry(pos, &head, node) { size_t line_len = strlen(pos->line); @@ -675,7 +675,7 @@ static int map__browse(struct map *self) struct map_browser mb = { .b = { .entries = &self->dso->symbols[self->type], - .refresh_entries = ui_browser__rb_tree_refresh, + .refresh = ui_browser__rb_tree_refresh, .seek = ui_browser__rb_tree_seek, .write = map_browser__write, }, @@ -720,7 +720,7 @@ struct hist_browser { static void hist_browser__reset(struct hist_browser *self); static int hist_browser__run(struct hist_browser *self, const char *title, struct newtExitStruct *es); -static unsigned int hist_browser__refresh_entries(struct ui_browser *self); +static unsigned int hist_browser__refresh(struct ui_browser *self); static void ui_browser__hists_seek(struct ui_browser *self, off_t offset, int whence); @@ -730,7 +730,7 @@ static struct hist_browser *hist_browser__new(struct hists *hists) if (self) { self->hists = hists; - self->b.refresh_entries = hist_browser__refresh_entries; + self->b.refresh = hist_browser__refresh; self->b.seek = ui_browser__hists_seek; } @@ -1338,7 +1338,7 @@ static int hist_browser__show_entry(struct hist_browser *self, return printed; } -static unsigned int hist_browser__refresh_entries(struct ui_browser *self) +static unsigned int hist_browser__refresh(struct ui_browser *self) { unsigned row = 0; struct rb_node *nd; -- cgit v1.2.3-18-g5258 From 903cce6eb9117550755de9bf92f3b48367b7dfe0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 19:15:48 -0300 Subject: perf hists: Handle verbose in hists__sort_list_width Otherwise entries will get chopped up on the window. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e7263d49bcf..62ec9b0e4b9 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -876,6 +876,9 @@ unsigned int hists__sort_list_width(struct hists *self) if (!se->elide) ret += 2 + hists__col_len(self, se->se_width_idx); + if (verbose) /* Addr + origin */ + ret += 3 + BITS_PER_LONG / 4; + return ret; } -- cgit v1.2.3-18-g5258 From fb89941ea761f53201959cc217a2c73f6fe13855 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 19:17:22 -0300 Subject: perf hists: Fixup addr snprintf width on 32 bit arches By using BITS_PER_LONG/4 as the width specifier. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 1c61a4f4aa8..b62a553cc67 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -196,7 +196,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, if (verbose) { char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; - ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o); + ret += repsep_snprintf(bf, size, "%*Lx %c ", + BITS_PER_LONG / 4, self->ip, o); } ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); @@ -204,7 +205,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, ret += repsep_snprintf(bf + ret, size - ret, "%s", self->ms.sym->name); else - ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip); + ret += repsep_snprintf(bf + ret, size - ret, "%*Lx", + BITS_PER_LONG / 4, self->ip); return ret; } -- cgit v1.2.3-18-g5258 From 80d50cae1b9cc958171c36fffc7357a5abad808c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 19:28:27 -0300 Subject: perf ui: Add search by name/addr to the map__browser Only in verbose mode so as not to bloat struct symbol too much. The key used is '/', just like in vi, less, etc. More work is needed to allocate space on the symbol in a more clear way. This experiment shows how to do it for the hist_browser, in the main window. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Srikar Dronamraju Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 18 +++++++- tools/perf/util/newt.c | 109 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 117 insertions(+), 10 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 2f4b92925b2..4a7a7436245 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -478,8 +478,24 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) * so don't allocate extra space that won't be used in the stdio * implementation. */ - if (use_browser > 0) + if (use_browser > 0) { symbol_conf.priv_size = sizeof(struct sym_priv); + /* + * For searching by name on the "Browse map details". + * providing it only in verbose mode not to bloat too + * much struct symbol. + */ + if (verbose) { + /* + * XXX: Need to provide a less kludgy way to ask for + * more space per symbol, the u32 is for the index on + * the ui browser. + * See symbol__browser_index. + */ + symbol_conf.priv_size += sizeof(u32); + symbol_conf.sort_by_name = true; + } + } if (symbol__init() < 0) return -1; diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index f98a2406dd8..e2deae0704d 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -128,6 +128,39 @@ static void ui_helpline__puts(const char *msg) ui_helpline__push(msg); } +static int ui_entry__read(const char *title, char *bf, size_t size, int width) +{ + struct newtExitStruct es; + newtComponent form, entry; + const char *result; + int err = -1; + + newtCenteredWindow(width, 1, title); + form = newtForm(NULL, NULL, 0); + if (form == NULL) + return -1; + + entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL); + if (entry == NULL) + goto out_free_form; + + newtFormAddComponent(form, entry); + newtFormAddHotKey(form, NEWT_KEY_ENTER); + newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + newtFormAddHotKey(form, NEWT_KEY_LEFT); + newtFormAddHotKey(form, CTRL('c')); + newtFormRun(form, &es); + + if (result != NULL) { + strncpy(bf, result, size); + err = 0; + } +out_free_form: + newtPopWindow(); + newtFormDestroy(form); + return 0; +} + static char browser__last_msg[1024]; int browser__show_help(const char *format, va_list ap) @@ -670,6 +703,67 @@ static void map_browser__write(struct ui_browser *self, void *nd, int row) slsmg_write_nstring(sym->name, mb->namelen); } +/* FIXME uber-kludgy, see comment on cmd_report... */ +static u32 *symbol__browser_index(struct symbol *self) +{ + return ((void *)self) - sizeof(struct rb_node) - sizeof(u32); +} + +static int map_browser__search(struct map_browser *self) +{ + char target[512]; + struct symbol *sym; + int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40); + + if (err) + return err; + + if (target[0] == '0' && tolower(target[1]) == 'x') { + u64 addr = strtoull(target, NULL, 16); + sym = map__find_symbol(self->map, addr, NULL); + } else + sym = map__find_symbol_by_name(self->map, target, NULL); + + if (sym != NULL) { + u32 *idx = symbol__browser_index(sym); + + self->b.first_visible_entry = &sym->rb_node; + self->b.index = self->b.first_visible_entry_idx = *idx; + } else + ui_helpline__fpush("%s not found!", target); + + return 0; +} + +static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) +{ + if (ui_browser__show(&self->b, self->map->dso->long_name) < 0) + return -1; + + ui_helpline__fpush("Press <- or ESC to exit, %s / to search", + verbose ? "" : "restart with -v to use"); + newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); + if (verbose) + newtFormAddHotKey(self->b.form, '/'); + + while (1) { + ui_browser__run(&self->b, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + if (verbose && es->u.key == '/') + map_browser__search(self); + else + break; + } + + newtFormDestroy(self->b.form); + newtPopWindow(); + ui_helpline__pop(); + return 0; +} + static int map__browse(struct map *self) { struct map_browser mb = { @@ -679,14 +773,12 @@ static int map__browse(struct map *self) .seek = ui_browser__rb_tree_seek, .write = map_browser__write, }, + .map = self, }; struct newtExitStruct es; struct rb_node *nd; char tmp[BITS_PER_LONG / 4]; u64 maxaddr = 0; - int ret; - - ui_helpline__push("Press <- or ESC to exit"); for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) { struct symbol *pos = rb_entry(nd, struct symbol, rb_node); @@ -695,17 +787,16 @@ static int map__browse(struct map *self) mb.namelen = pos->namelen; if (maxaddr < pos->end) maxaddr = pos->end; + if (verbose) { + u32 *idx = symbol__browser_index(pos); + *idx = mb.b.nr_entries; + } ++mb.b.nr_entries; } mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); mb.b.width += mb.addrlen * 2 + 4 + mb.namelen; - ui_browser__show(&mb.b, self->dso->long_name); - ret = ui_browser__run(&mb.b, &es); - newtFormDestroy(mb.b.form); - newtPopWindow(); - ui_helpline__pop(); - return ret; + return map_browser__run(&mb, &es); } /* -------------------------------------------------------------------- */ -- cgit v1.2.3-18-g5258 From 7935f65f77e1cd19fe0a094fc1b34e258565751e Mon Sep 17 00:00:00 2001 From: Andrea Gelmini Date: Thu, 5 Aug 2010 15:51:39 +0200 Subject: perf probe: Remove duplicated #include Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Frederic Weisbecker LKML-Reference: <1281016299-23958-15-git-send-email-andrea.gelmini@gelma.net> Signed-off-by: Andrea Gelmini Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 1 - 1 file changed, 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 840f1aabbb7..6c7750da43d 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -33,7 +33,6 @@ #include #include -#include "string.h" #include "event.h" #include "debug.h" #include "util.h" -- cgit v1.2.3-18-g5258 From b7eead86d2951dab733d36554e8166a2a91d7363 Mon Sep 17 00:00:00 2001 From: Andrea Gelmini Date: Thu, 5 Aug 2010 15:51:38 +0200 Subject: perf trace: Clean up #includes Removed duplicated #includes util/trace-event.h and util/exec_cmd.h. Grouped and sorted all the #includes. Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Tom Zanussi Cc: Frederic Weisbecker Cc: Thomas Gleixner LKML-Reference: <1281016299-23958-14-git-send-email-andrea.gelmini@gelma.net> Signed-off-by: Andrea Gelmini Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 294da725a57..40a6a2992d1 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1,13 +1,16 @@ #include "builtin.h" -#include "util/util.h" +#include "perf.h" #include "util/cache.h" +#include "util/debug.h" +#include "util/exec_cmd.h" +#include "util/header.h" +#include "util/parse-options.h" +#include "util/session.h" #include "util/symbol.h" #include "util/thread.h" -#include "util/header.h" -#include "util/exec_cmd.h" #include "util/trace-event.h" -#include "util/session.h" +#include "util/util.h" static char const *script_name; static char const *generate_script_lang; @@ -59,14 +62,6 @@ static int cleanup_scripting(void) return scripting_ops->stop_script(); } -#include "util/parse-options.h" - -#include "perf.h" -#include "util/debug.h" - -#include "util/trace-event.h" -#include "util/exec_cmd.h" - static char const *input_name = "perf.data"; static int process_sample_event(event_t *event, struct perf_session *session) -- cgit v1.2.3-18-g5258 From 33e26a1b492d21283d1372ddd547562d371dab98 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 5 Aug 2010 22:27:51 +0200 Subject: perf timechart: Adjust confusing if indentation Outdent the code following the if. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r disable braces4@ position p1,p2; statement S1,S2; @@ ( if (...) { ... } | if (...) S1@p1 S2@p2 ) @script:python@ p1 << r.p1; p2 << r.p2; @@ if (p1[0].column == p2[0].column): cocci.print_main("branch",p1) cocci.print_secs("after",p2) // Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar LKML-Reference: Signed-off-by: Julia Lawall Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-timechart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 5161619d471..9bcc38f0b70 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -455,8 +455,8 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) if (p->current->state != TYPE_NONE) pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp); - p->current->state_since = timestamp; - p->current->state = TYPE_RUNNING; + p->current->state_since = timestamp; + p->current->state = TYPE_RUNNING; } if (prev_p->current) { -- cgit v1.2.3-18-g5258 From 71e7cf3a37ba6189fa7215555e8e760b400fc23b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 19:41:44 -0300 Subject: perf report: Speed up exit path When cmd_record exits the whole perf binary will exit right after, so no need to traverse lots of complex data structures freeing them. Sticked a comment for leak detectives and for a experiment with obstacks to be performed so that we can speed up freeing that memory. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Nick Piggin Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 4a7a7436245..55fc1f46892 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -348,7 +348,18 @@ static int __cmd_report(void) hists__tty_browse_tree(&session->hists_tree, help); out_delete: - perf_session__delete(session); + /* + * Speed up the exit process, for large files this can + * take quite a while. + * + * XXX Enable this when using valgrind or if we ever + * librarize this command. + * + * Also experiment with obstacks to see how much speed + * up we'll get here. + * + * perf_session__delete(session); + */ return ret; } -- cgit v1.2.3-18-g5258 From 58432e1f3625ef22b347ec8f9487e1852aa9ad67 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 3 Aug 2010 11:11:36 +0900 Subject: perf probe: Fix to copy the type for raw parameters Copy type field if it is for raw parameters. Without this fix, perf probe drops the type if user passes it for raw parameters (e.g. %ax:u32 will be converted to %ax). Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Frederic Weisbecker Signed-off-by: Masami Hiramatsu LKML-Reference: <4C577AD8.50808@hitachi.com> Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 6c7750da43d..525136684d4 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -705,8 +705,12 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) pf->tvar->value = strdup(pf->pvar->var); if (pf->tvar->value == NULL) return -ENOMEM; - else - return 0; + if (pf->pvar->type) { + pf->tvar->type = strdup(pf->pvar->type); + if (pf->tvar->type == NULL) + return -ENOMEM; + } + return 0; } pr_debug("Searching '%s' variable in context.\n", -- cgit v1.2.3-18-g5258 From 449e5b247ca7c9dc9fc3391b7eebfefdeb2ce1fc Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 3 Aug 2010 11:11:40 +0900 Subject: perf probe: Fix memory leaks in add_perf_probe_events Fix several memory leaks of pkgs and tevs in add_perf_probe_events(). Reported-by: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: linux-kernel@vger.kernel.org LKML-Reference: <4C577ADC.1000309@hitachi.com> Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 2e665cb8405..e72f05c3bef 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -1606,8 +1606,10 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, /* Init vmlinux path */ ret = init_vmlinux(); - if (ret < 0) + if (ret < 0) { + free(pkgs); return ret; + } /* Loop 1: convert all events */ for (i = 0; i < npevs; i++) { @@ -1625,10 +1627,13 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, pkgs[i].ntevs, force_add); end: - /* Loop 3: cleanup trace events */ - for (i = 0; i < npevs; i++) + /* Loop 3: cleanup and free trace events */ + for (i = 0; i < npevs; i++) { for (j = 0; j < pkgs[i].ntevs; j++) clear_probe_trace_event(&pkgs[i].tevs[j]); + free(pkgs[i].tevs); + } + free(pkgs); return ret; } -- cgit v1.2.3-18-g5258 From 43730982c3e9355dd8bd6b31f0a0a3508ad4209d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 6 Aug 2010 16:51:12 -0300 Subject: perf tui: Introduce list_head based generic ui_browser refresh routine So that building other browser based on structures linked via a linked list can be as easy as it is already for the ones linked via an rb_tree. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/include/linux/list.h | 8 ++++++ tools/perf/util/newt.c | 49 ++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 25 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index dbe4b814382..f5ca26e53fb 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h @@ -15,4 +15,12 @@ static inline void list_del_range(struct list_head *begin, begin->prev->next = end->next; end->next->prev = begin->prev; } + +/** + * list_for_each_from - iterate over a list from one of its nodes + * @pos: the &struct list_head to use as a loop cursor, from where to start + * @head: the head for your list. + */ +#define list_for_each_from(pos, head) \ + for (; prefetch(pos->next), pos != (head); pos = pos->next) #endif diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e2deae0704d..37fe8eb811c 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -456,20 +456,24 @@ static int ui_browser__show(struct ui_browser *self, const char *title) return 0; } -static int objdump_line__show(struct objdump_line *self, struct list_head *head, - int width, struct hist_entry *he, int len, - bool current_entry) +static void annotate_browser__write(struct ui_browser *self, void *entry, int row) { - if (self->offset != -1) { + struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); + bool current_entry = ui_browser__is_current_entry(self, row); + int width = self->width; + + if (ol->offset != -1) { + struct hist_entry *he = self->priv; struct symbol *sym = he->ms.sym; + int len = he->ms.sym->end - he->ms.sym->start; unsigned int hits = 0; double percent = 0.0; int color; struct sym_priv *priv = symbol__priv(sym); struct sym_ext *sym_ext = priv->ext; struct sym_hist *h = priv->hist; - s64 offset = self->offset; - struct objdump_line *next = objdump__get_next_ip_line(head, self); + s64 offset = ol->offset; + struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol); while (offset < (s64)len && (next == NULL || offset < next->offset)) { @@ -497,12 +501,10 @@ static int objdump_line__show(struct objdump_line *self, struct list_head *head, SLsmg_write_char(':'); slsmg_write_nstring(" ", 8); - if (!*self->line) + if (!*ol->line) slsmg_write_nstring(" ", width - 18); else - slsmg_write_nstring(self->line, width - 18); - - return 0; + slsmg_write_nstring(ol->line, width - 18); } static int ui_browser__refresh(struct ui_browser *self) @@ -607,24 +609,20 @@ static char *callchain_list__sym_name(struct callchain_list *self, return bf; } -static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self) +static unsigned int ui_browser__list_head_refresh(struct ui_browser *self) { - struct objdump_line *pos; + struct list_head *pos; struct list_head *head = self->entries; - struct hist_entry *he = self->priv; int row = 0; - int len = he->ms.sym->end - he->ms.sym->start; if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) self->first_visible_entry = head->next; - pos = list_entry(self->first_visible_entry, struct objdump_line, node); + pos = self->first_visible_entry; - list_for_each_entry_from(pos, head, node) { - bool current_entry = ui_browser__is_current_entry(self, row); + list_for_each_from(pos, head) { SLsmg_gotorc(self->top + row, self->left); - objdump_line__show(pos, head, self->width, - he, len, current_entry); + self->write(self, pos, row); if (++row == self->height) break; } @@ -634,10 +632,16 @@ static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self int hist_entry__tui_annotate(struct hist_entry *self) { - struct ui_browser browser; struct newtExitStruct es; struct objdump_line *pos, *n; LIST_HEAD(head); + struct ui_browser browser = { + .entries = &head, + .refresh = ui_browser__list_head_refresh, + .seek = ui_browser__list_head_seek, + .write = annotate_browser__write, + .priv = self, + }; int ret; if (self->ms.sym == NULL) @@ -653,11 +657,6 @@ int hist_entry__tui_annotate(struct hist_entry *self) ui_helpline__push("Press <- or ESC to exit"); - memset(&browser, 0, sizeof(browser)); - browser.entries = &head; - browser.refresh = hist_entry__annotate_browser_refresh; - browser.seek = ui_browser__list_head_seek; - browser.priv = self; list_for_each_entry(pos, &head, node) { size_t line_len = strlen(pos->line); if (browser.width < line_len) -- cgit v1.2.3-18-g5258 From ef8f34aabf2450a9fb36b2c87fe0ea0b86a38195 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 6 Aug 2010 17:35:02 -0300 Subject: perf ui: Start breaking down newt.c into multiple files As new TUI features get added the newt.c file is growing a lot and its name is growing misleading as an effort is being made to reduce the coupling with libnewt. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 10 +- tools/perf/util/newt.c | 306 +----------------------------------------- tools/perf/util/ui/browser.c | 313 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/ui/browser.h | 43 ++++++ 4 files changed, 368 insertions(+), 304 deletions(-) create mode 100644 tools/perf/util/ui/browser.c create mode 100644 tools/perf/util/ui/browser.h (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 26f626d45a9..d5bce768b4b 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -157,9 +157,8 @@ all:: # # Define NO_DWARF if you do not want debug-info analysis feature at all. -$(shell sh -c 'mkdir -p $(OUTPUT)scripts/python/Perf-Trace-Util/' 2> /dev/null) -$(shell sh -c 'mkdir -p $(OUTPUT)scripts/perl/Perf-Trace-Util/' 2> /dev/null) -$(shell sh -c 'mkdir -p $(OUTPUT)util/scripting-engines/' 2> /dev/null) +$(shell sh -c 'mkdir -p $(OUTPUT)scripts/{perl,python}/Perf-Trace-Util/' 2> /dev/null) +$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui,scripting-engines}/' 2> /dev/null) $(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null) $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE @@ -569,6 +568,8 @@ else BASIC_CFLAGS += -I/usr/include/slang EXTLIBS += -lnewt -lslang LIB_OBJS += $(OUTPUT)util/newt.o + LIB_OBJS += $(OUTPUT)util/ui/browser.o + LIB_H += util/ui/browser.h endif endif @@ -969,6 +970,9 @@ $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< +$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 37fe8eb811c..266a9e06525 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -23,6 +23,7 @@ #include "session.h" #include "sort.h" #include "symbol.h" +#include "ui/browser.h" #if SLANG_VERSION < 20104 #define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args) @@ -35,6 +36,8 @@ #define sltt_set_color SLtt_set_color #endif +newtComponent newt_form__new(void); + struct ui_progress { newtComponent form, scale; }; @@ -190,7 +193,7 @@ static void newt_form__set_exit_keys(newtComponent self) newtFormAddHotKey(self, CTRL('c')); } -static newtComponent newt_form__new(void) +newtComponent newt_form__new(void) { newtComponent self = newtForm(NULL, NULL, 0); if (self) @@ -290,172 +293,6 @@ static void ui__error_window(const char *fmt, ...) va_end(ap); } -#define HE_COLORSET_TOP 50 -#define HE_COLORSET_MEDIUM 51 -#define HE_COLORSET_NORMAL 52 -#define HE_COLORSET_SELECTED 53 -#define HE_COLORSET_CODE 54 - -static int ui_browser__percent_color(double percent, bool current) -{ - if (current) - return HE_COLORSET_SELECTED; - if (percent >= MIN_RED) - return HE_COLORSET_TOP; - if (percent >= MIN_GREEN) - return HE_COLORSET_MEDIUM; - return HE_COLORSET_NORMAL; -} - -struct ui_browser { - newtComponent form, sb; - u64 index, first_visible_entry_idx; - void *first_visible_entry, *entries; - u16 top, left, width, height; - void *priv; - unsigned int (*refresh)(struct ui_browser *self); - void (*write)(struct ui_browser *self, void *entry, int row); - void (*seek)(struct ui_browser *self, - off_t offset, int whence); - u32 nr_entries; -}; - -static void ui_browser__list_head_seek(struct ui_browser *self, - off_t offset, int whence) -{ - struct list_head *head = self->entries; - struct list_head *pos; - - switch (whence) { - case SEEK_SET: - pos = head->next; - break; - case SEEK_CUR: - pos = self->first_visible_entry; - break; - case SEEK_END: - pos = head->prev; - break; - default: - return; - } - - if (offset > 0) { - while (offset-- != 0) - pos = pos->next; - } else { - while (offset++ != 0) - pos = pos->prev; - } - - self->first_visible_entry = pos; -} - -static void ui_browser__rb_tree_seek(struct ui_browser *self, - off_t offset, int whence) -{ - struct rb_root *root = self->entries; - struct rb_node *nd; - - switch (whence) { - case SEEK_SET: - nd = rb_first(root); - break; - case SEEK_CUR: - nd = self->first_visible_entry; - break; - case SEEK_END: - nd = rb_last(root); - break; - default: - return; - } - - if (offset > 0) { - while (offset-- != 0) - nd = rb_next(nd); - } else { - while (offset++ != 0) - nd = rb_prev(nd); - } - - self->first_visible_entry = nd; -} - -static unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) -{ - struct rb_node *nd; - int row = 0; - - if (self->first_visible_entry == NULL) - self->first_visible_entry = rb_first(self->entries); - - nd = self->first_visible_entry; - - while (nd != NULL) { - SLsmg_gotorc(self->top + row, self->left); - self->write(self, nd, row); - if (++row == self->height) - break; - nd = rb_next(nd); - } - - return row; -} - -static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) -{ - return (self->first_visible_entry_idx + row) == self->index; -} - -static void ui_browser__refresh_dimensions(struct ui_browser *self) -{ - int cols, rows; - newtGetScreenSize(&cols, &rows); - - if (self->width > cols - 4) - self->width = cols - 4; - self->height = rows - 5; - if (self->height > self->nr_entries) - self->height = self->nr_entries; - self->top = (rows - self->height) / 2; - self->left = (cols - self->width) / 2; -} - -static void ui_browser__reset_index(struct ui_browser *self) -{ - self->index = self->first_visible_entry_idx = 0; - self->seek(self, 0, SEEK_SET); -} - -static int ui_browser__show(struct ui_browser *self, const char *title) -{ - if (self->form != NULL) { - newtFormDestroy(self->form); - newtPopWindow(); - } - ui_browser__refresh_dimensions(self); - newtCenteredWindow(self->width, self->height, title); - self->form = newt_form__new(); - if (self->form == NULL) - return -1; - - self->sb = newtVerticalScrollbar(self->width, 0, self->height, - HE_COLORSET_NORMAL, - HE_COLORSET_SELECTED); - if (self->sb == NULL) - return -1; - - newtFormAddHotKey(self->form, NEWT_KEY_UP); - newtFormAddHotKey(self->form, NEWT_KEY_DOWN); - newtFormAddHotKey(self->form, NEWT_KEY_PGUP); - newtFormAddHotKey(self->form, NEWT_KEY_PGDN); - newtFormAddHotKey(self->form, NEWT_KEY_HOME); - newtFormAddHotKey(self->form, NEWT_KEY_END); - newtFormAddComponent(self->form, self->sb); - return 0; -} - static void annotate_browser__write(struct ui_browser *self, void *entry, int row) { struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); @@ -507,98 +344,6 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro slsmg_write_nstring(ol->line, width - 18); } -static int ui_browser__refresh(struct ui_browser *self) -{ - int row; - - newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); - row = self->refresh(self); - SLsmg_set_color(HE_COLORSET_NORMAL); - SLsmg_fill_region(self->top + row, self->left, - self->height - row, self->width, ' '); - - return 0; -} - -static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) -{ - if (ui_browser__refresh(self) < 0) - return -1; - - while (1) { - off_t offset; - - newtFormRun(self->form, es); - - if (es->reason != NEWT_EXIT_HOTKEY) - break; - if (is_exit_key(es->u.key)) - return es->u.key; - switch (es->u.key) { - case NEWT_KEY_DOWN: - if (self->index == self->nr_entries - 1) - break; - ++self->index; - if (self->index == self->first_visible_entry_idx + self->height) { - ++self->first_visible_entry_idx; - self->seek(self, +1, SEEK_CUR); - } - break; - case NEWT_KEY_UP: - if (self->index == 0) - break; - --self->index; - if (self->index < self->first_visible_entry_idx) { - --self->first_visible_entry_idx; - self->seek(self, -1, SEEK_CUR); - } - break; - case NEWT_KEY_PGDN: - case ' ': - if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) - break; - - offset = self->height; - if (self->index + offset > self->nr_entries - 1) - offset = self->nr_entries - 1 - self->index; - self->index += offset; - self->first_visible_entry_idx += offset; - self->seek(self, +offset, SEEK_CUR); - break; - case NEWT_KEY_PGUP: - if (self->first_visible_entry_idx == 0) - break; - - if (self->first_visible_entry_idx < self->height) - offset = self->first_visible_entry_idx; - else - offset = self->height; - - self->index -= offset; - self->first_visible_entry_idx -= offset; - self->seek(self, -offset, SEEK_CUR); - break; - case NEWT_KEY_HOME: - ui_browser__reset_index(self); - break; - case NEWT_KEY_END: - offset = self->height - 1; - if (offset >= self->nr_entries) - offset = self->nr_entries - 1; - - self->index = self->nr_entries - 1; - self->first_visible_entry_idx = self->index - offset; - self->seek(self, -offset, SEEK_END); - break; - default: - return es->u.key; - } - if (ui_browser__refresh(self) < 0) - return -1; - } - return 0; -} - static char *callchain_list__sym_name(struct callchain_list *self, char *bf, size_t bfsize) { @@ -609,27 +354,6 @@ static char *callchain_list__sym_name(struct callchain_list *self, return bf; } -static unsigned int ui_browser__list_head_refresh(struct ui_browser *self) -{ - struct list_head *pos; - struct list_head *head = self->entries; - int row = 0; - - if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) - self->first_visible_entry = head->next; - - pos = self->first_visible_entry; - - list_for_each_from(pos, head) { - SLsmg_gotorc(self->top + row, self->left); - self->write(self, pos, row); - if (++row == self->height) - break; - } - - return row; -} - int hist_entry__tui_annotate(struct hist_entry *self) { struct newtExitStruct es; @@ -1093,20 +817,6 @@ int hists__tui_browse_tree(struct rb_root *self, const char *help) return key; } -static struct newtPercentTreeColors { - const char *topColorFg, *topColorBg; - const char *mediumColorFg, *mediumColorBg; - const char *normalColorFg, *normalColorBg; - const char *selColorFg, *selColorBg; - const char *codeColorFg, *codeColorBg; -} defaultPercentTreeColors = { - "red", "lightgray", - "green", "lightgray", - "black", "lightgray", - "lightgray", "magenta", - "blue", "lightgray", -}; - static void newt_suspend(void *d __used) { newtSuspend(); @@ -1116,8 +826,6 @@ static void newt_suspend(void *d __used) void setup_browser(void) { - struct newtPercentTreeColors *c = &defaultPercentTreeColors; - if (!isatty(1) || !use_browser || dump_trace) { use_browser = 0; setup_pager(); @@ -1129,11 +837,7 @@ void setup_browser(void) newtCls(); newtSetSuspendCallback(newt_suspend, NULL); ui_helpline__puts(" "); - sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); - sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); - sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); - sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); - sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); + ui_browser__init(); } void exit_browser(bool wait_for_ok) diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c new file mode 100644 index 00000000000..0b2b9306312 --- /dev/null +++ b/tools/perf/util/ui/browser.c @@ -0,0 +1,313 @@ +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +/* + * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks + * the build if it isn't defined. Use the equivalent one that glibc + * has on features.h. + */ +#include +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG +#endif +#include +#include +#include +#include +#include +#include "browser.h" +#include "../color.h" +#include "../util.h" + +#if SLANG_VERSION < 20104 +#define sltt_set_color(obj, name, fg, bg) \ + SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg) +#else +#define sltt_set_color SLtt_set_color +#endif + +newtComponent newt_form__new(void); + +int ui_browser__percent_color(double percent, bool current) +{ + if (current) + return HE_COLORSET_SELECTED; + if (percent >= MIN_RED) + return HE_COLORSET_TOP; + if (percent >= MIN_GREEN) + return HE_COLORSET_MEDIUM; + return HE_COLORSET_NORMAL; +} + +void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) +{ + struct list_head *head = self->entries; + struct list_head *pos; + + switch (whence) { + case SEEK_SET: + pos = head->next; + break; + case SEEK_CUR: + pos = self->first_visible_entry; + break; + case SEEK_END: + pos = head->prev; + break; + default: + return; + } + + if (offset > 0) { + while (offset-- != 0) + pos = pos->next; + } else { + while (offset++ != 0) + pos = pos->prev; + } + + self->first_visible_entry = pos; +} + +void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence) +{ + struct rb_root *root = self->entries; + struct rb_node *nd; + + switch (whence) { + case SEEK_SET: + nd = rb_first(root); + break; + case SEEK_CUR: + nd = self->first_visible_entry; + break; + case SEEK_END: + nd = rb_last(root); + break; + default: + return; + } + + if (offset > 0) { + while (offset-- != 0) + nd = rb_next(nd); + } else { + while (offset++ != 0) + nd = rb_prev(nd); + } + + self->first_visible_entry = nd; +} + +unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) +{ + struct rb_node *nd; + int row = 0; + + if (self->first_visible_entry == NULL) + self->first_visible_entry = rb_first(self->entries); + + nd = self->first_visible_entry; + + while (nd != NULL) { + SLsmg_gotorc(self->top + row, self->left); + self->write(self, nd, row); + if (++row == self->height) + break; + nd = rb_next(nd); + } + + return row; +} + +bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) +{ + return (self->first_visible_entry_idx + row) == self->index; +} + +void ui_browser__refresh_dimensions(struct ui_browser *self) +{ + int cols, rows; + newtGetScreenSize(&cols, &rows); + + if (self->width > cols - 4) + self->width = cols - 4; + self->height = rows - 5; + if (self->height > self->nr_entries) + self->height = self->nr_entries; + self->top = (rows - self->height) / 2; + self->left = (cols - self->width) / 2; +} + +void ui_browser__reset_index(struct ui_browser *self) +{ + self->index = self->first_visible_entry_idx = 0; + self->seek(self, 0, SEEK_SET); +} + +int ui_browser__show(struct ui_browser *self, const char *title) +{ + if (self->form != NULL) { + newtFormDestroy(self->form); + newtPopWindow(); + } + ui_browser__refresh_dimensions(self); + newtCenteredWindow(self->width, self->height, title); + self->form = newt_form__new(); + if (self->form == NULL) + return -1; + + self->sb = newtVerticalScrollbar(self->width, 0, self->height, + HE_COLORSET_NORMAL, + HE_COLORSET_SELECTED); + if (self->sb == NULL) + return -1; + + newtFormAddHotKey(self->form, NEWT_KEY_UP); + newtFormAddHotKey(self->form, NEWT_KEY_DOWN); + newtFormAddHotKey(self->form, NEWT_KEY_PGUP); + newtFormAddHotKey(self->form, NEWT_KEY_PGDN); + newtFormAddHotKey(self->form, NEWT_KEY_HOME); + newtFormAddHotKey(self->form, NEWT_KEY_END); + newtFormAddComponent(self->form, self->sb); + return 0; +} + +int ui_browser__refresh(struct ui_browser *self) +{ + int row; + + newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); + row = self->refresh(self); + SLsmg_set_color(HE_COLORSET_NORMAL); + SLsmg_fill_region(self->top + row, self->left, + self->height - row, self->width, ' '); + + return 0; +} + +int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) +{ + if (ui_browser__refresh(self) < 0) + return -1; + + while (1) { + off_t offset; + + newtFormRun(self->form, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + if (is_exit_key(es->u.key)) + return es->u.key; + switch (es->u.key) { + case NEWT_KEY_DOWN: + if (self->index == self->nr_entries - 1) + break; + ++self->index; + if (self->index == self->first_visible_entry_idx + self->height) { + ++self->first_visible_entry_idx; + self->seek(self, +1, SEEK_CUR); + } + break; + case NEWT_KEY_UP: + if (self->index == 0) + break; + --self->index; + if (self->index < self->first_visible_entry_idx) { + --self->first_visible_entry_idx; + self->seek(self, -1, SEEK_CUR); + } + break; + case NEWT_KEY_PGDN: + case ' ': + if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) + break; + + offset = self->height; + if (self->index + offset > self->nr_entries - 1) + offset = self->nr_entries - 1 - self->index; + self->index += offset; + self->first_visible_entry_idx += offset; + self->seek(self, +offset, SEEK_CUR); + break; + case NEWT_KEY_PGUP: + if (self->first_visible_entry_idx == 0) + break; + + if (self->first_visible_entry_idx < self->height) + offset = self->first_visible_entry_idx; + else + offset = self->height; + + self->index -= offset; + self->first_visible_entry_idx -= offset; + self->seek(self, -offset, SEEK_CUR); + break; + case NEWT_KEY_HOME: + ui_browser__reset_index(self); + break; + case NEWT_KEY_END: + offset = self->height - 1; + if (offset >= self->nr_entries) + offset = self->nr_entries - 1; + + self->index = self->nr_entries - 1; + self->first_visible_entry_idx = self->index - offset; + self->seek(self, -offset, SEEK_END); + break; + default: + return es->u.key; + } + if (ui_browser__refresh(self) < 0) + return -1; + } + return 0; +} + +unsigned int ui_browser__list_head_refresh(struct ui_browser *self) +{ + struct list_head *pos; + struct list_head *head = self->entries; + int row = 0; + + if (self->first_visible_entry == NULL || + self->first_visible_entry == self->entries) + self->first_visible_entry = head->next; + + pos = self->first_visible_entry; + + list_for_each_from(pos, head) { + SLsmg_gotorc(self->top + row, self->left); + self->write(self, pos, row); + if (++row == self->height) + break; + } + + return row; +} + +static struct newtPercentTreeColors { + const char *topColorFg, *topColorBg; + const char *mediumColorFg, *mediumColorBg; + const char *normalColorFg, *normalColorBg; + const char *selColorFg, *selColorBg; + const char *codeColorFg, *codeColorBg; +} defaultPercentTreeColors = { + "red", "lightgray", + "green", "lightgray", + "black", "lightgray", + "lightgray", "magenta", + "blue", "lightgray", +}; + +void ui_browser__init(void) +{ + struct newtPercentTreeColors *c = &defaultPercentTreeColors; + + sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); + sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); + sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); + sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); + sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); +} diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h new file mode 100644 index 00000000000..bcc4391405b --- /dev/null +++ b/tools/perf/util/ui/browser.h @@ -0,0 +1,43 @@ +#ifndef _PERF_UI_BROWSER_H_ +#define _PERF_UI_BROWSER_H_ 1 + +#include +#include +#include "../types.h" + +#define HE_COLORSET_TOP 50 +#define HE_COLORSET_MEDIUM 51 +#define HE_COLORSET_NORMAL 52 +#define HE_COLORSET_SELECTED 53 +#define HE_COLORSET_CODE 54 + +struct ui_browser { + newtComponent form, sb; + u64 index, first_visible_entry_idx; + void *first_visible_entry, *entries; + u16 top, left, width, height; + void *priv; + unsigned int (*refresh)(struct ui_browser *self); + void (*write)(struct ui_browser *self, void *entry, int row); + void (*seek)(struct ui_browser *self, off_t offset, int whence); + u32 nr_entries; +}; + + +int ui_browser__percent_color(double percent, bool current); +bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row); +void ui_browser__refresh_dimensions(struct ui_browser *self); +void ui_browser__reset_index(struct ui_browser *self); + +int ui_browser__show(struct ui_browser *self, const char *title); +int ui_browser__refresh(struct ui_browser *self); +int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es); + +void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); +unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); + +void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence); +unsigned int ui_browser__list_head_refresh(struct ui_browser *self); + +void ui_browser__init(void); +#endif /* _PERF_UI_BROWSER_H_ */ -- cgit v1.2.3-18-g5258 From d247eb6b924bbc2f13748c89b6c72c7a3d46645c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 7 Aug 2010 13:56:04 -0300 Subject: perf ui: Shorten ui_browser member names LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 36 ++++++++++++++-------------- tools/perf/util/ui/browser.c | 57 ++++++++++++++++++++++---------------------- tools/perf/util/ui/browser.h | 6 ++--- 3 files changed, 49 insertions(+), 50 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 266a9e06525..9768be31f9c 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -450,8 +450,8 @@ static int map_browser__search(struct map_browser *self) if (sym != NULL) { u32 *idx = symbol__browser_index(sym); - self->b.first_visible_entry = &sym->rb_node; - self->b.index = self->b.first_visible_entry_idx = *idx; + self->b.top = &sym->rb_node; + self->b.index = self->b.top_idx = *idx; } else ui_helpline__fpush("%s not found!", target); @@ -967,7 +967,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, } SLsmg_set_color(color); - SLsmg_gotorc(self->b.top + row, self->b.left); + SLsmg_gotorc(self->b.y + row, self->b.x); slsmg_write_nstring(" ", offset + extra_offset); slsmg_printf("%c ", folded_sign); slsmg_write_nstring(str, width); @@ -1030,7 +1030,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *self, } s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - SLsmg_gotorc(self->b.top + row, self->b.left); + SLsmg_gotorc(self->b.y + row, self->b.x); SLsmg_set_color(color); slsmg_write_nstring(" ", offset); slsmg_printf("%c ", folded_sign); @@ -1110,7 +1110,7 @@ static int hist_browser__show_entry(struct hist_browser *self, } SLsmg_set_color(color); - SLsmg_gotorc(self->b.top + row, self->b.left); + SLsmg_gotorc(self->b.y + row, self->b.x); if (symbol_conf.use_callchain) { slsmg_printf("%c ", folded_sign); width -= 2; @@ -1138,10 +1138,10 @@ static unsigned int hist_browser__refresh(struct ui_browser *self) struct rb_node *nd; struct hist_browser *hb = container_of(self, struct hist_browser, b); - if (self->first_visible_entry == NULL) - self->first_visible_entry = rb_first(&hb->hists->entries); + if (self->top == NULL) + self->top = rb_first(&hb->hists->entries); - for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) { + for (nd = self->top; nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (h->filtered) @@ -1244,7 +1244,7 @@ static void ui_browser__hists_seek(struct ui_browser *self, nd = hists__filter_entries(rb_first(self->entries)); break; case SEEK_CUR: - nd = self->first_visible_entry; + nd = self->top; goto do_offset; case SEEK_END: nd = hists__filter_prev_entries(rb_last(self->entries)); @@ -1258,7 +1258,7 @@ static void ui_browser__hists_seek(struct ui_browser *self, * Moves not relative to the first visible entry invalidates its * row_offset: */ - h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node); + h = rb_entry(self->top, struct hist_entry, rb_node); h->row_offset = 0; /* @@ -1286,7 +1286,7 @@ do_offset: } else { h->row_offset += offset; offset = 0; - self->first_visible_entry = nd; + self->top = nd; break; } } @@ -1294,7 +1294,7 @@ do_offset: if (nd == NULL) break; --offset; - self->first_visible_entry = nd; + self->top = nd; } while (offset != 0); } else if (offset < 0) { while (1) { @@ -1307,7 +1307,7 @@ do_offset: } else { h->row_offset += offset; offset = 0; - self->first_visible_entry = nd; + self->top = nd; break; } } else { @@ -1317,7 +1317,7 @@ do_offset: } else { h->row_offset = h->nr_rows + offset; offset = 0; - self->first_visible_entry = nd; + self->top = nd; break; } } @@ -1327,7 +1327,7 @@ do_offset: if (nd == NULL) break; ++offset; - self->first_visible_entry = nd; + self->top = nd; if (offset == 0) { /* * Last unfiltered hist_entry, check if it is @@ -1342,7 +1342,7 @@ do_offset: first = false; } } else { - self->first_visible_entry = nd; + self->top = nd; h = rb_entry(nd, struct hist_entry, rb_node); h->row_offset = 0; } @@ -1463,7 +1463,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title, switch (es->u.key) { case 'd': { /* Debug */ static int seq; - struct hist_entry *h = rb_entry(self->b.first_visible_entry, + struct hist_entry *h = rb_entry(self->b.top, struct hist_entry, rb_node); ui_helpline__pop(); ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", @@ -1471,7 +1471,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title, self->hists->nr_entries, self->b.height, self->b.index, - self->b.first_visible_entry_idx, + self->b.top_idx, h->row_offset, h->nr_rows); } continue; diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 0b2b9306312..edbb7dd9eb2 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -49,7 +49,7 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc pos = head->next; break; case SEEK_CUR: - pos = self->first_visible_entry; + pos = self->top; break; case SEEK_END: pos = head->prev; @@ -66,7 +66,7 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc pos = pos->prev; } - self->first_visible_entry = pos; + self->top = pos; } void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence) @@ -79,7 +79,7 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence) nd = rb_first(root); break; case SEEK_CUR: - nd = self->first_visible_entry; + nd = self->top; break; case SEEK_END: nd = rb_last(root); @@ -96,7 +96,7 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence) nd = rb_prev(nd); } - self->first_visible_entry = nd; + self->top = nd; } unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) @@ -104,13 +104,13 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) struct rb_node *nd; int row = 0; - if (self->first_visible_entry == NULL) - self->first_visible_entry = rb_first(self->entries); + if (self->top == NULL) + self->top = rb_first(self->entries); - nd = self->first_visible_entry; + nd = self->top; while (nd != NULL) { - SLsmg_gotorc(self->top + row, self->left); + SLsmg_gotorc(self->y + row, self->x); self->write(self, nd, row); if (++row == self->height) break; @@ -122,7 +122,7 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) { - return (self->first_visible_entry_idx + row) == self->index; + return self->top_idx + row == self->index; } void ui_browser__refresh_dimensions(struct ui_browser *self) @@ -135,13 +135,13 @@ void ui_browser__refresh_dimensions(struct ui_browser *self) self->height = rows - 5; if (self->height > self->nr_entries) self->height = self->nr_entries; - self->top = (rows - self->height) / 2; - self->left = (cols - self->width) / 2; + self->y = (rows - self->height) / 2; + self->x = (cols - self->width) / 2; } void ui_browser__reset_index(struct ui_browser *self) { - self->index = self->first_visible_entry_idx = 0; + self->index = self->top_idx = 0; self->seek(self, 0, SEEK_SET); } @@ -180,7 +180,7 @@ int ui_browser__refresh(struct ui_browser *self) newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); row = self->refresh(self); SLsmg_set_color(HE_COLORSET_NORMAL); - SLsmg_fill_region(self->top + row, self->left, + SLsmg_fill_region(self->y + row, self->x, self->height - row, self->width, ' '); return 0; @@ -205,8 +205,8 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) if (self->index == self->nr_entries - 1) break; ++self->index; - if (self->index == self->first_visible_entry_idx + self->height) { - ++self->first_visible_entry_idx; + if (self->index == self->top_idx + self->height) { + ++self->top_idx; self->seek(self, +1, SEEK_CUR); } break; @@ -214,34 +214,34 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) if (self->index == 0) break; --self->index; - if (self->index < self->first_visible_entry_idx) { - --self->first_visible_entry_idx; + if (self->index < self->top_idx) { + --self->top_idx; self->seek(self, -1, SEEK_CUR); } break; case NEWT_KEY_PGDN: case ' ': - if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) + if (self->top_idx + self->height > self->nr_entries - 1) break; offset = self->height; if (self->index + offset > self->nr_entries - 1) offset = self->nr_entries - 1 - self->index; self->index += offset; - self->first_visible_entry_idx += offset; + self->top_idx += offset; self->seek(self, +offset, SEEK_CUR); break; case NEWT_KEY_PGUP: - if (self->first_visible_entry_idx == 0) + if (self->top_idx == 0) break; - if (self->first_visible_entry_idx < self->height) - offset = self->first_visible_entry_idx; + if (self->top_idx < self->height) + offset = self->top_idx; else offset = self->height; self->index -= offset; - self->first_visible_entry_idx -= offset; + self->top_idx -= offset; self->seek(self, -offset, SEEK_CUR); break; case NEWT_KEY_HOME: @@ -253,7 +253,7 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) offset = self->nr_entries - 1; self->index = self->nr_entries - 1; - self->first_visible_entry_idx = self->index - offset; + self->top_idx = self->index - offset; self->seek(self, -offset, SEEK_END); break; default: @@ -271,14 +271,13 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self) struct list_head *head = self->entries; int row = 0; - if (self->first_visible_entry == NULL || - self->first_visible_entry == self->entries) - self->first_visible_entry = head->next; + if (self->top == NULL || self->top == self->entries) + self->top = head->next; - pos = self->first_visible_entry; + pos = self->top; list_for_each_from(pos, head) { - SLsmg_gotorc(self->top + row, self->left); + SLsmg_gotorc(self->y + row, self->x); self->write(self, pos, row); if (++row == self->height) break; diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index bcc4391405b..8eed24cf285 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h @@ -13,9 +13,9 @@ struct ui_browser { newtComponent form, sb; - u64 index, first_visible_entry_idx; - void *first_visible_entry, *entries; - u16 top, left, width, height; + u64 index, top_idx; + void *top, *entries; + u16 y, x, width, height; void *priv; unsigned int (*refresh)(struct ui_browser *self); void (*write)(struct ui_browser *self, void *entry, int row); -- cgit v1.2.3-18-g5258 From 5575536fc7ad7577a4e687a13e2f49acebc519f3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 8 Aug 2010 19:48:31 -0300 Subject: perf ui: Move ui_helpline routines to separate file in util/ui/ LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 2 ++ tools/perf/util/newt.c | 38 +------------------------------------- tools/perf/util/ui/helpline.c | 43 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/ui/helpline.h | 9 +++++++++ 4 files changed, 55 insertions(+), 37 deletions(-) create mode 100644 tools/perf/util/ui/helpline.c create mode 100644 tools/perf/util/ui/helpline.h (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index d5bce768b4b..d77a101ab7d 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -569,7 +569,9 @@ else EXTLIBS += -lnewt -lslang LIB_OBJS += $(OUTPUT)util/newt.o LIB_OBJS += $(OUTPUT)util/ui/browser.o + LIB_OBJS += $(OUTPUT)util/ui/helpline.o LIB_H += util/ui/browser.h + LIB_H += util/ui/helpline.h endif endif diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 9768be31f9c..23f3b7d31bf 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -24,6 +24,7 @@ #include "sort.h" #include "symbol.h" #include "ui/browser.h" +#include "ui/helpline.h" #if SLANG_VERSION < 20104 #define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args) @@ -94,43 +95,6 @@ void ui_progress__delete(struct ui_progress *self) free(self); } -static void ui_helpline__pop(void) -{ - newtPopHelpLine(); -} - -static void ui_helpline__push(const char *msg) -{ - newtPushHelpLine(msg); -} - -static void ui_helpline__vpush(const char *fmt, va_list ap) -{ - char *s; - - if (vasprintf(&s, fmt, ap) < 0) - vfprintf(stderr, fmt, ap); - else { - ui_helpline__push(s); - free(s); - } -} - -static void ui_helpline__fpush(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - ui_helpline__vpush(fmt, ap); - va_end(ap); -} - -static void ui_helpline__puts(const char *msg) -{ - ui_helpline__pop(); - ui_helpline__push(msg); -} - static int ui_entry__read(const char *title, char *bf, size_t size, int width) { struct newtExitStruct es; diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c new file mode 100644 index 00000000000..6a11e130155 --- /dev/null +++ b/tools/perf/util/ui/helpline.c @@ -0,0 +1,43 @@ +#define _GNU_SOURCE +#include +#include +#include + +#include "helpline.h" + +void ui_helpline__pop(void) +{ + newtPopHelpLine(); +} + +void ui_helpline__push(const char *msg) +{ + newtPushHelpLine(msg); +} + +static void ui_helpline__vpush(const char *fmt, va_list ap) +{ + char *s; + + if (vasprintf(&s, fmt, ap) < 0) + vfprintf(stderr, fmt, ap); + else { + ui_helpline__push(s); + free(s); + } +} + +void ui_helpline__fpush(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + ui_helpline__vpush(fmt, ap); + va_end(ap); +} + +void ui_helpline__puts(const char *msg) +{ + ui_helpline__pop(); + ui_helpline__push(msg); +} diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h new file mode 100644 index 00000000000..56d8c1d8ffc --- /dev/null +++ b/tools/perf/util/ui/helpline.h @@ -0,0 +1,9 @@ +#ifndef _PERF_UI_HELPLINE_H_ +#define _PERF_UI_HELPLINE_H_ 1 + +void ui_helpline__pop(void); +void ui_helpline__push(const char *msg); +void ui_helpline__fpush(const char *fmt, ...); +void ui_helpline__puts(const char *msg); + +#endif /* _PERF_UI_HELPLINE_H_ */ -- cgit v1.2.3-18-g5258 From 34cea7f7c0620c964676eece258ef431a6608bce Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 8 Aug 2010 19:56:47 -0300 Subject: perf ui: Move ui_progress routines to separate file in util/ui/ LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 2 ++ tools/perf/util/debug.h | 4 +-- tools/perf/util/newt.c | 56 ---------------------------------------- tools/perf/util/ui/progress.c | 60 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/ui/progress.h | 11 ++++++++ 5 files changed, 74 insertions(+), 59 deletions(-) create mode 100644 tools/perf/util/ui/progress.c create mode 100644 tools/perf/util/ui/progress.h (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index d77a101ab7d..528c91469c0 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -570,8 +570,10 @@ else LIB_OBJS += $(OUTPUT)util/newt.o LIB_OBJS += $(OUTPUT)util/ui/browser.o LIB_OBJS += $(OUTPUT)util/ui/helpline.o + LIB_OBJS += $(OUTPUT)util/ui/progress.o LIB_H += util/ui/browser.h LIB_H += util/ui/helpline.h + LIB_H += util/ui/progress.h endif endif diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 047ac3324eb..a929b06cfc0 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -31,9 +31,7 @@ static inline void ui_progress__update(struct ui_progress *self __used, static inline void ui_progress__delete(struct ui_progress *self __used) {} #else int browser__show_help(const char *format, va_list ap); -struct ui_progress *ui_progress__new(const char *title, u64 total); -void ui_progress__update(struct ui_progress *self, u64 curr); -void ui_progress__delete(struct ui_progress *self); +#include "ui/progress.h" #endif #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 23f3b7d31bf..c0986d34295 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -39,62 +39,6 @@ newtComponent newt_form__new(void); -struct ui_progress { - newtComponent form, scale; -}; - -struct ui_progress *ui_progress__new(const char *title, u64 total) -{ - struct ui_progress *self = malloc(sizeof(*self)); - - if (self != NULL) { - int cols; - - if (use_browser <= 0) - return self; - newtGetScreenSize(&cols, NULL); - cols -= 4; - newtCenteredWindow(cols, 1, title); - self->form = newtForm(NULL, NULL, 0); - if (self->form == NULL) - goto out_free_self; - self->scale = newtScale(0, 0, cols, total); - if (self->scale == NULL) - goto out_free_form; - newtFormAddComponent(self->form, self->scale); - newtRefresh(); - } - - return self; - -out_free_form: - newtFormDestroy(self->form); -out_free_self: - free(self); - return NULL; -} - -void ui_progress__update(struct ui_progress *self, u64 curr) -{ - /* - * FIXME: We should have a per UI backend way of showing progress, - * stdio will just show a percentage as NN%, etc. - */ - if (use_browser <= 0) - return; - newtScaleSet(self->scale, curr); - newtRefresh(); -} - -void ui_progress__delete(struct ui_progress *self) -{ - if (use_browser > 0) { - newtFormDestroy(self->form); - newtPopWindow(); - } - free(self); -} - static int ui_entry__read(const char *title, char *bf, size_t size, int width) { struct newtExitStruct es; diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c new file mode 100644 index 00000000000..d7fc399d36b --- /dev/null +++ b/tools/perf/util/ui/progress.c @@ -0,0 +1,60 @@ +#include +#include +#include "../cache.h" +#include "progress.h" + +struct ui_progress { + newtComponent form, scale; +}; + +struct ui_progress *ui_progress__new(const char *title, u64 total) +{ + struct ui_progress *self = malloc(sizeof(*self)); + + if (self != NULL) { + int cols; + + if (use_browser <= 0) + return self; + newtGetScreenSize(&cols, NULL); + cols -= 4; + newtCenteredWindow(cols, 1, title); + self->form = newtForm(NULL, NULL, 0); + if (self->form == NULL) + goto out_free_self; + self->scale = newtScale(0, 0, cols, total); + if (self->scale == NULL) + goto out_free_form; + newtFormAddComponent(self->form, self->scale); + newtRefresh(); + } + + return self; + +out_free_form: + newtFormDestroy(self->form); +out_free_self: + free(self); + return NULL; +} + +void ui_progress__update(struct ui_progress *self, u64 curr) +{ + /* + * FIXME: We should have a per UI backend way of showing progress, + * stdio will just show a percentage as NN%, etc. + */ + if (use_browser <= 0) + return; + newtScaleSet(self->scale, curr); + newtRefresh(); +} + +void ui_progress__delete(struct ui_progress *self) +{ + if (use_browser > 0) { + newtFormDestroy(self->form); + newtPopWindow(); + } + free(self); +} diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h new file mode 100644 index 00000000000..a3820a0beb5 --- /dev/null +++ b/tools/perf/util/ui/progress.h @@ -0,0 +1,11 @@ +#ifndef _PERF_UI_PROGRESS_H_ +#define _PERF_UI_PROGRESS_H_ 1 + +struct ui_progress; + +struct ui_progress *ui_progress__new(const char *title, u64 total); +void ui_progress__delete(struct ui_progress *self); + +void ui_progress__update(struct ui_progress *self, u64 curr); + +#endif -- cgit v1.2.3-18-g5258 From 211ef12771e759a08e10c3c606e6a8b1663519e7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 14:54:09 -0300 Subject: perf ui: Move annotate browser to util/ui/browsers/ LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 7 +- tools/perf/util/debug.h | 1 + tools/perf/util/newt.c | 132 +-------------------------------- tools/perf/util/ui/browser.h | 1 + tools/perf/util/ui/browsers/annotate.c | 114 ++++++++++++++++++++++++++++ tools/perf/util/ui/libslang.h | 27 +++++++ 6 files changed, 151 insertions(+), 131 deletions(-) create mode 100644 tools/perf/util/ui/browsers/annotate.c create mode 100644 tools/perf/util/ui/libslang.h (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 528c91469c0..ae443b68e51 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -158,7 +158,7 @@ all:: # Define NO_DWARF if you do not want debug-info analysis feature at all. $(shell sh -c 'mkdir -p $(OUTPUT)scripts/{perl,python}/Perf-Trace-Util/' 2> /dev/null) -$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui,scripting-engines}/' 2> /dev/null) +$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui/browsers,scripting-engines}/' 2> /dev/null) $(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null) $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE @@ -569,10 +569,12 @@ else EXTLIBS += -lnewt -lslang LIB_OBJS += $(OUTPUT)util/newt.o LIB_OBJS += $(OUTPUT)util/ui/browser.o + LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o LIB_OBJS += $(OUTPUT)util/ui/helpline.o LIB_OBJS += $(OUTPUT)util/ui/progress.o LIB_H += util/ui/browser.h LIB_H += util/ui/helpline.h + LIB_H += util/ui/libslang.h LIB_H += util/ui/progress.h endif endif @@ -977,6 +979,9 @@ $(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< +$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index a929b06cfc0..ba4892e49be 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -30,6 +30,7 @@ static inline void ui_progress__update(struct ui_progress *self __used, static inline void ui_progress__delete(struct ui_progress *self __used) {} #else +extern char browser__last_msg[]; int browser__show_help(const char *format, va_list ap); #include "ui/progress.h" #endif diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index c0986d34295..f1882589c82 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -1,16 +1,7 @@ #define _GNU_SOURCE #include #undef _GNU_SOURCE -/* - * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks - * the build if it isn't defined. Use the equivalent one that glibc - * has on features.h. - */ -#include -#ifndef HAVE_LONG_LONG -#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG -#endif -#include +#include "ui/libslang.h" #include #include #include @@ -26,17 +17,6 @@ #include "ui/browser.h" #include "ui/helpline.h" -#if SLANG_VERSION < 20104 -#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args) -#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len) -#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\ - (char *)fg, (char *)bg) -#else -#define slsmg_printf SLsmg_printf -#define slsmg_write_nstring SLsmg_write_nstring -#define sltt_set_color SLtt_set_color -#endif - newtComponent newt_form__new(void); static int ui_entry__read(const char *title, char *bf, size_t size, int width) @@ -72,7 +52,7 @@ out_free_form: return 0; } -static char browser__last_msg[1024]; +char browser__last_msg[1024]; int browser__show_help(const char *format, va_list ap) { @@ -192,66 +172,6 @@ static bool dialog_yesno(const char *msg) return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } -static void ui__error_window(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap); - va_end(ap); -} - -static void annotate_browser__write(struct ui_browser *self, void *entry, int row) -{ - struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); - bool current_entry = ui_browser__is_current_entry(self, row); - int width = self->width; - - if (ol->offset != -1) { - struct hist_entry *he = self->priv; - struct symbol *sym = he->ms.sym; - int len = he->ms.sym->end - he->ms.sym->start; - unsigned int hits = 0; - double percent = 0.0; - int color; - struct sym_priv *priv = symbol__priv(sym); - struct sym_ext *sym_ext = priv->ext; - struct sym_hist *h = priv->hist; - s64 offset = ol->offset; - struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol); - - while (offset < (s64)len && - (next == NULL || offset < next->offset)) { - if (sym_ext) { - percent += sym_ext[offset].percent; - } else - hits += h->ip[offset]; - - ++offset; - } - - if (sym_ext == NULL && h->sum) - percent = 100.0 * hits / h->sum; - - color = ui_browser__percent_color(percent, current_entry); - SLsmg_set_color(color); - slsmg_printf(" %7.2f ", percent); - if (!current_entry) - SLsmg_set_color(HE_COLORSET_CODE); - } else { - int color = ui_browser__percent_color(0, current_entry); - SLsmg_set_color(color); - slsmg_write_nstring(" ", 9); - } - - SLsmg_write_char(':'); - slsmg_write_nstring(" ", 8); - if (!*ol->line) - slsmg_write_nstring(" ", width - 18); - else - slsmg_write_nstring(ol->line, width - 18); -} - static char *callchain_list__sym_name(struct callchain_list *self, char *bf, size_t bfsize) { @@ -262,54 +182,6 @@ static char *callchain_list__sym_name(struct callchain_list *self, return bf; } -int hist_entry__tui_annotate(struct hist_entry *self) -{ - struct newtExitStruct es; - struct objdump_line *pos, *n; - LIST_HEAD(head); - struct ui_browser browser = { - .entries = &head, - .refresh = ui_browser__list_head_refresh, - .seek = ui_browser__list_head_seek, - .write = annotate_browser__write, - .priv = self, - }; - int ret; - - if (self->ms.sym == NULL) - return -1; - - if (self->ms.map->dso->annotate_warned) - return -1; - - if (hist_entry__annotate(self, &head) < 0) { - ui__error_window(browser__last_msg); - return -1; - } - - ui_helpline__push("Press <- or ESC to exit"); - - list_for_each_entry(pos, &head, node) { - size_t line_len = strlen(pos->line); - if (browser.width < line_len) - browser.width = line_len; - ++browser.nr_entries; - } - - browser.width += 18; /* Percentage */ - ui_browser__show(&browser, self->ms.sym->name); - newtFormAddHotKey(browser.form, ' '); - ret = ui_browser__run(&browser, &es); - newtFormDestroy(browser.form); - newtPopWindow(); - list_for_each_entry_safe(pos, n, &head, node) { - list_del(&pos->node); - objdump_line__free(pos); - } - ui_helpline__pop(); - return ret; -} - /* -------------------------------------------------------------------- */ struct map_browser { diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index 8eed24cf285..856e3436172 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h @@ -3,6 +3,7 @@ #include #include +#include #include "../types.h" #define HE_COLORSET_TOP 50 diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c new file mode 100644 index 00000000000..783d277f219 --- /dev/null +++ b/tools/perf/util/ui/browsers/annotate.c @@ -0,0 +1,114 @@ +#include "../browser.h" +#include "../helpline.h" +#include "../libslang.h" +#include "../../hist.h" +#include "../../sort.h" +#include "../../symbol.h" + +static void ui__error_window(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap); + va_end(ap); +} + +static void annotate_browser__write(struct ui_browser *self, void *entry, int row) +{ + struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); + bool current_entry = ui_browser__is_current_entry(self, row); + int width = self->width; + + if (ol->offset != -1) { + struct hist_entry *he = self->priv; + struct symbol *sym = he->ms.sym; + int len = he->ms.sym->end - he->ms.sym->start; + unsigned int hits = 0; + double percent = 0.0; + int color; + struct sym_priv *priv = symbol__priv(sym); + struct sym_ext *sym_ext = priv->ext; + struct sym_hist *h = priv->hist; + s64 offset = ol->offset; + struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol); + + while (offset < (s64)len && + (next == NULL || offset < next->offset)) { + if (sym_ext) { + percent += sym_ext[offset].percent; + } else + hits += h->ip[offset]; + + ++offset; + } + + if (sym_ext == NULL && h->sum) + percent = 100.0 * hits / h->sum; + + color = ui_browser__percent_color(percent, current_entry); + SLsmg_set_color(color); + slsmg_printf(" %7.2f ", percent); + if (!current_entry) + SLsmg_set_color(HE_COLORSET_CODE); + } else { + int color = ui_browser__percent_color(0, current_entry); + SLsmg_set_color(color); + slsmg_write_nstring(" ", 9); + } + + SLsmg_write_char(':'); + slsmg_write_nstring(" ", 8); + if (!*ol->line) + slsmg_write_nstring(" ", width - 18); + else + slsmg_write_nstring(ol->line, width - 18); +} + +int hist_entry__tui_annotate(struct hist_entry *self) +{ + struct newtExitStruct es; + struct objdump_line *pos, *n; + LIST_HEAD(head); + struct ui_browser browser = { + .entries = &head, + .refresh = ui_browser__list_head_refresh, + .seek = ui_browser__list_head_seek, + .write = annotate_browser__write, + .priv = self, + }; + int ret; + + if (self->ms.sym == NULL) + return -1; + + if (self->ms.map->dso->annotate_warned) + return -1; + + if (hist_entry__annotate(self, &head) < 0) { + ui__error_window(browser__last_msg); + return -1; + } + + ui_helpline__push("Press <- or ESC to exit"); + + list_for_each_entry(pos, &head, node) { + size_t line_len = strlen(pos->line); + if (browser.width < line_len) + browser.width = line_len; + ++browser.nr_entries; + } + + browser.width += 18; /* Percentage */ + ui_browser__show(&browser, self->ms.sym->name); + newtFormAddHotKey(browser.form, ' '); + ret = ui_browser__run(&browser, &es); + newtFormDestroy(browser.form); + newtPopWindow(); + list_for_each_entry_safe(pos, n, &head, node) { + list_del(&pos->node); + objdump_line__free(pos); + } + ui_helpline__pop(); + return ret; +} diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h new file mode 100644 index 00000000000..5623da8e808 --- /dev/null +++ b/tools/perf/util/ui/libslang.h @@ -0,0 +1,27 @@ +#ifndef _PERF_UI_SLANG_H_ +#define _PERF_UI_SLANG_H_ 1 +/* + * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks + * the build if it isn't defined. Use the equivalent one that glibc + * has on features.h. + */ +#include +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG +#endif +#include + +#if SLANG_VERSION < 20104 +#define slsmg_printf(msg, args...) \ + SLsmg_printf((char *)msg, ##args) +#define slsmg_write_nstring(msg, len) \ + SLsmg_write_nstring((char *)msg, len) +#define sltt_set_color(obj, name, fg, bg) \ + SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg) +#else +#define slsmg_printf SLsmg_printf +#define slsmg_write_nstring SLsmg_write_nstring +#define sltt_set_color SLtt_set_color +#endif + +#endif /* _PERF_UI_SLANG_H_ */ -- cgit v1.2.3-18-g5258 From b1b0267336b1b74eeb8884bac4be96296b719e67 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:37:34 -0300 Subject: perf ui: Move map browser to util/ui/browsers/ LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 5 ++ tools/perf/util/newt.c | 156 +----------------------------------- tools/perf/util/ui/browsers/map.c | 163 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/ui/browsers/map.h | 6 ++ 4 files changed, 175 insertions(+), 155 deletions(-) create mode 100644 tools/perf/util/ui/browsers/map.c create mode 100644 tools/perf/util/ui/browsers/map.h (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index ae443b68e51..d118020014f 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -570,9 +570,11 @@ else LIB_OBJS += $(OUTPUT)util/newt.o LIB_OBJS += $(OUTPUT)util/ui/browser.o LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o + LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o LIB_OBJS += $(OUTPUT)util/ui/helpline.o LIB_OBJS += $(OUTPUT)util/ui/progress.o LIB_H += util/ui/browser.h + LIB_H += util/ui/browsers/map.h LIB_H += util/ui/helpline.h LIB_H += util/ui/libslang.h LIB_H += util/ui/progress.h @@ -982,6 +984,9 @@ $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< +$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index f1882589c82..b596926afa2 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -16,42 +16,10 @@ #include "symbol.h" #include "ui/browser.h" #include "ui/helpline.h" +#include "ui/browsers/map.h" newtComponent newt_form__new(void); -static int ui_entry__read(const char *title, char *bf, size_t size, int width) -{ - struct newtExitStruct es; - newtComponent form, entry; - const char *result; - int err = -1; - - newtCenteredWindow(width, 1, title); - form = newtForm(NULL, NULL, 0); - if (form == NULL) - return -1; - - entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL); - if (entry == NULL) - goto out_free_form; - - newtFormAddComponent(form, entry); - newtFormAddHotKey(form, NEWT_KEY_ENTER); - newtFormAddHotKey(form, NEWT_KEY_ESCAPE); - newtFormAddHotKey(form, NEWT_KEY_LEFT); - newtFormAddHotKey(form, CTRL('c')); - newtFormRun(form, &es); - - if (result != NULL) { - strncpy(bf, result, size); - err = 0; - } -out_free_form: - newtPopWindow(); - newtFormDestroy(form); - return 0; -} - char browser__last_msg[1024]; int browser__show_help(const char *format, va_list ap) @@ -182,128 +150,6 @@ static char *callchain_list__sym_name(struct callchain_list *self, return bf; } -/* -------------------------------------------------------------------- */ - -struct map_browser { - struct ui_browser b; - struct map *map; - u16 namelen; - u8 addrlen; -}; - -static void map_browser__write(struct ui_browser *self, void *nd, int row) -{ - struct symbol *sym = rb_entry(nd, struct symbol, rb_node); - struct map_browser *mb = container_of(self, struct map_browser, b); - bool current_entry = ui_browser__is_current_entry(self, row); - int color = ui_browser__percent_color(0, current_entry); - - SLsmg_set_color(color); - slsmg_printf("%*llx %*llx %c ", - mb->addrlen, sym->start, mb->addrlen, sym->end, - sym->binding == STB_GLOBAL ? 'g' : - sym->binding == STB_LOCAL ? 'l' : 'w'); - slsmg_write_nstring(sym->name, mb->namelen); -} - -/* FIXME uber-kludgy, see comment on cmd_report... */ -static u32 *symbol__browser_index(struct symbol *self) -{ - return ((void *)self) - sizeof(struct rb_node) - sizeof(u32); -} - -static int map_browser__search(struct map_browser *self) -{ - char target[512]; - struct symbol *sym; - int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40); - - if (err) - return err; - - if (target[0] == '0' && tolower(target[1]) == 'x') { - u64 addr = strtoull(target, NULL, 16); - sym = map__find_symbol(self->map, addr, NULL); - } else - sym = map__find_symbol_by_name(self->map, target, NULL); - - if (sym != NULL) { - u32 *idx = symbol__browser_index(sym); - - self->b.top = &sym->rb_node; - self->b.index = self->b.top_idx = *idx; - } else - ui_helpline__fpush("%s not found!", target); - - return 0; -} - -static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) -{ - if (ui_browser__show(&self->b, self->map->dso->long_name) < 0) - return -1; - - ui_helpline__fpush("Press <- or ESC to exit, %s / to search", - verbose ? "" : "restart with -v to use"); - newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); - newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); - if (verbose) - newtFormAddHotKey(self->b.form, '/'); - - while (1) { - ui_browser__run(&self->b, es); - - if (es->reason != NEWT_EXIT_HOTKEY) - break; - if (verbose && es->u.key == '/') - map_browser__search(self); - else - break; - } - - newtFormDestroy(self->b.form); - newtPopWindow(); - ui_helpline__pop(); - return 0; -} - -static int map__browse(struct map *self) -{ - struct map_browser mb = { - .b = { - .entries = &self->dso->symbols[self->type], - .refresh = ui_browser__rb_tree_refresh, - .seek = ui_browser__rb_tree_seek, - .write = map_browser__write, - }, - .map = self, - }; - struct newtExitStruct es; - struct rb_node *nd; - char tmp[BITS_PER_LONG / 4]; - u64 maxaddr = 0; - - for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) { - struct symbol *pos = rb_entry(nd, struct symbol, rb_node); - - if (mb.namelen < pos->namelen) - mb.namelen = pos->namelen; - if (maxaddr < pos->end) - maxaddr = pos->end; - if (verbose) { - u32 *idx = symbol__browser_index(pos); - *idx = mb.b.nr_entries; - } - ++mb.b.nr_entries; - } - - mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); - mb.b.width += mb.addrlen * 2 + 4 + mb.namelen; - return map_browser__run(&mb, &es); -} - -/* -------------------------------------------------------------------- */ - struct hist_browser { struct ui_browser b; struct hists *hists; diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c new file mode 100644 index 00000000000..b79f0c996ea --- /dev/null +++ b/tools/perf/util/ui/browsers/map.c @@ -0,0 +1,163 @@ +#include "../libslang.h" +#include +#include +#include +#include +#include +#include +#include "../../debug.h" +#include "../../symbol.h" +#include "../browser.h" +#include "../helpline.h" +#include "map.h" + +static int ui_entry__read(const char *title, char *bf, size_t size, int width) +{ + struct newtExitStruct es; + newtComponent form, entry; + const char *result; + int err = -1; + + newtCenteredWindow(width, 1, title); + form = newtForm(NULL, NULL, 0); + if (form == NULL) + return -1; + + entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL); + if (entry == NULL) + goto out_free_form; + + newtFormAddComponent(form, entry); + newtFormAddHotKey(form, NEWT_KEY_ENTER); + newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + newtFormAddHotKey(form, NEWT_KEY_LEFT); + newtFormAddHotKey(form, CTRL('c')); + newtFormRun(form, &es); + + if (result != NULL) { + strncpy(bf, result, size); + err = 0; + } +out_free_form: + newtPopWindow(); + newtFormDestroy(form); + return 0; +} + +struct map_browser { + struct ui_browser b; + struct map *map; + u16 namelen; + u8 addrlen; +}; + +static void map_browser__write(struct ui_browser *self, void *nd, int row) +{ + struct symbol *sym = rb_entry(nd, struct symbol, rb_node); + struct map_browser *mb = container_of(self, struct map_browser, b); + bool current_entry = ui_browser__is_current_entry(self, row); + int color = ui_browser__percent_color(0, current_entry); + + SLsmg_set_color(color); + slsmg_printf("%*llx %*llx %c ", + mb->addrlen, sym->start, mb->addrlen, sym->end, + sym->binding == STB_GLOBAL ? 'g' : + sym->binding == STB_LOCAL ? 'l' : 'w'); + slsmg_write_nstring(sym->name, mb->namelen); +} + +/* FIXME uber-kludgy, see comment on cmd_report... */ +static u32 *symbol__browser_index(struct symbol *self) +{ + return ((void *)self) - sizeof(struct rb_node) - sizeof(u32); +} + +static int map_browser__search(struct map_browser *self) +{ + char target[512]; + struct symbol *sym; + int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40); + + if (err) + return err; + + if (target[0] == '0' && tolower(target[1]) == 'x') { + u64 addr = strtoull(target, NULL, 16); + sym = map__find_symbol(self->map, addr, NULL); + } else + sym = map__find_symbol_by_name(self->map, target, NULL); + + if (sym != NULL) { + u32 *idx = symbol__browser_index(sym); + + self->b.top = &sym->rb_node; + self->b.index = self->b.top_idx = *idx; + } else + ui_helpline__fpush("%s not found!", target); + + return 0; +} + +static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) +{ + if (ui_browser__show(&self->b, self->map->dso->long_name) < 0) + return -1; + + ui_helpline__fpush("Press <- or ESC to exit, %s / to search", + verbose ? "" : "restart with -v to use"); + newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); + if (verbose) + newtFormAddHotKey(self->b.form, '/'); + + while (1) { + ui_browser__run(&self->b, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + if (verbose && es->u.key == '/') + map_browser__search(self); + else + break; + } + + newtFormDestroy(self->b.form); + newtPopWindow(); + ui_helpline__pop(); + return 0; +} + +int map__browse(struct map *self) +{ + struct map_browser mb = { + .b = { + .entries = &self->dso->symbols[self->type], + .refresh = ui_browser__rb_tree_refresh, + .seek = ui_browser__rb_tree_seek, + .write = map_browser__write, + }, + .map = self, + }; + struct newtExitStruct es; + struct rb_node *nd; + char tmp[BITS_PER_LONG / 4]; + u64 maxaddr = 0; + + for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) { + struct symbol *pos = rb_entry(nd, struct symbol, rb_node); + + if (mb.namelen < pos->namelen) + mb.namelen = pos->namelen; + if (maxaddr < pos->end) + maxaddr = pos->end; + if (verbose) { + u32 *idx = symbol__browser_index(pos); + *idx = mb.b.nr_entries; + } + ++mb.b.nr_entries; + } + + mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); + mb.b.width += mb.addrlen * 2 + 4 + mb.namelen; + return map_browser__run(&mb, &es); +} diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/util/ui/browsers/map.h new file mode 100644 index 00000000000..df8581a43e1 --- /dev/null +++ b/tools/perf/util/ui/browsers/map.h @@ -0,0 +1,6 @@ +#ifndef _PERF_UI_MAP_BROWSER_H_ +#define _PERF_UI_MAP_BROWSER_H_ 1 +struct map; + +int map__browse(struct map *self); +#endif /* _PERF_UI_MAP_BROWSER_H_ */ -- cgit v1.2.3-18-g5258 From 696b97a5d2de9e2b22699300835e675dfffe8592 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Mon, 9 Aug 2010 12:21:18 +0100 Subject: perf symbols: Ignore mapping symbols on ARM ARM ELF files use symbols with special names $a, $t, $d to identify regions of ARM code, Thumb code and data within code sections. This can cause confusing output from the perf tools, especially for partially stripped binaries, or binaries containing user-added zero-sized symbols (which may occur in hand-written assembler which hasn't been fully annotated with .size directives). This patch filters out these symbols at load time. LKML-Reference: <1281352878-8735-2-git-send-email-dave.martin@linaro.org> Signed-off-by: Dave Martin Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b6f5970f910..1a367734e01 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1079,6 +1079,16 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, if (!is_label && !elf_sym__is_a(&sym, map->type)) continue; + /* Reject ARM ELF "mapping symbols": these aren't unique and + * don't identify functions, so will confuse the profile + * output: */ + if (ehdr.e_machine == EM_ARM) { + if (!strcmp(elf_name, "$a") || + !strcmp(elf_name, "$d") || + !strcmp(elf_name, "$t")) + continue; + } + if (opdsec && sym.st_shndx == opdidx) { u32 offset = sym.st_value - opdshdr.sh_addr; u64 *opd = opddata->d_buf + offset; -- cgit v1.2.3-18-g5258 From d1b4f2491c3341c61c752049f73ba12553f978d8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:49:07 -0300 Subject: perf ui: Move hists browser to util/ui/browsers/ LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 4 + tools/perf/util/newt.c | 965 +----------------------------------- tools/perf/util/pstack.h | 2 + tools/perf/util/ui/browsers/hists.c | 950 +++++++++++++++++++++++++++++++++++ 4 files changed, 967 insertions(+), 954 deletions(-) create mode 100644 tools/perf/util/ui/browsers/hists.c (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index d118020014f..62e4d6f0dc8 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -570,6 +570,7 @@ else LIB_OBJS += $(OUTPUT)util/newt.o LIB_OBJS += $(OUTPUT)util/ui/browser.o LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o + LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o LIB_OBJS += $(OUTPUT)util/ui/helpline.o LIB_OBJS += $(OUTPUT)util/ui/progress.o @@ -984,6 +985,9 @@ $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< +$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index b596926afa2..6bccdaa159a 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -1,24 +1,19 @@ -#define _GNU_SOURCE -#include -#undef _GNU_SOURCE -#include "ui/libslang.h" -#include -#include -#include #include +#include +#include +#include +#include #include #include "cache.h" -#include "hist.h" -#include "pstack.h" -#include "session.h" -#include "sort.h" -#include "symbol.h" +#include "debug.h" #include "ui/browser.h" #include "ui/helpline.h" -#include "ui/browsers/map.h" newtComponent newt_form__new(void); +int popup_menu(int argc, char * const argv[]); +int ui__help_window(const char *text); +bool dialog_yesno(const char *msg); char browser__last_msg[1024]; @@ -57,7 +52,7 @@ newtComponent newt_form__new(void) return self; } -static int popup_menu(int argc, char * const argv[]) +int popup_menu(int argc, char * const argv[]) { struct newtExitStruct es; int i, rc = -1, max_len = 5; @@ -91,7 +86,7 @@ out_destroy_form: return rc; } -static int ui__help_window(const char *text) +int ui__help_window(const char *text) { struct newtExitStruct es; newtComponent tb, form = newt_form__new(); @@ -133,316 +128,13 @@ out_destroy_form: return rc; } -static bool dialog_yesno(const char *msg) +bool dialog_yesno(const char *msg) { /* newtWinChoice should really be accepting const char pointers... */ char yes[] = "Yes", no[] = "No"; return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } -static char *callchain_list__sym_name(struct callchain_list *self, - char *bf, size_t bfsize) -{ - if (self->ms.sym) - return self->ms.sym->name; - - snprintf(bf, bfsize, "%#Lx", self->ip); - return bf; -} - -struct hist_browser { - struct ui_browser b; - struct hists *hists; - struct hist_entry *he_selection; - struct map_symbol *selection; -}; - -static void hist_browser__reset(struct hist_browser *self); -static int hist_browser__run(struct hist_browser *self, const char *title, - struct newtExitStruct *es); -static unsigned int hist_browser__refresh(struct ui_browser *self); -static void ui_browser__hists_seek(struct ui_browser *self, - off_t offset, int whence); - -static struct hist_browser *hist_browser__new(struct hists *hists) -{ - struct hist_browser *self = zalloc(sizeof(*self)); - - if (self) { - self->hists = hists; - self->b.refresh = hist_browser__refresh; - self->b.seek = ui_browser__hists_seek; - } - - return self; -} - -static void hist_browser__delete(struct hist_browser *self) -{ - newtFormDestroy(self->b.form); - newtPopWindow(); - free(self); -} - -static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) -{ - return self->he_selection; -} - -static struct thread *hist_browser__selected_thread(struct hist_browser *self) -{ - return self->he_selection->thread; -} - -static int hist_browser__title(char *bf, size_t size, const char *ev_name, - const struct dso *dso, const struct thread *thread) -{ - int printed = 0; - - if (thread) - printed += snprintf(bf + printed, size - printed, - "Thread: %s(%d)", - (thread->comm_set ? thread->comm : ""), - thread->pid); - if (dso) - printed += snprintf(bf + printed, size - printed, - "%sDSO: %s", thread ? " " : "", - dso->short_name); - return printed ?: snprintf(bf, size, "Event: %s", ev_name); -} - -int hists__browse(struct hists *self, const char *helpline, const char *ev_name) -{ - struct hist_browser *browser = hist_browser__new(self); - struct pstack *fstack; - const struct thread *thread_filter = NULL; - const struct dso *dso_filter = NULL; - struct newtExitStruct es; - char msg[160]; - int key = -1; - - if (browser == NULL) - return -1; - - fstack = pstack__new(2); - if (fstack == NULL) - goto out; - - ui_helpline__push(helpline); - - hist_browser__title(msg, sizeof(msg), ev_name, - dso_filter, thread_filter); - - while (1) { - const struct thread *thread; - const struct dso *dso; - char *options[16]; - int nr_options = 0, choice = 0, i, - annotate = -2, zoom_dso = -2, zoom_thread = -2, - browse_map = -2; - - if (hist_browser__run(browser, msg, &es)) - break; - - thread = hist_browser__selected_thread(browser); - dso = browser->selection->map ? browser->selection->map->dso : NULL; - - if (es.reason == NEWT_EXIT_HOTKEY) { - key = es.u.key; - - switch (key) { - case NEWT_KEY_F1: - goto do_help; - case NEWT_KEY_TAB: - case NEWT_KEY_UNTAB: - /* - * Exit the browser, let hists__browser_tree - * go to the next or previous - */ - goto out_free_stack; - default:; - } - - key = toupper(key); - switch (key) { - case 'A': - if (browser->selection->map == NULL && - browser->selection->map->dso->annotate_warned) - continue; - goto do_annotate; - case 'D': - goto zoom_dso; - case 'T': - goto zoom_thread; - case 'H': - case '?': -do_help: - ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" - "<- Zoom out\n" - "a Annotate current symbol\n" - "h/?/F1 Show this window\n" - "d Zoom into current DSO\n" - "t Zoom into current Thread\n" - "q/CTRL+C Exit browser"); - continue; - default:; - } - if (is_exit_key(key)) { - if (key == NEWT_KEY_ESCAPE) { - if (dialog_yesno("Do you really want to exit?")) - break; - else - continue; - } else - break; - } - - if (es.u.key == NEWT_KEY_LEFT) { - const void *top; - - if (pstack__empty(fstack)) - continue; - top = pstack__pop(fstack); - if (top == &dso_filter) - goto zoom_out_dso; - if (top == &thread_filter) - goto zoom_out_thread; - continue; - } - } - - if (browser->selection->sym != NULL && - !browser->selection->map->dso->annotate_warned && - asprintf(&options[nr_options], "Annotate %s", - browser->selection->sym->name) > 0) - annotate = nr_options++; - - if (thread != NULL && - asprintf(&options[nr_options], "Zoom %s %s(%d) thread", - (thread_filter ? "out of" : "into"), - (thread->comm_set ? thread->comm : ""), - thread->pid) > 0) - zoom_thread = nr_options++; - - if (dso != NULL && - asprintf(&options[nr_options], "Zoom %s %s DSO", - (dso_filter ? "out of" : "into"), - (dso->kernel ? "the Kernel" : dso->short_name)) > 0) - zoom_dso = nr_options++; - - if (browser->selection->map != NULL && - asprintf(&options[nr_options], "Browse map details") > 0) - browse_map = nr_options++; - - options[nr_options++] = (char *)"Exit"; - - choice = popup_menu(nr_options, options); - - for (i = 0; i < nr_options - 1; ++i) - free(options[i]); - - if (choice == nr_options - 1) - break; - - if (choice == -1) - continue; - - if (choice == annotate) { - struct hist_entry *he; -do_annotate: - if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { - browser->selection->map->dso->annotate_warned = 1; - ui_helpline__puts("No vmlinux file found, can't " - "annotate with just a " - "kallsyms file"); - continue; - } - - he = hist_browser__selected_entry(browser); - if (he == NULL) - continue; - - hist_entry__tui_annotate(he); - } else if (choice == browse_map) - map__browse(browser->selection->map); - else if (choice == zoom_dso) { -zoom_dso: - if (dso_filter) { - pstack__remove(fstack, &dso_filter); -zoom_out_dso: - ui_helpline__pop(); - dso_filter = NULL; - } else { - if (dso == NULL) - continue; - ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", - dso->kernel ? "the Kernel" : dso->short_name); - dso_filter = dso; - pstack__push(fstack, &dso_filter); - } - hists__filter_by_dso(self, dso_filter); - hist_browser__title(msg, sizeof(msg), ev_name, - dso_filter, thread_filter); - hist_browser__reset(browser); - } else if (choice == zoom_thread) { -zoom_thread: - if (thread_filter) { - pstack__remove(fstack, &thread_filter); -zoom_out_thread: - ui_helpline__pop(); - thread_filter = NULL; - } else { - ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", - thread->comm_set ? thread->comm : "", - thread->pid); - thread_filter = thread; - pstack__push(fstack, &thread_filter); - } - hists__filter_by_thread(self, thread_filter); - hist_browser__title(msg, sizeof(msg), ev_name, - dso_filter, thread_filter); - hist_browser__reset(browser); - } - } -out_free_stack: - pstack__delete(fstack); -out: - hist_browser__delete(browser); - return key; -} - -int hists__tui_browse_tree(struct rb_root *self, const char *help) -{ - struct rb_node *first = rb_first(self), *nd = first, *next; - int key = 0; - - while (nd) { - struct hists *hists = rb_entry(nd, struct hists, rb_node); - const char *ev_name = __event_name(hists->type, hists->config); - - key = hists__browse(hists, help, ev_name); - - if (is_exit_key(key)) - break; - - switch (key) { - case NEWT_KEY_TAB: - next = rb_next(nd); - if (next) - nd = next; - break; - case NEWT_KEY_UNTAB: - if (nd == first) - continue; - nd = rb_prev(nd); - default: - break; - } - } - - return key; -} - static void newt_suspend(void *d __used) { newtSuspend(); @@ -476,638 +168,3 @@ void exit_browser(bool wait_for_ok) newtFinished(); } } - -static void hist_browser__refresh_dimensions(struct hist_browser *self) -{ - /* 3 == +/- toggle symbol before actual hist_entry rendering */ - self->b.width = 3 + (hists__sort_list_width(self->hists) + - sizeof("[k]")); -} - -static void hist_browser__reset(struct hist_browser *self) -{ - self->b.nr_entries = self->hists->nr_entries; - hist_browser__refresh_dimensions(self); - ui_browser__reset_index(&self->b); -} - -static char tree__folded_sign(bool unfolded) -{ - return unfolded ? '-' : '+'; -} - -static char map_symbol__folded(const struct map_symbol *self) -{ - return self->has_children ? tree__folded_sign(self->unfolded) : ' '; -} - -static char hist_entry__folded(const struct hist_entry *self) -{ - return map_symbol__folded(&self->ms); -} - -static char callchain_list__folded(const struct callchain_list *self) -{ - return map_symbol__folded(&self->ms); -} - -static bool map_symbol__toggle_fold(struct map_symbol *self) -{ - if (!self->has_children) - return false; - - self->unfolded = !self->unfolded; - return true; -} - -#define LEVEL_OFFSET_STEP 3 - -static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, - struct callchain_node *chain_node, - u64 total, int level, - unsigned short row, - off_t *row_offset, - bool *is_current_entry) -{ - struct rb_node *node; - int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; - u64 new_total, remaining; - - if (callchain_param.mode == CHAIN_GRAPH_REL) - new_total = chain_node->children_hit; - else - new_total = total; - - remaining = new_total; - node = rb_first(&chain_node->rb_root); - while (node) { - struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); - struct rb_node *next = rb_next(node); - u64 cumul = cumul_hits(child); - struct callchain_list *chain; - char folded_sign = ' '; - int first = true; - int extra_offset = 0; - - remaining -= cumul; - - list_for_each_entry(chain, &child->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; - const char *str; - int color; - bool was_first = first; - - if (first) { - first = false; - chain->ms.has_children = chain->list.next != &child->val || - rb_first(&child->rb_root) != NULL; - } else { - extra_offset = LEVEL_OFFSET_STEP; - chain->ms.has_children = chain->list.next == &child->val && - rb_first(&child->rb_root) != NULL; - } - - folded_sign = callchain_list__folded(chain); - if (*row_offset != 0) { - --*row_offset; - goto do_next; - } - - alloc_str = NULL; - str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - if (was_first) { - double percent = cumul * 100.0 / new_total; - - if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) - str = "Not enough memory!"; - else - str = alloc_str; - } - - color = HE_COLORSET_NORMAL; - width = self->b.width - (offset + extra_offset + 2); - if (ui_browser__is_current_entry(&self->b, row)) { - self->selection = &chain->ms; - color = HE_COLORSET_SELECTED; - *is_current_entry = true; - } - - SLsmg_set_color(color); - SLsmg_gotorc(self->b.y + row, self->b.x); - slsmg_write_nstring(" ", offset + extra_offset); - slsmg_printf("%c ", folded_sign); - slsmg_write_nstring(str, width); - free(alloc_str); - - if (++row == self->b.height) - goto out; -do_next: - if (folded_sign == '+') - break; - } - - if (folded_sign == '-') { - const int new_level = level + (extra_offset ? 2 : 1); - row += hist_browser__show_callchain_node_rb_tree(self, child, new_total, - new_level, row, row_offset, - is_current_entry); - } - if (row == self->b.height) - goto out; - node = next; - } -out: - return row - first_row; -} - -static int hist_browser__show_callchain_node(struct hist_browser *self, - struct callchain_node *node, - int level, unsigned short row, - off_t *row_offset, - bool *is_current_entry) -{ - struct callchain_list *chain; - int first_row = row, - offset = level * LEVEL_OFFSET_STEP, - width = self->b.width - offset; - char folded_sign = ' '; - - list_for_each_entry(chain, &node->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], *s; - int color; - /* - * FIXME: This should be moved to somewhere else, - * probably when the callchain is created, so as not to - * traverse it all over again - */ - chain->ms.has_children = rb_first(&node->rb_root) != NULL; - folded_sign = callchain_list__folded(chain); - - if (*row_offset != 0) { - --*row_offset; - continue; - } - - color = HE_COLORSET_NORMAL; - if (ui_browser__is_current_entry(&self->b, row)) { - self->selection = &chain->ms; - color = HE_COLORSET_SELECTED; - *is_current_entry = true; - } - - s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - SLsmg_gotorc(self->b.y + row, self->b.x); - SLsmg_set_color(color); - slsmg_write_nstring(" ", offset); - slsmg_printf("%c ", folded_sign); - slsmg_write_nstring(s, width - 2); - - if (++row == self->b.height) - goto out; - } - - if (folded_sign == '-') - row += hist_browser__show_callchain_node_rb_tree(self, node, - self->hists->stats.total_period, - level + 1, row, - row_offset, - is_current_entry); -out: - return row - first_row; -} - -static int hist_browser__show_callchain(struct hist_browser *self, - struct rb_root *chain, - int level, unsigned short row, - off_t *row_offset, - bool *is_current_entry) -{ - struct rb_node *nd; - int first_row = row; - - for (nd = rb_first(chain); nd; nd = rb_next(nd)) { - struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); - - row += hist_browser__show_callchain_node(self, node, level, - row, row_offset, - is_current_entry); - if (row == self->b.height) - break; - } - - return row - first_row; -} - -static int hist_browser__show_entry(struct hist_browser *self, - struct hist_entry *entry, - unsigned short row) -{ - char s[256]; - double percent; - int printed = 0; - int color, width = self->b.width; - char folded_sign = ' '; - bool current_entry = ui_browser__is_current_entry(&self->b, row); - off_t row_offset = entry->row_offset; - - if (current_entry) { - self->he_selection = entry; - self->selection = &entry->ms; - } - - if (symbol_conf.use_callchain) { - entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain); - folded_sign = hist_entry__folded(entry); - } - - if (row_offset == 0) { - hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, - 0, false, self->hists->stats.total_period); - percent = (entry->period * 100.0) / self->hists->stats.total_period; - - color = HE_COLORSET_SELECTED; - if (!current_entry) { - if (percent >= MIN_RED) - color = HE_COLORSET_TOP; - else if (percent >= MIN_GREEN) - color = HE_COLORSET_MEDIUM; - else - color = HE_COLORSET_NORMAL; - } - - SLsmg_set_color(color); - SLsmg_gotorc(self->b.y + row, self->b.x); - if (symbol_conf.use_callchain) { - slsmg_printf("%c ", folded_sign); - width -= 2; - } - slsmg_write_nstring(s, width); - ++row; - ++printed; - } else - --row_offset; - - if (folded_sign == '-' && row != self->b.height) { - printed += hist_browser__show_callchain(self, &entry->sorted_chain, - 1, row, &row_offset, - ¤t_entry); - if (current_entry) - self->he_selection = entry; - } - - return printed; -} - -static unsigned int hist_browser__refresh(struct ui_browser *self) -{ - unsigned row = 0; - struct rb_node *nd; - struct hist_browser *hb = container_of(self, struct hist_browser, b); - - if (self->top == NULL) - self->top = rb_first(&hb->hists->entries); - - for (nd = self->top; nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (h->filtered) - continue; - - row += hist_browser__show_entry(hb, h, row); - if (row == self->height) - break; - } - - return row; -} - -static void callchain_node__init_have_children_rb_tree(struct callchain_node *self) -{ - struct rb_node *nd = rb_first(&self->rb_root); - - for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { - struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); - struct callchain_list *chain; - int first = true; - - list_for_each_entry(chain, &child->val, list) { - if (first) { - first = false; - chain->ms.has_children = chain->list.next != &child->val || - rb_first(&child->rb_root) != NULL; - } else - chain->ms.has_children = chain->list.next == &child->val && - rb_first(&child->rb_root) != NULL; - } - - callchain_node__init_have_children_rb_tree(child); - } -} - -static void callchain_node__init_have_children(struct callchain_node *self) -{ - struct callchain_list *chain; - - list_for_each_entry(chain, &self->val, list) - chain->ms.has_children = rb_first(&self->rb_root) != NULL; - - callchain_node__init_have_children_rb_tree(self); -} - -static void callchain__init_have_children(struct rb_root *self) -{ - struct rb_node *nd; - - for (nd = rb_first(self); nd; nd = rb_next(nd)) { - struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); - callchain_node__init_have_children(node); - } -} - -static void hist_entry__init_have_children(struct hist_entry *self) -{ - if (!self->init_have_children) { - callchain__init_have_children(&self->sorted_chain); - self->init_have_children = true; - } -} - -static struct rb_node *hists__filter_entries(struct rb_node *nd) -{ - while (nd != NULL) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - if (!h->filtered) - return nd; - - nd = rb_next(nd); - } - - return NULL; -} - -static struct rb_node *hists__filter_prev_entries(struct rb_node *nd) -{ - while (nd != NULL) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - if (!h->filtered) - return nd; - - nd = rb_prev(nd); - } - - return NULL; -} - -static void ui_browser__hists_seek(struct ui_browser *self, - off_t offset, int whence) -{ - struct hist_entry *h; - struct rb_node *nd; - bool first = true; - - switch (whence) { - case SEEK_SET: - nd = hists__filter_entries(rb_first(self->entries)); - break; - case SEEK_CUR: - nd = self->top; - goto do_offset; - case SEEK_END: - nd = hists__filter_prev_entries(rb_last(self->entries)); - first = false; - break; - default: - return; - } - - /* - * Moves not relative to the first visible entry invalidates its - * row_offset: - */ - h = rb_entry(self->top, struct hist_entry, rb_node); - h->row_offset = 0; - - /* - * Here we have to check if nd is expanded (+), if it is we can't go - * the next top level hist_entry, instead we must compute an offset of - * what _not_ to show and not change the first visible entry. - * - * This offset increments when we are going from top to bottom and - * decreases when we're going from bottom to top. - * - * As we don't have backpointers to the top level in the callchains - * structure, we need to always print the whole hist_entry callchain, - * skipping the first ones that are before the first visible entry - * and stop when we printed enough lines to fill the screen. - */ -do_offset: - if (offset > 0) { - do { - h = rb_entry(nd, struct hist_entry, rb_node); - if (h->ms.unfolded) { - u16 remaining = h->nr_rows - h->row_offset; - if (offset > remaining) { - offset -= remaining; - h->row_offset = 0; - } else { - h->row_offset += offset; - offset = 0; - self->top = nd; - break; - } - } - nd = hists__filter_entries(rb_next(nd)); - if (nd == NULL) - break; - --offset; - self->top = nd; - } while (offset != 0); - } else if (offset < 0) { - while (1) { - h = rb_entry(nd, struct hist_entry, rb_node); - if (h->ms.unfolded) { - if (first) { - if (-offset > h->row_offset) { - offset += h->row_offset; - h->row_offset = 0; - } else { - h->row_offset += offset; - offset = 0; - self->top = nd; - break; - } - } else { - if (-offset > h->nr_rows) { - offset += h->nr_rows; - h->row_offset = 0; - } else { - h->row_offset = h->nr_rows + offset; - offset = 0; - self->top = nd; - break; - } - } - } - - nd = hists__filter_prev_entries(rb_prev(nd)); - if (nd == NULL) - break; - ++offset; - self->top = nd; - if (offset == 0) { - /* - * Last unfiltered hist_entry, check if it is - * unfolded, if it is then we should have - * row_offset at its last entry. - */ - h = rb_entry(nd, struct hist_entry, rb_node); - if (h->ms.unfolded) - h->row_offset = h->nr_rows; - break; - } - first = false; - } - } else { - self->top = nd; - h = rb_entry(nd, struct hist_entry, rb_node); - h->row_offset = 0; - } -} - -static int callchain_node__count_rows_rb_tree(struct callchain_node *self) -{ - int n = 0; - struct rb_node *nd; - - for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { - struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); - struct callchain_list *chain; - char folded_sign = ' '; /* No children */ - - list_for_each_entry(chain, &child->val, list) { - ++n; - /* We need this because we may not have children */ - folded_sign = callchain_list__folded(chain); - if (folded_sign == '+') - break; - } - - if (folded_sign == '-') /* Have children and they're unfolded */ - n += callchain_node__count_rows_rb_tree(child); - } - - return n; -} - -static int callchain_node__count_rows(struct callchain_node *node) -{ - struct callchain_list *chain; - bool unfolded = false; - int n = 0; - - list_for_each_entry(chain, &node->val, list) { - ++n; - unfolded = chain->ms.unfolded; - } - - if (unfolded) - n += callchain_node__count_rows_rb_tree(node); - - return n; -} - -static int callchain__count_rows(struct rb_root *chain) -{ - struct rb_node *nd; - int n = 0; - - for (nd = rb_first(chain); nd; nd = rb_next(nd)) { - struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); - n += callchain_node__count_rows(node); - } - - return n; -} - -static bool hist_browser__toggle_fold(struct hist_browser *self) -{ - if (map_symbol__toggle_fold(self->selection)) { - struct hist_entry *he = self->he_selection; - - hist_entry__init_have_children(he); - self->hists->nr_entries -= he->nr_rows; - - if (he->ms.unfolded) - he->nr_rows = callchain__count_rows(&he->sorted_chain); - else - he->nr_rows = 0; - self->hists->nr_entries += he->nr_rows; - self->b.nr_entries = self->hists->nr_entries; - - return true; - } - - /* If it doesn't have children, no toggling performed */ - return false; -} - -static int hist_browser__run(struct hist_browser *self, const char *title, - struct newtExitStruct *es) -{ - char str[256], unit; - unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; - - self->b.entries = &self->hists->entries; - self->b.nr_entries = self->hists->nr_entries; - - hist_browser__refresh_dimensions(self); - - nr_events = convert_unit(nr_events, &unit); - snprintf(str, sizeof(str), "Events: %lu%c ", - nr_events, unit); - newtDrawRootText(0, 0, str); - - if (ui_browser__show(&self->b, title) < 0) - return -1; - - newtFormAddHotKey(self->b.form, 'A'); - newtFormAddHotKey(self->b.form, 'a'); - newtFormAddHotKey(self->b.form, '?'); - newtFormAddHotKey(self->b.form, 'h'); - newtFormAddHotKey(self->b.form, 'H'); - newtFormAddHotKey(self->b.form, 'd'); - - newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); - newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); - newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); - - while (1) { - ui_browser__run(&self->b, es); - - if (es->reason != NEWT_EXIT_HOTKEY) - break; - switch (es->u.key) { - case 'd': { /* Debug */ - static int seq; - struct hist_entry *h = rb_entry(self->b.top, - struct hist_entry, rb_node); - ui_helpline__pop(); - ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", - seq++, self->b.nr_entries, - self->hists->nr_entries, - self->b.height, - self->b.index, - self->b.top_idx, - h->row_offset, h->nr_rows); - } - continue; - case NEWT_KEY_ENTER: - if (hist_browser__toggle_fold(self)) - break; - /* fall thru */ - default: - return 0; - } - } - return 0; -} diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h index 5ad07023504..4cedea59f51 100644 --- a/tools/perf/util/pstack.h +++ b/tools/perf/util/pstack.h @@ -1,6 +1,8 @@ #ifndef _PERF_PSTACK_ #define _PERF_PSTACK_ +#include + struct pstack; struct pstack *pstack__new(unsigned short max_nr_entries); void pstack__delete(struct pstack *self); diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c new file mode 100644 index 00000000000..9d32a4149ab --- /dev/null +++ b/tools/perf/util/ui/browsers/hists.c @@ -0,0 +1,950 @@ +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#include "../libslang.h" +#include +#include +#include +#include + +#include "../../hist.h" +#include "../../pstack.h" +#include "../../sort.h" +#include "../../util.h" + +#include "../browser.h" +#include "../helpline.h" +#include "../util.h" +#include "map.h" + +int ui__help_window(const char *text); +bool dialog_yesno(const char *msg); +int popup_menu(int argc, char * const argv[]); + +struct hist_browser { + struct ui_browser b; + struct hists *hists; + struct hist_entry *he_selection; + struct map_symbol *selection; +}; + +static void hist_browser__refresh_dimensions(struct hist_browser *self) +{ + /* 3 == +/- toggle symbol before actual hist_entry rendering */ + self->b.width = 3 + (hists__sort_list_width(self->hists) + + sizeof("[k]")); +} + +static void hist_browser__reset(struct hist_browser *self) +{ + self->b.nr_entries = self->hists->nr_entries; + hist_browser__refresh_dimensions(self); + ui_browser__reset_index(&self->b); +} + +static char tree__folded_sign(bool unfolded) +{ + return unfolded ? '-' : '+'; +} + +static char map_symbol__folded(const struct map_symbol *self) +{ + return self->has_children ? tree__folded_sign(self->unfolded) : ' '; +} + +static char hist_entry__folded(const struct hist_entry *self) +{ + return map_symbol__folded(&self->ms); +} + +static char callchain_list__folded(const struct callchain_list *self) +{ + return map_symbol__folded(&self->ms); +} + +static int callchain_node__count_rows_rb_tree(struct callchain_node *self) +{ + int n = 0; + struct rb_node *nd; + + for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { + struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); + struct callchain_list *chain; + char folded_sign = ' '; /* No children */ + + list_for_each_entry(chain, &child->val, list) { + ++n; + /* We need this because we may not have children */ + folded_sign = callchain_list__folded(chain); + if (folded_sign == '+') + break; + } + + if (folded_sign == '-') /* Have children and they're unfolded */ + n += callchain_node__count_rows_rb_tree(child); + } + + return n; +} + +static int callchain_node__count_rows(struct callchain_node *node) +{ + struct callchain_list *chain; + bool unfolded = false; + int n = 0; + + list_for_each_entry(chain, &node->val, list) { + ++n; + unfolded = chain->ms.unfolded; + } + + if (unfolded) + n += callchain_node__count_rows_rb_tree(node); + + return n; +} + +static int callchain__count_rows(struct rb_root *chain) +{ + struct rb_node *nd; + int n = 0; + + for (nd = rb_first(chain); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + n += callchain_node__count_rows(node); + } + + return n; +} + +static bool map_symbol__toggle_fold(struct map_symbol *self) +{ + if (!self->has_children) + return false; + + self->unfolded = !self->unfolded; + return true; +} + +static void callchain_node__init_have_children_rb_tree(struct callchain_node *self) +{ + struct rb_node *nd = rb_first(&self->rb_root); + + for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { + struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); + struct callchain_list *chain; + int first = true; + + list_for_each_entry(chain, &child->val, list) { + if (first) { + first = false; + chain->ms.has_children = chain->list.next != &child->val || + rb_first(&child->rb_root) != NULL; + } else + chain->ms.has_children = chain->list.next == &child->val && + rb_first(&child->rb_root) != NULL; + } + + callchain_node__init_have_children_rb_tree(child); + } +} + +static void callchain_node__init_have_children(struct callchain_node *self) +{ + struct callchain_list *chain; + + list_for_each_entry(chain, &self->val, list) + chain->ms.has_children = rb_first(&self->rb_root) != NULL; + + callchain_node__init_have_children_rb_tree(self); +} + +static void callchain__init_have_children(struct rb_root *self) +{ + struct rb_node *nd; + + for (nd = rb_first(self); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + callchain_node__init_have_children(node); + } +} + +static void hist_entry__init_have_children(struct hist_entry *self) +{ + if (!self->init_have_children) { + callchain__init_have_children(&self->sorted_chain); + self->init_have_children = true; + } +} + +static bool hist_browser__toggle_fold(struct hist_browser *self) +{ + if (map_symbol__toggle_fold(self->selection)) { + struct hist_entry *he = self->he_selection; + + hist_entry__init_have_children(he); + self->hists->nr_entries -= he->nr_rows; + + if (he->ms.unfolded) + he->nr_rows = callchain__count_rows(&he->sorted_chain); + else + he->nr_rows = 0; + self->hists->nr_entries += he->nr_rows; + self->b.nr_entries = self->hists->nr_entries; + + return true; + } + + /* If it doesn't have children, no toggling performed */ + return false; +} + +static int hist_browser__run(struct hist_browser *self, const char *title, + struct newtExitStruct *es) +{ + char str[256], unit; + unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; + + self->b.entries = &self->hists->entries; + self->b.nr_entries = self->hists->nr_entries; + + hist_browser__refresh_dimensions(self); + + nr_events = convert_unit(nr_events, &unit); + snprintf(str, sizeof(str), "Events: %lu%c ", + nr_events, unit); + newtDrawRootText(0, 0, str); + + if (ui_browser__show(&self->b, title) < 0) + return -1; + + newtFormAddHotKey(self->b.form, 'A'); + newtFormAddHotKey(self->b.form, 'a'); + newtFormAddHotKey(self->b.form, '?'); + newtFormAddHotKey(self->b.form, 'h'); + newtFormAddHotKey(self->b.form, 'H'); + newtFormAddHotKey(self->b.form, 'd'); + + newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); + newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); + + while (1) { + ui_browser__run(&self->b, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + switch (es->u.key) { + case 'd': { /* Debug */ + static int seq; + struct hist_entry *h = rb_entry(self->b.top, + struct hist_entry, rb_node); + ui_helpline__pop(); + ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", + seq++, self->b.nr_entries, + self->hists->nr_entries, + self->b.height, + self->b.index, + self->b.top_idx, + h->row_offset, h->nr_rows); + } + continue; + case NEWT_KEY_ENTER: + if (hist_browser__toggle_fold(self)) + break; + /* fall thru */ + default: + return 0; + } + } + return 0; +} + +static char *callchain_list__sym_name(struct callchain_list *self, + char *bf, size_t bfsize) +{ + if (self->ms.sym) + return self->ms.sym->name; + + snprintf(bf, bfsize, "%#Lx", self->ip); + return bf; +} + +#define LEVEL_OFFSET_STEP 3 + +static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, + struct callchain_node *chain_node, + u64 total, int level, + unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct rb_node *node; + int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; + u64 new_total, remaining; + + if (callchain_param.mode == CHAIN_GRAPH_REL) + new_total = chain_node->children_hit; + else + new_total = total; + + remaining = new_total; + node = rb_first(&chain_node->rb_root); + while (node) { + struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); + struct rb_node *next = rb_next(node); + u64 cumul = cumul_hits(child); + struct callchain_list *chain; + char folded_sign = ' '; + int first = true; + int extra_offset = 0; + + remaining -= cumul; + + list_for_each_entry(chain, &child->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; + const char *str; + int color; + bool was_first = first; + + if (first) { + first = false; + chain->ms.has_children = chain->list.next != &child->val || + rb_first(&child->rb_root) != NULL; + } else { + extra_offset = LEVEL_OFFSET_STEP; + chain->ms.has_children = chain->list.next == &child->val && + rb_first(&child->rb_root) != NULL; + } + + folded_sign = callchain_list__folded(chain); + if (*row_offset != 0) { + --*row_offset; + goto do_next; + } + + alloc_str = NULL; + str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + if (was_first) { + double percent = cumul * 100.0 / new_total; + + if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) + str = "Not enough memory!"; + else + str = alloc_str; + } + + color = HE_COLORSET_NORMAL; + width = self->b.width - (offset + extra_offset + 2); + if (ui_browser__is_current_entry(&self->b, row)) { + self->selection = &chain->ms; + color = HE_COLORSET_SELECTED; + *is_current_entry = true; + } + + SLsmg_set_color(color); + SLsmg_gotorc(self->b.y + row, self->b.x); + slsmg_write_nstring(" ", offset + extra_offset); + slsmg_printf("%c ", folded_sign); + slsmg_write_nstring(str, width); + free(alloc_str); + + if (++row == self->b.height) + goto out; +do_next: + if (folded_sign == '+') + break; + } + + if (folded_sign == '-') { + const int new_level = level + (extra_offset ? 2 : 1); + row += hist_browser__show_callchain_node_rb_tree(self, child, new_total, + new_level, row, row_offset, + is_current_entry); + } + if (row == self->b.height) + goto out; + node = next; + } +out: + return row - first_row; +} + +static int hist_browser__show_callchain_node(struct hist_browser *self, + struct callchain_node *node, + int level, unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct callchain_list *chain; + int first_row = row, + offset = level * LEVEL_OFFSET_STEP, + width = self->b.width - offset; + char folded_sign = ' '; + + list_for_each_entry(chain, &node->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], *s; + int color; + /* + * FIXME: This should be moved to somewhere else, + * probably when the callchain is created, so as not to + * traverse it all over again + */ + chain->ms.has_children = rb_first(&node->rb_root) != NULL; + folded_sign = callchain_list__folded(chain); + + if (*row_offset != 0) { + --*row_offset; + continue; + } + + color = HE_COLORSET_NORMAL; + if (ui_browser__is_current_entry(&self->b, row)) { + self->selection = &chain->ms; + color = HE_COLORSET_SELECTED; + *is_current_entry = true; + } + + s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + SLsmg_gotorc(self->b.y + row, self->b.x); + SLsmg_set_color(color); + slsmg_write_nstring(" ", offset); + slsmg_printf("%c ", folded_sign); + slsmg_write_nstring(s, width - 2); + + if (++row == self->b.height) + goto out; + } + + if (folded_sign == '-') + row += hist_browser__show_callchain_node_rb_tree(self, node, + self->hists->stats.total_period, + level + 1, row, + row_offset, + is_current_entry); +out: + return row - first_row; +} + +static int hist_browser__show_callchain(struct hist_browser *self, + struct rb_root *chain, + int level, unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct rb_node *nd; + int first_row = row; + + for (nd = rb_first(chain); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + + row += hist_browser__show_callchain_node(self, node, level, + row, row_offset, + is_current_entry); + if (row == self->b.height) + break; + } + + return row - first_row; +} + +static int hist_browser__show_entry(struct hist_browser *self, + struct hist_entry *entry, + unsigned short row) +{ + char s[256]; + double percent; + int printed = 0; + int color, width = self->b.width; + char folded_sign = ' '; + bool current_entry = ui_browser__is_current_entry(&self->b, row); + off_t row_offset = entry->row_offset; + + if (current_entry) { + self->he_selection = entry; + self->selection = &entry->ms; + } + + if (symbol_conf.use_callchain) { + entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain); + folded_sign = hist_entry__folded(entry); + } + + if (row_offset == 0) { + hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, + 0, false, self->hists->stats.total_period); + percent = (entry->period * 100.0) / self->hists->stats.total_period; + + color = HE_COLORSET_SELECTED; + if (!current_entry) { + if (percent >= MIN_RED) + color = HE_COLORSET_TOP; + else if (percent >= MIN_GREEN) + color = HE_COLORSET_MEDIUM; + else + color = HE_COLORSET_NORMAL; + } + + SLsmg_set_color(color); + SLsmg_gotorc(self->b.y + row, self->b.x); + if (symbol_conf.use_callchain) { + slsmg_printf("%c ", folded_sign); + width -= 2; + } + slsmg_write_nstring(s, width); + ++row; + ++printed; + } else + --row_offset; + + if (folded_sign == '-' && row != self->b.height) { + printed += hist_browser__show_callchain(self, &entry->sorted_chain, + 1, row, &row_offset, + ¤t_entry); + if (current_entry) + self->he_selection = entry; + } + + return printed; +} + +static unsigned int hist_browser__refresh(struct ui_browser *self) +{ + unsigned row = 0; + struct rb_node *nd; + struct hist_browser *hb = container_of(self, struct hist_browser, b); + + if (self->top == NULL) + self->top = rb_first(&hb->hists->entries); + + for (nd = self->top; nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (h->filtered) + continue; + + row += hist_browser__show_entry(hb, h, row); + if (row == self->height) + break; + } + + return row; +} + +static struct rb_node *hists__filter_entries(struct rb_node *nd) +{ + while (nd != NULL) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + if (!h->filtered) + return nd; + + nd = rb_next(nd); + } + + return NULL; +} + +static struct rb_node *hists__filter_prev_entries(struct rb_node *nd) +{ + while (nd != NULL) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + if (!h->filtered) + return nd; + + nd = rb_prev(nd); + } + + return NULL; +} + +static void ui_browser__hists_seek(struct ui_browser *self, + off_t offset, int whence) +{ + struct hist_entry *h; + struct rb_node *nd; + bool first = true; + + switch (whence) { + case SEEK_SET: + nd = hists__filter_entries(rb_first(self->entries)); + break; + case SEEK_CUR: + nd = self->top; + goto do_offset; + case SEEK_END: + nd = hists__filter_prev_entries(rb_last(self->entries)); + first = false; + break; + default: + return; + } + + /* + * Moves not relative to the first visible entry invalidates its + * row_offset: + */ + h = rb_entry(self->top, struct hist_entry, rb_node); + h->row_offset = 0; + + /* + * Here we have to check if nd is expanded (+), if it is we can't go + * the next top level hist_entry, instead we must compute an offset of + * what _not_ to show and not change the first visible entry. + * + * This offset increments when we are going from top to bottom and + * decreases when we're going from bottom to top. + * + * As we don't have backpointers to the top level in the callchains + * structure, we need to always print the whole hist_entry callchain, + * skipping the first ones that are before the first visible entry + * and stop when we printed enough lines to fill the screen. + */ +do_offset: + if (offset > 0) { + do { + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) { + u16 remaining = h->nr_rows - h->row_offset; + if (offset > remaining) { + offset -= remaining; + h->row_offset = 0; + } else { + h->row_offset += offset; + offset = 0; + self->top = nd; + break; + } + } + nd = hists__filter_entries(rb_next(nd)); + if (nd == NULL) + break; + --offset; + self->top = nd; + } while (offset != 0); + } else if (offset < 0) { + while (1) { + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) { + if (first) { + if (-offset > h->row_offset) { + offset += h->row_offset; + h->row_offset = 0; + } else { + h->row_offset += offset; + offset = 0; + self->top = nd; + break; + } + } else { + if (-offset > h->nr_rows) { + offset += h->nr_rows; + h->row_offset = 0; + } else { + h->row_offset = h->nr_rows + offset; + offset = 0; + self->top = nd; + break; + } + } + } + + nd = hists__filter_prev_entries(rb_prev(nd)); + if (nd == NULL) + break; + ++offset; + self->top = nd; + if (offset == 0) { + /* + * Last unfiltered hist_entry, check if it is + * unfolded, if it is then we should have + * row_offset at its last entry. + */ + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) + h->row_offset = h->nr_rows; + break; + } + first = false; + } + } else { + self->top = nd; + h = rb_entry(nd, struct hist_entry, rb_node); + h->row_offset = 0; + } +} + +static struct hist_browser *hist_browser__new(struct hists *hists) +{ + struct hist_browser *self = zalloc(sizeof(*self)); + + if (self) { + self->hists = hists; + self->b.refresh = hist_browser__refresh; + self->b.seek = ui_browser__hists_seek; + } + + return self; +} + +static void hist_browser__delete(struct hist_browser *self) +{ + newtFormDestroy(self->b.form); + newtPopWindow(); + free(self); +} + +static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) +{ + return self->he_selection; +} + +static struct thread *hist_browser__selected_thread(struct hist_browser *self) +{ + return self->he_selection->thread; +} + +static int hist_browser__title(char *bf, size_t size, const char *ev_name, + const struct dso *dso, const struct thread *thread) +{ + int printed = 0; + + if (thread) + printed += snprintf(bf + printed, size - printed, + "Thread: %s(%d)", + (thread->comm_set ? thread->comm : ""), + thread->pid); + if (dso) + printed += snprintf(bf + printed, size - printed, + "%sDSO: %s", thread ? " " : "", + dso->short_name); + return printed ?: snprintf(bf, size, "Event: %s", ev_name); +} + +int hists__browse(struct hists *self, const char *helpline, const char *ev_name) +{ + struct hist_browser *browser = hist_browser__new(self); + struct pstack *fstack; + const struct thread *thread_filter = NULL; + const struct dso *dso_filter = NULL; + struct newtExitStruct es; + char msg[160]; + int key = -1; + + if (browser == NULL) + return -1; + + fstack = pstack__new(2); + if (fstack == NULL) + goto out; + + ui_helpline__push(helpline); + + hist_browser__title(msg, sizeof(msg), ev_name, + dso_filter, thread_filter); + + while (1) { + const struct thread *thread; + const struct dso *dso; + char *options[16]; + int nr_options = 0, choice = 0, i, + annotate = -2, zoom_dso = -2, zoom_thread = -2, + browse_map = -2; + + if (hist_browser__run(browser, msg, &es)) + break; + + thread = hist_browser__selected_thread(browser); + dso = browser->selection->map ? browser->selection->map->dso : NULL; + + if (es.reason == NEWT_EXIT_HOTKEY) { + key = es.u.key; + + switch (key) { + case NEWT_KEY_F1: + goto do_help; + case NEWT_KEY_TAB: + case NEWT_KEY_UNTAB: + /* + * Exit the browser, let hists__browser_tree + * go to the next or previous + */ + goto out_free_stack; + default:; + } + + key = toupper(key); + switch (key) { + case 'A': + if (browser->selection->map == NULL && + browser->selection->map->dso->annotate_warned) + continue; + goto do_annotate; + case 'D': + goto zoom_dso; + case 'T': + goto zoom_thread; + case 'H': + case '?': +do_help: + ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" + "<- Zoom out\n" + "a Annotate current symbol\n" + "h/?/F1 Show this window\n" + "d Zoom into current DSO\n" + "t Zoom into current Thread\n" + "q/CTRL+C Exit browser"); + continue; + default:; + } + if (is_exit_key(key)) { + if (key == NEWT_KEY_ESCAPE && + !dialog_yesno("Do you really want to exit?")) + continue; + break; + } + + if (es.u.key == NEWT_KEY_LEFT) { + const void *top; + + if (pstack__empty(fstack)) + continue; + top = pstack__pop(fstack); + if (top == &dso_filter) + goto zoom_out_dso; + if (top == &thread_filter) + goto zoom_out_thread; + continue; + } + } + + if (browser->selection->sym != NULL && + !browser->selection->map->dso->annotate_warned && + asprintf(&options[nr_options], "Annotate %s", + browser->selection->sym->name) > 0) + annotate = nr_options++; + + if (thread != NULL && + asprintf(&options[nr_options], "Zoom %s %s(%d) thread", + (thread_filter ? "out of" : "into"), + (thread->comm_set ? thread->comm : ""), + thread->pid) > 0) + zoom_thread = nr_options++; + + if (dso != NULL && + asprintf(&options[nr_options], "Zoom %s %s DSO", + (dso_filter ? "out of" : "into"), + (dso->kernel ? "the Kernel" : dso->short_name)) > 0) + zoom_dso = nr_options++; + + if (browser->selection->map != NULL && + asprintf(&options[nr_options], "Browse map details") > 0) + browse_map = nr_options++; + + options[nr_options++] = (char *)"Exit"; + + choice = popup_menu(nr_options, options); + + for (i = 0; i < nr_options - 1; ++i) + free(options[i]); + + if (choice == nr_options - 1) + break; + + if (choice == -1) + continue; + + if (choice == annotate) { + struct hist_entry *he; +do_annotate: + if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { + browser->selection->map->dso->annotate_warned = 1; + ui_helpline__puts("No vmlinux file found, can't " + "annotate with just a " + "kallsyms file"); + continue; + } + + he = hist_browser__selected_entry(browser); + if (he == NULL) + continue; + + hist_entry__tui_annotate(he); + } else if (choice == browse_map) + map__browse(browser->selection->map); + else if (choice == zoom_dso) { +zoom_dso: + if (dso_filter) { + pstack__remove(fstack, &dso_filter); +zoom_out_dso: + ui_helpline__pop(); + dso_filter = NULL; + } else { + if (dso == NULL) + continue; + ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", + dso->kernel ? "the Kernel" : dso->short_name); + dso_filter = dso; + pstack__push(fstack, &dso_filter); + } + hists__filter_by_dso(self, dso_filter); + hist_browser__title(msg, sizeof(msg), ev_name, + dso_filter, thread_filter); + hist_browser__reset(browser); + } else if (choice == zoom_thread) { +zoom_thread: + if (thread_filter) { + pstack__remove(fstack, &thread_filter); +zoom_out_thread: + ui_helpline__pop(); + thread_filter = NULL; + } else { + ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", + thread->comm_set ? thread->comm : "", + thread->pid); + thread_filter = thread; + pstack__push(fstack, &thread_filter); + } + hists__filter_by_thread(self, thread_filter); + hist_browser__title(msg, sizeof(msg), ev_name, + dso_filter, thread_filter); + hist_browser__reset(browser); + } + } +out_free_stack: + pstack__delete(fstack); +out: + hist_browser__delete(browser); + return key; +} + +int hists__tui_browse_tree(struct rb_root *self, const char *help) +{ + struct rb_node *first = rb_first(self), *nd = first, *next; + int key = 0; + + while (nd) { + struct hists *hists = rb_entry(nd, struct hists, rb_node); + const char *ev_name = __event_name(hists->type, hists->config); + + key = hists__browse(hists, help, ev_name); + + if (is_exit_key(key)) + break; + + switch (key) { + case NEWT_KEY_TAB: + next = rb_next(nd); + if (next) + nd = next; + break; + case NEWT_KEY_UNTAB: + if (nd == first) + continue; + nd = rb_prev(nd); + default: + break; + } + } + + return key; +} -- cgit v1.2.3-18-g5258 From 1e6dd077a880ba5570beb690523b7a78a91a7615 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:58:50 -0300 Subject: perf ui: Complete the breakdown of util/newt.c LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 7 +- tools/perf/util/debug.c | 2 +- tools/perf/util/debug.h | 6 +- tools/perf/util/newt.c | 170 --------------------------------- tools/perf/util/ui/browsers/annotate.c | 2 +- tools/perf/util/ui/browsers/hists.c | 8 +- tools/perf/util/ui/helpline.c | 26 +++++ tools/perf/util/ui/helpline.h | 1 + tools/perf/util/ui/setup.c | 42 ++++++++ tools/perf/util/ui/util.c | 114 ++++++++++++++++++++++ tools/perf/util/ui/util.h | 10 ++ 11 files changed, 203 insertions(+), 185 deletions(-) delete mode 100644 tools/perf/util/newt.c create mode 100644 tools/perf/util/ui/setup.c create mode 100644 tools/perf/util/ui/util.c create mode 100644 tools/perf/util/ui/util.h (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 62e4d6f0dc8..41abb90df50 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -567,18 +567,20 @@ else # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h BASIC_CFLAGS += -I/usr/include/slang EXTLIBS += -lnewt -lslang - LIB_OBJS += $(OUTPUT)util/newt.o + LIB_OBJS += $(OUTPUT)util/ui/setup.o LIB_OBJS += $(OUTPUT)util/ui/browser.o LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o LIB_OBJS += $(OUTPUT)util/ui/helpline.o LIB_OBJS += $(OUTPUT)util/ui/progress.o + LIB_OBJS += $(OUTPUT)util/ui/util.o LIB_H += util/ui/browser.h LIB_H += util/ui/browsers/map.h LIB_H += util/ui/helpline.h LIB_H += util/ui/libslang.h LIB_H += util/ui/progress.h + LIB_H += util/ui/util.h endif endif @@ -976,9 +978,6 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< -$(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< - $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 318dab15d17..f9c7e3ad1aa 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -23,7 +23,7 @@ int eprintf(int level, const char *fmt, ...) if (verbose >= level) { va_start(args, fmt); if (use_browser > 0) - ret = browser__show_help(fmt, args); + ret = ui_helpline__show_help(fmt, args); else ret = vfprintf(stderr, fmt, args); va_end(args); diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index ba4892e49be..7a17ee061bc 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -14,7 +14,7 @@ void trace_event(event_t *event); struct ui_progress; #ifdef NO_NEWT_SUPPORT -static inline int browser__show_help(const char *format __used, va_list ap __used) +static inline int ui_helpline__show_help(const char *format __used, va_list ap __used) { return 0; } @@ -30,8 +30,8 @@ static inline void ui_progress__update(struct ui_progress *self __used, static inline void ui_progress__delete(struct ui_progress *self __used) {} #else -extern char browser__last_msg[]; -int browser__show_help(const char *format, va_list ap); +extern char ui_helpline__last_msg[]; +int ui_helpline__show_help(const char *format, va_list ap); #include "ui/progress.h" #endif diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c deleted file mode 100644 index 6bccdaa159a..00000000000 --- a/tools/perf/util/newt.c +++ /dev/null @@ -1,170 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "cache.h" -#include "debug.h" -#include "ui/browser.h" -#include "ui/helpline.h" - -newtComponent newt_form__new(void); -int popup_menu(int argc, char * const argv[]); -int ui__help_window(const char *text); -bool dialog_yesno(const char *msg); - -char browser__last_msg[1024]; - -int browser__show_help(const char *format, va_list ap) -{ - int ret; - static int backlog; - - ret = vsnprintf(browser__last_msg + backlog, - sizeof(browser__last_msg) - backlog, format, ap); - backlog += ret; - - if (browser__last_msg[backlog - 1] == '\n') { - ui_helpline__puts(browser__last_msg); - newtRefresh(); - backlog = 0; - } - - return ret; -} - -static void newt_form__set_exit_keys(newtComponent self) -{ - newtFormAddHotKey(self, NEWT_KEY_LEFT); - newtFormAddHotKey(self, NEWT_KEY_ESCAPE); - newtFormAddHotKey(self, 'Q'); - newtFormAddHotKey(self, 'q'); - newtFormAddHotKey(self, CTRL('c')); -} - -newtComponent newt_form__new(void) -{ - newtComponent self = newtForm(NULL, NULL, 0); - if (self) - newt_form__set_exit_keys(self); - return self; -} - -int popup_menu(int argc, char * const argv[]) -{ - struct newtExitStruct es; - int i, rc = -1, max_len = 5; - newtComponent listbox, form = newt_form__new(); - - if (form == NULL) - return -1; - - listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); - if (listbox == NULL) - goto out_destroy_form; - - newtFormAddComponent(form, listbox); - - for (i = 0; i < argc; ++i) { - int len = strlen(argv[i]); - if (len > max_len) - max_len = len; - if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i)) - goto out_destroy_form; - } - - newtCenteredWindow(max_len, argc, NULL); - newtFormRun(form, &es); - rc = newtListboxGetCurrent(listbox) - NULL; - if (es.reason == NEWT_EXIT_HOTKEY) - rc = -1; - newtPopWindow(); -out_destroy_form: - newtFormDestroy(form); - return rc; -} - -int ui__help_window(const char *text) -{ - struct newtExitStruct es; - newtComponent tb, form = newt_form__new(); - int rc = -1; - int max_len = 0, nr_lines = 0; - const char *t; - - if (form == NULL) - return -1; - - t = text; - while (1) { - const char *sep = strchr(t, '\n'); - int len; - - if (sep == NULL) - sep = strchr(t, '\0'); - len = sep - t; - if (max_len < len) - max_len = len; - ++nr_lines; - if (*sep == '\0') - break; - t = sep + 1; - } - - tb = newtTextbox(0, 0, max_len, nr_lines, 0); - if (tb == NULL) - goto out_destroy_form; - - newtTextboxSetText(tb, text); - newtFormAddComponent(form, tb); - newtCenteredWindow(max_len, nr_lines, NULL); - newtFormRun(form, &es); - newtPopWindow(); - rc = 0; -out_destroy_form: - newtFormDestroy(form); - return rc; -} - -bool dialog_yesno(const char *msg) -{ - /* newtWinChoice should really be accepting const char pointers... */ - char yes[] = "Yes", no[] = "No"; - return newtWinChoice(NULL, yes, no, (char *)msg) == 1; -} - -static void newt_suspend(void *d __used) -{ - newtSuspend(); - raise(SIGTSTP); - newtResume(); -} - -void setup_browser(void) -{ - if (!isatty(1) || !use_browser || dump_trace) { - use_browser = 0; - setup_pager(); - return; - } - - use_browser = 1; - newtInit(); - newtCls(); - newtSetSuspendCallback(newt_suspend, NULL); - ui_helpline__puts(" "); - ui_browser__init(); -} - -void exit_browser(bool wait_for_ok) -{ - if (use_browser > 0) { - if (wait_for_ok) { - char title[] = "Fatal Error", ok[] = "Ok"; - newtWinMessage(title, ok, browser__last_msg); - } - newtFinished(); - } -} diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 783d277f219..5b01df633f9 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -86,7 +86,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) return -1; if (hist_entry__annotate(self, &head) < 0) { - ui__error_window(browser__last_msg); + ui__error_window(ui_helpline__last_msg); return -1; } diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 9d32a4149ab..cee7998f1c3 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -17,10 +17,6 @@ #include "../util.h" #include "map.h" -int ui__help_window(const char *text); -bool dialog_yesno(const char *msg); -int popup_menu(int argc, char * const argv[]); - struct hist_browser { struct ui_browser b; struct hists *hists; @@ -798,7 +794,7 @@ do_help: } if (is_exit_key(key)) { if (key == NEWT_KEY_ESCAPE && - !dialog_yesno("Do you really want to exit?")) + !ui__dialog_yesno("Do you really want to exit?")) continue; break; } @@ -842,7 +838,7 @@ do_help: options[nr_options++] = (char *)"Exit"; - choice = popup_menu(nr_options, options); + choice = ui__popup_menu(nr_options, options); for (i = 0; i < nr_options - 1; ++i) free(options[i]); diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index 6a11e130155..ff584606a4d 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c @@ -3,6 +3,7 @@ #include #include +#include "../debug.h" #include "helpline.h" void ui_helpline__pop(void) @@ -41,3 +42,28 @@ void ui_helpline__puts(const char *msg) ui_helpline__pop(); ui_helpline__push(msg); } + +void ui_helpline__init(void) +{ + ui_helpline__puts(" "); +} + +char ui_helpline__last_msg[1024]; + +int ui_helpline__show_help(const char *format, va_list ap) +{ + int ret; + static int backlog; + + ret = vsnprintf(ui_helpline__last_msg + backlog, + sizeof(ui_helpline__last_msg) - backlog, format, ap); + backlog += ret; + + if (ui_helpline__last_msg[backlog - 1] == '\n') { + ui_helpline__puts(ui_helpline__last_msg); + newtRefresh(); + backlog = 0; + } + + return ret; +} diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h index 56d8c1d8ffc..5d1e5e72ffb 100644 --- a/tools/perf/util/ui/helpline.h +++ b/tools/perf/util/ui/helpline.h @@ -1,6 +1,7 @@ #ifndef _PERF_UI_HELPLINE_H_ #define _PERF_UI_HELPLINE_H_ 1 +void ui_helpline__init(void); void ui_helpline__pop(void); void ui_helpline__push(const char *msg); void ui_helpline__fpush(const char *fmt, ...); diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c new file mode 100644 index 00000000000..662085032eb --- /dev/null +++ b/tools/perf/util/ui/setup.c @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "../cache.h" +#include "../debug.h" +#include "browser.h" +#include "helpline.h" + +static void newt_suspend(void *d __used) +{ + newtSuspend(); + raise(SIGTSTP); + newtResume(); +} + +void setup_browser(void) +{ + if (!isatty(1) || !use_browser || dump_trace) { + use_browser = 0; + setup_pager(); + return; + } + + use_browser = 1; + newtInit(); + newtCls(); + newtSetSuspendCallback(newt_suspend, NULL); + ui_helpline__init(); + ui_browser__init(); +} + +void exit_browser(bool wait_for_ok) +{ + if (use_browser > 0) { + if (wait_for_ok) { + char title[] = "Fatal Error", ok[] = "Ok"; + newtWinMessage(title, ok, ui_helpline__last_msg); + } + newtFinished(); + } +} diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c new file mode 100644 index 00000000000..04600e26cee --- /dev/null +++ b/tools/perf/util/ui/util.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include + +#include "../cache.h" +#include "../debug.h" +#include "browser.h" +#include "helpline.h" +#include "util.h" + +newtComponent newt_form__new(void); + +static void newt_form__set_exit_keys(newtComponent self) +{ + newtFormAddHotKey(self, NEWT_KEY_LEFT); + newtFormAddHotKey(self, NEWT_KEY_ESCAPE); + newtFormAddHotKey(self, 'Q'); + newtFormAddHotKey(self, 'q'); + newtFormAddHotKey(self, CTRL('c')); +} + +newtComponent newt_form__new(void) +{ + newtComponent self = newtForm(NULL, NULL, 0); + if (self) + newt_form__set_exit_keys(self); + return self; +} + +int ui__popup_menu(int argc, char * const argv[]) +{ + struct newtExitStruct es; + int i, rc = -1, max_len = 5; + newtComponent listbox, form = newt_form__new(); + + if (form == NULL) + return -1; + + listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); + if (listbox == NULL) + goto out_destroy_form; + + newtFormAddComponent(form, listbox); + + for (i = 0; i < argc; ++i) { + int len = strlen(argv[i]); + if (len > max_len) + max_len = len; + if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i)) + goto out_destroy_form; + } + + newtCenteredWindow(max_len, argc, NULL); + newtFormRun(form, &es); + rc = newtListboxGetCurrent(listbox) - NULL; + if (es.reason == NEWT_EXIT_HOTKEY) + rc = -1; + newtPopWindow(); +out_destroy_form: + newtFormDestroy(form); + return rc; +} + +int ui__help_window(const char *text) +{ + struct newtExitStruct es; + newtComponent tb, form = newt_form__new(); + int rc = -1; + int max_len = 0, nr_lines = 0; + const char *t; + + if (form == NULL) + return -1; + + t = text; + while (1) { + const char *sep = strchr(t, '\n'); + int len; + + if (sep == NULL) + sep = strchr(t, '\0'); + len = sep - t; + if (max_len < len) + max_len = len; + ++nr_lines; + if (*sep == '\0') + break; + t = sep + 1; + } + + tb = newtTextbox(0, 0, max_len, nr_lines, 0); + if (tb == NULL) + goto out_destroy_form; + + newtTextboxSetText(tb, text); + newtFormAddComponent(form, tb); + newtCenteredWindow(max_len, nr_lines, NULL); + newtFormRun(form, &es); + newtPopWindow(); + rc = 0; +out_destroy_form: + newtFormDestroy(form); + return rc; +} + +bool ui__dialog_yesno(const char *msg) +{ + /* newtWinChoice should really be accepting const char pointers... */ + char yes[] = "Yes", no[] = "No"; + return newtWinChoice(NULL, yes, no, (char *)msg) == 1; +} diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h new file mode 100644 index 00000000000..afcbc1d9953 --- /dev/null +++ b/tools/perf/util/ui/util.h @@ -0,0 +1,10 @@ +#ifndef _PERF_UI_UTIL_H_ +#define _PERF_UI_UTIL_H_ 1 + +#include + +int ui__popup_menu(int argc, char * const argv[]); +int ui__help_window(const char *text); +bool ui__dialog_yesno(const char *msg); + +#endif /* _PERF_UI_UTIL_H_ */ -- cgit v1.2.3-18-g5258 From 92221162875ec48913d3f9710046e48d599c9cf2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 9 Aug 2010 15:30:40 -0300 Subject: perf annotate: Sort by hottest lines in the TUI Right now it will just sort and position at the hottest line, i.e. the one where more samples were taken. It will be at the center of the screen and later TAB/shift-TAB will cycle thru the hottest lines. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 2 +- tools/perf/util/hist.c | 13 +-- tools/perf/util/hist.h | 3 +- tools/perf/util/ui/browsers/annotate.c | 153 +++++++++++++++++++++++++-------- 4 files changed, 125 insertions(+), 46 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index fd20670ce98..1478dc64bf1 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -285,7 +285,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he) LIST_HEAD(head); struct objdump_line *pos, *n; - if (hist_entry__annotate(he, &head) < 0) + if (hist_entry__annotate(he, &head, 0) < 0) return -1; if (full_paths) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 62ec9b0e4b9..be22ae6ef05 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -983,9 +983,9 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) return 0; } -static struct objdump_line *objdump_line__new(s64 offset, char *line) +static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) { - struct objdump_line *self = malloc(sizeof(*self)); + struct objdump_line *self = malloc(sizeof(*self) + privsize); if (self != NULL) { self->offset = offset; @@ -1017,7 +1017,7 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head, } static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, - struct list_head *head) + struct list_head *head, size_t privsize) { struct symbol *sym = self->ms.sym; struct objdump_line *objdump_line; @@ -1068,7 +1068,7 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, offset = -1; } - objdump_line = objdump_line__new(offset, line); + objdump_line = objdump_line__new(offset, line, privsize); if (objdump_line == NULL) { free(line); return -1; @@ -1078,7 +1078,8 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, return 0; } -int hist_entry__annotate(struct hist_entry *self, struct list_head *head) +int hist_entry__annotate(struct hist_entry *self, struct list_head *head, + size_t privsize) { struct symbol *sym = self->ms.sym; struct map *map = self->ms.map; @@ -1143,7 +1144,7 @@ fallback: goto out_free_filename; while (!feof(file)) - if (hist_entry__parse_objdump_line(self, file, head) < 0) + if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0) break; pclose(file); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 65a48db46a2..587d375d343 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -101,7 +101,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); -int hist_entry__annotate(struct hist_entry *self, struct list_head *head); +int hist_entry__annotate(struct hist_entry *self, struct list_head *head, + size_t privsize); void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 5b01df633f9..763592b09d7 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -14,6 +14,23 @@ static void ui__error_window(const char *fmt, ...) va_end(ap); } +struct annotate_browser { + struct ui_browser b; + struct rb_root entries; +}; + +struct objdump_line_rb_node { + struct rb_node rb_node; + double percent; + u32 idx; +}; + +static inline +struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) +{ + return (struct objdump_line_rb_node *)(self + 1); +} + static void annotate_browser__write(struct ui_browser *self, void *entry, int row) { struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); @@ -21,17 +38,41 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro int width = self->width; if (ol->offset != -1) { - struct hist_entry *he = self->priv; - struct symbol *sym = he->ms.sym; - int len = he->ms.sym->end - he->ms.sym->start; + struct objdump_line_rb_node *olrb = objdump_line__rb(ol); + int color = ui_browser__percent_color(olrb->percent, current_entry); + SLsmg_set_color(color); + slsmg_printf(" %7.2f ", olrb->percent); + if (!current_entry) + SLsmg_set_color(HE_COLORSET_CODE); + } else { + int color = ui_browser__percent_color(0, current_entry); + SLsmg_set_color(color); + slsmg_write_nstring(" ", 9); + } + + SLsmg_write_char(':'); + slsmg_write_nstring(" ", 8); + if (!*ol->line) + slsmg_write_nstring(" ", width - 18); + else + slsmg_write_nstring(ol->line, width - 18); +} + +static double objdump_line__calc_percent(struct objdump_line *self, + struct list_head *head, + struct symbol *sym) +{ + double percent = 0.0; + + if (self->offset != -1) { + int len = sym->end - sym->start; unsigned int hits = 0; - double percent = 0.0; - int color; struct sym_priv *priv = symbol__priv(sym); struct sym_ext *sym_ext = priv->ext; struct sym_hist *h = priv->hist; - s64 offset = ol->offset; - struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol); + s64 offset = self->offset; + struct objdump_line *next = objdump__get_next_ip_line(head, self); + while (offset < (s64)len && (next == NULL || offset < next->offset)) { @@ -45,37 +86,45 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro if (sym_ext == NULL && h->sum) percent = 100.0 * hits / h->sum; - - color = ui_browser__percent_color(percent, current_entry); - SLsmg_set_color(color); - slsmg_printf(" %7.2f ", percent); - if (!current_entry) - SLsmg_set_color(HE_COLORSET_CODE); - } else { - int color = ui_browser__percent_color(0, current_entry); - SLsmg_set_color(color); - slsmg_write_nstring(" ", 9); } - SLsmg_write_char(':'); - slsmg_write_nstring(" ", 8); - if (!*ol->line) - slsmg_write_nstring(" ", width - 18); - else - slsmg_write_nstring(ol->line, width - 18); + return percent; +} + +static void objdump__insert_line(struct rb_root *self, + struct objdump_line_rb_node *line) +{ + struct rb_node **p = &self->rb_node; + struct rb_node *parent = NULL; + struct objdump_line_rb_node *l; + + while (*p != NULL) { + parent = *p; + l = rb_entry(parent, struct objdump_line_rb_node, rb_node); + if (line->percent < l->percent) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&line->rb_node, parent, p); + rb_insert_color(&line->rb_node, self); } int hist_entry__tui_annotate(struct hist_entry *self) { struct newtExitStruct es; struct objdump_line *pos, *n; + struct objdump_line_rb_node *rbpos; + struct rb_node *nd; LIST_HEAD(head); - struct ui_browser browser = { - .entries = &head, - .refresh = ui_browser__list_head_refresh, - .seek = ui_browser__list_head_seek, - .write = annotate_browser__write, - .priv = self, + struct annotate_browser browser = { + .b = { + .entries = &head, + .refresh = ui_browser__list_head_refresh, + .seek = ui_browser__list_head_seek, + .write = annotate_browser__write, + .priv = self, + }, }; int ret; @@ -85,7 +134,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) if (self->ms.map->dso->annotate_warned) return -1; - if (hist_entry__annotate(self, &head) < 0) { + if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) { ui__error_window(ui_helpline__last_msg); return -1; } @@ -94,16 +143,44 @@ int hist_entry__tui_annotate(struct hist_entry *self) list_for_each_entry(pos, &head, node) { size_t line_len = strlen(pos->line); - if (browser.width < line_len) - browser.width = line_len; - ++browser.nr_entries; + if (browser.b.width < line_len) + browser.b.width = line_len; + rbpos = objdump_line__rb(pos); + rbpos->idx = browser.b.nr_entries++; + rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym); + if (rbpos->percent < 0.01) + continue; + objdump__insert_line(&browser.entries, rbpos); + } + + /* + * Position the browser at the hottest line. + */ + nd = rb_last(&browser.entries); + if (nd != NULL) { + unsigned back; + + ui_browser__refresh_dimensions(&browser.b); + back = browser.b.height / 2; + rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node); + pos = ((struct objdump_line *)rbpos) - 1; + browser.b.top_idx = browser.b.index = rbpos->idx; + + while (browser.b.top_idx != 0 && back != 0) { + pos = list_entry(pos->node.prev, struct objdump_line, node); + + --browser.b.top_idx; + --back; + } + + browser.b.top = pos; } - browser.width += 18; /* Percentage */ - ui_browser__show(&browser, self->ms.sym->name); - newtFormAddHotKey(browser.form, ' '); - ret = ui_browser__run(&browser, &es); - newtFormDestroy(browser.form); + browser.b.width += 18; /* Percentage */ + ui_browser__show(&browser.b, self->ms.sym->name); + newtFormAddHotKey(browser.b.form, ' '); + ret = ui_browser__run(&browser.b, &es); + newtFormDestroy(browser.b.form); newtPopWindow(); list_for_each_entry_safe(pos, n, &head, node) { list_del(&pos->node); -- cgit v1.2.3-18-g5258 From 9e22d6377ce6f31b1cc0bff16daeda2780495061 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:09:02 -0300 Subject: perf ui: Make SPACE work as PGDN in all browsers Not just on the annotate one. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ui/browser.c | 1 + tools/perf/util/ui/browsers/annotate.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index edbb7dd9eb2..83d57487f6e 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -169,6 +169,7 @@ int ui_browser__show(struct ui_browser *self, const char *title) newtFormAddHotKey(self->form, NEWT_KEY_PGDN); newtFormAddHotKey(self->form, NEWT_KEY_HOME); newtFormAddHotKey(self->form, NEWT_KEY_END); + newtFormAddHotKey(self->form, ' '); newtFormAddComponent(self->form, self->sb); return 0; } diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 763592b09d7..d2156aebd4f 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -178,7 +178,6 @@ int hist_entry__tui_annotate(struct hist_entry *self) browser.b.width += 18; /* Percentage */ ui_browser__show(&browser.b, self->ms.sym->name); - newtFormAddHotKey(browser.b.form, ' '); ret = ui_browser__run(&browser.b, &es); newtFormDestroy(browser.b.form); newtPopWindow(); -- cgit v1.2.3-18-g5258 From f1e9214cc99644101d957c5c660946c6f2f86d7c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:14:53 -0300 Subject: perf annotate: Cycle thru sorted lines with samples The annotate TUI now starts centered on the line with most samples, i.e. the hottest line in the annotated function. Pressing TAB will center on the second hottest function and so on. Shift+TAB goes in the other direction. This way one can more easily sift thru the function hotspots. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ui/browsers/annotate.c | 102 +++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 25 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index d2156aebd4f..73e78ef38a5 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -17,6 +17,7 @@ static void ui__error_window(const char *fmt, ...) struct annotate_browser { struct ui_browser b; struct rb_root entries; + struct rb_node *curr_hot; }; struct objdump_line_rb_node { @@ -110,12 +111,83 @@ static void objdump__insert_line(struct rb_root *self, rb_insert_color(&line->rb_node, self); } +static void annotate_browser__set_top(struct annotate_browser *self, + struct rb_node *nd) +{ + struct objdump_line_rb_node *rbpos; + struct objdump_line *pos; + unsigned back; + + ui_browser__refresh_dimensions(&self->b); + back = self->b.height / 2; + rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node); + pos = ((struct objdump_line *)rbpos) - 1; + self->b.top_idx = self->b.index = rbpos->idx; + + while (self->b.top_idx != 0 && back != 0) { + pos = list_entry(pos->node.prev, struct objdump_line, node); + + --self->b.top_idx; + --back; + } + + self->b.top = pos; + self->curr_hot = nd; +} + +static int annotate_browser__run(struct annotate_browser *self, + struct newtExitStruct *es) +{ + struct rb_node *nd; + struct hist_entry *he = self->b.priv; + + if (ui_browser__show(&self->b, he->ms.sym->name) < 0) + return -1; + + ui_helpline__fpush("<- or ESC: exit, TAB/shift+TAB: cycle thru samples"); + newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + + nd = self->curr_hot; + if (nd) { + newtFormAddHotKey(self->b.form, NEWT_KEY_TAB); + newtFormAddHotKey(self->b.form, NEWT_KEY_UNTAB); + } + + while (1) { + ui_browser__run(&self->b, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + + switch (es->u.key) { + case NEWT_KEY_TAB: + nd = rb_prev(nd); + if (nd == NULL) + nd = rb_last(&self->entries); + annotate_browser__set_top(self, nd); + break; + case NEWT_KEY_UNTAB: + nd = rb_next(nd); + if (nd == NULL) + nd = rb_first(&self->entries); + annotate_browser__set_top(self, nd); + break; + default: + goto out; + } + } +out: + newtFormDestroy(self->b.form); + newtPopWindow(); + ui_helpline__pop(); + return 0; +} + int hist_entry__tui_annotate(struct hist_entry *self) { struct newtExitStruct es; struct objdump_line *pos, *n; struct objdump_line_rb_node *rbpos; - struct rb_node *nd; LIST_HEAD(head); struct annotate_browser browser = { .b = { @@ -156,35 +228,15 @@ int hist_entry__tui_annotate(struct hist_entry *self) /* * Position the browser at the hottest line. */ - nd = rb_last(&browser.entries); - if (nd != NULL) { - unsigned back; - - ui_browser__refresh_dimensions(&browser.b); - back = browser.b.height / 2; - rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node); - pos = ((struct objdump_line *)rbpos) - 1; - browser.b.top_idx = browser.b.index = rbpos->idx; - - while (browser.b.top_idx != 0 && back != 0) { - pos = list_entry(pos->node.prev, struct objdump_line, node); - - --browser.b.top_idx; - --back; - } - - browser.b.top = pos; - } + browser.curr_hot = rb_last(&browser.entries); + if (browser.curr_hot) + annotate_browser__set_top(&browser, browser.curr_hot); browser.b.width += 18; /* Percentage */ - ui_browser__show(&browser.b, self->ms.sym->name); - ret = ui_browser__run(&browser.b, &es); - newtFormDestroy(browser.b.form); - newtPopWindow(); + ret = annotate_browser__run(&browser, &es); list_for_each_entry_safe(pos, n, &head, node) { list_del(&pos->node); objdump_line__free(pos); } - ui_helpline__pop(); return ret; } -- cgit v1.2.3-18-g5258 From 59e8fe32fc0cc9dff6b0c269d099a49e004dc45e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:44:20 -0300 Subject: perf ui browser: Add ui_browser__show counterpart: __hide So that the common tasks of providing a helpline at __run entry and destroying the window and releasing resourses at exit can be abstracted away, reducing a bit more the coupling with libnewt. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ui/browser.c | 18 +++++++++++++++++- tools/perf/util/ui/browser.h | 4 +++- tools/perf/util/ui/browsers/annotate.c | 8 +++----- tools/perf/util/ui/browsers/hists.c | 5 ++++- tools/perf/util/ui/browsers/map.c | 10 ++++------ tools/perf/util/ui/helpline.c | 2 +- tools/perf/util/ui/helpline.h | 1 + 7 files changed, 33 insertions(+), 15 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 83d57487f6e..66f2d583d8c 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -16,6 +16,7 @@ #include #include #include "browser.h" +#include "helpline.h" #include "../color.h" #include "../util.h" @@ -145,8 +146,11 @@ void ui_browser__reset_index(struct ui_browser *self) self->seek(self, 0, SEEK_SET); } -int ui_browser__show(struct ui_browser *self, const char *title) +int ui_browser__show(struct ui_browser *self, const char *title, + const char *helpline, ...) { + va_list ap; + if (self->form != NULL) { newtFormDestroy(self->form); newtPopWindow(); @@ -171,9 +175,21 @@ int ui_browser__show(struct ui_browser *self, const char *title) newtFormAddHotKey(self->form, NEWT_KEY_END); newtFormAddHotKey(self->form, ' '); newtFormAddComponent(self->form, self->sb); + + va_start(ap, helpline); + ui_helpline__vpush(helpline, ap); + va_end(ap); return 0; } +void ui_browser__hide(struct ui_browser *self) +{ + newtFormDestroy(self->form); + newtPopWindow(); + self->form = NULL; + ui_helpline__pop(); +} + int ui_browser__refresh(struct ui_browser *self) { int row; diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index 856e3436172..0b9f829214f 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h @@ -30,7 +30,9 @@ bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row); void ui_browser__refresh_dimensions(struct ui_browser *self); void ui_browser__reset_index(struct ui_browser *self); -int ui_browser__show(struct ui_browser *self, const char *title); +int ui_browser__show(struct ui_browser *self, const char *title, + const char *helpline, ...); +void ui_browser__hide(struct ui_browser *self); int ui_browser__refresh(struct ui_browser *self); int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es); diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 73e78ef38a5..55ff792459a 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -141,10 +141,10 @@ static int annotate_browser__run(struct annotate_browser *self, struct rb_node *nd; struct hist_entry *he = self->b.priv; - if (ui_browser__show(&self->b, he->ms.sym->name) < 0) + if (ui_browser__show(&self->b, he->ms.sym->name, + "<- or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0) return -1; - ui_helpline__fpush("<- or ESC: exit, TAB/shift+TAB: cycle thru samples"); newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); nd = self->curr_hot; @@ -177,9 +177,7 @@ static int annotate_browser__run(struct annotate_browser *self, } } out: - newtFormDestroy(self->b.form); - newtPopWindow(); - ui_helpline__pop(); + ui_browser__hide(&self->b); return 0; } diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index cee7998f1c3..dd512b73b01 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -211,7 +211,8 @@ static int hist_browser__run(struct hist_browser *self, const char *title, nr_events, unit); newtDrawRootText(0, 0, str); - if (ui_browser__show(&self->b, title) < 0) + if (ui_browser__show(&self->b, title, + "Press '?' for help on key bindings") < 0) return -1; newtFormAddHotKey(self->b.form, 'A'); @@ -253,6 +254,8 @@ static int hist_browser__run(struct hist_browser *self, const char *title, return 0; } } + + ui_browser__hide(&self->b); return 0; } diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index b79f0c996ea..142b825b42b 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c @@ -100,11 +100,11 @@ static int map_browser__search(struct map_browser *self) static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) { - if (ui_browser__show(&self->b, self->map->dso->long_name) < 0) + if (ui_browser__show(&self->b, self->map->dso->long_name, + "Press <- or ESC to exit, %s / to search", + verbose ? "" : "restart with -v to use") < 0) return -1; - ui_helpline__fpush("Press <- or ESC to exit, %s / to search", - verbose ? "" : "restart with -v to use"); newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); if (verbose) @@ -121,9 +121,7 @@ static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) break; } - newtFormDestroy(self->b.form); - newtPopWindow(); - ui_helpline__pop(); + ui_browser__hide(&self->b); return 0; } diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index ff584606a4d..8d79daa4458 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c @@ -16,7 +16,7 @@ void ui_helpline__push(const char *msg) newtPushHelpLine(msg); } -static void ui_helpline__vpush(const char *fmt, va_list ap) +void ui_helpline__vpush(const char *fmt, va_list ap) { char *s; diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h index 5d1e5e72ffb..ab6028d0c40 100644 --- a/tools/perf/util/ui/helpline.h +++ b/tools/perf/util/ui/helpline.h @@ -4,6 +4,7 @@ void ui_helpline__init(void); void ui_helpline__pop(void); void ui_helpline__push(const char *msg); +void ui_helpline__vpush(const char *fmt, va_list ap); void ui_helpline__fpush(const char *fmt, ...); void ui_helpline__puts(const char *msg); -- cgit v1.2.3-18-g5258 From 4694153c252a6ae19704b5bb66466050256395a4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:50:07 -0300 Subject: perf ui hist browser: Fixup key bindings To match what is shown when '?' or 'H' is pressed, i.e. the keybind help window. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ui/browsers/hists.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index dd512b73b01..dafdf6775d7 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -215,12 +215,12 @@ static int hist_browser__run(struct hist_browser *self, const char *title, "Press '?' for help on key bindings") < 0) return -1; - newtFormAddHotKey(self->b.form, 'A'); newtFormAddHotKey(self->b.form, 'a'); newtFormAddHotKey(self->b.form, '?'); newtFormAddHotKey(self->b.form, 'h'); - newtFormAddHotKey(self->b.form, 'H'); newtFormAddHotKey(self->b.form, 'd'); + newtFormAddHotKey(self->b.form, 'D'); + newtFormAddHotKey(self->b.form, 't'); newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); @@ -232,7 +232,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title, if (es->reason != NEWT_EXIT_HOTKEY) break; switch (es->u.key) { - case 'd': { /* Debug */ + case 'D': { /* Debug */ static int seq; struct hist_entry *h = rb_entry(self->b.top, struct hist_entry, rb_node); @@ -771,18 +771,17 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) default:; } - key = toupper(key); switch (key) { - case 'A': + case 'a': if (browser->selection->map == NULL && browser->selection->map->dso->annotate_warned) continue; goto do_annotate; - case 'D': + case 'd': goto zoom_dso; - case 'T': + case 't': goto zoom_thread; - case 'H': + case 'h': case '?': do_help: ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" -- cgit v1.2.3-18-g5258 From 88d89da64951377962334b684634cfc1468aa93f Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 12 Aug 2010 21:50:00 +0200 Subject: perf: Add back list_head data types This commit: de5d9bf: Move list types from to . Moved the list head data types out of list.h, breaking the build. Add them to the perf types.h as well. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Steven Rostedt LKML-Reference: Signed-off-by: Ingo Molnar --- tools/perf/util/include/linux/types.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h index 196862a81a2..12de3b8112f 100644 --- a/tools/perf/util/include/linux/types.h +++ b/tools/perf/util/include/linux/types.h @@ -6,4 +6,16 @@ #define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)] +struct list_head { + struct list_head *next, *prev; +}; + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + #endif -- cgit v1.2.3-18-g5258 From e91846213241e3c46da8cbe992bceb1697de8d78 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 16 Aug 2010 10:43:54 -0300 Subject: perf annotate tui: Fix exit and RIGHT keys handling As part of ongoing effort to reduce the coupling with libnewt, browsers are being changed to return the exit key. The annotate browser is not returning it as expected by builtin-annotate when annotating multiple symbols (when 'perf annotate' is called without specifying a symbol name). Fix it by returning the exit key and also adding the RIGHT key as a exit key so that going to the next symbol in the TUI can work again. Cc: Frederic Weisbecker Cc: Peter Zijlstra LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ui/browsers/annotate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 55ff792459a..a90273e63f4 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -146,6 +146,7 @@ static int annotate_browser__run(struct annotate_browser *self, return -1; newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); nd = self->curr_hot; if (nd) { @@ -178,7 +179,7 @@ static int annotate_browser__run(struct annotate_browser *self, } out: ui_browser__hide(&self->b); - return 0; + return es->u.key; } int hist_entry__tui_annotate(struct hist_entry *self) -- cgit v1.2.3-18-g5258 From 033a273f9836b592dd568abd0f655be469d66704 Mon Sep 17 00:00:00 2001 From: Bernd Petrovitsch Date: Tue, 17 Aug 2010 12:22:08 -0300 Subject: perf tools: Fix build on POSIX shells POSIX sh does not specify the brace expansion, so fix it by replacing the global $(shell ...) lines quite at the top creating the output directories with real rules. Cc: Ingo Molnar Cc: Kusanagi Kouichi Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1282046280.5822.4.camel@thorin> Signed-off-by: Bernd Petrovitsch Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 41abb90df50..dcb9700b88d 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -157,10 +157,6 @@ all:: # # Define NO_DWARF if you do not want debug-info analysis feature at all. -$(shell sh -c 'mkdir -p $(OUTPUT)scripts/{perl,python}/Perf-Trace-Util/' 2> /dev/null) -$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui/browsers,scripting-engines}/' 2> /dev/null) -$(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null) - $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) -include $(OUTPUT)PERF-VERSION-FILE @@ -186,8 +182,6 @@ ifeq ($(ARCH),x86_64) ARCH := x86 endif -$(shell sh -c 'mkdir -p $(OUTPUT)arch/$(ARCH)/util/' 2> /dev/null) - # CFLAGS and LDFLAGS are for the users to override from the command line. # @@ -268,6 +262,7 @@ export prefix bindir sharedir sysconfdir CC = $(CROSS_COMPILE)gcc AR = $(CROSS_COMPILE)ar RM = rm -f +MKDIR = mkdir TAR = tar FIND = find INSTALL = install @@ -838,6 +833,7 @@ ifndef V QUIET_CC = @echo ' ' CC $@; QUIET_AR = @echo ' ' AR $@; QUIET_LINK = @echo ' ' LINK $@; + QUIET_MKDIR = @echo ' ' MKDIR $@; QUIET_BUILT_IN = @echo ' ' BUILTIN $@; QUIET_GEN = @echo ' ' GEN $@; QUIET_SUBDIR0 = +@subdir= @@ -1012,6 +1008,14 @@ $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(patsubst perf-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) builtin-revert.o wt-status.o: wt-status.h +# we compile into subdirectories. if the target directory is not the source directory, they might not exists. So +# we depend the various files onto their directories. +DIRECTORY_DEPS = $(LIB_OBJS) $(BUILTIN_OBJS) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h +$(DIRECTORY_DEPS): $(sort $(dir $(DIRECTORY_DEPS))) +# In the second step, we make a rule to actually create these directories +$(sort $(dir $(DIRECTORY_DEPS))): + $(QUIET_MKDIR)$(MKDIR) -p $@ 2>/dev/null + $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) -- cgit v1.2.3-18-g5258 From ecafda60e88031bcc4271c446f984ee883d69ea8 Mon Sep 17 00:00:00 2001 From: Kusanagi Kouichi Date: Wed, 18 Aug 2010 13:32:37 -0300 Subject: perf tools: Fix build error on read only source. Parts of the build process were generating files outside the specified O= directory, causing the build to fail on systems where the sources are in a read only file system. Fix it by using $(OUTPUT) on these locations. Also check that $(OUTPUT) actually exists, just like the top level kernel Makefile does. Otherwise the failure message emitted is completely misleading. Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <20100817140841.0859362C03A@msa106.auone-net.jp> Signed-off-by: Kusanagi Kouichi Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 14 ++++++++++---- tools/perf/feature-tests.mak | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index dcb9700b88d..4f1fa77c1fe 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -5,6 +5,12 @@ endif # The default target of this Makefile is... all:: +ifneq ($(OUTPUT),) +# check that the output directory actually exists +OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd) +$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) +endif + # Define V=1 to have a more verbose compile. # Define V=2 to have an even more verbose compile. # @@ -931,15 +937,15 @@ $(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt) $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh - $(QUIET_GEN)$(RM) $@ $@+ && \ + $(QUIET_GEN)$(RM) $(OUTPUT)$@ $(OUTPUT)$@+ && \ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \ -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ - $@.sh >$@+ && \ - chmod +x $@+ && \ - mv $@+ $(OUTPUT)$@ + $@.sh > $(OUTPUT)$@+ && \ + chmod +x $(OUTPUT)$@+ && \ + mv $(OUTPUT)$@+ $(OUTPUT)$@ configure: configure.ac $(QUIET_GEN)$(RM) $@ $<+ && \ diff --git a/tools/perf/feature-tests.mak b/tools/perf/feature-tests.mak index ddb68e601f0..7a7b6085905 100644 --- a/tools/perf/feature-tests.mak +++ b/tools/perf/feature-tests.mak @@ -113,7 +113,7 @@ endef # try-cc # Usage: option = $(call try-cc, source-to-build, cc-options) try-cc = $(shell sh -c \ - 'TMP="$(TMPOUT).$$$$"; \ + 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \ echo "$(1)" | \ $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \ rm -f "$$TMP"') -- cgit v1.2.3-18-g5258