aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2010-08-26 13:22:27 -0400
committerJ. Bruce Fields <bfields@redhat.com>2010-08-26 13:22:27 -0400
commitf632265d0ffb5acf331252d98c64939849d96bb2 (patch)
tree31187d9a726bf1ca6ca12e26ad8e7c609eaf4d8b /tools
parent7d94784293096c0a46897acdb83be5abd9278ece (diff)
parentda5cabf80e2433131bf0ed8993abc0f7ea618c73 (diff)
Merge commit 'v2.6.36-rc1' into HEAD
Diffstat (limited to 'tools')
-rw-r--r--tools/firewire/Makefile19
-rw-r--r--tools/firewire/decode-fcp.c213
-rw-r--r--tools/firewire/list.h62
-rw-r--r--tools/firewire/nosy-dump.c1031
-rw-r--r--tools/firewire/nosy-dump.h173
-rw-r--r--tools/perf/.gitignore2
-rw-r--r--tools/perf/Documentation/perf-buildid-cache.txt8
-rw-r--r--tools/perf/Documentation/perf-probe.txt8
-rw-r--r--tools/perf/Documentation/perf-record.txt13
-rw-r--r--tools/perf/Documentation/perf-stat.txt7
-rw-r--r--tools/perf/Documentation/perf-top.txt8
-rw-r--r--tools/perf/MANIFEST12
-rw-r--r--tools/perf/Makefile170
-rw-r--r--tools/perf/arch/arm/Makefile4
-rw-r--r--tools/perf/arch/arm/util/dwarf-regs.c64
-rw-r--r--tools/perf/arch/sh/Makefile4
-rw-r--r--tools/perf/arch/sh/util/dwarf-regs.c55
-rw-r--r--tools/perf/arch/sparc/Makefile4
-rw-r--r--tools/perf/arch/sparc/util/dwarf-regs.c43
-rw-r--r--tools/perf/builtin-annotate.c8
-rw-r--r--tools/perf/builtin-buildid-cache.c3
-rw-r--r--tools/perf/builtin-diff.c9
-rw-r--r--tools/perf/builtin-probe.c3
-rw-r--r--tools/perf/builtin-record.c86
-rw-r--r--tools/perf/builtin-report.c60
-rw-r--r--tools/perf/builtin-sched.c1
-rw-r--r--tools/perf/builtin-stat.c14
-rw-r--r--tools/perf/builtin-timechart.c15
-rw-r--r--tools/perf/builtin-top.c40
-rw-r--r--tools/perf/builtin-trace.c51
-rw-r--r--tools/perf/feature-tests.mak119
-rw-r--r--tools/perf/perf-archive.sh20
-rw-r--r--tools/perf/perf.c2
-rw-r--r--tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py30
-rw-r--r--tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py184
-rw-r--r--tools/perf/scripts/python/bin/sched-migration-record2
-rw-r--r--tools/perf/scripts/python/bin/sched-migration-report3
-rw-r--r--tools/perf/scripts/python/check-perf-trace.py3
-rw-r--r--tools/perf/scripts/python/sched-migration.py461
-rwxr-xr-xtools/perf/util/PERF-VERSION-GEN17
-rw-r--r--tools/perf/util/build-id.c28
-rw-r--r--tools/perf/util/cache.h1
-rw-r--r--tools/perf/util/callchain.c37
-rw-r--r--tools/perf/util/callchain.h7
-rw-r--r--tools/perf/util/config.c64
-rw-r--r--tools/perf/util/cpumap.c57
-rw-r--r--tools/perf/util/cpumap.h2
-rw-r--r--tools/perf/util/debug.c12
-rw-r--r--tools/perf/util/debug.h9
-rw-r--r--tools/perf/util/event.c124
-rw-r--r--tools/perf/util/event.h6
-rw-r--r--tools/perf/util/header.c13
-rw-r--r--tools/perf/util/hist.c221
-rw-r--r--tools/perf/util/hist.h33
-rw-r--r--tools/perf/util/include/linux/list.h8
-rw-r--r--tools/perf/util/include/linux/types.h12
-rw-r--r--tools/perf/util/map.c116
-rw-r--r--tools/perf/util/map.h14
-rw-r--r--tools/perf/util/newt.c1167
-rw-r--r--tools/perf/util/parse-events.c11
-rw-r--r--tools/perf/util/probe-event.c282
-rw-r--r--tools/perf/util/probe-event.h29
-rw-r--r--tools/perf/util/probe-finder.c257
-rw-r--r--tools/perf/util/probe-finder.h10
-rw-r--r--tools/perf/util/pstack.h2
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c50
-rw-r--r--tools/perf/util/session.c73
-rw-r--r--tools/perf/util/session.h2
-rw-r--r--tools/perf/util/sort.c46
-rw-r--r--tools/perf/util/sort.h22
-rw-r--r--tools/perf/util/symbol.c364
-rw-r--r--tools/perf/util/symbol.h19
-rw-r--r--tools/perf/util/thread.c18
-rw-r--r--tools/perf/util/thread.h7
-rw-r--r--tools/perf/util/ui/browser.c329
-rw-r--r--tools/perf/util/ui/browser.h46
-rw-r--r--tools/perf/util/ui/browsers/annotate.c240
-rw-r--r--tools/perf/util/ui/browsers/hists.c948
-rw-r--r--tools/perf/util/ui/browsers/map.c161
-rw-r--r--tools/perf/util/ui/browsers/map.h6
-rw-r--r--tools/perf/util/ui/helpline.c69
-rw-r--r--tools/perf/util/ui/helpline.h11
-rw-r--r--tools/perf/util/ui/libslang.h27
-rw-r--r--tools/perf/util/ui/progress.c60
-rw-r--r--tools/perf/util/ui/progress.h11
-rw-r--r--tools/perf/util/ui/setup.c42
-rw-r--r--tools/perf/util/ui/util.c114
-rw-r--r--tools/perf/util/ui/util.h10
-rw-r--r--tools/perf/util/util.h3
89 files changed, 6303 insertions, 1928 deletions
diff --git a/tools/firewire/Makefile b/tools/firewire/Makefile
new file mode 100644
index 000000000000..81767adaae7d
--- /dev/null
+++ b/tools/firewire/Makefile
@@ -0,0 +1,19 @@
1prefix = /usr
2nosy-dump-version = 0.4
3
4CC = gcc
5
6all : nosy-dump
7
8nosy-dump : CFLAGS = -Wall -O2 -g
9nosy-dump : CPPFLAGS = -DVERSION=\"$(nosy-dump-version)\" -I../../drivers/firewire
10nosy-dump : LDFLAGS = -g
11nosy-dump : LDLIBS = -lpopt
12
13nosy-dump : nosy-dump.o decode-fcp.o
14
15clean :
16 rm -rf *.o nosy-dump
17
18install :
19 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 000000000000..e41223b6a4c8
--- /dev/null
+++ b/tools/firewire/decode-fcp.c
@@ -0,0 +1,213 @@
1#include <linux/firewire-constants.h>
2#include <stdio.h>
3#include <stdlib.h>
4
5#include "list.h"
6#include "nosy-dump.h"
7
8#define CSR_FCP_COMMAND 0xfffff0000b00ull
9#define CSR_FCP_RESPONSE 0xfffff0000d00ull
10
11static const char * const ctype_names[] = {
12 [0x0] = "control", [0x8] = "not implemented",
13 [0x1] = "status", [0x9] = "accepted",
14 [0x2] = "specific inquiry", [0xa] = "rejected",
15 [0x3] = "notify", [0xb] = "in transition",
16 [0x4] = "general inquiry", [0xc] = "stable",
17 [0x5] = "(reserved 0x05)", [0xd] = "changed",
18 [0x6] = "(reserved 0x06)", [0xe] = "(reserved 0x0e)",
19 [0x7] = "(reserved 0x07)", [0xf] = "interim",
20};
21
22static const char * const subunit_type_names[] = {
23 [0x00] = "monitor", [0x10] = "(reserved 0x10)",
24 [0x01] = "audio", [0x11] = "(reserved 0x11)",
25 [0x02] = "printer", [0x12] = "(reserved 0x12)",
26 [0x03] = "disc", [0x13] = "(reserved 0x13)",
27 [0x04] = "tape recorder/player",[0x14] = "(reserved 0x14)",
28 [0x05] = "tuner", [0x15] = "(reserved 0x15)",
29 [0x06] = "ca", [0x16] = "(reserved 0x16)",
30 [0x07] = "camera", [0x17] = "(reserved 0x17)",
31 [0x08] = "(reserved 0x08)", [0x18] = "(reserved 0x18)",
32 [0x09] = "panel", [0x19] = "(reserved 0x19)",
33 [0x0a] = "bulletin board", [0x1a] = "(reserved 0x1a)",
34 [0x0b] = "camera storage", [0x1b] = "(reserved 0x1b)",
35 [0x0c] = "(reserved 0x0c)", [0x1c] = "vendor unique",
36 [0x0d] = "(reserved 0x0d)", [0x1d] = "all subunit types",
37 [0x0e] = "(reserved 0x0e)", [0x1e] = "subunit_type extended to next byte",
38 [0x0f] = "(reserved 0x0f)", [0x1f] = "unit",
39};
40
41struct avc_enum {
42 int value;
43 const char *name;
44};
45
46struct avc_field {
47 const char *name; /* Short name for field. */
48 int offset; /* Location of field, specified in bits; */
49 /* negative means from end of packet. */
50 int width; /* Width of field, 0 means use data_length. */
51 struct avc_enum *names;
52};
53
54struct avc_opcode_info {
55 const char *name;
56 struct avc_field fields[8];
57};
58
59struct avc_enum power_field_names[] = {
60 { 0x70, "on" },
61 { 0x60, "off" },
62 { }
63};
64
65static const struct avc_opcode_info opcode_info[256] = {
66
67 /* TA Document 1999026 */
68 /* AV/C Digital Interface Command Set General Specification 4.0 */
69 [0xb2] = { "power", {
70 { "state", 0, 8, power_field_names }
71 }
72 },
73 [0x30] = { "unit info", {
74 { "foo", 0, 8 },
75 { "unit_type", 8, 5 },
76 { "unit", 13, 3 },
77 { "company id", 16, 24 },
78 }
79 },
80 [0x31] = { "subunit info" },
81 [0x01] = { "reserve" },
82 [0xb0] = { "version" },
83 [0x00] = { "vendor dependent" },
84 [0x02] = { "plug info" },
85 [0x12] = { "channel usage" },
86 [0x24] = { "connect" },
87 [0x20] = { "connect av" },
88 [0x22] = { "connections" },
89 [0x11] = { "digital input" },
90 [0x10] = { "digital output" },
91 [0x25] = { "disconnect" },
92 [0x21] = { "disconnect av" },
93 [0x19] = { "input plug signal format" },
94 [0x18] = { "output plug signal format" },
95 [0x1f] = { "general bus setup" },
96
97 /* TA Document 1999025 */
98 /* AV/C Descriptor Mechanism Specification Version 1.0 */
99 [0x0c] = { "create descriptor" },
100 [0x08] = { "open descriptor" },
101 [0x09] = { "read descriptor" },
102 [0x0a] = { "write descriptor" },
103 [0x05] = { "open info block" },
104 [0x06] = { "read info block" },
105 [0x07] = { "write info block" },
106 [0x0b] = { "search descriptor" },
107 [0x0d] = { "object number select" },
108
109 /* TA Document 1999015 */
110 /* AV/C Command Set for Rate Control of Isochronous Data Flow 1.0 */
111 [0xb3] = { "rate", {
112 { "subfunction", 0, 8 },
113 { "result", 8, 8 },
114 { "plug_type", 16, 8 },
115 { "plug_id", 16, 8 },
116 }
117 },
118
119 /* TA Document 1999008 */
120 /* AV/C Audio Subunit Specification 1.0 */
121 [0xb8] = { "function block" },
122
123 /* TA Document 2001001 */
124 /* AV/C Panel Subunit Specification 1.1 */
125 [0x7d] = { "gui update" },
126 [0x7e] = { "push gui data" },
127 [0x7f] = { "user action" },
128 [0x7c] = { "pass through" },
129
130 /* */
131 [0x26] = { "asynchronous connection" },
132};
133
134struct avc_frame {
135 uint32_t operand0:8;
136 uint32_t opcode:8;
137 uint32_t subunit_id:3;
138 uint32_t subunit_type:5;
139 uint32_t ctype:4;
140 uint32_t cts:4;
141};
142
143static void
144decode_avc(struct link_transaction *t)
145{
146 struct avc_frame *frame =
147 (struct avc_frame *) t->request->packet.write_block.data;
148 const struct avc_opcode_info *info;
149 const char *name;
150 char buffer[32];
151 int i;
152
153 info = &opcode_info[frame->opcode];
154 if (info->name == NULL) {
155 snprintf(buffer, sizeof(buffer),
156 "(unknown opcode 0x%02x)", frame->opcode);
157 name = buffer;
158 } else {
159 name = info->name;
160 }
161
162 printf("av/c %s, subunit_type=%s, subunit_id=%d, opcode=%s",
163 ctype_names[frame->ctype], subunit_type_names[frame->subunit_type],
164 frame->subunit_id, name);
165
166 for (i = 0; info->fields[i].name != NULL; i++)
167 printf(", %s", info->fields[i].name);
168
169 printf("\n");
170}
171
172int
173decode_fcp(struct link_transaction *t)
174{
175 struct avc_frame *frame =
176 (struct avc_frame *) t->request->packet.write_block.data;
177 unsigned long long offset =
178 ((unsigned long long) t->request->packet.common.offset_high << 32) |
179 t->request->packet.common.offset_low;
180
181 if (t->request->packet.common.tcode != TCODE_WRITE_BLOCK_REQUEST)
182 return 0;
183
184 if (offset == CSR_FCP_COMMAND || offset == CSR_FCP_RESPONSE) {
185 switch (frame->cts) {
186 case 0x00:
187 decode_avc(t);
188 break;
189 case 0x01:
190 printf("cal fcp frame (cts=0x01)\n");
191 break;
192 case 0x02:
193 printf("ehs fcp frame (cts=0x02)\n");
194 break;
195 case 0x03:
196 printf("havi fcp frame (cts=0x03)\n");
197 break;
198 case 0x0e:
199 printf("vendor specific fcp frame (cts=0x0e)\n");
200 break;
201 case 0x0f:
202 printf("extended cts\n");
203 break;
204 default:
205 printf("reserved fcp frame (ctx=0x%02x)\n", frame->cts);
206 break;
207 }
208 return 1;
209 }
210
211 return 0;
212}
213
diff --git a/tools/firewire/list.h b/tools/firewire/list.h
new file mode 100644
index 000000000000..41f4bdadf634
--- /dev/null
+++ b/tools/firewire/list.h
@@ -0,0 +1,62 @@
1struct list {
2 struct list *next, *prev;
3};
4
5static inline void
6list_init(struct list *list)
7{
8 list->next = list;
9 list->prev = list;
10}
11
12static inline int
13list_empty(struct list *list)
14{
15 return list->next == list;
16}
17
18static inline void
19list_insert(struct list *link, struct list *new_link)
20{
21 new_link->prev = link->prev;
22 new_link->next = link;
23 new_link->prev->next = new_link;
24 new_link->next->prev = new_link;
25}
26
27static inline void
28list_append(struct list *list, struct list *new_link)
29{
30 list_insert((struct list *)list, new_link);
31}
32
33static inline void
34list_prepend(struct list *list, struct list *new_link)
35{
36 list_insert(list->next, new_link);
37}
38
39static inline void
40list_remove(struct list *link)
41{
42 link->prev->next = link->next;
43 link->next->prev = link->prev;
44}
45
46#define list_entry(link, type, member) \
47 ((type *)((char *)(link)-(unsigned long)(&((type *)0)->member)))
48
49#define list_head(list, type, member) \
50 list_entry((list)->next, type, member)
51
52#define list_tail(list, type, member) \
53 list_entry((list)->prev, type, member)
54
55#define list_next(elm, member) \
56 list_entry((elm)->member.next, typeof(*elm), member)
57
58#define list_for_each_entry(pos, list, member) \
59 for (pos = list_head(list, typeof(*pos), member); \
60 &pos->member != (list); \
61 pos = list_next(pos, member))
62
diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c
new file mode 100644
index 000000000000..f93b776370b6
--- /dev/null
+++ b/tools/firewire/nosy-dump.c
@@ -0,0 +1,1031 @@
1/*
2 * nosy-dump - Interface to snoop mode driver for TI PCILynx 1394 controllers
3 * Copyright (C) 2002-2006 Kristian Høgsberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20#include <byteswap.h>
21#include <endian.h>
22#include <fcntl.h>
23#include <linux/firewire-constants.h>
24#include <poll.h>
25#include <popt.h>
26#include <signal.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/ioctl.h>
31#include <sys/time.h>
32#include <termios.h>
33#include <unistd.h>
34
35#include "list.h"
36#include "nosy-dump.h"
37#include "nosy-user.h"
38
39enum {
40 PACKET_FIELD_DETAIL = 0x01,
41 PACKET_FIELD_DATA_LENGTH = 0x02,
42 /* Marks the fields we print in transaction view. */
43 PACKET_FIELD_TRANSACTION = 0x04,
44};
45
46static void print_packet(uint32_t *data, size_t length);
47static void decode_link_packet(struct link_packet *packet, size_t length,
48 int include_flags, int exclude_flags);
49static int run = 1;
50sig_t sys_sigint_handler;
51
52static char *option_nosy_device = "/dev/nosy";
53static char *option_view = "packet";
54static char *option_output;
55static char *option_input;
56static int option_hex;
57static int option_iso;
58static int option_cycle_start;
59static int option_version;
60static int option_verbose;
61
62enum {
63 VIEW_TRANSACTION,
64 VIEW_PACKET,
65 VIEW_STATS,
66};
67
68static const struct poptOption options[] = {
69 {
70 .longName = "device",
71 .shortName = 'd',
72 .argInfo = POPT_ARG_STRING,
73 .arg = &option_nosy_device,
74 .descrip = "Path to nosy device.",
75 .argDescrip = "DEVICE"
76 },
77 {
78 .longName = "view",
79 .argInfo = POPT_ARG_STRING,
80 .arg = &option_view,
81 .descrip = "Specify view of bus traffic: packet, transaction or stats.",
82 .argDescrip = "VIEW"
83 },
84 {
85 .longName = "hex",
86 .shortName = 'x',
87 .argInfo = POPT_ARG_NONE,
88 .arg = &option_hex,
89 .descrip = "Print each packet in hex.",
90 },
91 {
92 .longName = "iso",
93 .argInfo = POPT_ARG_NONE,
94 .arg = &option_iso,
95 .descrip = "Print iso packets.",
96 },
97 {
98 .longName = "cycle-start",
99 .argInfo = POPT_ARG_NONE,
100 .arg = &option_cycle_start,
101 .descrip = "Print cycle start packets.",
102 },
103 {
104 .longName = "verbose",
105 .shortName = 'v',
106 .argInfo = POPT_ARG_NONE,
107 .arg = &option_verbose,
108 .descrip = "Verbose packet view.",
109 },
110 {
111 .longName = "output",
112 .shortName = 'o',
113 .argInfo = POPT_ARG_STRING,
114 .arg = &option_output,
115 .descrip = "Log to output file.",
116 .argDescrip = "FILENAME"
117 },
118 {
119 .longName = "input",
120 .shortName = 'i',
121 .argInfo = POPT_ARG_STRING,
122 .arg = &option_input,
123 .descrip = "Decode log from file.",
124 .argDescrip = "FILENAME"
125 },
126 {
127 .longName = "version",
128 .argInfo = POPT_ARG_NONE,
129 .arg = &option_version,
130 .descrip = "Specify print version info.",
131 },
132 POPT_AUTOHELP
133 POPT_TABLEEND
134};
135
136/* Allow all ^C except the first to interrupt the program in the usual way. */
137static void
138sigint_handler(int signal_num)
139{
140 if (run == 1) {
141 run = 0;
142 signal(SIGINT, SIG_DFL);
143 }
144}
145
146static struct subaction *
147subaction_create(uint32_t *data, size_t length)
148{
149 struct subaction *sa;
150
151 /* we put the ack in the subaction struct for easy access. */
152 sa = malloc(sizeof *sa - sizeof sa->packet + length);
153 sa->ack = data[length / 4 - 1];
154 sa->length = length;
155 memcpy(&sa->packet, data, length);
156
157 return sa;
158}
159
160static void
161subaction_destroy(struct subaction *sa)
162{
163 free(sa);
164}
165
166static struct list pending_transaction_list = {
167 &pending_transaction_list, &pending_transaction_list
168};
169
170static struct link_transaction *
171link_transaction_lookup(int request_node, int response_node, int tlabel)
172{
173 struct link_transaction *t;
174
175 list_for_each_entry(t, &pending_transaction_list, link) {
176 if (t->request_node == request_node &&
177 t->response_node == response_node &&
178 t->tlabel == tlabel)
179 return t;
180 }
181
182 t = malloc(sizeof *t);
183 t->request_node = request_node;
184 t->response_node = response_node;
185 t->tlabel = tlabel;
186 list_init(&t->request_list);
187 list_init(&t->response_list);
188
189 list_append(&pending_transaction_list, &t->link);
190
191 return t;
192}
193
194static void
195link_transaction_destroy(struct link_transaction *t)
196{
197 struct subaction *sa;
198
199 while (!list_empty(&t->request_list)) {
200 sa = list_head(&t->request_list, struct subaction, link);
201 list_remove(&sa->link);
202 subaction_destroy(sa);
203 }
204 while (!list_empty(&t->response_list)) {
205 sa = list_head(&t->response_list, struct subaction, link);
206 list_remove(&sa->link);
207 subaction_destroy(sa);
208 }
209 free(t);
210}
211
212struct protocol_decoder {
213 const char *name;
214 int (*decode)(struct link_transaction *t);
215};
216
217static const struct protocol_decoder protocol_decoders[] = {
218 { "FCP", decode_fcp }
219};
220
221static void
222handle_transaction(struct link_transaction *t)
223{
224 struct subaction *sa;
225 int i;
226
227 if (!t->request) {
228 printf("BUG in handle_transaction\n");
229 return;
230 }
231
232 for (i = 0; i < array_length(protocol_decoders); i++)
233 if (protocol_decoders[i].decode(t))
234 break;
235
236 /* HACK: decode only fcp right now. */
237 return;
238
239 decode_link_packet(&t->request->packet, t->request->length,
240 PACKET_FIELD_TRANSACTION, 0);
241 if (t->response)
242 decode_link_packet(&t->response->packet, t->request->length,
243 PACKET_FIELD_TRANSACTION, 0);
244 else
245 printf("[no response]");
246
247 if (option_verbose) {
248 list_for_each_entry(sa, &t->request_list, link)
249 print_packet((uint32_t *) &sa->packet, sa->length);
250 list_for_each_entry(sa, &t->response_list, link)
251 print_packet((uint32_t *) &sa->packet, sa->length);
252 }
253 printf("\r\n");
254
255 link_transaction_destroy(t);
256}
257
258static void
259clear_pending_transaction_list(void)
260{
261 struct link_transaction *t;
262
263 while (!list_empty(&pending_transaction_list)) {
264 t = list_head(&pending_transaction_list,
265 struct link_transaction, link);
266 list_remove(&t->link);
267 link_transaction_destroy(t);
268 /* print unfinished transactions */
269 }
270}
271
272static const char * const tcode_names[] = {
273 [0x0] = "write_quadlet_request", [0x6] = "read_quadlet_response",
274 [0x1] = "write_block_request", [0x7] = "read_block_response",
275 [0x2] = "write_response", [0x8] = "cycle_start",
276 [0x3] = "reserved", [0x9] = "lock_request",
277 [0x4] = "read_quadlet_request", [0xa] = "iso_data",
278 [0x5] = "read_block_request", [0xb] = "lock_response",
279};
280
281static const char * const ack_names[] = {
282 [0x0] = "no ack", [0x8] = "reserved (0x08)",
283 [0x1] = "ack_complete", [0x9] = "reserved (0x09)",
284 [0x2] = "ack_pending", [0xa] = "reserved (0x0a)",
285 [0x3] = "reserved (0x03)", [0xb] = "reserved (0x0b)",
286 [0x4] = "ack_busy_x", [0xc] = "reserved (0x0c)",
287 [0x5] = "ack_busy_a", [0xd] = "ack_data_error",
288 [0x6] = "ack_busy_b", [0xe] = "ack_type_error",
289 [0x7] = "reserved (0x07)", [0xf] = "reserved (0x0f)",
290};
291
292static const char * const rcode_names[] = {
293 [0x0] = "complete", [0x4] = "conflict_error",
294 [0x1] = "reserved (0x01)", [0x5] = "data_error",
295 [0x2] = "reserved (0x02)", [0x6] = "type_error",
296 [0x3] = "reserved (0x03)", [0x7] = "address_error",
297};
298
299static const char * const retry_names[] = {
300 [0x0] = "retry_1",
301 [0x1] = "retry_x",
302 [0x2] = "retry_a",
303 [0x3] = "retry_b",
304};
305
306enum {
307 PACKET_RESERVED,
308 PACKET_REQUEST,
309 PACKET_RESPONSE,
310 PACKET_OTHER,
311};
312
313struct packet_info {
314 const char *name;
315 int type;
316 int response_tcode;
317 const struct packet_field *fields;
318 int field_count;
319};
320
321struct packet_field {
322 const char *name; /* Short name for field. */
323 int offset; /* Location of field, specified in bits; */
324 /* negative means from end of packet. */
325 int width; /* Width of field, 0 means use data_length. */
326 int flags; /* Show options. */
327 const char * const *value_names;
328};
329
330#define COMMON_REQUEST_FIELDS \
331 { "dest", 0, 16, PACKET_FIELD_TRANSACTION }, \
332 { "tl", 16, 6 }, \
333 { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \
334 { "tcode", 24, 4, PACKET_FIELD_TRANSACTION, tcode_names }, \
335 { "pri", 28, 4, PACKET_FIELD_DETAIL }, \
336 { "src", 32, 16, PACKET_FIELD_TRANSACTION }, \
337 { "offs", 48, 48, PACKET_FIELD_TRANSACTION }
338
339#define COMMON_RESPONSE_FIELDS \
340 { "dest", 0, 16 }, \
341 { "tl", 16, 6 }, \
342 { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \
343 { "tcode", 24, 4, 0, tcode_names }, \
344 { "pri", 28, 4, PACKET_FIELD_DETAIL }, \
345 { "src", 32, 16 }, \
346 { "rcode", 48, 4, PACKET_FIELD_TRANSACTION, rcode_names }
347
348static const struct packet_field read_quadlet_request_fields[] = {
349 COMMON_REQUEST_FIELDS,
350 { "crc", 96, 32, PACKET_FIELD_DETAIL },
351 { "ack", 156, 4, 0, ack_names },
352};
353
354static const struct packet_field read_quadlet_response_fields[] = {
355 COMMON_RESPONSE_FIELDS,
356 { "data", 96, 32, PACKET_FIELD_TRANSACTION },
357 { "crc", 128, 32, PACKET_FIELD_DETAIL },
358 { "ack", 188, 4, 0, ack_names },
359};
360
361static const struct packet_field read_block_request_fields[] = {
362 COMMON_REQUEST_FIELDS,
363 { "data_length", 96, 16, PACKET_FIELD_TRANSACTION },
364 { "extended_tcode", 112, 16 },
365 { "crc", 128, 32, PACKET_FIELD_DETAIL },
366 { "ack", 188, 4, 0, ack_names },
367};
368
369static const struct packet_field block_response_fields[] = {
370 COMMON_RESPONSE_FIELDS,
371 { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH },
372 { "extended_tcode", 112, 16 },
373 { "crc", 128, 32, PACKET_FIELD_DETAIL },
374 { "data", 160, 0, PACKET_FIELD_TRANSACTION },
375 { "crc", -64, 32, PACKET_FIELD_DETAIL },
376 { "ack", -4, 4, 0, ack_names },
377};
378
379static const struct packet_field write_quadlet_request_fields[] = {
380 COMMON_REQUEST_FIELDS,
381 { "data", 96, 32, PACKET_FIELD_TRANSACTION },
382 { "ack", -4, 4, 0, ack_names },
383};
384
385static const struct packet_field block_request_fields[] = {
386 COMMON_REQUEST_FIELDS,
387 { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH | PACKET_FIELD_TRANSACTION },
388 { "extended_tcode", 112, 16, PACKET_FIELD_TRANSACTION },
389 { "crc", 128, 32, PACKET_FIELD_DETAIL },
390 { "data", 160, 0, PACKET_FIELD_TRANSACTION },
391 { "crc", -64, 32, PACKET_FIELD_DETAIL },
392 { "ack", -4, 4, 0, ack_names },
393};
394
395static const struct packet_field write_response_fields[] = {
396 COMMON_RESPONSE_FIELDS,
397 { "reserved", 64, 32, PACKET_FIELD_DETAIL },
398 { "ack", -4, 4, 0, ack_names },
399};
400
401static const struct packet_field iso_data_fields[] = {
402 { "data_length", 0, 16, PACKET_FIELD_DATA_LENGTH },
403 { "tag", 16, 2 },
404 { "channel", 18, 6 },
405 { "tcode", 24, 4, 0, tcode_names },
406 { "sy", 28, 4 },
407 { "crc", 32, 32, PACKET_FIELD_DETAIL },
408 { "data", 64, 0 },
409 { "crc", -64, 32, PACKET_FIELD_DETAIL },
410 { "ack", -4, 4, 0, ack_names },
411};
412
413static const struct packet_info packet_info[] = {
414 {
415 .name = "write_quadlet_request",
416 .type = PACKET_REQUEST,
417 .response_tcode = TCODE_WRITE_RESPONSE,
418 .fields = write_quadlet_request_fields,
419 .field_count = array_length(write_quadlet_request_fields)
420 },
421 {
422 .name = "write_block_request",
423 .type = PACKET_REQUEST,
424 .response_tcode = TCODE_WRITE_RESPONSE,
425 .fields = block_request_fields,
426 .field_count = array_length(block_request_fields)
427 },
428 {
429 .name = "write_response",
430 .type = PACKET_RESPONSE,
431 .fields = write_response_fields,
432 .field_count = array_length(write_response_fields)
433 },
434 {
435 .name = "reserved",
436 .type = PACKET_RESERVED,
437 },
438 {
439 .name = "read_quadlet_request",
440 .type = PACKET_REQUEST,
441 .response_tcode = TCODE_READ_QUADLET_RESPONSE,
442 .fields = read_quadlet_request_fields,
443 .field_count = array_length(read_quadlet_request_fields)
444 },
445 {
446 .name = "read_block_request",
447 .type = PACKET_REQUEST,
448 .response_tcode = TCODE_READ_BLOCK_RESPONSE,
449 .fields = read_block_request_fields,
450 .field_count = array_length(read_block_request_fields)
451 },
452 {
453 .name = "read_quadlet_response",
454 .type = PACKET_RESPONSE,
455 .fields = read_quadlet_response_fields,
456 .field_count = array_length(read_quadlet_response_fields)
457 },
458 {
459 .name = "read_block_response",
460 .type = PACKET_RESPONSE,
461 .fields = block_response_fields,
462 .field_count = array_length(block_response_fields)
463 },
464 {
465 .name = "cycle_start",
466 .type = PACKET_OTHER,
467 .fields = write_quadlet_request_fields,
468 .field_count = array_length(write_quadlet_request_fields)
469 },
470 {
471 .name = "lock_request",
472 .type = PACKET_REQUEST,
473 .fields = block_request_fields,
474 .field_count = array_length(block_request_fields)
475 },
476 {
477 .name = "iso_data",
478 .type = PACKET_OTHER,
479 .fields = iso_data_fields,
480 .field_count = array_length(iso_data_fields)
481 },
482 {
483 .name = "lock_response",
484 .type = PACKET_RESPONSE,
485 .fields = block_response_fields,
486 .field_count = array_length(block_response_fields)
487 },
488};
489
490static int
491handle_request_packet(uint32_t *data, size_t length)
492{
493 struct link_packet *p = (struct link_packet *) data;
494 struct subaction *sa, *prev;
495 struct link_transaction *t;
496
497 t = link_transaction_lookup(p->common.source, p->common.destination,
498 p->common.tlabel);
499 sa = subaction_create(data, length);
500 t->request = sa;
501
502 if (!list_empty(&t->request_list)) {
503 prev = list_tail(&t->request_list,
504 struct subaction, link);
505
506 if (!ACK_BUSY(prev->ack)) {
507 /*
508 * error, we should only see ack_busy_* before the
509 * ack_pending/ack_complete -- this is an ack_pending
510 * instead (ack_complete would have finished the
511 * transaction).
512 */
513 }
514
515 if (prev->packet.common.tcode != sa->packet.common.tcode ||
516 prev->packet.common.tlabel != sa->packet.common.tlabel) {
517 /* memcmp() ? */
518 /* error, these should match for retries. */
519 }
520 }
521
522 list_append(&t->request_list, &sa->link);
523
524 switch (sa->ack) {
525 case ACK_COMPLETE:
526 if (p->common.tcode != TCODE_WRITE_QUADLET_REQUEST &&
527 p->common.tcode != TCODE_WRITE_BLOCK_REQUEST)
528 /* error, unified transactions only allowed for write */;
529 list_remove(&t->link);
530 handle_transaction(t);
531 break;
532
533 case ACK_NO_ACK:
534 case ACK_DATA_ERROR:
535 case ACK_TYPE_ERROR:
536 list_remove(&t->link);
537 handle_transaction(t);
538 break;
539
540 case ACK_PENDING:
541 /* request subaction phase over, wait for response. */
542 break;
543
544 case ACK_BUSY_X:
545 case ACK_BUSY_A:
546 case ACK_BUSY_B:
547 /* ok, wait for retry. */
548 /* check that retry protocol is respected. */
549 break;
550 }
551
552 return 1;
553}
554
555static int
556handle_response_packet(uint32_t *data, size_t length)
557{
558 struct link_packet *p = (struct link_packet *) data;
559 struct subaction *sa, *prev;
560 struct link_transaction *t;
561
562 t = link_transaction_lookup(p->common.destination, p->common.source,
563 p->common.tlabel);
564 if (list_empty(&t->request_list)) {
565 /* unsolicited response */
566 }
567
568 sa = subaction_create(data, length);
569 t->response = sa;
570
571 if (!list_empty(&t->response_list)) {
572 prev = list_tail(&t->response_list, struct subaction, link);
573
574 if (!ACK_BUSY(prev->ack)) {
575 /*
576 * error, we should only see ack_busy_* before the
577 * ack_pending/ack_complete
578 */
579 }
580
581 if (prev->packet.common.tcode != sa->packet.common.tcode ||
582 prev->packet.common.tlabel != sa->packet.common.tlabel) {
583 /* use memcmp() instead? */
584 /* error, these should match for retries. */
585 }
586 } else {
587 prev = list_tail(&t->request_list, struct subaction, link);
588 if (prev->ack != ACK_PENDING) {
589 /*
590 * error, should not get response unless last request got
591 * ack_pending.
592 */
593 }
594
595 if (packet_info[prev->packet.common.tcode].response_tcode !=
596 sa->packet.common.tcode) {
597 /* error, tcode mismatch */
598 }
599 }
600
601 list_append(&t->response_list, &sa->link);
602
603 switch (sa->ack) {
604 case ACK_COMPLETE:
605 case ACK_NO_ACK:
606 case ACK_DATA_ERROR:
607 case ACK_TYPE_ERROR:
608 list_remove(&t->link);
609 handle_transaction(t);
610 /* transaction complete, remove t from pending list. */
611 break;
612
613 case ACK_PENDING:
614 /* error for responses. */
615 break;
616
617 case ACK_BUSY_X:
618 case ACK_BUSY_A:
619 case ACK_BUSY_B:
620 /* no problem, wait for next retry */
621 break;
622 }
623
624 return 1;
625}
626
627static int
628handle_packet(uint32_t *data, size_t length)
629{
630 if (length == 0) {
631 printf("bus reset\r\n");
632 clear_pending_transaction_list();
633 } else if (length > sizeof(struct phy_packet)) {
634 struct link_packet *p = (struct link_packet *) data;
635
636 switch (packet_info[p->common.tcode].type) {
637 case PACKET_REQUEST:
638 return handle_request_packet(data, length);
639
640 case PACKET_RESPONSE:
641 return handle_response_packet(data, length);
642
643 case PACKET_OTHER:
644 case PACKET_RESERVED:
645 return 0;
646 }
647 }
648
649 return 1;
650}
651
652static unsigned int
653get_bits(struct link_packet *packet, int offset, int width)
654{
655 uint32_t *data = (uint32_t *) packet;
656 uint32_t index, shift, mask;
657
658 index = offset / 32 + 1;
659 shift = 32 - (offset & 31) - width;
660 mask = width == 32 ? ~0 : (1 << width) - 1;
661
662 return (data[index] >> shift) & mask;
663}
664
665#if __BYTE_ORDER == __LITTLE_ENDIAN
666#define byte_index(i) ((i) ^ 3)
667#elif __BYTE_ORDER == __BIG_ENDIAN
668#define byte_index(i) (i)
669#else
670#error unsupported byte order.
671#endif
672
673static void
674dump_data(unsigned char *data, int length)
675{
676 int i, print_length;
677
678 if (length > 128)
679 print_length = 128;
680 else
681 print_length = length;
682
683 for (i = 0; i < print_length; i++)
684 printf("%s%02hhx",
685 (i % 4 == 0 && i != 0) ? " " : "",
686 data[byte_index(i)]);
687
688 if (print_length < length)
689 printf(" (%d more bytes)", length - print_length);
690}
691
692static void
693decode_link_packet(struct link_packet *packet, size_t length,
694 int include_flags, int exclude_flags)
695{
696 const struct packet_info *pi;
697 int data_length = 0;
698 int i;
699
700 pi = &packet_info[packet->common.tcode];
701
702 for (i = 0; i < pi->field_count; i++) {
703 const struct packet_field *f = &pi->fields[i];
704 int offset;
705
706 if (f->flags & exclude_flags)
707 continue;
708 if (include_flags && !(f->flags & include_flags))
709 continue;
710
711 if (f->offset < 0)
712 offset = length * 8 + f->offset - 32;
713 else
714 offset = f->offset;
715
716 if (f->value_names != NULL) {
717 uint32_t bits;
718
719 bits = get_bits(packet, offset, f->width);
720 printf("%s", f->value_names[bits]);
721 } else if (f->width == 0) {
722 printf("%s=[", f->name);
723 dump_data((unsigned char *) packet + (offset / 8 + 4), data_length);
724 printf("]");
725 } else {
726 unsigned long long bits;
727 int high_width, low_width;
728
729 if ((offset & ~31) != ((offset + f->width - 1) & ~31)) {
730 /* Bit field spans quadlet boundary. */
731 high_width = ((offset + 31) & ~31) - offset;
732 low_width = f->width - high_width;
733
734 bits = get_bits(packet, offset, high_width);
735 bits = (bits << low_width) |
736 get_bits(packet, offset + high_width, low_width);
737 } else {
738 bits = get_bits(packet, offset, f->width);
739 }
740
741 printf("%s=0x%0*llx", f->name, (f->width + 3) / 4, bits);
742
743 if (f->flags & PACKET_FIELD_DATA_LENGTH)
744 data_length = bits;
745 }
746
747 if (i < pi->field_count - 1)
748 printf(", ");
749 }
750}
751
752static void
753print_packet(uint32_t *data, size_t length)
754{
755 int i;
756
757 printf("%6u ", data[0]);
758
759 if (length == 4) {
760 printf("bus reset");
761 } else if (length < sizeof(struct phy_packet)) {
762 printf("short packet: ");
763 for (i = 1; i < length / 4; i++)
764 printf("%s%08x", i == 0 ? "[" : " ", data[i]);
765 printf("]");
766
767 } else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) {
768 struct phy_packet *pp = (struct phy_packet *) data;
769
770 /* phy packet are 3 quadlets: the 1 quadlet payload,
771 * the bitwise inverse of the payload and the snoop
772 * mode ack */
773
774 switch (pp->common.identifier) {
775 case PHY_PACKET_CONFIGURATION:
776 if (!pp->phy_config.set_root && !pp->phy_config.set_gap_count) {
777 printf("ext phy config: phy_id=%02x", pp->phy_config.root_id);
778 } else {
779 printf("phy config:");
780 if (pp->phy_config.set_root)
781 printf(" set_root_id=%02x", pp->phy_config.root_id);
782 if (pp->phy_config.set_gap_count)
783 printf(" set_gap_count=%d", pp->phy_config.gap_count);
784 }
785 break;
786
787 case PHY_PACKET_LINK_ON:
788 printf("link-on packet, phy_id=%02x", pp->link_on.phy_id);
789 break;
790
791 case PHY_PACKET_SELF_ID:
792 if (pp->self_id.extended) {
793 printf("extended self id: phy_id=%02x, seq=%d",
794 pp->ext_self_id.phy_id, pp->ext_self_id.sequence);
795 } else {
796 static const char * const speed_names[] = {
797 "S100", "S200", "S400", "BETA"
798 };
799 printf("self id: phy_id=%02x, link %s, gap_count=%d, speed=%s%s%s",
800 pp->self_id.phy_id,
801 (pp->self_id.link_active ? "active" : "not active"),
802 pp->self_id.gap_count,
803 speed_names[pp->self_id.phy_speed],
804 (pp->self_id.contender ? ", irm contender" : ""),
805 (pp->self_id.initiated_reset ? ", initiator" : ""));
806 }
807 break;
808 default:
809 printf("unknown phy packet: ");
810 for (i = 1; i < length / 4; i++)
811 printf("%s%08x", i == 0 ? "[" : " ", data[i]);
812 printf("]");
813 break;
814 }
815 } else {
816 struct link_packet *packet = (struct link_packet *) data;
817
818 decode_link_packet(packet, length, 0,
819 option_verbose ? 0 : PACKET_FIELD_DETAIL);
820 }
821
822 if (option_hex) {
823 printf(" [");
824 dump_data((unsigned char *) data + 4, length - 4);
825 printf("]");
826 }
827
828 printf("\r\n");
829}
830
831#define HIDE_CURSOR "\033[?25l"
832#define SHOW_CURSOR "\033[?25h"
833#define CLEAR "\033[H\033[2J"
834
835static void
836print_stats(uint32_t *data, size_t length)
837{
838 static int bus_reset_count, short_packet_count, phy_packet_count;
839 static int tcode_count[16];
840 static struct timeval last_update;
841 struct timeval now;
842 int i;
843
844 if (length == 0)
845 bus_reset_count++;
846 else if (length < sizeof(struct phy_packet))
847 short_packet_count++;
848 else if (length == sizeof(struct phy_packet) && data[1] == ~data[2])
849 phy_packet_count++;
850 else {
851 struct link_packet *packet = (struct link_packet *) data;
852 tcode_count[packet->common.tcode]++;
853 }
854
855 gettimeofday(&now, NULL);
856 if (now.tv_sec <= last_update.tv_sec &&
857 now.tv_usec < last_update.tv_usec + 500000)
858 return;
859
860 last_update = now;
861 printf(CLEAR HIDE_CURSOR
862 " bus resets : %8d\n"
863 " short packets : %8d\n"
864 " phy packets : %8d\n",
865 bus_reset_count, short_packet_count, phy_packet_count);
866
867 for (i = 0; i < array_length(packet_info); i++)
868 if (packet_info[i].type != PACKET_RESERVED)
869 printf(" %-24s: %8d\n", packet_info[i].name, tcode_count[i]);
870 printf(SHOW_CURSOR "\n");
871}
872
873static struct termios saved_attributes;
874
875static void
876reset_input_mode(void)
877{
878 tcsetattr(STDIN_FILENO, TCSANOW, &saved_attributes);
879}
880
881static void
882set_input_mode(void)
883{
884 struct termios tattr;
885
886 /* Make sure stdin is a terminal. */
887 if (!isatty(STDIN_FILENO)) {
888 fprintf(stderr, "Not a terminal.\n");
889 exit(EXIT_FAILURE);
890 }
891
892 /* Save the terminal attributes so we can restore them later. */
893 tcgetattr(STDIN_FILENO, &saved_attributes);
894 atexit(reset_input_mode);
895
896 /* Set the funny terminal modes. */
897 tcgetattr(STDIN_FILENO, &tattr);
898 tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
899 tattr.c_cc[VMIN] = 1;
900 tattr.c_cc[VTIME] = 0;
901 tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr);
902}
903
904int main(int argc, const char *argv[])
905{
906 uint32_t buf[128 * 1024];
907 uint32_t filter;
908 int length, retval, view;
909 int fd = -1;
910 FILE *output = NULL, *input = NULL;
911 poptContext con;
912 char c;
913 struct pollfd pollfds[2];
914
915 sys_sigint_handler = signal(SIGINT, sigint_handler);
916
917 con = poptGetContext(NULL, argc, argv, options, 0);
918 retval = poptGetNextOpt(con);
919 if (retval < -1) {
920 poptPrintUsage(con, stdout, 0);
921 return -1;
922 }
923
924 if (option_version) {
925 printf("dump tool for nosy sniffer, version %s\n", VERSION);
926 return 0;
927 }
928
929 if (__BYTE_ORDER != __LITTLE_ENDIAN)
930 fprintf(stderr, "warning: nosy has only been tested on little "
931 "endian machines\n");
932
933 if (option_input != NULL) {
934 input = fopen(option_input, "r");
935 if (input == NULL) {
936 fprintf(stderr, "Could not open %s, %m\n", option_input);
937 return -1;
938 }
939 } else {
940 fd = open(option_nosy_device, O_RDWR);
941 if (fd < 0) {
942 fprintf(stderr, "Could not open %s, %m\n", option_nosy_device);
943 return -1;
944 }
945 set_input_mode();
946 }
947
948 if (strcmp(option_view, "transaction") == 0)
949 view = VIEW_TRANSACTION;
950 else if (strcmp(option_view, "stats") == 0)
951 view = VIEW_STATS;
952 else
953 view = VIEW_PACKET;
954
955 if (option_output) {
956 output = fopen(option_output, "w");
957 if (output == NULL) {
958 fprintf(stderr, "Could not open %s, %m\n", option_output);
959 return -1;
960 }
961 }
962
963 setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
964
965 filter = ~0;
966 if (!option_iso)
967 filter &= ~(1 << TCODE_STREAM_DATA);
968 if (!option_cycle_start)
969 filter &= ~(1 << TCODE_CYCLE_START);
970 if (view == VIEW_STATS)
971 filter = ~(1 << TCODE_CYCLE_START);
972
973 ioctl(fd, NOSY_IOC_FILTER, filter);
974
975 ioctl(fd, NOSY_IOC_START);
976
977 pollfds[0].fd = fd;
978 pollfds[0].events = POLLIN;
979 pollfds[1].fd = STDIN_FILENO;
980 pollfds[1].events = POLLIN;
981
982 while (run) {
983 if (input != NULL) {
984 if (fread(&length, sizeof length, 1, input) != 1)
985 return 0;
986 fread(buf, 1, length, input);
987 } else {
988 poll(pollfds, 2, -1);
989 if (pollfds[1].revents) {
990 read(STDIN_FILENO, &c, sizeof c);
991 switch (c) {
992 case 'q':
993 if (output != NULL)
994 fclose(output);
995 return 0;
996 }
997 }
998
999 if (pollfds[0].revents)
1000 length = read(fd, buf, sizeof buf);
1001 else
1002 continue;
1003 }
1004
1005 if (output != NULL) {
1006 fwrite(&length, sizeof length, 1, output);
1007 fwrite(buf, 1, length, output);
1008 }
1009
1010 switch (view) {
1011 case VIEW_TRANSACTION:
1012 handle_packet(buf, length);
1013 break;
1014 case VIEW_PACKET:
1015 print_packet(buf, length);
1016 break;
1017 case VIEW_STATS:
1018 print_stats(buf, length);
1019 break;
1020 }
1021 }
1022
1023 if (output != NULL)
1024 fclose(output);
1025
1026 close(fd);
1027
1028 poptFreeContext(con);
1029
1030 return 0;
1031}
diff --git a/tools/firewire/nosy-dump.h b/tools/firewire/nosy-dump.h
new file mode 100644
index 000000000000..3a4b5b33ba5d
--- /dev/null
+++ b/tools/firewire/nosy-dump.h
@@ -0,0 +1,173 @@
1#ifndef __nosy_dump_h__
2#define __nosy_dump_h__
3
4#define array_length(array) (sizeof(array) / sizeof(array[0]))
5
6#define ACK_NO_ACK 0x0
7#define ACK_DONE(a) ((a >> 2) == 0)
8#define ACK_BUSY(a) ((a >> 2) == 1)
9#define ACK_ERROR(a) ((a >> 2) == 3)
10
11#include <stdint.h>
12
13struct phy_packet {
14 uint32_t timestamp;
15 union {
16 struct {
17 uint32_t zero:24;
18 uint32_t phy_id:6;
19 uint32_t identifier:2;
20 } common, link_on;
21
22 struct {
23 uint32_t zero:16;
24 uint32_t gap_count:6;
25 uint32_t set_gap_count:1;
26 uint32_t set_root:1;
27 uint32_t root_id:6;
28 uint32_t identifier:2;
29 } phy_config;
30
31 struct {
32 uint32_t more_packets:1;
33 uint32_t initiated_reset:1;
34 uint32_t port2:2;
35 uint32_t port1:2;
36 uint32_t port0:2;
37 uint32_t power_class:3;
38 uint32_t contender:1;
39 uint32_t phy_delay:2;
40 uint32_t phy_speed:2;
41 uint32_t gap_count:6;
42 uint32_t link_active:1;
43 uint32_t extended:1;
44 uint32_t phy_id:6;
45 uint32_t identifier:2;
46 } self_id;
47
48 struct {
49 uint32_t more_packets:1;
50 uint32_t reserved1:1;
51 uint32_t porth:2;
52 uint32_t portg:2;
53 uint32_t portf:2;
54 uint32_t porte:2;
55 uint32_t portd:2;
56 uint32_t portc:2;
57 uint32_t portb:2;
58 uint32_t porta:2;
59 uint32_t reserved0:2;
60 uint32_t sequence:3;
61 uint32_t extended:1;
62 uint32_t phy_id:6;
63 uint32_t identifier:2;
64 } ext_self_id;
65 };
66 uint32_t inverted;
67 uint32_t ack;
68};
69
70#define TCODE_PHY_PACKET 0x10
71
72#define PHY_PACKET_CONFIGURATION 0x00
73#define PHY_PACKET_LINK_ON 0x01
74#define PHY_PACKET_SELF_ID 0x02
75
76struct link_packet {
77 uint32_t timestamp;
78 union {
79 struct {
80 uint32_t priority:4;
81 uint32_t tcode:4;
82 uint32_t rt:2;
83 uint32_t tlabel:6;
84 uint32_t destination:16;
85
86 uint32_t offset_high:16;
87 uint32_t source:16;
88
89 uint32_t offset_low;
90 } common;
91
92 struct {
93 uint32_t common[3];
94 uint32_t crc;
95 } read_quadlet;
96
97 struct {
98 uint32_t common[3];
99 uint32_t data;
100 uint32_t crc;
101 } read_quadlet_response;
102
103 struct {
104 uint32_t common[3];
105 uint32_t extended_tcode:16;
106 uint32_t data_length:16;
107 uint32_t crc;
108 } read_block;
109
110 struct {
111 uint32_t common[3];
112 uint32_t extended_tcode:16;
113 uint32_t data_length:16;
114 uint32_t crc;
115 uint32_t data[0];
116 /* crc and ack follows. */
117 } read_block_response;
118
119 struct {
120 uint32_t common[3];
121 uint32_t data;
122 uint32_t crc;
123 } write_quadlet;
124
125 struct {
126 uint32_t common[3];
127 uint32_t extended_tcode:16;
128 uint32_t data_length:16;
129 uint32_t crc;
130 uint32_t data[0];
131 /* crc and ack follows. */
132 } write_block;
133
134 struct {
135 uint32_t common[3];
136 uint32_t crc;
137 } write_response;
138
139 struct {
140 uint32_t common[3];
141 uint32_t data;
142 uint32_t crc;
143 } cycle_start;
144
145 struct {
146 uint32_t sy:4;
147 uint32_t tcode:4;
148 uint32_t channel:6;
149 uint32_t tag:2;
150 uint32_t data_length:16;
151
152 uint32_t crc;
153 } iso_data;
154 };
155};
156
157struct subaction {
158 uint32_t ack;
159 size_t length;
160 struct list link;
161 struct link_packet packet;
162};
163
164struct link_transaction {
165 int request_node, response_node, tlabel;
166 struct subaction *request, *response;
167 struct list request_list, response_list;
168 struct list link;
169};
170
171int decode_fcp(struct link_transaction *t);
172
173#endif /* __nosy_dump_h__ */
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore
index e1d60d780784..cb43289e447f 100644
--- a/tools/perf/.gitignore
+++ b/tools/perf/.gitignore
@@ -18,3 +18,5 @@ perf-archive
18tags 18tags
19TAGS 19TAGS
20cscope* 20cscope*
21config.mak
22config.mak.autogen
diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt
index 5d1a9500277f..c1057701a7dc 100644
--- a/tools/perf/Documentation/perf-buildid-cache.txt
+++ b/tools/perf/Documentation/perf-buildid-cache.txt
@@ -12,9 +12,9 @@ SYNOPSIS
12 12
13DESCRIPTION 13DESCRIPTION
14----------- 14-----------
15This command manages the build-id cache. It can add and remove files to the 15This command manages the build-id cache. It can add and remove files to/from
16cache. In the future it should as well purge older entries, set upper limits 16the cache. In the future it should as well purge older entries, set upper
17for the space used by the cache, etc. 17limits for the space used by the cache, etc.
18 18
19OPTIONS 19OPTIONS
20------- 20-------
@@ -23,7 +23,7 @@ OPTIONS
23 Add specified file to the cache. 23 Add specified file to the cache.
24-r:: 24-r::
25--remove=:: 25--remove=::
26 Remove specified file to the cache. 26 Remove specified file from the cache.
27-v:: 27-v::
28--verbose:: 28--verbose::
29 Be more verbose. 29 Be more verbose.
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 94a258c96a44..27d52dae5a43 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -31,6 +31,10 @@ OPTIONS
31--vmlinux=PATH:: 31--vmlinux=PATH::
32 Specify vmlinux path which has debuginfo (Dwarf binary). 32 Specify vmlinux path which has debuginfo (Dwarf binary).
33 33
34-s::
35--source=PATH::
36 Specify path to kernel source.
37
34-v:: 38-v::
35--verbose:: 39--verbose::
36 Be more verbose (show parsed arguments, etc). 40 Be more verbose (show parsed arguments, etc).
@@ -90,8 +94,8 @@ Each probe argument follows below syntax.
90 94
91 [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE] 95 [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
92 96
93'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'.) 97'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'.)
94'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. 98'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.
95 99
96LINE SYNTAX 100LINE SYNTAX
97----------- 101-----------
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index 34e255fc3e2f..3ee27dccfde9 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -103,6 +103,19 @@ OPTIONS
103--raw-samples:: 103--raw-samples::
104Collect raw sample records from all opened counters (default for tracepoint counters). 104Collect raw sample records from all opened counters (default for tracepoint counters).
105 105
106-C::
107--cpu::
108Collect samples only on the list of cpus provided. Multiple CPUs can be provided as a
109comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
110In per-thread mode with inheritance mode on (default), samples are captured only when
111the thread executes on the designated CPUs. Default is to monitor all CPUs.
112
113-N::
114--no-buildid-cache::
115Do not update the builid cache. This saves some overhead in situations
116where the information in the perf.data file (which includes buildids)
117is sufficient.
118
106SEE ALSO 119SEE ALSO
107-------- 120--------
108linkperf:perf-stat[1], linkperf:perf-list[1] 121linkperf:perf-stat[1], linkperf:perf-list[1]
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt
index 909fa766fa1c..4b3a2d46b437 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -46,6 +46,13 @@ OPTIONS
46-B:: 46-B::
47 print large numbers with thousands' separators according to locale 47 print large numbers with thousands' separators according to locale
48 48
49-C::
50--cpu=::
51Count only on the list of cpus provided. Multiple CPUs can be provided as a
52comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
53In per-thread mode, this option is ignored. The -a option is still necessary
54to activate system-wide monitoring. Default is to count on all CPUs.
55
49EXAMPLES 56EXAMPLES
50-------- 57--------
51 58
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 785b9fc32a46..1f9687663f2a 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -25,9 +25,11 @@ OPTIONS
25--count=<count>:: 25--count=<count>::
26 Event period to sample. 26 Event period to sample.
27 27
28-C <cpu>:: 28-C <cpu-list>::
29--CPU=<cpu>:: 29--cpu=<cpu>::
30 CPU to profile. 30Monitor only on the list of cpus provided. Multiple CPUs can be provided as a
31comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
32Default is to monitor all CPUS.
31 33
32-d <seconds>:: 34-d <seconds>::
33--delay=<seconds>:: 35--delay=<seconds>::
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
new file mode 100644
index 000000000000..8c7fc0c8f0b8
--- /dev/null
+++ b/tools/perf/MANIFEST
@@ -0,0 +1,12 @@
1tools/perf
2include/linux/perf_event.h
3include/linux/rbtree.h
4include/linux/list.h
5include/linux/hash.h
6include/linux/stringify.h
7lib/rbtree.c
8include/linux/swab.h
9arch/*/include/asm/unistd*.h
10include/linux/poison.h
11include/linux/magic.h
12include/linux/hw_breakpoint.h
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 3d8f31ed771d..41abb90df50d 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -157,9 +157,8 @@ all::
157# 157#
158# Define NO_DWARF if you do not want debug-info analysis feature at all. 158# Define NO_DWARF if you do not want debug-info analysis feature at all.
159 159
160$(shell sh -c 'mkdir -p $(OUTPUT)scripts/python/Perf-Trace-Util/' 2> /dev/null) 160$(shell sh -c 'mkdir -p $(OUTPUT)scripts/{perl,python}/Perf-Trace-Util/' 2> /dev/null)
161$(shell sh -c 'mkdir -p $(OUTPUT)scripts/perl/Perf-Trace-Util/' 2> /dev/null) 161$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui/browsers,scripting-engines}/' 2> /dev/null)
162$(shell sh -c 'mkdir -p $(OUTPUT)util/scripting-engines/' 2> /dev/null)
163$(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null) 162$(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null)
164 163
165$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE 164$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
@@ -285,14 +284,10 @@ else
285 QUIET_STDERR = ">/dev/null 2>&1" 284 QUIET_STDERR = ">/dev/null 2>&1"
286endif 285endif
287 286
288BITBUCKET = "/dev/null" 287-include feature-tests.mak
289 288
290ifneq ($(shell sh -c "(echo '\#include <stdio.h>'; echo 'int main(void) { return puts(\"hi\"); }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) "$(QUIET_STDERR)" && echo y"), y) 289ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y)
291 BITBUCKET = .perf.dev.null 290 CFLAGS := $(CFLAGS) -fstack-protector-all
292endif
293
294ifeq ($(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)
295 CFLAGS := $(CFLAGS) -fstack-protector-all
296endif 291endif
297 292
298 293
@@ -508,7 +503,8 @@ PERFLIBS = $(LIB_FILE)
508-include config.mak 503-include config.mak
509 504
510ifndef NO_DWARF 505ifndef NO_DWARF
511ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo '\#include <version.h>'; 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) 506FLAGS_DWARF=$(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS)
507ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y)
512 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); 508 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);
513 NO_DWARF := 1 509 NO_DWARF := 1
514endif # Dwarf support 510endif # Dwarf support
@@ -536,16 +532,18 @@ ifneq ($(OUTPUT),)
536 BASIC_CFLAGS += -I$(OUTPUT) 532 BASIC_CFLAGS += -I$(OUTPUT)
537endif 533endif
538 534
539ifeq ($(shell sh -c "(echo '\#include <libelf.h>'; 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) 535FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS)
540ifneq ($(shell sh -c "(echo '\#include <gnu/libc-version.h>'; 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) 536ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y)
541 msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); 537 FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS)
538 ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y)
539 msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static);
540 else
541 msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel);
542 endif
542endif 543endif
543 544
544 ifneq ($(shell sh -c "(echo '\#include <libelf.h>'; 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) 545ifneq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y)
545 BASIC_CFLAGS += -DLIBELF_NO_MMAP 546 BASIC_CFLAGS += -DLIBELF_NO_MMAP
546 endif
547else
548 msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]);
549endif 547endif
550 548
551ifndef NO_DWARF 549ifndef NO_DWARF
@@ -561,69 +559,93 @@ endif # NO_DWARF
561ifdef NO_NEWT 559ifdef NO_NEWT
562 BASIC_CFLAGS += -DNO_NEWT_SUPPORT 560 BASIC_CFLAGS += -DNO_NEWT_SUPPORT
563else 561else
564ifneq ($(shell sh -c "(echo '\#include <newt.h>'; 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) 562 FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt
565 msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); 563 ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y)
566 BASIC_CFLAGS += -DNO_NEWT_SUPPORT 564 msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev);
567else 565 BASIC_CFLAGS += -DNO_NEWT_SUPPORT
568 # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h 566 else
569 BASIC_CFLAGS += -I/usr/include/slang 567 # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
570 EXTLIBS += -lnewt -lslang 568 BASIC_CFLAGS += -I/usr/include/slang
571 LIB_OBJS += $(OUTPUT)util/newt.o 569 EXTLIBS += -lnewt -lslang
572endif 570 LIB_OBJS += $(OUTPUT)util/ui/setup.o
573endif # NO_NEWT 571 LIB_OBJS += $(OUTPUT)util/ui/browser.o
574 572 LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o
575ifndef NO_LIBPERL 573 LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o
576PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null` 574 LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o
577PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` 575 LIB_OBJS += $(OUTPUT)util/ui/helpline.o
576 LIB_OBJS += $(OUTPUT)util/ui/progress.o
577 LIB_OBJS += $(OUTPUT)util/ui/util.o
578 LIB_H += util/ui/browser.h
579 LIB_H += util/ui/browsers/map.h
580 LIB_H += util/ui/helpline.h
581 LIB_H += util/ui/libslang.h
582 LIB_H += util/ui/progress.h
583 LIB_H += util/ui/util.h
584 endif
578endif 585endif
579 586
580ifneq ($(shell sh -c "(echo '\#include <EXTERN.h>'; echo '\#include <perl.h>'; 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) 587ifdef NO_LIBPERL
581 BASIC_CFLAGS += -DNO_LIBPERL 588 BASIC_CFLAGS += -DNO_LIBPERL
582else 589else
583 ALL_LDFLAGS += $(PERL_EMBED_LDOPTS) 590 PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null`
584 LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o 591 PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null`
585 LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o 592 FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
586endif
587 593
588ifndef NO_LIBPYTHON 594 ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y)
589PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null` 595 BASIC_CFLAGS += -DNO_LIBPERL
590PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` 596 else
597 ALL_LDFLAGS += $(PERL_EMBED_LDOPTS)
598 LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o
599 LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o
600 endif
591endif 601endif
592 602
593ifneq ($(shell sh -c "(echo '\#include <Python.h>'; 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) 603ifdef NO_LIBPYTHON
594 BASIC_CFLAGS += -DNO_LIBPYTHON 604 BASIC_CFLAGS += -DNO_LIBPYTHON
595else 605else
596 ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS) 606 PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null`
597 LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o 607 PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null`
598 LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o 608 FLAGS_PYTHON_EMBED=$(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS)
609 ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y)
610 BASIC_CFLAGS += -DNO_LIBPYTHON
611 else
612 ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS)
613 LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o
614 LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o
615 endif
599endif 616endif
600 617
601ifdef NO_DEMANGLE 618ifdef NO_DEMANGLE
602 BASIC_CFLAGS += -DNO_DEMANGLE 619 BASIC_CFLAGS += -DNO_DEMANGLE
603else ifdef HAVE_CPLUS_DEMANGLE
604 EXTLIBS += -liberty
605 BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
606else 620else
607 has_bfd := $(shell sh -c "(echo '\#include <bfd.h>'; 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") 621 ifdef HAVE_CPLUS_DEMANGLE
608 622 EXTLIBS += -liberty
609 ifeq ($(has_bfd),y) 623 BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
610 EXTLIBS += -lbfd 624 else
611 else 625 FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd
612 has_bfd_iberty := $(shell sh -c "(echo '\#include <bfd.h>'; 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") 626 has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD))
613 ifeq ($(has_bfd_iberty),y) 627 ifeq ($(has_bfd),y)
614 EXTLIBS += -lbfd -liberty 628 EXTLIBS += -lbfd
615 else 629 else
616 has_bfd_iberty_z := $(shell sh -c "(echo '\#include <bfd.h>'; 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") 630 FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty
617 ifeq ($(has_bfd_iberty_z),y) 631 has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY))
618 EXTLIBS += -lbfd -liberty -lz 632 ifeq ($(has_bfd_iberty),y)
633 EXTLIBS += -lbfd -liberty
619 else 634 else
620 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") 635 FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz
621 ifeq ($(has_cplus_demangle),y) 636 has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z))
622 EXTLIBS += -liberty 637 ifeq ($(has_bfd_iberty_z),y)
623 BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE 638 EXTLIBS += -lbfd -liberty -lz
624 else 639 else
625 msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling) 640 FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty
626 BASIC_CFLAGS += -DNO_DEMANGLE 641 has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE))
642 ifeq ($(has_cplus_demangle),y)
643 EXTLIBS += -liberty
644 BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
645 else
646 msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling)
647 BASIC_CFLAGS += -DNO_DEMANGLE
648 endif
627 endif 649 endif
628 endif 650 endif
629 endif 651 endif
@@ -865,7 +887,7 @@ export TAR INSTALL DESTDIR SHELL_PATH
865 887
866SHELL = $(SHELL_PATH) 888SHELL = $(SHELL_PATH)
867 889
868all:: .perf.dev.null shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS 890all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS
869ifneq (,$X) 891ifneq (,$X)
870 $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';) 892 $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';)
871endif 893endif
@@ -956,7 +978,16 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS
956$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS 978$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
957 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< 979 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
958 980
959$(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS 981$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
982 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
983
984$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
985 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
986
987$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
988 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
989
990$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
960 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< 991 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
961 992
962$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS 993$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
@@ -1195,11 +1226,6 @@ clean:
1195.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS 1226.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS
1196.PHONY: .FORCE-PERF-BUILD-OPTIONS 1227.PHONY: .FORCE-PERF-BUILD-OPTIONS
1197 1228
1198.perf.dev.null:
1199 touch .perf.dev.null
1200
1201.INTERMEDIATE: .perf.dev.null
1202
1203### Make sure built-ins do not have dups and listed in perf.c 1229### Make sure built-ins do not have dups and listed in perf.c
1204# 1230#
1205check-builtins:: 1231check-builtins::
diff --git a/tools/perf/arch/arm/Makefile b/tools/perf/arch/arm/Makefile
new file mode 100644
index 000000000000..15130b50dfe3
--- /dev/null
+++ b/tools/perf/arch/arm/Makefile
@@ -0,0 +1,4 @@
1ifndef NO_DWARF
2PERF_HAVE_DWARF_REGS := 1
3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
4endif
diff --git a/tools/perf/arch/arm/util/dwarf-regs.c b/tools/perf/arch/arm/util/dwarf-regs.c
new file mode 100644
index 000000000000..fff6450c8c99
--- /dev/null
+++ b/tools/perf/arch/arm/util/dwarf-regs.c
@@ -0,0 +1,64 @@
1/*
2 * Mapping of DWARF debug register numbers into register names.
3 *
4 * Copyright (C) 2010 Will Deacon, ARM Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <libio.h>
12#include <dwarf-regs.h>
13
14struct pt_regs_dwarfnum {
15 const char *name;
16 unsigned int dwarfnum;
17};
18
19#define STR(s) #s
20#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
21#define GPR_DWARFNUM_NAME(num) \
22 {.name = STR(%r##num), .dwarfnum = num}
23#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
24
25/*
26 * Reference:
27 * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040a/IHI0040A_aadwarf.pdf
28 */
29static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
30 GPR_DWARFNUM_NAME(0),
31 GPR_DWARFNUM_NAME(1),
32 GPR_DWARFNUM_NAME(2),
33 GPR_DWARFNUM_NAME(3),
34 GPR_DWARFNUM_NAME(4),
35 GPR_DWARFNUM_NAME(5),
36 GPR_DWARFNUM_NAME(6),
37 GPR_DWARFNUM_NAME(7),
38 GPR_DWARFNUM_NAME(8),
39 GPR_DWARFNUM_NAME(9),
40 GPR_DWARFNUM_NAME(10),
41 REG_DWARFNUM_NAME("%fp", 11),
42 REG_DWARFNUM_NAME("%ip", 12),
43 REG_DWARFNUM_NAME("%sp", 13),
44 REG_DWARFNUM_NAME("%lr", 14),
45 REG_DWARFNUM_NAME("%pc", 15),
46 REG_DWARFNUM_END,
47};
48
49/**
50 * get_arch_regstr() - lookup register name from it's DWARF register number
51 * @n: the DWARF register number
52 *
53 * get_arch_regstr() returns the name of the register in struct
54 * regdwarfnum_table from it's DWARF register number. If the register is not
55 * found in the table, this returns NULL;
56 */
57const char *get_arch_regstr(unsigned int n)
58{
59 const struct pt_regs_dwarfnum *roff;
60 for (roff = regdwarfnum_table; roff->name != NULL; roff++)
61 if (roff->dwarfnum == n)
62 return roff->name;
63 return NULL;
64}
diff --git a/tools/perf/arch/sh/Makefile b/tools/perf/arch/sh/Makefile
new file mode 100644
index 000000000000..15130b50dfe3
--- /dev/null
+++ b/tools/perf/arch/sh/Makefile
@@ -0,0 +1,4 @@
1ifndef NO_DWARF
2PERF_HAVE_DWARF_REGS := 1
3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
4endif
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 000000000000..a11edb007a6c
--- /dev/null
+++ b/tools/perf/arch/sh/util/dwarf-regs.c
@@ -0,0 +1,55 @@
1/*
2 * Mapping of DWARF debug register numbers into register names.
3 *
4 * Copyright (C) 2010 Matt Fleming <matt@console-pimps.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 */
21
22#include <libio.h>
23#include <dwarf-regs.h>
24
25/*
26 * Generic dwarf analysis helpers
27 */
28
29#define SH_MAX_REGS 18
30const char *sh_regs_table[SH_MAX_REGS] = {
31 "r0",
32 "r1",
33 "r2",
34 "r3",
35 "r4",
36 "r5",
37 "r6",
38 "r7",
39 "r8",
40 "r9",
41 "r10",
42 "r11",
43 "r12",
44 "r13",
45 "r14",
46 "r15",
47 "pc",
48 "pr",
49};
50
51/* Return architecture dependent register string (for kprobe-tracer) */
52const char *get_arch_regstr(unsigned int n)
53{
54 return (n <= SH_MAX_REGS) ? sh_regs_table[n] : NULL;
55}
diff --git a/tools/perf/arch/sparc/Makefile b/tools/perf/arch/sparc/Makefile
new file mode 100644
index 000000000000..15130b50dfe3
--- /dev/null
+++ b/tools/perf/arch/sparc/Makefile
@@ -0,0 +1,4 @@
1ifndef NO_DWARF
2PERF_HAVE_DWARF_REGS := 1
3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
4endif
diff --git a/tools/perf/arch/sparc/util/dwarf-regs.c b/tools/perf/arch/sparc/util/dwarf-regs.c
new file mode 100644
index 000000000000..0ab88483720c
--- /dev/null
+++ b/tools/perf/arch/sparc/util/dwarf-regs.c
@@ -0,0 +1,43 @@
1/*
2 * Mapping of DWARF debug register numbers into register names.
3 *
4 * Copyright (C) 2010 David S. Miller <davem@davemloft.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <libio.h>
13#include <dwarf-regs.h>
14
15#define SPARC_MAX_REGS 96
16
17const char *sparc_regs_table[SPARC_MAX_REGS] = {
18 "%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7",
19 "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7",
20 "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7",
21 "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7",
22 "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
23 "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15",
24 "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23",
25 "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31",
26 "%f32", "%f33", "%f34", "%f35", "%f36", "%f37", "%f38", "%f39",
27 "%f40", "%f41", "%f42", "%f43", "%f44", "%f45", "%f46", "%f47",
28 "%f48", "%f49", "%f50", "%f51", "%f52", "%f53", "%f54", "%f55",
29 "%f56", "%f57", "%f58", "%f59", "%f60", "%f61", "%f62", "%f63",
30};
31
32/**
33 * get_arch_regstr() - lookup register name from it's DWARF register number
34 * @n: the DWARF register number
35 *
36 * get_arch_regstr() returns the name of the register in struct
37 * regdwarfnum_table from it's DWARF register number. If the register is not
38 * found in the table, this returns NULL;
39 */
40const char *get_arch_regstr(unsigned int n)
41{
42 return (n <= SPARC_MAX_REGS) ? sparc_regs_table[n] : NULL;
43}
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 96db5248e995..1478dc64bf15 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)
61static int process_sample_event(event_t *event, struct perf_session *session) 61static int process_sample_event(event_t *event, struct perf_session *session)
62{ 62{
63 struct addr_location al; 63 struct addr_location al;
64 struct sample_data data;
64 65
65 dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, 66 if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) {
66 event->ip.pid, event->ip.ip);
67
68 if (event__preprocess_sample(event, session, &al, NULL) < 0) {
69 pr_warning("problem processing %d event, skipping it.\n", 67 pr_warning("problem processing %d event, skipping it.\n",
70 event->header.type); 68 event->header.type);
71 return -1; 69 return -1;
@@ -287,7 +285,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
287 LIST_HEAD(head); 285 LIST_HEAD(head);
288 struct objdump_line *pos, *n; 286 struct objdump_line *pos, *n;
289 287
290 if (hist_entry__annotate(he, &head) < 0) 288 if (hist_entry__annotate(he, &head, 0) < 0)
291 return -1; 289 return -1;
292 290
293 if (full_paths) 291 if (full_paths)
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index f8e3d1852029..29ad20e67919 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)
78 struct str_node *pos; 78 struct str_node *pos;
79 char debugdir[PATH_MAX]; 79 char debugdir[PATH_MAX];
80 80
81 snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), 81 snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
82 DEBUG_CACHE_DIR);
83 82
84 if (add_name_list_str) { 83 if (add_name_list_str) {
85 list = strlist__new(true, add_name_list_str); 84 list = strlist__new(true, add_name_list_str);
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index a6e2fdc7a04e..fca1d4402910 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
35 struct addr_location al; 35 struct addr_location al;
36 struct sample_data data = { .period = 1, }; 36 struct sample_data data = { .period = 1, };
37 37
38 dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, 38 if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) {
39 event->ip.pid, event->ip.ip);
40
41 if (event__preprocess_sample(event, session, &al, NULL) < 0) {
42 pr_warning("problem processing %d event, skipping it.\n", 39 pr_warning("problem processing %d event, skipping it.\n",
43 event->header.type); 40 event->header.type);
44 return -1; 41 return -1;
@@ -47,8 +44,6 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi
47 if (al.filtered || al.sym == NULL) 44 if (al.filtered || al.sym == NULL)
48 return 0; 45 return 0;
49 46
50 event__parse_sample(event, session->sample_type, &data);
51
52 if (hists__add_entry(&session->hists, &al, data.period)) { 47 if (hists__add_entry(&session->hists, &al, data.period)) {
53 pr_warning("problem incrementing symbol period, skipping event\n"); 48 pr_warning("problem incrementing symbol period, skipping event\n");
54 return -1; 49 return -1;
@@ -185,8 +180,6 @@ static const struct option options[] = {
185 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), 180 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
186 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, 181 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
187 "load module symbols - WARNING: use only with -k and LIVE kernel"), 182 "load module symbols - WARNING: use only with -k and LIVE kernel"),
188 OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths,
189 "Don't shorten the pathnames taking into account the cwd"),
190 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", 183 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
191 "only consider symbols in these dsos"), 184 "only consider symbols in these dsos"),
192 OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", 185 OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index e4a4da32a568..199d5e19554f 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -182,6 +182,8 @@ static const struct option options[] = {
182 "Show source code lines.", opt_show_lines), 182 "Show source code lines.", opt_show_lines),
183 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, 183 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
184 "file", "vmlinux pathname"), 184 "file", "vmlinux pathname"),
185 OPT_STRING('s', "source", &symbol_conf.source_prefix,
186 "directory", "path to kernel source"),
185#endif 187#endif
186 OPT__DRY_RUN(&probe_event_dry_run), 188 OPT__DRY_RUN(&probe_event_dry_run),
187 OPT_INTEGER('\0', "max-probes", &params.max_probe_points, 189 OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
@@ -265,4 +267,3 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
265 } 267 }
266 return 0; 268 return 0;
267} 269}
268
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 9bc89050e6f8..ff77b805de71 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -49,7 +49,6 @@ static int group = 0;
49static int realtime_prio = 0; 49static int realtime_prio = 0;
50static bool raw_samples = false; 50static bool raw_samples = false;
51static bool system_wide = false; 51static bool system_wide = false;
52static int profile_cpu = -1;
53static pid_t target_pid = -1; 52static pid_t target_pid = -1;
54static pid_t target_tid = -1; 53static pid_t target_tid = -1;
55static pid_t *all_tids = NULL; 54static pid_t *all_tids = NULL;
@@ -61,6 +60,7 @@ static bool call_graph = false;
61static bool inherit_stat = false; 60static bool inherit_stat = false;
62static bool no_samples = false; 61static bool no_samples = false;
63static bool sample_address = false; 62static bool sample_address = false;
63static bool no_buildid = false;
64 64
65static long samples = 0; 65static long samples = 0;
66static u64 bytes_written = 0; 66static u64 bytes_written = 0;
@@ -74,6 +74,7 @@ static int file_new = 1;
74static off_t post_processing_offset; 74static off_t post_processing_offset;
75 75
76static struct perf_session *session; 76static struct perf_session *session;
77static const char *cpu_list;
77 78
78struct mmap_data { 79struct mmap_data {
79 int counter; 80 int counter;
@@ -193,7 +194,7 @@ static void sig_handler(int sig)
193 194
194static void sig_atexit(void) 195static void sig_atexit(void)
195{ 196{
196 if (child_pid != -1) 197 if (child_pid > 0)
197 kill(child_pid, SIGTERM); 198 kill(child_pid, SIGTERM);
198 199
199 if (signr == -1) 200 if (signr == -1)
@@ -268,12 +269,17 @@ static void create_counter(int counter, int cpu)
268 if (inherit_stat) 269 if (inherit_stat)
269 attr->inherit_stat = 1; 270 attr->inherit_stat = 1;
270 271
271 if (sample_address) 272 if (sample_address) {
272 attr->sample_type |= PERF_SAMPLE_ADDR; 273 attr->sample_type |= PERF_SAMPLE_ADDR;
274 attr->mmap_data = track;
275 }
273 276
274 if (call_graph) 277 if (call_graph)
275 attr->sample_type |= PERF_SAMPLE_CALLCHAIN; 278 attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
276 279
280 if (system_wide)
281 attr->sample_type |= PERF_SAMPLE_CPU;
282
277 if (raw_samples) { 283 if (raw_samples) {
278 attr->sample_type |= PERF_SAMPLE_TIME; 284 attr->sample_type |= PERF_SAMPLE_TIME;
279 attr->sample_type |= PERF_SAMPLE_RAW; 285 attr->sample_type |= PERF_SAMPLE_RAW;
@@ -300,7 +306,7 @@ try_again:
300 die("Permission error - are you root?\n" 306 die("Permission error - are you root?\n"
301 "\t Consider tweaking" 307 "\t Consider tweaking"
302 " /proc/sys/kernel/perf_event_paranoid.\n"); 308 " /proc/sys/kernel/perf_event_paranoid.\n");
303 else if (err == ENODEV && profile_cpu != -1) { 309 else if (err == ENODEV && cpu_list) {
304 die("No such device - did you specify" 310 die("No such device - did you specify"
305 " an out-of-range profile CPU?\n"); 311 " an out-of-range profile CPU?\n");
306 } 312 }
@@ -433,14 +439,14 @@ static void atexit_header(void)
433 439
434 process_buildids(); 440 process_buildids();
435 perf_header__write(&session->header, output, true); 441 perf_header__write(&session->header, output, true);
442 perf_session__delete(session);
443 symbol__exit();
436 } 444 }
437} 445}
438 446
439static void event__synthesize_guest_os(struct machine *machine, void *data) 447static void event__synthesize_guest_os(struct machine *machine, void *data)
440{ 448{
441 int err; 449 int err;
442 char *guest_kallsyms;
443 char path[PATH_MAX];
444 struct perf_session *psession = data; 450 struct perf_session *psession = data;
445 451
446 if (machine__is_host(machine)) 452 if (machine__is_host(machine))
@@ -460,13 +466,6 @@ static void event__synthesize_guest_os(struct machine *machine, void *data)
460 pr_err("Couldn't record guest kernel [%d]'s reference" 466 pr_err("Couldn't record guest kernel [%d]'s reference"
461 " relocation symbol.\n", machine->pid); 467 " relocation symbol.\n", machine->pid);
462 468
463 if (machine__is_default_guest(machine))
464 guest_kallsyms = (char *) symbol_conf.default_guest_kallsyms;
465 else {
466 sprintf(path, "%s/proc/kallsyms", machine->root_dir);
467 guest_kallsyms = path;
468 }
469
470 /* 469 /*
471 * We use _stext for guest kernel because guest kernel's /proc/kallsyms 470 * We use _stext for guest kernel because guest kernel's /proc/kallsyms
472 * have no _text sometimes. 471 * have no _text sometimes.
@@ -503,7 +502,6 @@ static int __cmd_record(int argc, const char **argv)
503{ 502{
504 int i, counter; 503 int i, counter;
505 struct stat st; 504 struct stat st;
506 pid_t pid = 0;
507 int flags; 505 int flags;
508 int err; 506 int err;
509 unsigned long waking = 0; 507 unsigned long waking = 0;
@@ -562,17 +560,20 @@ static int __cmd_record(int argc, const char **argv)
562 if (!file_new) { 560 if (!file_new) {
563 err = perf_header__read(session, output); 561 err = perf_header__read(session, output);
564 if (err < 0) 562 if (err < 0)
565 return err; 563 goto out_delete_session;
566 } 564 }
567 565
568 if (have_tracepoints(attrs, nr_counters)) 566 if (have_tracepoints(attrs, nr_counters))
569 perf_header__set_feat(&session->header, HEADER_TRACE_INFO); 567 perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
570 568
569 /*
570 * perf_session__delete(session) will be called at atexit_header()
571 */
571 atexit(atexit_header); 572 atexit(atexit_header);
572 573
573 if (forks) { 574 if (forks) {
574 child_pid = fork(); 575 child_pid = fork();
575 if (pid < 0) { 576 if (child_pid < 0) {
576 perror("failed to fork"); 577 perror("failed to fork");
577 exit(-1); 578 exit(-1);
578 } 579 }
@@ -623,10 +624,15 @@ static int __cmd_record(int argc, const char **argv)
623 close(child_ready_pipe[0]); 624 close(child_ready_pipe[0]);
624 } 625 }
625 626
626 if ((!system_wide && no_inherit) || profile_cpu != -1) { 627 nr_cpus = read_cpu_map(cpu_list);
627 open_counters(profile_cpu); 628 if (nr_cpus < 1) {
629 perror("failed to collect number of CPUs\n");
630 return -1;
631 }
632
633 if (!system_wide && no_inherit && !cpu_list) {
634 open_counters(-1);
628 } else { 635 } else {
629 nr_cpus = read_cpu_map();
630 for (i = 0; i < nr_cpus; i++) 636 for (i = 0; i < nr_cpus; i++)
631 open_counters(cpumap[i]); 637 open_counters(cpumap[i]);
632 } 638 }
@@ -705,7 +711,7 @@ static int __cmd_record(int argc, const char **argv)
705 if (perf_guest) 711 if (perf_guest)
706 perf_session__process_machines(session, event__synthesize_guest_os); 712 perf_session__process_machines(session, event__synthesize_guest_os);
707 713
708 if (!system_wide && profile_cpu == -1) 714 if (!system_wide)
709 event__synthesize_thread(target_tid, process_synthesized_event, 715 event__synthesize_thread(target_tid, process_synthesized_event,
710 session); 716 session);
711 else 717 else
@@ -767,6 +773,10 @@ static int __cmd_record(int argc, const char **argv)
767 bytes_written / 24); 773 bytes_written / 24);
768 774
769 return 0; 775 return 0;
776
777out_delete_session:
778 perf_session__delete(session);
779 return err;
770} 780}
771 781
772static const char * const record_usage[] = { 782static const char * const record_usage[] = {
@@ -795,8 +805,8 @@ static const struct option options[] = {
795 "system-wide collection from all CPUs"), 805 "system-wide collection from all CPUs"),
796 OPT_BOOLEAN('A', "append", &append_file, 806 OPT_BOOLEAN('A', "append", &append_file,
797 "append to the output file to do incremental profiling"), 807 "append to the output file to do incremental profiling"),
798 OPT_INTEGER('C', "profile_cpu", &profile_cpu, 808 OPT_STRING('C', "cpu", &cpu_list, "cpu",
799 "CPU to profile on"), 809 "list of cpus to monitor"),
800 OPT_BOOLEAN('f', "force", &force, 810 OPT_BOOLEAN('f', "force", &force,
801 "overwrite existing data file (deprecated)"), 811 "overwrite existing data file (deprecated)"),
802 OPT_U64('c', "count", &user_interval, "event period to sample"), 812 OPT_U64('c', "count", &user_interval, "event period to sample"),
@@ -816,17 +826,19 @@ static const struct option options[] = {
816 "Sample addresses"), 826 "Sample addresses"),
817 OPT_BOOLEAN('n', "no-samples", &no_samples, 827 OPT_BOOLEAN('n', "no-samples", &no_samples,
818 "don't sample"), 828 "don't sample"),
829 OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid,
830 "do not update the buildid cache"),
819 OPT_END() 831 OPT_END()
820}; 832};
821 833
822int cmd_record(int argc, const char **argv, const char *prefix __used) 834int cmd_record(int argc, const char **argv, const char *prefix __used)
823{ 835{
824 int i,j; 836 int i, j, err = -ENOMEM;
825 837
826 argc = parse_options(argc, argv, options, record_usage, 838 argc = parse_options(argc, argv, options, record_usage,
827 PARSE_OPT_STOP_AT_NON_OPTION); 839 PARSE_OPT_STOP_AT_NON_OPTION);
828 if (!argc && target_pid == -1 && target_tid == -1 && 840 if (!argc && target_pid == -1 && target_tid == -1 &&
829 !system_wide && profile_cpu == -1) 841 !system_wide && !cpu_list)
830 usage_with_options(record_usage, options); 842 usage_with_options(record_usage, options);
831 843
832 if (force && append_file) { 844 if (force && append_file) {
@@ -840,6 +852,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
840 } 852 }
841 853
842 symbol__init(); 854 symbol__init();
855 if (no_buildid)
856 disable_buildid_cache();
843 857
844 if (!nr_counters) { 858 if (!nr_counters) {
845 nr_counters = 1; 859 nr_counters = 1;
@@ -858,7 +872,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
858 } else { 872 } else {
859 all_tids=malloc(sizeof(pid_t)); 873 all_tids=malloc(sizeof(pid_t));
860 if (!all_tids) 874 if (!all_tids)
861 return -ENOMEM; 875 goto out_symbol_exit;
862 876
863 all_tids[0] = target_tid; 877 all_tids[0] = target_tid;
864 thread_num = 1; 878 thread_num = 1;
@@ -868,13 +882,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
868 for (j = 0; j < MAX_COUNTERS; j++) { 882 for (j = 0; j < MAX_COUNTERS; j++) {
869 fd[i][j] = malloc(sizeof(int)*thread_num); 883 fd[i][j] = malloc(sizeof(int)*thread_num);
870 if (!fd[i][j]) 884 if (!fd[i][j])
871 return -ENOMEM; 885 goto out_free_fd;
872 } 886 }
873 } 887 }
874 event_array = malloc( 888 event_array = malloc(
875 sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); 889 sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num);
876 if (!event_array) 890 if (!event_array)
877 return -ENOMEM; 891 goto out_free_fd;
878 892
879 if (user_interval != ULLONG_MAX) 893 if (user_interval != ULLONG_MAX)
880 default_interval = user_interval; 894 default_interval = user_interval;
@@ -890,8 +904,22 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
890 default_interval = freq; 904 default_interval = freq;
891 } else { 905 } else {
892 fprintf(stderr, "frequency and count are zero, aborting\n"); 906 fprintf(stderr, "frequency and count are zero, aborting\n");
893 exit(EXIT_FAILURE); 907 err = -EINVAL;
908 goto out_free_event_array;
894 } 909 }
895 910
896 return __cmd_record(argc, argv); 911 err = __cmd_record(argc, argv);
912
913out_free_event_array:
914 free(event_array);
915out_free_fd:
916 for (i = 0; i < MAX_NR_CPUS; i++) {
917 for (j = 0; j < MAX_COUNTERS; j++)
918 free(fd[i][j]);
919 }
920 free(all_tids);
921 all_tids = NULL;
922out_symbol_exit:
923 symbol__exit();
924 return err;
897} 925}
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 359205782964..55fc1f46892a 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -107,7 +107,7 @@ static int perf_session__add_hist_entry(struct perf_session *self,
107 goto out_free_syms; 107 goto out_free_syms;
108 err = 0; 108 err = 0;
109 if (symbol_conf.use_callchain) { 109 if (symbol_conf.use_callchain) {
110 err = append_chain(he->callchain, data->callchain, syms); 110 err = append_chain(he->callchain, data->callchain, syms, data->period);
111 if (err) 111 if (err)
112 goto out_free_syms; 112 goto out_free_syms;
113 } 113 }
@@ -155,30 +155,7 @@ static int process_sample_event(event_t *event, struct perf_session *session)
155 struct addr_location al; 155 struct addr_location al;
156 struct perf_event_attr *attr; 156 struct perf_event_attr *attr;
157 157
158 event__parse_sample(event, session->sample_type, &data); 158 if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) {
159
160 dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc,
161 data.pid, data.tid, data.ip, data.period);
162
163 if (session->sample_type & PERF_SAMPLE_CALLCHAIN) {
164 unsigned int i;
165
166 dump_printf("... chain: nr:%Lu\n", data.callchain->nr);
167
168 if (!ip_callchain__valid(data.callchain, event)) {
169 pr_debug("call-chain problem with event, "
170 "skipping it.\n");
171 return 0;
172 }
173
174 if (dump_trace) {
175 for (i = 0; i < data.callchain->nr; i++)
176 dump_printf("..... %2d: %016Lx\n",
177 i, data.callchain->ips[i]);
178 }
179 }
180
181 if (event__preprocess_sample(event, session, &al, NULL) < 0) {
182 fprintf(stderr, "problem processing %d event, skipping it.\n", 159 fprintf(stderr, "problem processing %d event, skipping it.\n",
183 event->header.type); 160 event->header.type);
184 return -1; 161 return -1;
@@ -371,7 +348,18 @@ static int __cmd_report(void)
371 hists__tty_browse_tree(&session->hists_tree, help); 348 hists__tty_browse_tree(&session->hists_tree, help);
372 349
373out_delete: 350out_delete:
374 perf_session__delete(session); 351 /*
352 * Speed up the exit process, for large files this can
353 * take quite a while.
354 *
355 * XXX Enable this when using valgrind or if we ever
356 * librarize this command.
357 *
358 * Also experiment with obstacks to see how much speed
359 * up we'll get here.
360 *
361 * perf_session__delete(session);
362 */
375 return ret; 363 return ret;
376} 364}
377 365
@@ -464,8 +452,6 @@ static const struct option options[] = {
464 "pretty printing style key: normal raw"), 452 "pretty printing style key: normal raw"),
465 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 453 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
466 "sort by key(s): pid, comm, dso, symbol, parent"), 454 "sort by key(s): pid, comm, dso, symbol, parent"),
467 OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths,
468 "Don't shorten the pathnames taking into account the cwd"),
469 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, 455 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
470 "Show sample percentage for different cpu modes"), 456 "Show sample percentage for different cpu modes"),
471 OPT_STRING('p', "parent", &parent_pattern, "regex", 457 OPT_STRING('p', "parent", &parent_pattern, "regex",
@@ -503,8 +489,24 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
503 * so don't allocate extra space that won't be used in the stdio 489 * so don't allocate extra space that won't be used in the stdio
504 * implementation. 490 * implementation.
505 */ 491 */
506 if (use_browser > 0) 492 if (use_browser > 0) {
507 symbol_conf.priv_size = sizeof(struct sym_priv); 493 symbol_conf.priv_size = sizeof(struct sym_priv);
494 /*
495 * For searching by name on the "Browse map details".
496 * providing it only in verbose mode not to bloat too
497 * much struct symbol.
498 */
499 if (verbose) {
500 /*
501 * XXX: Need to provide a less kludgy way to ask for
502 * more space per symbol, the u32 is for the index on
503 * the ui browser.
504 * See symbol__browser_index.
505 */
506 symbol_conf.priv_size += sizeof(u32);
507 symbol_conf.sort_by_name = true;
508 }
509 }
508 510
509 if (symbol__init() < 0) 511 if (symbol__init() < 0)
510 return -1; 512 return -1;
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index f67bce2a83b4..55f3b5dcc731 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -1645,6 +1645,7 @@ static struct perf_event_ops event_ops = {
1645 .sample = process_sample_event, 1645 .sample = process_sample_event,
1646 .comm = event__process_comm, 1646 .comm = event__process_comm,
1647 .lost = event__process_lost, 1647 .lost = event__process_lost,
1648 .fork = event__process_task,
1648 .ordered_samples = true, 1649 .ordered_samples = true,
1649}; 1650};
1650 1651
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 9a39ca3c3ac4..a6b4d44f9502 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -69,7 +69,7 @@ static struct perf_event_attr default_attrs[] = {
69}; 69};
70 70
71static bool system_wide = false; 71static bool system_wide = false;
72static unsigned int nr_cpus = 0; 72static int nr_cpus = 0;
73static int run_idx = 0; 73static int run_idx = 0;
74 74
75static int run_count = 1; 75static int run_count = 1;
@@ -82,6 +82,7 @@ static int thread_num = 0;
82static pid_t child_pid = -1; 82static pid_t child_pid = -1;
83static bool null_run = false; 83static bool null_run = false;
84static bool big_num = false; 84static bool big_num = false;
85static const char *cpu_list;
85 86
86 87
87static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; 88static int *fd[MAX_NR_CPUS][MAX_COUNTERS];
@@ -158,7 +159,7 @@ static int create_perf_stat_counter(int counter)
158 PERF_FORMAT_TOTAL_TIME_RUNNING; 159 PERF_FORMAT_TOTAL_TIME_RUNNING;
159 160
160 if (system_wide) { 161 if (system_wide) {
161 unsigned int cpu; 162 int cpu;
162 163
163 for (cpu = 0; cpu < nr_cpus; cpu++) { 164 for (cpu = 0; cpu < nr_cpus; cpu++) {
164 fd[cpu][counter][0] = sys_perf_event_open(attr, 165 fd[cpu][counter][0] = sys_perf_event_open(attr,
@@ -208,7 +209,7 @@ static inline int nsec_counter(int counter)
208static void read_counter(int counter) 209static void read_counter(int counter)
209{ 210{
210 u64 count[3], single_count[3]; 211 u64 count[3], single_count[3];
211 unsigned int cpu; 212 int cpu;
212 size_t res, nv; 213 size_t res, nv;
213 int scaled; 214 int scaled;
214 int i, thread; 215 int i, thread;
@@ -542,6 +543,8 @@ static const struct option options[] = {
542 "null run - dont start any counters"), 543 "null run - dont start any counters"),
543 OPT_BOOLEAN('B', "big-num", &big_num, 544 OPT_BOOLEAN('B', "big-num", &big_num,
544 "print large numbers with thousands\' separators"), 545 "print large numbers with thousands\' separators"),
546 OPT_STRING('C', "cpu", &cpu_list, "cpu",
547 "list of cpus to monitor in system-wide"),
545 OPT_END() 548 OPT_END()
546}; 549};
547 550
@@ -566,10 +569,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
566 } 569 }
567 570
568 if (system_wide) 571 if (system_wide)
569 nr_cpus = read_cpu_map(); 572 nr_cpus = read_cpu_map(cpu_list);
570 else 573 else
571 nr_cpus = 1; 574 nr_cpus = 1;
572 575
576 if (nr_cpus < 1)
577 usage_with_options(stat_usage, options);
578
573 if (target_pid != -1) { 579 if (target_pid != -1) {
574 target_tid = target_pid; 580 target_tid = target_pid;
575 thread_num = find_all_tid(target_pid, &all_tids); 581 thread_num = find_all_tid(target_pid, &all_tids);
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 5a52ed9fc10b..9bcc38f0b706 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -300,8 +300,9 @@ struct trace_entry {
300 300
301struct power_entry { 301struct power_entry {
302 struct trace_entry te; 302 struct trace_entry te;
303 s64 type; 303 u64 type;
304 s64 value; 304 u64 value;
305 u64 cpu_id;
305}; 306};
306 307
307#define TASK_COMM_LEN 16 308#define TASK_COMM_LEN 16
@@ -454,8 +455,8 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
454 if (p->current->state != TYPE_NONE) 455 if (p->current->state != TYPE_NONE)
455 pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp); 456 pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp);
456 457
457 p->current->state_since = timestamp; 458 p->current->state_since = timestamp;
458 p->current->state = TYPE_RUNNING; 459 p->current->state = TYPE_RUNNING;
459 } 460 }
460 461
461 if (prev_p->current) { 462 if (prev_p->current) {
@@ -498,13 +499,13 @@ static int process_sample_event(event_t *event, struct perf_session *session)
498 return 0; 499 return 0;
499 500
500 if (strcmp(event_str, "power:power_start") == 0) 501 if (strcmp(event_str, "power:power_start") == 0)
501 c_state_start(data.cpu, data.time, pe->value); 502 c_state_start(pe->cpu_id, data.time, pe->value);
502 503
503 if (strcmp(event_str, "power:power_end") == 0) 504 if (strcmp(event_str, "power:power_end") == 0)
504 c_state_end(data.cpu, data.time); 505 c_state_end(pe->cpu_id, data.time);
505 506
506 if (strcmp(event_str, "power:power_frequency") == 0) 507 if (strcmp(event_str, "power:power_frequency") == 0)
507 p_state_change(data.cpu, data.time, pe->value); 508 p_state_change(pe->cpu_id, data.time, pe->value);
508 509
509 if (strcmp(event_str, "sched:sched_wakeup") == 0) 510 if (strcmp(event_str, "sched:sched_wakeup") == 0)
510 sched_wakeup(data.cpu, data.time, data.pid, te); 511 sched_wakeup(data.cpu, data.time, data.pid, te);
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index a66f4272b994..b513e40974f4 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;
102static int sym_pcnt_filter = 5; 102static int sym_pcnt_filter = 5;
103static int sym_counter = 0; 103static int sym_counter = 0;
104static int display_weighted = -1; 104static int display_weighted = -1;
105static const char *cpu_list;
105 106
106/* 107/*
107 * Symbols 108 * Symbols
@@ -982,6 +983,7 @@ static void event__process_sample(const event_t *self,
982 u64 ip = self->ip.ip; 983 u64 ip = self->ip.ip;
983 struct sym_entry *syme; 984 struct sym_entry *syme;
984 struct addr_location al; 985 struct addr_location al;
986 struct sample_data data;
985 struct machine *machine; 987 struct machine *machine;
986 u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 988 u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
987 989
@@ -1024,7 +1026,8 @@ static void event__process_sample(const event_t *self,
1024 if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) 1026 if (self->header.misc & PERF_RECORD_MISC_EXACT_IP)
1025 exact_samples++; 1027 exact_samples++;
1026 1028
1027 if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || 1029 if (event__preprocess_sample(self, session, &al, &data,
1030 symbol_filter) < 0 ||
1028 al.filtered) 1031 al.filtered)
1029 return; 1032 return;
1030 1033
@@ -1079,26 +1082,6 @@ static void event__process_sample(const event_t *self,
1079 } 1082 }
1080} 1083}
1081 1084
1082static int event__process(event_t *event, struct perf_session *session)
1083{
1084 switch (event->header.type) {
1085 case PERF_RECORD_COMM:
1086 event__process_comm(event, session);
1087 break;
1088 case PERF_RECORD_MMAP:
1089 event__process_mmap(event, session);
1090 break;
1091 case PERF_RECORD_FORK:
1092 case PERF_RECORD_EXIT:
1093 event__process_task(event, session);
1094 break;
1095 default:
1096 break;
1097 }
1098
1099 return 0;
1100}
1101
1102struct mmap_data { 1085struct mmap_data {
1103 int counter; 1086 int counter;
1104 void *base; 1087 void *base;
@@ -1351,8 +1334,8 @@ static const struct option options[] = {
1351 "profile events on existing thread id"), 1334 "profile events on existing thread id"),
1352 OPT_BOOLEAN('a', "all-cpus", &system_wide, 1335 OPT_BOOLEAN('a', "all-cpus", &system_wide,
1353 "system-wide collection from all CPUs"), 1336 "system-wide collection from all CPUs"),
1354 OPT_INTEGER('C', "CPU", &profile_cpu, 1337 OPT_STRING('C', "cpu", &cpu_list, "cpu",
1355 "CPU to profile on"), 1338 "list of cpus to monitor"),
1356 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, 1339 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
1357 "file", "vmlinux pathname"), 1340 "file", "vmlinux pathname"),
1358 OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, 1341 OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols,
@@ -1428,10 +1411,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
1428 return -ENOMEM; 1411 return -ENOMEM;
1429 1412
1430 /* CPU and PID are mutually exclusive */ 1413 /* CPU and PID are mutually exclusive */
1431 if (target_tid > 0 && profile_cpu != -1) { 1414 if (target_tid > 0 && cpu_list) {
1432 printf("WARNING: PID switch overriding CPU\n"); 1415 printf("WARNING: PID switch overriding CPU\n");
1433 sleep(1); 1416 sleep(1);
1434 profile_cpu = -1; 1417 cpu_list = NULL;
1435 } 1418 }
1436 1419
1437 if (!nr_counters) 1420 if (!nr_counters)
@@ -1469,10 +1452,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
1469 attrs[counter].sample_period = default_interval; 1452 attrs[counter].sample_period = default_interval;
1470 } 1453 }
1471 1454
1472 if (target_tid != -1 || profile_cpu != -1) 1455 if (target_tid != -1)
1473 nr_cpus = 1; 1456 nr_cpus = 1;
1474 else 1457 else
1475 nr_cpus = read_cpu_map(); 1458 nr_cpus = read_cpu_map(cpu_list);
1459
1460 if (nr_cpus < 1)
1461 usage_with_options(top_usage, options);
1476 1462
1477 get_term_dimensions(&winsize); 1463 get_term_dimensions(&winsize);
1478 if (print_entries == 0) { 1464 if (print_entries == 0) {
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index dddf3f01b5ab..40a6a2992d15 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -1,18 +1,22 @@
1#include "builtin.h" 1#include "builtin.h"
2 2
3#include "util/util.h" 3#include "perf.h"
4#include "util/cache.h" 4#include "util/cache.h"
5#include "util/debug.h"
6#include "util/exec_cmd.h"
7#include "util/header.h"
8#include "util/parse-options.h"
9#include "util/session.h"
5#include "util/symbol.h" 10#include "util/symbol.h"
6#include "util/thread.h" 11#include "util/thread.h"
7#include "util/header.h"
8#include "util/exec_cmd.h"
9#include "util/trace-event.h" 12#include "util/trace-event.h"
10#include "util/session.h" 13#include "util/util.h"
11 14
12static char const *script_name; 15static char const *script_name;
13static char const *generate_script_lang; 16static char const *generate_script_lang;
14static bool debug_ordering; 17static bool debug_mode;
15static u64 last_timestamp; 18static u64 last_timestamp;
19static u64 nr_unordered;
16 20
17static int default_start_script(const char *script __unused, 21static int default_start_script(const char *script __unused,
18 int argc __unused, 22 int argc __unused,
@@ -58,14 +62,6 @@ static int cleanup_scripting(void)
58 return scripting_ops->stop_script(); 62 return scripting_ops->stop_script();
59} 63}
60 64
61#include "util/parse-options.h"
62
63#include "perf.h"
64#include "util/debug.h"
65
66#include "util/trace-event.h"
67#include "util/exec_cmd.h"
68
69static char const *input_name = "perf.data"; 65static char const *input_name = "perf.data";
70 66
71static int process_sample_event(event_t *event, struct perf_session *session) 67static int process_sample_event(event_t *event, struct perf_session *session)
@@ -91,13 +87,15 @@ static int process_sample_event(event_t *event, struct perf_session *session)
91 } 87 }
92 88
93 if (session->sample_type & PERF_SAMPLE_RAW) { 89 if (session->sample_type & PERF_SAMPLE_RAW) {
94 if (debug_ordering) { 90 if (debug_mode) {
95 if (data.time < last_timestamp) { 91 if (data.time < last_timestamp) {
96 pr_err("Samples misordered, previous: %llu " 92 pr_err("Samples misordered, previous: %llu "
97 "this: %llu\n", last_timestamp, 93 "this: %llu\n", last_timestamp,
98 data.time); 94 data.time);
95 nr_unordered++;
99 } 96 }
100 last_timestamp = data.time; 97 last_timestamp = data.time;
98 return 0;
101 } 99 }
102 /* 100 /*
103 * FIXME: better resolve from pid from the struct trace_entry 101 * FIXME: better resolve from pid from the struct trace_entry
@@ -113,6 +111,15 @@ static int process_sample_event(event_t *event, struct perf_session *session)
113 return 0; 111 return 0;
114} 112}
115 113
114static u64 nr_lost;
115
116static int process_lost_event(event_t *event, struct perf_session *session __used)
117{
118 nr_lost += event->lost.lost;
119
120 return 0;
121}
122
116static struct perf_event_ops event_ops = { 123static struct perf_event_ops event_ops = {
117 .sample = process_sample_event, 124 .sample = process_sample_event,
118 .comm = event__process_comm, 125 .comm = event__process_comm,
@@ -120,6 +127,7 @@ static struct perf_event_ops event_ops = {
120 .event_type = event__process_event_type, 127 .event_type = event__process_event_type,
121 .tracing_data = event__process_tracing_data, 128 .tracing_data = event__process_tracing_data,
122 .build_id = event__process_build_id, 129 .build_id = event__process_build_id,
130 .lost = process_lost_event,
123 .ordered_samples = true, 131 .ordered_samples = true,
124}; 132};
125 133
@@ -132,9 +140,18 @@ static void sig_handler(int sig __unused)
132 140
133static int __cmd_trace(struct perf_session *session) 141static int __cmd_trace(struct perf_session *session)
134{ 142{
143 int ret;
144
135 signal(SIGINT, sig_handler); 145 signal(SIGINT, sig_handler);
136 146
137 return perf_session__process_events(session, &event_ops); 147 ret = perf_session__process_events(session, &event_ops);
148
149 if (debug_mode) {
150 pr_err("Misordered timestamps: %llu\n", nr_unordered);
151 pr_err("Lost events: %llu\n", nr_lost);
152 }
153
154 return ret;
138} 155}
139 156
140struct script_spec { 157struct script_spec {
@@ -544,8 +561,8 @@ static const struct option options[] = {
544 "generate perf-trace.xx script in specified language"), 561 "generate perf-trace.xx script in specified language"),
545 OPT_STRING('i', "input", &input_name, "file", 562 OPT_STRING('i', "input", &input_name, "file",
546 "input file name"), 563 "input file name"),
547 OPT_BOOLEAN('d', "debug-ordering", &debug_ordering, 564 OPT_BOOLEAN('d', "debug-mode", &debug_mode,
548 "check that samples time ordering is monotonic"), 565 "do various checks like samples ordering and lost events"),
549 566
550 OPT_END() 567 OPT_END()
551}; 568};
diff --git a/tools/perf/feature-tests.mak b/tools/perf/feature-tests.mak
new file mode 100644
index 000000000000..ddb68e601f0e
--- /dev/null
+++ b/tools/perf/feature-tests.mak
@@ -0,0 +1,119 @@
1define SOURCE_HELLO
2#include <stdio.h>
3int main(void)
4{
5 return puts(\"hi\");
6}
7endef
8
9ifndef NO_DWARF
10define SOURCE_DWARF
11#include <dwarf.h>
12#include <libdw.h>
13#include <version.h>
14#ifndef _ELFUTILS_PREREQ
15#error
16#endif
17
18int main(void)
19{
20 Dwarf *dbg = dwarf_begin(0, DWARF_C_READ);
21 return (long)dbg;
22}
23endef
24endif
25
26define SOURCE_LIBELF
27#include <libelf.h>
28
29int main(void)
30{
31 Elf *elf = elf_begin(0, ELF_C_READ, 0);
32 return (long)elf;
33}
34endef
35
36define SOURCE_GLIBC
37#include <gnu/libc-version.h>
38
39int main(void)
40{
41 const char *version = gnu_get_libc_version();
42 return (long)version;
43}
44endef
45
46define SOURCE_ELF_MMAP
47#include <libelf.h>
48int main(void)
49{
50 Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0);
51 return (long)elf;
52}
53endef
54
55ifndef NO_NEWT
56define SOURCE_NEWT
57#include <newt.h>
58
59int main(void)
60{
61 newtInit();
62 newtCls();
63 return newtFinished();
64}
65endef
66endif
67
68ifndef NO_LIBPERL
69define SOURCE_PERL_EMBED
70#include <EXTERN.h>
71#include <perl.h>
72
73int main(void)
74{
75perl_alloc();
76return 0;
77}
78endef
79endif
80
81ifndef NO_LIBPYTHON
82define SOURCE_PYTHON_EMBED
83#include <Python.h>
84
85int main(void)
86{
87 Py_Initialize();
88 return 0;
89}
90endef
91endif
92
93define SOURCE_BFD
94#include <bfd.h>
95
96int main(void)
97{
98 bfd_demangle(0, 0, 0);
99 return 0;
100}
101endef
102
103define SOURCE_CPLUS_DEMANGLE
104extern char *cplus_demangle(const char *, int);
105
106int main(void)
107{
108 cplus_demangle(0, 0);
109 return 0;
110}
111endef
112
113# try-cc
114# Usage: option = $(call try-cc, source-to-build, cc-options)
115try-cc = $(shell sh -c \
116 'TMP="$(TMPOUT).$$$$"; \
117 echo "$(1)" | \
118 $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \
119 rm -f "$$TMP"')
diff --git a/tools/perf/perf-archive.sh b/tools/perf/perf-archive.sh
index 2e7a4f417e20..677e59d62a8d 100644
--- a/tools/perf/perf-archive.sh
+++ b/tools/perf/perf-archive.sh
@@ -7,7 +7,17 @@ if [ $# -ne 0 ] ; then
7 PERF_DATA=$1 7 PERF_DATA=$1
8fi 8fi
9 9
10DEBUGDIR=~/.debug/ 10#
11# PERF_BUILDID_DIR environment variable set by perf
12# path to buildid directory, default to $HOME/.debug
13#
14if [ -z $PERF_BUILDID_DIR ]; then
15 PERF_BUILDID_DIR=~/.debug/
16else
17 # append / to make substitutions work
18 PERF_BUILDID_DIR=$PERF_BUILDID_DIR/
19fi
20
11BUILDIDS=$(mktemp /tmp/perf-archive-buildids.XXXXXX) 21BUILDIDS=$(mktemp /tmp/perf-archive-buildids.XXXXXX)
12NOBUILDID=0000000000000000000000000000000000000000 22NOBUILDID=0000000000000000000000000000000000000000
13 23
@@ -22,13 +32,13 @@ MANIFEST=$(mktemp /tmp/perf-archive-manifest.XXXXXX)
22 32
23cut -d ' ' -f 1 $BUILDIDS | \ 33cut -d ' ' -f 1 $BUILDIDS | \
24while read build_id ; do 34while read build_id ; do
25 linkname=$DEBUGDIR.build-id/${build_id:0:2}/${build_id:2} 35 linkname=$PERF_BUILDID_DIR.build-id/${build_id:0:2}/${build_id:2}
26 filename=$(readlink -f $linkname) 36 filename=$(readlink -f $linkname)
27 echo ${linkname#$DEBUGDIR} >> $MANIFEST 37 echo ${linkname#$PERF_BUILDID_DIR} >> $MANIFEST
28 echo ${filename#$DEBUGDIR} >> $MANIFEST 38 echo ${filename#$PERF_BUILDID_DIR} >> $MANIFEST
29done 39done
30 40
31tar cfj $PERF_DATA.tar.bz2 -C $DEBUGDIR -T $MANIFEST 41tar cfj $PERF_DATA.tar.bz2 -C $PERF_BUILDID_DIR -T $MANIFEST
32rm -f $MANIFEST $BUILDIDS 42rm -f $MANIFEST $BUILDIDS
33echo -e "Now please run:\n" 43echo -e "Now please run:\n"
34echo -e "$ tar xvf $PERF_DATA.tar.bz2 -C ~/.debug\n" 44echo -e "$ tar xvf $PERF_DATA.tar.bz2 -C ~/.debug\n"
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 6e4871191138..cdd6c03f1e14 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -458,6 +458,8 @@ int main(int argc, const char **argv)
458 handle_options(&argv, &argc, NULL); 458 handle_options(&argv, &argc, NULL);
459 commit_pager_choice(); 459 commit_pager_choice();
460 set_debugfs_path(); 460 set_debugfs_path();
461 set_buildid_dir();
462
461 if (argc > 0) { 463 if (argc > 0) {
462 if (!prefixcmp(argv[0], "--")) 464 if (!prefixcmp(argv[0], "--"))
463 argv[0] += 2; 465 argv[0] += 2;
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 1dc464ee2ca8..aad7525bca1d 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):
89 value &= ~idx 89 value &= ~idx
90 90
91 return string 91 return string
92
93
94def taskState(state):
95 states = {
96 0 : "R",
97 1 : "S",
98 2 : "D",
99 64: "DEAD"
100 }
101
102 if state not in states:
103 return "Unknown"
104
105 return states[state]
106
107
108class EventHeaders:
109 def __init__(self, common_cpu, common_secs, common_nsecs,
110 common_pid, common_comm):
111 self.cpu = common_cpu
112 self.secs = common_secs
113 self.nsecs = common_nsecs
114 self.pid = common_pid
115 self.comm = common_comm
116
117 def ts(self):
118 return (self.secs * (10 ** 9)) + self.nsecs
119
120 def ts_format(self):
121 return "%d.%d" % (self.secs, int(self.nsecs / 1000))
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 000000000000..ae9a56e43e05
--- /dev/null
+++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py
@@ -0,0 +1,184 @@
1# SchedGui.py - Python extension for perf trace, basic GUI code for
2# traces drawing and overview.
3#
4# Copyright (C) 2010 by Frederic Weisbecker <fweisbec@gmail.com>
5#
6# This software is distributed under the terms of the GNU General
7# Public License ("GPL") version 2 as published by the Free Software
8# Foundation.
9
10
11try:
12 import wx
13except ImportError:
14 raise ImportError, "You need to install the wxpython lib for this script"
15
16
17class RootFrame(wx.Frame):
18 Y_OFFSET = 100
19 RECT_HEIGHT = 100
20 RECT_SPACE = 50
21 EVENT_MARKING_WIDTH = 5
22
23 def __init__(self, sched_tracer, title, parent = None, id = -1):
24 wx.Frame.__init__(self, parent, id, title)
25
26 (self.screen_width, self.screen_height) = wx.GetDisplaySize()
27 self.screen_width -= 10
28 self.screen_height -= 10
29 self.zoom = 0.5
30 self.scroll_scale = 20
31 self.sched_tracer = sched_tracer
32 self.sched_tracer.set_root_win(self)
33 (self.ts_start, self.ts_end) = sched_tracer.interval()
34 self.update_width_virtual()
35 self.nr_rects = sched_tracer.nr_rectangles() + 1
36 self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE))
37
38 # whole window panel
39 self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height))
40
41 # scrollable container
42 self.scroll = wx.ScrolledWindow(self.panel)
43 self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale)
44 self.scroll.EnableScrolling(True, True)
45 self.scroll.SetFocus()
46
47 # scrollable drawing area
48 self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width - 15, self.screen_height / 2))
49 self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint)
50 self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press)
51 self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down)
52 self.scroll.Bind(wx.EVT_PAINT, self.on_paint)
53 self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press)
54 self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down)
55
56 self.scroll.Fit()
57 self.Fit()
58
59 self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_virtual, wx.SIZE_USE_EXISTING)
60
61 self.txt = None
62
63 self.Show(True)
64
65 def us_to_px(self, val):
66 return val / (10 ** 3) * self.zoom
67
68 def px_to_us(self, val):
69 return (val / self.zoom) * (10 ** 3)
70
71 def scroll_start(self):
72 (x, y) = self.scroll.GetViewStart()
73 return (x * self.scroll_scale, y * self.scroll_scale)
74
75 def scroll_start_us(self):
76 (x, y) = self.scroll_start()
77 return self.px_to_us(x)
78
79 def paint_rectangle_zone(self, nr, color, top_color, start, end):
80 offset_px = self.us_to_px(start - self.ts_start)
81 width_px = self.us_to_px(end - self.ts_start)
82
83 offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE))
84 width_py = RootFrame.RECT_HEIGHT
85
86 dc = self.dc
87
88 if top_color is not None:
89 (r, g, b) = top_color
90 top_color = wx.Colour(r, g, b)
91 brush = wx.Brush(top_color, wx.SOLID)
92 dc.SetBrush(brush)
93 dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH)
94 width_py -= RootFrame.EVENT_MARKING_WIDTH
95 offset_py += RootFrame.EVENT_MARKING_WIDTH
96
97 (r ,g, b) = color
98 color = wx.Colour(r, g, b)
99 brush = wx.Brush(color, wx.SOLID)
100 dc.SetBrush(brush)
101 dc.DrawRectangle(offset_px, offset_py, width_px, width_py)
102
103 def update_rectangles(self, dc, start, end):
104 start += self.ts_start
105 end += self.ts_start
106 self.sched_tracer.fill_zone(start, end)
107
108 def on_paint(self, event):
109 dc = wx.PaintDC(self.scroll_panel)
110 self.dc = dc
111
112 width = min(self.width_virtual, self.screen_width)
113 (x, y) = self.scroll_start()
114 start = self.px_to_us(x)
115 end = self.px_to_us(x + width)
116 self.update_rectangles(dc, start, end)
117
118 def rect_from_ypixel(self, y):
119 y -= RootFrame.Y_OFFSET
120 rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)
121 height = y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)
122
123 if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGHT:
124 return -1
125
126 return rect
127
128 def update_summary(self, txt):
129 if self.txt:
130 self.txt.Destroy()
131 self.txt = wx.StaticText(self.panel, -1, txt, (0, (self.screen_height / 2) + 50))
132
133
134 def on_mouse_down(self, event):
135 (x, y) = event.GetPositionTuple()
136 rect = self.rect_from_ypixel(y)
137 if rect == -1:
138 return
139
140 t = self.px_to_us(x) + self.ts_start
141
142 self.sched_tracer.mouse_down(rect, t)
143
144
145 def update_width_virtual(self):
146 self.width_virtual = self.us_to_px(self.ts_end - self.ts_start)
147
148 def __zoom(self, x):
149 self.update_width_virtual()
150 (xpos, ypos) = self.scroll.GetViewStart()
151 xpos = self.us_to_px(x) / self.scroll_scale
152 self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xpos, ypos)
153 self.Refresh()
154
155 def zoom_in(self):
156 x = self.scroll_start_us()
157 self.zoom *= 2
158 self.__zoom(x)
159
160 def zoom_out(self):
161 x = self.scroll_start_us()
162 self.zoom /= 2
163 self.__zoom(x)
164
165
166 def on_key_press(self, event):
167 key = event.GetRawKeyCode()
168 if key == ord("+"):
169 self.zoom_in()
170 return
171 if key == ord("-"):
172 self.zoom_out()
173 return
174
175 key = event.GetKeyCode()
176 (x, y) = self.scroll.GetViewStart()
177 if key == wx.WXK_RIGHT:
178 self.scroll.Scroll(x + 1, y)
179 elif key == wx.WXK_LEFT:
180 self.scroll.Scroll(x - 1, y)
181 elif key == wx.WXK_DOWN:
182 self.scroll.Scroll(x, y + 1)
183 elif key == wx.WXK_UP:
184 self.scroll.Scroll(x, y - 1)
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 000000000000..17a3e9bd9e8f
--- /dev/null
+++ b/tools/perf/scripts/python/bin/sched-migration-record
@@ -0,0 +1,2 @@
1#!/bin/bash
2perf 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 000000000000..61d05f72e443
--- /dev/null
+++ b/tools/perf/scripts/python/bin/sched-migration-report
@@ -0,0 +1,3 @@
1#!/bin/bash
2# description: sched migration overview
3perf trace $@ -s ~/libexec/perf-core/scripts/python/sched-migration.py
diff --git a/tools/perf/scripts/python/check-perf-trace.py b/tools/perf/scripts/python/check-perf-trace.py
index 964d934395ff..d9f7893e315c 100644
--- a/tools/perf/scripts/python/check-perf-trace.py
+++ b/tools/perf/scripts/python/check-perf-trace.py
@@ -51,8 +51,7 @@ def kmem__kmalloc(event_name, context, common_cpu,
51 51
52 flag_str("kmem__kmalloc", "gfp_flags", gfp_flags)), 52 flag_str("kmem__kmalloc", "gfp_flags", gfp_flags)),
53 53
54def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs, 54def trace_unhandled(event_name, context, event_fields_dict):
55 common_pid, common_comm):
56 try: 55 try:
57 unhandled[event_name] += 1 56 unhandled[event_name] += 1
58 except TypeError: 57 except TypeError:
diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py
new file mode 100644
index 000000000000..b934383c3364
--- /dev/null
+++ b/tools/perf/scripts/python/sched-migration.py
@@ -0,0 +1,461 @@
1#!/usr/bin/python
2#
3# Cpu task migration overview toy
4#
5# Copyright (C) 2010 Frederic Weisbecker <fweisbec@gmail.com>
6#
7# perf trace event handlers have been generated by perf trace -g python
8#
9# This software is distributed under the terms of the GNU General
10# Public License ("GPL") version 2 as published by the Free Software
11# Foundation.
12
13
14import os
15import sys
16
17from collections import defaultdict
18from UserList import UserList
19
20sys.path.append(os.environ['PERF_EXEC_PATH'] + \
21 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
22sys.path.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace')
23
24from perf_trace_context import *
25from Core import *
26from SchedGui import *
27
28
29threads = { 0 : "idle"}
30
31def thread_name(pid):
32 return "%s:%d" % (threads[pid], pid)
33
34class RunqueueEventUnknown:
35 @staticmethod
36 def color():
37 return None
38
39 def __repr__(self):
40 return "unknown"
41
42class RunqueueEventSleep:
43 @staticmethod
44 def color():
45 return (0, 0, 0xff)
46
47 def __init__(self, sleeper):
48 self.sleeper = sleeper
49
50 def __repr__(self):
51 return "%s gone to sleep" % thread_name(self.sleeper)
52
53class RunqueueEventWakeup:
54 @staticmethod
55 def color():
56 return (0xff, 0xff, 0)
57
58 def __init__(self, wakee):
59 self.wakee = wakee
60
61 def __repr__(self):
62 return "%s woke up" % thread_name(self.wakee)
63
64class RunqueueEventFork:
65 @staticmethod
66 def color():
67 return (0, 0xff, 0)
68
69 def __init__(self, child):
70 self.child = child
71
72 def __repr__(self):
73 return "new forked task %s" % thread_name(self.child)
74
75class RunqueueMigrateIn:
76 @staticmethod
77 def color():
78 return (0, 0xf0, 0xff)
79
80 def __init__(self, new):
81 self.new = new
82
83 def __repr__(self):
84 return "task migrated in %s" % thread_name(self.new)
85
86class RunqueueMigrateOut:
87 @staticmethod
88 def color():
89 return (0xff, 0, 0xff)
90
91 def __init__(self, old):
92 self.old = old
93
94 def __repr__(self):
95 return "task migrated out %s" % thread_name(self.old)
96
97class RunqueueSnapshot:
98 def __init__(self, tasks = [0], event = RunqueueEventUnknown()):
99 self.tasks = tuple(tasks)
100 self.event = event
101
102 def sched_switch(self, prev, prev_state, next):
103 event = RunqueueEventUnknown()
104
105 if taskState(prev_state) == "R" and next in self.tasks \
106 and prev in self.tasks:
107 return self
108
109 if taskState(prev_state) != "R":
110 event = RunqueueEventSleep(prev)
111
112 next_tasks = list(self.tasks[:])
113 if prev in self.tasks:
114 if taskState(prev_state) != "R":
115 next_tasks.remove(prev)
116 elif taskState(prev_state) == "R":
117 next_tasks.append(prev)
118
119 if next not in next_tasks:
120 next_tasks.append(next)
121
122 return RunqueueSnapshot(next_tasks, event)
123
124 def migrate_out(self, old):
125 if old not in self.tasks:
126 return self
127 next_tasks = [task for task in self.tasks if task != old]
128
129 return RunqueueSnapshot(next_tasks, RunqueueMigrateOut(old))
130
131 def __migrate_in(self, new, event):
132 if new in self.tasks:
133 self.event = event
134 return self
135 next_tasks = self.tasks[:] + tuple([new])
136
137 return RunqueueSnapshot(next_tasks, event)
138
139 def migrate_in(self, new):
140 return self.__migrate_in(new, RunqueueMigrateIn(new))
141
142 def wake_up(self, new):
143 return self.__migrate_in(new, RunqueueEventWakeup(new))
144
145 def wake_up_new(self, new):
146 return self.__migrate_in(new, RunqueueEventFork(new))
147
148 def load(self):
149 """ Provide the number of tasks on the runqueue.
150 Don't count idle"""
151 return len(self.tasks) - 1
152
153 def __repr__(self):
154 ret = self.tasks.__repr__()
155 ret += self.origin_tostring()
156
157 return ret
158
159class TimeSlice:
160 def __init__(self, start, prev):
161 self.start = start
162 self.prev = prev
163 self.end = start
164 # cpus that triggered the event
165 self.event_cpus = []
166 if prev is not None:
167 self.total_load = prev.total_load
168 self.rqs = prev.rqs.copy()
169 else:
170 self.rqs = defaultdict(RunqueueSnapshot)
171 self.total_load = 0
172
173 def __update_total_load(self, old_rq, new_rq):
174 diff = new_rq.load() - old_rq.load()
175 self.total_load += diff
176
177 def sched_switch(self, ts_list, prev, prev_state, next, cpu):
178 old_rq = self.prev.rqs[cpu]
179 new_rq = old_rq.sched_switch(prev, prev_state, next)
180
181 if old_rq is new_rq:
182 return
183
184 self.rqs[cpu] = new_rq
185 self.__update_total_load(old_rq, new_rq)
186 ts_list.append(self)
187 self.event_cpus = [cpu]
188
189 def migrate(self, ts_list, new, old_cpu, new_cpu):
190 if old_cpu == new_cpu:
191 return
192 old_rq = self.prev.rqs[old_cpu]
193 out_rq = old_rq.migrate_out(new)
194 self.rqs[old_cpu] = out_rq
195 self.__update_total_load(old_rq, out_rq)
196
197 new_rq = self.prev.rqs[new_cpu]
198 in_rq = new_rq.migrate_in(new)
199 self.rqs[new_cpu] = in_rq
200 self.__update_total_load(new_rq, in_rq)
201
202 ts_list.append(self)
203
204 if old_rq is not out_rq:
205 self.event_cpus.append(old_cpu)
206 self.event_cpus.append(new_cpu)
207
208 def wake_up(self, ts_list, pid, cpu, fork):
209 old_rq = self.prev.rqs[cpu]
210 if fork:
211 new_rq = old_rq.wake_up_new(pid)
212 else:
213 new_rq = old_rq.wake_up(pid)
214
215 if new_rq is old_rq:
216 return
217 self.rqs[cpu] = new_rq
218 self.__update_total_load(old_rq, new_rq)
219 ts_list.append(self)
220 self.event_cpus = [cpu]
221
222 def next(self, t):
223 self.end = t
224 return TimeSlice(t, self)
225
226class TimeSliceList(UserList):
227 def __init__(self, arg = []):
228 self.data = arg
229
230 def get_time_slice(self, ts):
231 if len(self.data) == 0:
232 slice = TimeSlice(ts, TimeSlice(-1, None))
233 else:
234 slice = self.data[-1].next(ts)
235 return slice
236
237 def find_time_slice(self, ts):
238 start = 0
239 end = len(self.data)
240 found = -1
241 searching = True
242 while searching:
243 if start == end or start == end - 1:
244 searching = False
245
246 i = (end + start) / 2
247 if self.data[i].start <= ts and self.data[i].end >= ts:
248 found = i
249 end = i
250 continue
251
252 if self.data[i].end < ts:
253 start = i
254
255 elif self.data[i].start > ts:
256 end = i
257
258 return found
259
260 def set_root_win(self, win):
261 self.root_win = win
262
263 def mouse_down(self, cpu, t):
264 idx = self.find_time_slice(t)
265 if idx == -1:
266 return
267
268 ts = self[idx]
269 rq = ts.rqs[cpu]
270 raw = "CPU: %d\n" % cpu
271 raw += "Last event : %s\n" % rq.event.__repr__()
272 raw += "Timestamp : %d.%06d\n" % (ts.start / (10 ** 9), (ts.start % (10 ** 9)) / 1000)
273 raw += "Duration : %6d us\n" % ((ts.end - ts.start) / (10 ** 6))
274 raw += "Load = %d\n" % rq.load()
275 for t in rq.tasks:
276 raw += "%s \n" % thread_name(t)
277
278 self.root_win.update_summary(raw)
279
280 def update_rectangle_cpu(self, slice, cpu):
281 rq = slice.rqs[cpu]
282
283 if slice.total_load != 0:
284 load_rate = rq.load() / float(slice.total_load)
285 else:
286 load_rate = 0
287
288 red_power = int(0xff - (0xff * load_rate))
289 color = (0xff, red_power, red_power)
290
291 top_color = None
292
293 if cpu in slice.event_cpus:
294 top_color = rq.event.color()
295
296 self.root_win.paint_rectangle_zone(cpu, color, top_color, slice.start, slice.end)
297
298 def fill_zone(self, start, end):
299 i = self.find_time_slice(start)
300 if i == -1:
301 return
302
303 for i in xrange(i, len(self.data)):
304 timeslice = self.data[i]
305 if timeslice.start > end:
306 return
307
308 for cpu in timeslice.rqs:
309 self.update_rectangle_cpu(timeslice, cpu)
310
311 def interval(self):
312 if len(self.data) == 0:
313 return (0, 0)
314
315 return (self.data[0].start, self.data[-1].end)
316
317 def nr_rectangles(self):
318 last_ts = self.data[-1]
319 max_cpu = 0
320 for cpu in last_ts.rqs:
321 if cpu > max_cpu:
322 max_cpu = cpu
323 return max_cpu
324
325
326class SchedEventProxy:
327 def __init__(self):
328 self.current_tsk = defaultdict(lambda : -1)
329 self.timeslices = TimeSliceList()
330
331 def sched_switch(self, headers, prev_comm, prev_pid, prev_prio, prev_state,
332 next_comm, next_pid, next_prio):
333 """ Ensure the task we sched out this cpu is really the one
334 we logged. Otherwise we may have missed traces """
335
336 on_cpu_task = self.current_tsk[headers.cpu]
337
338 if on_cpu_task != -1 and on_cpu_task != prev_pid:
339 print "Sched switch event rejected ts: %s cpu: %d prev: %s(%d) next: %s(%d)" % \
340 (headers.ts_format(), headers.cpu, prev_comm, prev_pid, next_comm, next_pid)
341
342 threads[prev_pid] = prev_comm
343 threads[next_pid] = next_comm
344 self.current_tsk[headers.cpu] = next_pid
345
346 ts = self.timeslices.get_time_slice(headers.ts())
347 ts.sched_switch(self.timeslices, prev_pid, prev_state, next_pid, headers.cpu)
348
349 def migrate(self, headers, pid, prio, orig_cpu, dest_cpu):
350 ts = self.timeslices.get_time_slice(headers.ts())
351 ts.migrate(self.timeslices, pid, orig_cpu, dest_cpu)
352
353 def wake_up(self, headers, comm, pid, success, target_cpu, fork):
354 if success == 0:
355 return
356 ts = self.timeslices.get_time_slice(headers.ts())
357 ts.wake_up(self.timeslices, pid, target_cpu, fork)
358
359
360def trace_begin():
361 global parser
362 parser = SchedEventProxy()
363
364def trace_end():
365 app = wx.App(False)
366 timeslices = parser.timeslices
367 frame = RootFrame(timeslices, "Migration")
368 app.MainLoop()
369
370def sched__sched_stat_runtime(event_name, context, common_cpu,
371 common_secs, common_nsecs, common_pid, common_comm,
372 comm, pid, runtime, vruntime):
373 pass
374
375def sched__sched_stat_iowait(event_name, context, common_cpu,
376 common_secs, common_nsecs, common_pid, common_comm,
377 comm, pid, delay):
378 pass
379
380def sched__sched_stat_sleep(event_name, context, common_cpu,
381 common_secs, common_nsecs, common_pid, common_comm,
382 comm, pid, delay):
383 pass
384
385def sched__sched_stat_wait(event_name, context, common_cpu,
386 common_secs, common_nsecs, common_pid, common_comm,
387 comm, pid, delay):
388 pass
389
390def sched__sched_process_fork(event_name, context, common_cpu,
391 common_secs, common_nsecs, common_pid, common_comm,
392 parent_comm, parent_pid, child_comm, child_pid):
393 pass
394
395def sched__sched_process_wait(event_name, context, common_cpu,
396 common_secs, common_nsecs, common_pid, common_comm,
397 comm, pid, prio):
398 pass
399
400def sched__sched_process_exit(event_name, context, common_cpu,
401 common_secs, common_nsecs, common_pid, common_comm,
402 comm, pid, prio):
403 pass
404
405def sched__sched_process_free(event_name, context, common_cpu,
406 common_secs, common_nsecs, common_pid, common_comm,
407 comm, pid, prio):
408 pass
409
410def sched__sched_migrate_task(event_name, context, common_cpu,
411 common_secs, common_nsecs, common_pid, common_comm,
412 comm, pid, prio, orig_cpu,
413 dest_cpu):
414 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
415 common_pid, common_comm)
416 parser.migrate(headers, pid, prio, orig_cpu, dest_cpu)
417
418def sched__sched_switch(event_name, context, common_cpu,
419 common_secs, common_nsecs, common_pid, common_comm,
420 prev_comm, prev_pid, prev_prio, prev_state,
421 next_comm, next_pid, next_prio):
422
423 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
424 common_pid, common_comm)
425 parser.sched_switch(headers, prev_comm, prev_pid, prev_prio, prev_state,
426 next_comm, next_pid, next_prio)
427
428def sched__sched_wakeup_new(event_name, context, common_cpu,
429 common_secs, common_nsecs, common_pid, common_comm,
430 comm, pid, prio, success,
431 target_cpu):
432 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
433 common_pid, common_comm)
434 parser.wake_up(headers, comm, pid, success, target_cpu, 1)
435
436def sched__sched_wakeup(event_name, context, common_cpu,
437 common_secs, common_nsecs, common_pid, common_comm,
438 comm, pid, prio, success,
439 target_cpu):
440 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
441 common_pid, common_comm)
442 parser.wake_up(headers, comm, pid, success, target_cpu, 0)
443
444def sched__sched_wait_task(event_name, context, common_cpu,
445 common_secs, common_nsecs, common_pid, common_comm,
446 comm, pid, prio):
447 pass
448
449def sched__sched_kthread_stop_ret(event_name, context, common_cpu,
450 common_secs, common_nsecs, common_pid, common_comm,
451 ret):
452 pass
453
454def sched__sched_kthread_stop(event_name, context, common_cpu,
455 common_secs, common_nsecs, common_pid, common_comm,
456 comm, pid):
457 pass
458
459def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs,
460 common_pid, common_comm):
461 pass
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN
index 49ece7921914..97d76562a1a0 100755
--- a/tools/perf/util/PERF-VERSION-GEN
+++ b/tools/perf/util/PERF-VERSION-GEN
@@ -5,17 +5,13 @@ if [ $# -eq 1 ] ; then
5fi 5fi
6 6
7GVF=${OUTPUT}PERF-VERSION-FILE 7GVF=${OUTPUT}PERF-VERSION-FILE
8DEF_VER=v0.0.2.PERF
9 8
10LF=' 9LF='
11' 10'
12 11
13# First see if there is a version file (included in release tarballs), 12# First check if there is a .git to get the version from git describe
14# then try git-describe, then default. 13# otherwise try to get the version from the kernel makefile
15if test -f version 14if test -d ../../.git -o -f ../../.git &&
16then
17 VN=$(cat version) || VN="$DEF_VER"
18elif test -d .git -o -f .git &&
19 VN=$(git describe --abbrev=4 HEAD 2>/dev/null) && 15 VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
20 case "$VN" in 16 case "$VN" in
21 *$LF*) (exit 1) ;; 17 *$LF*) (exit 1) ;;
@@ -27,7 +23,12 @@ elif test -d .git -o -f .git &&
27then 23then
28 VN=$(echo "$VN" | sed -e 's/-/./g'); 24 VN=$(echo "$VN" | sed -e 's/-/./g');
29else 25else
30 VN="$DEF_VER" 26 eval `grep '^VERSION\s*=' ../../Makefile|tr -d ' '`
27 eval `grep '^PATCHLEVEL\s*=' ../../Makefile|tr -d ' '`
28 eval `grep '^SUBLEVEL\s*=' ../../Makefile|tr -d ' '`
29 eval `grep '^EXTRAVERSION\s*=' ../../Makefile|tr -d ' '`
30
31 VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}"
31fi 32fi
32 33
33VN=$(expr "$VN" : v*'\(.*\)') 34VN=$(expr "$VN" : v*'\(.*\)')
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 70c5cf87d020..e437edb72417 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -12,6 +12,7 @@
12#include "event.h" 12#include "event.h"
13#include "symbol.h" 13#include "symbol.h"
14#include <linux/kernel.h> 14#include <linux/kernel.h>
15#include "debug.h"
15 16
16static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) 17static int build_id__mark_dso_hit(event_t *event, struct perf_session *session)
17{ 18{
@@ -34,28 +35,43 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session)
34 return 0; 35 return 0;
35} 36}
36 37
38static int event__exit_del_thread(event_t *self, struct perf_session *session)
39{
40 struct thread *thread = perf_session__findnew(session, self->fork.tid);
41
42 dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
43 self->fork.ppid, self->fork.ptid);
44
45 if (thread) {
46 rb_erase(&thread->rb_node, &session->threads);
47 session->last_match = NULL;
48 thread__delete(thread);
49 }
50
51 return 0;
52}
53
37struct perf_event_ops build_id__mark_dso_hit_ops = { 54struct perf_event_ops build_id__mark_dso_hit_ops = {
38 .sample = build_id__mark_dso_hit, 55 .sample = build_id__mark_dso_hit,
39 .mmap = event__process_mmap, 56 .mmap = event__process_mmap,
40 .fork = event__process_task, 57 .fork = event__process_task,
58 .exit = event__exit_del_thread,
41}; 59};
42 60
43char *dso__build_id_filename(struct dso *self, char *bf, size_t size) 61char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
44{ 62{
45 char build_id_hex[BUILD_ID_SIZE * 2 + 1]; 63 char build_id_hex[BUILD_ID_SIZE * 2 + 1];
46 const char *home;
47 64
48 if (!self->has_build_id) 65 if (!self->has_build_id)
49 return NULL; 66 return NULL;
50 67
51 build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); 68 build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex);
52 home = getenv("HOME");
53 if (bf == NULL) { 69 if (bf == NULL) {
54 if (asprintf(&bf, "%s/%s/.build-id/%.2s/%s", home, 70 if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir,
55 DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2) < 0) 71 build_id_hex, build_id_hex + 2) < 0)
56 return NULL; 72 return NULL;
57 } else 73 } else
58 snprintf(bf, size, "%s/%s/.build-id/%.2s/%s", home, 74 snprintf(bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
59 DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2); 75 build_id_hex, build_id_hex + 2);
60 return bf; 76 return bf;
61} 77}
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 65fe664fddf6..27e9ebe4076e 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 *);
23extern int perf_config_int(const char *, const char *); 23extern int perf_config_int(const char *, const char *);
24extern int perf_config_bool(const char *, const char *); 24extern int perf_config_bool(const char *, const char *);
25extern int config_error_nonbool(const char *); 25extern int config_error_nonbool(const char *);
26extern const char *perf_config_dirname(const char *, const char *);
26 27
27/* pager.c */ 28/* pager.c */
28extern void setup_pager(void); 29extern void setup_pager(void);
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 62b69ad4aa73..f231f43424d2 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -18,7 +18,7 @@
18#include "util.h" 18#include "util.h"
19#include "callchain.h" 19#include "callchain.h"
20 20
21bool ip_callchain__valid(struct ip_callchain *chain, event_t *event) 21bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
22{ 22{
23 unsigned int chain_size = event->header.size; 23 unsigned int chain_size = event->header.size;
24 chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; 24 chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
@@ -230,7 +230,7 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start)
230 230
231static void 231static void
232add_child(struct callchain_node *parent, struct resolved_chain *chain, 232add_child(struct callchain_node *parent, struct resolved_chain *chain,
233 int start) 233 int start, u64 period)
234{ 234{
235 struct callchain_node *new; 235 struct callchain_node *new;
236 236
@@ -238,7 +238,7 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain,
238 fill_node(new, chain, start); 238 fill_node(new, chain, start);
239 239
240 new->children_hit = 0; 240 new->children_hit = 0;
241 new->hit = 1; 241 new->hit = period;
242} 242}
243 243
244/* 244/*
@@ -248,7 +248,8 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain,
248 */ 248 */
249static void 249static void
250split_add_child(struct callchain_node *parent, struct resolved_chain *chain, 250split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
251 struct callchain_list *to_split, int idx_parents, int idx_local) 251 struct callchain_list *to_split, int idx_parents, int idx_local,
252 u64 period)
252{ 253{
253 struct callchain_node *new; 254 struct callchain_node *new;
254 struct list_head *old_tail; 255 struct list_head *old_tail;
@@ -275,41 +276,41 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
275 /* create a new child for the new branch if any */ 276 /* create a new child for the new branch if any */
276 if (idx_total < chain->nr) { 277 if (idx_total < chain->nr) {
277 parent->hit = 0; 278 parent->hit = 0;
278 add_child(parent, chain, idx_total); 279 add_child(parent, chain, idx_total, period);
279 parent->children_hit++; 280 parent->children_hit += period;
280 } else { 281 } else {
281 parent->hit = 1; 282 parent->hit = period;
282 } 283 }
283} 284}
284 285
285static int 286static int
286__append_chain(struct callchain_node *root, struct resolved_chain *chain, 287__append_chain(struct callchain_node *root, struct resolved_chain *chain,
287 unsigned int start); 288 unsigned int start, u64 period);
288 289
289static void 290static void
290__append_chain_children(struct callchain_node *root, 291__append_chain_children(struct callchain_node *root,
291 struct resolved_chain *chain, 292 struct resolved_chain *chain,
292 unsigned int start) 293 unsigned int start, u64 period)
293{ 294{
294 struct callchain_node *rnode; 295 struct callchain_node *rnode;
295 296
296 /* lookup in childrens */ 297 /* lookup in childrens */
297 chain_for_each_child(rnode, root) { 298 chain_for_each_child(rnode, root) {
298 unsigned int ret = __append_chain(rnode, chain, start); 299 unsigned int ret = __append_chain(rnode, chain, start, period);
299 300
300 if (!ret) 301 if (!ret)
301 goto inc_children_hit; 302 goto inc_children_hit;
302 } 303 }
303 /* nothing in children, add to the current node */ 304 /* nothing in children, add to the current node */
304 add_child(root, chain, start); 305 add_child(root, chain, start, period);
305 306
306inc_children_hit: 307inc_children_hit:
307 root->children_hit++; 308 root->children_hit += period;
308} 309}
309 310
310static int 311static int
311__append_chain(struct callchain_node *root, struct resolved_chain *chain, 312__append_chain(struct callchain_node *root, struct resolved_chain *chain,
312 unsigned int start) 313 unsigned int start, u64 period)
313{ 314{
314 struct callchain_list *cnode; 315 struct callchain_list *cnode;
315 unsigned int i = start; 316 unsigned int i = start;
@@ -345,18 +346,18 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain,
345 346
346 /* we match only a part of the node. Split it and add the new chain */ 347 /* we match only a part of the node. Split it and add the new chain */
347 if (i - start < root->val_nr) { 348 if (i - start < root->val_nr) {
348 split_add_child(root, chain, cnode, start, i - start); 349 split_add_child(root, chain, cnode, start, i - start, period);
349 return 0; 350 return 0;
350 } 351 }
351 352
352 /* we match 100% of the path, increment the hit */ 353 /* we match 100% of the path, increment the hit */
353 if (i - start == root->val_nr && i == chain->nr) { 354 if (i - start == root->val_nr && i == chain->nr) {
354 root->hit++; 355 root->hit += period;
355 return 0; 356 return 0;
356 } 357 }
357 358
358 /* We match the node and still have a part remaining */ 359 /* We match the node and still have a part remaining */
359 __append_chain_children(root, chain, i); 360 __append_chain_children(root, chain, i, period);
360 361
361 return 0; 362 return 0;
362} 363}
@@ -380,7 +381,7 @@ static void filter_context(struct ip_callchain *old, struct resolved_chain *new,
380 381
381 382
382int append_chain(struct callchain_node *root, struct ip_callchain *chain, 383int append_chain(struct callchain_node *root, struct ip_callchain *chain,
383 struct map_symbol *syms) 384 struct map_symbol *syms, u64 period)
384{ 385{
385 struct resolved_chain *filtered; 386 struct resolved_chain *filtered;
386 387
@@ -397,7 +398,7 @@ int append_chain(struct callchain_node *root, struct ip_callchain *chain,
397 if (!filtered->nr) 398 if (!filtered->nr)
398 goto end; 399 goto end;
399 400
400 __append_chain_children(root, filtered, 0); 401 __append_chain_children(root, filtered, 0, period);
401end: 402end:
402 free(filtered); 403 free(filtered);
403 404
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 1ca73e4a2723..624a96c636fd 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -49,6 +49,9 @@ static inline void callchain_init(struct callchain_node *node)
49 INIT_LIST_HEAD(&node->brothers); 49 INIT_LIST_HEAD(&node->brothers);
50 INIT_LIST_HEAD(&node->children); 50 INIT_LIST_HEAD(&node->children);
51 INIT_LIST_HEAD(&node->val); 51 INIT_LIST_HEAD(&node->val);
52
53 node->parent = NULL;
54 node->hit = 0;
52} 55}
53 56
54static inline u64 cumul_hits(struct callchain_node *node) 57static inline u64 cumul_hits(struct callchain_node *node)
@@ -58,7 +61,7 @@ static inline u64 cumul_hits(struct callchain_node *node)
58 61
59int register_callchain_param(struct callchain_param *param); 62int register_callchain_param(struct callchain_param *param);
60int append_chain(struct callchain_node *root, struct ip_callchain *chain, 63int append_chain(struct callchain_node *root, struct ip_callchain *chain,
61 struct map_symbol *syms); 64 struct map_symbol *syms, u64 period);
62 65
63bool ip_callchain__valid(struct ip_callchain *chain, event_t *event); 66bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
64#endif /* __PERF_CALLCHAIN_H */ 67#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index dabe892d0e53..e02d78cae70f 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -11,6 +11,11 @@
11 11
12#define MAXNAME (256) 12#define MAXNAME (256)
13 13
14#define DEBUG_CACHE_DIR ".debug"
15
16
17char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
18
14static FILE *config_file; 19static FILE *config_file;
15static const char *config_file_name; 20static const char *config_file_name;
16static int config_linenr; 21static int config_linenr;
@@ -127,7 +132,7 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
127 break; 132 break;
128 if (!iskeychar(c)) 133 if (!iskeychar(c))
129 break; 134 break;
130 name[len++] = tolower(c); 135 name[len++] = c;
131 if (len >= MAXNAME) 136 if (len >= MAXNAME)
132 return -1; 137 return -1;
133 } 138 }
@@ -327,6 +332,13 @@ int perf_config_bool(const char *name, const char *value)
327 return !!perf_config_bool_or_int(name, value, &discard); 332 return !!perf_config_bool_or_int(name, value, &discard);
328} 333}
329 334
335const char *perf_config_dirname(const char *name, const char *value)
336{
337 if (!name)
338 return NULL;
339 return value;
340}
341
330static int perf_default_core_config(const char *var __used, const char *value __used) 342static int perf_default_core_config(const char *var __used, const char *value __used)
331{ 343{
332 /* Add other config variables here and to Documentation/config.txt. */ 344 /* Add other config variables here and to Documentation/config.txt. */
@@ -428,3 +440,53 @@ int config_error_nonbool(const char *var)
428{ 440{
429 return error("Missing value for '%s'", var); 441 return error("Missing value for '%s'", var);
430} 442}
443
444struct buildid_dir_config {
445 char *dir;
446};
447
448static int buildid_dir_command_config(const char *var, const char *value,
449 void *data)
450{
451 struct buildid_dir_config *c = data;
452 const char *v;
453
454 /* same dir for all commands */
455 if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) {
456 v = perf_config_dirname(var, value);
457 if (!v)
458 return -1;
459 strncpy(c->dir, v, MAXPATHLEN-1);
460 c->dir[MAXPATHLEN-1] = '\0';
461 }
462 return 0;
463}
464
465static void check_buildid_dir_config(void)
466{
467 struct buildid_dir_config c;
468 c.dir = buildid_dir;
469 perf_config(buildid_dir_command_config, &c);
470}
471
472void set_buildid_dir(void)
473{
474 buildid_dir[0] = '\0';
475
476 /* try config file */
477 check_buildid_dir_config();
478
479 /* default to $HOME/.debug */
480 if (buildid_dir[0] == '\0') {
481 char *v = getenv("HOME");
482 if (v) {
483 snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
484 v, DEBUG_CACHE_DIR);
485 } else {
486 strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
487 }
488 buildid_dir[MAXPATHLEN-1] = '\0';
489 }
490 /* for communicating with external commands */
491 setenv("PERF_BUILDID_DIR", buildid_dir, 1);
492}
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 4e01490e51e5..0f9b8d7a7d7e 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -20,7 +20,7 @@ static int default_cpu_map(void)
20 return nr_cpus; 20 return nr_cpus;
21} 21}
22 22
23int read_cpu_map(void) 23static int read_all_cpu_map(void)
24{ 24{
25 FILE *onlnf; 25 FILE *onlnf;
26 int nr_cpus = 0; 26 int nr_cpus = 0;
@@ -57,3 +57,58 @@ int read_cpu_map(void)
57 57
58 return default_cpu_map(); 58 return default_cpu_map();
59} 59}
60
61int read_cpu_map(const char *cpu_list)
62{
63 unsigned long start_cpu, end_cpu = 0;
64 char *p = NULL;
65 int i, nr_cpus = 0;
66
67 if (!cpu_list)
68 return read_all_cpu_map();
69
70 if (!isdigit(*cpu_list))
71 goto invalid;
72
73 while (isdigit(*cpu_list)) {
74 p = NULL;
75 start_cpu = strtoul(cpu_list, &p, 0);
76 if (start_cpu >= INT_MAX
77 || (*p != '\0' && *p != ',' && *p != '-'))
78 goto invalid;
79
80 if (*p == '-') {
81 cpu_list = ++p;
82 p = NULL;
83 end_cpu = strtoul(cpu_list, &p, 0);
84
85 if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
86 goto invalid;
87
88 if (end_cpu < start_cpu)
89 goto invalid;
90 } else {
91 end_cpu = start_cpu;
92 }
93
94 for (; start_cpu <= end_cpu; start_cpu++) {
95 /* check for duplicates */
96 for (i = 0; i < nr_cpus; i++)
97 if (cpumap[i] == (int)start_cpu)
98 goto invalid;
99
100 assert(nr_cpus < MAX_NR_CPUS);
101 cpumap[nr_cpus++] = (int)start_cpu;
102 }
103 if (*p)
104 ++p;
105
106 cpu_list = p;
107 }
108 if (nr_cpus > 0)
109 return nr_cpus;
110
111 return default_cpu_map();
112invalid:
113 return -1;
114}
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 86c78bb33098..3e60f56e490e 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -1,7 +1,7 @@
1#ifndef __PERF_CPUMAP_H 1#ifndef __PERF_CPUMAP_H
2#define __PERF_CPUMAP_H 2#define __PERF_CPUMAP_H
3 3
4extern int read_cpu_map(void); 4extern int read_cpu_map(const char *cpu_list);
5extern int cpumap[]; 5extern int cpumap[];
6 6
7#endif /* __PERF_CPUMAP_H */ 7#endif /* __PERF_CPUMAP_H */
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 6cddff2bc970..f9c7e3ad1aa7 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -23,7 +23,7 @@ int eprintf(int level, const char *fmt, ...)
23 if (verbose >= level) { 23 if (verbose >= level) {
24 va_start(args, fmt); 24 va_start(args, fmt);
25 if (use_browser > 0) 25 if (use_browser > 0)
26 ret = browser__show_help(fmt, args); 26 ret = ui_helpline__show_help(fmt, args);
27 else 27 else
28 ret = vfprintf(stderr, fmt, args); 28 ret = vfprintf(stderr, fmt, args);
29 va_end(args); 29 va_end(args);
@@ -86,12 +86,10 @@ void trace_event(event_t *event)
86 dump_printf_color(" ", color); 86 dump_printf_color(" ", color);
87 for (j = 0; j < 15-(i & 15); j++) 87 for (j = 0; j < 15-(i & 15); j++)
88 dump_printf_color(" ", color); 88 dump_printf_color(" ", color);
89 for (j = 0; j < (i & 15); j++) { 89 for (j = i & ~15; j <= i; j++) {
90 if (isprint(raw_event[i-15+j])) 90 dump_printf_color("%c", color,
91 dump_printf_color("%c", color, 91 isprint(raw_event[j]) ?
92 raw_event[i-15+j]); 92 raw_event[j] : '.');
93 else
94 dump_printf_color(".", color);
95 } 93 }
96 dump_printf_color("\n", color); 94 dump_printf_color("\n", color);
97 } 95 }
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 047ac3324ebe..7a17ee061bcb 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -14,7 +14,7 @@ void trace_event(event_t *event);
14struct ui_progress; 14struct ui_progress;
15 15
16#ifdef NO_NEWT_SUPPORT 16#ifdef NO_NEWT_SUPPORT
17static inline int browser__show_help(const char *format __used, va_list ap __used) 17static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
18{ 18{
19 return 0; 19 return 0;
20} 20}
@@ -30,10 +30,9 @@ static inline void ui_progress__update(struct ui_progress *self __used,
30 30
31static inline void ui_progress__delete(struct ui_progress *self __used) {} 31static inline void ui_progress__delete(struct ui_progress *self __used) {}
32#else 32#else
33int browser__show_help(const char *format, va_list ap); 33extern char ui_helpline__last_msg[];
34struct ui_progress *ui_progress__new(const char *title, u64 total); 34int ui_helpline__show_help(const char *format, va_list ap);
35void ui_progress__update(struct ui_progress *self, u64 curr); 35#include "ui/progress.h"
36void ui_progress__delete(struct ui_progress *self);
37#endif 36#endif
38 37
39#endif /* __PERF_DEBUG_H */ 38#endif /* __PERF_DEBUG_H */
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 50771b5813ee..dab9e754a281 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,
151 continue; 151 continue;
152 pbf += n + 3; 152 pbf += n + 3;
153 if (*pbf == 'x') { /* vm_exec */ 153 if (*pbf == 'x') { /* vm_exec */
154 u64 vm_pgoff;
155 char *execname = strchr(bf, '/'); 154 char *execname = strchr(bf, '/');
156 155
157 /* Catch VDSO */ 156 /* Catch VDSO */
@@ -162,12 +161,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
162 continue; 161 continue;
163 162
164 pbf += 3; 163 pbf += 3;
165 n = hex2u64(pbf, &vm_pgoff); 164 n = hex2u64(pbf, &ev.mmap.pgoff);
166 /* pgoff is in bytes, not pages */
167 if (n >= 0)
168 ev.mmap.pgoff = vm_pgoff << getpagesize();
169 else
170 ev.mmap.pgoff = 0;
171 165
172 size = strlen(execname); 166 size = strlen(execname);
173 execname[size - 1] = '\0'; /* Remove \n */ 167 execname[size - 1] = '\0'; /* Remove \n */
@@ -340,41 +334,41 @@ int event__synthesize_kernel_mmap(event__handler_t process,
340 return process(&ev, session); 334 return process(&ev, session);
341} 335}
342 336
343static void thread__comm_adjust(struct thread *self) 337static void thread__comm_adjust(struct thread *self, struct hists *hists)
344{ 338{
345 char *comm = self->comm; 339 char *comm = self->comm;
346 340
347 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && 341 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
348 (!symbol_conf.comm_list || 342 (!symbol_conf.comm_list ||
349 strlist__has_entry(symbol_conf.comm_list, comm))) { 343 strlist__has_entry(symbol_conf.comm_list, comm))) {
350 unsigned int slen = strlen(comm); 344 u16 slen = strlen(comm);
351 345
352 if (slen > comms__col_width) { 346 if (hists__new_col_len(hists, HISTC_COMM, slen))
353 comms__col_width = slen; 347 hists__set_col_len(hists, HISTC_THREAD, slen + 6);
354 threads__col_width = slen + 6;
355 }
356 } 348 }
357} 349}
358 350
359static int thread__set_comm_adjust(struct thread *self, const char *comm) 351static int thread__set_comm_adjust(struct thread *self, const char *comm,
352 struct hists *hists)
360{ 353{
361 int ret = thread__set_comm(self, comm); 354 int ret = thread__set_comm(self, comm);
362 355
363 if (ret) 356 if (ret)
364 return ret; 357 return ret;
365 358
366 thread__comm_adjust(self); 359 thread__comm_adjust(self, hists);
367 360
368 return 0; 361 return 0;
369} 362}
370 363
371int event__process_comm(event_t *self, struct perf_session *session) 364int event__process_comm(event_t *self, struct perf_session *session)
372{ 365{
373 struct thread *thread = perf_session__findnew(session, self->comm.pid); 366 struct thread *thread = perf_session__findnew(session, self->comm.tid);
374 367
375 dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); 368 dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid);
376 369
377 if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { 370 if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm,
371 &session->hists)) {
378 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); 372 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
379 return -1; 373 return -1;
380 } 374 }
@@ -456,6 +450,7 @@ static int event__process_kernel_mmap(event_t *self,
456 goto out_problem; 450 goto out_problem;
457 451
458 map->dso->short_name = name; 452 map->dso->short_name = name;
453 map->dso->sname_alloc = 1;
459 map->end = map->start + self->mmap.len; 454 map->end = map->start + self->mmap.len;
460 } else if (is_kernel_mmap) { 455 } else if (is_kernel_mmap) {
461 const char *symbol_name = (self->mmap.filename + 456 const char *symbol_name = (self->mmap.filename +
@@ -514,12 +509,13 @@ int event__process_mmap(event_t *self, struct perf_session *session)
514 if (machine == NULL) 509 if (machine == NULL)
515 goto out_problem; 510 goto out_problem;
516 thread = perf_session__findnew(session, self->mmap.pid); 511 thread = perf_session__findnew(session, self->mmap.pid);
512 if (thread == NULL)
513 goto out_problem;
517 map = map__new(&machine->user_dsos, self->mmap.start, 514 map = map__new(&machine->user_dsos, self->mmap.start,
518 self->mmap.len, self->mmap.pgoff, 515 self->mmap.len, self->mmap.pgoff,
519 self->mmap.pid, self->mmap.filename, 516 self->mmap.pid, self->mmap.filename,
520 MAP__FUNCTION, session->cwd, session->cwdlen); 517 MAP__FUNCTION);
521 518 if (map == NULL)
522 if (thread == NULL || map == NULL)
523 goto out_problem; 519 goto out_problem;
524 520
525 thread__insert_map(thread, map); 521 thread__insert_map(thread, map);
@@ -532,19 +528,16 @@ out_problem:
532 528
533int event__process_task(event_t *self, struct perf_session *session) 529int event__process_task(event_t *self, struct perf_session *session)
534{ 530{
535 struct thread *thread = perf_session__findnew(session, self->fork.pid); 531 struct thread *thread = perf_session__findnew(session, self->fork.tid);
536 struct thread *parent = perf_session__findnew(session, self->fork.ppid); 532 struct thread *parent = perf_session__findnew(session, self->fork.ptid);
537 533
538 dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, 534 dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
539 self->fork.ppid, self->fork.ptid); 535 self->fork.ppid, self->fork.ptid);
540 /*
541 * A thread clone will have the same PID for both parent and child.
542 */
543 if (thread == parent)
544 return 0;
545 536
546 if (self->header.type == PERF_RECORD_EXIT) 537 if (self->header.type == PERF_RECORD_EXIT) {
538 perf_session__remove_thread(session, thread);
547 return 0; 539 return 0;
540 }
548 541
549 if (thread == NULL || parent == NULL || 542 if (thread == NULL || parent == NULL ||
550 thread__fork(thread, parent) < 0) { 543 thread__fork(thread, parent) < 0) {
@@ -555,6 +548,26 @@ int event__process_task(event_t *self, struct perf_session *session)
555 return 0; 548 return 0;
556} 549}
557 550
551int event__process(event_t *event, struct perf_session *session)
552{
553 switch (event->header.type) {
554 case PERF_RECORD_COMM:
555 event__process_comm(event, session);
556 break;
557 case PERF_RECORD_MMAP:
558 event__process_mmap(event, session);
559 break;
560 case PERF_RECORD_FORK:
561 case PERF_RECORD_EXIT:
562 event__process_task(event, session);
563 break;
564 default:
565 break;
566 }
567
568 return 0;
569}
570
558void thread__find_addr_map(struct thread *self, 571void thread__find_addr_map(struct thread *self,
559 struct perf_session *session, u8 cpumode, 572 struct perf_session *session, u8 cpumode,
560 enum map_type type, pid_t pid, u64 addr, 573 enum map_type type, pid_t pid, u64 addr,
@@ -644,27 +657,49 @@ void thread__find_addr_location(struct thread *self,
644 al->sym = NULL; 657 al->sym = NULL;
645} 658}
646 659
647static void dso__calc_col_width(struct dso *self) 660static void dso__calc_col_width(struct dso *self, struct hists *hists)
648{ 661{
649 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && 662 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
650 (!symbol_conf.dso_list || 663 (!symbol_conf.dso_list ||
651 strlist__has_entry(symbol_conf.dso_list, self->name))) { 664 strlist__has_entry(symbol_conf.dso_list, self->name))) {
652 u16 slen = self->short_name_len; 665 u16 slen = dso__name_len(self);
653 if (verbose) 666 hists__new_col_len(hists, HISTC_DSO, slen);
654 slen = self->long_name_len;
655 if (dsos__col_width < slen)
656 dsos__col_width = slen;
657 } 667 }
658 668
659 self->slen_calculated = 1; 669 self->slen_calculated = 1;
660} 670}
661 671
662int event__preprocess_sample(const event_t *self, struct perf_session *session, 672int event__preprocess_sample(const event_t *self, struct perf_session *session,
663 struct addr_location *al, symbol_filter_t filter) 673 struct addr_location *al, struct sample_data *data,
674 symbol_filter_t filter)
664{ 675{
665 u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 676 u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
666 struct thread *thread = perf_session__findnew(session, self->ip.pid); 677 struct thread *thread;
678
679 event__parse_sample(self, session->sample_type, data);
680
681 dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n",
682 self->header.misc, data->pid, data->tid, data->ip,
683 data->period, data->cpu);
667 684
685 if (session->sample_type & PERF_SAMPLE_CALLCHAIN) {
686 unsigned int i;
687
688 dump_printf("... chain: nr:%Lu\n", data->callchain->nr);
689
690 if (!ip_callchain__valid(data->callchain, self)) {
691 pr_debug("call-chain problem with event, "
692 "skipping it.\n");
693 goto out_filtered;
694 }
695
696 if (dump_trace) {
697 for (i = 0; i < data->callchain->nr; i++)
698 dump_printf("..... %2d: %016Lx\n",
699 i, data->callchain->ips[i]);
700 }
701 }
702 thread = perf_session__findnew(session, self->ip.pid);
668 if (thread == NULL) 703 if (thread == NULL)
669 return -1; 704 return -1;
670 705
@@ -690,6 +725,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
690 al->map ? al->map->dso->long_name : 725 al->map ? al->map->dso->long_name :
691 al->level == 'H' ? "[hypervisor]" : "<not found>"); 726 al->level == 'H' ? "[hypervisor]" : "<not found>");
692 al->sym = NULL; 727 al->sym = NULL;
728 al->cpu = data->cpu;
693 729
694 if (al->map) { 730 if (al->map) {
695 if (symbol_conf.dso_list && 731 if (symbol_conf.dso_list &&
@@ -706,16 +742,17 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
706 * sampled. 742 * sampled.
707 */ 743 */
708 if (!sort_dso.elide && !al->map->dso->slen_calculated) 744 if (!sort_dso.elide && !al->map->dso->slen_calculated)
709 dso__calc_col_width(al->map->dso); 745 dso__calc_col_width(al->map->dso, &session->hists);
710 746
711 al->sym = map__find_symbol(al->map, al->addr, filter); 747 al->sym = map__find_symbol(al->map, al->addr, filter);
712 } else { 748 } else {
713 const unsigned int unresolved_col_width = BITS_PER_LONG / 4; 749 const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
714 750
715 if (dsos__col_width < unresolved_col_width && 751 if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width &&
716 !symbol_conf.col_width_list_str && !symbol_conf.field_sep && 752 !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
717 !symbol_conf.dso_list) 753 !symbol_conf.dso_list)
718 dsos__col_width = unresolved_col_width; 754 hists__set_col_len(&session->hists, HISTC_DSO,
755 unresolved_col_width);
719 } 756 }
720 757
721 if (symbol_conf.sym_list && al->sym && 758 if (symbol_conf.sym_list && al->sym &&
@@ -729,9 +766,9 @@ out_filtered:
729 return 0; 766 return 0;
730} 767}
731 768
732int event__parse_sample(event_t *event, u64 type, struct sample_data *data) 769int event__parse_sample(const event_t *event, u64 type, struct sample_data *data)
733{ 770{
734 u64 *array = event->sample.array; 771 const u64 *array = event->sample.array;
735 772
736 if (type & PERF_SAMPLE_IP) { 773 if (type & PERF_SAMPLE_IP) {
737 data->ip = event->ip.ip; 774 data->ip = event->ip.ip;
@@ -770,7 +807,8 @@ int event__parse_sample(event_t *event, u64 type, struct sample_data *data)
770 u32 *p = (u32 *)array; 807 u32 *p = (u32 *)array;
771 data->cpu = *p; 808 data->cpu = *p;
772 array++; 809 array++;
773 } 810 } else
811 data->cpu = -1;
774 812
775 if (type & PERF_SAMPLE_PERIOD) { 813 if (type & PERF_SAMPLE_PERIOD) {
776 data->period = *array; 814 data->period = *array;
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 8577085db067..8e790dae7026 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -154,11 +154,13 @@ int event__process_comm(event_t *self, struct perf_session *session);
154int event__process_lost(event_t *self, struct perf_session *session); 154int event__process_lost(event_t *self, struct perf_session *session);
155int event__process_mmap(event_t *self, struct perf_session *session); 155int event__process_mmap(event_t *self, struct perf_session *session);
156int event__process_task(event_t *self, struct perf_session *session); 156int event__process_task(event_t *self, struct perf_session *session);
157int event__process(event_t *event, struct perf_session *session);
157 158
158struct addr_location; 159struct addr_location;
159int event__preprocess_sample(const event_t *self, struct perf_session *session, 160int event__preprocess_sample(const event_t *self, struct perf_session *session,
160 struct addr_location *al, symbol_filter_t filter); 161 struct addr_location *al, struct sample_data *data,
161int event__parse_sample(event_t *event, u64 type, struct sample_data *data); 162 symbol_filter_t filter);
163int event__parse_sample(const event_t *event, u64 type, struct sample_data *data);
162 164
163extern const char *event__name[]; 165extern const char *event__name[];
164 166
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 1f62435f96c2..d7e67b167ea3 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -16,6 +16,8 @@
16#include "symbol.h" 16#include "symbol.h"
17#include "debug.h" 17#include "debug.h"
18 18
19static bool no_buildid_cache = false;
20
19/* 21/*
20 * Create new perf.data header attribute: 22 * Create new perf.data header attribute:
21 */ 23 */
@@ -385,8 +387,7 @@ static int perf_session__cache_build_ids(struct perf_session *self)
385 int ret; 387 int ret;
386 char debugdir[PATH_MAX]; 388 char debugdir[PATH_MAX];
387 389
388 snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), 390 snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
389 DEBUG_CACHE_DIR);
390 391
391 if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) 392 if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
392 return -1; 393 return -1;
@@ -471,7 +472,8 @@ static int perf_header__adds_write(struct perf_header *self, int fd)
471 } 472 }
472 buildid_sec->size = lseek(fd, 0, SEEK_CUR) - 473 buildid_sec->size = lseek(fd, 0, SEEK_CUR) -
473 buildid_sec->offset; 474 buildid_sec->offset;
474 perf_session__cache_build_ids(session); 475 if (!no_buildid_cache)
476 perf_session__cache_build_ids(session);
475 } 477 }
476 478
477 lseek(fd, sec_start, SEEK_SET); 479 lseek(fd, sec_start, SEEK_SET);
@@ -1190,3 +1192,8 @@ int event__process_build_id(event_t *self,
1190 session); 1192 session);
1191 return 0; 1193 return 0;
1192} 1194}
1195
1196void disable_buildid_cache(void)
1197{
1198 no_buildid_cache = true;
1199}
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index cbf7eae2ce09..be22ae6ef055 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -5,11 +5,61 @@
5#include "sort.h" 5#include "sort.h"
6#include <math.h> 6#include <math.h>
7 7
8enum hist_filter {
9 HIST_FILTER__DSO,
10 HIST_FILTER__THREAD,
11 HIST_FILTER__PARENT,
12};
13
8struct callchain_param callchain_param = { 14struct callchain_param callchain_param = {
9 .mode = CHAIN_GRAPH_REL, 15 .mode = CHAIN_GRAPH_REL,
10 .min_percent = 0.5 16 .min_percent = 0.5
11}; 17};
12 18
19u16 hists__col_len(struct hists *self, enum hist_column col)
20{
21 return self->col_len[col];
22}
23
24void hists__set_col_len(struct hists *self, enum hist_column col, u16 len)
25{
26 self->col_len[col] = len;
27}
28
29bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len)
30{
31 if (len > hists__col_len(self, col)) {
32 hists__set_col_len(self, col, len);
33 return true;
34 }
35 return false;
36}
37
38static void hists__reset_col_len(struct hists *self)
39{
40 enum hist_column col;
41
42 for (col = 0; col < HISTC_NR_COLS; ++col)
43 hists__set_col_len(self, col, 0);
44}
45
46static void hists__calc_col_len(struct hists *self, struct hist_entry *h)
47{
48 u16 len;
49
50 if (h->ms.sym)
51 hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen);
52
53 len = thread__comm_len(h->thread);
54 if (hists__new_col_len(self, HISTC_COMM, len))
55 hists__set_col_len(self, HISTC_THREAD, len + 6);
56
57 if (h->ms.map) {
58 len = dso__name_len(h->ms.map->dso);
59 hists__new_col_len(self, HISTC_DSO, len);
60 }
61}
62
13static void hist_entry__add_cpumode_period(struct hist_entry *self, 63static void hist_entry__add_cpumode_period(struct hist_entry *self,
14 unsigned int cpumode, u64 period) 64 unsigned int cpumode, u64 period)
15{ 65{
@@ -43,6 +93,8 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
43 if (self != NULL) { 93 if (self != NULL) {
44 *self = *template; 94 *self = *template;
45 self->nr_events = 1; 95 self->nr_events = 1;
96 if (self->ms.map)
97 self->ms.map->referenced = true;
46 if (symbol_conf.use_callchain) 98 if (symbol_conf.use_callchain)
47 callchain_init(self->callchain); 99 callchain_init(self->callchain);
48 } 100 }
@@ -50,11 +102,19 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
50 return self; 102 return self;
51} 103}
52 104
53static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) 105static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h)
54{ 106{
55 if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) 107 if (!h->filtered) {
56 self->max_sym_namelen = entry->ms.sym->namelen; 108 hists__calc_col_len(self, h);
57 ++self->nr_entries; 109 ++self->nr_entries;
110 }
111}
112
113static u8 symbol__parent_filter(const struct symbol *parent)
114{
115 if (symbol_conf.exclude_other && parent == NULL)
116 return 1 << HIST_FILTER__PARENT;
117 return 0;
58} 118}
59 119
60struct hist_entry *__hists__add_entry(struct hists *self, 120struct hist_entry *__hists__add_entry(struct hists *self,
@@ -70,10 +130,12 @@ struct hist_entry *__hists__add_entry(struct hists *self,
70 .map = al->map, 130 .map = al->map,
71 .sym = al->sym, 131 .sym = al->sym,
72 }, 132 },
133 .cpu = al->cpu,
73 .ip = al->addr, 134 .ip = al->addr,
74 .level = al->level, 135 .level = al->level,
75 .period = period, 136 .period = period,
76 .parent = sym_parent, 137 .parent = sym_parent,
138 .filtered = symbol__parent_filter(sym_parent),
77 }; 139 };
78 int cmp; 140 int cmp;
79 141
@@ -191,7 +253,7 @@ void hists__collapse_resort(struct hists *self)
191 tmp = RB_ROOT; 253 tmp = RB_ROOT;
192 next = rb_first(&self->entries); 254 next = rb_first(&self->entries);
193 self->nr_entries = 0; 255 self->nr_entries = 0;
194 self->max_sym_namelen = 0; 256 hists__reset_col_len(self);
195 257
196 while (next) { 258 while (next) {
197 n = rb_entry(next, struct hist_entry, rb_node); 259 n = rb_entry(next, struct hist_entry, rb_node);
@@ -248,7 +310,7 @@ void hists__output_resort(struct hists *self)
248 next = rb_first(&self->entries); 310 next = rb_first(&self->entries);
249 311
250 self->nr_entries = 0; 312 self->nr_entries = 0;
251 self->max_sym_namelen = 0; 313 hists__reset_col_len(self);
252 314
253 while (next) { 315 while (next) {
254 n = rb_entry(next, struct hist_entry, rb_node); 316 n = rb_entry(next, struct hist_entry, rb_node);
@@ -515,8 +577,9 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
515} 577}
516 578
517int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, 579int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
518 struct hists *pair_hists, bool show_displacement, 580 struct hists *hists, struct hists *pair_hists,
519 long displacement, bool color, u64 session_total) 581 bool show_displacement, long displacement,
582 bool color, u64 session_total)
520{ 583{
521 struct sort_entry *se; 584 struct sort_entry *se;
522 u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; 585 u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
@@ -620,24 +683,25 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
620 683
621 ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); 684 ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
622 ret += se->se_snprintf(self, s + ret, size - ret, 685 ret += se->se_snprintf(self, s + ret, size - ret,
623 se->se_width ? *se->se_width : 0); 686 hists__col_len(hists, se->se_width_idx));
624 } 687 }
625 688
626 return ret; 689 return ret;
627} 690}
628 691
629int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, 692int hist_entry__fprintf(struct hist_entry *self, struct hists *hists,
630 bool show_displacement, long displacement, FILE *fp, 693 struct hists *pair_hists, bool show_displacement,
631 u64 session_total) 694 long displacement, FILE *fp, u64 session_total)
632{ 695{
633 char bf[512]; 696 char bf[512];
634 hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, 697 hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists,
635 show_displacement, displacement, 698 show_displacement, displacement,
636 true, session_total); 699 true, session_total);
637 return fprintf(fp, "%s\n", bf); 700 return fprintf(fp, "%s\n", bf);
638} 701}
639 702
640static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, 703static size_t hist_entry__fprintf_callchain(struct hist_entry *self,
704 struct hists *hists, FILE *fp,
641 u64 session_total) 705 u64 session_total)
642{ 706{
643 int left_margin = 0; 707 int left_margin = 0;
@@ -645,7 +709,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp,
645 if (sort__first_dimension == SORT_COMM) { 709 if (sort__first_dimension == SORT_COMM) {
646 struct sort_entry *se = list_first_entry(&hist_entry__sort_list, 710 struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
647 typeof(*se), list); 711 typeof(*se), list);
648 left_margin = se->se_width ? *se->se_width : 0; 712 left_margin = hists__col_len(hists, se->se_width_idx);
649 left_margin -= thread__comm_len(self->thread); 713 left_margin -= thread__comm_len(self->thread);
650 } 714 }
651 715
@@ -716,17 +780,17 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
716 continue; 780 continue;
717 } 781 }
718 width = strlen(se->se_header); 782 width = strlen(se->se_header);
719 if (se->se_width) { 783 if (symbol_conf.col_width_list_str) {
720 if (symbol_conf.col_width_list_str) { 784 if (col_width) {
721 if (col_width) { 785 hists__set_col_len(self, se->se_width_idx,
722 *se->se_width = atoi(col_width); 786 atoi(col_width));
723 col_width = strchr(col_width, ','); 787 col_width = strchr(col_width, ',');
724 if (col_width) 788 if (col_width)
725 ++col_width; 789 ++col_width;
726 }
727 } 790 }
728 width = *se->se_width = max(*se->se_width, width);
729 } 791 }
792 if (!hists__new_col_len(self, se->se_width_idx, width))
793 width = hists__col_len(self, se->se_width_idx);
730 fprintf(fp, " %*s", width, se->se_header); 794 fprintf(fp, " %*s", width, se->se_header);
731 } 795 }
732 fprintf(fp, "\n"); 796 fprintf(fp, "\n");
@@ -749,9 +813,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
749 continue; 813 continue;
750 814
751 fprintf(fp, " "); 815 fprintf(fp, " ");
752 if (se->se_width) 816 width = hists__col_len(self, se->se_width_idx);
753 width = *se->se_width; 817 if (width == 0)
754 else
755 width = strlen(se->se_header); 818 width = strlen(se->se_header);
756 for (i = 0; i < width; i++) 819 for (i = 0; i < width; i++)
757 fprintf(fp, "."); 820 fprintf(fp, ".");
@@ -771,12 +834,12 @@ print_entries:
771 displacement = 0; 834 displacement = 0;
772 ++position; 835 ++position;
773 } 836 }
774 ret += hist_entry__fprintf(h, pair, show_displacement, 837 ret += hist_entry__fprintf(h, self, pair, show_displacement,
775 displacement, fp, self->stats.total_period); 838 displacement, fp, self->stats.total_period);
776 839
777 if (symbol_conf.use_callchain) 840 if (symbol_conf.use_callchain)
778 ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); 841 ret += hist_entry__fprintf_callchain(h, self, fp,
779 842 self->stats.total_period);
780 if (h->ms.map == NULL && verbose > 1) { 843 if (h->ms.map == NULL && verbose > 1) {
781 __map_groups__fprintf_maps(&h->thread->mg, 844 __map_groups__fprintf_maps(&h->thread->mg,
782 MAP__FUNCTION, verbose, fp); 845 MAP__FUNCTION, verbose, fp);
@@ -789,10 +852,52 @@ print_entries:
789 return ret; 852 return ret;
790} 853}
791 854
792enum hist_filter { 855/*
793 HIST_FILTER__DSO, 856 * See hists__fprintf to match the column widths
794 HIST_FILTER__THREAD, 857 */
795}; 858unsigned int hists__sort_list_width(struct hists *self)
859{
860 struct sort_entry *se;
861 int ret = 9; /* total % */
862
863 if (symbol_conf.show_cpu_utilization) {
864 ret += 7; /* count_sys % */
865 ret += 6; /* count_us % */
866 if (perf_guest) {
867 ret += 13; /* count_guest_sys % */
868 ret += 12; /* count_guest_us % */
869 }
870 }
871
872 if (symbol_conf.show_nr_samples)
873 ret += 11;
874
875 list_for_each_entry(se, &hist_entry__sort_list, list)
876 if (!se->elide)
877 ret += 2 + hists__col_len(self, se->se_width_idx);
878
879 if (verbose) /* Addr + origin */
880 ret += 3 + BITS_PER_LONG / 4;
881
882 return ret;
883}
884
885static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h,
886 enum hist_filter filter)
887{
888 h->filtered &= ~(1 << filter);
889 if (h->filtered)
890 return;
891
892 ++self->nr_entries;
893 if (h->ms.unfolded)
894 self->nr_entries += h->nr_rows;
895 h->row_offset = 0;
896 self->stats.total_period += h->period;
897 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
898
899 hists__calc_col_len(self, h);
900}
796 901
797void hists__filter_by_dso(struct hists *self, const struct dso *dso) 902void hists__filter_by_dso(struct hists *self, const struct dso *dso)
798{ 903{
@@ -800,7 +905,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso)
800 905
801 self->nr_entries = self->stats.total_period = 0; 906 self->nr_entries = self->stats.total_period = 0;
802 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 907 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
803 self->max_sym_namelen = 0; 908 hists__reset_col_len(self);
804 909
805 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 910 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
806 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 911 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
@@ -813,15 +918,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso)
813 continue; 918 continue;
814 } 919 }
815 920
816 h->filtered &= ~(1 << HIST_FILTER__DSO); 921 hists__remove_entry_filter(self, h, HIST_FILTER__DSO);
817 if (!h->filtered) {
818 ++self->nr_entries;
819 self->stats.total_period += h->period;
820 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
821 if (h->ms.sym &&
822 self->max_sym_namelen < h->ms.sym->namelen)
823 self->max_sym_namelen = h->ms.sym->namelen;
824 }
825 } 922 }
826} 923}
827 924
@@ -831,7 +928,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread)
831 928
832 self->nr_entries = self->stats.total_period = 0; 929 self->nr_entries = self->stats.total_period = 0;
833 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 930 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
834 self->max_sym_namelen = 0; 931 hists__reset_col_len(self);
835 932
836 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 933 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
837 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 934 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
@@ -840,15 +937,8 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread)
840 h->filtered |= (1 << HIST_FILTER__THREAD); 937 h->filtered |= (1 << HIST_FILTER__THREAD);
841 continue; 938 continue;
842 } 939 }
843 h->filtered &= ~(1 << HIST_FILTER__THREAD); 940
844 if (!h->filtered) { 941 hists__remove_entry_filter(self, h, HIST_FILTER__THREAD);
845 ++self->nr_entries;
846 self->stats.total_period += h->period;
847 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
848 if (h->ms.sym &&
849 self->max_sym_namelen < h->ms.sym->namelen)
850 self->max_sym_namelen = h->ms.sym->namelen;
851 }
852 } 942 }
853} 943}
854 944
@@ -893,9 +983,9 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
893 return 0; 983 return 0;
894} 984}
895 985
896static struct objdump_line *objdump_line__new(s64 offset, char *line) 986static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
897{ 987{
898 struct objdump_line *self = malloc(sizeof(*self)); 988 struct objdump_line *self = malloc(sizeof(*self) + privsize);
899 989
900 if (self != NULL) { 990 if (self != NULL) {
901 self->offset = offset; 991 self->offset = offset;
@@ -927,7 +1017,7 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
927} 1017}
928 1018
929static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, 1019static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
930 struct list_head *head) 1020 struct list_head *head, size_t privsize)
931{ 1021{
932 struct symbol *sym = self->ms.sym; 1022 struct symbol *sym = self->ms.sym;
933 struct objdump_line *objdump_line; 1023 struct objdump_line *objdump_line;
@@ -965,16 +1055,20 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
965 * Parse hexa addresses followed by ':' 1055 * Parse hexa addresses followed by ':'
966 */ 1056 */
967 line_ip = strtoull(tmp, &tmp2, 16); 1057 line_ip = strtoull(tmp, &tmp2, 16);
968 if (*tmp2 != ':') 1058 if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0')
969 line_ip = -1; 1059 line_ip = -1;
970 } 1060 }
971 1061
972 if (line_ip != -1) { 1062 if (line_ip != -1) {
973 u64 start = map__rip_2objdump(self->ms.map, sym->start); 1063 u64 start = map__rip_2objdump(self->ms.map, sym->start),
1064 end = map__rip_2objdump(self->ms.map, sym->end);
1065
974 offset = line_ip - start; 1066 offset = line_ip - start;
1067 if (offset < 0 || (u64)line_ip > end)
1068 offset = -1;
975 } 1069 }
976 1070
977 objdump_line = objdump_line__new(offset, line); 1071 objdump_line = objdump_line__new(offset, line, privsize);
978 if (objdump_line == NULL) { 1072 if (objdump_line == NULL) {
979 free(line); 1073 free(line);
980 return -1; 1074 return -1;
@@ -984,7 +1078,8 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
984 return 0; 1078 return 0;
985} 1079}
986 1080
987int hist_entry__annotate(struct hist_entry *self, struct list_head *head) 1081int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
1082 size_t privsize)
988{ 1083{
989 struct symbol *sym = self->ms.sym; 1084 struct symbol *sym = self->ms.sym;
990 struct map *map = self->ms.map; 1085 struct map *map = self->ms.map;
@@ -1037,7 +1132,7 @@ fallback:
1037 dso, dso->long_name, sym, sym->name); 1132 dso, dso->long_name, sym, sym->name);
1038 1133
1039 snprintf(command, sizeof(command), 1134 snprintf(command, sizeof(command),
1040 "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", 1135 "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
1041 map__rip_2objdump(map, sym->start), 1136 map__rip_2objdump(map, sym->start),
1042 map__rip_2objdump(map, sym->end), 1137 map__rip_2objdump(map, sym->end),
1043 filename, filename); 1138 filename, filename);
@@ -1049,7 +1144,7 @@ fallback:
1049 goto out_free_filename; 1144 goto out_free_filename;
1050 1145
1051 while (!feof(file)) 1146 while (!feof(file))
1052 if (hist_entry__parse_objdump_line(self, file, head) < 0) 1147 if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
1053 break; 1148 break;
1054 1149
1055 pclose(file); 1150 pclose(file);
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 83fa33a7b38b..587d375d3430 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -56,6 +56,16 @@ struct events_stats {
56 u32 nr_unknown_events; 56 u32 nr_unknown_events;
57}; 57};
58 58
59enum hist_column {
60 HISTC_SYMBOL,
61 HISTC_DSO,
62 HISTC_THREAD,
63 HISTC_COMM,
64 HISTC_PARENT,
65 HISTC_CPU,
66 HISTC_NR_COLS, /* Last entry */
67};
68
59struct hists { 69struct hists {
60 struct rb_node rb_node; 70 struct rb_node rb_node;
61 struct rb_root entries; 71 struct rb_root entries;
@@ -64,7 +74,7 @@ struct hists {
64 u64 config; 74 u64 config;
65 u64 event_stream; 75 u64 event_stream;
66 u32 type; 76 u32 type;
67 u32 max_sym_namelen; 77 u16 col_len[HISTC_NR_COLS];
68}; 78};
69 79
70struct hist_entry *__hists__add_entry(struct hists *self, 80struct hist_entry *__hists__add_entry(struct hists *self,
@@ -72,12 +82,13 @@ struct hist_entry *__hists__add_entry(struct hists *self,
72 struct symbol *parent, u64 period); 82 struct symbol *parent, u64 period);
73extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); 83extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
74extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); 84extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
75int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, 85int hist_entry__fprintf(struct hist_entry *self, struct hists *hists,
76 bool show_displacement, long displacement, FILE *fp, 86 struct hists *pair_hists, bool show_displacement,
77 u64 total); 87 long displacement, FILE *fp, u64 total);
78int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, 88int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
79 struct hists *pair_hists, bool show_displacement, 89 struct hists *hists, struct hists *pair_hists,
80 long displacement, bool color, u64 total); 90 bool show_displacement, long displacement,
91 bool color, u64 total);
81void hist_entry__free(struct hist_entry *); 92void hist_entry__free(struct hist_entry *);
82 93
83void hists__output_resort(struct hists *self); 94void hists__output_resort(struct hists *self);
@@ -90,11 +101,16 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
90 bool show_displacement, FILE *fp); 101 bool show_displacement, FILE *fp);
91 102
92int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); 103int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
93int hist_entry__annotate(struct hist_entry *self, struct list_head *head); 104int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
105 size_t privsize);
94 106
95void hists__filter_by_dso(struct hists *self, const struct dso *dso); 107void hists__filter_by_dso(struct hists *self, const struct dso *dso);
96void hists__filter_by_thread(struct hists *self, const struct thread *thread); 108void hists__filter_by_thread(struct hists *self, const struct thread *thread);
97 109
110u16 hists__col_len(struct hists *self, enum hist_column col);
111void hists__set_col_len(struct hists *self, enum hist_column col, u16 len);
112bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len);
113
98#ifdef NO_NEWT_SUPPORT 114#ifdef NO_NEWT_SUPPORT
99static inline int hists__browse(struct hists *self __used, 115static inline int hists__browse(struct hists *self __used,
100 const char *helpline __used, 116 const char *helpline __used,
@@ -126,4 +142,7 @@ int hist_entry__tui_annotate(struct hist_entry *self);
126 142
127int hists__tui_browse_tree(struct rb_root *self, const char *help); 143int hists__tui_browse_tree(struct rb_root *self, const char *help);
128#endif 144#endif
145
146unsigned int hists__sort_list_width(struct hists *self);
147
129#endif /* __PERF_HIST_H */ 148#endif /* __PERF_HIST_H */
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h
index dbe4b814382a..f5ca26e53fbb 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,
15 begin->prev->next = end->next; 15 begin->prev->next = end->next;
16 end->next->prev = begin->prev; 16 end->next->prev = begin->prev;
17} 17}
18
19/**
20 * list_for_each_from - iterate over a list from one of its nodes
21 * @pos: the &struct list_head to use as a loop cursor, from where to start
22 * @head: the head for your list.
23 */
24#define list_for_each_from(pos, head) \
25 for (; prefetch(pos->next), pos != (head); pos = pos->next)
18#endif 26#endif
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h
index 196862a81a21..12de3b8112f9 100644
--- a/tools/perf/util/include/linux/types.h
+++ b/tools/perf/util/include/linux/types.h
@@ -6,4 +6,16 @@
6#define DECLARE_BITMAP(name,bits) \ 6#define DECLARE_BITMAP(name,bits) \
7 unsigned long name[BITS_TO_LONGS(bits)] 7 unsigned long name[BITS_TO_LONGS(bits)]
8 8
9struct list_head {
10 struct list_head *next, *prev;
11};
12
13struct hlist_head {
14 struct hlist_node *first;
15};
16
17struct hlist_node {
18 struct hlist_node *next, **pprev;
19};
20
9#endif 21#endif
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index e672f2fef65b..3a7eb6ec0eec 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)
17 return strcmp(filename, "//anon") == 0; 17 return strcmp(filename, "//anon") == 0;
18} 18}
19 19
20static int strcommon(const char *pathname, char *cwd, int cwdlen)
21{
22 int n = 0;
23
24 while (n < cwdlen && pathname[n] == cwd[n])
25 ++n;
26
27 return n;
28}
29
30void map__init(struct map *self, enum map_type type, 20void map__init(struct map *self, enum map_type type,
31 u64 start, u64 end, u64 pgoff, struct dso *dso) 21 u64 start, u64 end, u64 pgoff, struct dso *dso)
32{ 22{
@@ -39,11 +29,12 @@ void map__init(struct map *self, enum map_type type,
39 self->unmap_ip = map__unmap_ip; 29 self->unmap_ip = map__unmap_ip;
40 RB_CLEAR_NODE(&self->rb_node); 30 RB_CLEAR_NODE(&self->rb_node);
41 self->groups = NULL; 31 self->groups = NULL;
32 self->referenced = false;
42} 33}
43 34
44struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, 35struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
45 u64 pgoff, u32 pid, char *filename, 36 u64 pgoff, u32 pid, char *filename,
46 enum map_type type, char *cwd, int cwdlen) 37 enum map_type type)
47{ 38{
48 struct map *self = malloc(sizeof(*self)); 39 struct map *self = malloc(sizeof(*self));
49 40
@@ -52,16 +43,6 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
52 struct dso *dso; 43 struct dso *dso;
53 int anon; 44 int anon;
54 45
55 if (cwd) {
56 int n = strcommon(filename, cwd, cwdlen);
57
58 if (n == cwdlen) {
59 snprintf(newfilename, sizeof(newfilename),
60 ".%s", filename + n);
61 filename = newfilename;
62 }
63 }
64
65 anon = is_anon_memory(filename); 46 anon = is_anon_memory(filename);
66 47
67 if (anon) { 48 if (anon) {
@@ -248,6 +229,39 @@ void map_groups__init(struct map_groups *self)
248 self->machine = NULL; 229 self->machine = NULL;
249} 230}
250 231
232static void maps__delete(struct rb_root *self)
233{
234 struct rb_node *next = rb_first(self);
235
236 while (next) {
237 struct map *pos = rb_entry(next, struct map, rb_node);
238
239 next = rb_next(&pos->rb_node);
240 rb_erase(&pos->rb_node, self);
241 map__delete(pos);
242 }
243}
244
245static void maps__delete_removed(struct list_head *self)
246{
247 struct map *pos, *n;
248
249 list_for_each_entry_safe(pos, n, self, node) {
250 list_del(&pos->node);
251 map__delete(pos);
252 }
253}
254
255void map_groups__exit(struct map_groups *self)
256{
257 int i;
258
259 for (i = 0; i < MAP__NR_TYPES; ++i) {
260 maps__delete(&self->maps[i]);
261 maps__delete_removed(&self->removed_maps[i]);
262 }
263}
264
251void map_groups__flush(struct map_groups *self) 265void map_groups__flush(struct map_groups *self)
252{ 266{
253 int type; 267 int type;
@@ -374,6 +388,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
374{ 388{
375 struct rb_root *root = &self->maps[map->type]; 389 struct rb_root *root = &self->maps[map->type];
376 struct rb_node *next = rb_first(root); 390 struct rb_node *next = rb_first(root);
391 int err = 0;
377 392
378 while (next) { 393 while (next) {
379 struct map *pos = rb_entry(next, struct map, rb_node); 394 struct map *pos = rb_entry(next, struct map, rb_node);
@@ -390,20 +405,16 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
390 405
391 rb_erase(&pos->rb_node, root); 406 rb_erase(&pos->rb_node, root);
392 /* 407 /*
393 * We may have references to this map, for instance in some
394 * hist_entry instances, so just move them to a separate
395 * list.
396 */
397 list_add_tail(&pos->node, &self->removed_maps[map->type]);
398 /*
399 * Now check if we need to create new maps for areas not 408 * Now check if we need to create new maps for areas not
400 * overlapped by the new map: 409 * overlapped by the new map:
401 */ 410 */
402 if (map->start > pos->start) { 411 if (map->start > pos->start) {
403 struct map *before = map__clone(pos); 412 struct map *before = map__clone(pos);
404 413
405 if (before == NULL) 414 if (before == NULL) {
406 return -ENOMEM; 415 err = -ENOMEM;
416 goto move_map;
417 }
407 418
408 before->end = map->start - 1; 419 before->end = map->start - 1;
409 map_groups__insert(self, before); 420 map_groups__insert(self, before);
@@ -414,14 +425,27 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
414 if (map->end < pos->end) { 425 if (map->end < pos->end) {
415 struct map *after = map__clone(pos); 426 struct map *after = map__clone(pos);
416 427
417 if (after == NULL) 428 if (after == NULL) {
418 return -ENOMEM; 429 err = -ENOMEM;
430 goto move_map;
431 }
419 432
420 after->start = map->end + 1; 433 after->start = map->end + 1;
421 map_groups__insert(self, after); 434 map_groups__insert(self, after);
422 if (verbose >= 2) 435 if (verbose >= 2)
423 map__fprintf(after, fp); 436 map__fprintf(after, fp);
424 } 437 }
438move_map:
439 /*
440 * If we have references, just move them to a separate list.
441 */
442 if (pos->referenced)
443 list_add_tail(&pos->node, &self->removed_maps[map->type]);
444 else
445 map__delete(pos);
446
447 if (err)
448 return err;
425 } 449 }
426 450
427 return 0; 451 return 0;
@@ -493,6 +517,11 @@ void maps__insert(struct rb_root *maps, struct map *map)
493 rb_insert_color(&map->rb_node, maps); 517 rb_insert_color(&map->rb_node, maps);
494} 518}
495 519
520void maps__remove(struct rb_root *self, struct map *map)
521{
522 rb_erase(&map->rb_node, self);
523}
524
496struct map *maps__find(struct rb_root *maps, u64 ip) 525struct map *maps__find(struct rb_root *maps, u64 ip)
497{ 526{
498 struct rb_node **p = &maps->rb_node; 527 struct rb_node **p = &maps->rb_node;
@@ -526,6 +555,31 @@ int machine__init(struct machine *self, const char *root_dir, pid_t pid)
526 return self->root_dir == NULL ? -ENOMEM : 0; 555 return self->root_dir == NULL ? -ENOMEM : 0;
527} 556}
528 557
558static void dsos__delete(struct list_head *self)
559{
560 struct dso *pos, *n;
561
562 list_for_each_entry_safe(pos, n, self, node) {
563 list_del(&pos->node);
564 dso__delete(pos);
565 }
566}
567
568void machine__exit(struct machine *self)
569{
570 map_groups__exit(&self->kmaps);
571 dsos__delete(&self->user_dsos);
572 dsos__delete(&self->kernel_dsos);
573 free(self->root_dir);
574 self->root_dir = NULL;
575}
576
577void machine__delete(struct machine *self)
578{
579 machine__exit(self);
580 free(self);
581}
582
529struct machine *machines__add(struct rb_root *self, pid_t pid, 583struct machine *machines__add(struct rb_root *self, pid_t pid,
530 const char *root_dir) 584 const char *root_dir)
531{ 585{
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index f39134512829..78575796d5f3 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -29,7 +29,8 @@ struct map {
29 }; 29 };
30 u64 start; 30 u64 start;
31 u64 end; 31 u64 end;
32 enum map_type type; 32 u8 /* enum map_type */ type;
33 bool referenced;
33 u32 priv; 34 u32 priv;
34 u64 pgoff; 35 u64 pgoff;
35 36
@@ -106,7 +107,7 @@ void map__init(struct map *self, enum map_type type,
106 u64 start, u64 end, u64 pgoff, struct dso *dso); 107 u64 start, u64 end, u64 pgoff, struct dso *dso);
107struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, 108struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
108 u64 pgoff, u32 pid, char *filename, 109 u64 pgoff, u32 pid, char *filename,
109 enum map_type type, char *cwd, int cwdlen); 110 enum map_type type);
110void map__delete(struct map *self); 111void map__delete(struct map *self);
111struct map *map__clone(struct map *self); 112struct map *map__clone(struct map *self);
112int map__overlap(struct map *l, struct map *r); 113int map__overlap(struct map *l, struct map *r);
@@ -125,8 +126,10 @@ void map__reloc_vmlinux(struct map *self);
125size_t __map_groups__fprintf_maps(struct map_groups *self, 126size_t __map_groups__fprintf_maps(struct map_groups *self,
126 enum map_type type, int verbose, FILE *fp); 127 enum map_type type, int verbose, FILE *fp);
127void maps__insert(struct rb_root *maps, struct map *map); 128void maps__insert(struct rb_root *maps, struct map *map);
129void maps__remove(struct rb_root *self, struct map *map);
128struct map *maps__find(struct rb_root *maps, u64 addr); 130struct map *maps__find(struct rb_root *maps, u64 addr);
129void map_groups__init(struct map_groups *self); 131void map_groups__init(struct map_groups *self);
132void map_groups__exit(struct map_groups *self);
130int map_groups__clone(struct map_groups *self, 133int map_groups__clone(struct map_groups *self,
131 struct map_groups *parent, enum map_type type); 134 struct map_groups *parent, enum map_type type);
132size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); 135size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp);
@@ -142,6 +145,8 @@ struct machine *machines__find(struct rb_root *self, pid_t pid);
142struct machine *machines__findnew(struct rb_root *self, pid_t pid); 145struct machine *machines__findnew(struct rb_root *self, pid_t pid);
143char *machine__mmap_name(struct machine *self, char *bf, size_t size); 146char *machine__mmap_name(struct machine *self, char *bf, size_t size);
144int machine__init(struct machine *self, const char *root_dir, pid_t pid); 147int machine__init(struct machine *self, const char *root_dir, pid_t pid);
148void machine__exit(struct machine *self);
149void machine__delete(struct machine *self);
145 150
146/* 151/*
147 * Default guest kernel is defined by parameter --guestkallsyms 152 * Default guest kernel is defined by parameter --guestkallsyms
@@ -163,6 +168,11 @@ static inline void map_groups__insert(struct map_groups *self, struct map *map)
163 map->groups = self; 168 map->groups = self;
164} 169}
165 170
171static inline void map_groups__remove(struct map_groups *self, struct map *map)
172{
173 maps__remove(&self->maps[map->type], map);
174}
175
166static inline struct map *map_groups__find(struct map_groups *self, 176static inline struct map *map_groups__find(struct map_groups *self,
167 enum map_type type, u64 addr) 177 enum map_type type, u64 addr)
168{ 178{
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
deleted file mode 100644
index cf182ca132fe..000000000000
--- a/tools/perf/util/newt.c
+++ /dev/null
@@ -1,1167 +0,0 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4/*
5 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
6 * the build if it isn't defined. Use the equivalent one that glibc
7 * has on features.h.
8 */
9#include <features.h>
10#ifndef HAVE_LONG_LONG
11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
12#endif
13#include <slang.h>
14#include <stdlib.h>
15#include <newt.h>
16#include <sys/ttydefaults.h>
17
18#include "cache.h"
19#include "hist.h"
20#include "pstack.h"
21#include "session.h"
22#include "sort.h"
23#include "symbol.h"
24
25#if SLANG_VERSION < 20104
26#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
27#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
28#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
29 (char *)fg, (char *)bg)
30#else
31#define slsmg_printf SLsmg_printf
32#define slsmg_write_nstring SLsmg_write_nstring
33#define sltt_set_color SLtt_set_color
34#endif
35
36struct ui_progress {
37 newtComponent form, scale;
38};
39
40struct ui_progress *ui_progress__new(const char *title, u64 total)
41{
42 struct ui_progress *self = malloc(sizeof(*self));
43
44 if (self != NULL) {
45 int cols;
46 newtGetScreenSize(&cols, NULL);
47 cols -= 4;
48 newtCenteredWindow(cols, 1, title);
49 self->form = newtForm(NULL, NULL, 0);
50 if (self->form == NULL)
51 goto out_free_self;
52 self->scale = newtScale(0, 0, cols, total);
53 if (self->scale == NULL)
54 goto out_free_form;
55 newtFormAddComponent(self->form, self->scale);
56 newtRefresh();
57 }
58
59 return self;
60
61out_free_form:
62 newtFormDestroy(self->form);
63out_free_self:
64 free(self);
65 return NULL;
66}
67
68void ui_progress__update(struct ui_progress *self, u64 curr)
69{
70 newtScaleSet(self->scale, curr);
71 newtRefresh();
72}
73
74void ui_progress__delete(struct ui_progress *self)
75{
76 newtFormDestroy(self->form);
77 newtPopWindow();
78 free(self);
79}
80
81static void ui_helpline__pop(void)
82{
83 newtPopHelpLine();
84}
85
86static void ui_helpline__push(const char *msg)
87{
88 newtPushHelpLine(msg);
89}
90
91static void ui_helpline__vpush(const char *fmt, va_list ap)
92{
93 char *s;
94
95 if (vasprintf(&s, fmt, ap) < 0)
96 vfprintf(stderr, fmt, ap);
97 else {
98 ui_helpline__push(s);
99 free(s);
100 }
101}
102
103static void ui_helpline__fpush(const char *fmt, ...)
104{
105 va_list ap;
106
107 va_start(ap, fmt);
108 ui_helpline__vpush(fmt, ap);
109 va_end(ap);
110}
111
112static void ui_helpline__puts(const char *msg)
113{
114 ui_helpline__pop();
115 ui_helpline__push(msg);
116}
117
118static char browser__last_msg[1024];
119
120int browser__show_help(const char *format, va_list ap)
121{
122 int ret;
123 static int backlog;
124
125 ret = vsnprintf(browser__last_msg + backlog,
126 sizeof(browser__last_msg) - backlog, format, ap);
127 backlog += ret;
128
129 if (browser__last_msg[backlog - 1] == '\n') {
130 ui_helpline__puts(browser__last_msg);
131 newtRefresh();
132 backlog = 0;
133 }
134
135 return ret;
136}
137
138static void newt_form__set_exit_keys(newtComponent self)
139{
140 newtFormAddHotKey(self, NEWT_KEY_LEFT);
141 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
142 newtFormAddHotKey(self, 'Q');
143 newtFormAddHotKey(self, 'q');
144 newtFormAddHotKey(self, CTRL('c'));
145}
146
147static newtComponent newt_form__new(void)
148{
149 newtComponent self = newtForm(NULL, NULL, 0);
150 if (self)
151 newt_form__set_exit_keys(self);
152 return self;
153}
154
155static int popup_menu(int argc, char * const argv[])
156{
157 struct newtExitStruct es;
158 int i, rc = -1, max_len = 5;
159 newtComponent listbox, form = newt_form__new();
160
161 if (form == NULL)
162 return -1;
163
164 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
165 if (listbox == NULL)
166 goto out_destroy_form;
167
168 newtFormAddComponent(form, listbox);
169
170 for (i = 0; i < argc; ++i) {
171 int len = strlen(argv[i]);
172 if (len > max_len)
173 max_len = len;
174 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
175 goto out_destroy_form;
176 }
177
178 newtCenteredWindow(max_len, argc, NULL);
179 newtFormRun(form, &es);
180 rc = newtListboxGetCurrent(listbox) - NULL;
181 if (es.reason == NEWT_EXIT_HOTKEY)
182 rc = -1;
183 newtPopWindow();
184out_destroy_form:
185 newtFormDestroy(form);
186 return rc;
187}
188
189static int ui__help_window(const char *text)
190{
191 struct newtExitStruct es;
192 newtComponent tb, form = newt_form__new();
193 int rc = -1;
194 int max_len = 0, nr_lines = 0;
195 const char *t;
196
197 if (form == NULL)
198 return -1;
199
200 t = text;
201 while (1) {
202 const char *sep = strchr(t, '\n');
203 int len;
204
205 if (sep == NULL)
206 sep = strchr(t, '\0');
207 len = sep - t;
208 if (max_len < len)
209 max_len = len;
210 ++nr_lines;
211 if (*sep == '\0')
212 break;
213 t = sep + 1;
214 }
215
216 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
217 if (tb == NULL)
218 goto out_destroy_form;
219
220 newtTextboxSetText(tb, text);
221 newtFormAddComponent(form, tb);
222 newtCenteredWindow(max_len, nr_lines, NULL);
223 newtFormRun(form, &es);
224 newtPopWindow();
225 rc = 0;
226out_destroy_form:
227 newtFormDestroy(form);
228 return rc;
229}
230
231static bool dialog_yesno(const char *msg)
232{
233 /* newtWinChoice should really be accepting const char pointers... */
234 char yes[] = "Yes", no[] = "No";
235 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
236}
237
238static void ui__error_window(const char *fmt, ...)
239{
240 va_list ap;
241
242 va_start(ap, fmt);
243 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
244 va_end(ap);
245}
246
247#define HE_COLORSET_TOP 50
248#define HE_COLORSET_MEDIUM 51
249#define HE_COLORSET_NORMAL 52
250#define HE_COLORSET_SELECTED 53
251#define HE_COLORSET_CODE 54
252
253static int ui_browser__percent_color(double percent, bool current)
254{
255 if (current)
256 return HE_COLORSET_SELECTED;
257 if (percent >= MIN_RED)
258 return HE_COLORSET_TOP;
259 if (percent >= MIN_GREEN)
260 return HE_COLORSET_MEDIUM;
261 return HE_COLORSET_NORMAL;
262}
263
264struct ui_browser {
265 newtComponent form, sb;
266 u64 index, first_visible_entry_idx;
267 void *first_visible_entry, *entries;
268 u16 top, left, width, height;
269 void *priv;
270 u32 nr_entries;
271};
272
273static void ui_browser__refresh_dimensions(struct ui_browser *self)
274{
275 int cols, rows;
276 newtGetScreenSize(&cols, &rows);
277
278 if (self->width > cols - 4)
279 self->width = cols - 4;
280 self->height = rows - 5;
281 if (self->height > self->nr_entries)
282 self->height = self->nr_entries;
283 self->top = (rows - self->height) / 2;
284 self->left = (cols - self->width) / 2;
285}
286
287static void ui_browser__reset_index(struct ui_browser *self)
288{
289 self->index = self->first_visible_entry_idx = 0;
290 self->first_visible_entry = NULL;
291}
292
293static int objdump_line__show(struct objdump_line *self, struct list_head *head,
294 int width, struct hist_entry *he, int len,
295 bool current_entry)
296{
297 if (self->offset != -1) {
298 struct symbol *sym = he->ms.sym;
299 unsigned int hits = 0;
300 double percent = 0.0;
301 int color;
302 struct sym_priv *priv = symbol__priv(sym);
303 struct sym_ext *sym_ext = priv->ext;
304 struct sym_hist *h = priv->hist;
305 s64 offset = self->offset;
306 struct objdump_line *next = objdump__get_next_ip_line(head, self);
307
308 while (offset < (s64)len &&
309 (next == NULL || offset < next->offset)) {
310 if (sym_ext) {
311 percent += sym_ext[offset].percent;
312 } else
313 hits += h->ip[offset];
314
315 ++offset;
316 }
317
318 if (sym_ext == NULL && h->sum)
319 percent = 100.0 * hits / h->sum;
320
321 color = ui_browser__percent_color(percent, current_entry);
322 SLsmg_set_color(color);
323 slsmg_printf(" %7.2f ", percent);
324 if (!current_entry)
325 SLsmg_set_color(HE_COLORSET_CODE);
326 } else {
327 int color = ui_browser__percent_color(0, current_entry);
328 SLsmg_set_color(color);
329 slsmg_write_nstring(" ", 9);
330 }
331
332 SLsmg_write_char(':');
333 slsmg_write_nstring(" ", 8);
334 if (!*self->line)
335 slsmg_write_nstring(" ", width - 18);
336 else
337 slsmg_write_nstring(self->line, width - 18);
338
339 return 0;
340}
341
342static int ui_browser__refresh_entries(struct ui_browser *self)
343{
344 struct objdump_line *pos;
345 struct list_head *head = self->entries;
346 struct hist_entry *he = self->priv;
347 int row = 0;
348 int len = he->ms.sym->end - he->ms.sym->start;
349
350 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
351 self->first_visible_entry = head->next;
352
353 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
354
355 list_for_each_entry_from(pos, head, node) {
356 bool current_entry = (self->first_visible_entry_idx + row) == self->index;
357 SLsmg_gotorc(self->top + row, self->left);
358 objdump_line__show(pos, head, self->width,
359 he, len, current_entry);
360 if (++row == self->height)
361 break;
362 }
363
364 SLsmg_set_color(HE_COLORSET_NORMAL);
365 SLsmg_fill_region(self->top + row, self->left,
366 self->height - row, self->width, ' ');
367
368 return 0;
369}
370
371static int ui_browser__run(struct ui_browser *self, const char *title,
372 struct newtExitStruct *es)
373{
374 if (self->form) {
375 newtFormDestroy(self->form);
376 newtPopWindow();
377 }
378
379 ui_browser__refresh_dimensions(self);
380 newtCenteredWindow(self->width + 2, self->height, title);
381 self->form = newt_form__new();
382 if (self->form == NULL)
383 return -1;
384
385 self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
386 HE_COLORSET_NORMAL,
387 HE_COLORSET_SELECTED);
388 if (self->sb == NULL)
389 return -1;
390
391 newtFormAddHotKey(self->form, NEWT_KEY_UP);
392 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
393 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
394 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
395 newtFormAddHotKey(self->form, ' ');
396 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
397 newtFormAddHotKey(self->form, NEWT_KEY_END);
398 newtFormAddHotKey(self->form, NEWT_KEY_TAB);
399 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
400
401 if (ui_browser__refresh_entries(self) < 0)
402 return -1;
403 newtFormAddComponent(self->form, self->sb);
404
405 while (1) {
406 unsigned int offset;
407
408 newtFormRun(self->form, es);
409
410 if (es->reason != NEWT_EXIT_HOTKEY)
411 break;
412 if (is_exit_key(es->u.key))
413 return es->u.key;
414 switch (es->u.key) {
415 case NEWT_KEY_DOWN:
416 if (self->index == self->nr_entries - 1)
417 break;
418 ++self->index;
419 if (self->index == self->first_visible_entry_idx + self->height) {
420 struct list_head *pos = self->first_visible_entry;
421 ++self->first_visible_entry_idx;
422 self->first_visible_entry = pos->next;
423 }
424 break;
425 case NEWT_KEY_UP:
426 if (self->index == 0)
427 break;
428 --self->index;
429 if (self->index < self->first_visible_entry_idx) {
430 struct list_head *pos = self->first_visible_entry;
431 --self->first_visible_entry_idx;
432 self->first_visible_entry = pos->prev;
433 }
434 break;
435 case NEWT_KEY_PGDN:
436 case ' ':
437 if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
438 break;
439
440 offset = self->height;
441 if (self->index + offset > self->nr_entries - 1)
442 offset = self->nr_entries - 1 - self->index;
443 self->index += offset;
444 self->first_visible_entry_idx += offset;
445
446 while (offset--) {
447 struct list_head *pos = self->first_visible_entry;
448 self->first_visible_entry = pos->next;
449 }
450
451 break;
452 case NEWT_KEY_PGUP:
453 if (self->first_visible_entry_idx == 0)
454 break;
455
456 if (self->first_visible_entry_idx < self->height)
457 offset = self->first_visible_entry_idx;
458 else
459 offset = self->height;
460
461 self->index -= offset;
462 self->first_visible_entry_idx -= offset;
463
464 while (offset--) {
465 struct list_head *pos = self->first_visible_entry;
466 self->first_visible_entry = pos->prev;
467 }
468 break;
469 case NEWT_KEY_HOME:
470 ui_browser__reset_index(self);
471 break;
472 case NEWT_KEY_END: {
473 struct list_head *head = self->entries;
474 offset = self->height - 1;
475
476 if (offset > self->nr_entries)
477 offset = self->nr_entries;
478
479 self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
480 self->first_visible_entry = head->prev;
481 while (offset-- != 0) {
482 struct list_head *pos = self->first_visible_entry;
483 self->first_visible_entry = pos->prev;
484 }
485 }
486 break;
487 case NEWT_KEY_RIGHT:
488 case NEWT_KEY_LEFT:
489 case NEWT_KEY_TAB:
490 return es->u.key;
491 default:
492 continue;
493 }
494 if (ui_browser__refresh_entries(self) < 0)
495 return -1;
496 }
497 return 0;
498}
499
500/*
501 * When debugging newt problems it was useful to be able to "unroll"
502 * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
503 * a source file with the sequence of calls to these methods, to then
504 * tweak the arrays to get the intended results, so I'm keeping this code
505 * here, may be useful again in the future.
506 */
507#undef NEWT_DEBUG
508
509static void newt_checkbox_tree__add(newtComponent tree, const char *str,
510 void *priv, int *indexes)
511{
512#ifdef NEWT_DEBUG
513 /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
514 int i = 0, len = 40 - strlen(str);
515
516 fprintf(stderr,
517 "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
518 len, len, " ", str, priv);
519 while (indexes[i] != NEWT_ARG_LAST) {
520 if (indexes[i] != NEWT_ARG_APPEND)
521 fprintf(stderr, " %d,", indexes[i]);
522 else
523 fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
524 ++i;
525 }
526 fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
527 fflush(stderr);
528#endif
529 newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
530}
531
532static char *callchain_list__sym_name(struct callchain_list *self,
533 char *bf, size_t bfsize)
534{
535 if (self->ms.sym)
536 return self->ms.sym->name;
537
538 snprintf(bf, bfsize, "%#Lx", self->ip);
539 return bf;
540}
541
542static void __callchain__append_graph_browser(struct callchain_node *self,
543 newtComponent tree, u64 total,
544 int *indexes, int depth)
545{
546 struct rb_node *node;
547 u64 new_total, remaining;
548 int idx = 0;
549
550 if (callchain_param.mode == CHAIN_GRAPH_REL)
551 new_total = self->children_hit;
552 else
553 new_total = total;
554
555 remaining = new_total;
556 node = rb_first(&self->rb_root);
557 while (node) {
558 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
559 struct rb_node *next = rb_next(node);
560 u64 cumul = cumul_hits(child);
561 struct callchain_list *chain;
562 int first = true, printed = 0;
563 int chain_idx = -1;
564 remaining -= cumul;
565
566 indexes[depth] = NEWT_ARG_APPEND;
567 indexes[depth + 1] = NEWT_ARG_LAST;
568
569 list_for_each_entry(chain, &child->val, list) {
570 char ipstr[BITS_PER_LONG / 4 + 1],
571 *alloc_str = NULL;
572 const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
573
574 if (first) {
575 double percent = cumul * 100.0 / new_total;
576
577 first = false;
578 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
579 str = "Not enough memory!";
580 else
581 str = alloc_str;
582 } else {
583 indexes[depth] = idx;
584 indexes[depth + 1] = NEWT_ARG_APPEND;
585 indexes[depth + 2] = NEWT_ARG_LAST;
586 ++chain_idx;
587 }
588 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
589 free(alloc_str);
590 ++printed;
591 }
592
593 indexes[depth] = idx;
594 if (chain_idx != -1)
595 indexes[depth + 1] = chain_idx;
596 if (printed != 0)
597 ++idx;
598 __callchain__append_graph_browser(child, tree, new_total, indexes,
599 depth + (chain_idx != -1 ? 2 : 1));
600 node = next;
601 }
602}
603
604static void callchain__append_graph_browser(struct callchain_node *self,
605 newtComponent tree, u64 total,
606 int *indexes, int parent_idx)
607{
608 struct callchain_list *chain;
609 int i = 0;
610
611 indexes[1] = NEWT_ARG_APPEND;
612 indexes[2] = NEWT_ARG_LAST;
613
614 list_for_each_entry(chain, &self->val, list) {
615 char ipstr[BITS_PER_LONG / 4 + 1], *str;
616
617 if (chain->ip >= PERF_CONTEXT_MAX)
618 continue;
619
620 if (!i++ && sort__first_dimension == SORT_SYM)
621 continue;
622
623 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
624 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
625 }
626
627 indexes[1] = parent_idx;
628 indexes[2] = NEWT_ARG_APPEND;
629 indexes[3] = NEWT_ARG_LAST;
630 __callchain__append_graph_browser(self, tree, total, indexes, 2);
631}
632
633static void hist_entry__append_callchain_browser(struct hist_entry *self,
634 newtComponent tree, u64 total, int parent_idx)
635{
636 struct rb_node *rb_node;
637 int indexes[1024] = { [0] = parent_idx, };
638 int idx = 0;
639 struct callchain_node *chain;
640
641 rb_node = rb_first(&self->sorted_chain);
642 while (rb_node) {
643 chain = rb_entry(rb_node, struct callchain_node, rb_node);
644 switch (callchain_param.mode) {
645 case CHAIN_FLAT:
646 break;
647 case CHAIN_GRAPH_ABS: /* falldown */
648 case CHAIN_GRAPH_REL:
649 callchain__append_graph_browser(chain, tree, total, indexes, idx++);
650 break;
651 case CHAIN_NONE:
652 default:
653 break;
654 }
655 rb_node = rb_next(rb_node);
656 }
657}
658
659static size_t hist_entry__append_browser(struct hist_entry *self,
660 newtComponent tree, u64 total)
661{
662 char s[256];
663 size_t ret;
664
665 if (symbol_conf.exclude_other && !self->parent)
666 return 0;
667
668 ret = hist_entry__snprintf(self, s, sizeof(s), NULL,
669 false, 0, false, total);
670 if (symbol_conf.use_callchain) {
671 int indexes[2];
672
673 indexes[0] = NEWT_ARG_APPEND;
674 indexes[1] = NEWT_ARG_LAST;
675 newt_checkbox_tree__add(tree, s, &self->ms, indexes);
676 } else
677 newtListboxAppendEntry(tree, s, &self->ms);
678
679 return ret;
680}
681
682int hist_entry__tui_annotate(struct hist_entry *self)
683{
684 struct ui_browser browser;
685 struct newtExitStruct es;
686 struct objdump_line *pos, *n;
687 LIST_HEAD(head);
688 int ret;
689
690 if (self->ms.sym == NULL)
691 return -1;
692
693 if (self->ms.map->dso->annotate_warned)
694 return -1;
695
696 if (hist_entry__annotate(self, &head) < 0) {
697 ui__error_window(browser__last_msg);
698 return -1;
699 }
700
701 ui_helpline__push("Press <- or ESC to exit");
702
703 memset(&browser, 0, sizeof(browser));
704 browser.entries = &head;
705 browser.priv = self;
706 list_for_each_entry(pos, &head, node) {
707 size_t line_len = strlen(pos->line);
708 if (browser.width < line_len)
709 browser.width = line_len;
710 ++browser.nr_entries;
711 }
712
713 browser.width += 18; /* Percentage */
714 ret = ui_browser__run(&browser, self->ms.sym->name, &es);
715 newtFormDestroy(browser.form);
716 newtPopWindow();
717 list_for_each_entry_safe(pos, n, &head, node) {
718 list_del(&pos->node);
719 objdump_line__free(pos);
720 }
721 ui_helpline__pop();
722 return ret;
723}
724
725static const void *newt__symbol_tree_get_current(newtComponent self)
726{
727 if (symbol_conf.use_callchain)
728 return newtCheckboxTreeGetCurrent(self);
729 return newtListboxGetCurrent(self);
730}
731
732static void hist_browser__selection(newtComponent self, void *data)
733{
734 const struct map_symbol **symbol_ptr = data;
735 *symbol_ptr = newt__symbol_tree_get_current(self);
736}
737
738struct hist_browser {
739 newtComponent form, tree;
740 const struct map_symbol *selection;
741};
742
743static struct hist_browser *hist_browser__new(void)
744{
745 struct hist_browser *self = malloc(sizeof(*self));
746
747 if (self != NULL)
748 self->form = NULL;
749
750 return self;
751}
752
753static void hist_browser__delete(struct hist_browser *self)
754{
755 newtFormDestroy(self->form);
756 newtPopWindow();
757 free(self);
758}
759
760static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
761 const char *title)
762{
763 int max_len = 0, idx, cols, rows;
764 struct ui_progress *progress;
765 struct rb_node *nd;
766 u64 curr_hist = 0;
767 char seq[] = ".", unit;
768 char str[256];
769 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
770
771 if (self->form) {
772 newtFormDestroy(self->form);
773 newtPopWindow();
774 }
775
776 nr_events = convert_unit(nr_events, &unit);
777 snprintf(str, sizeof(str), "Events: %lu%c ",
778 nr_events, unit);
779 newtDrawRootText(0, 0, str);
780
781 newtGetScreenSize(NULL, &rows);
782
783 if (symbol_conf.use_callchain)
784 self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
785 NEWT_FLAG_SCROLL);
786 else
787 self->tree = newtListbox(0, 0, rows - 5,
788 (NEWT_FLAG_SCROLL |
789 NEWT_FLAG_RETURNEXIT));
790
791 newtComponentAddCallback(self->tree, hist_browser__selection,
792 &self->selection);
793
794 progress = ui_progress__new("Adding entries to the browser...",
795 hists->nr_entries);
796 if (progress == NULL)
797 return -1;
798
799 idx = 0;
800 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
801 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
802 int len;
803
804 if (h->filtered)
805 continue;
806
807 len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
808 if (len > max_len)
809 max_len = len;
810 if (symbol_conf.use_callchain)
811 hist_entry__append_callchain_browser(h, self->tree,
812 hists->stats.total_period, idx++);
813 ++curr_hist;
814 if (curr_hist % 5)
815 ui_progress__update(progress, curr_hist);
816 }
817
818 ui_progress__delete(progress);
819
820 newtGetScreenSize(&cols, &rows);
821
822 if (max_len > cols)
823 max_len = cols - 3;
824
825 if (!symbol_conf.use_callchain)
826 newtListboxSetWidth(self->tree, max_len);
827
828 newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
829 rows - 5, title);
830 self->form = newt_form__new();
831 if (self->form == NULL)
832 return -1;
833
834 newtFormAddHotKey(self->form, 'A');
835 newtFormAddHotKey(self->form, 'a');
836 newtFormAddHotKey(self->form, 'D');
837 newtFormAddHotKey(self->form, 'd');
838 newtFormAddHotKey(self->form, 'T');
839 newtFormAddHotKey(self->form, 't');
840 newtFormAddHotKey(self->form, '?');
841 newtFormAddHotKey(self->form, 'H');
842 newtFormAddHotKey(self->form, 'h');
843 newtFormAddHotKey(self->form, NEWT_KEY_F1);
844 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
845 newtFormAddHotKey(self->form, NEWT_KEY_TAB);
846 newtFormAddHotKey(self->form, NEWT_KEY_UNTAB);
847 newtFormAddComponents(self->form, self->tree, NULL);
848 self->selection = newt__symbol_tree_get_current(self->tree);
849
850 return 0;
851}
852
853static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
854{
855 int *indexes;
856
857 if (!symbol_conf.use_callchain)
858 goto out;
859
860 indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection);
861 if (indexes) {
862 bool is_hist_entry = indexes[1] == NEWT_ARG_LAST;
863 free(indexes);
864 if (is_hist_entry)
865 goto out;
866 }
867 return NULL;
868out:
869 return container_of(self->selection, struct hist_entry, ms);
870}
871
872static struct thread *hist_browser__selected_thread(struct hist_browser *self)
873{
874 struct hist_entry *he = hist_browser__selected_entry(self);
875 return he ? he->thread : NULL;
876}
877
878static int hist_browser__title(char *bf, size_t size, const char *ev_name,
879 const struct dso *dso, const struct thread *thread)
880{
881 int printed = 0;
882
883 if (thread)
884 printed += snprintf(bf + printed, size - printed,
885 "Thread: %s(%d)",
886 (thread->comm_set ? thread->comm : ""),
887 thread->pid);
888 if (dso)
889 printed += snprintf(bf + printed, size - printed,
890 "%sDSO: %s", thread ? " " : "",
891 dso->short_name);
892 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
893}
894
895int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
896{
897 struct hist_browser *browser = hist_browser__new();
898 struct pstack *fstack;
899 const struct thread *thread_filter = NULL;
900 const struct dso *dso_filter = NULL;
901 struct newtExitStruct es;
902 char msg[160];
903 int key = -1;
904
905 if (browser == NULL)
906 return -1;
907
908 fstack = pstack__new(2);
909 if (fstack == NULL)
910 goto out;
911
912 ui_helpline__push(helpline);
913
914 hist_browser__title(msg, sizeof(msg), ev_name,
915 dso_filter, thread_filter);
916 if (hist_browser__populate(browser, self, msg) < 0)
917 goto out_free_stack;
918
919 while (1) {
920 const struct thread *thread;
921 const struct dso *dso;
922 char *options[16];
923 int nr_options = 0, choice = 0, i,
924 annotate = -2, zoom_dso = -2, zoom_thread = -2;
925
926 newtFormRun(browser->form, &es);
927
928 thread = hist_browser__selected_thread(browser);
929 dso = browser->selection->map ? browser->selection->map->dso : NULL;
930
931 if (es.reason == NEWT_EXIT_HOTKEY) {
932 key = es.u.key;
933
934 switch (key) {
935 case NEWT_KEY_F1:
936 goto do_help;
937 case NEWT_KEY_TAB:
938 case NEWT_KEY_UNTAB:
939 /*
940 * Exit the browser, let hists__browser_tree
941 * go to the next or previous
942 */
943 goto out_free_stack;
944 default:;
945 }
946
947 key = toupper(key);
948 switch (key) {
949 case 'A':
950 if (browser->selection->map == NULL &&
951 browser->selection->map->dso->annotate_warned)
952 continue;
953 goto do_annotate;
954 case 'D':
955 goto zoom_dso;
956 case 'T':
957 goto zoom_thread;
958 case 'H':
959 case '?':
960do_help:
961 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
962 "<- Zoom out\n"
963 "a Annotate current symbol\n"
964 "h/?/F1 Show this window\n"
965 "d Zoom into current DSO\n"
966 "t Zoom into current Thread\n"
967 "q/CTRL+C Exit browser");
968 continue;
969 default:;
970 }
971 if (is_exit_key(key)) {
972 if (key == NEWT_KEY_ESCAPE) {
973 if (dialog_yesno("Do you really want to exit?"))
974 break;
975 else
976 continue;
977 } else
978 break;
979 }
980
981 if (es.u.key == NEWT_KEY_LEFT) {
982 const void *top;
983
984 if (pstack__empty(fstack))
985 continue;
986 top = pstack__pop(fstack);
987 if (top == &dso_filter)
988 goto zoom_out_dso;
989 if (top == &thread_filter)
990 goto zoom_out_thread;
991 continue;
992 }
993 }
994
995 if (browser->selection->sym != NULL &&
996 !browser->selection->map->dso->annotate_warned &&
997 asprintf(&options[nr_options], "Annotate %s",
998 browser->selection->sym->name) > 0)
999 annotate = nr_options++;
1000
1001 if (thread != NULL &&
1002 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1003 (thread_filter ? "out of" : "into"),
1004 (thread->comm_set ? thread->comm : ""),
1005 thread->pid) > 0)
1006 zoom_thread = nr_options++;
1007
1008 if (dso != NULL &&
1009 asprintf(&options[nr_options], "Zoom %s %s DSO",
1010 (dso_filter ? "out of" : "into"),
1011 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1012 zoom_dso = nr_options++;
1013
1014 options[nr_options++] = (char *)"Exit";
1015
1016 choice = popup_menu(nr_options, options);
1017
1018 for (i = 0; i < nr_options - 1; ++i)
1019 free(options[i]);
1020
1021 if (choice == nr_options - 1)
1022 break;
1023
1024 if (choice == -1)
1025 continue;
1026
1027 if (choice == annotate) {
1028 struct hist_entry *he;
1029do_annotate:
1030 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
1031 browser->selection->map->dso->annotate_warned = 1;
1032 ui_helpline__puts("No vmlinux file found, can't "
1033 "annotate with just a "
1034 "kallsyms file");
1035 continue;
1036 }
1037
1038 he = hist_browser__selected_entry(browser);
1039 if (he == NULL)
1040 continue;
1041
1042 hist_entry__tui_annotate(he);
1043 } else if (choice == zoom_dso) {
1044zoom_dso:
1045 if (dso_filter) {
1046 pstack__remove(fstack, &dso_filter);
1047zoom_out_dso:
1048 ui_helpline__pop();
1049 dso_filter = NULL;
1050 } else {
1051 if (dso == NULL)
1052 continue;
1053 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1054 dso->kernel ? "the Kernel" : dso->short_name);
1055 dso_filter = dso;
1056 pstack__push(fstack, &dso_filter);
1057 }
1058 hists__filter_by_dso(self, dso_filter);
1059 hist_browser__title(msg, sizeof(msg), ev_name,
1060 dso_filter, thread_filter);
1061 if (hist_browser__populate(browser, self, msg) < 0)
1062 goto out;
1063 } else if (choice == zoom_thread) {
1064zoom_thread:
1065 if (thread_filter) {
1066 pstack__remove(fstack, &thread_filter);
1067zoom_out_thread:
1068 ui_helpline__pop();
1069 thread_filter = NULL;
1070 } else {
1071 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1072 thread->comm_set ? thread->comm : "",
1073 thread->pid);
1074 thread_filter = thread;
1075 pstack__push(fstack, &thread_filter);
1076 }
1077 hists__filter_by_thread(self, thread_filter);
1078 hist_browser__title(msg, sizeof(msg), ev_name,
1079 dso_filter, thread_filter);
1080 if (hist_browser__populate(browser, self, msg) < 0)
1081 goto out;
1082 }
1083 }
1084out_free_stack:
1085 pstack__delete(fstack);
1086out:
1087 hist_browser__delete(browser);
1088 return key;
1089}
1090
1091int hists__tui_browse_tree(struct rb_root *self, const char *help)
1092{
1093 struct rb_node *first = rb_first(self), *nd = first, *next;
1094 int key = 0;
1095
1096 while (nd) {
1097 struct hists *hists = rb_entry(nd, struct hists, rb_node);
1098 const char *ev_name = __event_name(hists->type, hists->config);
1099
1100 key = hists__browse(hists, help, ev_name);
1101
1102 if (is_exit_key(key))
1103 break;
1104
1105 switch (key) {
1106 case NEWT_KEY_TAB:
1107 next = rb_next(nd);
1108 if (next)
1109 nd = next;
1110 break;
1111 case NEWT_KEY_UNTAB:
1112 if (nd == first)
1113 continue;
1114 nd = rb_prev(nd);
1115 default:
1116 break;
1117 }
1118 }
1119
1120 return key;
1121}
1122
1123static struct newtPercentTreeColors {
1124 const char *topColorFg, *topColorBg;
1125 const char *mediumColorFg, *mediumColorBg;
1126 const char *normalColorFg, *normalColorBg;
1127 const char *selColorFg, *selColorBg;
1128 const char *codeColorFg, *codeColorBg;
1129} defaultPercentTreeColors = {
1130 "red", "lightgray",
1131 "green", "lightgray",
1132 "black", "lightgray",
1133 "lightgray", "magenta",
1134 "blue", "lightgray",
1135};
1136
1137void setup_browser(void)
1138{
1139 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
1140
1141 if (!isatty(1) || !use_browser || dump_trace) {
1142 use_browser = 0;
1143 setup_pager();
1144 return;
1145 }
1146
1147 use_browser = 1;
1148 newtInit();
1149 newtCls();
1150 ui_helpline__puts(" ");
1151 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
1152 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
1153 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
1154 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
1155 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
1156}
1157
1158void exit_browser(bool wait_for_ok)
1159{
1160 if (use_browser > 0) {
1161 if (wait_for_ok) {
1162 char title[] = "Fatal Error", ok[] = "Ok";
1163 newtWinMessage(title, ok, browser__last_msg);
1164 }
1165 newtFinished();
1166 }
1167}
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 9bf0f402ca73..4af5bd59cfd1 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)
602 return EVT_FAILED; 602 return EVT_FAILED;
603 } 603 }
604 604
605 /* We should find a nice way to override the access type */ 605 /*
606 attr->bp_len = HW_BREAKPOINT_LEN_4; 606 * We should find a nice way to override the access length
607 * Provide some defaults for now
608 */
609 if (attr->bp_type == HW_BREAKPOINT_X)
610 attr->bp_len = sizeof(long);
611 else
612 attr->bp_len = HW_BREAKPOINT_LEN_4;
613
607 attr->type = PERF_TYPE_BREAKPOINT; 614 attr->type = PERF_TYPE_BREAKPOINT;
608 615
609 return EVT_HANDLED; 616 return EVT_HANDLED;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 914c67095d96..e72f05c3bef0 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * probe-event.c : perf-probe definition to kprobe_events format converter 2 * probe-event.c : perf-probe definition to probe_events format converter
3 * 3 *
4 * Written by Masami Hiramatsu <mhiramat@redhat.com> 4 * Written by Masami Hiramatsu <mhiramat@redhat.com>
5 * 5 *
@@ -120,8 +120,11 @@ static int open_vmlinux(void)
120 return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); 120 return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
121} 121}
122 122
123/* Convert trace point to probe point with debuginfo */ 123/*
124static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, 124 * Convert trace point to probe point with debuginfo
125 * Currently only handles kprobes.
126 */
127static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
125 struct perf_probe_point *pp) 128 struct perf_probe_point *pp)
126{ 129{
127 struct symbol *sym; 130 struct symbol *sym;
@@ -151,8 +154,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
151} 154}
152 155
153/* Try to find perf_probe_event with debuginfo */ 156/* Try to find perf_probe_event with debuginfo */
154static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, 157static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
155 struct kprobe_trace_event **tevs, 158 struct probe_trace_event **tevs,
156 int max_tevs) 159 int max_tevs)
157{ 160{
158 bool need_dwarf = perf_probe_event_need_dwarf(pev); 161 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,
169 } 172 }
170 173
171 /* Searching trace events corresponding to probe event */ 174 /* Searching trace events corresponding to probe event */
172 ntevs = find_kprobe_trace_events(fd, pev, tevs, max_tevs); 175 ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs);
173 close(fd); 176 close(fd);
174 177
175 if (ntevs > 0) { /* Succeeded to find trace events */ 178 if (ntevs > 0) { /* Succeeded to find trace events */
176 pr_debug("find %d kprobe_trace_events.\n", ntevs); 179 pr_debug("find %d probe_trace_events.\n", ntevs);
177 return ntevs; 180 return ntevs;
178 } 181 }
179 182
@@ -195,6 +198,65 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
195 return ntevs; 198 return ntevs;
196} 199}
197 200
201/*
202 * Find a src file from a DWARF tag path. Prepend optional source path prefix
203 * and chop off leading directories that do not exist. Result is passed back as
204 * a newly allocated path on success.
205 * Return 0 if file was found and readable, -errno otherwise.
206 */
207static int get_real_path(const char *raw_path, const char *comp_dir,
208 char **new_path)
209{
210 const char *prefix = symbol_conf.source_prefix;
211
212 if (!prefix) {
213 if (raw_path[0] != '/' && comp_dir)
214 /* If not an absolute path, try to use comp_dir */
215 prefix = comp_dir;
216 else {
217 if (access(raw_path, R_OK) == 0) {
218 *new_path = strdup(raw_path);
219 return 0;
220 } else
221 return -errno;
222 }
223 }
224
225 *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2));
226 if (!*new_path)
227 return -ENOMEM;
228
229 for (;;) {
230 sprintf(*new_path, "%s/%s", prefix, raw_path);
231
232 if (access(*new_path, R_OK) == 0)
233 return 0;
234
235 if (!symbol_conf.source_prefix)
236 /* In case of searching comp_dir, don't retry */
237 return -errno;
238
239 switch (errno) {
240 case ENAMETOOLONG:
241 case ENOENT:
242 case EROFS:
243 case EFAULT:
244 raw_path = strchr(++raw_path, '/');
245 if (!raw_path) {
246 free(*new_path);
247 *new_path = NULL;
248 return -ENOENT;
249 }
250 continue;
251
252 default:
253 free(*new_path);
254 *new_path = NULL;
255 return -errno;
256 }
257 }
258}
259
198#define LINEBUF_SIZE 256 260#define LINEBUF_SIZE 256
199#define NR_ADDITIONAL_LINES 2 261#define NR_ADDITIONAL_LINES 2
200 262
@@ -244,6 +306,7 @@ int show_line_range(struct line_range *lr)
244 struct line_node *ln; 306 struct line_node *ln;
245 FILE *fp; 307 FILE *fp;
246 int fd, ret; 308 int fd, ret;
309 char *tmp;
247 310
248 /* Search a line range */ 311 /* Search a line range */
249 ret = init_vmlinux(); 312 ret = init_vmlinux();
@@ -266,6 +329,15 @@ int show_line_range(struct line_range *lr)
266 return ret; 329 return ret;
267 } 330 }
268 331
332 /* Convert source file path */
333 tmp = lr->path;
334 ret = get_real_path(tmp, lr->comp_dir, &lr->path);
335 free(tmp); /* Free old path */
336 if (ret < 0) {
337 pr_warning("Failed to find source file. (%d)\n", ret);
338 return ret;
339 }
340
269 setup_pager(); 341 setup_pager();
270 342
271 if (lr->function) 343 if (lr->function)
@@ -308,8 +380,8 @@ end:
308 380
309#else /* !DWARF_SUPPORT */ 381#else /* !DWARF_SUPPORT */
310 382
311static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, 383static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
312 struct perf_probe_point *pp) 384 struct perf_probe_point *pp)
313{ 385{
314 pp->function = strdup(tp->symbol); 386 pp->function = strdup(tp->symbol);
315 if (pp->function == NULL) 387 if (pp->function == NULL)
@@ -320,8 +392,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
320 return 0; 392 return 0;
321} 393}
322 394
323static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, 395static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
324 struct kprobe_trace_event **tevs __unused, 396 struct probe_trace_event **tevs __unused,
325 int max_tevs __unused) 397 int max_tevs __unused)
326{ 398{
327 if (perf_probe_event_need_dwarf(pev)) { 399 if (perf_probe_event_need_dwarf(pev)) {
@@ -557,7 +629,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
557/* Parse perf-probe event argument */ 629/* Parse perf-probe event argument */
558static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) 630static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
559{ 631{
560 char *tmp; 632 char *tmp, *goodname;
561 struct perf_probe_arg_field **fieldp; 633 struct perf_probe_arg_field **fieldp;
562 634
563 pr_debug("parsing arg: %s into ", str); 635 pr_debug("parsing arg: %s into ", str);
@@ -580,7 +652,7 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
580 pr_debug("type:%s ", arg->type); 652 pr_debug("type:%s ", arg->type);
581 } 653 }
582 654
583 tmp = strpbrk(str, "-."); 655 tmp = strpbrk(str, "-.[");
584 if (!is_c_varname(str) || !tmp) { 656 if (!is_c_varname(str) || !tmp) {
585 /* A variable, register, symbol or special value */ 657 /* A variable, register, symbol or special value */
586 arg->var = strdup(str); 658 arg->var = strdup(str);
@@ -590,10 +662,11 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
590 return 0; 662 return 0;
591 } 663 }
592 664
593 /* Structure fields */ 665 /* Structure fields or array element */
594 arg->var = strndup(str, tmp - str); 666 arg->var = strndup(str, tmp - str);
595 if (arg->var == NULL) 667 if (arg->var == NULL)
596 return -ENOMEM; 668 return -ENOMEM;
669 goodname = arg->var;
597 pr_debug("%s, ", arg->var); 670 pr_debug("%s, ", arg->var);
598 fieldp = &arg->field; 671 fieldp = &arg->field;
599 672
@@ -601,22 +674,38 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
601 *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); 674 *fieldp = zalloc(sizeof(struct perf_probe_arg_field));
602 if (*fieldp == NULL) 675 if (*fieldp == NULL)
603 return -ENOMEM; 676 return -ENOMEM;
604 if (*tmp == '.') { 677 if (*tmp == '[') { /* Array */
605 str = tmp + 1; 678 str = tmp;
606 (*fieldp)->ref = false; 679 (*fieldp)->index = strtol(str + 1, &tmp, 0);
607 } else if (tmp[1] == '>') {
608 str = tmp + 2;
609 (*fieldp)->ref = true; 680 (*fieldp)->ref = true;
610 } else { 681 if (*tmp != ']' || tmp == str + 1) {
611 semantic_error("Argument parse error: %s\n", str); 682 semantic_error("Array index must be a"
612 return -EINVAL; 683 " number.\n");
684 return -EINVAL;
685 }
686 tmp++;
687 if (*tmp == '\0')
688 tmp = NULL;
689 } else { /* Structure */
690 if (*tmp == '.') {
691 str = tmp + 1;
692 (*fieldp)->ref = false;
693 } else if (tmp[1] == '>') {
694 str = tmp + 2;
695 (*fieldp)->ref = true;
696 } else {
697 semantic_error("Argument parse error: %s\n",
698 str);
699 return -EINVAL;
700 }
701 tmp = strpbrk(str, "-.[");
613 } 702 }
614
615 tmp = strpbrk(str, "-.");
616 if (tmp) { 703 if (tmp) {
617 (*fieldp)->name = strndup(str, tmp - str); 704 (*fieldp)->name = strndup(str, tmp - str);
618 if ((*fieldp)->name == NULL) 705 if ((*fieldp)->name == NULL)
619 return -ENOMEM; 706 return -ENOMEM;
707 if (*str != '[')
708 goodname = (*fieldp)->name;
620 pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); 709 pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref);
621 fieldp = &(*fieldp)->next; 710 fieldp = &(*fieldp)->next;
622 } 711 }
@@ -624,11 +713,13 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
624 (*fieldp)->name = strdup(str); 713 (*fieldp)->name = strdup(str);
625 if ((*fieldp)->name == NULL) 714 if ((*fieldp)->name == NULL)
626 return -ENOMEM; 715 return -ENOMEM;
716 if (*str != '[')
717 goodname = (*fieldp)->name;
627 pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); 718 pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);
628 719
629 /* If no name is specified, set the last field name */ 720 /* If no name is specified, set the last field name (not array index)*/
630 if (!arg->name) { 721 if (!arg->name) {
631 arg->name = strdup((*fieldp)->name); 722 arg->name = strdup(goodname);
632 if (arg->name == NULL) 723 if (arg->name == NULL)
633 return -ENOMEM; 724 return -ENOMEM;
634 } 725 }
@@ -693,16 +784,17 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
693 return false; 784 return false;
694} 785}
695 786
696/* Parse kprobe_events event into struct probe_point */ 787/* Parse probe_events event into struct probe_point */
697int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) 788static int parse_probe_trace_command(const char *cmd,
789 struct probe_trace_event *tev)
698{ 790{
699 struct kprobe_trace_point *tp = &tev->point; 791 struct probe_trace_point *tp = &tev->point;
700 char pr; 792 char pr;
701 char *p; 793 char *p;
702 int ret, i, argc; 794 int ret, i, argc;
703 char **argv; 795 char **argv;
704 796
705 pr_debug("Parsing kprobe_events: %s\n", cmd); 797 pr_debug("Parsing probe_events: %s\n", cmd);
706 argv = argv_split(cmd, &argc); 798 argv = argv_split(cmd, &argc);
707 if (!argv) { 799 if (!argv) {
708 pr_debug("Failed to split arguments.\n"); 800 pr_debug("Failed to split arguments.\n");
@@ -734,7 +826,7 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
734 tp->offset = 0; 826 tp->offset = 0;
735 827
736 tev->nargs = argc - 2; 828 tev->nargs = argc - 2;
737 tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); 829 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
738 if (tev->args == NULL) { 830 if (tev->args == NULL) {
739 ret = -ENOMEM; 831 ret = -ENOMEM;
740 goto out; 832 goto out;
@@ -776,8 +868,11 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
776 len -= ret; 868 len -= ret;
777 869
778 while (field) { 870 while (field) {
779 ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".", 871 if (field->name[0] == '[')
780 field->name); 872 ret = e_snprintf(tmp, len, "%s", field->name);
873 else
874 ret = e_snprintf(tmp, len, "%s%s",
875 field->ref ? "->" : ".", field->name);
781 if (ret <= 0) 876 if (ret <= 0)
782 goto error; 877 goto error;
783 tmp += ret; 878 tmp += ret;
@@ -877,13 +972,13 @@ char *synthesize_perf_probe_command(struct perf_probe_event *pev)
877} 972}
878#endif 973#endif
879 974
880static int __synthesize_kprobe_trace_arg_ref(struct kprobe_trace_arg_ref *ref, 975static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref,
881 char **buf, size_t *buflen, 976 char **buf, size_t *buflen,
882 int depth) 977 int depth)
883{ 978{
884 int ret; 979 int ret;
885 if (ref->next) { 980 if (ref->next) {
886 depth = __synthesize_kprobe_trace_arg_ref(ref->next, buf, 981 depth = __synthesize_probe_trace_arg_ref(ref->next, buf,
887 buflen, depth + 1); 982 buflen, depth + 1);
888 if (depth < 0) 983 if (depth < 0)
889 goto out; 984 goto out;
@@ -901,9 +996,10 @@ out:
901 996
902} 997}
903 998
904static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, 999static int synthesize_probe_trace_arg(struct probe_trace_arg *arg,
905 char *buf, size_t buflen) 1000 char *buf, size_t buflen)
906{ 1001{
1002 struct probe_trace_arg_ref *ref = arg->ref;
907 int ret, depth = 0; 1003 int ret, depth = 0;
908 char *tmp = buf; 1004 char *tmp = buf;
909 1005
@@ -917,16 +1013,24 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
917 buf += ret; 1013 buf += ret;
918 buflen -= ret; 1014 buflen -= ret;
919 1015
1016 /* Special case: @XXX */
1017 if (arg->value[0] == '@' && arg->ref)
1018 ref = ref->next;
1019
920 /* Dereferencing arguments */ 1020 /* Dereferencing arguments */
921 if (arg->ref) { 1021 if (ref) {
922 depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf, 1022 depth = __synthesize_probe_trace_arg_ref(ref, &buf,
923 &buflen, 1); 1023 &buflen, 1);
924 if (depth < 0) 1024 if (depth < 0)
925 return depth; 1025 return depth;
926 } 1026 }
927 1027
928 /* Print argument value */ 1028 /* Print argument value */
929 ret = e_snprintf(buf, buflen, "%s", arg->value); 1029 if (arg->value[0] == '@' && arg->ref)
1030 ret = e_snprintf(buf, buflen, "%s%+ld", arg->value,
1031 arg->ref->offset);
1032 else
1033 ret = e_snprintf(buf, buflen, "%s", arg->value);
930 if (ret < 0) 1034 if (ret < 0)
931 return ret; 1035 return ret;
932 buf += ret; 1036 buf += ret;
@@ -951,9 +1055,9 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
951 return buf - tmp; 1055 return buf - tmp;
952} 1056}
953 1057
954char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) 1058char *synthesize_probe_trace_command(struct probe_trace_event *tev)
955{ 1059{
956 struct kprobe_trace_point *tp = &tev->point; 1060 struct probe_trace_point *tp = &tev->point;
957 char *buf; 1061 char *buf;
958 int i, len, ret; 1062 int i, len, ret;
959 1063
@@ -969,7 +1073,7 @@ char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev)
969 goto error; 1073 goto error;
970 1074
971 for (i = 0; i < tev->nargs; i++) { 1075 for (i = 0; i < tev->nargs; i++) {
972 ret = synthesize_kprobe_trace_arg(&tev->args[i], buf + len, 1076 ret = synthesize_probe_trace_arg(&tev->args[i], buf + len,
973 MAX_CMDLEN - len); 1077 MAX_CMDLEN - len);
974 if (ret <= 0) 1078 if (ret <= 0)
975 goto error; 1079 goto error;
@@ -982,7 +1086,7 @@ error:
982 return NULL; 1086 return NULL;
983} 1087}
984 1088
985int convert_to_perf_probe_event(struct kprobe_trace_event *tev, 1089static int convert_to_perf_probe_event(struct probe_trace_event *tev,
986 struct perf_probe_event *pev) 1090 struct perf_probe_event *pev)
987{ 1091{
988 char buf[64] = ""; 1092 char buf[64] = "";
@@ -995,7 +1099,7 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
995 return -ENOMEM; 1099 return -ENOMEM;
996 1100
997 /* Convert trace_point to probe_point */ 1101 /* Convert trace_point to probe_point */
998 ret = convert_to_perf_probe_point(&tev->point, &pev->point); 1102 ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point);
999 if (ret < 0) 1103 if (ret < 0)
1000 return ret; 1104 return ret;
1001 1105
@@ -1008,7 +1112,7 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
1008 if (tev->args[i].name) 1112 if (tev->args[i].name)
1009 pev->args[i].name = strdup(tev->args[i].name); 1113 pev->args[i].name = strdup(tev->args[i].name);
1010 else { 1114 else {
1011 ret = synthesize_kprobe_trace_arg(&tev->args[i], 1115 ret = synthesize_probe_trace_arg(&tev->args[i],
1012 buf, 64); 1116 buf, 64);
1013 pev->args[i].name = strdup(buf); 1117 pev->args[i].name = strdup(buf);
1014 } 1118 }
@@ -1059,9 +1163,9 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
1059 memset(pev, 0, sizeof(*pev)); 1163 memset(pev, 0, sizeof(*pev));
1060} 1164}
1061 1165
1062void clear_kprobe_trace_event(struct kprobe_trace_event *tev) 1166static void clear_probe_trace_event(struct probe_trace_event *tev)
1063{ 1167{
1064 struct kprobe_trace_arg_ref *ref, *next; 1168 struct probe_trace_arg_ref *ref, *next;
1065 int i; 1169 int i;
1066 1170
1067 if (tev->event) 1171 if (tev->event)
@@ -1122,7 +1226,7 @@ static int open_kprobe_events(bool readwrite)
1122} 1226}
1123 1227
1124/* Get raw string list of current kprobe_events */ 1228/* Get raw string list of current kprobe_events */
1125static struct strlist *get_kprobe_trace_command_rawlist(int fd) 1229static struct strlist *get_probe_trace_command_rawlist(int fd)
1126{ 1230{
1127 int ret, idx; 1231 int ret, idx;
1128 FILE *fp; 1232 FILE *fp;
@@ -1190,7 +1294,7 @@ static int show_perf_probe_event(struct perf_probe_event *pev)
1190int show_perf_probe_events(void) 1294int show_perf_probe_events(void)
1191{ 1295{
1192 int fd, ret; 1296 int fd, ret;
1193 struct kprobe_trace_event tev; 1297 struct probe_trace_event tev;
1194 struct perf_probe_event pev; 1298 struct perf_probe_event pev;
1195 struct strlist *rawlist; 1299 struct strlist *rawlist;
1196 struct str_node *ent; 1300 struct str_node *ent;
@@ -1207,20 +1311,20 @@ int show_perf_probe_events(void)
1207 if (fd < 0) 1311 if (fd < 0)
1208 return fd; 1312 return fd;
1209 1313
1210 rawlist = get_kprobe_trace_command_rawlist(fd); 1314 rawlist = get_probe_trace_command_rawlist(fd);
1211 close(fd); 1315 close(fd);
1212 if (!rawlist) 1316 if (!rawlist)
1213 return -ENOENT; 1317 return -ENOENT;
1214 1318
1215 strlist__for_each(ent, rawlist) { 1319 strlist__for_each(ent, rawlist) {
1216 ret = parse_kprobe_trace_command(ent->s, &tev); 1320 ret = parse_probe_trace_command(ent->s, &tev);
1217 if (ret >= 0) { 1321 if (ret >= 0) {
1218 ret = convert_to_perf_probe_event(&tev, &pev); 1322 ret = convert_to_perf_probe_event(&tev, &pev);
1219 if (ret >= 0) 1323 if (ret >= 0)
1220 ret = show_perf_probe_event(&pev); 1324 ret = show_perf_probe_event(&pev);
1221 } 1325 }
1222 clear_perf_probe_event(&pev); 1326 clear_perf_probe_event(&pev);
1223 clear_kprobe_trace_event(&tev); 1327 clear_probe_trace_event(&tev);
1224 if (ret < 0) 1328 if (ret < 0)
1225 break; 1329 break;
1226 } 1330 }
@@ -1230,20 +1334,19 @@ int show_perf_probe_events(void)
1230} 1334}
1231 1335
1232/* Get current perf-probe event names */ 1336/* Get current perf-probe event names */
1233static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) 1337static struct strlist *get_probe_trace_event_names(int fd, bool include_group)
1234{ 1338{
1235 char buf[128]; 1339 char buf[128];
1236 struct strlist *sl, *rawlist; 1340 struct strlist *sl, *rawlist;
1237 struct str_node *ent; 1341 struct str_node *ent;
1238 struct kprobe_trace_event tev; 1342 struct probe_trace_event tev;
1239 int ret = 0; 1343 int ret = 0;
1240 1344
1241 memset(&tev, 0, sizeof(tev)); 1345 memset(&tev, 0, sizeof(tev));
1242 1346 rawlist = get_probe_trace_command_rawlist(fd);
1243 rawlist = get_kprobe_trace_command_rawlist(fd);
1244 sl = strlist__new(true, NULL); 1347 sl = strlist__new(true, NULL);
1245 strlist__for_each(ent, rawlist) { 1348 strlist__for_each(ent, rawlist) {
1246 ret = parse_kprobe_trace_command(ent->s, &tev); 1349 ret = parse_probe_trace_command(ent->s, &tev);
1247 if (ret < 0) 1350 if (ret < 0)
1248 break; 1351 break;
1249 if (include_group) { 1352 if (include_group) {
@@ -1253,7 +1356,7 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group)
1253 ret = strlist__add(sl, buf); 1356 ret = strlist__add(sl, buf);
1254 } else 1357 } else
1255 ret = strlist__add(sl, tev.event); 1358 ret = strlist__add(sl, tev.event);
1256 clear_kprobe_trace_event(&tev); 1359 clear_probe_trace_event(&tev);
1257 if (ret < 0) 1360 if (ret < 0)
1258 break; 1361 break;
1259 } 1362 }
@@ -1266,13 +1369,13 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group)
1266 return sl; 1369 return sl;
1267} 1370}
1268 1371
1269static int write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) 1372static int write_probe_trace_event(int fd, struct probe_trace_event *tev)
1270{ 1373{
1271 int ret = 0; 1374 int ret = 0;
1272 char *buf = synthesize_kprobe_trace_command(tev); 1375 char *buf = synthesize_probe_trace_command(tev);
1273 1376
1274 if (!buf) { 1377 if (!buf) {
1275 pr_debug("Failed to synthesize kprobe trace event.\n"); 1378 pr_debug("Failed to synthesize probe trace event.\n");
1276 return -EINVAL; 1379 return -EINVAL;
1277 } 1380 }
1278 1381
@@ -1325,12 +1428,12 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
1325 return ret; 1428 return ret;
1326} 1429}
1327 1430
1328static int __add_kprobe_trace_events(struct perf_probe_event *pev, 1431static int __add_probe_trace_events(struct perf_probe_event *pev,
1329 struct kprobe_trace_event *tevs, 1432 struct probe_trace_event *tevs,
1330 int ntevs, bool allow_suffix) 1433 int ntevs, bool allow_suffix)
1331{ 1434{
1332 int i, fd, ret; 1435 int i, fd, ret;
1333 struct kprobe_trace_event *tev = NULL; 1436 struct probe_trace_event *tev = NULL;
1334 char buf[64]; 1437 char buf[64];
1335 const char *event, *group; 1438 const char *event, *group;
1336 struct strlist *namelist; 1439 struct strlist *namelist;
@@ -1339,7 +1442,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev,
1339 if (fd < 0) 1442 if (fd < 0)
1340 return fd; 1443 return fd;
1341 /* Get current event names */ 1444 /* Get current event names */
1342 namelist = get_kprobe_trace_event_names(fd, false); 1445 namelist = get_probe_trace_event_names(fd, false);
1343 if (!namelist) { 1446 if (!namelist) {
1344 pr_debug("Failed to get current event list.\n"); 1447 pr_debug("Failed to get current event list.\n");
1345 return -EIO; 1448 return -EIO;
@@ -1374,7 +1477,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev,
1374 ret = -ENOMEM; 1477 ret = -ENOMEM;
1375 break; 1478 break;
1376 } 1479 }
1377 ret = write_kprobe_trace_event(fd, tev); 1480 ret = write_probe_trace_event(fd, tev);
1378 if (ret < 0) 1481 if (ret < 0)
1379 break; 1482 break;
1380 /* Add added event name to namelist */ 1483 /* Add added event name to namelist */
@@ -1411,21 +1514,21 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev,
1411 return ret; 1514 return ret;
1412} 1515}
1413 1516
1414static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, 1517static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1415 struct kprobe_trace_event **tevs, 1518 struct probe_trace_event **tevs,
1416 int max_tevs) 1519 int max_tevs)
1417{ 1520{
1418 struct symbol *sym; 1521 struct symbol *sym;
1419 int ret = 0, i; 1522 int ret = 0, i;
1420 struct kprobe_trace_event *tev; 1523 struct probe_trace_event *tev;
1421 1524
1422 /* Convert perf_probe_event with debuginfo */ 1525 /* Convert perf_probe_event with debuginfo */
1423 ret = try_to_find_kprobe_trace_events(pev, tevs, max_tevs); 1526 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs);
1424 if (ret != 0) 1527 if (ret != 0)
1425 return ret; 1528 return ret;
1426 1529
1427 /* Allocate trace event buffer */ 1530 /* Allocate trace event buffer */
1428 tev = *tevs = zalloc(sizeof(struct kprobe_trace_event)); 1531 tev = *tevs = zalloc(sizeof(struct probe_trace_event));
1429 if (tev == NULL) 1532 if (tev == NULL)
1430 return -ENOMEM; 1533 return -ENOMEM;
1431 1534
@@ -1438,7 +1541,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
1438 tev->point.offset = pev->point.offset; 1541 tev->point.offset = pev->point.offset;
1439 tev->nargs = pev->nargs; 1542 tev->nargs = pev->nargs;
1440 if (tev->nargs) { 1543 if (tev->nargs) {
1441 tev->args = zalloc(sizeof(struct kprobe_trace_arg) 1544 tev->args = zalloc(sizeof(struct probe_trace_arg)
1442 * tev->nargs); 1545 * tev->nargs);
1443 if (tev->args == NULL) { 1546 if (tev->args == NULL) {
1444 ret = -ENOMEM; 1547 ret = -ENOMEM;
@@ -1479,7 +1582,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
1479 1582
1480 return 1; 1583 return 1;
1481error: 1584error:
1482 clear_kprobe_trace_event(tev); 1585 clear_probe_trace_event(tev);
1483 free(tev); 1586 free(tev);
1484 *tevs = NULL; 1587 *tevs = NULL;
1485 return ret; 1588 return ret;
@@ -1487,7 +1590,7 @@ error:
1487 1590
1488struct __event_package { 1591struct __event_package {
1489 struct perf_probe_event *pev; 1592 struct perf_probe_event *pev;
1490 struct kprobe_trace_event *tevs; 1593 struct probe_trace_event *tevs;
1491 int ntevs; 1594 int ntevs;
1492}; 1595};
1493 1596
@@ -1503,14 +1606,16 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1503 1606
1504 /* Init vmlinux path */ 1607 /* Init vmlinux path */
1505 ret = init_vmlinux(); 1608 ret = init_vmlinux();
1506 if (ret < 0) 1609 if (ret < 0) {
1610 free(pkgs);
1507 return ret; 1611 return ret;
1612 }
1508 1613
1509 /* Loop 1: convert all events */ 1614 /* Loop 1: convert all events */
1510 for (i = 0; i < npevs; i++) { 1615 for (i = 0; i < npevs; i++) {
1511 pkgs[i].pev = &pevs[i]; 1616 pkgs[i].pev = &pevs[i];
1512 /* Convert with or without debuginfo */ 1617 /* Convert with or without debuginfo */
1513 ret = convert_to_kprobe_trace_events(pkgs[i].pev, 1618 ret = convert_to_probe_trace_events(pkgs[i].pev,
1514 &pkgs[i].tevs, max_tevs); 1619 &pkgs[i].tevs, max_tevs);
1515 if (ret < 0) 1620 if (ret < 0)
1516 goto end; 1621 goto end;
@@ -1519,24 +1624,27 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1519 1624
1520 /* Loop 2: add all events */ 1625 /* Loop 2: add all events */
1521 for (i = 0; i < npevs && ret >= 0; i++) 1626 for (i = 0; i < npevs && ret >= 0; i++)
1522 ret = __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, 1627 ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
1523 pkgs[i].ntevs, force_add); 1628 pkgs[i].ntevs, force_add);
1524end: 1629end:
1525 /* Loop 3: cleanup trace events */ 1630 /* Loop 3: cleanup and free trace events */
1526 for (i = 0; i < npevs; i++) 1631 for (i = 0; i < npevs; i++) {
1527 for (j = 0; j < pkgs[i].ntevs; j++) 1632 for (j = 0; j < pkgs[i].ntevs; j++)
1528 clear_kprobe_trace_event(&pkgs[i].tevs[j]); 1633 clear_probe_trace_event(&pkgs[i].tevs[j]);
1634 free(pkgs[i].tevs);
1635 }
1636 free(pkgs);
1529 1637
1530 return ret; 1638 return ret;
1531} 1639}
1532 1640
1533static int __del_trace_kprobe_event(int fd, struct str_node *ent) 1641static int __del_trace_probe_event(int fd, struct str_node *ent)
1534{ 1642{
1535 char *p; 1643 char *p;
1536 char buf[128]; 1644 char buf[128];
1537 int ret; 1645 int ret;
1538 1646
1539 /* Convert from perf-probe event to trace-kprobe event */ 1647 /* Convert from perf-probe event to trace-probe event */
1540 ret = e_snprintf(buf, 128, "-:%s", ent->s); 1648 ret = e_snprintf(buf, 128, "-:%s", ent->s);
1541 if (ret < 0) 1649 if (ret < 0)
1542 goto error; 1650 goto error;
@@ -1562,7 +1670,7 @@ error:
1562 return ret; 1670 return ret;
1563} 1671}
1564 1672
1565static int del_trace_kprobe_event(int fd, const char *group, 1673static int del_trace_probe_event(int fd, const char *group,
1566 const char *event, struct strlist *namelist) 1674 const char *event, struct strlist *namelist)
1567{ 1675{
1568 char buf[128]; 1676 char buf[128];
@@ -1579,7 +1687,7 @@ static int del_trace_kprobe_event(int fd, const char *group,
1579 strlist__for_each_safe(ent, n, namelist) 1687 strlist__for_each_safe(ent, n, namelist)
1580 if (strglobmatch(ent->s, buf)) { 1688 if (strglobmatch(ent->s, buf)) {
1581 found++; 1689 found++;
1582 ret = __del_trace_kprobe_event(fd, ent); 1690 ret = __del_trace_probe_event(fd, ent);
1583 if (ret < 0) 1691 if (ret < 0)
1584 break; 1692 break;
1585 strlist__remove(namelist, ent); 1693 strlist__remove(namelist, ent);
@@ -1588,7 +1696,7 @@ static int del_trace_kprobe_event(int fd, const char *group,
1588 ent = strlist__find(namelist, buf); 1696 ent = strlist__find(namelist, buf);
1589 if (ent) { 1697 if (ent) {
1590 found++; 1698 found++;
1591 ret = __del_trace_kprobe_event(fd, ent); 1699 ret = __del_trace_probe_event(fd, ent);
1592 if (ret >= 0) 1700 if (ret >= 0)
1593 strlist__remove(namelist, ent); 1701 strlist__remove(namelist, ent);
1594 } 1702 }
@@ -1612,7 +1720,7 @@ int del_perf_probe_events(struct strlist *dellist)
1612 return fd; 1720 return fd;
1613 1721
1614 /* Get current event names */ 1722 /* Get current event names */
1615 namelist = get_kprobe_trace_event_names(fd, true); 1723 namelist = get_probe_trace_event_names(fd, true);
1616 if (namelist == NULL) 1724 if (namelist == NULL)
1617 return -EINVAL; 1725 return -EINVAL;
1618 1726
@@ -1633,7 +1741,7 @@ int del_perf_probe_events(struct strlist *dellist)
1633 event = str; 1741 event = str;
1634 } 1742 }
1635 pr_debug("Group: %s, Event: %s\n", group, event); 1743 pr_debug("Group: %s, Event: %s\n", group, event);
1636 ret = del_trace_kprobe_event(fd, group, event, namelist); 1744 ret = del_trace_probe_event(fd, group, event, namelist);
1637 free(str); 1745 free(str);
1638 if (ret < 0) 1746 if (ret < 0)
1639 break; 1747 break;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index e9db1a214ca4..5af39243a25b 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -7,33 +7,33 @@
7extern bool probe_event_dry_run; 7extern bool probe_event_dry_run;
8 8
9/* kprobe-tracer tracing point */ 9/* kprobe-tracer tracing point */
10struct kprobe_trace_point { 10struct probe_trace_point {
11 char *symbol; /* Base symbol */ 11 char *symbol; /* Base symbol */
12 unsigned long offset; /* Offset from symbol */ 12 unsigned long offset; /* Offset from symbol */
13 bool retprobe; /* Return probe flag */ 13 bool retprobe; /* Return probe flag */
14}; 14};
15 15
16/* kprobe-tracer tracing argument referencing offset */ 16/* probe-tracer tracing argument referencing offset */
17struct kprobe_trace_arg_ref { 17struct probe_trace_arg_ref {
18 struct kprobe_trace_arg_ref *next; /* Next reference */ 18 struct probe_trace_arg_ref *next; /* Next reference */
19 long offset; /* Offset value */ 19 long offset; /* Offset value */
20}; 20};
21 21
22/* kprobe-tracer tracing argument */ 22/* kprobe-tracer tracing argument */
23struct kprobe_trace_arg { 23struct probe_trace_arg {
24 char *name; /* Argument name */ 24 char *name; /* Argument name */
25 char *value; /* Base value */ 25 char *value; /* Base value */
26 char *type; /* Type name */ 26 char *type; /* Type name */
27 struct kprobe_trace_arg_ref *ref; /* Referencing offset */ 27 struct probe_trace_arg_ref *ref; /* Referencing offset */
28}; 28};
29 29
30/* kprobe-tracer tracing event (point + arg) */ 30/* kprobe-tracer tracing event (point + arg) */
31struct kprobe_trace_event { 31struct probe_trace_event {
32 char *event; /* Event name */ 32 char *event; /* Event name */
33 char *group; /* Group name */ 33 char *group; /* Group name */
34 struct kprobe_trace_point point; /* Trace point */ 34 struct probe_trace_point point; /* Trace point */
35 int nargs; /* Number of args */ 35 int nargs; /* Number of args */
36 struct kprobe_trace_arg *args; /* Arguments */ 36 struct probe_trace_arg *args; /* Arguments */
37}; 37};
38 38
39/* Perf probe probing point */ 39/* Perf probe probing point */
@@ -50,6 +50,7 @@ struct perf_probe_point {
50struct perf_probe_arg_field { 50struct perf_probe_arg_field {
51 struct perf_probe_arg_field *next; /* Next field */ 51 struct perf_probe_arg_field *next; /* Next field */
52 char *name; /* Name of the field */ 52 char *name; /* Name of the field */
53 long index; /* Array index number */
53 bool ref; /* Referencing flag */ 54 bool ref; /* Referencing flag */
54}; 55};
55 56
@@ -85,31 +86,25 @@ struct line_range {
85 int end; /* End line number */ 86 int end; /* End line number */
86 int offset; /* Start line offset */ 87 int offset; /* Start line offset */
87 char *path; /* Real path name */ 88 char *path; /* Real path name */
89 char *comp_dir; /* Compile directory */
88 struct list_head line_list; /* Visible lines */ 90 struct list_head line_list; /* Visible lines */
89}; 91};
90 92
91/* Command string to events */ 93/* Command string to events */
92extern int parse_perf_probe_command(const char *cmd, 94extern int parse_perf_probe_command(const char *cmd,
93 struct perf_probe_event *pev); 95 struct perf_probe_event *pev);
94extern int parse_kprobe_trace_command(const char *cmd,
95 struct kprobe_trace_event *tev);
96 96
97/* Events to command string */ 97/* Events to command string */
98extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); 98extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
99extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); 99extern char *synthesize_probe_trace_command(struct probe_trace_event *tev);
100extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, 100extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf,
101 size_t len); 101 size_t len);
102 102
103/* Check the perf_probe_event needs debuginfo */ 103/* Check the perf_probe_event needs debuginfo */
104extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); 104extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
105 105
106/* Convert from kprobe_trace_event to perf_probe_event */
107extern int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
108 struct perf_probe_event *pev);
109
110/* Release event contents */ 106/* Release event contents */
111extern void clear_perf_probe_event(struct perf_probe_event *pev); 107extern void clear_perf_probe_event(struct perf_probe_event *pev);
112extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev);
113 108
114/* Command string to line-range */ 109/* Command string to line-range */
115extern int parse_line_range_desc(const char *cmd, struct line_range *lr); 110extern 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 d964cb199c67..525136684d4e 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -33,10 +33,10 @@
33#include <ctype.h> 33#include <ctype.h>
34#include <dwarf-regs.h> 34#include <dwarf-regs.h>
35 35
36#include "string.h"
37#include "event.h" 36#include "event.h"
38#include "debug.h" 37#include "debug.h"
39#include "util.h" 38#include "util.h"
39#include "symbol.h"
40#include "probe-finder.h" 40#include "probe-finder.h"
41 41
42/* Kprobe tracer basic type is up to u64 */ 42/* Kprobe tracer basic type is up to u64 */
@@ -143,12 +143,21 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname)
143 return src; 143 return src;
144} 144}
145 145
146/* Get DW_AT_comp_dir (should be NULL with older gcc) */
147static const char *cu_get_comp_dir(Dwarf_Die *cu_die)
148{
149 Dwarf_Attribute attr;
150 if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL)
151 return NULL;
152 return dwarf_formstring(&attr);
153}
154
146/* Compare diename and tname */ 155/* Compare diename and tname */
147static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) 156static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
148{ 157{
149 const char *name; 158 const char *name;
150 name = dwarf_diename(dw_die); 159 name = dwarf_diename(dw_die);
151 return name ? strcmp(tname, name) : -1; 160 return name ? (strcmp(tname, name) == 0) : false;
152} 161}
153 162
154/* Get type die, but skip qualifiers and typedef */ 163/* Get type die, but skip qualifiers and typedef */
@@ -319,7 +328,7 @@ static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
319 tag = dwarf_tag(die_mem); 328 tag = dwarf_tag(die_mem);
320 if ((tag == DW_TAG_formal_parameter || 329 if ((tag == DW_TAG_formal_parameter ||
321 tag == DW_TAG_variable) && 330 tag == DW_TAG_variable) &&
322 (die_compare_name(die_mem, name) == 0)) 331 die_compare_name(die_mem, name))
323 return DIE_FIND_CB_FOUND; 332 return DIE_FIND_CB_FOUND;
324 333
325 return DIE_FIND_CB_CONTINUE; 334 return DIE_FIND_CB_CONTINUE;
@@ -338,7 +347,7 @@ static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
338 const char *name = data; 347 const char *name = data;
339 348
340 if ((dwarf_tag(die_mem) == DW_TAG_member) && 349 if ((dwarf_tag(die_mem) == DW_TAG_member) &&
341 (die_compare_name(die_mem, name) == 0)) 350 die_compare_name(die_mem, name))
342 return DIE_FIND_CB_FOUND; 351 return DIE_FIND_CB_FOUND;
343 352
344 return DIE_FIND_CB_SIBLING; 353 return DIE_FIND_CB_SIBLING;
@@ -356,14 +365,50 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
356 * Probe finder related functions 365 * Probe finder related functions
357 */ 366 */
358 367
368static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
369{
370 struct probe_trace_arg_ref *ref;
371 ref = zalloc(sizeof(struct probe_trace_arg_ref));
372 if (ref != NULL)
373 ref->offset = offs;
374 return ref;
375}
376
359/* Show a location */ 377/* Show a location */
360static int convert_location(Dwarf_Op *op, struct probe_finder *pf) 378static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
361{ 379{
380 Dwarf_Attribute attr;
381 Dwarf_Op *op;
382 size_t nops;
362 unsigned int regn; 383 unsigned int regn;
363 Dwarf_Word offs = 0; 384 Dwarf_Word offs = 0;
364 bool ref = false; 385 bool ref = false;
365 const char *regs; 386 const char *regs;
366 struct kprobe_trace_arg *tvar = pf->tvar; 387 struct probe_trace_arg *tvar = pf->tvar;
388 int ret;
389
390 /* TODO: handle more than 1 exprs */
391 if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
392 dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
393 nops == 0) {
394 /* TODO: Support const_value */
395 pr_err("Failed to find the location of %s at this address.\n"
396 " Perhaps, it has been optimized out.\n", pf->pvar->var);
397 return -ENOENT;
398 }
399
400 if (op->atom == DW_OP_addr) {
401 /* Static variables on memory (not stack), make @varname */
402 ret = strlen(dwarf_diename(vr_die));
403 tvar->value = zalloc(ret + 2);
404 if (tvar->value == NULL)
405 return -ENOMEM;
406 snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die));
407 tvar->ref = alloc_trace_arg_ref((long)offs);
408 if (tvar->ref == NULL)
409 return -ENOMEM;
410 return 0;
411 }
367 412
368 /* If this is based on frame buffer, set the offset */ 413 /* If this is based on frame buffer, set the offset */
369 if (op->atom == DW_OP_fbreg) { 414 if (op->atom == DW_OP_fbreg) {
@@ -405,27 +450,72 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
405 return -ENOMEM; 450 return -ENOMEM;
406 451
407 if (ref) { 452 if (ref) {
408 tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); 453 tvar->ref = alloc_trace_arg_ref((long)offs);
409 if (tvar->ref == NULL) 454 if (tvar->ref == NULL)
410 return -ENOMEM; 455 return -ENOMEM;
411 tvar->ref->offset = (long)offs;
412 } 456 }
413 return 0; 457 return 0;
414} 458}
415 459
416static int convert_variable_type(Dwarf_Die *vr_die, 460static int convert_variable_type(Dwarf_Die *vr_die,
417 struct kprobe_trace_arg *targ) 461 struct probe_trace_arg *tvar,
462 const char *cast)
418{ 463{
464 struct probe_trace_arg_ref **ref_ptr = &tvar->ref;
419 Dwarf_Die type; 465 Dwarf_Die type;
420 char buf[16]; 466 char buf[16];
421 int ret; 467 int ret;
422 468
469 /* TODO: check all types */
470 if (cast && strcmp(cast, "string") != 0) {
471 /* Non string type is OK */
472 tvar->type = strdup(cast);
473 return (tvar->type == NULL) ? -ENOMEM : 0;
474 }
475
423 if (die_get_real_type(vr_die, &type) == NULL) { 476 if (die_get_real_type(vr_die, &type) == NULL) {
424 pr_warning("Failed to get a type information of %s.\n", 477 pr_warning("Failed to get a type information of %s.\n",
425 dwarf_diename(vr_die)); 478 dwarf_diename(vr_die));
426 return -ENOENT; 479 return -ENOENT;
427 } 480 }
428 481
482 pr_debug("%s type is %s.\n",
483 dwarf_diename(vr_die), dwarf_diename(&type));
484
485 if (cast && strcmp(cast, "string") == 0) { /* String type */
486 ret = dwarf_tag(&type);
487 if (ret != DW_TAG_pointer_type &&
488 ret != DW_TAG_array_type) {
489 pr_warning("Failed to cast into string: "
490 "%s(%s) is not a pointer nor array.",
491 dwarf_diename(vr_die), dwarf_diename(&type));
492 return -EINVAL;
493 }
494 if (ret == DW_TAG_pointer_type) {
495 if (die_get_real_type(&type, &type) == NULL) {
496 pr_warning("Failed to get a type information.");
497 return -ENOENT;
498 }
499 while (*ref_ptr)
500 ref_ptr = &(*ref_ptr)->next;
501 /* Add new reference with offset +0 */
502 *ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref));
503 if (*ref_ptr == NULL) {
504 pr_warning("Out of memory error\n");
505 return -ENOMEM;
506 }
507 }
508 if (!die_compare_name(&type, "char") &&
509 !die_compare_name(&type, "unsigned char")) {
510 pr_warning("Failed to cast into string: "
511 "%s is not (unsigned) char *.",
512 dwarf_diename(vr_die));
513 return -EINVAL;
514 }
515 tvar->type = strdup(cast);
516 return (tvar->type == NULL) ? -ENOMEM : 0;
517 }
518
429 ret = die_get_byte_size(&type) * 8; 519 ret = die_get_byte_size(&type) * 8;
430 if (ret) { 520 if (ret) {
431 /* Check the bitwidth */ 521 /* Check the bitwidth */
@@ -445,8 +535,8 @@ static int convert_variable_type(Dwarf_Die *vr_die,
445 strerror(-ret)); 535 strerror(-ret));
446 return ret; 536 return ret;
447 } 537 }
448 targ->type = strdup(buf); 538 tvar->type = strdup(buf);
449 if (targ->type == NULL) 539 if (tvar->type == NULL)
450 return -ENOMEM; 540 return -ENOMEM;
451 } 541 }
452 return 0; 542 return 0;
@@ -454,22 +544,50 @@ static int convert_variable_type(Dwarf_Die *vr_die,
454 544
455static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, 545static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
456 struct perf_probe_arg_field *field, 546 struct perf_probe_arg_field *field,
457 struct kprobe_trace_arg_ref **ref_ptr, 547 struct probe_trace_arg_ref **ref_ptr,
458 Dwarf_Die *die_mem) 548 Dwarf_Die *die_mem)
459{ 549{
460 struct kprobe_trace_arg_ref *ref = *ref_ptr; 550 struct probe_trace_arg_ref *ref = *ref_ptr;
461 Dwarf_Die type; 551 Dwarf_Die type;
462 Dwarf_Word offs; 552 Dwarf_Word offs;
463 int ret; 553 int ret, tag;
464 554
465 pr_debug("converting %s in %s\n", field->name, varname); 555 pr_debug("converting %s in %s\n", field->name, varname);
466 if (die_get_real_type(vr_die, &type) == NULL) { 556 if (die_get_real_type(vr_die, &type) == NULL) {
467 pr_warning("Failed to get the type of %s.\n", varname); 557 pr_warning("Failed to get the type of %s.\n", varname);
468 return -ENOENT; 558 return -ENOENT;
469 } 559 }
470 560 pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type));
471 /* Check the pointer and dereference */ 561 tag = dwarf_tag(&type);
472 if (dwarf_tag(&type) == DW_TAG_pointer_type) { 562
563 if (field->name[0] == '[' &&
564 (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) {
565 if (field->next)
566 /* Save original type for next field */
567 memcpy(die_mem, &type, sizeof(*die_mem));
568 /* Get the type of this array */
569 if (die_get_real_type(&type, &type) == NULL) {
570 pr_warning("Failed to get the type of %s.\n", varname);
571 return -ENOENT;
572 }
573 pr_debug2("Array real type: (%x)\n",
574 (unsigned)dwarf_dieoffset(&type));
575 if (tag == DW_TAG_pointer_type) {
576 ref = zalloc(sizeof(struct probe_trace_arg_ref));
577 if (ref == NULL)
578 return -ENOMEM;
579 if (*ref_ptr)
580 (*ref_ptr)->next = ref;
581 else
582 *ref_ptr = ref;
583 }
584 ref->offset += die_get_byte_size(&type) * field->index;
585 if (!field->next)
586 /* Save vr_die for converting types */
587 memcpy(die_mem, vr_die, sizeof(*die_mem));
588 goto next;
589 } else if (tag == DW_TAG_pointer_type) {
590 /* Check the pointer and dereference */
473 if (!field->ref) { 591 if (!field->ref) {
474 pr_err("Semantic error: %s must be referred by '->'\n", 592 pr_err("Semantic error: %s must be referred by '->'\n",
475 field->name); 593 field->name);
@@ -486,7 +604,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
486 return -EINVAL; 604 return -EINVAL;
487 } 605 }
488 606
489 ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); 607 ref = zalloc(sizeof(struct probe_trace_arg_ref));
490 if (ref == NULL) 608 if (ref == NULL)
491 return -ENOMEM; 609 return -ENOMEM;
492 if (*ref_ptr) 610 if (*ref_ptr)
@@ -495,10 +613,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
495 *ref_ptr = ref; 613 *ref_ptr = ref;
496 } else { 614 } else {
497 /* Verify it is a data structure */ 615 /* Verify it is a data structure */
498 if (dwarf_tag(&type) != DW_TAG_structure_type) { 616 if (tag != DW_TAG_structure_type) {
499 pr_warning("%s is not a data structure.\n", varname); 617 pr_warning("%s is not a data structure.\n", varname);
500 return -EINVAL; 618 return -EINVAL;
501 } 619 }
620 if (field->name[0] == '[') {
621 pr_err("Semantic error: %s is not a pointor nor array.",
622 varname);
623 return -EINVAL;
624 }
502 if (field->ref) { 625 if (field->ref) {
503 pr_err("Semantic error: %s must be referred by '.'\n", 626 pr_err("Semantic error: %s must be referred by '.'\n",
504 field->name); 627 field->name);
@@ -525,6 +648,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
525 } 648 }
526 ref->offset += (long)offs; 649 ref->offset += (long)offs;
527 650
651next:
528 /* Converting next field */ 652 /* Converting next field */
529 if (field->next) 653 if (field->next)
530 return convert_variable_fields(die_mem, field->name, 654 return convert_variable_fields(die_mem, field->name,
@@ -536,51 +660,32 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
536/* Show a variables in kprobe event format */ 660/* Show a variables in kprobe event format */
537static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) 661static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
538{ 662{
539 Dwarf_Attribute attr;
540 Dwarf_Die die_mem; 663 Dwarf_Die die_mem;
541 Dwarf_Op *expr;
542 size_t nexpr;
543 int ret; 664 int ret;
544 665
545 if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) 666 pr_debug("Converting variable %s into trace event.\n",
546 goto error; 667 dwarf_diename(vr_die));
547 /* TODO: handle more than 1 exprs */
548 ret = dwarf_getlocation_addr(&attr, pf->addr, &expr, &nexpr, 1);
549 if (ret <= 0 || nexpr == 0)
550 goto error;
551 668
552 ret = convert_location(expr, pf); 669 ret = convert_variable_location(vr_die, pf);
553 if (ret == 0 && pf->pvar->field) { 670 if (ret == 0 && pf->pvar->field) {
554 ret = convert_variable_fields(vr_die, pf->pvar->var, 671 ret = convert_variable_fields(vr_die, pf->pvar->var,
555 pf->pvar->field, &pf->tvar->ref, 672 pf->pvar->field, &pf->tvar->ref,
556 &die_mem); 673 &die_mem);
557 vr_die = &die_mem; 674 vr_die = &die_mem;
558 } 675 }
559 if (ret == 0) { 676 if (ret == 0)
560 if (pf->pvar->type) { 677 ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type);
561 pf->tvar->type = strdup(pf->pvar->type);
562 if (pf->tvar->type == NULL)
563 ret = -ENOMEM;
564 } else
565 ret = convert_variable_type(vr_die, pf->tvar);
566 }
567 /* *expr will be cached in libdw. Don't free it. */ 678 /* *expr will be cached in libdw. Don't free it. */
568 return ret; 679 return ret;
569error:
570 /* TODO: Support const_value */
571 pr_err("Failed to find the location of %s at this address.\n"
572 " Perhaps, it has been optimized out.\n", pf->pvar->var);
573 return -ENOENT;
574} 680}
575 681
576/* Find a variable in a subprogram die */ 682/* Find a variable in a subprogram die */
577static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) 683static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
578{ 684{
579 Dwarf_Die vr_die; 685 Dwarf_Die vr_die, *scopes;
580 char buf[32], *ptr; 686 char buf[32], *ptr;
581 int ret; 687 int ret, nscopes;
582 688
583 /* TODO: Support arrays */
584 if (pf->pvar->name) 689 if (pf->pvar->name)
585 pf->tvar->name = strdup(pf->pvar->name); 690 pf->tvar->name = strdup(pf->pvar->name);
586 else { 691 else {
@@ -600,25 +705,43 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
600 pf->tvar->value = strdup(pf->pvar->var); 705 pf->tvar->value = strdup(pf->pvar->var);
601 if (pf->tvar->value == NULL) 706 if (pf->tvar->value == NULL)
602 return -ENOMEM; 707 return -ENOMEM;
603 else 708 if (pf->pvar->type) {
604 return 0; 709 pf->tvar->type = strdup(pf->pvar->type);
710 if (pf->tvar->type == NULL)
711 return -ENOMEM;
712 }
713 return 0;
605 } 714 }
606 715
607 pr_debug("Searching '%s' variable in context.\n", 716 pr_debug("Searching '%s' variable in context.\n",
608 pf->pvar->var); 717 pf->pvar->var);
609 /* Search child die for local variables and parameters. */ 718 /* Search child die for local variables and parameters. */
610 if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) { 719 if (die_find_variable(sp_die, pf->pvar->var, &vr_die))
720 ret = convert_variable(&vr_die, pf);
721 else {
722 /* Search upper class */
723 nscopes = dwarf_getscopes_die(sp_die, &scopes);
724 if (nscopes > 0) {
725 ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var,
726 0, NULL, 0, 0, &vr_die);
727 if (ret >= 0)
728 ret = convert_variable(&vr_die, pf);
729 else
730 ret = -ENOENT;
731 free(scopes);
732 } else
733 ret = -ENOENT;
734 }
735 if (ret < 0)
611 pr_warning("Failed to find '%s' in this function.\n", 736 pr_warning("Failed to find '%s' in this function.\n",
612 pf->pvar->var); 737 pf->pvar->var);
613 return -ENOENT; 738 return ret;
614 }
615 return convert_variable(&vr_die, pf);
616} 739}
617 740
618/* Show a probe point to output buffer */ 741/* Show a probe point to output buffer */
619static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) 742static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
620{ 743{
621 struct kprobe_trace_event *tev; 744 struct probe_trace_event *tev;
622 Dwarf_Addr eaddr; 745 Dwarf_Addr eaddr;
623 Dwarf_Die die_mem; 746 Dwarf_Die die_mem;
624 const char *name; 747 const char *name;
@@ -683,7 +806,7 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
683 806
684 /* Find each argument */ 807 /* Find each argument */
685 tev->nargs = pf->pev->nargs; 808 tev->nargs = pf->pev->nargs;
686 tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); 809 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
687 if (tev->args == NULL) 810 if (tev->args == NULL)
688 return -ENOMEM; 811 return -ENOMEM;
689 for (i = 0; i < pf->pev->nargs; i++) { 812 for (i = 0; i < pf->pev->nargs; i++) {
@@ -897,7 +1020,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
897 1020
898 /* Check tag and diename */ 1021 /* Check tag and diename */
899 if (dwarf_tag(sp_die) != DW_TAG_subprogram || 1022 if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
900 die_compare_name(sp_die, pp->function) != 0) 1023 !die_compare_name(sp_die, pp->function))
901 return DWARF_CB_OK; 1024 return DWARF_CB_OK;
902 1025
903 pf->fname = dwarf_decl_file(sp_die); 1026 pf->fname = dwarf_decl_file(sp_die);
@@ -940,9 +1063,9 @@ static int find_probe_point_by_func(struct probe_finder *pf)
940 return _param.retval; 1063 return _param.retval;
941} 1064}
942 1065
943/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ 1066/* Find probe_trace_events specified by perf_probe_event from debuginfo */
944int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, 1067int find_probe_trace_events(int fd, struct perf_probe_event *pev,
945 struct kprobe_trace_event **tevs, int max_tevs) 1068 struct probe_trace_event **tevs, int max_tevs)
946{ 1069{
947 struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; 1070 struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs};
948 struct perf_probe_point *pp = &pev->point; 1071 struct perf_probe_point *pp = &pev->point;
@@ -952,7 +1075,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
952 Dwarf *dbg; 1075 Dwarf *dbg;
953 int ret = 0; 1076 int ret = 0;
954 1077
955 pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); 1078 pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
956 if (pf.tevs == NULL) 1079 if (pf.tevs == NULL)
957 return -ENOMEM; 1080 return -ENOMEM;
958 *tevs = pf.tevs; 1081 *tevs = pf.tevs;
@@ -1096,7 +1219,7 @@ end:
1096static int line_range_add_line(const char *src, unsigned int lineno, 1219static int line_range_add_line(const char *src, unsigned int lineno,
1097 struct line_range *lr) 1220 struct line_range *lr)
1098{ 1221{
1099 /* Copy real path */ 1222 /* Copy source path */
1100 if (!lr->path) { 1223 if (!lr->path) {
1101 lr->path = strdup(src); 1224 lr->path = strdup(src);
1102 if (lr->path == NULL) 1225 if (lr->path == NULL)
@@ -1220,7 +1343,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
1220 struct line_range *lr = lf->lr; 1343 struct line_range *lr = lf->lr;
1221 1344
1222 if (dwarf_tag(sp_die) == DW_TAG_subprogram && 1345 if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
1223 die_compare_name(sp_die, lr->function) == 0) { 1346 die_compare_name(sp_die, lr->function)) {
1224 lf->fname = dwarf_decl_file(sp_die); 1347 lf->fname = dwarf_decl_file(sp_die);
1225 dwarf_decl_line(sp_die, &lr->offset); 1348 dwarf_decl_line(sp_die, &lr->offset);
1226 pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); 1349 pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
@@ -1263,6 +1386,7 @@ int find_line_range(int fd, struct line_range *lr)
1263 size_t cuhl; 1386 size_t cuhl;
1264 Dwarf_Die *diep; 1387 Dwarf_Die *diep;
1265 Dwarf *dbg; 1388 Dwarf *dbg;
1389 const char *comp_dir;
1266 1390
1267 dbg = dwarf_begin(fd, DWARF_C_READ); 1391 dbg = dwarf_begin(fd, DWARF_C_READ);
1268 if (!dbg) { 1392 if (!dbg) {
@@ -1298,7 +1422,18 @@ int find_line_range(int fd, struct line_range *lr)
1298 } 1422 }
1299 off = noff; 1423 off = noff;
1300 } 1424 }
1301 pr_debug("path: %lx\n", (unsigned long)lr->path); 1425
1426 /* Store comp_dir */
1427 if (lf.found) {
1428 comp_dir = cu_get_comp_dir(&lf.cu_die);
1429 if (comp_dir) {
1430 lr->comp_dir = strdup(comp_dir);
1431 if (!lr->comp_dir)
1432 ret = -ENOMEM;
1433 }
1434 }
1435
1436 pr_debug("path: %s\n", lr->path);
1302 dwarf_end(dbg); 1437 dwarf_end(dbg);
1303 1438
1304 return (ret < 0) ? ret : lf.found; 1439 return (ret < 0) ? ret : lf.found;
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index e1f61dcd18ff..4507d519f183 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)
16} 16}
17 17
18#ifdef DWARF_SUPPORT 18#ifdef DWARF_SUPPORT
19/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ 19/* Find probe_trace_events specified by perf_probe_event from debuginfo */
20extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, 20extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
21 struct kprobe_trace_event **tevs, 21 struct probe_trace_event **tevs,
22 int max_tevs); 22 int max_tevs);
23 23
24/* Find a perf_probe_point from debuginfo */ 24/* Find a perf_probe_point from debuginfo */
@@ -33,7 +33,7 @@ extern int find_line_range(int fd, struct line_range *lr);
33 33
34struct probe_finder { 34struct probe_finder {
35 struct perf_probe_event *pev; /* Target probe event */ 35 struct perf_probe_event *pev; /* Target probe event */
36 struct kprobe_trace_event *tevs; /* Result trace events */ 36 struct probe_trace_event *tevs; /* Result trace events */
37 int ntevs; /* Number of trace events */ 37 int ntevs; /* Number of trace events */
38 int max_tevs; /* Max number of trace events */ 38 int max_tevs; /* Max number of trace events */
39 39
@@ -50,7 +50,7 @@ struct probe_finder {
50#endif 50#endif
51 Dwarf_Op *fb_ops; /* Frame base attribute */ 51 Dwarf_Op *fb_ops; /* Frame base attribute */
52 struct perf_probe_arg *pvar; /* Current target variable */ 52 struct perf_probe_arg *pvar; /* Current target variable */
53 struct kprobe_trace_arg *tvar; /* Current result variable */ 53 struct probe_trace_arg *tvar; /* Current result variable */
54}; 54};
55 55
56struct line_finder { 56struct line_finder {
diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h
index 5ad07023504b..4cedea59f518 100644
--- a/tools/perf/util/pstack.h
+++ b/tools/perf/util/pstack.h
@@ -1,6 +1,8 @@
1#ifndef _PERF_PSTACK_ 1#ifndef _PERF_PSTACK_
2#define _PERF_PSTACK_ 2#define _PERF_PSTACK_
3 3
4#include <stdbool.h>
5
4struct pstack; 6struct pstack;
5struct pstack *pstack__new(unsigned short max_nr_entries); 7struct pstack *pstack__new(unsigned short max_nr_entries);
6void pstack__delete(struct pstack *self); 8void pstack__delete(struct pstack *self);
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 81f39cab3aaa..33a632523743 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -208,7 +208,7 @@ static void python_process_event(int cpu, void *data,
208 int size __unused, 208 int size __unused,
209 unsigned long long nsecs, char *comm) 209 unsigned long long nsecs, char *comm)
210{ 210{
211 PyObject *handler, *retval, *context, *t, *obj; 211 PyObject *handler, *retval, *context, *t, *obj, *dict = NULL;
212 static char handler_name[256]; 212 static char handler_name[256];
213 struct format_field *field; 213 struct format_field *field;
214 unsigned long long val; 214 unsigned long long val;
@@ -232,6 +232,14 @@ static void python_process_event(int cpu, void *data,
232 232
233 sprintf(handler_name, "%s__%s", event->system, event->name); 233 sprintf(handler_name, "%s__%s", event->system, event->name);
234 234
235 handler = PyDict_GetItemString(main_dict, handler_name);
236 if (handler && !PyCallable_Check(handler))
237 handler = NULL;
238 if (!handler) {
239 dict = PyDict_New();
240 if (!dict)
241 Py_FatalError("couldn't create Python dict");
242 }
235 s = nsecs / NSECS_PER_SEC; 243 s = nsecs / NSECS_PER_SEC;
236 ns = nsecs - s * NSECS_PER_SEC; 244 ns = nsecs - s * NSECS_PER_SEC;
237 245
@@ -242,12 +250,20 @@ static void python_process_event(int cpu, void *data,
242 PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); 250 PyTuple_SetItem(t, n++, PyString_FromString(handler_name));
243 PyTuple_SetItem(t, n++, 251 PyTuple_SetItem(t, n++,
244 PyCObject_FromVoidPtr(scripting_context, NULL)); 252 PyCObject_FromVoidPtr(scripting_context, NULL));
245 PyTuple_SetItem(t, n++, PyInt_FromLong(cpu));
246 PyTuple_SetItem(t, n++, PyInt_FromLong(s));
247 PyTuple_SetItem(t, n++, PyInt_FromLong(ns));
248 PyTuple_SetItem(t, n++, PyInt_FromLong(pid));
249 PyTuple_SetItem(t, n++, PyString_FromString(comm));
250 253
254 if (handler) {
255 PyTuple_SetItem(t, n++, PyInt_FromLong(cpu));
256 PyTuple_SetItem(t, n++, PyInt_FromLong(s));
257 PyTuple_SetItem(t, n++, PyInt_FromLong(ns));
258 PyTuple_SetItem(t, n++, PyInt_FromLong(pid));
259 PyTuple_SetItem(t, n++, PyString_FromString(comm));
260 } else {
261 PyDict_SetItemString(dict, "common_cpu", PyInt_FromLong(cpu));
262 PyDict_SetItemString(dict, "common_s", PyInt_FromLong(s));
263 PyDict_SetItemString(dict, "common_ns", PyInt_FromLong(ns));
264 PyDict_SetItemString(dict, "common_pid", PyInt_FromLong(pid));
265 PyDict_SetItemString(dict, "common_comm", PyString_FromString(comm));
266 }
251 for (field = event->format.fields; field; field = field->next) { 267 for (field = event->format.fields; field; field = field->next) {
252 if (field->flags & FIELD_IS_STRING) { 268 if (field->flags & FIELD_IS_STRING) {
253 int offset; 269 int offset;
@@ -272,27 +288,31 @@ static void python_process_event(int cpu, void *data,
272 obj = PyLong_FromUnsignedLongLong(val); 288 obj = PyLong_FromUnsignedLongLong(val);
273 } 289 }
274 } 290 }
275 PyTuple_SetItem(t, n++, obj); 291 if (handler)
292 PyTuple_SetItem(t, n++, obj);
293 else
294 PyDict_SetItemString(dict, field->name, obj);
295
276 } 296 }
297 if (!handler)
298 PyTuple_SetItem(t, n++, dict);
277 299
278 if (_PyTuple_Resize(&t, n) == -1) 300 if (_PyTuple_Resize(&t, n) == -1)
279 Py_FatalError("error resizing Python tuple"); 301 Py_FatalError("error resizing Python tuple");
280 302
281 handler = PyDict_GetItemString(main_dict, handler_name); 303 if (handler) {
282 if (handler && PyCallable_Check(handler)) {
283 retval = PyObject_CallObject(handler, t); 304 retval = PyObject_CallObject(handler, t);
284 if (retval == NULL) 305 if (retval == NULL)
285 handler_call_die(handler_name); 306 handler_call_die(handler_name);
286 } else { 307 } else {
287 handler = PyDict_GetItemString(main_dict, "trace_unhandled"); 308 handler = PyDict_GetItemString(main_dict, "trace_unhandled");
288 if (handler && PyCallable_Check(handler)) { 309 if (handler && PyCallable_Check(handler)) {
289 if (_PyTuple_Resize(&t, N_COMMON_FIELDS) == -1)
290 Py_FatalError("error resizing Python tuple");
291 310
292 retval = PyObject_CallObject(handler, t); 311 retval = PyObject_CallObject(handler, t);
293 if (retval == NULL) 312 if (retval == NULL)
294 handler_call_die("trace_unhandled"); 313 handler_call_die("trace_unhandled");
295 } 314 }
315 Py_DECREF(dict);
296 } 316 }
297 317
298 Py_DECREF(t); 318 Py_DECREF(t);
@@ -548,12 +568,10 @@ static int python_generate_script(const char *outfile)
548 } 568 }
549 569
550 fprintf(ofp, "def trace_unhandled(event_name, context, " 570 fprintf(ofp, "def trace_unhandled(event_name, context, "
551 "common_cpu, common_secs, common_nsecs,\n\t\t" 571 "event_fields_dict):\n");
552 "common_pid, common_comm):\n");
553 572
554 fprintf(ofp, "\t\tprint_header(event_name, common_cpu, " 573 fprintf(ofp, "\t\tprint ' '.join(['%%s=%%s'%%(k,str(v))"
555 "common_secs, common_nsecs,\n\t\tcommon_pid, " 574 "for k,v in sorted(event_fields_dict.items())])\n\n");
556 "common_comm)\n\n");
557 575
558 fprintf(ofp, "def print_header(" 576 fprintf(ofp, "def print_header("
559 "event_name, cpu, secs, nsecs, pid, comm):\n" 577 "event_name, cpu, secs, nsecs, pid, comm):\n"
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 8f83a1835766..fa9d652c2dc3 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)
27 27
28 self->fd = open(self->filename, O_RDONLY); 28 self->fd = open(self->filename, O_RDONLY);
29 if (self->fd < 0) { 29 if (self->fd < 0) {
30 pr_err("failed to open file: %s", self->filename); 30 int err = errno;
31 if (!strcmp(self->filename, "perf.data")) 31
32 pr_err("failed to open %s: %s", self->filename, strerror(err));
33 if (err == ENOENT && !strcmp(self->filename, "perf.data"))
32 pr_err(" (try 'perf record' first)"); 34 pr_err(" (try 'perf record' first)");
33 pr_err("\n"); 35 pr_err("\n");
34 return -errno; 36 return -errno;
@@ -77,6 +79,12 @@ int perf_session__create_kernel_maps(struct perf_session *self)
77 return ret; 79 return ret;
78} 80}
79 81
82static void perf_session__destroy_kernel_maps(struct perf_session *self)
83{
84 machine__destroy_kernel_maps(&self->host_machine);
85 machines__destroy_guest_kernel_maps(&self->machines);
86}
87
80struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) 88struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe)
81{ 89{
82 size_t len = filename ? strlen(filename) + 1 : 0; 90 size_t len = filename ? strlen(filename) + 1 : 0;
@@ -90,11 +98,10 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc
90 98
91 memcpy(self->filename, filename, len); 99 memcpy(self->filename, filename, len);
92 self->threads = RB_ROOT; 100 self->threads = RB_ROOT;
101 INIT_LIST_HEAD(&self->dead_threads);
93 self->hists_tree = RB_ROOT; 102 self->hists_tree = RB_ROOT;
94 self->last_match = NULL; 103 self->last_match = NULL;
95 self->mmap_window = 32; 104 self->mmap_window = 32;
96 self->cwd = NULL;
97 self->cwdlen = 0;
98 self->machines = RB_ROOT; 105 self->machines = RB_ROOT;
99 self->repipe = repipe; 106 self->repipe = repipe;
100 INIT_LIST_HEAD(&self->ordered_samples.samples_head); 107 INIT_LIST_HEAD(&self->ordered_samples.samples_head);
@@ -123,14 +130,51 @@ out_delete:
123 return NULL; 130 return NULL;
124} 131}
125 132
133static void perf_session__delete_dead_threads(struct perf_session *self)
134{
135 struct thread *n, *t;
136
137 list_for_each_entry_safe(t, n, &self->dead_threads, node) {
138 list_del(&t->node);
139 thread__delete(t);
140 }
141}
142
143static void perf_session__delete_threads(struct perf_session *self)
144{
145 struct rb_node *nd = rb_first(&self->threads);
146
147 while (nd) {
148 struct thread *t = rb_entry(nd, struct thread, rb_node);
149
150 rb_erase(&t->rb_node, &self->threads);
151 nd = rb_next(nd);
152 thread__delete(t);
153 }
154}
155
126void perf_session__delete(struct perf_session *self) 156void perf_session__delete(struct perf_session *self)
127{ 157{
128 perf_header__exit(&self->header); 158 perf_header__exit(&self->header);
159 perf_session__destroy_kernel_maps(self);
160 perf_session__delete_dead_threads(self);
161 perf_session__delete_threads(self);
162 machine__exit(&self->host_machine);
129 close(self->fd); 163 close(self->fd);
130 free(self->cwd);
131 free(self); 164 free(self);
132} 165}
133 166
167void perf_session__remove_thread(struct perf_session *self, struct thread *th)
168{
169 self->last_match = NULL;
170 rb_erase(&th->rb_node, &self->threads);
171 /*
172 * We may have references to this thread, for instance in some hist_entry
173 * instances, so just move them to a separate list.
174 */
175 list_add_tail(&th->node, &self->dead_threads);
176}
177
134static bool symbol__match_parent_regex(struct symbol *sym) 178static bool symbol__match_parent_regex(struct symbol *sym)
135{ 179{
136 if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) 180 if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
@@ -819,23 +863,6 @@ int perf_session__process_events(struct perf_session *self,
819 if (perf_session__register_idle_thread(self) == NULL) 863 if (perf_session__register_idle_thread(self) == NULL)
820 return -ENOMEM; 864 return -ENOMEM;
821 865
822 if (!symbol_conf.full_paths) {
823 char bf[PATH_MAX];
824
825 if (getcwd(bf, sizeof(bf)) == NULL) {
826 err = -errno;
827out_getcwd_err:
828 pr_err("failed to get the current directory\n");
829 goto out_err;
830 }
831 self->cwd = strdup(bf);
832 if (self->cwd == NULL) {
833 err = -ENOMEM;
834 goto out_getcwd_err;
835 }
836 self->cwdlen = strlen(self->cwd);
837 }
838
839 if (!self->fd_pipe) 866 if (!self->fd_pipe)
840 err = __perf_session__process_events(self, 867 err = __perf_session__process_events(self,
841 self->header.data_offset, 868 self->header.data_offset,
@@ -843,7 +870,7 @@ out_getcwd_err:
843 self->size, ops); 870 self->size, ops);
844 else 871 else
845 err = __perf_session__process_pipe_events(self, ops); 872 err = __perf_session__process_pipe_events(self, ops);
846out_err: 873
847 return err; 874 return err;
848} 875}
849 876
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 55c6881b218d..9fa0fc2a863f 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -26,6 +26,7 @@ struct perf_session {
26 unsigned long size; 26 unsigned long size;
27 unsigned long mmap_window; 27 unsigned long mmap_window;
28 struct rb_root threads; 28 struct rb_root threads;
29 struct list_head dead_threads;
29 struct thread *last_match; 30 struct thread *last_match;
30 struct machine host_machine; 31 struct machine host_machine;
31 struct rb_root machines; 32 struct rb_root machines;
@@ -99,6 +100,7 @@ int perf_session__create_kernel_maps(struct perf_session *self);
99 100
100int do_read(int fd, void *buf, size_t size); 101int do_read(int fd, void *buf, size_t size);
101void perf_session__update_sample_type(struct perf_session *self); 102void perf_session__update_sample_type(struct perf_session *self);
103void perf_session__remove_thread(struct perf_session *self, struct thread *th);
102 104
103static inline 105static inline
104struct machine *perf_session__find_host_machine(struct perf_session *self) 106struct machine *perf_session__find_host_machine(struct perf_session *self)
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 2316cb5a4116..b62a553cc67d 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -1,4 +1,5 @@
1#include "sort.h" 1#include "sort.h"
2#include "hist.h"
2 3
3regex_t parent_regex; 4regex_t parent_regex;
4const char default_parent_pattern[] = "^sys_|^do_page_fault"; 5const char default_parent_pattern[] = "^sys_|^do_page_fault";
@@ -10,10 +11,6 @@ int sort__has_parent = 0;
10 11
11enum sort_type sort__first_dimension; 12enum sort_type sort__first_dimension;
12 13
13unsigned int dsos__col_width;
14unsigned int comms__col_width;
15unsigned int threads__col_width;
16static unsigned int parent_symbol__col_width;
17char * field_sep; 14char * field_sep;
18 15
19LIST_HEAD(hist_entry__sort_list); 16LIST_HEAD(hist_entry__sort_list);
@@ -28,12 +25,14 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
28 size_t size, unsigned int width); 25 size_t size, unsigned int width);
29static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, 26static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
30 size_t size, unsigned int width); 27 size_t size, unsigned int width);
28static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
29 size_t size, unsigned int width);
31 30
32struct sort_entry sort_thread = { 31struct sort_entry sort_thread = {
33 .se_header = "Command: Pid", 32 .se_header = "Command: Pid",
34 .se_cmp = sort__thread_cmp, 33 .se_cmp = sort__thread_cmp,
35 .se_snprintf = hist_entry__thread_snprintf, 34 .se_snprintf = hist_entry__thread_snprintf,
36 .se_width = &threads__col_width, 35 .se_width_idx = HISTC_THREAD,
37}; 36};
38 37
39struct sort_entry sort_comm = { 38struct sort_entry sort_comm = {
@@ -41,27 +40,35 @@ struct sort_entry sort_comm = {
41 .se_cmp = sort__comm_cmp, 40 .se_cmp = sort__comm_cmp,
42 .se_collapse = sort__comm_collapse, 41 .se_collapse = sort__comm_collapse,
43 .se_snprintf = hist_entry__comm_snprintf, 42 .se_snprintf = hist_entry__comm_snprintf,
44 .se_width = &comms__col_width, 43 .se_width_idx = HISTC_COMM,
45}; 44};
46 45
47struct sort_entry sort_dso = { 46struct sort_entry sort_dso = {
48 .se_header = "Shared Object", 47 .se_header = "Shared Object",
49 .se_cmp = sort__dso_cmp, 48 .se_cmp = sort__dso_cmp,
50 .se_snprintf = hist_entry__dso_snprintf, 49 .se_snprintf = hist_entry__dso_snprintf,
51 .se_width = &dsos__col_width, 50 .se_width_idx = HISTC_DSO,
52}; 51};
53 52
54struct sort_entry sort_sym = { 53struct sort_entry sort_sym = {
55 .se_header = "Symbol", 54 .se_header = "Symbol",
56 .se_cmp = sort__sym_cmp, 55 .se_cmp = sort__sym_cmp,
57 .se_snprintf = hist_entry__sym_snprintf, 56 .se_snprintf = hist_entry__sym_snprintf,
57 .se_width_idx = HISTC_SYMBOL,
58}; 58};
59 59
60struct sort_entry sort_parent = { 60struct sort_entry sort_parent = {
61 .se_header = "Parent symbol", 61 .se_header = "Parent symbol",
62 .se_cmp = sort__parent_cmp, 62 .se_cmp = sort__parent_cmp,
63 .se_snprintf = hist_entry__parent_snprintf, 63 .se_snprintf = hist_entry__parent_snprintf,
64 .se_width = &parent_symbol__col_width, 64 .se_width_idx = HISTC_PARENT,
65};
66
67struct sort_entry sort_cpu = {
68 .se_header = "CPU",
69 .se_cmp = sort__cpu_cmp,
70 .se_snprintf = hist_entry__cpu_snprintf,
71 .se_width_idx = HISTC_CPU,
65}; 72};
66 73
67struct sort_dimension { 74struct sort_dimension {
@@ -76,6 +83,7 @@ static struct sort_dimension sort_dimensions[] = {
76 { .name = "dso", .entry = &sort_dso, }, 83 { .name = "dso", .entry = &sort_dso, },
77 { .name = "symbol", .entry = &sort_sym, }, 84 { .name = "symbol", .entry = &sort_sym, },
78 { .name = "parent", .entry = &sort_parent, }, 85 { .name = "parent", .entry = &sort_parent, },
86 { .name = "cpu", .entry = &sort_cpu, },
79}; 87};
80 88
81int64_t cmp_null(void *l, void *r) 89int64_t cmp_null(void *l, void *r)
@@ -188,7 +196,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
188 196
189 if (verbose) { 197 if (verbose) {
190 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; 198 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
191 ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o); 199 ret += repsep_snprintf(bf, size, "%*Lx %c ",
200 BITS_PER_LONG / 4, self->ip, o);
192 } 201 }
193 202
194 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); 203 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
@@ -196,7 +205,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
196 ret += repsep_snprintf(bf + ret, size - ret, "%s", 205 ret += repsep_snprintf(bf + ret, size - ret, "%s",
197 self->ms.sym->name); 206 self->ms.sym->name);
198 else 207 else
199 ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip); 208 ret += repsep_snprintf(bf + ret, size - ret, "%*Lx",
209 BITS_PER_LONG / 4, self->ip);
200 210
201 return ret; 211 return ret;
202} 212}
@@ -242,6 +252,20 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
242 self->parent ? self->parent->name : "[other]"); 252 self->parent ? self->parent->name : "[other]");
243} 253}
244 254
255/* --sort cpu */
256
257int64_t
258sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
259{
260 return right->cpu - left->cpu;
261}
262
263static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
264 size_t size, unsigned int width)
265{
266 return repsep_snprintf(bf, size, "%-*d", width, self->cpu);
267}
268
245int sort_dimension__add(const char *tok) 269int sort_dimension__add(const char *tok)
246{ 270{
247 unsigned int i; 271 unsigned int i;
@@ -281,6 +305,8 @@ int sort_dimension__add(const char *tok)
281 sort__first_dimension = SORT_SYM; 305 sort__first_dimension = SORT_SYM;
282 else if (!strcmp(sd->name, "parent")) 306 else if (!strcmp(sd->name, "parent"))
283 sort__first_dimension = SORT_PARENT; 307 sort__first_dimension = SORT_PARENT;
308 else if (!strcmp(sd->name, "cpu"))
309 sort__first_dimension = SORT_CPU;
284 } 310 }
285 311
286 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 312 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 0d61c4082f43..46e531d09e8b 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -36,11 +36,14 @@ extern struct sort_entry sort_comm;
36extern struct sort_entry sort_dso; 36extern struct sort_entry sort_dso;
37extern struct sort_entry sort_sym; 37extern struct sort_entry sort_sym;
38extern struct sort_entry sort_parent; 38extern struct sort_entry sort_parent;
39extern unsigned int dsos__col_width;
40extern unsigned int comms__col_width;
41extern unsigned int threads__col_width;
42extern enum sort_type sort__first_dimension; 39extern enum sort_type sort__first_dimension;
43 40
41/**
42 * struct hist_entry - histogram entry
43 *
44 * @row_offset - offset from the first callchain expanded to appear on screen
45 * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding
46 */
44struct hist_entry { 47struct hist_entry {
45 struct rb_node rb_node; 48 struct rb_node rb_node;
46 u64 period; 49 u64 period;
@@ -51,7 +54,14 @@ struct hist_entry {
51 struct map_symbol ms; 54 struct map_symbol ms;
52 struct thread *thread; 55 struct thread *thread;
53 u64 ip; 56 u64 ip;
57 s32 cpu;
54 u32 nr_events; 58 u32 nr_events;
59
60 /* XXX These two should move to some tree widget lib */
61 u16 row_offset;
62 u16 nr_rows;
63
64 bool init_have_children;
55 char level; 65 char level;
56 u8 filtered; 66 u8 filtered;
57 struct symbol *parent; 67 struct symbol *parent;
@@ -68,7 +78,8 @@ enum sort_type {
68 SORT_COMM, 78 SORT_COMM,
69 SORT_DSO, 79 SORT_DSO,
70 SORT_SYM, 80 SORT_SYM,
71 SORT_PARENT 81 SORT_PARENT,
82 SORT_CPU,
72}; 83};
73 84
74/* 85/*
@@ -84,7 +95,7 @@ struct sort_entry {
84 int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); 95 int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
85 int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, 96 int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size,
86 unsigned int width); 97 unsigned int width);
87 unsigned int *se_width; 98 u8 se_width_idx;
88 bool elide; 99 bool elide;
89}; 100};
90 101
@@ -104,6 +115,7 @@ extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *);
104extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); 115extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *);
105extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); 116extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *);
106extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); 117extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *);
118int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right);
107extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); 119extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int);
108extern int sort_dimension__add(const char *); 120extern int sort_dimension__add(const char *);
109void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, 121void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 7fd6b151feb5..1a367734e016 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -12,6 +12,7 @@
12#include <fcntl.h> 12#include <fcntl.h>
13#include <unistd.h> 13#include <unistd.h>
14#include "build-id.h" 14#include "build-id.h"
15#include "debug.h"
15#include "symbol.h" 16#include "symbol.h"
16#include "strlist.h" 17#include "strlist.h"
17 18
@@ -25,6 +26,8 @@
25#define NT_GNU_BUILD_ID 3 26#define NT_GNU_BUILD_ID 3
26#endif 27#endif
27 28
29static bool dso__build_id_equal(const struct dso *self, u8 *build_id);
30static int elf_read_build_id(Elf *elf, void *bf, size_t size);
28static void dsos__add(struct list_head *head, struct dso *dso); 31static void dsos__add(struct list_head *head, struct dso *dso);
29static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); 32static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
30static int dso__load_kernel_sym(struct dso *self, struct map *map, 33static int dso__load_kernel_sym(struct dso *self, struct map *map,
@@ -40,6 +43,14 @@ struct symbol_conf symbol_conf = {
40 .try_vmlinux_path = true, 43 .try_vmlinux_path = true,
41}; 44};
42 45
46int dso__name_len(const struct dso *self)
47{
48 if (verbose)
49 return self->long_name_len;
50
51 return self->short_name_len;
52}
53
43bool dso__loaded(const struct dso *self, enum map_type type) 54bool dso__loaded(const struct dso *self, enum map_type type)
44{ 55{
45 return self->loaded & (1 << type); 56 return self->loaded & (1 << type);
@@ -120,7 +131,8 @@ static void map_groups__fixup_end(struct map_groups *self)
120 __map_groups__fixup_end(self, i); 131 __map_groups__fixup_end(self, i);
121} 132}
122 133
123static struct symbol *symbol__new(u64 start, u64 len, const char *name) 134static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
135 const char *name)
124{ 136{
125 size_t namelen = strlen(name) + 1; 137 size_t namelen = strlen(name) + 1;
126 struct symbol *self = calloc(1, (symbol_conf.priv_size + 138 struct symbol *self = calloc(1, (symbol_conf.priv_size +
@@ -133,6 +145,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name)
133 145
134 self->start = start; 146 self->start = start;
135 self->end = len ? start + len - 1 : start; 147 self->end = len ? start + len - 1 : start;
148 self->binding = binding;
136 self->namelen = namelen - 1; 149 self->namelen = namelen - 1;
137 150
138 pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); 151 pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);
@@ -149,8 +162,11 @@ void symbol__delete(struct symbol *self)
149 162
150static size_t symbol__fprintf(struct symbol *self, FILE *fp) 163static size_t symbol__fprintf(struct symbol *self, FILE *fp)
151{ 164{
152 return fprintf(fp, " %llx-%llx %s\n", 165 return fprintf(fp, " %llx-%llx %c %s\n",
153 self->start, self->end, self->name); 166 self->start, self->end,
167 self->binding == STB_GLOBAL ? 'g' :
168 self->binding == STB_LOCAL ? 'l' : 'w',
169 self->name);
154} 170}
155 171
156void dso__set_long_name(struct dso *self, char *name) 172void dso__set_long_name(struct dso *self, char *name)
@@ -215,7 +231,9 @@ void dso__delete(struct dso *self)
215 int i; 231 int i;
216 for (i = 0; i < MAP__NR_TYPES; ++i) 232 for (i = 0; i < MAP__NR_TYPES; ++i)
217 symbols__delete(&self->symbols[i]); 233 symbols__delete(&self->symbols[i]);
218 if (self->long_name != self->name) 234 if (self->sname_alloc)
235 free((char *)self->short_name);
236 if (self->lname_alloc)
219 free(self->long_name); 237 free(self->long_name);
220 free(self); 238 free(self);
221} 239}
@@ -440,6 +458,14 @@ struct process_kallsyms_args {
440 struct dso *dso; 458 struct dso *dso;
441}; 459};
442 460
461static u8 kallsyms2elf_type(char type)
462{
463 if (type == 'W')
464 return STB_WEAK;
465
466 return isupper(type) ? STB_GLOBAL : STB_LOCAL;
467}
468
443static int map__process_kallsym_symbol(void *arg, const char *name, 469static int map__process_kallsym_symbol(void *arg, const char *name,
444 char type, u64 start) 470 char type, u64 start)
445{ 471{
@@ -453,7 +479,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
453 /* 479 /*
454 * Will fix up the end later, when we have all symbols sorted. 480 * Will fix up the end later, when we have all symbols sorted.
455 */ 481 */
456 sym = symbol__new(start, 0, name); 482 sym = symbol__new(start, 0, kallsyms2elf_type(type), name);
457 483
458 if (sym == NULL) 484 if (sym == NULL)
459 return -ENOMEM; 485 return -ENOMEM;
@@ -648,7 +674,7 @@ static int dso__load_perf_map(struct dso *self, struct map *map,
648 if (len + 2 >= line_len) 674 if (len + 2 >= line_len)
649 continue; 675 continue;
650 676
651 sym = symbol__new(start, size, line + len); 677 sym = symbol__new(start, size, STB_GLOBAL, line + len);
652 678
653 if (sym == NULL) 679 if (sym == NULL)
654 goto out_delete_line; 680 goto out_delete_line;
@@ -860,7 +886,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
860 "%s@plt", elf_sym__name(&sym, symstrs)); 886 "%s@plt", elf_sym__name(&sym, symstrs));
861 887
862 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 888 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
863 sympltname); 889 STB_GLOBAL, sympltname);
864 if (!f) 890 if (!f)
865 goto out_elf_end; 891 goto out_elf_end;
866 892
@@ -882,7 +908,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
882 "%s@plt", elf_sym__name(&sym, symstrs)); 908 "%s@plt", elf_sym__name(&sym, symstrs));
883 909
884 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 910 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
885 sympltname); 911 STB_GLOBAL, sympltname);
886 if (!f) 912 if (!f)
887 goto out_elf_end; 913 goto out_elf_end;
888 914
@@ -933,8 +959,28 @@ static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type
933 } 959 }
934} 960}
935 961
962static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
963{
964 Elf_Scn *sec = NULL;
965 GElf_Shdr shdr;
966 size_t cnt = 1;
967
968 while ((sec = elf_nextscn(elf, sec)) != NULL) {
969 gelf_getshdr(sec, &shdr);
970
971 if ((addr >= shdr.sh_addr) &&
972 (addr < (shdr.sh_addr + shdr.sh_size)))
973 return cnt;
974
975 ++cnt;
976 }
977
978 return -1;
979}
980
936static int dso__load_sym(struct dso *self, struct map *map, const char *name, 981static int dso__load_sym(struct dso *self, struct map *map, const char *name,
937 int fd, symbol_filter_t filter, int kmodule) 982 int fd, symbol_filter_t filter, int kmodule,
983 int want_symtab)
938{ 984{
939 struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; 985 struct kmap *kmap = self->kernel ? map__kmap(map) : NULL;
940 struct map *curr_map = map; 986 struct map *curr_map = map;
@@ -944,31 +990,51 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
944 int err = -1; 990 int err = -1;
945 uint32_t idx; 991 uint32_t idx;
946 GElf_Ehdr ehdr; 992 GElf_Ehdr ehdr;
947 GElf_Shdr shdr; 993 GElf_Shdr shdr, opdshdr;
948 Elf_Data *syms; 994 Elf_Data *syms, *opddata = NULL;
949 GElf_Sym sym; 995 GElf_Sym sym;
950 Elf_Scn *sec, *sec_strndx; 996 Elf_Scn *sec, *sec_strndx, *opdsec;
951 Elf *elf; 997 Elf *elf;
952 int nr = 0; 998 int nr = 0;
999 size_t opdidx = 0;
953 1000
954 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); 1001 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
955 if (elf == NULL) { 1002 if (elf == NULL) {
956 pr_err("%s: cannot read %s ELF file.\n", __func__, name); 1003 pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
957 goto out_close; 1004 goto out_close;
958 } 1005 }
959 1006
960 if (gelf_getehdr(elf, &ehdr) == NULL) { 1007 if (gelf_getehdr(elf, &ehdr) == NULL) {
961 pr_err("%s: cannot get elf header.\n", __func__); 1008 pr_debug("%s: cannot get elf header.\n", __func__);
962 goto out_elf_end; 1009 goto out_elf_end;
963 } 1010 }
964 1011
1012 /* Always reject images with a mismatched build-id: */
1013 if (self->has_build_id) {
1014 u8 build_id[BUILD_ID_SIZE];
1015
1016 if (elf_read_build_id(elf, build_id,
1017 BUILD_ID_SIZE) != BUILD_ID_SIZE)
1018 goto out_elf_end;
1019
1020 if (!dso__build_id_equal(self, build_id))
1021 goto out_elf_end;
1022 }
1023
965 sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); 1024 sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
966 if (sec == NULL) { 1025 if (sec == NULL) {
1026 if (want_symtab)
1027 goto out_elf_end;
1028
967 sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); 1029 sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
968 if (sec == NULL) 1030 if (sec == NULL)
969 goto out_elf_end; 1031 goto out_elf_end;
970 } 1032 }
971 1033
1034 opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx);
1035 if (opdsec)
1036 opddata = elf_rawdata(opdsec, NULL);
1037
972 syms = elf_getdata(sec, NULL); 1038 syms = elf_getdata(sec, NULL);
973 if (syms == NULL) 1039 if (syms == NULL)
974 goto out_elf_end; 1040 goto out_elf_end;
@@ -1013,6 +1079,23 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1013 if (!is_label && !elf_sym__is_a(&sym, map->type)) 1079 if (!is_label && !elf_sym__is_a(&sym, map->type))
1014 continue; 1080 continue;
1015 1081
1082 /* Reject ARM ELF "mapping symbols": these aren't unique and
1083 * don't identify functions, so will confuse the profile
1084 * output: */
1085 if (ehdr.e_machine == EM_ARM) {
1086 if (!strcmp(elf_name, "$a") ||
1087 !strcmp(elf_name, "$d") ||
1088 !strcmp(elf_name, "$t"))
1089 continue;
1090 }
1091
1092 if (opdsec && sym.st_shndx == opdidx) {
1093 u32 offset = sym.st_value - opdshdr.sh_addr;
1094 u64 *opd = opddata->d_buf + offset;
1095 sym.st_value = *opd;
1096 sym.st_shndx = elf_addr_to_index(elf, sym.st_value);
1097 }
1098
1016 sec = elf_getscn(elf, sym.st_shndx); 1099 sec = elf_getscn(elf, sym.st_shndx);
1017 if (!sec) 1100 if (!sec)
1018 goto out_elf_end; 1101 goto out_elf_end;
@@ -1086,7 +1169,8 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1086 if (demangled != NULL) 1169 if (demangled != NULL)
1087 elf_name = demangled; 1170 elf_name = demangled;
1088new_symbol: 1171new_symbol:
1089 f = symbol__new(sym.st_value, sym.st_size, elf_name); 1172 f = symbol__new(sym.st_value, sym.st_size,
1173 GELF_ST_BIND(sym.st_info), elf_name);
1090 free(demangled); 1174 free(demangled);
1091 if (!f) 1175 if (!f)
1092 goto out_elf_end; 1176 goto out_elf_end;
@@ -1151,37 +1235,26 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
1151 */ 1235 */
1152#define NOTE_ALIGN(n) (((n) + 3) & -4U) 1236#define NOTE_ALIGN(n) (((n) + 3) & -4U)
1153 1237
1154int filename__read_build_id(const char *filename, void *bf, size_t size) 1238static int elf_read_build_id(Elf *elf, void *bf, size_t size)
1155{ 1239{
1156 int fd, err = -1; 1240 int err = -1;
1157 GElf_Ehdr ehdr; 1241 GElf_Ehdr ehdr;
1158 GElf_Shdr shdr; 1242 GElf_Shdr shdr;
1159 Elf_Data *data; 1243 Elf_Data *data;
1160 Elf_Scn *sec; 1244 Elf_Scn *sec;
1161 Elf_Kind ek; 1245 Elf_Kind ek;
1162 void *ptr; 1246 void *ptr;
1163 Elf *elf;
1164 1247
1165 if (size < BUILD_ID_SIZE) 1248 if (size < BUILD_ID_SIZE)
1166 goto out; 1249 goto out;
1167 1250
1168 fd = open(filename, O_RDONLY);
1169 if (fd < 0)
1170 goto out;
1171
1172 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
1173 if (elf == NULL) {
1174 pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
1175 goto out_close;
1176 }
1177
1178 ek = elf_kind(elf); 1251 ek = elf_kind(elf);
1179 if (ek != ELF_K_ELF) 1252 if (ek != ELF_K_ELF)
1180 goto out_elf_end; 1253 goto out;
1181 1254
1182 if (gelf_getehdr(elf, &ehdr) == NULL) { 1255 if (gelf_getehdr(elf, &ehdr) == NULL) {
1183 pr_err("%s: cannot get elf header.\n", __func__); 1256 pr_err("%s: cannot get elf header.\n", __func__);
1184 goto out_elf_end; 1257 goto out;
1185 } 1258 }
1186 1259
1187 sec = elf_section_by_name(elf, &ehdr, &shdr, 1260 sec = elf_section_by_name(elf, &ehdr, &shdr,
@@ -1190,12 +1263,12 @@ int filename__read_build_id(const char *filename, void *bf, size_t size)
1190 sec = elf_section_by_name(elf, &ehdr, &shdr, 1263 sec = elf_section_by_name(elf, &ehdr, &shdr,
1191 ".notes", NULL); 1264 ".notes", NULL);
1192 if (sec == NULL) 1265 if (sec == NULL)
1193 goto out_elf_end; 1266 goto out;
1194 } 1267 }
1195 1268
1196 data = elf_getdata(sec, NULL); 1269 data = elf_getdata(sec, NULL);
1197 if (data == NULL) 1270 if (data == NULL)
1198 goto out_elf_end; 1271 goto out;
1199 1272
1200 ptr = data->d_buf; 1273 ptr = data->d_buf;
1201 while (ptr < (data->d_buf + data->d_size)) { 1274 while (ptr < (data->d_buf + data->d_size)) {
@@ -1217,7 +1290,31 @@ int filename__read_build_id(const char *filename, void *bf, size_t size)
1217 } 1290 }
1218 ptr += descsz; 1291 ptr += descsz;
1219 } 1292 }
1220out_elf_end: 1293
1294out:
1295 return err;
1296}
1297
1298int filename__read_build_id(const char *filename, void *bf, size_t size)
1299{
1300 int fd, err = -1;
1301 Elf *elf;
1302
1303 if (size < BUILD_ID_SIZE)
1304 goto out;
1305
1306 fd = open(filename, O_RDONLY);
1307 if (fd < 0)
1308 goto out;
1309
1310 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
1311 if (elf == NULL) {
1312 pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
1313 goto out_close;
1314 }
1315
1316 err = elf_read_build_id(elf, bf, size);
1317
1221 elf_end(elf); 1318 elf_end(elf);
1222out_close: 1319out_close:
1223 close(fd); 1320 close(fd);
@@ -1293,11 +1390,11 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
1293{ 1390{
1294 int size = PATH_MAX; 1391 int size = PATH_MAX;
1295 char *name; 1392 char *name;
1296 u8 build_id[BUILD_ID_SIZE];
1297 int ret = -1; 1393 int ret = -1;
1298 int fd; 1394 int fd;
1299 struct machine *machine; 1395 struct machine *machine;
1300 const char *root_dir; 1396 const char *root_dir;
1397 int want_symtab;
1301 1398
1302 dso__set_loaded(self, map->type); 1399 dso__set_loaded(self, map->type);
1303 1400
@@ -1324,13 +1421,18 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
1324 return ret; 1421 return ret;
1325 } 1422 }
1326 1423
1327 self->origin = DSO__ORIG_BUILD_ID_CACHE; 1424 /* Iterate over candidate debug images.
1328 if (dso__build_id_filename(self, name, size) != NULL) 1425 * On the first pass, only load images if they have a full symtab.
1329 goto open_file; 1426 * Failing that, do a second pass where we accept .dynsym also
1330more: 1427 */
1331 do { 1428 for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1;
1332 self->origin++; 1429 self->origin != DSO__ORIG_NOT_FOUND;
1430 self->origin++) {
1333 switch (self->origin) { 1431 switch (self->origin) {
1432 case DSO__ORIG_BUILD_ID_CACHE:
1433 if (dso__build_id_filename(self, name, size) == NULL)
1434 continue;
1435 break;
1334 case DSO__ORIG_FEDORA: 1436 case DSO__ORIG_FEDORA:
1335 snprintf(name, size, "/usr/lib/debug%s.debug", 1437 snprintf(name, size, "/usr/lib/debug%s.debug",
1336 self->long_name); 1438 self->long_name);
@@ -1339,21 +1441,20 @@ more:
1339 snprintf(name, size, "/usr/lib/debug%s", 1441 snprintf(name, size, "/usr/lib/debug%s",
1340 self->long_name); 1442 self->long_name);
1341 break; 1443 break;
1342 case DSO__ORIG_BUILDID: 1444 case DSO__ORIG_BUILDID: {
1343 if (filename__read_build_id(self->long_name, build_id, 1445 char build_id_hex[BUILD_ID_SIZE * 2 + 1];
1344 sizeof(build_id))) { 1446
1345 char build_id_hex[BUILD_ID_SIZE * 2 + 1]; 1447 if (!self->has_build_id)
1346 build_id__sprintf(build_id, sizeof(build_id), 1448 continue;
1347 build_id_hex); 1449
1348 snprintf(name, size, 1450 build_id__sprintf(self->build_id,
1349 "/usr/lib/debug/.build-id/%.2s/%s.debug", 1451 sizeof(self->build_id),
1350 build_id_hex, build_id_hex + 2); 1452 build_id_hex);
1351 if (self->has_build_id) 1453 snprintf(name, size,
1352 goto compare_build_id; 1454 "/usr/lib/debug/.build-id/%.2s/%s.debug",
1353 break; 1455 build_id_hex, build_id_hex + 2);
1354 } 1456 }
1355 self->origin++; 1457 break;
1356 /* Fall thru */
1357 case DSO__ORIG_DSO: 1458 case DSO__ORIG_DSO:
1358 snprintf(name, size, "%s", self->long_name); 1459 snprintf(name, size, "%s", self->long_name);
1359 break; 1460 break;
@@ -1366,36 +1467,41 @@ more:
1366 break; 1467 break;
1367 1468
1368 default: 1469 default:
1369 goto out; 1470 /*
1471 * If we wanted a full symtab but no image had one,
1472 * relax our requirements and repeat the search.
1473 */
1474 if (want_symtab) {
1475 want_symtab = 0;
1476 self->origin = DSO__ORIG_BUILD_ID_CACHE;
1477 } else
1478 continue;
1370 } 1479 }
1371 1480
1372 if (self->has_build_id) { 1481 /* Name is now the name of the next image to try */
1373 if (filename__read_build_id(name, build_id,
1374 sizeof(build_id)) < 0)
1375 goto more;
1376compare_build_id:
1377 if (!dso__build_id_equal(self, build_id))
1378 goto more;
1379 }
1380open_file:
1381 fd = open(name, O_RDONLY); 1482 fd = open(name, O_RDONLY);
1382 } while (fd < 0); 1483 if (fd < 0)
1484 continue;
1383 1485
1384 ret = dso__load_sym(self, map, name, fd, filter, 0); 1486 ret = dso__load_sym(self, map, name, fd, filter, 0,
1385 close(fd); 1487 want_symtab);
1488 close(fd);
1386 1489
1387 /* 1490 /*
1388 * Some people seem to have debuginfo files _WITHOUT_ debug info!?!? 1491 * Some people seem to have debuginfo files _WITHOUT_ debug
1389 */ 1492 * info!?!?
1390 if (!ret) 1493 */
1391 goto more; 1494 if (!ret)
1495 continue;
1392 1496
1393 if (ret > 0) { 1497 if (ret > 0) {
1394 int nr_plt = dso__synthesize_plt_symbols(self, map, filter); 1498 int nr_plt = dso__synthesize_plt_symbols(self, map, filter);
1395 if (nr_plt > 0) 1499 if (nr_plt > 0)
1396 ret += nr_plt; 1500 ret += nr_plt;
1501 break;
1502 }
1397 } 1503 }
1398out: 1504
1399 free(name); 1505 free(name);
1400 if (ret < 0 && strstr(self->name, " (deleted)") != NULL) 1506 if (ret < 0 && strstr(self->name, " (deleted)") != NULL)
1401 return 0; 1507 return 0;
@@ -1443,6 +1549,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self,
1443{ 1549{
1444 struct dirent *dent; 1550 struct dirent *dent;
1445 DIR *dir = opendir(dir_name); 1551 DIR *dir = opendir(dir_name);
1552 int ret = 0;
1446 1553
1447 if (!dir) { 1554 if (!dir) {
1448 pr_debug("%s: cannot open %s dir\n", __func__, dir_name); 1555 pr_debug("%s: cannot open %s dir\n", __func__, dir_name);
@@ -1465,8 +1572,9 @@ static int map_groups__set_modules_path_dir(struct map_groups *self,
1465 1572
1466 snprintf(path, sizeof(path), "%s/%s", 1573 snprintf(path, sizeof(path), "%s/%s",
1467 dir_name, dent->d_name); 1574 dir_name, dent->d_name);
1468 if (map_groups__set_modules_path_dir(self, path) < 0) 1575 ret = map_groups__set_modules_path_dir(self, path);
1469 goto failure; 1576 if (ret < 0)
1577 goto out;
1470 } else { 1578 } else {
1471 char *dot = strrchr(dent->d_name, '.'), 1579 char *dot = strrchr(dent->d_name, '.'),
1472 dso_name[PATH_MAX]; 1580 dso_name[PATH_MAX];
@@ -1487,17 +1595,19 @@ static int map_groups__set_modules_path_dir(struct map_groups *self,
1487 dir_name, dent->d_name); 1595 dir_name, dent->d_name);
1488 1596
1489 long_name = strdup(path); 1597 long_name = strdup(path);
1490 if (long_name == NULL) 1598 if (long_name == NULL) {
1491 goto failure; 1599 ret = -1;
1600 goto out;
1601 }
1492 dso__set_long_name(map->dso, long_name); 1602 dso__set_long_name(map->dso, long_name);
1603 map->dso->lname_alloc = 1;
1493 dso__kernel_module_get_build_id(map->dso, ""); 1604 dso__kernel_module_get_build_id(map->dso, "");
1494 } 1605 }
1495 } 1606 }
1496 1607
1497 return 0; 1608out:
1498failure:
1499 closedir(dir); 1609 closedir(dir);
1500 return -1; 1610 return ret;
1501} 1611}
1502 1612
1503static char *get_kernel_version(const char *root_dir) 1613static char *get_kernel_version(const char *root_dir)
@@ -1653,36 +1763,12 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
1653{ 1763{
1654 int err = -1, fd; 1764 int err = -1, fd;
1655 1765
1656 if (self->has_build_id) {
1657 u8 build_id[BUILD_ID_SIZE];
1658
1659 if (filename__read_build_id(vmlinux, build_id,
1660 sizeof(build_id)) < 0) {
1661 pr_debug("No build_id in %s, ignoring it\n", vmlinux);
1662 return -1;
1663 }
1664 if (!dso__build_id_equal(self, build_id)) {
1665 char expected_build_id[BUILD_ID_SIZE * 2 + 1],
1666 vmlinux_build_id[BUILD_ID_SIZE * 2 + 1];
1667
1668 build_id__sprintf(self->build_id,
1669 sizeof(self->build_id),
1670 expected_build_id);
1671 build_id__sprintf(build_id, sizeof(build_id),
1672 vmlinux_build_id);
1673 pr_debug("build_id in %s is %s while expected is %s, "
1674 "ignoring it\n", vmlinux, vmlinux_build_id,
1675 expected_build_id);
1676 return -1;
1677 }
1678 }
1679
1680 fd = open(vmlinux, O_RDONLY); 1766 fd = open(vmlinux, O_RDONLY);
1681 if (fd < 0) 1767 if (fd < 0)
1682 return -1; 1768 return -1;
1683 1769
1684 dso__set_loaded(self, map->type); 1770 dso__set_loaded(self, map->type);
1685 err = dso__load_sym(self, map, vmlinux, fd, filter, 0); 1771 err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
1686 close(fd); 1772 close(fd);
1687 1773
1688 if (err > 0) 1774 if (err > 0)
@@ -1745,7 +1831,12 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
1745 if (symbol_conf.vmlinux_name != NULL) { 1831 if (symbol_conf.vmlinux_name != NULL) {
1746 err = dso__load_vmlinux(self, map, 1832 err = dso__load_vmlinux(self, map,
1747 symbol_conf.vmlinux_name, filter); 1833 symbol_conf.vmlinux_name, filter);
1748 goto out_try_fixup; 1834 if (err > 0) {
1835 dso__set_long_name(self,
1836 strdup(symbol_conf.vmlinux_name));
1837 goto out_fixup;
1838 }
1839 return err;
1749 } 1840 }
1750 1841
1751 if (vmlinux_path != NULL) { 1842 if (vmlinux_path != NULL) {
@@ -1806,7 +1897,6 @@ do_kallsyms:
1806 pr_debug("Using %s for symbols\n", kallsyms_filename); 1897 pr_debug("Using %s for symbols\n", kallsyms_filename);
1807 free(kallsyms_allocated_filename); 1898 free(kallsyms_allocated_filename);
1808 1899
1809out_try_fixup:
1810 if (err > 0) { 1900 if (err > 0) {
1811out_fixup: 1901out_fixup:
1812 if (kallsyms_filename != NULL) 1902 if (kallsyms_filename != NULL)
@@ -2041,6 +2131,36 @@ int __machine__create_kernel_maps(struct machine *self, struct dso *kernel)
2041 return 0; 2131 return 0;
2042} 2132}
2043 2133
2134void machine__destroy_kernel_maps(struct machine *self)
2135{
2136 enum map_type type;
2137
2138 for (type = 0; type < MAP__NR_TYPES; ++type) {
2139 struct kmap *kmap;
2140
2141 if (self->vmlinux_maps[type] == NULL)
2142 continue;
2143
2144 kmap = map__kmap(self->vmlinux_maps[type]);
2145 map_groups__remove(&self->kmaps, self->vmlinux_maps[type]);
2146 if (kmap->ref_reloc_sym) {
2147 /*
2148 * ref_reloc_sym is shared among all maps, so free just
2149 * on one of them.
2150 */
2151 if (type == MAP__FUNCTION) {
2152 free((char *)kmap->ref_reloc_sym->name);
2153 kmap->ref_reloc_sym->name = NULL;
2154 free(kmap->ref_reloc_sym);
2155 }
2156 kmap->ref_reloc_sym = NULL;
2157 }
2158
2159 map__delete(self->vmlinux_maps[type]);
2160 self->vmlinux_maps[type] = NULL;
2161 }
2162}
2163
2044int machine__create_kernel_maps(struct machine *self) 2164int machine__create_kernel_maps(struct machine *self)
2045{ 2165{
2046 struct dso *kernel = machine__create_kernel(self); 2166 struct dso *kernel = machine__create_kernel(self);
@@ -2182,6 +2302,15 @@ out_free_comm_list:
2182 return -1; 2302 return -1;
2183} 2303}
2184 2304
2305void symbol__exit(void)
2306{
2307 strlist__delete(symbol_conf.sym_list);
2308 strlist__delete(symbol_conf.dso_list);
2309 strlist__delete(symbol_conf.comm_list);
2310 vmlinux_path__exit();
2311 symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
2312}
2313
2185int machines__create_kernel_maps(struct rb_root *self, pid_t pid) 2314int machines__create_kernel_maps(struct rb_root *self, pid_t pid)
2186{ 2315{
2187 struct machine *machine = machines__findnew(self, pid); 2316 struct machine *machine = machines__findnew(self, pid);
@@ -2276,6 +2405,19 @@ failure:
2276 return ret; 2405 return ret;
2277} 2406}
2278 2407
2408void machines__destroy_guest_kernel_maps(struct rb_root *self)
2409{
2410 struct rb_node *next = rb_first(self);
2411
2412 while (next) {
2413 struct machine *pos = rb_entry(next, struct machine, rb_node);
2414
2415 next = rb_next(&pos->rb_node);
2416 rb_erase(&pos->rb_node, self);
2417 machine__delete(pos);
2418 }
2419}
2420
2279int machine__load_kallsyms(struct machine *self, const char *filename, 2421int machine__load_kallsyms(struct machine *self, const char *filename,
2280 enum map_type type, symbol_filter_t filter) 2422 enum map_type type, symbol_filter_t filter)
2281{ 2423{
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 5e02d2c17154..b7a8da4af5a0 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -9,8 +9,6 @@
9#include <linux/rbtree.h> 9#include <linux/rbtree.h>
10#include <stdio.h> 10#include <stdio.h>
11 11
12#define DEBUG_CACHE_DIR ".debug"
13
14#ifdef HAVE_CPLUS_DEMANGLE 12#ifdef HAVE_CPLUS_DEMANGLE
15extern char *cplus_demangle(const char *, int); 13extern char *cplus_demangle(const char *, int);
16 14
@@ -55,6 +53,7 @@ struct symbol {
55 u64 start; 53 u64 start;
56 u64 end; 54 u64 end;
57 u16 namelen; 55 u16 namelen;
56 u8 binding;
58 char name[0]; 57 char name[0];
59}; 58};
60 59
@@ -70,9 +69,9 @@ struct symbol_conf {
70 show_nr_samples, 69 show_nr_samples,
71 use_callchain, 70 use_callchain,
72 exclude_other, 71 exclude_other,
73 full_paths,
74 show_cpu_utilization; 72 show_cpu_utilization;
75 const char *vmlinux_name, 73 const char *vmlinux_name,
74 *source_prefix,
76 *field_sep; 75 *field_sep;
77 const char *default_guest_vmlinux_name, 76 const char *default_guest_vmlinux_name,
78 *default_guest_kallsyms, 77 *default_guest_kallsyms,
@@ -103,6 +102,8 @@ struct ref_reloc_sym {
103struct map_symbol { 102struct map_symbol {
104 struct map *map; 103 struct map *map;
105 struct symbol *sym; 104 struct symbol *sym;
105 bool unfolded;
106 bool has_children;
106}; 107};
107 108
108struct addr_location { 109struct addr_location {
@@ -112,7 +113,8 @@ struct addr_location {
112 u64 addr; 113 u64 addr;
113 char level; 114 char level;
114 bool filtered; 115 bool filtered;
115 unsigned int cpumode; 116 u8 cpumode;
117 s32 cpu;
116}; 118};
117 119
118enum dso_kernel_type { 120enum dso_kernel_type {
@@ -125,12 +127,14 @@ struct dso {
125 struct list_head node; 127 struct list_head node;
126 struct rb_root symbols[MAP__NR_TYPES]; 128 struct rb_root symbols[MAP__NR_TYPES];
127 struct rb_root symbol_names[MAP__NR_TYPES]; 129 struct rb_root symbol_names[MAP__NR_TYPES];
130 enum dso_kernel_type kernel;
128 u8 adjust_symbols:1; 131 u8 adjust_symbols:1;
129 u8 slen_calculated:1; 132 u8 slen_calculated:1;
130 u8 has_build_id:1; 133 u8 has_build_id:1;
131 enum dso_kernel_type kernel;
132 u8 hit:1; 134 u8 hit:1;
133 u8 annotate_warned:1; 135 u8 annotate_warned:1;
136 u8 sname_alloc:1;
137 u8 lname_alloc:1;
134 unsigned char origin; 138 unsigned char origin;
135 u8 sorted_by_name; 139 u8 sorted_by_name;
136 u8 loaded; 140 u8 loaded;
@@ -146,6 +150,8 @@ struct dso *dso__new(const char *name);
146struct dso *dso__new_kernel(const char *name); 150struct dso *dso__new_kernel(const char *name);
147void dso__delete(struct dso *self); 151void dso__delete(struct dso *self);
148 152
153int dso__name_len(const struct dso *self);
154
149bool dso__loaded(const struct dso *self, enum map_type type); 155bool dso__loaded(const struct dso *self, enum map_type type);
150bool dso__sorted_by_name(const struct dso *self, enum map_type type); 156bool dso__sorted_by_name(const struct dso *self, enum map_type type);
151 157
@@ -207,13 +213,16 @@ int kallsyms__parse(const char *filename, void *arg,
207 int (*process_symbol)(void *arg, const char *name, 213 int (*process_symbol)(void *arg, const char *name,
208 char type, u64 start)); 214 char type, u64 start));
209 215
216void machine__destroy_kernel_maps(struct machine *self);
210int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); 217int __machine__create_kernel_maps(struct machine *self, struct dso *kernel);
211int machine__create_kernel_maps(struct machine *self); 218int machine__create_kernel_maps(struct machine *self);
212 219
213int machines__create_kernel_maps(struct rb_root *self, pid_t pid); 220int machines__create_kernel_maps(struct rb_root *self, pid_t pid);
214int machines__create_guest_kernel_maps(struct rb_root *self); 221int machines__create_guest_kernel_maps(struct rb_root *self);
222void machines__destroy_guest_kernel_maps(struct rb_root *self);
215 223
216int symbol__init(void); 224int symbol__init(void);
225void symbol__exit(void);
217bool symbol_type__is_a(char symbol_type, enum map_type map_type); 226bool symbol_type__is_a(char symbol_type, enum map_type map_type);
218 227
219size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp); 228size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp);
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 1f7ecd47f499..8c72d888e449 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -7,6 +7,15 @@
7#include "util.h" 7#include "util.h"
8#include "debug.h" 8#include "debug.h"
9 9
10/* Skip "." and ".." directories */
11static int filter(const struct dirent *dir)
12{
13 if (dir->d_name[0] == '.')
14 return 0;
15 else
16 return 1;
17}
18
10int find_all_tid(int pid, pid_t ** all_tid) 19int find_all_tid(int pid, pid_t ** all_tid)
11{ 20{
12 char name[256]; 21 char name[256];
@@ -16,7 +25,7 @@ int find_all_tid(int pid, pid_t ** all_tid)
16 int i; 25 int i;
17 26
18 sprintf(name, "/proc/%d/task", pid); 27 sprintf(name, "/proc/%d/task", pid);
19 items = scandir(name, &namelist, NULL, NULL); 28 items = scandir(name, &namelist, filter, NULL);
20 if (items <= 0) 29 if (items <= 0)
21 return -ENOENT; 30 return -ENOENT;
22 *all_tid = malloc(sizeof(pid_t) * items); 31 *all_tid = malloc(sizeof(pid_t) * items);
@@ -53,6 +62,13 @@ static struct thread *thread__new(pid_t pid)
53 return self; 62 return self;
54} 63}
55 64
65void thread__delete(struct thread *self)
66{
67 map_groups__exit(&self->mg);
68 free(self->comm);
69 free(self);
70}
71
56int thread__set_comm(struct thread *self, const char *comm) 72int thread__set_comm(struct thread *self, const char *comm)
57{ 73{
58 int err; 74 int err;
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 1dfd9ff8bdcd..688500ff826f 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -6,7 +6,10 @@
6#include "symbol.h" 6#include "symbol.h"
7 7
8struct thread { 8struct thread {
9 struct rb_node rb_node; 9 union {
10 struct rb_node rb_node;
11 struct list_head node;
12 };
10 struct map_groups mg; 13 struct map_groups mg;
11 pid_t pid; 14 pid_t pid;
12 char shortname[3]; 15 char shortname[3];
@@ -17,6 +20,8 @@ struct thread {
17 20
18struct perf_session; 21struct perf_session;
19 22
23void thread__delete(struct thread *self);
24
20int find_all_tid(int pid, pid_t ** all_tid); 25int find_all_tid(int pid, pid_t ** all_tid);
21int thread__set_comm(struct thread *self, const char *comm); 26int thread__set_comm(struct thread *self, const char *comm);
22int thread__comm_len(struct thread *self); 27int thread__comm_len(struct thread *self);
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
new file mode 100644
index 000000000000..66f2d583d8c4
--- /dev/null
+++ b/tools/perf/util/ui/browser.c
@@ -0,0 +1,329 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4/*
5 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
6 * the build if it isn't defined. Use the equivalent one that glibc
7 * has on features.h.
8 */
9#include <features.h>
10#ifndef HAVE_LONG_LONG
11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
12#endif
13#include <slang.h>
14#include <linux/list.h>
15#include <linux/rbtree.h>
16#include <stdlib.h>
17#include <sys/ttydefaults.h>
18#include "browser.h"
19#include "helpline.h"
20#include "../color.h"
21#include "../util.h"
22
23#if SLANG_VERSION < 20104
24#define sltt_set_color(obj, name, fg, bg) \
25 SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
26#else
27#define sltt_set_color SLtt_set_color
28#endif
29
30newtComponent newt_form__new(void);
31
32int ui_browser__percent_color(double percent, bool current)
33{
34 if (current)
35 return HE_COLORSET_SELECTED;
36 if (percent >= MIN_RED)
37 return HE_COLORSET_TOP;
38 if (percent >= MIN_GREEN)
39 return HE_COLORSET_MEDIUM;
40 return HE_COLORSET_NORMAL;
41}
42
43void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
44{
45 struct list_head *head = self->entries;
46 struct list_head *pos;
47
48 switch (whence) {
49 case SEEK_SET:
50 pos = head->next;
51 break;
52 case SEEK_CUR:
53 pos = self->top;
54 break;
55 case SEEK_END:
56 pos = head->prev;
57 break;
58 default:
59 return;
60 }
61
62 if (offset > 0) {
63 while (offset-- != 0)
64 pos = pos->next;
65 } else {
66 while (offset++ != 0)
67 pos = pos->prev;
68 }
69
70 self->top = pos;
71}
72
73void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
74{
75 struct rb_root *root = self->entries;
76 struct rb_node *nd;
77
78 switch (whence) {
79 case SEEK_SET:
80 nd = rb_first(root);
81 break;
82 case SEEK_CUR:
83 nd = self->top;
84 break;
85 case SEEK_END:
86 nd = rb_last(root);
87 break;
88 default:
89 return;
90 }
91
92 if (offset > 0) {
93 while (offset-- != 0)
94 nd = rb_next(nd);
95 } else {
96 while (offset++ != 0)
97 nd = rb_prev(nd);
98 }
99
100 self->top = nd;
101}
102
103unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
104{
105 struct rb_node *nd;
106 int row = 0;
107
108 if (self->top == NULL)
109 self->top = rb_first(self->entries);
110
111 nd = self->top;
112
113 while (nd != NULL) {
114 SLsmg_gotorc(self->y + row, self->x);
115 self->write(self, nd, row);
116 if (++row == self->height)
117 break;
118 nd = rb_next(nd);
119 }
120
121 return row;
122}
123
124bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
125{
126 return self->top_idx + row == self->index;
127}
128
129void ui_browser__refresh_dimensions(struct ui_browser *self)
130{
131 int cols, rows;
132 newtGetScreenSize(&cols, &rows);
133
134 if (self->width > cols - 4)
135 self->width = cols - 4;
136 self->height = rows - 5;
137 if (self->height > self->nr_entries)
138 self->height = self->nr_entries;
139 self->y = (rows - self->height) / 2;
140 self->x = (cols - self->width) / 2;
141}
142
143void ui_browser__reset_index(struct ui_browser *self)
144{
145 self->index = self->top_idx = 0;
146 self->seek(self, 0, SEEK_SET);
147}
148
149int ui_browser__show(struct ui_browser *self, const char *title,
150 const char *helpline, ...)
151{
152 va_list ap;
153
154 if (self->form != NULL) {
155 newtFormDestroy(self->form);
156 newtPopWindow();
157 }
158 ui_browser__refresh_dimensions(self);
159 newtCenteredWindow(self->width, self->height, title);
160 self->form = newt_form__new();
161 if (self->form == NULL)
162 return -1;
163
164 self->sb = newtVerticalScrollbar(self->width, 0, self->height,
165 HE_COLORSET_NORMAL,
166 HE_COLORSET_SELECTED);
167 if (self->sb == NULL)
168 return -1;
169
170 newtFormAddHotKey(self->form, NEWT_KEY_UP);
171 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
172 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
173 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
174 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
175 newtFormAddHotKey(self->form, NEWT_KEY_END);
176 newtFormAddHotKey(self->form, ' ');
177 newtFormAddComponent(self->form, self->sb);
178
179 va_start(ap, helpline);
180 ui_helpline__vpush(helpline, ap);
181 va_end(ap);
182 return 0;
183}
184
185void ui_browser__hide(struct ui_browser *self)
186{
187 newtFormDestroy(self->form);
188 newtPopWindow();
189 self->form = NULL;
190 ui_helpline__pop();
191}
192
193int ui_browser__refresh(struct ui_browser *self)
194{
195 int row;
196
197 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
198 row = self->refresh(self);
199 SLsmg_set_color(HE_COLORSET_NORMAL);
200 SLsmg_fill_region(self->y + row, self->x,
201 self->height - row, self->width, ' ');
202
203 return 0;
204}
205
206int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
207{
208 if (ui_browser__refresh(self) < 0)
209 return -1;
210
211 while (1) {
212 off_t offset;
213
214 newtFormRun(self->form, es);
215
216 if (es->reason != NEWT_EXIT_HOTKEY)
217 break;
218 if (is_exit_key(es->u.key))
219 return es->u.key;
220 switch (es->u.key) {
221 case NEWT_KEY_DOWN:
222 if (self->index == self->nr_entries - 1)
223 break;
224 ++self->index;
225 if (self->index == self->top_idx + self->height) {
226 ++self->top_idx;
227 self->seek(self, +1, SEEK_CUR);
228 }
229 break;
230 case NEWT_KEY_UP:
231 if (self->index == 0)
232 break;
233 --self->index;
234 if (self->index < self->top_idx) {
235 --self->top_idx;
236 self->seek(self, -1, SEEK_CUR);
237 }
238 break;
239 case NEWT_KEY_PGDN:
240 case ' ':
241 if (self->top_idx + self->height > self->nr_entries - 1)
242 break;
243
244 offset = self->height;
245 if (self->index + offset > self->nr_entries - 1)
246 offset = self->nr_entries - 1 - self->index;
247 self->index += offset;
248 self->top_idx += offset;
249 self->seek(self, +offset, SEEK_CUR);
250 break;
251 case NEWT_KEY_PGUP:
252 if (self->top_idx == 0)
253 break;
254
255 if (self->top_idx < self->height)
256 offset = self->top_idx;
257 else
258 offset = self->height;
259
260 self->index -= offset;
261 self->top_idx -= offset;
262 self->seek(self, -offset, SEEK_CUR);
263 break;
264 case NEWT_KEY_HOME:
265 ui_browser__reset_index(self);
266 break;
267 case NEWT_KEY_END:
268 offset = self->height - 1;
269 if (offset >= self->nr_entries)
270 offset = self->nr_entries - 1;
271
272 self->index = self->nr_entries - 1;
273 self->top_idx = self->index - offset;
274 self->seek(self, -offset, SEEK_END);
275 break;
276 default:
277 return es->u.key;
278 }
279 if (ui_browser__refresh(self) < 0)
280 return -1;
281 }
282 return 0;
283}
284
285unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
286{
287 struct list_head *pos;
288 struct list_head *head = self->entries;
289 int row = 0;
290
291 if (self->top == NULL || self->top == self->entries)
292 self->top = head->next;
293
294 pos = self->top;
295
296 list_for_each_from(pos, head) {
297 SLsmg_gotorc(self->y + row, self->x);
298 self->write(self, pos, row);
299 if (++row == self->height)
300 break;
301 }
302
303 return row;
304}
305
306static struct newtPercentTreeColors {
307 const char *topColorFg, *topColorBg;
308 const char *mediumColorFg, *mediumColorBg;
309 const char *normalColorFg, *normalColorBg;
310 const char *selColorFg, *selColorBg;
311 const char *codeColorFg, *codeColorBg;
312} defaultPercentTreeColors = {
313 "red", "lightgray",
314 "green", "lightgray",
315 "black", "lightgray",
316 "lightgray", "magenta",
317 "blue", "lightgray",
318};
319
320void ui_browser__init(void)
321{
322 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
323
324 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
325 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
326 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
327 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
328 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
329}
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h
new file mode 100644
index 000000000000..0b9f829214f7
--- /dev/null
+++ b/tools/perf/util/ui/browser.h
@@ -0,0 +1,46 @@
1#ifndef _PERF_UI_BROWSER_H_
2#define _PERF_UI_BROWSER_H_ 1
3
4#include <stdbool.h>
5#include <newt.h>
6#include <sys/types.h>
7#include "../types.h"
8
9#define HE_COLORSET_TOP 50
10#define HE_COLORSET_MEDIUM 51
11#define HE_COLORSET_NORMAL 52
12#define HE_COLORSET_SELECTED 53
13#define HE_COLORSET_CODE 54
14
15struct ui_browser {
16 newtComponent form, sb;
17 u64 index, top_idx;
18 void *top, *entries;
19 u16 y, x, width, height;
20 void *priv;
21 unsigned int (*refresh)(struct ui_browser *self);
22 void (*write)(struct ui_browser *self, void *entry, int row);
23 void (*seek)(struct ui_browser *self, off_t offset, int whence);
24 u32 nr_entries;
25};
26
27
28int ui_browser__percent_color(double percent, bool current);
29bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
30void ui_browser__refresh_dimensions(struct ui_browser *self);
31void ui_browser__reset_index(struct ui_browser *self);
32
33int ui_browser__show(struct ui_browser *self, const char *title,
34 const char *helpline, ...);
35void ui_browser__hide(struct ui_browser *self);
36int ui_browser__refresh(struct ui_browser *self);
37int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es);
38
39void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
40unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
41
42void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence);
43unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
44
45void ui_browser__init(void);
46#endif /* _PERF_UI_BROWSER_H_ */
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
new file mode 100644
index 000000000000..55ff792459ac
--- /dev/null
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -0,0 +1,240 @@
1#include "../browser.h"
2#include "../helpline.h"
3#include "../libslang.h"
4#include "../../hist.h"
5#include "../../sort.h"
6#include "../../symbol.h"
7
8static void ui__error_window(const char *fmt, ...)
9{
10 va_list ap;
11
12 va_start(ap, fmt);
13 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
14 va_end(ap);
15}
16
17struct annotate_browser {
18 struct ui_browser b;
19 struct rb_root entries;
20 struct rb_node *curr_hot;
21};
22
23struct objdump_line_rb_node {
24 struct rb_node rb_node;
25 double percent;
26 u32 idx;
27};
28
29static inline
30struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
31{
32 return (struct objdump_line_rb_node *)(self + 1);
33}
34
35static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
36{
37 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
38 bool current_entry = ui_browser__is_current_entry(self, row);
39 int width = self->width;
40
41 if (ol->offset != -1) {
42 struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
43 int color = ui_browser__percent_color(olrb->percent, current_entry);
44 SLsmg_set_color(color);
45 slsmg_printf(" %7.2f ", olrb->percent);
46 if (!current_entry)
47 SLsmg_set_color(HE_COLORSET_CODE);
48 } else {
49 int color = ui_browser__percent_color(0, current_entry);
50 SLsmg_set_color(color);
51 slsmg_write_nstring(" ", 9);
52 }
53
54 SLsmg_write_char(':');
55 slsmg_write_nstring(" ", 8);
56 if (!*ol->line)
57 slsmg_write_nstring(" ", width - 18);
58 else
59 slsmg_write_nstring(ol->line, width - 18);
60}
61
62static double objdump_line__calc_percent(struct objdump_line *self,
63 struct list_head *head,
64 struct symbol *sym)
65{
66 double percent = 0.0;
67
68 if (self->offset != -1) {
69 int len = sym->end - sym->start;
70 unsigned int hits = 0;
71 struct sym_priv *priv = symbol__priv(sym);
72 struct sym_ext *sym_ext = priv->ext;
73 struct sym_hist *h = priv->hist;
74 s64 offset = self->offset;
75 struct objdump_line *next = objdump__get_next_ip_line(head, self);
76
77
78 while (offset < (s64)len &&
79 (next == NULL || offset < next->offset)) {
80 if (sym_ext) {
81 percent += sym_ext[offset].percent;
82 } else
83 hits += h->ip[offset];
84
85 ++offset;
86 }
87
88 if (sym_ext == NULL && h->sum)
89 percent = 100.0 * hits / h->sum;
90 }
91
92 return percent;
93}
94
95static void objdump__insert_line(struct rb_root *self,
96 struct objdump_line_rb_node *line)
97{
98 struct rb_node **p = &self->rb_node;
99 struct rb_node *parent = NULL;
100 struct objdump_line_rb_node *l;
101
102 while (*p != NULL) {
103 parent = *p;
104 l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
105 if (line->percent < l->percent)
106 p = &(*p)->rb_left;
107 else
108 p = &(*p)->rb_right;
109 }
110 rb_link_node(&line->rb_node, parent, p);
111 rb_insert_color(&line->rb_node, self);
112}
113
114static void annotate_browser__set_top(struct annotate_browser *self,
115 struct rb_node *nd)
116{
117 struct objdump_line_rb_node *rbpos;
118 struct objdump_line *pos;
119 unsigned back;
120
121 ui_browser__refresh_dimensions(&self->b);
122 back = self->b.height / 2;
123 rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
124 pos = ((struct objdump_line *)rbpos) - 1;
125 self->b.top_idx = self->b.index = rbpos->idx;
126
127 while (self->b.top_idx != 0 && back != 0) {
128 pos = list_entry(pos->node.prev, struct objdump_line, node);
129
130 --self->b.top_idx;
131 --back;
132 }
133
134 self->b.top = pos;
135 self->curr_hot = nd;
136}
137
138static int annotate_browser__run(struct annotate_browser *self,
139 struct newtExitStruct *es)
140{
141 struct rb_node *nd;
142 struct hist_entry *he = self->b.priv;
143
144 if (ui_browser__show(&self->b, he->ms.sym->name,
145 "<- or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
146 return -1;
147
148 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
149
150 nd = self->curr_hot;
151 if (nd) {
152 newtFormAddHotKey(self->b.form, NEWT_KEY_TAB);
153 newtFormAddHotKey(self->b.form, NEWT_KEY_UNTAB);
154 }
155
156 while (1) {
157 ui_browser__run(&self->b, es);
158
159 if (es->reason != NEWT_EXIT_HOTKEY)
160 break;
161
162 switch (es->u.key) {
163 case NEWT_KEY_TAB:
164 nd = rb_prev(nd);
165 if (nd == NULL)
166 nd = rb_last(&self->entries);
167 annotate_browser__set_top(self, nd);
168 break;
169 case NEWT_KEY_UNTAB:
170 nd = rb_next(nd);
171 if (nd == NULL)
172 nd = rb_first(&self->entries);
173 annotate_browser__set_top(self, nd);
174 break;
175 default:
176 goto out;
177 }
178 }
179out:
180 ui_browser__hide(&self->b);
181 return 0;
182}
183
184int hist_entry__tui_annotate(struct hist_entry *self)
185{
186 struct newtExitStruct es;
187 struct objdump_line *pos, *n;
188 struct objdump_line_rb_node *rbpos;
189 LIST_HEAD(head);
190 struct annotate_browser browser = {
191 .b = {
192 .entries = &head,
193 .refresh = ui_browser__list_head_refresh,
194 .seek = ui_browser__list_head_seek,
195 .write = annotate_browser__write,
196 .priv = self,
197 },
198 };
199 int ret;
200
201 if (self->ms.sym == NULL)
202 return -1;
203
204 if (self->ms.map->dso->annotate_warned)
205 return -1;
206
207 if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) {
208 ui__error_window(ui_helpline__last_msg);
209 return -1;
210 }
211
212 ui_helpline__push("Press <- or ESC to exit");
213
214 list_for_each_entry(pos, &head, node) {
215 size_t line_len = strlen(pos->line);
216 if (browser.b.width < line_len)
217 browser.b.width = line_len;
218 rbpos = objdump_line__rb(pos);
219 rbpos->idx = browser.b.nr_entries++;
220 rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym);
221 if (rbpos->percent < 0.01)
222 continue;
223 objdump__insert_line(&browser.entries, rbpos);
224 }
225
226 /*
227 * Position the browser at the hottest line.
228 */
229 browser.curr_hot = rb_last(&browser.entries);
230 if (browser.curr_hot)
231 annotate_browser__set_top(&browser, browser.curr_hot);
232
233 browser.b.width += 18; /* Percentage */
234 ret = annotate_browser__run(&browser, &es);
235 list_for_each_entry_safe(pos, n, &head, node) {
236 list_del(&pos->node);
237 objdump_line__free(pos);
238 }
239 return ret;
240}
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
new file mode 100644
index 000000000000..dafdf6775d77
--- /dev/null
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -0,0 +1,948 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4#include "../libslang.h"
5#include <stdlib.h>
6#include <string.h>
7#include <newt.h>
8#include <linux/rbtree.h>
9
10#include "../../hist.h"
11#include "../../pstack.h"
12#include "../../sort.h"
13#include "../../util.h"
14
15#include "../browser.h"
16#include "../helpline.h"
17#include "../util.h"
18#include "map.h"
19
20struct hist_browser {
21 struct ui_browser b;
22 struct hists *hists;
23 struct hist_entry *he_selection;
24 struct map_symbol *selection;
25};
26
27static void hist_browser__refresh_dimensions(struct hist_browser *self)
28{
29 /* 3 == +/- toggle symbol before actual hist_entry rendering */
30 self->b.width = 3 + (hists__sort_list_width(self->hists) +
31 sizeof("[k]"));
32}
33
34static void hist_browser__reset(struct hist_browser *self)
35{
36 self->b.nr_entries = self->hists->nr_entries;
37 hist_browser__refresh_dimensions(self);
38 ui_browser__reset_index(&self->b);
39}
40
41static char tree__folded_sign(bool unfolded)
42{
43 return unfolded ? '-' : '+';
44}
45
46static char map_symbol__folded(const struct map_symbol *self)
47{
48 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
49}
50
51static char hist_entry__folded(const struct hist_entry *self)
52{
53 return map_symbol__folded(&self->ms);
54}
55
56static char callchain_list__folded(const struct callchain_list *self)
57{
58 return map_symbol__folded(&self->ms);
59}
60
61static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
62{
63 int n = 0;
64 struct rb_node *nd;
65
66 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
67 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
68 struct callchain_list *chain;
69 char folded_sign = ' '; /* No children */
70
71 list_for_each_entry(chain, &child->val, list) {
72 ++n;
73 /* We need this because we may not have children */
74 folded_sign = callchain_list__folded(chain);
75 if (folded_sign == '+')
76 break;
77 }
78
79 if (folded_sign == '-') /* Have children and they're unfolded */
80 n += callchain_node__count_rows_rb_tree(child);
81 }
82
83 return n;
84}
85
86static int callchain_node__count_rows(struct callchain_node *node)
87{
88 struct callchain_list *chain;
89 bool unfolded = false;
90 int n = 0;
91
92 list_for_each_entry(chain, &node->val, list) {
93 ++n;
94 unfolded = chain->ms.unfolded;
95 }
96
97 if (unfolded)
98 n += callchain_node__count_rows_rb_tree(node);
99
100 return n;
101}
102
103static int callchain__count_rows(struct rb_root *chain)
104{
105 struct rb_node *nd;
106 int n = 0;
107
108 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
109 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
110 n += callchain_node__count_rows(node);
111 }
112
113 return n;
114}
115
116static bool map_symbol__toggle_fold(struct map_symbol *self)
117{
118 if (!self->has_children)
119 return false;
120
121 self->unfolded = !self->unfolded;
122 return true;
123}
124
125static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
126{
127 struct rb_node *nd = rb_first(&self->rb_root);
128
129 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
130 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
131 struct callchain_list *chain;
132 int first = true;
133
134 list_for_each_entry(chain, &child->val, list) {
135 if (first) {
136 first = false;
137 chain->ms.has_children = chain->list.next != &child->val ||
138 rb_first(&child->rb_root) != NULL;
139 } else
140 chain->ms.has_children = chain->list.next == &child->val &&
141 rb_first(&child->rb_root) != NULL;
142 }
143
144 callchain_node__init_have_children_rb_tree(child);
145 }
146}
147
148static void callchain_node__init_have_children(struct callchain_node *self)
149{
150 struct callchain_list *chain;
151
152 list_for_each_entry(chain, &self->val, list)
153 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
154
155 callchain_node__init_have_children_rb_tree(self);
156}
157
158static void callchain__init_have_children(struct rb_root *self)
159{
160 struct rb_node *nd;
161
162 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
163 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
164 callchain_node__init_have_children(node);
165 }
166}
167
168static void hist_entry__init_have_children(struct hist_entry *self)
169{
170 if (!self->init_have_children) {
171 callchain__init_have_children(&self->sorted_chain);
172 self->init_have_children = true;
173 }
174}
175
176static bool hist_browser__toggle_fold(struct hist_browser *self)
177{
178 if (map_symbol__toggle_fold(self->selection)) {
179 struct hist_entry *he = self->he_selection;
180
181 hist_entry__init_have_children(he);
182 self->hists->nr_entries -= he->nr_rows;
183
184 if (he->ms.unfolded)
185 he->nr_rows = callchain__count_rows(&he->sorted_chain);
186 else
187 he->nr_rows = 0;
188 self->hists->nr_entries += he->nr_rows;
189 self->b.nr_entries = self->hists->nr_entries;
190
191 return true;
192 }
193
194 /* If it doesn't have children, no toggling performed */
195 return false;
196}
197
198static int hist_browser__run(struct hist_browser *self, const char *title,
199 struct newtExitStruct *es)
200{
201 char str[256], unit;
202 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
203
204 self->b.entries = &self->hists->entries;
205 self->b.nr_entries = self->hists->nr_entries;
206
207 hist_browser__refresh_dimensions(self);
208
209 nr_events = convert_unit(nr_events, &unit);
210 snprintf(str, sizeof(str), "Events: %lu%c ",
211 nr_events, unit);
212 newtDrawRootText(0, 0, str);
213
214 if (ui_browser__show(&self->b, title,
215 "Press '?' for help on key bindings") < 0)
216 return -1;
217
218 newtFormAddHotKey(self->b.form, 'a');
219 newtFormAddHotKey(self->b.form, '?');
220 newtFormAddHotKey(self->b.form, 'h');
221 newtFormAddHotKey(self->b.form, 'd');
222 newtFormAddHotKey(self->b.form, 'D');
223 newtFormAddHotKey(self->b.form, 't');
224
225 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
226 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
227 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
228
229 while (1) {
230 ui_browser__run(&self->b, es);
231
232 if (es->reason != NEWT_EXIT_HOTKEY)
233 break;
234 switch (es->u.key) {
235 case 'D': { /* Debug */
236 static int seq;
237 struct hist_entry *h = rb_entry(self->b.top,
238 struct hist_entry, rb_node);
239 ui_helpline__pop();
240 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
241 seq++, self->b.nr_entries,
242 self->hists->nr_entries,
243 self->b.height,
244 self->b.index,
245 self->b.top_idx,
246 h->row_offset, h->nr_rows);
247 }
248 continue;
249 case NEWT_KEY_ENTER:
250 if (hist_browser__toggle_fold(self))
251 break;
252 /* fall thru */
253 default:
254 return 0;
255 }
256 }
257
258 ui_browser__hide(&self->b);
259 return 0;
260}
261
262static char *callchain_list__sym_name(struct callchain_list *self,
263 char *bf, size_t bfsize)
264{
265 if (self->ms.sym)
266 return self->ms.sym->name;
267
268 snprintf(bf, bfsize, "%#Lx", self->ip);
269 return bf;
270}
271
272#define LEVEL_OFFSET_STEP 3
273
274static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
275 struct callchain_node *chain_node,
276 u64 total, int level,
277 unsigned short row,
278 off_t *row_offset,
279 bool *is_current_entry)
280{
281 struct rb_node *node;
282 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
283 u64 new_total, remaining;
284
285 if (callchain_param.mode == CHAIN_GRAPH_REL)
286 new_total = chain_node->children_hit;
287 else
288 new_total = total;
289
290 remaining = new_total;
291 node = rb_first(&chain_node->rb_root);
292 while (node) {
293 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
294 struct rb_node *next = rb_next(node);
295 u64 cumul = cumul_hits(child);
296 struct callchain_list *chain;
297 char folded_sign = ' ';
298 int first = true;
299 int extra_offset = 0;
300
301 remaining -= cumul;
302
303 list_for_each_entry(chain, &child->val, list) {
304 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
305 const char *str;
306 int color;
307 bool was_first = first;
308
309 if (first) {
310 first = false;
311 chain->ms.has_children = chain->list.next != &child->val ||
312 rb_first(&child->rb_root) != NULL;
313 } else {
314 extra_offset = LEVEL_OFFSET_STEP;
315 chain->ms.has_children = chain->list.next == &child->val &&
316 rb_first(&child->rb_root) != NULL;
317 }
318
319 folded_sign = callchain_list__folded(chain);
320 if (*row_offset != 0) {
321 --*row_offset;
322 goto do_next;
323 }
324
325 alloc_str = NULL;
326 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
327 if (was_first) {
328 double percent = cumul * 100.0 / new_total;
329
330 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
331 str = "Not enough memory!";
332 else
333 str = alloc_str;
334 }
335
336 color = HE_COLORSET_NORMAL;
337 width = self->b.width - (offset + extra_offset + 2);
338 if (ui_browser__is_current_entry(&self->b, row)) {
339 self->selection = &chain->ms;
340 color = HE_COLORSET_SELECTED;
341 *is_current_entry = true;
342 }
343
344 SLsmg_set_color(color);
345 SLsmg_gotorc(self->b.y + row, self->b.x);
346 slsmg_write_nstring(" ", offset + extra_offset);
347 slsmg_printf("%c ", folded_sign);
348 slsmg_write_nstring(str, width);
349 free(alloc_str);
350
351 if (++row == self->b.height)
352 goto out;
353do_next:
354 if (folded_sign == '+')
355 break;
356 }
357
358 if (folded_sign == '-') {
359 const int new_level = level + (extra_offset ? 2 : 1);
360 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
361 new_level, row, row_offset,
362 is_current_entry);
363 }
364 if (row == self->b.height)
365 goto out;
366 node = next;
367 }
368out:
369 return row - first_row;
370}
371
372static int hist_browser__show_callchain_node(struct hist_browser *self,
373 struct callchain_node *node,
374 int level, unsigned short row,
375 off_t *row_offset,
376 bool *is_current_entry)
377{
378 struct callchain_list *chain;
379 int first_row = row,
380 offset = level * LEVEL_OFFSET_STEP,
381 width = self->b.width - offset;
382 char folded_sign = ' ';
383
384 list_for_each_entry(chain, &node->val, list) {
385 char ipstr[BITS_PER_LONG / 4 + 1], *s;
386 int color;
387 /*
388 * FIXME: This should be moved to somewhere else,
389 * probably when the callchain is created, so as not to
390 * traverse it all over again
391 */
392 chain->ms.has_children = rb_first(&node->rb_root) != NULL;
393 folded_sign = callchain_list__folded(chain);
394
395 if (*row_offset != 0) {
396 --*row_offset;
397 continue;
398 }
399
400 color = HE_COLORSET_NORMAL;
401 if (ui_browser__is_current_entry(&self->b, row)) {
402 self->selection = &chain->ms;
403 color = HE_COLORSET_SELECTED;
404 *is_current_entry = true;
405 }
406
407 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
408 SLsmg_gotorc(self->b.y + row, self->b.x);
409 SLsmg_set_color(color);
410 slsmg_write_nstring(" ", offset);
411 slsmg_printf("%c ", folded_sign);
412 slsmg_write_nstring(s, width - 2);
413
414 if (++row == self->b.height)
415 goto out;
416 }
417
418 if (folded_sign == '-')
419 row += hist_browser__show_callchain_node_rb_tree(self, node,
420 self->hists->stats.total_period,
421 level + 1, row,
422 row_offset,
423 is_current_entry);
424out:
425 return row - first_row;
426}
427
428static int hist_browser__show_callchain(struct hist_browser *self,
429 struct rb_root *chain,
430 int level, unsigned short row,
431 off_t *row_offset,
432 bool *is_current_entry)
433{
434 struct rb_node *nd;
435 int first_row = row;
436
437 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
438 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
439
440 row += hist_browser__show_callchain_node(self, node, level,
441 row, row_offset,
442 is_current_entry);
443 if (row == self->b.height)
444 break;
445 }
446
447 return row - first_row;
448}
449
450static int hist_browser__show_entry(struct hist_browser *self,
451 struct hist_entry *entry,
452 unsigned short row)
453{
454 char s[256];
455 double percent;
456 int printed = 0;
457 int color, width = self->b.width;
458 char folded_sign = ' ';
459 bool current_entry = ui_browser__is_current_entry(&self->b, row);
460 off_t row_offset = entry->row_offset;
461
462 if (current_entry) {
463 self->he_selection = entry;
464 self->selection = &entry->ms;
465 }
466
467 if (symbol_conf.use_callchain) {
468 entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
469 folded_sign = hist_entry__folded(entry);
470 }
471
472 if (row_offset == 0) {
473 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
474 0, false, self->hists->stats.total_period);
475 percent = (entry->period * 100.0) / self->hists->stats.total_period;
476
477 color = HE_COLORSET_SELECTED;
478 if (!current_entry) {
479 if (percent >= MIN_RED)
480 color = HE_COLORSET_TOP;
481 else if (percent >= MIN_GREEN)
482 color = HE_COLORSET_MEDIUM;
483 else
484 color = HE_COLORSET_NORMAL;
485 }
486
487 SLsmg_set_color(color);
488 SLsmg_gotorc(self->b.y + row, self->b.x);
489 if (symbol_conf.use_callchain) {
490 slsmg_printf("%c ", folded_sign);
491 width -= 2;
492 }
493 slsmg_write_nstring(s, width);
494 ++row;
495 ++printed;
496 } else
497 --row_offset;
498
499 if (folded_sign == '-' && row != self->b.height) {
500 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
501 1, row, &row_offset,
502 &current_entry);
503 if (current_entry)
504 self->he_selection = entry;
505 }
506
507 return printed;
508}
509
510static unsigned int hist_browser__refresh(struct ui_browser *self)
511{
512 unsigned row = 0;
513 struct rb_node *nd;
514 struct hist_browser *hb = container_of(self, struct hist_browser, b);
515
516 if (self->top == NULL)
517 self->top = rb_first(&hb->hists->entries);
518
519 for (nd = self->top; nd; nd = rb_next(nd)) {
520 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
521
522 if (h->filtered)
523 continue;
524
525 row += hist_browser__show_entry(hb, h, row);
526 if (row == self->height)
527 break;
528 }
529
530 return row;
531}
532
533static struct rb_node *hists__filter_entries(struct rb_node *nd)
534{
535 while (nd != NULL) {
536 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
537 if (!h->filtered)
538 return nd;
539
540 nd = rb_next(nd);
541 }
542
543 return NULL;
544}
545
546static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
547{
548 while (nd != NULL) {
549 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
550 if (!h->filtered)
551 return nd;
552
553 nd = rb_prev(nd);
554 }
555
556 return NULL;
557}
558
559static void ui_browser__hists_seek(struct ui_browser *self,
560 off_t offset, int whence)
561{
562 struct hist_entry *h;
563 struct rb_node *nd;
564 bool first = true;
565
566 switch (whence) {
567 case SEEK_SET:
568 nd = hists__filter_entries(rb_first(self->entries));
569 break;
570 case SEEK_CUR:
571 nd = self->top;
572 goto do_offset;
573 case SEEK_END:
574 nd = hists__filter_prev_entries(rb_last(self->entries));
575 first = false;
576 break;
577 default:
578 return;
579 }
580
581 /*
582 * Moves not relative to the first visible entry invalidates its
583 * row_offset:
584 */
585 h = rb_entry(self->top, struct hist_entry, rb_node);
586 h->row_offset = 0;
587
588 /*
589 * Here we have to check if nd is expanded (+), if it is we can't go
590 * the next top level hist_entry, instead we must compute an offset of
591 * what _not_ to show and not change the first visible entry.
592 *
593 * This offset increments when we are going from top to bottom and
594 * decreases when we're going from bottom to top.
595 *
596 * As we don't have backpointers to the top level in the callchains
597 * structure, we need to always print the whole hist_entry callchain,
598 * skipping the first ones that are before the first visible entry
599 * and stop when we printed enough lines to fill the screen.
600 */
601do_offset:
602 if (offset > 0) {
603 do {
604 h = rb_entry(nd, struct hist_entry, rb_node);
605 if (h->ms.unfolded) {
606 u16 remaining = h->nr_rows - h->row_offset;
607 if (offset > remaining) {
608 offset -= remaining;
609 h->row_offset = 0;
610 } else {
611 h->row_offset += offset;
612 offset = 0;
613 self->top = nd;
614 break;
615 }
616 }
617 nd = hists__filter_entries(rb_next(nd));
618 if (nd == NULL)
619 break;
620 --offset;
621 self->top = nd;
622 } while (offset != 0);
623 } else if (offset < 0) {
624 while (1) {
625 h = rb_entry(nd, struct hist_entry, rb_node);
626 if (h->ms.unfolded) {
627 if (first) {
628 if (-offset > h->row_offset) {
629 offset += h->row_offset;
630 h->row_offset = 0;
631 } else {
632 h->row_offset += offset;
633 offset = 0;
634 self->top = nd;
635 break;
636 }
637 } else {
638 if (-offset > h->nr_rows) {
639 offset += h->nr_rows;
640 h->row_offset = 0;
641 } else {
642 h->row_offset = h->nr_rows + offset;
643 offset = 0;
644 self->top = nd;
645 break;
646 }
647 }
648 }
649
650 nd = hists__filter_prev_entries(rb_prev(nd));
651 if (nd == NULL)
652 break;
653 ++offset;
654 self->top = nd;
655 if (offset == 0) {
656 /*
657 * Last unfiltered hist_entry, check if it is
658 * unfolded, if it is then we should have
659 * row_offset at its last entry.
660 */
661 h = rb_entry(nd, struct hist_entry, rb_node);
662 if (h->ms.unfolded)
663 h->row_offset = h->nr_rows;
664 break;
665 }
666 first = false;
667 }
668 } else {
669 self->top = nd;
670 h = rb_entry(nd, struct hist_entry, rb_node);
671 h->row_offset = 0;
672 }
673}
674
675static struct hist_browser *hist_browser__new(struct hists *hists)
676{
677 struct hist_browser *self = zalloc(sizeof(*self));
678
679 if (self) {
680 self->hists = hists;
681 self->b.refresh = hist_browser__refresh;
682 self->b.seek = ui_browser__hists_seek;
683 }
684
685 return self;
686}
687
688static void hist_browser__delete(struct hist_browser *self)
689{
690 newtFormDestroy(self->b.form);
691 newtPopWindow();
692 free(self);
693}
694
695static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
696{
697 return self->he_selection;
698}
699
700static struct thread *hist_browser__selected_thread(struct hist_browser *self)
701{
702 return self->he_selection->thread;
703}
704
705static int hist_browser__title(char *bf, size_t size, const char *ev_name,
706 const struct dso *dso, const struct thread *thread)
707{
708 int printed = 0;
709
710 if (thread)
711 printed += snprintf(bf + printed, size - printed,
712 "Thread: %s(%d)",
713 (thread->comm_set ? thread->comm : ""),
714 thread->pid);
715 if (dso)
716 printed += snprintf(bf + printed, size - printed,
717 "%sDSO: %s", thread ? " " : "",
718 dso->short_name);
719 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
720}
721
722int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
723{
724 struct hist_browser *browser = hist_browser__new(self);
725 struct pstack *fstack;
726 const struct thread *thread_filter = NULL;
727 const struct dso *dso_filter = NULL;
728 struct newtExitStruct es;
729 char msg[160];
730 int key = -1;
731
732 if (browser == NULL)
733 return -1;
734
735 fstack = pstack__new(2);
736 if (fstack == NULL)
737 goto out;
738
739 ui_helpline__push(helpline);
740
741 hist_browser__title(msg, sizeof(msg), ev_name,
742 dso_filter, thread_filter);
743
744 while (1) {
745 const struct thread *thread;
746 const struct dso *dso;
747 char *options[16];
748 int nr_options = 0, choice = 0, i,
749 annotate = -2, zoom_dso = -2, zoom_thread = -2,
750 browse_map = -2;
751
752 if (hist_browser__run(browser, msg, &es))
753 break;
754
755 thread = hist_browser__selected_thread(browser);
756 dso = browser->selection->map ? browser->selection->map->dso : NULL;
757
758 if (es.reason == NEWT_EXIT_HOTKEY) {
759 key = es.u.key;
760
761 switch (key) {
762 case NEWT_KEY_F1:
763 goto do_help;
764 case NEWT_KEY_TAB:
765 case NEWT_KEY_UNTAB:
766 /*
767 * Exit the browser, let hists__browser_tree
768 * go to the next or previous
769 */
770 goto out_free_stack;
771 default:;
772 }
773
774 switch (key) {
775 case 'a':
776 if (browser->selection->map == NULL &&
777 browser->selection->map->dso->annotate_warned)
778 continue;
779 goto do_annotate;
780 case 'd':
781 goto zoom_dso;
782 case 't':
783 goto zoom_thread;
784 case 'h':
785 case '?':
786do_help:
787 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
788 "<- Zoom out\n"
789 "a Annotate current symbol\n"
790 "h/?/F1 Show this window\n"
791 "d Zoom into current DSO\n"
792 "t Zoom into current Thread\n"
793 "q/CTRL+C Exit browser");
794 continue;
795 default:;
796 }
797 if (is_exit_key(key)) {
798 if (key == NEWT_KEY_ESCAPE &&
799 !ui__dialog_yesno("Do you really want to exit?"))
800 continue;
801 break;
802 }
803
804 if (es.u.key == NEWT_KEY_LEFT) {
805 const void *top;
806
807 if (pstack__empty(fstack))
808 continue;
809 top = pstack__pop(fstack);
810 if (top == &dso_filter)
811 goto zoom_out_dso;
812 if (top == &thread_filter)
813 goto zoom_out_thread;
814 continue;
815 }
816 }
817
818 if (browser->selection->sym != NULL &&
819 !browser->selection->map->dso->annotate_warned &&
820 asprintf(&options[nr_options], "Annotate %s",
821 browser->selection->sym->name) > 0)
822 annotate = nr_options++;
823
824 if (thread != NULL &&
825 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
826 (thread_filter ? "out of" : "into"),
827 (thread->comm_set ? thread->comm : ""),
828 thread->pid) > 0)
829 zoom_thread = nr_options++;
830
831 if (dso != NULL &&
832 asprintf(&options[nr_options], "Zoom %s %s DSO",
833 (dso_filter ? "out of" : "into"),
834 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
835 zoom_dso = nr_options++;
836
837 if (browser->selection->map != NULL &&
838 asprintf(&options[nr_options], "Browse map details") > 0)
839 browse_map = nr_options++;
840
841 options[nr_options++] = (char *)"Exit";
842
843 choice = ui__popup_menu(nr_options, options);
844
845 for (i = 0; i < nr_options - 1; ++i)
846 free(options[i]);
847
848 if (choice == nr_options - 1)
849 break;
850
851 if (choice == -1)
852 continue;
853
854 if (choice == annotate) {
855 struct hist_entry *he;
856do_annotate:
857 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
858 browser->selection->map->dso->annotate_warned = 1;
859 ui_helpline__puts("No vmlinux file found, can't "
860 "annotate with just a "
861 "kallsyms file");
862 continue;
863 }
864
865 he = hist_browser__selected_entry(browser);
866 if (he == NULL)
867 continue;
868
869 hist_entry__tui_annotate(he);
870 } else if (choice == browse_map)
871 map__browse(browser->selection->map);
872 else if (choice == zoom_dso) {
873zoom_dso:
874 if (dso_filter) {
875 pstack__remove(fstack, &dso_filter);
876zoom_out_dso:
877 ui_helpline__pop();
878 dso_filter = NULL;
879 } else {
880 if (dso == NULL)
881 continue;
882 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
883 dso->kernel ? "the Kernel" : dso->short_name);
884 dso_filter = dso;
885 pstack__push(fstack, &dso_filter);
886 }
887 hists__filter_by_dso(self, dso_filter);
888 hist_browser__title(msg, sizeof(msg), ev_name,
889 dso_filter, thread_filter);
890 hist_browser__reset(browser);
891 } else if (choice == zoom_thread) {
892zoom_thread:
893 if (thread_filter) {
894 pstack__remove(fstack, &thread_filter);
895zoom_out_thread:
896 ui_helpline__pop();
897 thread_filter = NULL;
898 } else {
899 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
900 thread->comm_set ? thread->comm : "",
901 thread->pid);
902 thread_filter = thread;
903 pstack__push(fstack, &thread_filter);
904 }
905 hists__filter_by_thread(self, thread_filter);
906 hist_browser__title(msg, sizeof(msg), ev_name,
907 dso_filter, thread_filter);
908 hist_browser__reset(browser);
909 }
910 }
911out_free_stack:
912 pstack__delete(fstack);
913out:
914 hist_browser__delete(browser);
915 return key;
916}
917
918int hists__tui_browse_tree(struct rb_root *self, const char *help)
919{
920 struct rb_node *first = rb_first(self), *nd = first, *next;
921 int key = 0;
922
923 while (nd) {
924 struct hists *hists = rb_entry(nd, struct hists, rb_node);
925 const char *ev_name = __event_name(hists->type, hists->config);
926
927 key = hists__browse(hists, help, ev_name);
928
929 if (is_exit_key(key))
930 break;
931
932 switch (key) {
933 case NEWT_KEY_TAB:
934 next = rb_next(nd);
935 if (next)
936 nd = next;
937 break;
938 case NEWT_KEY_UNTAB:
939 if (nd == first)
940 continue;
941 nd = rb_prev(nd);
942 default:
943 break;
944 }
945 }
946
947 return key;
948}
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
new file mode 100644
index 000000000000..142b825b42bf
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.c
@@ -0,0 +1,161 @@
1#include "../libslang.h"
2#include <elf.h>
3#include <newt.h>
4#include <sys/ttydefaults.h>
5#include <ctype.h>
6#include <string.h>
7#include <linux/bitops.h>
8#include "../../debug.h"
9#include "../../symbol.h"
10#include "../browser.h"
11#include "../helpline.h"
12#include "map.h"
13
14static int ui_entry__read(const char *title, char *bf, size_t size, int width)
15{
16 struct newtExitStruct es;
17 newtComponent form, entry;
18 const char *result;
19 int err = -1;
20
21 newtCenteredWindow(width, 1, title);
22 form = newtForm(NULL, NULL, 0);
23 if (form == NULL)
24 return -1;
25
26 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
27 if (entry == NULL)
28 goto out_free_form;
29
30 newtFormAddComponent(form, entry);
31 newtFormAddHotKey(form, NEWT_KEY_ENTER);
32 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
33 newtFormAddHotKey(form, NEWT_KEY_LEFT);
34 newtFormAddHotKey(form, CTRL('c'));
35 newtFormRun(form, &es);
36
37 if (result != NULL) {
38 strncpy(bf, result, size);
39 err = 0;
40 }
41out_free_form:
42 newtPopWindow();
43 newtFormDestroy(form);
44 return 0;
45}
46
47struct map_browser {
48 struct ui_browser b;
49 struct map *map;
50 u16 namelen;
51 u8 addrlen;
52};
53
54static void map_browser__write(struct ui_browser *self, void *nd, int row)
55{
56 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
57 struct map_browser *mb = container_of(self, struct map_browser, b);
58 bool current_entry = ui_browser__is_current_entry(self, row);
59 int color = ui_browser__percent_color(0, current_entry);
60
61 SLsmg_set_color(color);
62 slsmg_printf("%*llx %*llx %c ",
63 mb->addrlen, sym->start, mb->addrlen, sym->end,
64 sym->binding == STB_GLOBAL ? 'g' :
65 sym->binding == STB_LOCAL ? 'l' : 'w');
66 slsmg_write_nstring(sym->name, mb->namelen);
67}
68
69/* FIXME uber-kludgy, see comment on cmd_report... */
70static u32 *symbol__browser_index(struct symbol *self)
71{
72 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
73}
74
75static int map_browser__search(struct map_browser *self)
76{
77 char target[512];
78 struct symbol *sym;
79 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
80
81 if (err)
82 return err;
83
84 if (target[0] == '0' && tolower(target[1]) == 'x') {
85 u64 addr = strtoull(target, NULL, 16);
86 sym = map__find_symbol(self->map, addr, NULL);
87 } else
88 sym = map__find_symbol_by_name(self->map, target, NULL);
89
90 if (sym != NULL) {
91 u32 *idx = symbol__browser_index(sym);
92
93 self->b.top = &sym->rb_node;
94 self->b.index = self->b.top_idx = *idx;
95 } else
96 ui_helpline__fpush("%s not found!", target);
97
98 return 0;
99}
100
101static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
102{
103 if (ui_browser__show(&self->b, self->map->dso->long_name,
104 "Press <- or ESC to exit, %s / to search",
105 verbose ? "" : "restart with -v to use") < 0)
106 return -1;
107
108 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
109 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
110 if (verbose)
111 newtFormAddHotKey(self->b.form, '/');
112
113 while (1) {
114 ui_browser__run(&self->b, es);
115
116 if (es->reason != NEWT_EXIT_HOTKEY)
117 break;
118 if (verbose && es->u.key == '/')
119 map_browser__search(self);
120 else
121 break;
122 }
123
124 ui_browser__hide(&self->b);
125 return 0;
126}
127
128int map__browse(struct map *self)
129{
130 struct map_browser mb = {
131 .b = {
132 .entries = &self->dso->symbols[self->type],
133 .refresh = ui_browser__rb_tree_refresh,
134 .seek = ui_browser__rb_tree_seek,
135 .write = map_browser__write,
136 },
137 .map = self,
138 };
139 struct newtExitStruct es;
140 struct rb_node *nd;
141 char tmp[BITS_PER_LONG / 4];
142 u64 maxaddr = 0;
143
144 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
145 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
146
147 if (mb.namelen < pos->namelen)
148 mb.namelen = pos->namelen;
149 if (maxaddr < pos->end)
150 maxaddr = pos->end;
151 if (verbose) {
152 u32 *idx = symbol__browser_index(pos);
153 *idx = mb.b.nr_entries;
154 }
155 ++mb.b.nr_entries;
156 }
157
158 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
159 mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
160 return map_browser__run(&mb, &es);
161}
diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/util/ui/browsers/map.h
new file mode 100644
index 000000000000..df8581a43e17
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.h
@@ -0,0 +1,6 @@
1#ifndef _PERF_UI_MAP_BROWSER_H_
2#define _PERF_UI_MAP_BROWSER_H_ 1
3struct map;
4
5int map__browse(struct map *self);
6#endif /* _PERF_UI_MAP_BROWSER_H_ */
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c
new file mode 100644
index 000000000000..8d79daa4458a
--- /dev/null
+++ b/tools/perf/util/ui/helpline.c
@@ -0,0 +1,69 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#include <stdlib.h>
4#include <newt.h>
5
6#include "../debug.h"
7#include "helpline.h"
8
9void ui_helpline__pop(void)
10{
11 newtPopHelpLine();
12}
13
14void ui_helpline__push(const char *msg)
15{
16 newtPushHelpLine(msg);
17}
18
19void ui_helpline__vpush(const char *fmt, va_list ap)
20{
21 char *s;
22
23 if (vasprintf(&s, fmt, ap) < 0)
24 vfprintf(stderr, fmt, ap);
25 else {
26 ui_helpline__push(s);
27 free(s);
28 }
29}
30
31void ui_helpline__fpush(const char *fmt, ...)
32{
33 va_list ap;
34
35 va_start(ap, fmt);
36 ui_helpline__vpush(fmt, ap);
37 va_end(ap);
38}
39
40void ui_helpline__puts(const char *msg)
41{
42 ui_helpline__pop();
43 ui_helpline__push(msg);
44}
45
46void ui_helpline__init(void)
47{
48 ui_helpline__puts(" ");
49}
50
51char ui_helpline__last_msg[1024];
52
53int ui_helpline__show_help(const char *format, va_list ap)
54{
55 int ret;
56 static int backlog;
57
58 ret = vsnprintf(ui_helpline__last_msg + backlog,
59 sizeof(ui_helpline__last_msg) - backlog, format, ap);
60 backlog += ret;
61
62 if (ui_helpline__last_msg[backlog - 1] == '\n') {
63 ui_helpline__puts(ui_helpline__last_msg);
64 newtRefresh();
65 backlog = 0;
66 }
67
68 return ret;
69}
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h
new file mode 100644
index 000000000000..ab6028d0c401
--- /dev/null
+++ b/tools/perf/util/ui/helpline.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_HELPLINE_H_
2#define _PERF_UI_HELPLINE_H_ 1
3
4void ui_helpline__init(void);
5void ui_helpline__pop(void);
6void ui_helpline__push(const char *msg);
7void ui_helpline__vpush(const char *fmt, va_list ap);
8void ui_helpline__fpush(const char *fmt, ...);
9void ui_helpline__puts(const char *msg);
10
11#endif /* _PERF_UI_HELPLINE_H_ */
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h
new file mode 100644
index 000000000000..5623da8e8080
--- /dev/null
+++ b/tools/perf/util/ui/libslang.h
@@ -0,0 +1,27 @@
1#ifndef _PERF_UI_SLANG_H_
2#define _PERF_UI_SLANG_H_ 1
3/*
4 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
5 * the build if it isn't defined. Use the equivalent one that glibc
6 * has on features.h.
7 */
8#include <features.h>
9#ifndef HAVE_LONG_LONG
10#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
11#endif
12#include <slang.h>
13
14#if SLANG_VERSION < 20104
15#define slsmg_printf(msg, args...) \
16 SLsmg_printf((char *)msg, ##args)
17#define slsmg_write_nstring(msg, len) \
18 SLsmg_write_nstring((char *)msg, len)
19#define sltt_set_color(obj, name, fg, bg) \
20 SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
21#else
22#define slsmg_printf SLsmg_printf
23#define slsmg_write_nstring SLsmg_write_nstring
24#define sltt_set_color SLtt_set_color
25#endif
26
27#endif /* _PERF_UI_SLANG_H_ */
diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c
new file mode 100644
index 000000000000..d7fc399d36b3
--- /dev/null
+++ b/tools/perf/util/ui/progress.c
@@ -0,0 +1,60 @@
1#include <stdlib.h>
2#include <newt.h>
3#include "../cache.h"
4#include "progress.h"
5
6struct ui_progress {
7 newtComponent form, scale;
8};
9
10struct ui_progress *ui_progress__new(const char *title, u64 total)
11{
12 struct ui_progress *self = malloc(sizeof(*self));
13
14 if (self != NULL) {
15 int cols;
16
17 if (use_browser <= 0)
18 return self;
19 newtGetScreenSize(&cols, NULL);
20 cols -= 4;
21 newtCenteredWindow(cols, 1, title);
22 self->form = newtForm(NULL, NULL, 0);
23 if (self->form == NULL)
24 goto out_free_self;
25 self->scale = newtScale(0, 0, cols, total);
26 if (self->scale == NULL)
27 goto out_free_form;
28 newtFormAddComponent(self->form, self->scale);
29 newtRefresh();
30 }
31
32 return self;
33
34out_free_form:
35 newtFormDestroy(self->form);
36out_free_self:
37 free(self);
38 return NULL;
39}
40
41void ui_progress__update(struct ui_progress *self, u64 curr)
42{
43 /*
44 * FIXME: We should have a per UI backend way of showing progress,
45 * stdio will just show a percentage as NN%, etc.
46 */
47 if (use_browser <= 0)
48 return;
49 newtScaleSet(self->scale, curr);
50 newtRefresh();
51}
52
53void ui_progress__delete(struct ui_progress *self)
54{
55 if (use_browser > 0) {
56 newtFormDestroy(self->form);
57 newtPopWindow();
58 }
59 free(self);
60}
diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h
new file mode 100644
index 000000000000..a3820a0beb5b
--- /dev/null
+++ b/tools/perf/util/ui/progress.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_PROGRESS_H_
2#define _PERF_UI_PROGRESS_H_ 1
3
4struct ui_progress;
5
6struct ui_progress *ui_progress__new(const char *title, u64 total);
7void ui_progress__delete(struct ui_progress *self);
8
9void ui_progress__update(struct ui_progress *self, u64 curr);
10
11#endif
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c
new file mode 100644
index 000000000000..662085032eb7
--- /dev/null
+++ b/tools/perf/util/ui/setup.c
@@ -0,0 +1,42 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdbool.h>
4
5#include "../cache.h"
6#include "../debug.h"
7#include "browser.h"
8#include "helpline.h"
9
10static void newt_suspend(void *d __used)
11{
12 newtSuspend();
13 raise(SIGTSTP);
14 newtResume();
15}
16
17void setup_browser(void)
18{
19 if (!isatty(1) || !use_browser || dump_trace) {
20 use_browser = 0;
21 setup_pager();
22 return;
23 }
24
25 use_browser = 1;
26 newtInit();
27 newtCls();
28 newtSetSuspendCallback(newt_suspend, NULL);
29 ui_helpline__init();
30 ui_browser__init();
31}
32
33void exit_browser(bool wait_for_ok)
34{
35 if (use_browser > 0) {
36 if (wait_for_ok) {
37 char title[] = "Fatal Error", ok[] = "Ok";
38 newtWinMessage(title, ok, ui_helpline__last_msg);
39 }
40 newtFinished();
41 }
42}
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c
new file mode 100644
index 000000000000..04600e26ceea
--- /dev/null
+++ b/tools/perf/util/ui/util.c
@@ -0,0 +1,114 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdio.h>
4#include <stdbool.h>
5#include <string.h>
6#include <sys/ttydefaults.h>
7
8#include "../cache.h"
9#include "../debug.h"
10#include "browser.h"
11#include "helpline.h"
12#include "util.h"
13
14newtComponent newt_form__new(void);
15
16static void newt_form__set_exit_keys(newtComponent self)
17{
18 newtFormAddHotKey(self, NEWT_KEY_LEFT);
19 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
20 newtFormAddHotKey(self, 'Q');
21 newtFormAddHotKey(self, 'q');
22 newtFormAddHotKey(self, CTRL('c'));
23}
24
25newtComponent newt_form__new(void)
26{
27 newtComponent self = newtForm(NULL, NULL, 0);
28 if (self)
29 newt_form__set_exit_keys(self);
30 return self;
31}
32
33int ui__popup_menu(int argc, char * const argv[])
34{
35 struct newtExitStruct es;
36 int i, rc = -1, max_len = 5;
37 newtComponent listbox, form = newt_form__new();
38
39 if (form == NULL)
40 return -1;
41
42 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
43 if (listbox == NULL)
44 goto out_destroy_form;
45
46 newtFormAddComponent(form, listbox);
47
48 for (i = 0; i < argc; ++i) {
49 int len = strlen(argv[i]);
50 if (len > max_len)
51 max_len = len;
52 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
53 goto out_destroy_form;
54 }
55
56 newtCenteredWindow(max_len, argc, NULL);
57 newtFormRun(form, &es);
58 rc = newtListboxGetCurrent(listbox) - NULL;
59 if (es.reason == NEWT_EXIT_HOTKEY)
60 rc = -1;
61 newtPopWindow();
62out_destroy_form:
63 newtFormDestroy(form);
64 return rc;
65}
66
67int ui__help_window(const char *text)
68{
69 struct newtExitStruct es;
70 newtComponent tb, form = newt_form__new();
71 int rc = -1;
72 int max_len = 0, nr_lines = 0;
73 const char *t;
74
75 if (form == NULL)
76 return -1;
77
78 t = text;
79 while (1) {
80 const char *sep = strchr(t, '\n');
81 int len;
82
83 if (sep == NULL)
84 sep = strchr(t, '\0');
85 len = sep - t;
86 if (max_len < len)
87 max_len = len;
88 ++nr_lines;
89 if (*sep == '\0')
90 break;
91 t = sep + 1;
92 }
93
94 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
95 if (tb == NULL)
96 goto out_destroy_form;
97
98 newtTextboxSetText(tb, text);
99 newtFormAddComponent(form, tb);
100 newtCenteredWindow(max_len, nr_lines, NULL);
101 newtFormRun(form, &es);
102 newtPopWindow();
103 rc = 0;
104out_destroy_form:
105 newtFormDestroy(form);
106 return rc;
107}
108
109bool ui__dialog_yesno(const char *msg)
110{
111 /* newtWinChoice should really be accepting const char pointers... */
112 char yes[] = "Yes", no[] = "No";
113 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
114}
diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h
new file mode 100644
index 000000000000..afcbc1d99531
--- /dev/null
+++ b/tools/perf/util/ui/util.h
@@ -0,0 +1,10 @@
1#ifndef _PERF_UI_UTIL_H_
2#define _PERF_UI_UTIL_H_ 1
3
4#include <stdbool.h>
5
6int ui__popup_menu(int argc, char * const argv[]);
7int ui__help_window(const char *text);
8bool ui__dialog_yesno(const char *msg);
9
10#endif /* _PERF_UI_UTIL_H_ */
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 4e8b6b0c551c..f380fed74359 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -89,6 +89,7 @@
89 89
90extern const char *graph_line; 90extern const char *graph_line;
91extern const char *graph_dotted_line; 91extern const char *graph_dotted_line;
92extern char buildid_dir[];
92 93
93/* On most systems <limits.h> would have given us this, but 94/* On most systems <limits.h> would have given us this, but
94 * not on some systems (e.g. GNU/Hurd). 95 * not on some systems (e.g. GNU/Hurd).
@@ -152,6 +153,8 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
152extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); 153extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
153 154
154extern int prefixcmp(const char *str, const char *prefix); 155extern int prefixcmp(const char *str, const char *prefix);
156extern void set_buildid_dir(void);
157extern void disable_buildid_cache(void);
155 158
156static inline const char *skip_prefix(const char *str, const char *prefix) 159static inline const char *skip_prefix(const char *str, const char *prefix)
157{ 160{