Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (163 commits) tracing: Fix compile issue for trace_sched_wakeup.c [S390] hardirq: remove pointless header file includes [IA64] Move local_softirq_pending() definition perf, powerpc: Fix power_pmu_event_init to not use event->ctx ftrace: Remove recursion between recordmcount and scripts/mod/empty jump_label: Add COND_STMT(), reducer wrappery perf: Optimize sw events perf: Use jump_labels to optimize the scheduler hooks jump_label: Add atomic_t interface jump_label: Use more consistent naming perf, hw_breakpoint: Fix crash in hw_breakpoint creation perf: Find task before event alloc perf: Fix task refcount bugs perf: Fix group moving irq_work: Add generic hardirq context callbacks perf_events: Fix transaction recovery in group_sched_in() perf_events: Fix bogus AMD64 generic TLB events perf_events: Fix bogus context time tracking tracing: Remove parent recording in latency tracer graph options tracing: Use one prologue for the preempt irqs off tracer function tracers ...
This commit is contained in:
@@ -8,7 +8,7 @@ perf-annotate - Read perf.data (created by perf record) and display annotated co
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf annotate' [-i <file> | --input=file] symbol_name
|
||||
'perf annotate' [-i <file> | --input=file] [symbol_name]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -24,6 +24,13 @@ OPTIONS
|
||||
--input=::
|
||||
Input file name. (default: perf.data)
|
||||
|
||||
--stdio:: Use the stdio interface.
|
||||
|
||||
--tui:: Use the TUI interface Use of --tui requires a tty, if one is not
|
||||
present, as when piping to other commands, the stdio interface is
|
||||
used. This interfaces starts by centering on the line with more
|
||||
samples, TAB/UNTAB cycles thru the lines with more samples.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1]
|
||||
linkperf:perf-record[1], linkperf:perf-report[1]
|
||||
|
||||
@@ -65,6 +65,13 @@ OPTIONS
|
||||
the tree is considered as a new profiled object. +
|
||||
Default: fractal,0.5.
|
||||
|
||||
--stdio:: Use the stdio interface.
|
||||
|
||||
--tui:: Use the TUI interface, that is integrated with annotate and allows
|
||||
zooming into DSOs or threads, among other features. Use of --tui
|
||||
requires a tty, if one is not present, as when piping to other
|
||||
commands, the stdio interface is used.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1]
|
||||
|
||||
+24
-6
@@ -313,6 +313,9 @@ TEST_PROGRAMS =
|
||||
|
||||
SCRIPT_SH += perf-archive.sh
|
||||
|
||||
grep-libs = $(filter -l%,$(1))
|
||||
strip-libs = $(filter-out -l%,$(1))
|
||||
|
||||
#
|
||||
# No Perl scripts right now:
|
||||
#
|
||||
@@ -588,14 +591,17 @@ endif
|
||||
ifdef NO_LIBPERL
|
||||
BASIC_CFLAGS += -DNO_LIBPERL
|
||||
else
|
||||
PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null`
|
||||
PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null)
|
||||
PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS))
|
||||
PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS))
|
||||
PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null`
|
||||
FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
|
||||
|
||||
ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y)
|
||||
BASIC_CFLAGS += -DNO_LIBPERL
|
||||
else
|
||||
ALL_LDFLAGS += $(PERL_EMBED_LDOPTS)
|
||||
ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS)
|
||||
EXTLIBS += $(PERL_EMBED_LIBADD)
|
||||
LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o
|
||||
LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o
|
||||
endif
|
||||
@@ -604,13 +610,16 @@ endif
|
||||
ifdef NO_LIBPYTHON
|
||||
BASIC_CFLAGS += -DNO_LIBPYTHON
|
||||
else
|
||||
PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null`
|
||||
PYTHON_EMBED_LDOPTS = $(shell python-config --ldflags 2>/dev/null)
|
||||
PYTHON_EMBED_LDFLAGS = $(call strip-libs,$(PYTHON_EMBED_LDOPTS))
|
||||
PYTHON_EMBED_LIBADD = $(call grep-libs,$(PYTHON_EMBED_LDOPTS))
|
||||
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)
|
||||
ALL_LDFLAGS += $(PYTHON_EMBED_LDFLAGS)
|
||||
EXTLIBS += $(PYTHON_EMBED_LIBADD)
|
||||
LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o
|
||||
LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o
|
||||
endif
|
||||
@@ -653,6 +662,15 @@ else
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
ifdef NO_STRLCPY
|
||||
BASIC_CFLAGS += -DNO_STRLCPY
|
||||
else
|
||||
ifneq ($(call try-cc,$(SOURCE_STRLCPY),),y)
|
||||
BASIC_CFLAGS += -DNO_STRLCPY
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef CC_LD_DYNPATH
|
||||
ifdef NO_R_TO_GCC_LINKER
|
||||
# Some gcc does not accept and pass -R to the linker to specify
|
||||
@@ -910,8 +928,8 @@ $(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
|
||||
$(ALL_CFLAGS) -c $(filter %.c,$^) -o $@
|
||||
|
||||
$(OUTPUT)perf$X: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(OUTPUT)perf.o \
|
||||
$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(OUTPUT)perf.o \
|
||||
$(BUILTIN_OBJS) $(LIBS) -o $@
|
||||
|
||||
$(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
static char const *input_name = "perf.data";
|
||||
|
||||
static bool force;
|
||||
static bool force, use_tui, use_stdio;
|
||||
|
||||
static bool full_paths;
|
||||
|
||||
@@ -321,7 +321,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
|
||||
|
||||
static void hists__find_annotations(struct hists *self)
|
||||
{
|
||||
struct rb_node *first = rb_first(&self->entries), *nd = first;
|
||||
struct rb_node *nd = rb_first(&self->entries), *next;
|
||||
int key = KEY_RIGHT;
|
||||
|
||||
while (nd) {
|
||||
@@ -343,20 +343,19 @@ find_next:
|
||||
|
||||
if (use_browser > 0) {
|
||||
key = hist_entry__tui_annotate(he);
|
||||
if (is_exit_key(key))
|
||||
break;
|
||||
switch (key) {
|
||||
case KEY_RIGHT:
|
||||
case '\t':
|
||||
nd = rb_next(nd);
|
||||
next = rb_next(nd);
|
||||
break;
|
||||
case KEY_LEFT:
|
||||
if (nd == first)
|
||||
continue;
|
||||
nd = rb_prev(nd);
|
||||
default:
|
||||
next = rb_prev(nd);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (next != NULL)
|
||||
nd = next;
|
||||
} else {
|
||||
hist_entry__tty_annotate(he);
|
||||
nd = rb_next(nd);
|
||||
@@ -428,6 +427,8 @@ static const struct option options[] = {
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
||||
"dump raw trace in ASCII"),
|
||||
OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
|
||||
OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
|
||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||
"file", "vmlinux pathname"),
|
||||
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
|
||||
@@ -443,6 +444,11 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
|
||||
{
|
||||
argc = parse_options(argc, argv, options, annotate_usage, 0);
|
||||
|
||||
if (use_stdio)
|
||||
use_browser = 0;
|
||||
else if (use_tui)
|
||||
use_browser = 1;
|
||||
|
||||
setup_browser();
|
||||
|
||||
symbol_conf.priv_size = sizeof(struct sym_priv);
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
static char const *input_name = "perf.data";
|
||||
|
||||
static bool force;
|
||||
static bool force, use_tui, use_stdio;
|
||||
static bool hide_unresolved;
|
||||
static bool dont_use_callchains;
|
||||
|
||||
@@ -107,7 +107,8 @@ static int perf_session__add_hist_entry(struct perf_session *self,
|
||||
goto out_free_syms;
|
||||
err = 0;
|
||||
if (symbol_conf.use_callchain) {
|
||||
err = append_chain(he->callchain, data->callchain, syms, data->period);
|
||||
err = callchain_append(he->callchain, data->callchain, syms,
|
||||
data->period);
|
||||
if (err)
|
||||
goto out_free_syms;
|
||||
}
|
||||
@@ -450,6 +451,8 @@ static const struct option options[] = {
|
||||
"Show per-thread event counters"),
|
||||
OPT_STRING(0, "pretty", &pretty_printing_style, "key",
|
||||
"pretty printing style key: normal raw"),
|
||||
OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
|
||||
OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
|
||||
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
|
||||
"sort by key(s): pid, comm, dso, symbol, parent"),
|
||||
OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
|
||||
@@ -482,8 +485,15 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
|
||||
{
|
||||
argc = parse_options(argc, argv, options, report_usage, 0);
|
||||
|
||||
if (use_stdio)
|
||||
use_browser = 0;
|
||||
else if (use_tui)
|
||||
use_browser = 1;
|
||||
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
setup_browser();
|
||||
else
|
||||
use_browser = 0;
|
||||
/*
|
||||
* Only in the newt browser we are doing integrated annotation,
|
||||
* so don't allocate extra space that won't be used in the stdio
|
||||
|
||||
@@ -110,6 +110,17 @@ int main(void)
|
||||
}
|
||||
endef
|
||||
|
||||
define SOURCE_STRLCPY
|
||||
#include <stdlib.h>
|
||||
extern size_t strlcpy(char *dest, const char *src, size_t size);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
strlcpy(NULL, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
endef
|
||||
|
||||
# try-cc
|
||||
# Usage: option = $(call try-cc, source-to-build, cc-options)
|
||||
try-cc = $(shell sh -c \
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
perf record -a -e net:net_dev_xmit -e net:net_dev_queue \
|
||||
-e net:netif_receive_skb -e net:netif_rx \
|
||||
-e skb:consume_skb -e skb:kfree_skb \
|
||||
-e skb:skb_copy_datagram_iovec -e napi:napi_poll \
|
||||
-e irq:irq_handler_entry -e irq:irq_handler_exit \
|
||||
-e irq:softirq_entry -e irq:softirq_exit \
|
||||
-e irq:softirq_raise $@
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# description: display a process of packet and processing time
|
||||
# args: [tx] [rx] [dev=] [debug]
|
||||
|
||||
perf trace -s ~/libexec/perf-core/scripts/python/netdev-times.py $@
|
||||
@@ -0,0 +1,464 @@
|
||||
# Display a process of packets and processed time.
|
||||
# It helps us to investigate networking or network device.
|
||||
#
|
||||
# options
|
||||
# tx: show only tx chart
|
||||
# rx: show only rx chart
|
||||
# dev=: show only thing related to specified device
|
||||
# debug: work with debug mode. It shows buffer status.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
|
||||
|
||||
from perf_trace_context import *
|
||||
from Core import *
|
||||
from Util import *
|
||||
|
||||
all_event_list = []; # insert all tracepoint event related with this script
|
||||
irq_dic = {}; # key is cpu and value is a list which stacks irqs
|
||||
# which raise NET_RX softirq
|
||||
net_rx_dic = {}; # key is cpu and value include time of NET_RX softirq-entry
|
||||
# and a list which stacks receive
|
||||
receive_hunk_list = []; # a list which include a sequence of receive events
|
||||
rx_skb_list = []; # received packet list for matching
|
||||
# skb_copy_datagram_iovec
|
||||
|
||||
buffer_budget = 65536; # the budget of rx_skb_list, tx_queue_list and
|
||||
# tx_xmit_list
|
||||
of_count_rx_skb_list = 0; # overflow count
|
||||
|
||||
tx_queue_list = []; # list of packets which pass through dev_queue_xmit
|
||||
of_count_tx_queue_list = 0; # overflow count
|
||||
|
||||
tx_xmit_list = []; # list of packets which pass through dev_hard_start_xmit
|
||||
of_count_tx_xmit_list = 0; # overflow count
|
||||
|
||||
tx_free_list = []; # list of packets which is freed
|
||||
|
||||
# options
|
||||
show_tx = 0;
|
||||
show_rx = 0;
|
||||
dev = 0; # store a name of device specified by option "dev="
|
||||
debug = 0;
|
||||
|
||||
# indices of event_info tuple
|
||||
EINFO_IDX_NAME= 0
|
||||
EINFO_IDX_CONTEXT=1
|
||||
EINFO_IDX_CPU= 2
|
||||
EINFO_IDX_TIME= 3
|
||||
EINFO_IDX_PID= 4
|
||||
EINFO_IDX_COMM= 5
|
||||
|
||||
# Calculate a time interval(msec) from src(nsec) to dst(nsec)
|
||||
def diff_msec(src, dst):
|
||||
return (dst - src) / 1000000.0
|
||||
|
||||
# Display a process of transmitting a packet
|
||||
def print_transmit(hunk):
|
||||
if dev != 0 and hunk['dev'].find(dev) < 0:
|
||||
return
|
||||
print "%7s %5d %6d.%06dsec %12.3fmsec %12.3fmsec" % \
|
||||
(hunk['dev'], hunk['len'],
|
||||
nsecs_secs(hunk['queue_t']),
|
||||
nsecs_nsecs(hunk['queue_t'])/1000,
|
||||
diff_msec(hunk['queue_t'], hunk['xmit_t']),
|
||||
diff_msec(hunk['xmit_t'], hunk['free_t']))
|
||||
|
||||
# Format for displaying rx packet processing
|
||||
PF_IRQ_ENTRY= " irq_entry(+%.3fmsec irq=%d:%s)"
|
||||
PF_SOFT_ENTRY=" softirq_entry(+%.3fmsec)"
|
||||
PF_NAPI_POLL= " napi_poll_exit(+%.3fmsec %s)"
|
||||
PF_JOINT= " |"
|
||||
PF_WJOINT= " | |"
|
||||
PF_NET_RECV= " |---netif_receive_skb(+%.3fmsec skb=%x len=%d)"
|
||||
PF_NET_RX= " |---netif_rx(+%.3fmsec skb=%x)"
|
||||
PF_CPY_DGRAM= " | skb_copy_datagram_iovec(+%.3fmsec %d:%s)"
|
||||
PF_KFREE_SKB= " | kfree_skb(+%.3fmsec location=%x)"
|
||||
PF_CONS_SKB= " | consume_skb(+%.3fmsec)"
|
||||
|
||||
# Display a process of received packets and interrputs associated with
|
||||
# a NET_RX softirq
|
||||
def print_receive(hunk):
|
||||
show_hunk = 0
|
||||
irq_list = hunk['irq_list']
|
||||
cpu = irq_list[0]['cpu']
|
||||
base_t = irq_list[0]['irq_ent_t']
|
||||
# check if this hunk should be showed
|
||||
if dev != 0:
|
||||
for i in range(len(irq_list)):
|
||||
if irq_list[i]['name'].find(dev) >= 0:
|
||||
show_hunk = 1
|
||||
break
|
||||
else:
|
||||
show_hunk = 1
|
||||
if show_hunk == 0:
|
||||
return
|
||||
|
||||
print "%d.%06dsec cpu=%d" % \
|
||||
(nsecs_secs(base_t), nsecs_nsecs(base_t)/1000, cpu)
|
||||
for i in range(len(irq_list)):
|
||||
print PF_IRQ_ENTRY % \
|
||||
(diff_msec(base_t, irq_list[i]['irq_ent_t']),
|
||||
irq_list[i]['irq'], irq_list[i]['name'])
|
||||
print PF_JOINT
|
||||
irq_event_list = irq_list[i]['event_list']
|
||||
for j in range(len(irq_event_list)):
|
||||
irq_event = irq_event_list[j]
|
||||
if irq_event['event'] == 'netif_rx':
|
||||
print PF_NET_RX % \
|
||||
(diff_msec(base_t, irq_event['time']),
|
||||
irq_event['skbaddr'])
|
||||
print PF_JOINT
|
||||
print PF_SOFT_ENTRY % \
|
||||
diff_msec(base_t, hunk['sirq_ent_t'])
|
||||
print PF_JOINT
|
||||
event_list = hunk['event_list']
|
||||
for i in range(len(event_list)):
|
||||
event = event_list[i]
|
||||
if event['event_name'] == 'napi_poll':
|
||||
print PF_NAPI_POLL % \
|
||||
(diff_msec(base_t, event['event_t']), event['dev'])
|
||||
if i == len(event_list) - 1:
|
||||
print ""
|
||||
else:
|
||||
print PF_JOINT
|
||||
else:
|
||||
print PF_NET_RECV % \
|
||||
(diff_msec(base_t, event['event_t']), event['skbaddr'],
|
||||
event['len'])
|
||||
if 'comm' in event.keys():
|
||||
print PF_WJOINT
|
||||
print PF_CPY_DGRAM % \
|
||||
(diff_msec(base_t, event['comm_t']),
|
||||
event['pid'], event['comm'])
|
||||
elif 'handle' in event.keys():
|
||||
print PF_WJOINT
|
||||
if event['handle'] == "kfree_skb":
|
||||
print PF_KFREE_SKB % \
|
||||
(diff_msec(base_t,
|
||||
event['comm_t']),
|
||||
event['location'])
|
||||
elif event['handle'] == "consume_skb":
|
||||
print PF_CONS_SKB % \
|
||||
diff_msec(base_t,
|
||||
event['comm_t'])
|
||||
print PF_JOINT
|
||||
|
||||
def trace_begin():
|
||||
global show_tx
|
||||
global show_rx
|
||||
global dev
|
||||
global debug
|
||||
|
||||
for i in range(len(sys.argv)):
|
||||
if i == 0:
|
||||
continue
|
||||
arg = sys.argv[i]
|
||||
if arg == 'tx':
|
||||
show_tx = 1
|
||||
elif arg =='rx':
|
||||
show_rx = 1
|
||||
elif arg.find('dev=',0, 4) >= 0:
|
||||
dev = arg[4:]
|
||||
elif arg == 'debug':
|
||||
debug = 1
|
||||
if show_tx == 0 and show_rx == 0:
|
||||
show_tx = 1
|
||||
show_rx = 1
|
||||
|
||||
def trace_end():
|
||||
# order all events in time
|
||||
all_event_list.sort(lambda a,b :cmp(a[EINFO_IDX_TIME],
|
||||
b[EINFO_IDX_TIME]))
|
||||
# process all events
|
||||
for i in range(len(all_event_list)):
|
||||
event_info = all_event_list[i]
|
||||
name = event_info[EINFO_IDX_NAME]
|
||||
if name == 'irq__softirq_exit':
|
||||
handle_irq_softirq_exit(event_info)
|
||||
elif name == 'irq__softirq_entry':
|
||||
handle_irq_softirq_entry(event_info)
|
||||
elif name == 'irq__softirq_raise':
|
||||
handle_irq_softirq_raise(event_info)
|
||||
elif name == 'irq__irq_handler_entry':
|
||||
handle_irq_handler_entry(event_info)
|
||||
elif name == 'irq__irq_handler_exit':
|
||||
handle_irq_handler_exit(event_info)
|
||||
elif name == 'napi__napi_poll':
|
||||
handle_napi_poll(event_info)
|
||||
elif name == 'net__netif_receive_skb':
|
||||
handle_netif_receive_skb(event_info)
|
||||
elif name == 'net__netif_rx':
|
||||
handle_netif_rx(event_info)
|
||||
elif name == 'skb__skb_copy_datagram_iovec':
|
||||
handle_skb_copy_datagram_iovec(event_info)
|
||||
elif name == 'net__net_dev_queue':
|
||||
handle_net_dev_queue(event_info)
|
||||
elif name == 'net__net_dev_xmit':
|
||||
handle_net_dev_xmit(event_info)
|
||||
elif name == 'skb__kfree_skb':
|
||||
handle_kfree_skb(event_info)
|
||||
elif name == 'skb__consume_skb':
|
||||
handle_consume_skb(event_info)
|
||||
# display receive hunks
|
||||
if show_rx:
|
||||
for i in range(len(receive_hunk_list)):
|
||||
print_receive(receive_hunk_list[i])
|
||||
# display transmit hunks
|
||||
if show_tx:
|
||||
print " dev len Qdisc " \
|
||||
" netdevice free"
|
||||
for i in range(len(tx_free_list)):
|
||||
print_transmit(tx_free_list[i])
|
||||
if debug:
|
||||
print "debug buffer status"
|
||||
print "----------------------------"
|
||||
print "xmit Qdisc:remain:%d overflow:%d" % \
|
||||
(len(tx_queue_list), of_count_tx_queue_list)
|
||||
print "xmit netdevice:remain:%d overflow:%d" % \
|
||||
(len(tx_xmit_list), of_count_tx_xmit_list)
|
||||
print "receive:remain:%d overflow:%d" % \
|
||||
(len(rx_skb_list), of_count_rx_skb_list)
|
||||
|
||||
# called from perf, when it finds a correspoinding event
|
||||
def irq__softirq_entry(name, context, cpu, sec, nsec, pid, comm, vec):
|
||||
if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
|
||||
return
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def irq__softirq_exit(name, context, cpu, sec, nsec, pid, comm, vec):
|
||||
if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
|
||||
return
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def irq__softirq_raise(name, context, cpu, sec, nsec, pid, comm, vec):
|
||||
if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
|
||||
return
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def irq__irq_handler_entry(name, context, cpu, sec, nsec, pid, comm,
|
||||
irq, irq_name):
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
|
||||
irq, irq_name)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def irq__irq_handler_exit(name, context, cpu, sec, nsec, pid, comm, irq, ret):
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, irq, ret)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def napi__napi_poll(name, context, cpu, sec, nsec, pid, comm, napi, dev_name):
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
|
||||
napi, dev_name)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def net__netif_receive_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr,
|
||||
skblen, dev_name):
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
|
||||
skbaddr, skblen, dev_name)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def net__netif_rx(name, context, cpu, sec, nsec, pid, comm, skbaddr,
|
||||
skblen, dev_name):
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
|
||||
skbaddr, skblen, dev_name)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def net__net_dev_queue(name, context, cpu, sec, nsec, pid, comm,
|
||||
skbaddr, skblen, dev_name):
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
|
||||
skbaddr, skblen, dev_name)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def net__net_dev_xmit(name, context, cpu, sec, nsec, pid, comm,
|
||||
skbaddr, skblen, rc, dev_name):
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
|
||||
skbaddr, skblen, rc ,dev_name)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm,
|
||||
skbaddr, protocol, location):
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
|
||||
skbaddr, protocol, location)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def skb__consume_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr):
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
|
||||
skbaddr)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def skb__skb_copy_datagram_iovec(name, context, cpu, sec, nsec, pid, comm,
|
||||
skbaddr, skblen):
|
||||
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
|
||||
skbaddr, skblen)
|
||||
all_event_list.append(event_info)
|
||||
|
||||
def handle_irq_handler_entry(event_info):
|
||||
(name, context, cpu, time, pid, comm, irq, irq_name) = event_info
|
||||
if cpu not in irq_dic.keys():
|
||||
irq_dic[cpu] = []
|
||||
irq_record = {'irq':irq, 'name':irq_name, 'cpu':cpu, 'irq_ent_t':time}
|
||||
irq_dic[cpu].append(irq_record)
|
||||
|
||||
def handle_irq_handler_exit(event_info):
|
||||
(name, context, cpu, time, pid, comm, irq, ret) = event_info
|
||||
if cpu not in irq_dic.keys():
|
||||
return
|
||||
irq_record = irq_dic[cpu].pop()
|
||||
if irq != irq_record['irq']:
|
||||
return
|
||||
irq_record.update({'irq_ext_t':time})
|
||||
# if an irq doesn't include NET_RX softirq, drop.
|
||||
if 'event_list' in irq_record.keys():
|
||||
irq_dic[cpu].append(irq_record)
|
||||
|
||||
def handle_irq_softirq_raise(event_info):
|
||||
(name, context, cpu, time, pid, comm, vec) = event_info
|
||||
if cpu not in irq_dic.keys() \
|
||||
or len(irq_dic[cpu]) == 0:
|
||||
return
|
||||
irq_record = irq_dic[cpu].pop()
|
||||
if 'event_list' in irq_record.keys():
|
||||
irq_event_list = irq_record['event_list']
|
||||
else:
|
||||
irq_event_list = []
|
||||
irq_event_list.append({'time':time, 'event':'sirq_raise'})
|
||||
irq_record.update({'event_list':irq_event_list})
|
||||
irq_dic[cpu].append(irq_record)
|
||||
|
||||
def handle_irq_softirq_entry(event_info):
|
||||
(name, context, cpu, time, pid, comm, vec) = event_info
|
||||
net_rx_dic[cpu] = {'sirq_ent_t':time, 'event_list':[]}
|
||||
|
||||
def handle_irq_softirq_exit(event_info):
|
||||
(name, context, cpu, time, pid, comm, vec) = event_info
|
||||
irq_list = []
|
||||
event_list = 0
|
||||
if cpu in irq_dic.keys():
|
||||
irq_list = irq_dic[cpu]
|
||||
del irq_dic[cpu]
|
||||
if cpu in net_rx_dic.keys():
|
||||
sirq_ent_t = net_rx_dic[cpu]['sirq_ent_t']
|
||||
event_list = net_rx_dic[cpu]['event_list']
|
||||
del net_rx_dic[cpu]
|
||||
if irq_list == [] or event_list == 0:
|
||||
return
|
||||
rec_data = {'sirq_ent_t':sirq_ent_t, 'sirq_ext_t':time,
|
||||
'irq_list':irq_list, 'event_list':event_list}
|
||||
# merge information realted to a NET_RX softirq
|
||||
receive_hunk_list.append(rec_data)
|
||||
|
||||
def handle_napi_poll(event_info):
|
||||
(name, context, cpu, time, pid, comm, napi, dev_name) = event_info
|
||||
if cpu in net_rx_dic.keys():
|
||||
event_list = net_rx_dic[cpu]['event_list']
|
||||
rec_data = {'event_name':'napi_poll',
|
||||
'dev':dev_name, 'event_t':time}
|
||||
event_list.append(rec_data)
|
||||
|
||||
def handle_netif_rx(event_info):
|
||||
(name, context, cpu, time, pid, comm,
|
||||
skbaddr, skblen, dev_name) = event_info
|
||||
if cpu not in irq_dic.keys() \
|
||||
or len(irq_dic[cpu]) == 0:
|
||||
return
|
||||
irq_record = irq_dic[cpu].pop()
|
||||
if 'event_list' in irq_record.keys():
|
||||
irq_event_list = irq_record['event_list']
|
||||
else:
|
||||
irq_event_list = []
|
||||
irq_event_list.append({'time':time, 'event':'netif_rx',
|
||||
'skbaddr':skbaddr, 'skblen':skblen, 'dev_name':dev_name})
|
||||
irq_record.update({'event_list':irq_event_list})
|
||||
irq_dic[cpu].append(irq_record)
|
||||
|
||||
def handle_netif_receive_skb(event_info):
|
||||
global of_count_rx_skb_list
|
||||
|
||||
(name, context, cpu, time, pid, comm,
|
||||
skbaddr, skblen, dev_name) = event_info
|
||||
if cpu in net_rx_dic.keys():
|
||||
rec_data = {'event_name':'netif_receive_skb',
|
||||
'event_t':time, 'skbaddr':skbaddr, 'len':skblen}
|
||||
event_list = net_rx_dic[cpu]['event_list']
|
||||
event_list.append(rec_data)
|
||||
rx_skb_list.insert(0, rec_data)
|
||||
if len(rx_skb_list) > buffer_budget:
|
||||
rx_skb_list.pop()
|
||||
of_count_rx_skb_list += 1
|
||||
|
||||
def handle_net_dev_queue(event_info):
|
||||
global of_count_tx_queue_list
|
||||
|
||||
(name, context, cpu, time, pid, comm,
|
||||
skbaddr, skblen, dev_name) = event_info
|
||||
skb = {'dev':dev_name, 'skbaddr':skbaddr, 'len':skblen, 'queue_t':time}
|
||||
tx_queue_list.insert(0, skb)
|
||||
if len(tx_queue_list) > buffer_budget:
|
||||
tx_queue_list.pop()
|
||||
of_count_tx_queue_list += 1
|
||||
|
||||
def handle_net_dev_xmit(event_info):
|
||||
global of_count_tx_xmit_list
|
||||
|
||||
(name, context, cpu, time, pid, comm,
|
||||
skbaddr, skblen, rc, dev_name) = event_info
|
||||
if rc == 0: # NETDEV_TX_OK
|
||||
for i in range(len(tx_queue_list)):
|
||||
skb = tx_queue_list[i]
|
||||
if skb['skbaddr'] == skbaddr:
|
||||
skb['xmit_t'] = time
|
||||
tx_xmit_list.insert(0, skb)
|
||||
del tx_queue_list[i]
|
||||
if len(tx_xmit_list) > buffer_budget:
|
||||
tx_xmit_list.pop()
|
||||
of_count_tx_xmit_list += 1
|
||||
return
|
||||
|
||||
def handle_kfree_skb(event_info):
|
||||
(name, context, cpu, time, pid, comm,
|
||||
skbaddr, protocol, location) = event_info
|
||||
for i in range(len(tx_queue_list)):
|
||||
skb = tx_queue_list[i]
|
||||
if skb['skbaddr'] == skbaddr:
|
||||
del tx_queue_list[i]
|
||||
return
|
||||
for i in range(len(tx_xmit_list)):
|
||||
skb = tx_xmit_list[i]
|
||||
if skb['skbaddr'] == skbaddr:
|
||||
skb['free_t'] = time
|
||||
tx_free_list.append(skb)
|
||||
del tx_xmit_list[i]
|
||||
return
|
||||
for i in range(len(rx_skb_list)):
|
||||
rec_data = rx_skb_list[i]
|
||||
if rec_data['skbaddr'] == skbaddr:
|
||||
rec_data.update({'handle':"kfree_skb",
|
||||
'comm':comm, 'pid':pid, 'comm_t':time})
|
||||
del rx_skb_list[i]
|
||||
return
|
||||
|
||||
def handle_consume_skb(event_info):
|
||||
(name, context, cpu, time, pid, comm, skbaddr) = event_info
|
||||
for i in range(len(tx_xmit_list)):
|
||||
skb = tx_xmit_list[i]
|
||||
if skb['skbaddr'] == skbaddr:
|
||||
skb['free_t'] = time
|
||||
tx_free_list.append(skb)
|
||||
del tx_xmit_list[i]
|
||||
return
|
||||
|
||||
def handle_skb_copy_datagram_iovec(event_info):
|
||||
(name, context, cpu, time, pid, comm, skbaddr, skblen) = event_info
|
||||
for i in range(len(rx_skb_list)):
|
||||
rec_data = rx_skb_list[i]
|
||||
if skbaddr == rec_data['skbaddr']:
|
||||
rec_data.update({'handle':"skb_copy_datagram_iovec",
|
||||
'comm':comm, 'pid':pid, 'comm_t':time})
|
||||
del rx_skb_list[i]
|
||||
return
|
||||
@@ -82,6 +82,8 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2
|
||||
extern char *perf_pathdup(const char *fmt, ...)
|
||||
__attribute__((format (printf, 1, 2)));
|
||||
|
||||
#ifdef NO_STRLCPY
|
||||
extern size_t strlcpy(char *dest, const char *src, size_t size);
|
||||
#endif
|
||||
|
||||
#endif /* __PERF_CACHE_H */
|
||||
|
||||
+78
-20
@@ -28,6 +28,9 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
|
||||
#define chain_for_each_child(child, parent) \
|
||||
list_for_each_entry(child, &parent->children, brothers)
|
||||
|
||||
#define chain_for_each_child_safe(child, next, parent) \
|
||||
list_for_each_entry_safe(child, next, &parent->children, brothers)
|
||||
|
||||
static void
|
||||
rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
|
||||
enum chain_mode mode)
|
||||
@@ -86,10 +89,10 @@ __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
|
||||
* sort them by hit
|
||||
*/
|
||||
static void
|
||||
sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
|
||||
sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
|
||||
u64 min_hit, struct callchain_param *param __used)
|
||||
{
|
||||
__sort_chain_flat(rb_root, node, min_hit);
|
||||
__sort_chain_flat(rb_root, &root->node, min_hit);
|
||||
}
|
||||
|
||||
static void __sort_chain_graph_abs(struct callchain_node *node,
|
||||
@@ -108,11 +111,11 @@ static void __sort_chain_graph_abs(struct callchain_node *node,
|
||||
}
|
||||
|
||||
static void
|
||||
sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_node *chain_root,
|
||||
sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,
|
||||
u64 min_hit, struct callchain_param *param __used)
|
||||
{
|
||||
__sort_chain_graph_abs(chain_root, min_hit);
|
||||
rb_root->rb_node = chain_root->rb_root.rb_node;
|
||||
__sort_chain_graph_abs(&chain_root->node, min_hit);
|
||||
rb_root->rb_node = chain_root->node.rb_root.rb_node;
|
||||
}
|
||||
|
||||
static void __sort_chain_graph_rel(struct callchain_node *node,
|
||||
@@ -133,11 +136,11 @@ static void __sort_chain_graph_rel(struct callchain_node *node,
|
||||
}
|
||||
|
||||
static void
|
||||
sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root,
|
||||
sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root,
|
||||
u64 min_hit __used, struct callchain_param *param)
|
||||
{
|
||||
__sort_chain_graph_rel(chain_root, param->min_percent / 100.0);
|
||||
rb_root->rb_node = chain_root->rb_root.rb_node;
|
||||
__sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0);
|
||||
rb_root->rb_node = chain_root->node.rb_root.rb_node;
|
||||
}
|
||||
|
||||
int register_callchain_param(struct callchain_param *param)
|
||||
@@ -284,19 +287,18 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
|
||||
}
|
||||
|
||||
static int
|
||||
__append_chain(struct callchain_node *root, struct resolved_chain *chain,
|
||||
unsigned int start, u64 period);
|
||||
append_chain(struct callchain_node *root, struct resolved_chain *chain,
|
||||
unsigned int start, u64 period);
|
||||
|
||||
static void
|
||||
__append_chain_children(struct callchain_node *root,
|
||||
struct resolved_chain *chain,
|
||||
unsigned int start, u64 period)
|
||||
append_chain_children(struct callchain_node *root, struct resolved_chain *chain,
|
||||
unsigned int start, u64 period)
|
||||
{
|
||||
struct callchain_node *rnode;
|
||||
|
||||
/* lookup in childrens */
|
||||
chain_for_each_child(rnode, root) {
|
||||
unsigned int ret = __append_chain(rnode, chain, start, period);
|
||||
unsigned int ret = append_chain(rnode, chain, start, period);
|
||||
|
||||
if (!ret)
|
||||
goto inc_children_hit;
|
||||
@@ -309,8 +311,8 @@ inc_children_hit:
|
||||
}
|
||||
|
||||
static int
|
||||
__append_chain(struct callchain_node *root, struct resolved_chain *chain,
|
||||
unsigned int start, u64 period)
|
||||
append_chain(struct callchain_node *root, struct resolved_chain *chain,
|
||||
unsigned int start, u64 period)
|
||||
{
|
||||
struct callchain_list *cnode;
|
||||
unsigned int i = start;
|
||||
@@ -357,7 +359,7 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain,
|
||||
}
|
||||
|
||||
/* We match the node and still have a part remaining */
|
||||
__append_chain_children(root, chain, i, period);
|
||||
append_chain_children(root, chain, i, period);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -380,8 +382,8 @@ static void filter_context(struct ip_callchain *old, struct resolved_chain *new,
|
||||
}
|
||||
|
||||
|
||||
int append_chain(struct callchain_node *root, struct ip_callchain *chain,
|
||||
struct map_symbol *syms, u64 period)
|
||||
int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
|
||||
struct map_symbol *syms, u64 period)
|
||||
{
|
||||
struct resolved_chain *filtered;
|
||||
|
||||
@@ -398,9 +400,65 @@ int append_chain(struct callchain_node *root, struct ip_callchain *chain,
|
||||
if (!filtered->nr)
|
||||
goto end;
|
||||
|
||||
__append_chain_children(root, filtered, 0, period);
|
||||
append_chain_children(&root->node, filtered, 0, period);
|
||||
|
||||
if (filtered->nr > root->max_depth)
|
||||
root->max_depth = filtered->nr;
|
||||
end:
|
||||
free(filtered);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
merge_chain_branch(struct callchain_node *dst, struct callchain_node *src,
|
||||
struct resolved_chain *chain)
|
||||
{
|
||||
struct callchain_node *child, *next_child;
|
||||
struct callchain_list *list, *next_list;
|
||||
int old_pos = chain->nr;
|
||||
int err = 0;
|
||||
|
||||
list_for_each_entry_safe(list, next_list, &src->val, list) {
|
||||
chain->ips[chain->nr].ip = list->ip;
|
||||
chain->ips[chain->nr].ms = list->ms;
|
||||
chain->nr++;
|
||||
list_del(&list->list);
|
||||
free(list);
|
||||
}
|
||||
|
||||
if (src->hit)
|
||||
append_chain_children(dst, chain, 0, src->hit);
|
||||
|
||||
chain_for_each_child_safe(child, next_child, src) {
|
||||
err = merge_chain_branch(dst, child, chain);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
list_del(&child->brothers);
|
||||
free(child);
|
||||
}
|
||||
|
||||
chain->nr = old_pos;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int callchain_merge(struct callchain_root *dst, struct callchain_root *src)
|
||||
{
|
||||
struct resolved_chain *chain;
|
||||
int err;
|
||||
|
||||
chain = malloc(sizeof(*chain) +
|
||||
src->max_depth * sizeof(struct resolved_ip));
|
||||
if (!chain)
|
||||
return -ENOMEM;
|
||||
|
||||
chain->nr = 0;
|
||||
|
||||
err = merge_chain_branch(&dst->node, &src->node, chain);
|
||||
|
||||
free(chain);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
+17
-10
@@ -26,9 +26,14 @@ struct callchain_node {
|
||||
u64 children_hit;
|
||||
};
|
||||
|
||||
struct callchain_root {
|
||||
u64 max_depth;
|
||||
struct callchain_node node;
|
||||
};
|
||||
|
||||
struct callchain_param;
|
||||
|
||||
typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *,
|
||||
typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *,
|
||||
u64, struct callchain_param *);
|
||||
|
||||
struct callchain_param {
|
||||
@@ -44,15 +49,16 @@ struct callchain_list {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static inline void callchain_init(struct callchain_node *node)
|
||||
static inline void callchain_init(struct callchain_root *root)
|
||||
{
|
||||
INIT_LIST_HEAD(&node->brothers);
|
||||
INIT_LIST_HEAD(&node->children);
|
||||
INIT_LIST_HEAD(&node->val);
|
||||
INIT_LIST_HEAD(&root->node.brothers);
|
||||
INIT_LIST_HEAD(&root->node.children);
|
||||
INIT_LIST_HEAD(&root->node.val);
|
||||
|
||||
node->children_hit = 0;
|
||||
node->parent = NULL;
|
||||
node->hit = 0;
|
||||
root->node.parent = NULL;
|
||||
root->node.hit = 0;
|
||||
root->node.children_hit = 0;
|
||||
root->max_depth = 0;
|
||||
}
|
||||
|
||||
static inline u64 cumul_hits(struct callchain_node *node)
|
||||
@@ -61,8 +67,9 @@ static inline u64 cumul_hits(struct callchain_node *node)
|
||||
}
|
||||
|
||||
int register_callchain_param(struct callchain_param *param);
|
||||
int append_chain(struct callchain_node *root, struct ip_callchain *chain,
|
||||
struct map_symbol *syms, u64 period);
|
||||
int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
|
||||
struct map_symbol *syms, u64 period);
|
||||
int callchain_merge(struct callchain_root *dst, struct callchain_root *src);
|
||||
|
||||
bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
|
||||
#endif /* __PERF_CALLCHAIN_H */
|
||||
|
||||
@@ -87,7 +87,7 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self,
|
||||
|
||||
static struct hist_entry *hist_entry__new(struct hist_entry *template)
|
||||
{
|
||||
size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0;
|
||||
size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
|
||||
struct hist_entry *self = malloc(sizeof(*self) + callchain_size);
|
||||
|
||||
if (self != NULL) {
|
||||
@@ -226,6 +226,8 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
|
||||
|
||||
if (!cmp) {
|
||||
iter->period += he->period;
|
||||
if (symbol_conf.use_callchain)
|
||||
callchain_merge(iter->callchain, he->callchain);
|
||||
hist_entry__free(he);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ static const char *get_perf_dir(void)
|
||||
return ".";
|
||||
}
|
||||
|
||||
#ifdef NO_STRLCPY
|
||||
size_t strlcpy(char *dest, const char *src, size_t size)
|
||||
{
|
||||
size_t ret = strlen(src);
|
||||
@@ -33,7 +34,7 @@ size_t strlcpy(char *dest, const char *src, size_t size)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static char *get_pathname(void)
|
||||
{
|
||||
|
||||
@@ -70,7 +70,7 @@ struct hist_entry {
|
||||
struct hist_entry *pair;
|
||||
struct rb_root sorted_chain;
|
||||
};
|
||||
struct callchain_node callchain[0];
|
||||
struct callchain_root callchain[0];
|
||||
};
|
||||
|
||||
enum sort_type {
|
||||
|
||||
@@ -388,6 +388,20 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp)
|
||||
return fprintf(fp, "%s", sbuild_id);
|
||||
}
|
||||
|
||||
size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp)
|
||||
{
|
||||
size_t ret = 0;
|
||||
struct rb_node *nd;
|
||||
struct symbol_name_rb_node *pos;
|
||||
|
||||
for (nd = rb_first(&self->symbol_names[type]); nd; nd = rb_next(nd)) {
|
||||
pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
|
||||
fprintf(fp, "%s\n", pos->sym.name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
|
||||
@@ -182,6 +182,7 @@ size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
|
||||
size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
|
||||
|
||||
size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
|
||||
size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp);
|
||||
size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
|
||||
|
||||
enum dso_origin {
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#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 <features.h>
|
||||
#ifndef HAVE_LONG_LONG
|
||||
#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
|
||||
#endif
|
||||
#include <slang.h>
|
||||
#include "libslang.h"
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <stdlib.h>
|
||||
@@ -19,17 +9,9 @@
|
||||
#include "helpline.h"
|
||||
#include "../color.h"
|
||||
#include "../util.h"
|
||||
#include <stdio.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)
|
||||
static int ui_browser__percent_color(double percent, bool current)
|
||||
{
|
||||
if (current)
|
||||
return HE_COLORSET_SELECTED;
|
||||
@@ -40,6 +22,23 @@ int ui_browser__percent_color(double percent, bool current)
|
||||
return HE_COLORSET_NORMAL;
|
||||
}
|
||||
|
||||
void ui_browser__set_color(struct ui_browser *self __used, int color)
|
||||
{
|
||||
SLsmg_set_color(color);
|
||||
}
|
||||
|
||||
void ui_browser__set_percent_color(struct ui_browser *self,
|
||||
double percent, bool current)
|
||||
{
|
||||
int color = ui_browser__percent_color(percent, current);
|
||||
ui_browser__set_color(self, color);
|
||||
}
|
||||
|
||||
void ui_browser__gotorc(struct ui_browser *self, int y, int x)
|
||||
{
|
||||
SLsmg_gotorc(self->y + y, self->x + x);
|
||||
}
|
||||
|
||||
void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
|
||||
{
|
||||
struct list_head *head = self->entries;
|
||||
@@ -111,7 +110,7 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
|
||||
nd = self->top;
|
||||
|
||||
while (nd != NULL) {
|
||||
SLsmg_gotorc(self->y + row, self->x);
|
||||
ui_browser__gotorc(self, row, 0);
|
||||
self->write(self, nd, row);
|
||||
if (++row == self->height)
|
||||
break;
|
||||
@@ -131,13 +130,10 @@ 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->y = (rows - self->height) / 2;
|
||||
self->x = (cols - self->width) / 2;
|
||||
self->width = cols - 1;
|
||||
self->height = rows - 2;
|
||||
self->y = 1;
|
||||
self->x = 0;
|
||||
}
|
||||
|
||||
void ui_browser__reset_index(struct ui_browser *self)
|
||||
@@ -146,34 +142,48 @@ void ui_browser__reset_index(struct ui_browser *self)
|
||||
self->seek(self, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
void ui_browser__add_exit_key(struct ui_browser *self, int key)
|
||||
{
|
||||
newtFormAddHotKey(self->form, key);
|
||||
}
|
||||
|
||||
void ui_browser__add_exit_keys(struct ui_browser *self, int keys[])
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (keys[i] && i < 64) {
|
||||
ui_browser__add_exit_key(self, keys[i]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
int ui_browser__show(struct ui_browser *self, const char *title,
|
||||
const char *helpline, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int keys[] = { NEWT_KEY_UP, NEWT_KEY_DOWN, NEWT_KEY_PGUP,
|
||||
NEWT_KEY_PGDN, NEWT_KEY_HOME, NEWT_KEY_END, ' ',
|
||||
NEWT_KEY_LEFT, NEWT_KEY_ESCAPE, 'q', CTRL('c'), 0 };
|
||||
|
||||
if (self->form != NULL) {
|
||||
if (self->form != NULL)
|
||||
newtFormDestroy(self->form);
|
||||
newtPopWindow();
|
||||
}
|
||||
|
||||
ui_browser__refresh_dimensions(self);
|
||||
newtCenteredWindow(self->width, self->height, title);
|
||||
self->form = newt_form__new();
|
||||
self->form = newtForm(NULL, NULL, 0);
|
||||
if (self->form == NULL)
|
||||
return -1;
|
||||
|
||||
self->sb = newtVerticalScrollbar(self->width, 0, self->height,
|
||||
self->sb = newtVerticalScrollbar(self->width, 1, 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);
|
||||
newtFormAddHotKey(self->form, ' ');
|
||||
SLsmg_gotorc(0, 0);
|
||||
ui_browser__set_color(self, NEWT_COLORSET_ROOT);
|
||||
slsmg_write_nstring(title, self->width);
|
||||
|
||||
ui_browser__add_exit_keys(self, keys);
|
||||
newtFormAddComponent(self->form, self->sb);
|
||||
|
||||
va_start(ap, helpline);
|
||||
@@ -185,7 +195,6 @@ int ui_browser__show(struct ui_browser *self, const char *title,
|
||||
void ui_browser__hide(struct ui_browser *self)
|
||||
{
|
||||
newtFormDestroy(self->form);
|
||||
newtPopWindow();
|
||||
self->form = NULL;
|
||||
ui_helpline__pop();
|
||||
}
|
||||
@@ -196,28 +205,28 @@ 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);
|
||||
ui_browser__set_color(self, HE_COLORSET_NORMAL);
|
||||
SLsmg_fill_region(self->y + row, self->x,
|
||||
self->height - row, self->width, ' ');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
|
||||
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);
|
||||
newtFormRun(self->form, &es);
|
||||
|
||||
if (es->reason != NEWT_EXIT_HOTKEY)
|
||||
if (es.reason != NEWT_EXIT_HOTKEY)
|
||||
break;
|
||||
if (is_exit_key(es->u.key))
|
||||
return es->u.key;
|
||||
switch (es->u.key) {
|
||||
switch (es.u.key) {
|
||||
case NEWT_KEY_DOWN:
|
||||
if (self->index == self->nr_entries - 1)
|
||||
break;
|
||||
@@ -274,12 +283,12 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
|
||||
self->seek(self, -offset, SEEK_END);
|
||||
break;
|
||||
default:
|
||||
return es->u.key;
|
||||
return es.u.key;
|
||||
}
|
||||
if (ui_browser__refresh(self) < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
|
||||
@@ -294,7 +303,7 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
|
||||
pos = self->top;
|
||||
|
||||
list_for_each_from(pos, head) {
|
||||
SLsmg_gotorc(self->y + row, self->x);
|
||||
ui_browser__gotorc(self, row, 0);
|
||||
self->write(self, pos, row);
|
||||
if (++row == self->height)
|
||||
break;
|
||||
|
||||
@@ -25,16 +25,21 @@ struct ui_browser {
|
||||
};
|
||||
|
||||
|
||||
int ui_browser__percent_color(double percent, bool current);
|
||||
void ui_browser__set_color(struct ui_browser *self, int color);
|
||||
void ui_browser__set_percent_color(struct ui_browser *self,
|
||||
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);
|
||||
|
||||
void ui_browser__gotorc(struct ui_browser *self, int y, int x);
|
||||
void ui_browser__add_exit_key(struct ui_browser *self, int key);
|
||||
void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]);
|
||||
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);
|
||||
int ui_browser__run(struct ui_browser *self);
|
||||
|
||||
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);
|
||||
|
||||
@@ -40,14 +40,12 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
|
||||
|
||||
if (ol->offset != -1) {
|
||||
struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
|
||||
int color = ui_browser__percent_color(olrb->percent, current_entry);
|
||||
SLsmg_set_color(color);
|
||||
ui_browser__set_percent_color(self, olrb->percent, current_entry);
|
||||
slsmg_printf(" %7.2f ", olrb->percent);
|
||||
if (!current_entry)
|
||||
SLsmg_set_color(HE_COLORSET_CODE);
|
||||
ui_browser__set_color(self, HE_COLORSET_CODE);
|
||||
} else {
|
||||
int color = ui_browser__percent_color(0, current_entry);
|
||||
SLsmg_set_color(color);
|
||||
ui_browser__set_percent_color(self, 0, current_entry);
|
||||
slsmg_write_nstring(" ", 9);
|
||||
}
|
||||
|
||||
@@ -135,32 +133,31 @@ static void annotate_browser__set_top(struct annotate_browser *self,
|
||||
self->curr_hot = nd;
|
||||
}
|
||||
|
||||
static int annotate_browser__run(struct annotate_browser *self,
|
||||
struct newtExitStruct *es)
|
||||
static int annotate_browser__run(struct annotate_browser *self)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
struct hist_entry *he = self->b.priv;
|
||||
int key;
|
||||
|
||||
if (ui_browser__show(&self->b, he->ms.sym->name,
|
||||
"<- or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
|
||||
"<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
|
||||
return -1;
|
||||
|
||||
newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
|
||||
newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
|
||||
/*
|
||||
* To allow builtin-annotate to cycle thru multiple symbols by
|
||||
* examining the exit key for this function.
|
||||
*/
|
||||
ui_browser__add_exit_key(&self->b, NEWT_KEY_RIGHT);
|
||||
|
||||
nd = self->curr_hot;
|
||||
if (nd) {
|
||||
newtFormAddHotKey(self->b.form, NEWT_KEY_TAB);
|
||||
newtFormAddHotKey(self->b.form, NEWT_KEY_UNTAB);
|
||||
int tabs[] = { NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0 };
|
||||
ui_browser__add_exit_keys(&self->b, tabs);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
ui_browser__run(&self->b, es);
|
||||
key = ui_browser__run(&self->b);
|
||||
|
||||
if (es->reason != NEWT_EXIT_HOTKEY)
|
||||
break;
|
||||
|
||||
switch (es->u.key) {
|
||||
switch (key) {
|
||||
case NEWT_KEY_TAB:
|
||||
nd = rb_prev(nd);
|
||||
if (nd == NULL)
|
||||
@@ -179,12 +176,11 @@ static int annotate_browser__run(struct annotate_browser *self,
|
||||
}
|
||||
out:
|
||||
ui_browser__hide(&self->b);
|
||||
return es->u.key;
|
||||
return key;
|
||||
}
|
||||
|
||||
int hist_entry__tui_annotate(struct hist_entry *self)
|
||||
{
|
||||
struct newtExitStruct es;
|
||||
struct objdump_line *pos, *n;
|
||||
struct objdump_line_rb_node *rbpos;
|
||||
LIST_HEAD(head);
|
||||
@@ -232,7 +228,7 @@ int hist_entry__tui_annotate(struct hist_entry *self)
|
||||
annotate_browser__set_top(&browser, browser.curr_hot);
|
||||
|
||||
browser.b.width += 18; /* Percentage */
|
||||
ret = annotate_browser__run(&browser, &es);
|
||||
ret = annotate_browser__run(&browser);
|
||||
list_for_each_entry_safe(pos, n, &head, node) {
|
||||
list_del(&pos->node);
|
||||
objdump_line__free(pos);
|
||||
|
||||
+196
-131
@@ -58,6 +58,11 @@ static char callchain_list__folded(const struct callchain_list *self)
|
||||
return map_symbol__folded(&self->ms);
|
||||
}
|
||||
|
||||
static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
|
||||
{
|
||||
self->unfolded = unfold ? self->has_children : false;
|
||||
}
|
||||
|
||||
static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
|
||||
{
|
||||
int n = 0;
|
||||
@@ -129,16 +134,16 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *se
|
||||
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;
|
||||
bool 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;
|
||||
!RB_EMPTY_ROOT(&child->rb_root);
|
||||
} else
|
||||
chain->ms.has_children = chain->list.next == &child->val &&
|
||||
rb_first(&child->rb_root) != NULL;
|
||||
!RB_EMPTY_ROOT(&child->rb_root);
|
||||
}
|
||||
|
||||
callchain_node__init_have_children_rb_tree(child);
|
||||
@@ -150,7 +155,7 @@ 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;
|
||||
chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
|
||||
|
||||
callchain_node__init_have_children_rb_tree(self);
|
||||
}
|
||||
@@ -168,6 +173,7 @@ static void callchain__init_have_children(struct rb_root *self)
|
||||
static void hist_entry__init_have_children(struct hist_entry *self)
|
||||
{
|
||||
if (!self->init_have_children) {
|
||||
self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
|
||||
callchain__init_have_children(&self->sorted_chain);
|
||||
self->init_have_children = true;
|
||||
}
|
||||
@@ -195,43 +201,114 @@ static bool hist_browser__toggle_fold(struct hist_browser *self)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int hist_browser__run(struct hist_browser *self, const char *title,
|
||||
struct newtExitStruct *es)
|
||||
static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
|
||||
{
|
||||
char str[256], unit;
|
||||
unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
|
||||
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;
|
||||
bool has_children = false;
|
||||
|
||||
list_for_each_entry(chain, &child->val, list) {
|
||||
++n;
|
||||
map_symbol__set_folding(&chain->ms, unfold);
|
||||
has_children = chain->ms.has_children;
|
||||
}
|
||||
|
||||
if (has_children)
|
||||
n += callchain_node__set_folding_rb_tree(child, unfold);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
|
||||
{
|
||||
struct callchain_list *chain;
|
||||
bool has_children = false;
|
||||
int n = 0;
|
||||
|
||||
list_for_each_entry(chain, &node->val, list) {
|
||||
++n;
|
||||
map_symbol__set_folding(&chain->ms, unfold);
|
||||
has_children = chain->ms.has_children;
|
||||
}
|
||||
|
||||
if (has_children)
|
||||
n += callchain_node__set_folding_rb_tree(node, unfold);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int callchain__set_folding(struct rb_root *chain, bool unfold)
|
||||
{
|
||||
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__set_folding(node, unfold);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
|
||||
{
|
||||
hist_entry__init_have_children(self);
|
||||
map_symbol__set_folding(&self->ms, unfold);
|
||||
|
||||
if (self->ms.has_children) {
|
||||
int n = callchain__set_folding(&self->sorted_chain, unfold);
|
||||
self->nr_rows = unfold ? n : 0;
|
||||
} else
|
||||
self->nr_rows = 0;
|
||||
}
|
||||
|
||||
static void hists__set_folding(struct hists *self, bool unfold)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
|
||||
self->nr_entries = 0;
|
||||
|
||||
for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
|
||||
struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
|
||||
hist_entry__set_folding(he, unfold);
|
||||
self->nr_entries += 1 + he->nr_rows;
|
||||
}
|
||||
}
|
||||
|
||||
static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
|
||||
{
|
||||
hists__set_folding(self->hists, unfold);
|
||||
self->b.nr_entries = self->hists->nr_entries;
|
||||
/* Go to the start, we may be way after valid entries after a collapse */
|
||||
ui_browser__reset_index(&self->b);
|
||||
}
|
||||
|
||||
static int hist_browser__run(struct hist_browser *self, const char *title)
|
||||
{
|
||||
int key;
|
||||
int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't',
|
||||
NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, 0, };
|
||||
|
||||
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,
|
||||
"Press '?' for help on key bindings") < 0)
|
||||
return -1;
|
||||
|
||||
newtFormAddHotKey(self->b.form, 'a');
|
||||
newtFormAddHotKey(self->b.form, '?');
|
||||
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);
|
||||
newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
|
||||
ui_browser__add_exit_keys(&self->b, exit_keys);
|
||||
|
||||
while (1) {
|
||||
ui_browser__run(&self->b, es);
|
||||
key = ui_browser__run(&self->b);
|
||||
|
||||
if (es->reason != NEWT_EXIT_HOTKEY)
|
||||
break;
|
||||
switch (es->u.key) {
|
||||
switch (key) {
|
||||
case 'D': { /* Debug */
|
||||
static int seq;
|
||||
struct hist_entry *h = rb_entry(self->b.top,
|
||||
@@ -245,18 +322,26 @@ static int hist_browser__run(struct hist_browser *self, const char *title,
|
||||
self->b.top_idx,
|
||||
h->row_offset, h->nr_rows);
|
||||
}
|
||||
continue;
|
||||
break;
|
||||
case 'C':
|
||||
/* Collapse the whole world. */
|
||||
hist_browser__set_folding(self, false);
|
||||
break;
|
||||
case 'E':
|
||||
/* Expand the whole world. */
|
||||
hist_browser__set_folding(self, true);
|
||||
break;
|
||||
case NEWT_KEY_ENTER:
|
||||
if (hist_browser__toggle_fold(self))
|
||||
break;
|
||||
/* fall thru */
|
||||
default:
|
||||
return 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
ui_browser__hide(&self->b);
|
||||
return 0;
|
||||
return key;
|
||||
}
|
||||
|
||||
static char *callchain_list__sym_name(struct callchain_list *self,
|
||||
@@ -306,15 +391,10 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
|
||||
int color;
|
||||
bool was_first = first;
|
||||
|
||||
if (first) {
|
||||
if (first)
|
||||
first = false;
|
||||
chain->ms.has_children = chain->list.next != &child->val ||
|
||||
rb_first(&child->rb_root) != NULL;
|
||||
} else {
|
||||
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) {
|
||||
@@ -341,8 +421,8 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
|
||||
*is_current_entry = true;
|
||||
}
|
||||
|
||||
SLsmg_set_color(color);
|
||||
SLsmg_gotorc(self->b.y + row, self->b.x);
|
||||
ui_browser__set_color(&self->b, color);
|
||||
ui_browser__gotorc(&self->b, row, 0);
|
||||
slsmg_write_nstring(" ", offset + extra_offset);
|
||||
slsmg_printf("%c ", folded_sign);
|
||||
slsmg_write_nstring(str, width);
|
||||
@@ -384,12 +464,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,
|
||||
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) {
|
||||
@@ -405,8 +480,8 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,
|
||||
}
|
||||
|
||||
s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
|
||||
SLsmg_gotorc(self->b.y + row, self->b.x);
|
||||
SLsmg_set_color(color);
|
||||
ui_browser__gotorc(&self->b, row, 0);
|
||||
ui_browser__set_color(&self->b, color);
|
||||
slsmg_write_nstring(" ", offset);
|
||||
slsmg_printf("%c ", folded_sign);
|
||||
slsmg_write_nstring(s, width - 2);
|
||||
@@ -465,7 +540,7 @@ static int hist_browser__show_entry(struct hist_browser *self,
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain) {
|
||||
entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
|
||||
hist_entry__init_have_children(entry);
|
||||
folded_sign = hist_entry__folded(entry);
|
||||
}
|
||||
|
||||
@@ -484,8 +559,8 @@ static int hist_browser__show_entry(struct hist_browser *self,
|
||||
color = HE_COLORSET_NORMAL;
|
||||
}
|
||||
|
||||
SLsmg_set_color(color);
|
||||
SLsmg_gotorc(self->b.y + row, self->b.x);
|
||||
ui_browser__set_color(&self->b, color);
|
||||
ui_browser__gotorc(&self->b, row, 0);
|
||||
if (symbol_conf.use_callchain) {
|
||||
slsmg_printf("%c ", folded_sign);
|
||||
width -= 2;
|
||||
@@ -687,8 +762,6 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
|
||||
|
||||
static void hist_browser__delete(struct hist_browser *self)
|
||||
{
|
||||
newtFormDestroy(self->b.form);
|
||||
newtPopWindow();
|
||||
free(self);
|
||||
}
|
||||
|
||||
@@ -702,21 +775,26 @@ 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)
|
||||
static int hists__browser_title(struct hists *self, char *bf, size_t size,
|
||||
const char *ev_name, const struct dso *dso,
|
||||
const struct thread *thread)
|
||||
{
|
||||
int printed = 0;
|
||||
char unit;
|
||||
int printed;
|
||||
unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
|
||||
|
||||
nr_events = convert_unit(nr_events, &unit);
|
||||
printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
|
||||
|
||||
if (thread)
|
||||
printed += snprintf(bf + printed, size - printed,
|
||||
"Thread: %s(%d)",
|
||||
(thread->comm_set ? thread->comm : ""),
|
||||
", 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);
|
||||
", DSO: %s", dso->short_name);
|
||||
return printed;
|
||||
}
|
||||
|
||||
int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
|
||||
@@ -725,7 +803,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
|
||||
struct pstack *fstack;
|
||||
const struct thread *thread_filter = NULL;
|
||||
const struct dso *dso_filter = NULL;
|
||||
struct newtExitStruct es;
|
||||
char msg[160];
|
||||
int key = -1;
|
||||
|
||||
@@ -738,9 +815,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
|
||||
|
||||
ui_helpline__push(helpline);
|
||||
|
||||
hist_browser__title(msg, sizeof(msg), ev_name,
|
||||
dso_filter, thread_filter);
|
||||
|
||||
hists__browser_title(self, msg, sizeof(msg), ev_name,
|
||||
dso_filter, thread_filter);
|
||||
while (1) {
|
||||
const struct thread *thread;
|
||||
const struct dso *dso;
|
||||
@@ -749,70 +825,63 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
|
||||
annotate = -2, zoom_dso = -2, zoom_thread = -2,
|
||||
browse_map = -2;
|
||||
|
||||
if (hist_browser__run(browser, msg, &es))
|
||||
break;
|
||||
key = hist_browser__run(browser, msg);
|
||||
|
||||
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:;
|
||||
}
|
||||
|
||||
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");
|
||||
switch (key) {
|
||||
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;
|
||||
case 'a':
|
||||
if (browser->selection->map == NULL &&
|
||||
browser->selection->map->dso->annotate_warned)
|
||||
continue;
|
||||
default:;
|
||||
}
|
||||
if (is_exit_key(key)) {
|
||||
if (key == NEWT_KEY_ESCAPE &&
|
||||
!ui__dialog_yesno("Do you really want to exit?"))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
goto do_annotate;
|
||||
case 'd':
|
||||
goto zoom_dso;
|
||||
case 't':
|
||||
goto zoom_thread;
|
||||
case NEWT_KEY_F1:
|
||||
case 'h':
|
||||
case '?':
|
||||
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"
|
||||
"C Collapse all callchains\n"
|
||||
"E Expand all callchains\n"
|
||||
"d Zoom into current DSO\n"
|
||||
"t Zoom into current Thread\n"
|
||||
"q/CTRL+C Exit browser");
|
||||
continue;
|
||||
case NEWT_KEY_ENTER:
|
||||
case NEWT_KEY_RIGHT:
|
||||
/* menu */
|
||||
break;
|
||||
case NEWT_KEY_LEFT: {
|
||||
const void *top;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
case NEWT_KEY_ESCAPE:
|
||||
if (!ui__dialog_yesno("Do you really want to exit?"))
|
||||
continue;
|
||||
/* Fall thru */
|
||||
default:
|
||||
goto out_free_stack;
|
||||
}
|
||||
|
||||
if (browser->selection->sym != NULL &&
|
||||
@@ -885,8 +954,8 @@ zoom_out_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);
|
||||
hists__browser_title(self, msg, sizeof(msg), ev_name,
|
||||
dso_filter, thread_filter);
|
||||
hist_browser__reset(browser);
|
||||
} else if (choice == zoom_thread) {
|
||||
zoom_thread:
|
||||
@@ -903,8 +972,8 @@ zoom_out_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);
|
||||
hists__browser_title(self, msg, sizeof(msg), ev_name,
|
||||
dso_filter, thread_filter);
|
||||
hist_browser__reset(browser);
|
||||
}
|
||||
}
|
||||
@@ -925,10 +994,6 @@ int hists__tui_browse_tree(struct rb_root *self, const char *help)
|
||||
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);
|
||||
@@ -940,7 +1005,7 @@ int hists__tui_browse_tree(struct rb_root *self, const char *help)
|
||||
continue;
|
||||
nd = rb_prev(nd);
|
||||
default:
|
||||
break;
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "../libslang.h"
|
||||
#include <elf.h>
|
||||
#include <newt.h>
|
||||
#include <sys/ttydefaults.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
@@ -47,7 +46,6 @@ out_free_form:
|
||||
struct map_browser {
|
||||
struct ui_browser b;
|
||||
struct map *map;
|
||||
u16 namelen;
|
||||
u8 addrlen;
|
||||
};
|
||||
|
||||
@@ -56,14 +54,16 @@ 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);
|
||||
int width;
|
||||
|
||||
SLsmg_set_color(color);
|
||||
ui_browser__set_percent_color(self, 0, current_entry);
|
||||
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);
|
||||
width = self->width - ((mb->addrlen * 2) + 4);
|
||||
if (width > 0)
|
||||
slsmg_write_nstring(sym->name, width);
|
||||
}
|
||||
|
||||
/* FIXME uber-kludgy, see comment on cmd_report... */
|
||||
@@ -98,31 +98,29 @@ static int map_browser__search(struct map_browser *self)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
|
||||
static int map_browser__run(struct map_browser *self)
|
||||
{
|
||||
int key;
|
||||
|
||||
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;
|
||||
|
||||
newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
|
||||
newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
|
||||
if (verbose)
|
||||
newtFormAddHotKey(self->b.form, '/');
|
||||
ui_browser__add_exit_key(&self->b, '/');
|
||||
|
||||
while (1) {
|
||||
ui_browser__run(&self->b, es);
|
||||
key = ui_browser__run(&self->b);
|
||||
|
||||
if (es->reason != NEWT_EXIT_HOTKEY)
|
||||
break;
|
||||
if (verbose && es->u.key == '/')
|
||||
if (verbose && key == '/')
|
||||
map_browser__search(self);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
ui_browser__hide(&self->b);
|
||||
return 0;
|
||||
return key;
|
||||
}
|
||||
|
||||
int map__browse(struct map *self)
|
||||
@@ -136,7 +134,6 @@ int map__browse(struct map *self)
|
||||
},
|
||||
.map = self,
|
||||
};
|
||||
struct newtExitStruct es;
|
||||
struct rb_node *nd;
|
||||
char tmp[BITS_PER_LONG / 4];
|
||||
u64 maxaddr = 0;
|
||||
@@ -144,8 +141,6 @@ int map__browse(struct map *self)
|
||||
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) {
|
||||
@@ -156,6 +151,5 @@ int map__browse(struct map *self)
|
||||
}
|
||||
|
||||
mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
|
||||
mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
|
||||
return map_browser__run(&mb, &es);
|
||||
return map_browser__run(&mb);
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
#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);
|
||||
@@ -22,7 +20,7 @@ static void newt_form__set_exit_keys(newtComponent self)
|
||||
newtFormAddHotKey(self, CTRL('c'));
|
||||
}
|
||||
|
||||
newtComponent newt_form__new(void)
|
||||
static newtComponent newt_form__new(void)
|
||||
{
|
||||
newtComponent self = newtForm(NULL, NULL, 0);
|
||||
if (self)
|
||||
|
||||
@@ -266,19 +266,6 @@ bool strglobmatch(const char *str, const char *pat);
|
||||
bool strlazymatch(const char *str, const char *pat);
|
||||
unsigned long convert_unit(unsigned long value, char *unit);
|
||||
|
||||
#ifndef ESC
|
||||
#define ESC 27
|
||||
#endif
|
||||
|
||||
static inline bool is_exit_key(int key)
|
||||
{
|
||||
char up;
|
||||
if (key == CTRL('c') || key == ESC)
|
||||
return true;
|
||||
up = toupper(key);
|
||||
return up == 'Q';
|
||||
}
|
||||
|
||||
#define _STR(x) #x
|
||||
#define STR(x) _STR(x)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user