aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
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/Makefile142
-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/builtin-annotate.c8
-rw-r--r--tools/perf/builtin-buildid-cache.c3
-rw-r--r--tools/perf/builtin-buildid-list.c4
-rw-r--r--tools/perf/builtin-diff.c9
-rw-r--r--tools/perf/builtin-probe.c3
-rw-r--r--tools/perf/builtin-record.c81
-rw-r--r--tools/perf/builtin-report.c58
-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/sched-migration.py461
-rw-r--r--tools/perf/util/build-id.c28
-rw-r--r--tools/perf/util/cache.h1
-rw-r--r--tools/perf/util/callchain.c2
-rw-r--r--tools/perf/util/callchain.h2
-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.c107
-rw-r--r--tools/perf/util/event.h6
-rw-r--r--tools/perf/util/header.c13
-rw-r--r--tools/perf/util/hist.c230
-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.c1178
-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/session.c62
-rw-r--r--tools/perf/util/sort.c46
-rw-r--r--tools/perf/util/sort.h22
-rw-r--r--tools/perf/util/symbol.c339
-rw-r--r--tools/perf/util/symbol.h19
-rw-r--r--tools/perf/util/thread.c7
-rw-r--r--tools/perf/util/thread.h2
-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
83 files changed, 6119 insertions, 1872 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 d75c28a825f5..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,64 +559,86 @@ 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 620else
604 ifdef HAVE_CPLUS_DEMANGLE 621 ifdef HAVE_CPLUS_DEMANGLE
605 EXTLIBS += -liberty 622 EXTLIBS += -liberty
606 BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE 623 BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
607 else 624 else
608 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") 625 FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd
609 626 has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD))
610 ifeq ($(has_bfd),y) 627 ifeq ($(has_bfd),y)
611 EXTLIBS += -lbfd 628 EXTLIBS += -lbfd
612 else 629 else
613 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") 630 FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty
631 has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY))
614 ifeq ($(has_bfd_iberty),y) 632 ifeq ($(has_bfd_iberty),y)
615 EXTLIBS += -lbfd -liberty 633 EXTLIBS += -lbfd -liberty
616 else 634 else
617 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") 635 FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz
636 has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z))
618 ifeq ($(has_bfd_iberty_z),y) 637 ifeq ($(has_bfd_iberty_z),y)
619 EXTLIBS += -lbfd -liberty -lz 638 EXTLIBS += -lbfd -liberty -lz
620 else 639 else
621 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") 640 FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty
641 has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE))
622 ifeq ($(has_cplus_demangle),y) 642 ifeq ($(has_cplus_demangle),y)
623 EXTLIBS += -liberty 643 EXTLIBS += -liberty
624 BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE 644 BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
@@ -867,7 +887,7 @@ export TAR INSTALL DESTDIR SHELL_PATH
867 887
868SHELL = $(SHELL_PATH) 888SHELL = $(SHELL_PATH)
869 889
870all:: .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
871ifneq (,$X) 891ifneq (,$X)
872 $(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';)
873endif 893endif
@@ -958,7 +978,16 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS
958$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS 978$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
959 $(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)"' $<
960 980
961$(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
962 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< 991 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
963 992
964$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS 993$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
@@ -1197,11 +1226,6 @@ clean:
1197.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS 1226.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS
1198.PHONY: .FORCE-PERF-BUILD-OPTIONS 1227.PHONY: .FORCE-PERF-BUILD-OPTIONS
1199 1228
1200.perf.dev.null:
1201 touch .perf.dev.null
1202
1203.INTERMEDIATE: .perf.dev.null
1204
1205### 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
1206# 1230#
1207check-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/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-buildid-list.c b/tools/perf/builtin-buildid-list.c
index 99890728409e..44a47e13bd67 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -43,10 +43,8 @@ static int __cmd_buildid_list(void)
43 if (session == NULL) 43 if (session == NULL)
44 return -1; 44 return -1;
45 45
46 if (with_hits) { 46 if (with_hits)
47 symbol_conf.full_paths = true;
48 perf_session__process_events(session, &build_id__mark_dso_hit_ops); 47 perf_session__process_events(session, &build_id__mark_dso_hit_ops);
49 }
50 48
51 perf_session__fprintf_dsos_buildid(session, stdout, with_hits); 49 perf_session__fprintf_dsos_buildid(session, stdout, with_hits);
52 50
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 711745f56bba..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;
@@ -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.
@@ -561,12 +560,15 @@ static int __cmd_record(int argc, const char **argv)
561 if (!file_new) { 560 if (!file_new) {
562 err = perf_header__read(session, output); 561 err = perf_header__read(session, output);
563 if (err < 0) 562 if (err < 0)
564 return err; 563 goto out_delete_session;
565 } 564 }
566 565
567 if (have_tracepoints(attrs, nr_counters)) 566 if (have_tracepoints(attrs, nr_counters))
568 perf_header__set_feat(&session->header, HEADER_TRACE_INFO); 567 perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
569 568
569 /*
570 * perf_session__delete(session) will be called at atexit_header()
571 */
570 atexit(atexit_header); 572 atexit(atexit_header);
571 573
572 if (forks) { 574 if (forks) {
@@ -622,10 +624,15 @@ static int __cmd_record(int argc, const char **argv)
622 close(child_ready_pipe[0]); 624 close(child_ready_pipe[0]);
623 } 625 }
624 626
625 if ((!system_wide && no_inherit) || profile_cpu != -1) { 627 nr_cpus = read_cpu_map(cpu_list);
626 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);
627 } else { 635 } else {
628 nr_cpus = read_cpu_map();
629 for (i = 0; i < nr_cpus; i++) 636 for (i = 0; i < nr_cpus; i++)
630 open_counters(cpumap[i]); 637 open_counters(cpumap[i]);
631 } 638 }
@@ -704,7 +711,7 @@ static int __cmd_record(int argc, const char **argv)
704 if (perf_guest) 711 if (perf_guest)
705 perf_session__process_machines(session, event__synthesize_guest_os); 712 perf_session__process_machines(session, event__synthesize_guest_os);
706 713
707 if (!system_wide && profile_cpu == -1) 714 if (!system_wide)
708 event__synthesize_thread(target_tid, process_synthesized_event, 715 event__synthesize_thread(target_tid, process_synthesized_event,
709 session); 716 session);
710 else 717 else
@@ -766,6 +773,10 @@ static int __cmd_record(int argc, const char **argv)
766 bytes_written / 24); 773 bytes_written / 24);
767 774
768 return 0; 775 return 0;
776
777out_delete_session:
778 perf_session__delete(session);
779 return err;
769} 780}
770 781
771static const char * const record_usage[] = { 782static const char * const record_usage[] = {
@@ -794,8 +805,8 @@ static const struct option options[] = {
794 "system-wide collection from all CPUs"), 805 "system-wide collection from all CPUs"),
795 OPT_BOOLEAN('A', "append", &append_file, 806 OPT_BOOLEAN('A', "append", &append_file,
796 "append to the output file to do incremental profiling"), 807 "append to the output file to do incremental profiling"),
797 OPT_INTEGER('C', "profile_cpu", &profile_cpu, 808 OPT_STRING('C', "cpu", &cpu_list, "cpu",
798 "CPU to profile on"), 809 "list of cpus to monitor"),
799 OPT_BOOLEAN('f', "force", &force, 810 OPT_BOOLEAN('f', "force", &force,
800 "overwrite existing data file (deprecated)"), 811 "overwrite existing data file (deprecated)"),
801 OPT_U64('c', "count", &user_interval, "event period to sample"), 812 OPT_U64('c', "count", &user_interval, "event period to sample"),
@@ -815,17 +826,19 @@ static const struct option options[] = {
815 "Sample addresses"), 826 "Sample addresses"),
816 OPT_BOOLEAN('n', "no-samples", &no_samples, 827 OPT_BOOLEAN('n', "no-samples", &no_samples,
817 "don't sample"), 828 "don't sample"),
829 OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid,
830 "do not update the buildid cache"),
818 OPT_END() 831 OPT_END()
819}; 832};
820 833
821int cmd_record(int argc, const char **argv, const char *prefix __used) 834int cmd_record(int argc, const char **argv, const char *prefix __used)
822{ 835{
823 int i,j; 836 int i, j, err = -ENOMEM;
824 837
825 argc = parse_options(argc, argv, options, record_usage, 838 argc = parse_options(argc, argv, options, record_usage,
826 PARSE_OPT_STOP_AT_NON_OPTION); 839 PARSE_OPT_STOP_AT_NON_OPTION);
827 if (!argc && target_pid == -1 && target_tid == -1 && 840 if (!argc && target_pid == -1 && target_tid == -1 &&
828 !system_wide && profile_cpu == -1) 841 !system_wide && !cpu_list)
829 usage_with_options(record_usage, options); 842 usage_with_options(record_usage, options);
830 843
831 if (force && append_file) { 844 if (force && append_file) {
@@ -839,6 +852,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
839 } 852 }
840 853
841 symbol__init(); 854 symbol__init();
855 if (no_buildid)
856 disable_buildid_cache();
842 857
843 if (!nr_counters) { 858 if (!nr_counters) {
844 nr_counters = 1; 859 nr_counters = 1;
@@ -857,7 +872,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
857 } else { 872 } else {
858 all_tids=malloc(sizeof(pid_t)); 873 all_tids=malloc(sizeof(pid_t));
859 if (!all_tids) 874 if (!all_tids)
860 return -ENOMEM; 875 goto out_symbol_exit;
861 876
862 all_tids[0] = target_tid; 877 all_tids[0] = target_tid;
863 thread_num = 1; 878 thread_num = 1;
@@ -867,13 +882,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
867 for (j = 0; j < MAX_COUNTERS; j++) { 882 for (j = 0; j < MAX_COUNTERS; j++) {
868 fd[i][j] = malloc(sizeof(int)*thread_num); 883 fd[i][j] = malloc(sizeof(int)*thread_num);
869 if (!fd[i][j]) 884 if (!fd[i][j])
870 return -ENOMEM; 885 goto out_free_fd;
871 } 886 }
872 } 887 }
873 event_array = malloc( 888 event_array = malloc(
874 sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); 889 sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num);
875 if (!event_array) 890 if (!event_array)
876 return -ENOMEM; 891 goto out_free_fd;
877 892
878 if (user_interval != ULLONG_MAX) 893 if (user_interval != ULLONG_MAX)
879 default_interval = user_interval; 894 default_interval = user_interval;
@@ -889,8 +904,22 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
889 default_interval = freq; 904 default_interval = freq;
890 } else { 905 } else {
891 fprintf(stderr, "frequency and count are zero, aborting\n"); 906 fprintf(stderr, "frequency and count are zero, aborting\n");
892 exit(EXIT_FAILURE); 907 err = -EINVAL;
908 goto out_free_event_array;
893 } 909 }
894 910
895 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;
896} 925}
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index fd7407c7205c..55fc1f46892a 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -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-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/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/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 52c777e451ed..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;
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index f2e9ee164bd8..624a96c636fd 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -63,5 +63,5 @@ int register_callchain_param(struct callchain_param *param);
63int append_chain(struct callchain_node *root, struct ip_callchain *chain, 63int append_chain(struct callchain_node *root, struct ip_callchain *chain,
64 struct map_symbol *syms, u64 period); 64 struct map_symbol *syms, u64 period);
65 65
66bool ip_callchain__valid(struct ip_callchain *chain, event_t *event); 66bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
67#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 2fbf6a463c81..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,30 +334,29 @@ 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}
@@ -374,7 +367,8 @@ int event__process_comm(event_t *self, struct perf_session *session)
374 367
375 dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); 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);
@@ -552,6 +548,26 @@ int event__process_task(event_t *self, struct perf_session *session)
552 return 0; 548 return 0;
553} 549}
554 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
555void thread__find_addr_map(struct thread *self, 571void thread__find_addr_map(struct thread *self,
556 struct perf_session *session, u8 cpumode, 572 struct perf_session *session, u8 cpumode,
557 enum map_type type, pid_t pid, u64 addr, 573 enum map_type type, pid_t pid, u64 addr,
@@ -641,27 +657,49 @@ void thread__find_addr_location(struct thread *self,
641 al->sym = NULL; 657 al->sym = NULL;
642} 658}
643 659
644static void dso__calc_col_width(struct dso *self) 660static void dso__calc_col_width(struct dso *self, struct hists *hists)
645{ 661{
646 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && 662 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
647 (!symbol_conf.dso_list || 663 (!symbol_conf.dso_list ||
648 strlist__has_entry(symbol_conf.dso_list, self->name))) { 664 strlist__has_entry(symbol_conf.dso_list, self->name))) {
649 u16 slen = self->short_name_len; 665 u16 slen = dso__name_len(self);
650 if (verbose) 666 hists__new_col_len(hists, HISTC_DSO, slen);
651 slen = self->long_name_len;
652 if (dsos__col_width < slen)
653 dsos__col_width = slen;
654 } 667 }
655 668
656 self->slen_calculated = 1; 669 self->slen_calculated = 1;
657} 670}
658 671
659int event__preprocess_sample(const event_t *self, struct perf_session *session, 672int event__preprocess_sample(const event_t *self, struct perf_session *session,
660 struct addr_location *al, symbol_filter_t filter) 673 struct addr_location *al, struct sample_data *data,
674 symbol_filter_t filter)
661{ 675{
662 u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 676 u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
663 struct thread *thread = perf_session__findnew(session, self->ip.pid); 677 struct thread *thread;
664 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);
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);
665 if (thread == NULL) 703 if (thread == NULL)
666 return -1; 704 return -1;
667 705
@@ -687,6 +725,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
687 al->map ? al->map->dso->long_name : 725 al->map ? al->map->dso->long_name :
688 al->level == 'H' ? "[hypervisor]" : "<not found>"); 726 al->level == 'H' ? "[hypervisor]" : "<not found>");
689 al->sym = NULL; 727 al->sym = NULL;
728 al->cpu = data->cpu;
690 729
691 if (al->map) { 730 if (al->map) {
692 if (symbol_conf.dso_list && 731 if (symbol_conf.dso_list &&
@@ -703,16 +742,17 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
703 * sampled. 742 * sampled.
704 */ 743 */
705 if (!sort_dso.elide && !al->map->dso->slen_calculated) 744 if (!sort_dso.elide && !al->map->dso->slen_calculated)
706 dso__calc_col_width(al->map->dso); 745 dso__calc_col_width(al->map->dso, &session->hists);
707 746
708 al->sym = map__find_symbol(al->map, al->addr, filter); 747 al->sym = map__find_symbol(al->map, al->addr, filter);
709 } else { 748 } else {
710 const unsigned int unresolved_col_width = BITS_PER_LONG / 4; 749 const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
711 750
712 if (dsos__col_width < unresolved_col_width && 751 if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width &&
713 !symbol_conf.col_width_list_str && !symbol_conf.field_sep && 752 !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
714 !symbol_conf.dso_list) 753 !symbol_conf.dso_list)
715 dsos__col_width = unresolved_col_width; 754 hists__set_col_len(&session->hists, HISTC_DSO,
755 unresolved_col_width);
716 } 756 }
717 757
718 if (symbol_conf.sym_list && al->sym && 758 if (symbol_conf.sym_list && al->sym &&
@@ -726,9 +766,9 @@ out_filtered:
726 return 0; 766 return 0;
727} 767}
728 768
729int 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)
730{ 770{
731 u64 *array = event->sample.array; 771 const u64 *array = event->sample.array;
732 772
733 if (type & PERF_SAMPLE_IP) { 773 if (type & PERF_SAMPLE_IP) {
734 data->ip = event->ip.ip; 774 data->ip = event->ip.ip;
@@ -767,7 +807,8 @@ int event__parse_sample(event_t *event, u64 type, struct sample_data *data)
767 u32 *p = (u32 *)array; 807 u32 *p = (u32 *)array;
768 data->cpu = *p; 808 data->cpu = *p;
769 array++; 809 array++;
770 } 810 } else
811 data->cpu = -1;
771 812
772 if (type & PERF_SAMPLE_PERIOD) { 813 if (type & PERF_SAMPLE_PERIOD) {
773 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 784ee0bdda77..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,29 +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 int ret; 697 hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists,
635 698 show_displacement, displacement,
636 ret = hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, 699 true, session_total);
637 show_displacement, displacement,
638 true, session_total);
639 if (!ret)
640 return 0;
641
642 return fprintf(fp, "%s\n", bf); 700 return fprintf(fp, "%s\n", bf);
643} 701}
644 702
645static 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,
646 u64 session_total) 705 u64 session_total)
647{ 706{
648 int left_margin = 0; 707 int left_margin = 0;
@@ -650,7 +709,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp,
650 if (sort__first_dimension == SORT_COMM) { 709 if (sort__first_dimension == SORT_COMM) {
651 struct sort_entry *se = list_first_entry(&hist_entry__sort_list, 710 struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
652 typeof(*se), list); 711 typeof(*se), list);
653 left_margin = se->se_width ? *se->se_width : 0; 712 left_margin = hists__col_len(hists, se->se_width_idx);
654 left_margin -= thread__comm_len(self->thread); 713 left_margin -= thread__comm_len(self->thread);
655 } 714 }
656 715
@@ -721,17 +780,17 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
721 continue; 780 continue;
722 } 781 }
723 width = strlen(se->se_header); 782 width = strlen(se->se_header);
724 if (se->se_width) { 783 if (symbol_conf.col_width_list_str) {
725 if (symbol_conf.col_width_list_str) { 784 if (col_width) {
726 if (col_width) { 785 hists__set_col_len(self, se->se_width_idx,
727 *se->se_width = atoi(col_width); 786 atoi(col_width));
728 col_width = strchr(col_width, ','); 787 col_width = strchr(col_width, ',');
729 if (col_width) 788 if (col_width)
730 ++col_width; 789 ++col_width;
731 }
732 } 790 }
733 width = *se->se_width = max(*se->se_width, width);
734 } 791 }
792 if (!hists__new_col_len(self, se->se_width_idx, width))
793 width = hists__col_len(self, se->se_width_idx);
735 fprintf(fp, " %*s", width, se->se_header); 794 fprintf(fp, " %*s", width, se->se_header);
736 } 795 }
737 fprintf(fp, "\n"); 796 fprintf(fp, "\n");
@@ -754,9 +813,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
754 continue; 813 continue;
755 814
756 fprintf(fp, " "); 815 fprintf(fp, " ");
757 if (se->se_width) 816 width = hists__col_len(self, se->se_width_idx);
758 width = *se->se_width; 817 if (width == 0)
759 else
760 width = strlen(se->se_header); 818 width = strlen(se->se_header);
761 for (i = 0; i < width; i++) 819 for (i = 0; i < width; i++)
762 fprintf(fp, "."); 820 fprintf(fp, ".");
@@ -767,7 +825,6 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
767print_entries: 825print_entries:
768 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 826 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
769 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 827 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
770 int cnt;
771 828
772 if (show_displacement) { 829 if (show_displacement) {
773 if (h->pair != NULL) 830 if (h->pair != NULL)
@@ -777,17 +834,12 @@ print_entries:
777 displacement = 0; 834 displacement = 0;
778 ++position; 835 ++position;
779 } 836 }
780 cnt = hist_entry__fprintf(h, pair, show_displacement, 837 ret += hist_entry__fprintf(h, self, pair, show_displacement,
781 displacement, fp, self->stats.total_period); 838 displacement, fp, self->stats.total_period);
782 /* Ignore those that didn't match the parent filter */
783 if (!cnt)
784 continue;
785
786 ret += cnt;
787 839
788 if (symbol_conf.use_callchain) 840 if (symbol_conf.use_callchain)
789 ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); 841 ret += hist_entry__fprintf_callchain(h, self, fp,
790 842 self->stats.total_period);
791 if (h->ms.map == NULL && verbose > 1) { 843 if (h->ms.map == NULL && verbose > 1) {
792 __map_groups__fprintf_maps(&h->thread->mg, 844 __map_groups__fprintf_maps(&h->thread->mg,
793 MAP__FUNCTION, verbose, fp); 845 MAP__FUNCTION, verbose, fp);
@@ -800,10 +852,52 @@ print_entries:
800 return ret; 852 return ret;
801} 853}
802 854
803enum hist_filter { 855/*
804 HIST_FILTER__DSO, 856 * See hists__fprintf to match the column widths
805 HIST_FILTER__THREAD, 857 */
806}; 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}
807 901
808void hists__filter_by_dso(struct hists *self, const struct dso *dso) 902void hists__filter_by_dso(struct hists *self, const struct dso *dso)
809{ 903{
@@ -811,7 +905,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso)
811 905
812 self->nr_entries = self->stats.total_period = 0; 906 self->nr_entries = self->stats.total_period = 0;
813 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 907 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
814 self->max_sym_namelen = 0; 908 hists__reset_col_len(self);
815 909
816 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 910 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
817 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);
@@ -824,15 +918,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso)
824 continue; 918 continue;
825 } 919 }
826 920
827 h->filtered &= ~(1 << HIST_FILTER__DSO); 921 hists__remove_entry_filter(self, h, HIST_FILTER__DSO);
828 if (!h->filtered) {
829 ++self->nr_entries;
830 self->stats.total_period += h->period;
831 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
832 if (h->ms.sym &&
833 self->max_sym_namelen < h->ms.sym->namelen)
834 self->max_sym_namelen = h->ms.sym->namelen;
835 }
836 } 922 }
837} 923}
838 924
@@ -842,7 +928,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread)
842 928
843 self->nr_entries = self->stats.total_period = 0; 929 self->nr_entries = self->stats.total_period = 0;
844 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 930 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
845 self->max_sym_namelen = 0; 931 hists__reset_col_len(self);
846 932
847 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 933 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
848 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);
@@ -851,15 +937,8 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread)
851 h->filtered |= (1 << HIST_FILTER__THREAD); 937 h->filtered |= (1 << HIST_FILTER__THREAD);
852 continue; 938 continue;
853 } 939 }
854 h->filtered &= ~(1 << HIST_FILTER__THREAD); 940
855 if (!h->filtered) { 941 hists__remove_entry_filter(self, h, HIST_FILTER__THREAD);
856 ++self->nr_entries;
857 self->stats.total_period += h->period;
858 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
859 if (h->ms.sym &&
860 self->max_sym_namelen < h->ms.sym->namelen)
861 self->max_sym_namelen = h->ms.sym->namelen;
862 }
863 } 942 }
864} 943}
865 944
@@ -904,9 +983,9 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
904 return 0; 983 return 0;
905} 984}
906 985
907static struct objdump_line *objdump_line__new(s64 offset, char *line) 986static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
908{ 987{
909 struct objdump_line *self = malloc(sizeof(*self)); 988 struct objdump_line *self = malloc(sizeof(*self) + privsize);
910 989
911 if (self != NULL) { 990 if (self != NULL) {
912 self->offset = offset; 991 self->offset = offset;
@@ -938,7 +1017,7 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
938} 1017}
939 1018
940static 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,
941 struct list_head *head) 1020 struct list_head *head, size_t privsize)
942{ 1021{
943 struct symbol *sym = self->ms.sym; 1022 struct symbol *sym = self->ms.sym;
944 struct objdump_line *objdump_line; 1023 struct objdump_line *objdump_line;
@@ -989,7 +1068,7 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
989 offset = -1; 1068 offset = -1;
990 } 1069 }
991 1070
992 objdump_line = objdump_line__new(offset, line); 1071 objdump_line = objdump_line__new(offset, line, privsize);
993 if (objdump_line == NULL) { 1072 if (objdump_line == NULL) {
994 free(line); 1073 free(line);
995 return -1; 1074 return -1;
@@ -999,7 +1078,8 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
999 return 0; 1078 return 0;
1000} 1079}
1001 1080
1002int 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)
1003{ 1083{
1004 struct symbol *sym = self->ms.sym; 1084 struct symbol *sym = self->ms.sym;
1005 struct map *map = self->ms.map; 1085 struct map *map = self->ms.map;
@@ -1052,7 +1132,7 @@ fallback:
1052 dso, dso->long_name, sym, sym->name); 1132 dso, dso->long_name, sym, sym->name);
1053 1133
1054 snprintf(command, sizeof(command), 1134 snprintf(command, sizeof(command),
1055 "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",
1056 map__rip_2objdump(map, sym->start), 1136 map__rip_2objdump(map, sym->start),
1057 map__rip_2objdump(map, sym->end), 1137 map__rip_2objdump(map, sym->end),
1058 filename, filename); 1138 filename, filename);
@@ -1064,7 +1144,7 @@ fallback:
1064 goto out_free_filename; 1144 goto out_free_filename;
1065 1145
1066 while (!feof(file)) 1146 while (!feof(file))
1067 if (hist_entry__parse_objdump_line(self, file, head) < 0) 1147 if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
1068 break; 1148 break;
1069 1149
1070 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 7537ca15900b..000000000000
--- a/tools/perf/util/newt.c
+++ /dev/null
@@ -1,1178 +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
47 if (use_browser <= 0)
48 return self;
49 newtGetScreenSize(&cols, NULL);
50 cols -= 4;
51 newtCenteredWindow(cols, 1, title);
52 self->form = newtForm(NULL, NULL, 0);
53 if (self->form == NULL)
54 goto out_free_self;
55 self->scale = newtScale(0, 0, cols, total);
56 if (self->scale == NULL)
57 goto out_free_form;
58 newtFormAddComponent(self->form, self->scale);
59 newtRefresh();
60 }
61
62 return self;
63
64out_free_form:
65 newtFormDestroy(self->form);
66out_free_self:
67 free(self);
68 return NULL;
69}
70
71void ui_progress__update(struct ui_progress *self, u64 curr)
72{
73 /*
74 * FIXME: We should have a per UI backend way of showing progress,
75 * stdio will just show a percentage as NN%, etc.
76 */
77 if (use_browser <= 0)
78 return;
79 newtScaleSet(self->scale, curr);
80 newtRefresh();
81}
82
83void ui_progress__delete(struct ui_progress *self)
84{
85 if (use_browser > 0) {
86 newtFormDestroy(self->form);
87 newtPopWindow();
88 }
89 free(self);
90}
91
92static void ui_helpline__pop(void)
93{
94 newtPopHelpLine();
95}
96
97static void ui_helpline__push(const char *msg)
98{
99 newtPushHelpLine(msg);
100}
101
102static void ui_helpline__vpush(const char *fmt, va_list ap)
103{
104 char *s;
105
106 if (vasprintf(&s, fmt, ap) < 0)
107 vfprintf(stderr, fmt, ap);
108 else {
109 ui_helpline__push(s);
110 free(s);
111 }
112}
113
114static void ui_helpline__fpush(const char *fmt, ...)
115{
116 va_list ap;
117
118 va_start(ap, fmt);
119 ui_helpline__vpush(fmt, ap);
120 va_end(ap);
121}
122
123static void ui_helpline__puts(const char *msg)
124{
125 ui_helpline__pop();
126 ui_helpline__push(msg);
127}
128
129static char browser__last_msg[1024];
130
131int browser__show_help(const char *format, va_list ap)
132{
133 int ret;
134 static int backlog;
135
136 ret = vsnprintf(browser__last_msg + backlog,
137 sizeof(browser__last_msg) - backlog, format, ap);
138 backlog += ret;
139
140 if (browser__last_msg[backlog - 1] == '\n') {
141 ui_helpline__puts(browser__last_msg);
142 newtRefresh();
143 backlog = 0;
144 }
145
146 return ret;
147}
148
149static void newt_form__set_exit_keys(newtComponent self)
150{
151 newtFormAddHotKey(self, NEWT_KEY_LEFT);
152 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
153 newtFormAddHotKey(self, 'Q');
154 newtFormAddHotKey(self, 'q');
155 newtFormAddHotKey(self, CTRL('c'));
156}
157
158static newtComponent newt_form__new(void)
159{
160 newtComponent self = newtForm(NULL, NULL, 0);
161 if (self)
162 newt_form__set_exit_keys(self);
163 return self;
164}
165
166static int popup_menu(int argc, char * const argv[])
167{
168 struct newtExitStruct es;
169 int i, rc = -1, max_len = 5;
170 newtComponent listbox, form = newt_form__new();
171
172 if (form == NULL)
173 return -1;
174
175 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
176 if (listbox == NULL)
177 goto out_destroy_form;
178
179 newtFormAddComponent(form, listbox);
180
181 for (i = 0; i < argc; ++i) {
182 int len = strlen(argv[i]);
183 if (len > max_len)
184 max_len = len;
185 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
186 goto out_destroy_form;
187 }
188
189 newtCenteredWindow(max_len, argc, NULL);
190 newtFormRun(form, &es);
191 rc = newtListboxGetCurrent(listbox) - NULL;
192 if (es.reason == NEWT_EXIT_HOTKEY)
193 rc = -1;
194 newtPopWindow();
195out_destroy_form:
196 newtFormDestroy(form);
197 return rc;
198}
199
200static int ui__help_window(const char *text)
201{
202 struct newtExitStruct es;
203 newtComponent tb, form = newt_form__new();
204 int rc = -1;
205 int max_len = 0, nr_lines = 0;
206 const char *t;
207
208 if (form == NULL)
209 return -1;
210
211 t = text;
212 while (1) {
213 const char *sep = strchr(t, '\n');
214 int len;
215
216 if (sep == NULL)
217 sep = strchr(t, '\0');
218 len = sep - t;
219 if (max_len < len)
220 max_len = len;
221 ++nr_lines;
222 if (*sep == '\0')
223 break;
224 t = sep + 1;
225 }
226
227 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
228 if (tb == NULL)
229 goto out_destroy_form;
230
231 newtTextboxSetText(tb, text);
232 newtFormAddComponent(form, tb);
233 newtCenteredWindow(max_len, nr_lines, NULL);
234 newtFormRun(form, &es);
235 newtPopWindow();
236 rc = 0;
237out_destroy_form:
238 newtFormDestroy(form);
239 return rc;
240}
241
242static bool dialog_yesno(const char *msg)
243{
244 /* newtWinChoice should really be accepting const char pointers... */
245 char yes[] = "Yes", no[] = "No";
246 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
247}
248
249static void ui__error_window(const char *fmt, ...)
250{
251 va_list ap;
252
253 va_start(ap, fmt);
254 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
255 va_end(ap);
256}
257
258#define HE_COLORSET_TOP 50
259#define HE_COLORSET_MEDIUM 51
260#define HE_COLORSET_NORMAL 52
261#define HE_COLORSET_SELECTED 53
262#define HE_COLORSET_CODE 54
263
264static int ui_browser__percent_color(double percent, bool current)
265{
266 if (current)
267 return HE_COLORSET_SELECTED;
268 if (percent >= MIN_RED)
269 return HE_COLORSET_TOP;
270 if (percent >= MIN_GREEN)
271 return HE_COLORSET_MEDIUM;
272 return HE_COLORSET_NORMAL;
273}
274
275struct ui_browser {
276 newtComponent form, sb;
277 u64 index, first_visible_entry_idx;
278 void *first_visible_entry, *entries;
279 u16 top, left, width, height;
280 void *priv;
281 u32 nr_entries;
282};
283
284static void ui_browser__refresh_dimensions(struct ui_browser *self)
285{
286 int cols, rows;
287 newtGetScreenSize(&cols, &rows);
288
289 if (self->width > cols - 4)
290 self->width = cols - 4;
291 self->height = rows - 5;
292 if (self->height > self->nr_entries)
293 self->height = self->nr_entries;
294 self->top = (rows - self->height) / 2;
295 self->left = (cols - self->width) / 2;
296}
297
298static void ui_browser__reset_index(struct ui_browser *self)
299{
300 self->index = self->first_visible_entry_idx = 0;
301 self->first_visible_entry = NULL;
302}
303
304static int objdump_line__show(struct objdump_line *self, struct list_head *head,
305 int width, struct hist_entry *he, int len,
306 bool current_entry)
307{
308 if (self->offset != -1) {
309 struct symbol *sym = he->ms.sym;
310 unsigned int hits = 0;
311 double percent = 0.0;
312 int color;
313 struct sym_priv *priv = symbol__priv(sym);
314 struct sym_ext *sym_ext = priv->ext;
315 struct sym_hist *h = priv->hist;
316 s64 offset = self->offset;
317 struct objdump_line *next = objdump__get_next_ip_line(head, self);
318
319 while (offset < (s64)len &&
320 (next == NULL || offset < next->offset)) {
321 if (sym_ext) {
322 percent += sym_ext[offset].percent;
323 } else
324 hits += h->ip[offset];
325
326 ++offset;
327 }
328
329 if (sym_ext == NULL && h->sum)
330 percent = 100.0 * hits / h->sum;
331
332 color = ui_browser__percent_color(percent, current_entry);
333 SLsmg_set_color(color);
334 slsmg_printf(" %7.2f ", percent);
335 if (!current_entry)
336 SLsmg_set_color(HE_COLORSET_CODE);
337 } else {
338 int color = ui_browser__percent_color(0, current_entry);
339 SLsmg_set_color(color);
340 slsmg_write_nstring(" ", 9);
341 }
342
343 SLsmg_write_char(':');
344 slsmg_write_nstring(" ", 8);
345 if (!*self->line)
346 slsmg_write_nstring(" ", width - 18);
347 else
348 slsmg_write_nstring(self->line, width - 18);
349
350 return 0;
351}
352
353static int ui_browser__refresh_entries(struct ui_browser *self)
354{
355 struct objdump_line *pos;
356 struct list_head *head = self->entries;
357 struct hist_entry *he = self->priv;
358 int row = 0;
359 int len = he->ms.sym->end - he->ms.sym->start;
360
361 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
362 self->first_visible_entry = head->next;
363
364 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
365
366 list_for_each_entry_from(pos, head, node) {
367 bool current_entry = (self->first_visible_entry_idx + row) == self->index;
368 SLsmg_gotorc(self->top + row, self->left);
369 objdump_line__show(pos, head, self->width,
370 he, len, current_entry);
371 if (++row == self->height)
372 break;
373 }
374
375 SLsmg_set_color(HE_COLORSET_NORMAL);
376 SLsmg_fill_region(self->top + row, self->left,
377 self->height - row, self->width, ' ');
378
379 return 0;
380}
381
382static int ui_browser__run(struct ui_browser *self, const char *title,
383 struct newtExitStruct *es)
384{
385 if (self->form) {
386 newtFormDestroy(self->form);
387 newtPopWindow();
388 }
389
390 ui_browser__refresh_dimensions(self);
391 newtCenteredWindow(self->width + 2, self->height, title);
392 self->form = newt_form__new();
393 if (self->form == NULL)
394 return -1;
395
396 self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
397 HE_COLORSET_NORMAL,
398 HE_COLORSET_SELECTED);
399 if (self->sb == NULL)
400 return -1;
401
402 newtFormAddHotKey(self->form, NEWT_KEY_UP);
403 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
404 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
405 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
406 newtFormAddHotKey(self->form, ' ');
407 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
408 newtFormAddHotKey(self->form, NEWT_KEY_END);
409 newtFormAddHotKey(self->form, NEWT_KEY_TAB);
410 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
411
412 if (ui_browser__refresh_entries(self) < 0)
413 return -1;
414 newtFormAddComponent(self->form, self->sb);
415
416 while (1) {
417 unsigned int offset;
418
419 newtFormRun(self->form, es);
420
421 if (es->reason != NEWT_EXIT_HOTKEY)
422 break;
423 if (is_exit_key(es->u.key))
424 return es->u.key;
425 switch (es->u.key) {
426 case NEWT_KEY_DOWN:
427 if (self->index == self->nr_entries - 1)
428 break;
429 ++self->index;
430 if (self->index == self->first_visible_entry_idx + self->height) {
431 struct list_head *pos = self->first_visible_entry;
432 ++self->first_visible_entry_idx;
433 self->first_visible_entry = pos->next;
434 }
435 break;
436 case NEWT_KEY_UP:
437 if (self->index == 0)
438 break;
439 --self->index;
440 if (self->index < self->first_visible_entry_idx) {
441 struct list_head *pos = self->first_visible_entry;
442 --self->first_visible_entry_idx;
443 self->first_visible_entry = pos->prev;
444 }
445 break;
446 case NEWT_KEY_PGDN:
447 case ' ':
448 if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
449 break;
450
451 offset = self->height;
452 if (self->index + offset > self->nr_entries - 1)
453 offset = self->nr_entries - 1 - self->index;
454 self->index += offset;
455 self->first_visible_entry_idx += offset;
456
457 while (offset--) {
458 struct list_head *pos = self->first_visible_entry;
459 self->first_visible_entry = pos->next;
460 }
461
462 break;
463 case NEWT_KEY_PGUP:
464 if (self->first_visible_entry_idx == 0)
465 break;
466
467 if (self->first_visible_entry_idx < self->height)
468 offset = self->first_visible_entry_idx;
469 else
470 offset = self->height;
471
472 self->index -= offset;
473 self->first_visible_entry_idx -= offset;
474
475 while (offset--) {
476 struct list_head *pos = self->first_visible_entry;
477 self->first_visible_entry = pos->prev;
478 }
479 break;
480 case NEWT_KEY_HOME:
481 ui_browser__reset_index(self);
482 break;
483 case NEWT_KEY_END: {
484 struct list_head *head = self->entries;
485 offset = self->height - 1;
486
487 if (offset > self->nr_entries)
488 offset = self->nr_entries;
489
490 self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
491 self->first_visible_entry = head->prev;
492 while (offset-- != 0) {
493 struct list_head *pos = self->first_visible_entry;
494 self->first_visible_entry = pos->prev;
495 }
496 }
497 break;
498 case NEWT_KEY_RIGHT:
499 case NEWT_KEY_LEFT:
500 case NEWT_KEY_TAB:
501 return es->u.key;
502 default:
503 continue;
504 }
505 if (ui_browser__refresh_entries(self) < 0)
506 return -1;
507 }
508 return 0;
509}
510
511/*
512 * When debugging newt problems it was useful to be able to "unroll"
513 * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
514 * a source file with the sequence of calls to these methods, to then
515 * tweak the arrays to get the intended results, so I'm keeping this code
516 * here, may be useful again in the future.
517 */
518#undef NEWT_DEBUG
519
520static void newt_checkbox_tree__add(newtComponent tree, const char *str,
521 void *priv, int *indexes)
522{
523#ifdef NEWT_DEBUG
524 /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
525 int i = 0, len = 40 - strlen(str);
526
527 fprintf(stderr,
528 "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
529 len, len, " ", str, priv);
530 while (indexes[i] != NEWT_ARG_LAST) {
531 if (indexes[i] != NEWT_ARG_APPEND)
532 fprintf(stderr, " %d,", indexes[i]);
533 else
534 fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
535 ++i;
536 }
537 fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
538 fflush(stderr);
539#endif
540 newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
541}
542
543static char *callchain_list__sym_name(struct callchain_list *self,
544 char *bf, size_t bfsize)
545{
546 if (self->ms.sym)
547 return self->ms.sym->name;
548
549 snprintf(bf, bfsize, "%#Lx", self->ip);
550 return bf;
551}
552
553static void __callchain__append_graph_browser(struct callchain_node *self,
554 newtComponent tree, u64 total,
555 int *indexes, int depth)
556{
557 struct rb_node *node;
558 u64 new_total, remaining;
559 int idx = 0;
560
561 if (callchain_param.mode == CHAIN_GRAPH_REL)
562 new_total = self->children_hit;
563 else
564 new_total = total;
565
566 remaining = new_total;
567 node = rb_first(&self->rb_root);
568 while (node) {
569 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
570 struct rb_node *next = rb_next(node);
571 u64 cumul = cumul_hits(child);
572 struct callchain_list *chain;
573 int first = true, printed = 0;
574 int chain_idx = -1;
575 remaining -= cumul;
576
577 indexes[depth] = NEWT_ARG_APPEND;
578 indexes[depth + 1] = NEWT_ARG_LAST;
579
580 list_for_each_entry(chain, &child->val, list) {
581 char ipstr[BITS_PER_LONG / 4 + 1],
582 *alloc_str = NULL;
583 const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
584
585 if (first) {
586 double percent = cumul * 100.0 / new_total;
587
588 first = false;
589 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
590 str = "Not enough memory!";
591 else
592 str = alloc_str;
593 } else {
594 indexes[depth] = idx;
595 indexes[depth + 1] = NEWT_ARG_APPEND;
596 indexes[depth + 2] = NEWT_ARG_LAST;
597 ++chain_idx;
598 }
599 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
600 free(alloc_str);
601 ++printed;
602 }
603
604 indexes[depth] = idx;
605 if (chain_idx != -1)
606 indexes[depth + 1] = chain_idx;
607 if (printed != 0)
608 ++idx;
609 __callchain__append_graph_browser(child, tree, new_total, indexes,
610 depth + (chain_idx != -1 ? 2 : 1));
611 node = next;
612 }
613}
614
615static void callchain__append_graph_browser(struct callchain_node *self,
616 newtComponent tree, u64 total,
617 int *indexes, int parent_idx)
618{
619 struct callchain_list *chain;
620 int i = 0;
621
622 indexes[1] = NEWT_ARG_APPEND;
623 indexes[2] = NEWT_ARG_LAST;
624
625 list_for_each_entry(chain, &self->val, list) {
626 char ipstr[BITS_PER_LONG / 4 + 1], *str;
627
628 if (chain->ip >= PERF_CONTEXT_MAX)
629 continue;
630
631 if (!i++ && sort__first_dimension == SORT_SYM)
632 continue;
633
634 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
635 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
636 }
637
638 indexes[1] = parent_idx;
639 indexes[2] = NEWT_ARG_APPEND;
640 indexes[3] = NEWT_ARG_LAST;
641 __callchain__append_graph_browser(self, tree, total, indexes, 2);
642}
643
644static void hist_entry__append_callchain_browser(struct hist_entry *self,
645 newtComponent tree, u64 total, int parent_idx)
646{
647 struct rb_node *rb_node;
648 int indexes[1024] = { [0] = parent_idx, };
649 int idx = 0;
650 struct callchain_node *chain;
651
652 rb_node = rb_first(&self->sorted_chain);
653 while (rb_node) {
654 chain = rb_entry(rb_node, struct callchain_node, rb_node);
655 switch (callchain_param.mode) {
656 case CHAIN_FLAT:
657 break;
658 case CHAIN_GRAPH_ABS: /* falldown */
659 case CHAIN_GRAPH_REL:
660 callchain__append_graph_browser(chain, tree, total, indexes, idx++);
661 break;
662 case CHAIN_NONE:
663 default:
664 break;
665 }
666 rb_node = rb_next(rb_node);
667 }
668}
669
670static size_t hist_entry__append_browser(struct hist_entry *self,
671 newtComponent tree, u64 total)
672{
673 char s[256];
674 size_t ret;
675
676 if (symbol_conf.exclude_other && !self->parent)
677 return 0;
678
679 ret = hist_entry__snprintf(self, s, sizeof(s), NULL,
680 false, 0, false, total);
681 if (symbol_conf.use_callchain) {
682 int indexes[2];
683
684 indexes[0] = NEWT_ARG_APPEND;
685 indexes[1] = NEWT_ARG_LAST;
686 newt_checkbox_tree__add(tree, s, &self->ms, indexes);
687 } else
688 newtListboxAppendEntry(tree, s, &self->ms);
689
690 return ret;
691}
692
693int hist_entry__tui_annotate(struct hist_entry *self)
694{
695 struct ui_browser browser;
696 struct newtExitStruct es;
697 struct objdump_line *pos, *n;
698 LIST_HEAD(head);
699 int ret;
700
701 if (self->ms.sym == NULL)
702 return -1;
703
704 if (self->ms.map->dso->annotate_warned)
705 return -1;
706
707 if (hist_entry__annotate(self, &head) < 0) {
708 ui__error_window(browser__last_msg);
709 return -1;
710 }
711
712 ui_helpline__push("Press <- or ESC to exit");
713
714 memset(&browser, 0, sizeof(browser));
715 browser.entries = &head;
716 browser.priv = self;
717 list_for_each_entry(pos, &head, node) {
718 size_t line_len = strlen(pos->line);
719 if (browser.width < line_len)
720 browser.width = line_len;
721 ++browser.nr_entries;
722 }
723
724 browser.width += 18; /* Percentage */
725 ret = ui_browser__run(&browser, self->ms.sym->name, &es);
726 newtFormDestroy(browser.form);
727 newtPopWindow();
728 list_for_each_entry_safe(pos, n, &head, node) {
729 list_del(&pos->node);
730 objdump_line__free(pos);
731 }
732 ui_helpline__pop();
733 return ret;
734}
735
736static const void *newt__symbol_tree_get_current(newtComponent self)
737{
738 if (symbol_conf.use_callchain)
739 return newtCheckboxTreeGetCurrent(self);
740 return newtListboxGetCurrent(self);
741}
742
743static void hist_browser__selection(newtComponent self, void *data)
744{
745 const struct map_symbol **symbol_ptr = data;
746 *symbol_ptr = newt__symbol_tree_get_current(self);
747}
748
749struct hist_browser {
750 newtComponent form, tree;
751 const struct map_symbol *selection;
752};
753
754static struct hist_browser *hist_browser__new(void)
755{
756 struct hist_browser *self = malloc(sizeof(*self));
757
758 if (self != NULL)
759 self->form = NULL;
760
761 return self;
762}
763
764static void hist_browser__delete(struct hist_browser *self)
765{
766 newtFormDestroy(self->form);
767 newtPopWindow();
768 free(self);
769}
770
771static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
772 const char *title)
773{
774 int max_len = 0, idx, cols, rows;
775 struct ui_progress *progress;
776 struct rb_node *nd;
777 u64 curr_hist = 0;
778 char seq[] = ".", unit;
779 char str[256];
780 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
781
782 if (self->form) {
783 newtFormDestroy(self->form);
784 newtPopWindow();
785 }
786
787 nr_events = convert_unit(nr_events, &unit);
788 snprintf(str, sizeof(str), "Events: %lu%c ",
789 nr_events, unit);
790 newtDrawRootText(0, 0, str);
791
792 newtGetScreenSize(NULL, &rows);
793
794 if (symbol_conf.use_callchain)
795 self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
796 NEWT_FLAG_SCROLL);
797 else
798 self->tree = newtListbox(0, 0, rows - 5,
799 (NEWT_FLAG_SCROLL |
800 NEWT_FLAG_RETURNEXIT));
801
802 newtComponentAddCallback(self->tree, hist_browser__selection,
803 &self->selection);
804
805 progress = ui_progress__new("Adding entries to the browser...",
806 hists->nr_entries);
807 if (progress == NULL)
808 return -1;
809
810 idx = 0;
811 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
812 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
813 int len;
814
815 if (h->filtered)
816 continue;
817
818 len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
819 if (len > max_len)
820 max_len = len;
821 if (symbol_conf.use_callchain)
822 hist_entry__append_callchain_browser(h, self->tree,
823 hists->stats.total_period, idx++);
824 ++curr_hist;
825 if (curr_hist % 5)
826 ui_progress__update(progress, curr_hist);
827 }
828
829 ui_progress__delete(progress);
830
831 newtGetScreenSize(&cols, &rows);
832
833 if (max_len > cols)
834 max_len = cols - 3;
835
836 if (!symbol_conf.use_callchain)
837 newtListboxSetWidth(self->tree, max_len);
838
839 newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
840 rows - 5, title);
841 self->form = newt_form__new();
842 if (self->form == NULL)
843 return -1;
844
845 newtFormAddHotKey(self->form, 'A');
846 newtFormAddHotKey(self->form, 'a');
847 newtFormAddHotKey(self->form, 'D');
848 newtFormAddHotKey(self->form, 'd');
849 newtFormAddHotKey(self->form, 'T');
850 newtFormAddHotKey(self->form, 't');
851 newtFormAddHotKey(self->form, '?');
852 newtFormAddHotKey(self->form, 'H');
853 newtFormAddHotKey(self->form, 'h');
854 newtFormAddHotKey(self->form, NEWT_KEY_F1);
855 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
856 newtFormAddHotKey(self->form, NEWT_KEY_TAB);
857 newtFormAddHotKey(self->form, NEWT_KEY_UNTAB);
858 newtFormAddComponents(self->form, self->tree, NULL);
859 self->selection = newt__symbol_tree_get_current(self->tree);
860
861 return 0;
862}
863
864static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
865{
866 int *indexes;
867
868 if (!symbol_conf.use_callchain)
869 goto out;
870
871 indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection);
872 if (indexes) {
873 bool is_hist_entry = indexes[1] == NEWT_ARG_LAST;
874 free(indexes);
875 if (is_hist_entry)
876 goto out;
877 }
878 return NULL;
879out:
880 return container_of(self->selection, struct hist_entry, ms);
881}
882
883static struct thread *hist_browser__selected_thread(struct hist_browser *self)
884{
885 struct hist_entry *he = hist_browser__selected_entry(self);
886 return he ? he->thread : NULL;
887}
888
889static int hist_browser__title(char *bf, size_t size, const char *ev_name,
890 const struct dso *dso, const struct thread *thread)
891{
892 int printed = 0;
893
894 if (thread)
895 printed += snprintf(bf + printed, size - printed,
896 "Thread: %s(%d)",
897 (thread->comm_set ? thread->comm : ""),
898 thread->pid);
899 if (dso)
900 printed += snprintf(bf + printed, size - printed,
901 "%sDSO: %s", thread ? " " : "",
902 dso->short_name);
903 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
904}
905
906int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
907{
908 struct hist_browser *browser = hist_browser__new();
909 struct pstack *fstack;
910 const struct thread *thread_filter = NULL;
911 const struct dso *dso_filter = NULL;
912 struct newtExitStruct es;
913 char msg[160];
914 int key = -1;
915
916 if (browser == NULL)
917 return -1;
918
919 fstack = pstack__new(2);
920 if (fstack == NULL)
921 goto out;
922
923 ui_helpline__push(helpline);
924
925 hist_browser__title(msg, sizeof(msg), ev_name,
926 dso_filter, thread_filter);
927 if (hist_browser__populate(browser, self, msg) < 0)
928 goto out_free_stack;
929
930 while (1) {
931 const struct thread *thread;
932 const struct dso *dso;
933 char *options[16];
934 int nr_options = 0, choice = 0, i,
935 annotate = -2, zoom_dso = -2, zoom_thread = -2;
936
937 newtFormRun(browser->form, &es);
938
939 thread = hist_browser__selected_thread(browser);
940 dso = browser->selection->map ? browser->selection->map->dso : NULL;
941
942 if (es.reason == NEWT_EXIT_HOTKEY) {
943 key = es.u.key;
944
945 switch (key) {
946 case NEWT_KEY_F1:
947 goto do_help;
948 case NEWT_KEY_TAB:
949 case NEWT_KEY_UNTAB:
950 /*
951 * Exit the browser, let hists__browser_tree
952 * go to the next or previous
953 */
954 goto out_free_stack;
955 default:;
956 }
957
958 key = toupper(key);
959 switch (key) {
960 case 'A':
961 if (browser->selection->map == NULL &&
962 browser->selection->map->dso->annotate_warned)
963 continue;
964 goto do_annotate;
965 case 'D':
966 goto zoom_dso;
967 case 'T':
968 goto zoom_thread;
969 case 'H':
970 case '?':
971do_help:
972 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
973 "<- Zoom out\n"
974 "a Annotate current symbol\n"
975 "h/?/F1 Show this window\n"
976 "d Zoom into current DSO\n"
977 "t Zoom into current Thread\n"
978 "q/CTRL+C Exit browser");
979 continue;
980 default:;
981 }
982 if (is_exit_key(key)) {
983 if (key == NEWT_KEY_ESCAPE) {
984 if (dialog_yesno("Do you really want to exit?"))
985 break;
986 else
987 continue;
988 } else
989 break;
990 }
991
992 if (es.u.key == NEWT_KEY_LEFT) {
993 const void *top;
994
995 if (pstack__empty(fstack))
996 continue;
997 top = pstack__pop(fstack);
998 if (top == &dso_filter)
999 goto zoom_out_dso;
1000 if (top == &thread_filter)
1001 goto zoom_out_thread;
1002 continue;
1003 }
1004 }
1005
1006 if (browser->selection->sym != NULL &&
1007 !browser->selection->map->dso->annotate_warned &&
1008 asprintf(&options[nr_options], "Annotate %s",
1009 browser->selection->sym->name) > 0)
1010 annotate = nr_options++;
1011
1012 if (thread != NULL &&
1013 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1014 (thread_filter ? "out of" : "into"),
1015 (thread->comm_set ? thread->comm : ""),
1016 thread->pid) > 0)
1017 zoom_thread = nr_options++;
1018
1019 if (dso != NULL &&
1020 asprintf(&options[nr_options], "Zoom %s %s DSO",
1021 (dso_filter ? "out of" : "into"),
1022 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1023 zoom_dso = nr_options++;
1024
1025 options[nr_options++] = (char *)"Exit";
1026
1027 choice = popup_menu(nr_options, options);
1028
1029 for (i = 0; i < nr_options - 1; ++i)
1030 free(options[i]);
1031
1032 if (choice == nr_options - 1)
1033 break;
1034
1035 if (choice == -1)
1036 continue;
1037
1038 if (choice == annotate) {
1039 struct hist_entry *he;
1040do_annotate:
1041 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
1042 browser->selection->map->dso->annotate_warned = 1;
1043 ui_helpline__puts("No vmlinux file found, can't "
1044 "annotate with just a "
1045 "kallsyms file");
1046 continue;
1047 }
1048
1049 he = hist_browser__selected_entry(browser);
1050 if (he == NULL)
1051 continue;
1052
1053 hist_entry__tui_annotate(he);
1054 } else if (choice == zoom_dso) {
1055zoom_dso:
1056 if (dso_filter) {
1057 pstack__remove(fstack, &dso_filter);
1058zoom_out_dso:
1059 ui_helpline__pop();
1060 dso_filter = NULL;
1061 } else {
1062 if (dso == NULL)
1063 continue;
1064 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1065 dso->kernel ? "the Kernel" : dso->short_name);
1066 dso_filter = dso;
1067 pstack__push(fstack, &dso_filter);
1068 }
1069 hists__filter_by_dso(self, dso_filter);
1070 hist_browser__title(msg, sizeof(msg), ev_name,
1071 dso_filter, thread_filter);
1072 if (hist_browser__populate(browser, self, msg) < 0)
1073 goto out;
1074 } else if (choice == zoom_thread) {
1075zoom_thread:
1076 if (thread_filter) {
1077 pstack__remove(fstack, &thread_filter);
1078zoom_out_thread:
1079 ui_helpline__pop();
1080 thread_filter = NULL;
1081 } else {
1082 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1083 thread->comm_set ? thread->comm : "",
1084 thread->pid);
1085 thread_filter = thread;
1086 pstack__push(fstack, &thread_filter);
1087 }
1088 hists__filter_by_thread(self, thread_filter);
1089 hist_browser__title(msg, sizeof(msg), ev_name,
1090 dso_filter, thread_filter);
1091 if (hist_browser__populate(browser, self, msg) < 0)
1092 goto out;
1093 }
1094 }
1095out_free_stack:
1096 pstack__delete(fstack);
1097out:
1098 hist_browser__delete(browser);
1099 return key;
1100}
1101
1102int hists__tui_browse_tree(struct rb_root *self, const char *help)
1103{
1104 struct rb_node *first = rb_first(self), *nd = first, *next;
1105 int key = 0;
1106
1107 while (nd) {
1108 struct hists *hists = rb_entry(nd, struct hists, rb_node);
1109 const char *ev_name = __event_name(hists->type, hists->config);
1110
1111 key = hists__browse(hists, help, ev_name);
1112
1113 if (is_exit_key(key))
1114 break;
1115
1116 switch (key) {
1117 case NEWT_KEY_TAB:
1118 next = rb_next(nd);
1119 if (next)
1120 nd = next;
1121 break;
1122 case NEWT_KEY_UNTAB:
1123 if (nd == first)
1124 continue;
1125 nd = rb_prev(nd);
1126 default:
1127 break;
1128 }
1129 }
1130
1131 return key;
1132}
1133
1134static struct newtPercentTreeColors {
1135 const char *topColorFg, *topColorBg;
1136 const char *mediumColorFg, *mediumColorBg;
1137 const char *normalColorFg, *normalColorBg;
1138 const char *selColorFg, *selColorBg;
1139 const char *codeColorFg, *codeColorBg;
1140} defaultPercentTreeColors = {
1141 "red", "lightgray",
1142 "green", "lightgray",
1143 "black", "lightgray",
1144 "lightgray", "magenta",
1145 "blue", "lightgray",
1146};
1147
1148void setup_browser(void)
1149{
1150 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
1151
1152 if (!isatty(1) || !use_browser || dump_trace) {
1153 use_browser = 0;
1154 setup_pager();
1155 return;
1156 }
1157
1158 use_browser = 1;
1159 newtInit();
1160 newtCls();
1161 ui_helpline__puts(" ");
1162 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
1163 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
1164 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
1165 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
1166 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
1167}
1168
1169void exit_browser(bool wait_for_ok)
1170{
1171 if (use_browser > 0) {
1172 if (wait_for_ok) {
1173 char title[] = "Fatal Error", ok[] = "Ok";
1174 newtWinMessage(title, ok, browser__last_msg);
1175 }
1176 newtFinished();
1177 }
1178}
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/session.c b/tools/perf/util/session.c
index c422cd676313..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;
@@ -94,8 +102,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc
94 self->hists_tree = RB_ROOT; 102 self->hists_tree = RB_ROOT;
95 self->last_match = NULL; 103 self->last_match = NULL;
96 self->mmap_window = 32; 104 self->mmap_window = 32;
97 self->cwd = NULL;
98 self->cwdlen = 0;
99 self->machines = RB_ROOT; 105 self->machines = RB_ROOT;
100 self->repipe = repipe; 106 self->repipe = repipe;
101 INIT_LIST_HEAD(&self->ordered_samples.samples_head); 107 INIT_LIST_HEAD(&self->ordered_samples.samples_head);
@@ -124,16 +130,43 @@ out_delete:
124 return NULL; 130 return NULL;
125} 131}
126 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
127void perf_session__delete(struct perf_session *self) 156void perf_session__delete(struct perf_session *self)
128{ 157{
129 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);
130 close(self->fd); 163 close(self->fd);
131 free(self->cwd);
132 free(self); 164 free(self);
133} 165}
134 166
135void perf_session__remove_thread(struct perf_session *self, struct thread *th) 167void perf_session__remove_thread(struct perf_session *self, struct thread *th)
136{ 168{
169 self->last_match = NULL;
137 rb_erase(&th->rb_node, &self->threads); 170 rb_erase(&th->rb_node, &self->threads);
138 /* 171 /*
139 * We may have references to this thread, for instance in some hist_entry 172 * We may have references to this thread, for instance in some hist_entry
@@ -830,23 +863,6 @@ int perf_session__process_events(struct perf_session *self,
830 if (perf_session__register_idle_thread(self) == NULL) 863 if (perf_session__register_idle_thread(self) == NULL)
831 return -ENOMEM; 864 return -ENOMEM;
832 865
833 if (!symbol_conf.full_paths) {
834 char bf[PATH_MAX];
835
836 if (getcwd(bf, sizeof(bf)) == NULL) {
837 err = -errno;
838out_getcwd_err:
839 pr_err("failed to get the current directory\n");
840 goto out_err;
841 }
842 self->cwd = strdup(bf);
843 if (self->cwd == NULL) {
844 err = -ENOMEM;
845 goto out_getcwd_err;
846 }
847 self->cwdlen = strlen(self->cwd);
848 }
849
850 if (!self->fd_pipe) 866 if (!self->fd_pipe)
851 err = __perf_session__process_events(self, 867 err = __perf_session__process_events(self,
852 self->header.data_offset, 868 self->header.data_offset,
@@ -854,7 +870,7 @@ out_getcwd_err:
854 self->size, ops); 870 self->size, ops);
855 else 871 else
856 err = __perf_session__process_pipe_events(self, ops); 872 err = __perf_session__process_pipe_events(self, ops);
857out_err: 873
858 return err; 874 return err;
859} 875}
860 876
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 5b276833e2bf..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;
@@ -1494,6 +1600,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self,
1494 goto out; 1600 goto out;
1495 } 1601 }
1496 dso__set_long_name(map->dso, long_name); 1602 dso__set_long_name(map->dso, long_name);
1603 map->dso->lname_alloc = 1;
1497 dso__kernel_module_get_build_id(map->dso, ""); 1604 dso__kernel_module_get_build_id(map->dso, "");
1498 } 1605 }
1499 } 1606 }
@@ -1656,36 +1763,12 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
1656{ 1763{
1657 int err = -1, fd; 1764 int err = -1, fd;
1658 1765
1659 if (self->has_build_id) {
1660 u8 build_id[BUILD_ID_SIZE];
1661
1662 if (filename__read_build_id(vmlinux, build_id,
1663 sizeof(build_id)) < 0) {
1664 pr_debug("No build_id in %s, ignoring it\n", vmlinux);
1665 return -1;
1666 }
1667 if (!dso__build_id_equal(self, build_id)) {
1668 char expected_build_id[BUILD_ID_SIZE * 2 + 1],
1669 vmlinux_build_id[BUILD_ID_SIZE * 2 + 1];
1670
1671 build_id__sprintf(self->build_id,
1672 sizeof(self->build_id),
1673 expected_build_id);
1674 build_id__sprintf(build_id, sizeof(build_id),
1675 vmlinux_build_id);
1676 pr_debug("build_id in %s is %s while expected is %s, "
1677 "ignoring it\n", vmlinux, vmlinux_build_id,
1678 expected_build_id);
1679 return -1;
1680 }
1681 }
1682
1683 fd = open(vmlinux, O_RDONLY); 1766 fd = open(vmlinux, O_RDONLY);
1684 if (fd < 0) 1767 if (fd < 0)
1685 return -1; 1768 return -1;
1686 1769
1687 dso__set_loaded(self, map->type); 1770 dso__set_loaded(self, map->type);
1688 err = dso__load_sym(self, map, vmlinux, fd, filter, 0); 1771 err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
1689 close(fd); 1772 close(fd);
1690 1773
1691 if (err > 0) 1774 if (err > 0)
@@ -2048,6 +2131,36 @@ int __machine__create_kernel_maps(struct machine *self, struct dso *kernel)
2048 return 0; 2131 return 0;
2049} 2132}
2050 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
2051int machine__create_kernel_maps(struct machine *self) 2164int machine__create_kernel_maps(struct machine *self)
2052{ 2165{
2053 struct dso *kernel = machine__create_kernel(self); 2166 struct dso *kernel = machine__create_kernel(self);
@@ -2189,6 +2302,15 @@ out_free_comm_list:
2189 return -1; 2302 return -1;
2190} 2303}
2191 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
2192int machines__create_kernel_maps(struct rb_root *self, pid_t pid) 2314int machines__create_kernel_maps(struct rb_root *self, pid_t pid)
2193{ 2315{
2194 struct machine *machine = machines__findnew(self, pid); 2316 struct machine *machine = machines__findnew(self, pid);
@@ -2283,6 +2405,19 @@ failure:
2283 return ret; 2405 return ret;
2284} 2406}
2285 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
2286int machine__load_kallsyms(struct machine *self, const char *filename, 2421int machine__load_kallsyms(struct machine *self, const char *filename,
2287 enum map_type type, symbol_filter_t filter) 2422 enum map_type type, symbol_filter_t filter)
2288{ 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 9a448b47400c..8c72d888e449 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -62,6 +62,13 @@ static struct thread *thread__new(pid_t pid)
62 return self; 62 return self;
63} 63}
64 64
65void thread__delete(struct thread *self)
66{
67 map_groups__exit(&self->mg);
68 free(self->comm);
69 free(self);
70}
71
65int thread__set_comm(struct thread *self, const char *comm) 72int thread__set_comm(struct thread *self, const char *comm)
66{ 73{
67 int err; 74 int err;
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index ee6bbcf277ca..688500ff826f 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -20,6 +20,8 @@ struct thread {
20 20
21struct perf_session; 21struct perf_session;
22 22
23void thread__delete(struct thread *self);
24
23int find_all_tid(int pid, pid_t ** all_tid); 25int find_all_tid(int pid, pid_t ** all_tid);
24int thread__set_comm(struct thread *self, const char *comm); 26int thread__set_comm(struct thread *self, const char *comm);
25int 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{