diff options
Diffstat (limited to 'tools/perf/util')
78 files changed, 9900 insertions, 1589 deletions
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c new file mode 100644 index 000000000000..04904b35ba81 --- /dev/null +++ b/tools/perf/util/build-id.c | |||
@@ -0,0 +1,39 @@ | |||
1 | /* | ||
2 | * build-id.c | ||
3 | * | ||
4 | * build-id support | ||
5 | * | ||
6 | * Copyright (C) 2009, 2010 Red Hat Inc. | ||
7 | * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com> | ||
8 | */ | ||
9 | #include "build-id.h" | ||
10 | #include "event.h" | ||
11 | #include "symbol.h" | ||
12 | #include <linux/kernel.h> | ||
13 | |||
14 | static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) | ||
15 | { | ||
16 | struct addr_location al; | ||
17 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
18 | struct thread *thread = perf_session__findnew(session, event->ip.pid); | ||
19 | |||
20 | if (thread == NULL) { | ||
21 | pr_err("problem processing %d event, skipping it.\n", | ||
22 | event->header.type); | ||
23 | return -1; | ||
24 | } | ||
25 | |||
26 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, | ||
27 | event->ip.ip, &al); | ||
28 | |||
29 | if (al.map != NULL) | ||
30 | al.map->dso->hit = 1; | ||
31 | |||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | struct perf_event_ops build_id__mark_dso_hit_ops = { | ||
36 | .sample = build_id__mark_dso_hit, | ||
37 | .mmap = event__process_mmap, | ||
38 | .fork = event__process_task, | ||
39 | }; | ||
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h new file mode 100644 index 000000000000..1d981d63cf9a --- /dev/null +++ b/tools/perf/util/build-id.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef PERF_BUILD_ID_H_ | ||
2 | #define PERF_BUILD_ID_H_ 1 | ||
3 | |||
4 | #include "session.h" | ||
5 | |||
6 | extern struct perf_event_ops build_id__mark_dso_hit_ops; | ||
7 | |||
8 | #endif | ||
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 6f8ea9d210b6..918eb376abe3 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -1,10 +1,15 @@ | |||
1 | #ifndef CACHE_H | 1 | #ifndef __PERF_CACHE_H |
2 | #define CACHE_H | 2 | #define __PERF_CACHE_H |
3 | 3 | ||
4 | #include "util.h" | 4 | #include "util.h" |
5 | #include "strbuf.h" | 5 | #include "strbuf.h" |
6 | #include "../perf.h" | 6 | #include "../perf.h" |
7 | 7 | ||
8 | #define CMD_EXEC_PATH "--exec-path" | ||
9 | #define CMD_PERF_DIR "--perf-dir=" | ||
10 | #define CMD_WORK_TREE "--work-tree=" | ||
11 | #define CMD_DEBUGFS_DIR "--debugfs-dir=" | ||
12 | |||
8 | #define PERF_DIR_ENVIRONMENT "PERF_DIR" | 13 | #define PERF_DIR_ENVIRONMENT "PERF_DIR" |
9 | #define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE" | 14 | #define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE" |
10 | #define DEFAULT_PERF_DIR_ENVIRONMENT ".perf" | 15 | #define DEFAULT_PERF_DIR_ENVIRONMENT ".perf" |
@@ -117,4 +122,4 @@ extern char *perf_pathdup(const char *fmt, ...) | |||
117 | 122 | ||
118 | extern size_t strlcpy(char *dest, const char *src, size_t size); | 123 | extern size_t strlcpy(char *dest, const char *src, size_t size); |
119 | 124 | ||
120 | #endif /* CACHE_H */ | 125 | #endif /* __PERF_CACHE_H */ |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 3b8380f1b478..b3b71258272a 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -206,7 +206,7 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, | |||
206 | } | 206 | } |
207 | node->val_nr = chain->nr - start; | 207 | node->val_nr = chain->nr - start; |
208 | if (!node->val_nr) | 208 | if (!node->val_nr) |
209 | printf("Warning: empty node in callchain tree\n"); | 209 | pr_warning("Warning: empty node in callchain tree\n"); |
210 | } | 210 | } |
211 | 211 | ||
212 | static void | 212 | static void |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 43cf3ea9e088..ad4626de4c2b 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -58,4 +58,4 @@ static inline u64 cumul_hits(struct callchain_node *node) | |||
58 | int register_callchain_param(struct callchain_param *param); | 58 | int register_callchain_param(struct callchain_param *param); |
59 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | 59 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, |
60 | struct symbol **syms); | 60 | struct symbol **syms); |
61 | #endif | 61 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 58d597564b99..24e8809210bb 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef COLOR_H | 1 | #ifndef __PERF_COLOR_H |
2 | #define COLOR_H | 2 | #define __PERF_COLOR_H |
3 | 3 | ||
4 | /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ | 4 | /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ |
5 | #define COLOR_MAXLEN 24 | 5 | #define COLOR_MAXLEN 24 |
@@ -39,4 +39,4 @@ int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *bu | |||
39 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); | 39 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); |
40 | const char *get_percent_color(double percent); | 40 | const char *get_percent_color(double percent); |
41 | 41 | ||
42 | #endif /* COLOR_H */ | 42 | #endif /* __PERF_COLOR_H */ |
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c new file mode 100644 index 000000000000..4e01490e51e5 --- /dev/null +++ b/tools/perf/util/cpumap.c | |||
@@ -0,0 +1,59 @@ | |||
1 | #include "util.h" | ||
2 | #include "../perf.h" | ||
3 | #include "cpumap.h" | ||
4 | #include <assert.h> | ||
5 | #include <stdio.h> | ||
6 | |||
7 | int cpumap[MAX_NR_CPUS]; | ||
8 | |||
9 | static int default_cpu_map(void) | ||
10 | { | ||
11 | int nr_cpus, i; | ||
12 | |||
13 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | ||
14 | assert(nr_cpus <= MAX_NR_CPUS); | ||
15 | assert((int)nr_cpus >= 0); | ||
16 | |||
17 | for (i = 0; i < nr_cpus; ++i) | ||
18 | cpumap[i] = i; | ||
19 | |||
20 | return nr_cpus; | ||
21 | } | ||
22 | |||
23 | int read_cpu_map(void) | ||
24 | { | ||
25 | FILE *onlnf; | ||
26 | int nr_cpus = 0; | ||
27 | int n, cpu, prev; | ||
28 | char sep; | ||
29 | |||
30 | onlnf = fopen("/sys/devices/system/cpu/online", "r"); | ||
31 | if (!onlnf) | ||
32 | return default_cpu_map(); | ||
33 | |||
34 | sep = 0; | ||
35 | prev = -1; | ||
36 | for (;;) { | ||
37 | n = fscanf(onlnf, "%u%c", &cpu, &sep); | ||
38 | if (n <= 0) | ||
39 | break; | ||
40 | if (prev >= 0) { | ||
41 | assert(nr_cpus + cpu - prev - 1 < MAX_NR_CPUS); | ||
42 | while (++prev < cpu) | ||
43 | cpumap[nr_cpus++] = prev; | ||
44 | } | ||
45 | assert (nr_cpus < MAX_NR_CPUS); | ||
46 | cpumap[nr_cpus++] = cpu; | ||
47 | if (n == 2 && sep == '-') | ||
48 | prev = cpu; | ||
49 | else | ||
50 | prev = -1; | ||
51 | if (n == 1 || sep == '\n') | ||
52 | break; | ||
53 | } | ||
54 | fclose(onlnf); | ||
55 | if (nr_cpus > 0) | ||
56 | return nr_cpus; | ||
57 | |||
58 | return default_cpu_map(); | ||
59 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h new file mode 100644 index 000000000000..86c78bb33098 --- /dev/null +++ b/tools/perf/util/cpumap.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef __PERF_CPUMAP_H | ||
2 | #define __PERF_CPUMAP_H | ||
3 | |||
4 | extern int read_cpu_map(void); | ||
5 | extern int cpumap[]; | ||
6 | |||
7 | #endif /* __PERF_CPUMAP_H */ | ||
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c index 0b791bd346bc..35073621e5de 100644 --- a/tools/perf/util/ctype.c +++ b/tools/perf/util/ctype.c | |||
@@ -29,3 +29,11 @@ unsigned char sane_ctype[256] = { | |||
29 | A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */ | 29 | A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */ |
30 | /* Nothing in the 128.. range */ | 30 | /* Nothing in the 128.. range */ |
31 | }; | 31 | }; |
32 | |||
33 | const char *graph_line = | ||
34 | "_____________________________________________________________________" | ||
35 | "_____________________________________________________________________"; | ||
36 | const char *graph_dotted_line = | ||
37 | "---------------------------------------------------------------------" | ||
38 | "---------------------------------------------------------------------" | ||
39 | "---------------------------------------------------------------------"; | ||
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index e8ca98fe0bd4..0905600c3851 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -9,16 +9,17 @@ | |||
9 | #include "color.h" | 9 | #include "color.h" |
10 | #include "event.h" | 10 | #include "event.h" |
11 | #include "debug.h" | 11 | #include "debug.h" |
12 | #include "util.h" | ||
12 | 13 | ||
13 | int verbose = 0; | 14 | int verbose = 0; |
14 | int dump_trace = 0; | 15 | int dump_trace = 0; |
15 | 16 | ||
16 | int eprintf(const char *fmt, ...) | 17 | int eprintf(int level, const char *fmt, ...) |
17 | { | 18 | { |
18 | va_list args; | 19 | va_list args; |
19 | int ret = 0; | 20 | int ret = 0; |
20 | 21 | ||
21 | if (verbose) { | 22 | if (verbose >= level) { |
22 | va_start(args, fmt); | 23 | va_start(args, fmt); |
23 | ret = vfprintf(stderr, fmt, args); | 24 | ret = vfprintf(stderr, fmt, args); |
24 | va_end(args); | 25 | va_end(args); |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 437eea58ce40..c6c24c522dea 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -1,8 +1,15 @@ | |||
1 | /* For debugging general purposes */ | 1 | /* For debugging general purposes */ |
2 | #ifndef __PERF_DEBUG_H | ||
3 | #define __PERF_DEBUG_H | ||
4 | |||
5 | #include "event.h" | ||
2 | 6 | ||
3 | extern int verbose; | 7 | extern int verbose; |
4 | extern int dump_trace; | 8 | extern int dump_trace; |
5 | 9 | ||
6 | int eprintf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 10 | int eprintf(int level, |
11 | const char *fmt, ...) __attribute__((format(printf, 2, 3))); | ||
7 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 12 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); |
8 | void trace_event(event_t *event); | 13 | void trace_event(event_t *event); |
14 | |||
15 | #endif /* __PERF_DEBUG_H */ | ||
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c new file mode 100644 index 000000000000..a88fefc0cc0a --- /dev/null +++ b/tools/perf/util/debugfs.c | |||
@@ -0,0 +1,240 @@ | |||
1 | #include "util.h" | ||
2 | #include "debugfs.h" | ||
3 | #include "cache.h" | ||
4 | |||
5 | static int debugfs_premounted; | ||
6 | static char debugfs_mountpoint[MAX_PATH+1]; | ||
7 | |||
8 | static const char *debugfs_known_mountpoints[] = { | ||
9 | "/sys/kernel/debug/", | ||
10 | "/debug/", | ||
11 | 0, | ||
12 | }; | ||
13 | |||
14 | /* use this to force a umount */ | ||
15 | void debugfs_force_cleanup(void) | ||
16 | { | ||
17 | debugfs_find_mountpoint(); | ||
18 | debugfs_premounted = 0; | ||
19 | debugfs_umount(); | ||
20 | } | ||
21 | |||
22 | /* construct a full path to a debugfs element */ | ||
23 | int debugfs_make_path(const char *element, char *buffer, int size) | ||
24 | { | ||
25 | int len; | ||
26 | |||
27 | if (strlen(debugfs_mountpoint) == 0) { | ||
28 | buffer[0] = '\0'; | ||
29 | return -1; | ||
30 | } | ||
31 | |||
32 | len = strlen(debugfs_mountpoint) + strlen(element) + 1; | ||
33 | if (len >= size) | ||
34 | return len+1; | ||
35 | |||
36 | snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element); | ||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | static int debugfs_found; | ||
41 | |||
42 | /* find the path to the mounted debugfs */ | ||
43 | const char *debugfs_find_mountpoint(void) | ||
44 | { | ||
45 | const char **ptr; | ||
46 | char type[100]; | ||
47 | FILE *fp; | ||
48 | |||
49 | if (debugfs_found) | ||
50 | return (const char *) debugfs_mountpoint; | ||
51 | |||
52 | ptr = debugfs_known_mountpoints; | ||
53 | while (*ptr) { | ||
54 | if (debugfs_valid_mountpoint(*ptr) == 0) { | ||
55 | debugfs_found = 1; | ||
56 | strcpy(debugfs_mountpoint, *ptr); | ||
57 | return debugfs_mountpoint; | ||
58 | } | ||
59 | ptr++; | ||
60 | } | ||
61 | |||
62 | /* give up and parse /proc/mounts */ | ||
63 | fp = fopen("/proc/mounts", "r"); | ||
64 | if (fp == NULL) | ||
65 | die("Can't open /proc/mounts for read"); | ||
66 | |||
67 | while (fscanf(fp, "%*s %" | ||
68 | STR(MAX_PATH) | ||
69 | "s %99s %*s %*d %*d\n", | ||
70 | debugfs_mountpoint, type) == 2) { | ||
71 | if (strcmp(type, "debugfs") == 0) | ||
72 | break; | ||
73 | } | ||
74 | fclose(fp); | ||
75 | |||
76 | if (strcmp(type, "debugfs") != 0) | ||
77 | return NULL; | ||
78 | |||
79 | debugfs_found = 1; | ||
80 | |||
81 | return debugfs_mountpoint; | ||
82 | } | ||
83 | |||
84 | /* verify that a mountpoint is actually a debugfs instance */ | ||
85 | |||
86 | int debugfs_valid_mountpoint(const char *debugfs) | ||
87 | { | ||
88 | struct statfs st_fs; | ||
89 | |||
90 | if (statfs(debugfs, &st_fs) < 0) | ||
91 | return -ENOENT; | ||
92 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
93 | return -ENOENT; | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | |||
99 | int debugfs_valid_entry(const char *path) | ||
100 | { | ||
101 | struct stat st; | ||
102 | |||
103 | if (stat(path, &st)) | ||
104 | return -errno; | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | /* mount the debugfs somewhere if it's not mounted */ | ||
110 | |||
111 | char *debugfs_mount(const char *mountpoint) | ||
112 | { | ||
113 | /* see if it's already mounted */ | ||
114 | if (debugfs_find_mountpoint()) { | ||
115 | debugfs_premounted = 1; | ||
116 | return debugfs_mountpoint; | ||
117 | } | ||
118 | |||
119 | /* if not mounted and no argument */ | ||
120 | if (mountpoint == NULL) { | ||
121 | /* see if environment variable set */ | ||
122 | mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT); | ||
123 | /* if no environment variable, use default */ | ||
124 | if (mountpoint == NULL) | ||
125 | mountpoint = "/sys/kernel/debug"; | ||
126 | } | ||
127 | |||
128 | if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0) | ||
129 | return NULL; | ||
130 | |||
131 | /* save the mountpoint */ | ||
132 | strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); | ||
133 | debugfs_found = 1; | ||
134 | |||
135 | return debugfs_mountpoint; | ||
136 | } | ||
137 | |||
138 | /* umount the debugfs */ | ||
139 | |||
140 | int debugfs_umount(void) | ||
141 | { | ||
142 | char umountcmd[128]; | ||
143 | int ret; | ||
144 | |||
145 | /* if it was already mounted, leave it */ | ||
146 | if (debugfs_premounted) | ||
147 | return 0; | ||
148 | |||
149 | /* make sure it's a valid mount point */ | ||
150 | ret = debugfs_valid_mountpoint(debugfs_mountpoint); | ||
151 | if (ret) | ||
152 | return ret; | ||
153 | |||
154 | snprintf(umountcmd, sizeof(umountcmd), | ||
155 | "/bin/umount %s", debugfs_mountpoint); | ||
156 | return system(umountcmd); | ||
157 | } | ||
158 | |||
159 | int debugfs_write(const char *entry, const char *value) | ||
160 | { | ||
161 | char path[MAX_PATH+1]; | ||
162 | int ret, count; | ||
163 | int fd; | ||
164 | |||
165 | /* construct the path */ | ||
166 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
167 | |||
168 | /* verify that it exists */ | ||
169 | ret = debugfs_valid_entry(path); | ||
170 | if (ret) | ||
171 | return ret; | ||
172 | |||
173 | /* get how many chars we're going to write */ | ||
174 | count = strlen(value); | ||
175 | |||
176 | /* open the debugfs entry */ | ||
177 | fd = open(path, O_RDWR); | ||
178 | if (fd < 0) | ||
179 | return -errno; | ||
180 | |||
181 | while (count > 0) { | ||
182 | /* write it */ | ||
183 | ret = write(fd, value, count); | ||
184 | if (ret <= 0) { | ||
185 | if (ret == EAGAIN) | ||
186 | continue; | ||
187 | close(fd); | ||
188 | return -errno; | ||
189 | } | ||
190 | count -= ret; | ||
191 | } | ||
192 | |||
193 | /* close it */ | ||
194 | close(fd); | ||
195 | |||
196 | /* return success */ | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * read a debugfs entry | ||
202 | * returns the number of chars read or a negative errno | ||
203 | */ | ||
204 | int debugfs_read(const char *entry, char *buffer, size_t size) | ||
205 | { | ||
206 | char path[MAX_PATH+1]; | ||
207 | int ret; | ||
208 | int fd; | ||
209 | |||
210 | /* construct the path */ | ||
211 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
212 | |||
213 | /* verify that it exists */ | ||
214 | ret = debugfs_valid_entry(path); | ||
215 | if (ret) | ||
216 | return ret; | ||
217 | |||
218 | /* open the debugfs entry */ | ||
219 | fd = open(path, O_RDONLY); | ||
220 | if (fd < 0) | ||
221 | return -errno; | ||
222 | |||
223 | do { | ||
224 | /* read it */ | ||
225 | ret = read(fd, buffer, size); | ||
226 | if (ret == 0) { | ||
227 | close(fd); | ||
228 | return EOF; | ||
229 | } | ||
230 | } while (ret < 0 && errno == EAGAIN); | ||
231 | |||
232 | /* close it */ | ||
233 | close(fd); | ||
234 | |||
235 | /* make *sure* there's a null character at the end */ | ||
236 | buffer[ret] = '\0'; | ||
237 | |||
238 | /* return the number of chars read */ | ||
239 | return ret; | ||
240 | } | ||
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h new file mode 100644 index 000000000000..83a02879745f --- /dev/null +++ b/tools/perf/util/debugfs.h | |||
@@ -0,0 +1,25 @@ | |||
1 | #ifndef __DEBUGFS_H__ | ||
2 | #define __DEBUGFS_H__ | ||
3 | |||
4 | #include <sys/mount.h> | ||
5 | |||
6 | #ifndef MAX_PATH | ||
7 | # define MAX_PATH 256 | ||
8 | #endif | ||
9 | |||
10 | #ifndef STR | ||
11 | # define _STR(x) #x | ||
12 | # define STR(x) _STR(x) | ||
13 | #endif | ||
14 | |||
15 | extern const char *debugfs_find_mountpoint(void); | ||
16 | extern int debugfs_valid_mountpoint(const char *debugfs); | ||
17 | extern int debugfs_valid_entry(const char *path); | ||
18 | extern char *debugfs_mount(const char *mountpoint); | ||
19 | extern int debugfs_umount(void); | ||
20 | extern int debugfs_write(const char *entry, const char *value); | ||
21 | extern int debugfs_read(const char *entry, char *buffer, size_t size); | ||
22 | extern void debugfs_force_cleanup(void); | ||
23 | extern int debugfs_make_path(const char *element, char *buffer, int size); | ||
24 | |||
25 | #endif /* __DEBUGFS_H__ */ | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c new file mode 100644 index 000000000000..705ec63548b4 --- /dev/null +++ b/tools/perf/util/event.c | |||
@@ -0,0 +1,612 @@ | |||
1 | #include <linux/types.h> | ||
2 | #include "event.h" | ||
3 | #include "debug.h" | ||
4 | #include "session.h" | ||
5 | #include "sort.h" | ||
6 | #include "string.h" | ||
7 | #include "strlist.h" | ||
8 | #include "thread.h" | ||
9 | |||
10 | static pid_t event__synthesize_comm(pid_t pid, int full, | ||
11 | event__handler_t process, | ||
12 | struct perf_session *session) | ||
13 | { | ||
14 | event_t ev; | ||
15 | char filename[PATH_MAX]; | ||
16 | char bf[BUFSIZ]; | ||
17 | FILE *fp; | ||
18 | size_t size = 0; | ||
19 | DIR *tasks; | ||
20 | struct dirent dirent, *next; | ||
21 | pid_t tgid = 0; | ||
22 | |||
23 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); | ||
24 | |||
25 | fp = fopen(filename, "r"); | ||
26 | if (fp == NULL) { | ||
27 | out_race: | ||
28 | /* | ||
29 | * We raced with a task exiting - just return: | ||
30 | */ | ||
31 | pr_debug("couldn't open %s\n", filename); | ||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | memset(&ev.comm, 0, sizeof(ev.comm)); | ||
36 | while (!ev.comm.comm[0] || !ev.comm.pid) { | ||
37 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
38 | goto out_failure; | ||
39 | |||
40 | if (memcmp(bf, "Name:", 5) == 0) { | ||
41 | char *name = bf + 5; | ||
42 | while (*name && isspace(*name)) | ||
43 | ++name; | ||
44 | size = strlen(name) - 1; | ||
45 | memcpy(ev.comm.comm, name, size++); | ||
46 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | ||
47 | char *tgids = bf + 5; | ||
48 | while (*tgids && isspace(*tgids)) | ||
49 | ++tgids; | ||
50 | tgid = ev.comm.pid = atoi(tgids); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | ev.comm.header.type = PERF_RECORD_COMM; | ||
55 | size = ALIGN(size, sizeof(u64)); | ||
56 | ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size); | ||
57 | |||
58 | if (!full) { | ||
59 | ev.comm.tid = pid; | ||
60 | |||
61 | process(&ev, session); | ||
62 | goto out_fclose; | ||
63 | } | ||
64 | |||
65 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | ||
66 | |||
67 | tasks = opendir(filename); | ||
68 | if (tasks == NULL) | ||
69 | goto out_race; | ||
70 | |||
71 | while (!readdir_r(tasks, &dirent, &next) && next) { | ||
72 | char *end; | ||
73 | pid = strtol(dirent.d_name, &end, 10); | ||
74 | if (*end) | ||
75 | continue; | ||
76 | |||
77 | ev.comm.tid = pid; | ||
78 | |||
79 | process(&ev, session); | ||
80 | } | ||
81 | closedir(tasks); | ||
82 | |||
83 | out_fclose: | ||
84 | fclose(fp); | ||
85 | return tgid; | ||
86 | |||
87 | out_failure: | ||
88 | pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); | ||
89 | return -1; | ||
90 | } | ||
91 | |||
92 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | ||
93 | event__handler_t process, | ||
94 | struct perf_session *session) | ||
95 | { | ||
96 | char filename[PATH_MAX]; | ||
97 | FILE *fp; | ||
98 | |||
99 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); | ||
100 | |||
101 | fp = fopen(filename, "r"); | ||
102 | if (fp == NULL) { | ||
103 | /* | ||
104 | * We raced with a task exiting - just return: | ||
105 | */ | ||
106 | pr_debug("couldn't open %s\n", filename); | ||
107 | return -1; | ||
108 | } | ||
109 | |||
110 | while (1) { | ||
111 | char bf[BUFSIZ], *pbf = bf; | ||
112 | event_t ev = { | ||
113 | .header = { | ||
114 | .type = PERF_RECORD_MMAP, | ||
115 | .misc = 0, /* Just like the kernel, see kernel/perf_event.c __perf_event_mmap */ | ||
116 | }, | ||
117 | }; | ||
118 | int n; | ||
119 | size_t size; | ||
120 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
121 | break; | ||
122 | |||
123 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | ||
124 | n = hex2u64(pbf, &ev.mmap.start); | ||
125 | if (n < 0) | ||
126 | continue; | ||
127 | pbf += n + 1; | ||
128 | n = hex2u64(pbf, &ev.mmap.len); | ||
129 | if (n < 0) | ||
130 | continue; | ||
131 | pbf += n + 3; | ||
132 | if (*pbf == 'x') { /* vm_exec */ | ||
133 | char *execname = strchr(bf, '/'); | ||
134 | |||
135 | /* Catch VDSO */ | ||
136 | if (execname == NULL) | ||
137 | execname = strstr(bf, "[vdso]"); | ||
138 | |||
139 | if (execname == NULL) | ||
140 | continue; | ||
141 | |||
142 | size = strlen(execname); | ||
143 | execname[size - 1] = '\0'; /* Remove \n */ | ||
144 | memcpy(ev.mmap.filename, execname, size); | ||
145 | size = ALIGN(size, sizeof(u64)); | ||
146 | ev.mmap.len -= ev.mmap.start; | ||
147 | ev.mmap.header.size = (sizeof(ev.mmap) - | ||
148 | (sizeof(ev.mmap.filename) - size)); | ||
149 | ev.mmap.pid = tgid; | ||
150 | ev.mmap.tid = pid; | ||
151 | |||
152 | process(&ev, session); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | fclose(fp); | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | int event__synthesize_modules(event__handler_t process, | ||
161 | struct perf_session *session) | ||
162 | { | ||
163 | struct rb_node *nd; | ||
164 | |||
165 | for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]); | ||
166 | nd; nd = rb_next(nd)) { | ||
167 | event_t ev; | ||
168 | size_t size; | ||
169 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
170 | |||
171 | if (pos->dso->kernel) | ||
172 | continue; | ||
173 | |||
174 | size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); | ||
175 | memset(&ev, 0, sizeof(ev)); | ||
176 | ev.mmap.header.misc = 1; /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ | ||
177 | ev.mmap.header.type = PERF_RECORD_MMAP; | ||
178 | ev.mmap.header.size = (sizeof(ev.mmap) - | ||
179 | (sizeof(ev.mmap.filename) - size)); | ||
180 | ev.mmap.start = pos->start; | ||
181 | ev.mmap.len = pos->end - pos->start; | ||
182 | |||
183 | memcpy(ev.mmap.filename, pos->dso->long_name, | ||
184 | pos->dso->long_name_len + 1); | ||
185 | process(&ev, session); | ||
186 | } | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | int event__synthesize_thread(pid_t pid, event__handler_t process, | ||
192 | struct perf_session *session) | ||
193 | { | ||
194 | pid_t tgid = event__synthesize_comm(pid, 1, process, session); | ||
195 | if (tgid == -1) | ||
196 | return -1; | ||
197 | return event__synthesize_mmap_events(pid, tgid, process, session); | ||
198 | } | ||
199 | |||
200 | void event__synthesize_threads(event__handler_t process, | ||
201 | struct perf_session *session) | ||
202 | { | ||
203 | DIR *proc; | ||
204 | struct dirent dirent, *next; | ||
205 | |||
206 | proc = opendir("/proc"); | ||
207 | |||
208 | while (!readdir_r(proc, &dirent, &next) && next) { | ||
209 | char *end; | ||
210 | pid_t pid = strtol(dirent.d_name, &end, 10); | ||
211 | |||
212 | if (*end) /* only interested in proper numerical dirents */ | ||
213 | continue; | ||
214 | |||
215 | event__synthesize_thread(pid, process, session); | ||
216 | } | ||
217 | |||
218 | closedir(proc); | ||
219 | } | ||
220 | |||
221 | struct process_symbol_args { | ||
222 | const char *name; | ||
223 | u64 start; | ||
224 | }; | ||
225 | |||
226 | static int find_symbol_cb(void *arg, const char *name, char type, u64 start) | ||
227 | { | ||
228 | struct process_symbol_args *args = arg; | ||
229 | |||
230 | /* | ||
231 | * Must be a function or at least an alias, as in PARISC64, where "_text" is | ||
232 | * an 'A' to the same address as "_stext". | ||
233 | */ | ||
234 | if (!(symbol_type__is_a(type, MAP__FUNCTION) || | ||
235 | type == 'A') || strcmp(name, args->name)) | ||
236 | return 0; | ||
237 | |||
238 | args->start = start; | ||
239 | return 1; | ||
240 | } | ||
241 | |||
242 | int event__synthesize_kernel_mmap(event__handler_t process, | ||
243 | struct perf_session *session, | ||
244 | const char *symbol_name) | ||
245 | { | ||
246 | size_t size; | ||
247 | event_t ev = { | ||
248 | .header = { | ||
249 | .type = PERF_RECORD_MMAP, | ||
250 | .misc = 1, /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ | ||
251 | }, | ||
252 | }; | ||
253 | /* | ||
254 | * We should get this from /sys/kernel/sections/.text, but till that is | ||
255 | * available use this, and after it is use this as a fallback for older | ||
256 | * kernels. | ||
257 | */ | ||
258 | struct process_symbol_args args = { .name = symbol_name, }; | ||
259 | |||
260 | if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0) | ||
261 | return -ENOENT; | ||
262 | |||
263 | size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), | ||
264 | "[kernel.kallsyms.%s]", symbol_name) + 1; | ||
265 | size = ALIGN(size, sizeof(u64)); | ||
266 | ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); | ||
267 | ev.mmap.pgoff = args.start; | ||
268 | ev.mmap.start = session->vmlinux_maps[MAP__FUNCTION]->start; | ||
269 | ev.mmap.len = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ; | ||
270 | |||
271 | return process(&ev, session); | ||
272 | } | ||
273 | |||
274 | static void thread__comm_adjust(struct thread *self) | ||
275 | { | ||
276 | char *comm = self->comm; | ||
277 | |||
278 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
279 | (!symbol_conf.comm_list || | ||
280 | strlist__has_entry(symbol_conf.comm_list, comm))) { | ||
281 | unsigned int slen = strlen(comm); | ||
282 | |||
283 | if (slen > comms__col_width) { | ||
284 | comms__col_width = slen; | ||
285 | threads__col_width = slen + 6; | ||
286 | } | ||
287 | } | ||
288 | } | ||
289 | |||
290 | static int thread__set_comm_adjust(struct thread *self, const char *comm) | ||
291 | { | ||
292 | int ret = thread__set_comm(self, comm); | ||
293 | |||
294 | if (ret) | ||
295 | return ret; | ||
296 | |||
297 | thread__comm_adjust(self); | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | int event__process_comm(event_t *self, struct perf_session *session) | ||
303 | { | ||
304 | struct thread *thread = perf_session__findnew(session, self->comm.pid); | ||
305 | |||
306 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); | ||
307 | |||
308 | if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { | ||
309 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
310 | return -1; | ||
311 | } | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | int event__process_lost(event_t *self, struct perf_session *session) | ||
317 | { | ||
318 | dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); | ||
319 | session->events_stats.lost += self->lost.lost; | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | int event__process_mmap(event_t *self, struct perf_session *session) | ||
324 | { | ||
325 | struct thread *thread; | ||
326 | struct map *map; | ||
327 | |||
328 | dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", | ||
329 | self->mmap.pid, self->mmap.tid, self->mmap.start, | ||
330 | self->mmap.len, self->mmap.pgoff, self->mmap.filename); | ||
331 | |||
332 | if (self->mmap.pid == 0) { | ||
333 | static const char kmmap_prefix[] = "[kernel.kallsyms."; | ||
334 | |||
335 | if (self->mmap.filename[0] == '/') { | ||
336 | char short_module_name[1024]; | ||
337 | char *name = strrchr(self->mmap.filename, '/'), *dot; | ||
338 | |||
339 | if (name == NULL) | ||
340 | goto out_problem; | ||
341 | |||
342 | ++name; /* skip / */ | ||
343 | dot = strrchr(name, '.'); | ||
344 | if (dot == NULL) | ||
345 | goto out_problem; | ||
346 | |||
347 | snprintf(short_module_name, sizeof(short_module_name), | ||
348 | "[%.*s]", (int)(dot - name), name); | ||
349 | strxfrchar(short_module_name, '-', '_'); | ||
350 | |||
351 | map = perf_session__new_module_map(session, | ||
352 | self->mmap.start, | ||
353 | self->mmap.filename); | ||
354 | if (map == NULL) | ||
355 | goto out_problem; | ||
356 | |||
357 | name = strdup(short_module_name); | ||
358 | if (name == NULL) | ||
359 | goto out_problem; | ||
360 | |||
361 | map->dso->short_name = name; | ||
362 | map->end = map->start + self->mmap.len; | ||
363 | } else if (memcmp(self->mmap.filename, kmmap_prefix, | ||
364 | sizeof(kmmap_prefix) - 1) == 0) { | ||
365 | const char *symbol_name = (self->mmap.filename + | ||
366 | sizeof(kmmap_prefix) - 1); | ||
367 | /* | ||
368 | * Should be there already, from the build-id table in | ||
369 | * the header. | ||
370 | */ | ||
371 | struct dso *kernel = __dsos__findnew(&dsos__kernel, | ||
372 | "[kernel.kallsyms]"); | ||
373 | if (kernel == NULL) | ||
374 | goto out_problem; | ||
375 | |||
376 | kernel->kernel = 1; | ||
377 | if (__perf_session__create_kernel_maps(session, kernel) < 0) | ||
378 | goto out_problem; | ||
379 | |||
380 | session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start; | ||
381 | session->vmlinux_maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; | ||
382 | /* | ||
383 | * Be a bit paranoid here, some perf.data file came with | ||
384 | * a zero sized synthesized MMAP event for the kernel. | ||
385 | */ | ||
386 | if (session->vmlinux_maps[MAP__FUNCTION]->end == 0) | ||
387 | session->vmlinux_maps[MAP__FUNCTION]->end = ~0UL; | ||
388 | |||
389 | perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name, | ||
390 | self->mmap.pgoff); | ||
391 | } | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | thread = perf_session__findnew(session, self->mmap.pid); | ||
396 | map = map__new(&self->mmap, MAP__FUNCTION, | ||
397 | session->cwd, session->cwdlen); | ||
398 | |||
399 | if (thread == NULL || map == NULL) | ||
400 | goto out_problem; | ||
401 | |||
402 | thread__insert_map(thread, map); | ||
403 | return 0; | ||
404 | |||
405 | out_problem: | ||
406 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | int event__process_task(event_t *self, struct perf_session *session) | ||
411 | { | ||
412 | struct thread *thread = perf_session__findnew(session, self->fork.pid); | ||
413 | struct thread *parent = perf_session__findnew(session, self->fork.ppid); | ||
414 | |||
415 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, | ||
416 | self->fork.ppid, self->fork.ptid); | ||
417 | /* | ||
418 | * A thread clone will have the same PID for both parent and child. | ||
419 | */ | ||
420 | if (thread == parent) | ||
421 | return 0; | ||
422 | |||
423 | if (self->header.type == PERF_RECORD_EXIT) | ||
424 | return 0; | ||
425 | |||
426 | if (thread == NULL || parent == NULL || | ||
427 | thread__fork(thread, parent) < 0) { | ||
428 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | ||
429 | return -1; | ||
430 | } | ||
431 | |||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | void thread__find_addr_map(struct thread *self, | ||
436 | struct perf_session *session, u8 cpumode, | ||
437 | enum map_type type, u64 addr, | ||
438 | struct addr_location *al) | ||
439 | { | ||
440 | struct map_groups *mg = &self->mg; | ||
441 | |||
442 | al->thread = self; | ||
443 | al->addr = addr; | ||
444 | |||
445 | if (cpumode == PERF_RECORD_MISC_KERNEL) { | ||
446 | al->level = 'k'; | ||
447 | mg = &session->kmaps; | ||
448 | } else if (cpumode == PERF_RECORD_MISC_USER) | ||
449 | al->level = '.'; | ||
450 | else { | ||
451 | al->level = 'H'; | ||
452 | al->map = NULL; | ||
453 | return; | ||
454 | } | ||
455 | try_again: | ||
456 | al->map = map_groups__find(mg, type, al->addr); | ||
457 | if (al->map == NULL) { | ||
458 | /* | ||
459 | * If this is outside of all known maps, and is a negative | ||
460 | * address, try to look it up in the kernel dso, as it might be | ||
461 | * a vsyscall or vdso (which executes in user-mode). | ||
462 | * | ||
463 | * XXX This is nasty, we should have a symbol list in the | ||
464 | * "[vdso]" dso, but for now lets use the old trick of looking | ||
465 | * in the whole kernel symbol list. | ||
466 | */ | ||
467 | if ((long long)al->addr < 0 && mg != &session->kmaps) { | ||
468 | mg = &session->kmaps; | ||
469 | goto try_again; | ||
470 | } | ||
471 | } else | ||
472 | al->addr = al->map->map_ip(al->map, al->addr); | ||
473 | } | ||
474 | |||
475 | void thread__find_addr_location(struct thread *self, | ||
476 | struct perf_session *session, u8 cpumode, | ||
477 | enum map_type type, u64 addr, | ||
478 | struct addr_location *al, | ||
479 | symbol_filter_t filter) | ||
480 | { | ||
481 | thread__find_addr_map(self, session, cpumode, type, addr, al); | ||
482 | if (al->map != NULL) | ||
483 | al->sym = map__find_symbol(al->map, al->addr, filter); | ||
484 | else | ||
485 | al->sym = NULL; | ||
486 | } | ||
487 | |||
488 | static void dso__calc_col_width(struct dso *self) | ||
489 | { | ||
490 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
491 | (!symbol_conf.dso_list || | ||
492 | strlist__has_entry(symbol_conf.dso_list, self->name))) { | ||
493 | unsigned int slen = strlen(self->name); | ||
494 | if (slen > dsos__col_width) | ||
495 | dsos__col_width = slen; | ||
496 | } | ||
497 | |||
498 | self->slen_calculated = 1; | ||
499 | } | ||
500 | |||
501 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | ||
502 | struct addr_location *al, symbol_filter_t filter) | ||
503 | { | ||
504 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
505 | struct thread *thread = perf_session__findnew(session, self->ip.pid); | ||
506 | |||
507 | if (thread == NULL) | ||
508 | return -1; | ||
509 | |||
510 | if (symbol_conf.comm_list && | ||
511 | !strlist__has_entry(symbol_conf.comm_list, thread->comm)) | ||
512 | goto out_filtered; | ||
513 | |||
514 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | ||
515 | |||
516 | thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION, | ||
517 | self->ip.ip, al, filter); | ||
518 | dump_printf(" ...... dso: %s\n", | ||
519 | al->map ? al->map->dso->long_name : | ||
520 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | ||
521 | /* | ||
522 | * We have to do this here as we may have a dso with no symbol hit that | ||
523 | * has a name longer than the ones with symbols sampled. | ||
524 | */ | ||
525 | if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated) | ||
526 | dso__calc_col_width(al->map->dso); | ||
527 | |||
528 | if (symbol_conf.dso_list && | ||
529 | (!al->map || !al->map->dso || | ||
530 | !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) || | ||
531 | (al->map->dso->short_name != al->map->dso->long_name && | ||
532 | strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name))))) | ||
533 | goto out_filtered; | ||
534 | |||
535 | if (symbol_conf.sym_list && al->sym && | ||
536 | !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) | ||
537 | goto out_filtered; | ||
538 | |||
539 | al->filtered = false; | ||
540 | return 0; | ||
541 | |||
542 | out_filtered: | ||
543 | al->filtered = true; | ||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data) | ||
548 | { | ||
549 | u64 *array = event->sample.array; | ||
550 | |||
551 | if (type & PERF_SAMPLE_IP) { | ||
552 | data->ip = event->ip.ip; | ||
553 | array++; | ||
554 | } | ||
555 | |||
556 | if (type & PERF_SAMPLE_TID) { | ||
557 | u32 *p = (u32 *)array; | ||
558 | data->pid = p[0]; | ||
559 | data->tid = p[1]; | ||
560 | array++; | ||
561 | } | ||
562 | |||
563 | if (type & PERF_SAMPLE_TIME) { | ||
564 | data->time = *array; | ||
565 | array++; | ||
566 | } | ||
567 | |||
568 | if (type & PERF_SAMPLE_ADDR) { | ||
569 | data->addr = *array; | ||
570 | array++; | ||
571 | } | ||
572 | |||
573 | if (type & PERF_SAMPLE_ID) { | ||
574 | data->id = *array; | ||
575 | array++; | ||
576 | } | ||
577 | |||
578 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
579 | data->stream_id = *array; | ||
580 | array++; | ||
581 | } | ||
582 | |||
583 | if (type & PERF_SAMPLE_CPU) { | ||
584 | u32 *p = (u32 *)array; | ||
585 | data->cpu = *p; | ||
586 | array++; | ||
587 | } | ||
588 | |||
589 | if (type & PERF_SAMPLE_PERIOD) { | ||
590 | data->period = *array; | ||
591 | array++; | ||
592 | } | ||
593 | |||
594 | if (type & PERF_SAMPLE_READ) { | ||
595 | pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); | ||
596 | return -1; | ||
597 | } | ||
598 | |||
599 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
600 | data->callchain = (struct ip_callchain *)array; | ||
601 | array += 1 + data->callchain->nr; | ||
602 | } | ||
603 | |||
604 | if (type & PERF_SAMPLE_RAW) { | ||
605 | u32 *p = (u32 *)array; | ||
606 | data->raw_size = *p; | ||
607 | p++; | ||
608 | data->raw_data = p; | ||
609 | } | ||
610 | |||
611 | return 0; | ||
612 | } | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 2c9c26d6ded0..a33b94952e34 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -1,14 +1,10 @@ | |||
1 | #ifndef __PERF_RECORD_H | 1 | #ifndef __PERF_RECORD_H |
2 | #define __PERF_RECORD_H | 2 | #define __PERF_RECORD_H |
3 | #include "../perf.h" | ||
4 | #include "util.h" | ||
5 | #include <linux/list.h> | ||
6 | 3 | ||
7 | enum { | 4 | #include <limits.h> |
8 | SHOW_KERNEL = 1, | 5 | |
9 | SHOW_USER = 2, | 6 | #include "../perf.h" |
10 | SHOW_HV = 4, | 7 | #include "map.h" |
11 | }; | ||
12 | 8 | ||
13 | /* | 9 | /* |
14 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * | 10 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * |
@@ -60,11 +56,32 @@ struct read_event { | |||
60 | u64 id; | 56 | u64 id; |
61 | }; | 57 | }; |
62 | 58 | ||
63 | struct sample_event{ | 59 | struct sample_event { |
64 | struct perf_event_header header; | 60 | struct perf_event_header header; |
65 | u64 array[]; | 61 | u64 array[]; |
66 | }; | 62 | }; |
67 | 63 | ||
64 | struct sample_data { | ||
65 | u64 ip; | ||
66 | u32 pid, tid; | ||
67 | u64 time; | ||
68 | u64 addr; | ||
69 | u64 id; | ||
70 | u64 stream_id; | ||
71 | u32 cpu; | ||
72 | u64 period; | ||
73 | struct ip_callchain *callchain; | ||
74 | u32 raw_size; | ||
75 | void *raw_data; | ||
76 | }; | ||
77 | |||
78 | #define BUILD_ID_SIZE 20 | ||
79 | |||
80 | struct build_id_event { | ||
81 | struct perf_event_header header; | ||
82 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | ||
83 | char filename[]; | ||
84 | }; | ||
68 | 85 | ||
69 | typedef union event_union { | 86 | typedef union event_union { |
70 | struct perf_event_header header; | 87 | struct perf_event_header header; |
@@ -77,28 +94,44 @@ typedef union event_union { | |||
77 | struct sample_event sample; | 94 | struct sample_event sample; |
78 | } event_t; | 95 | } event_t; |
79 | 96 | ||
80 | struct map { | 97 | struct events_stats { |
81 | struct list_head node; | 98 | u64 total; |
82 | u64 start; | 99 | u64 lost; |
83 | u64 end; | ||
84 | u64 pgoff; | ||
85 | u64 (*map_ip)(struct map *, u64); | ||
86 | struct dso *dso; | ||
87 | }; | 100 | }; |
88 | 101 | ||
89 | static inline u64 map__map_ip(struct map *map, u64 ip) | 102 | struct event_stat_id { |
90 | { | 103 | struct rb_node rb_node; |
91 | return ip - map->start + map->pgoff; | 104 | struct rb_root hists; |
92 | } | 105 | struct events_stats stats; |
106 | u64 config; | ||
107 | u64 event_stream; | ||
108 | u32 type; | ||
109 | }; | ||
110 | |||
111 | void event__print_totals(void); | ||
112 | |||
113 | struct perf_session; | ||
114 | |||
115 | typedef int (*event__handler_t)(event_t *event, struct perf_session *session); | ||
116 | |||
117 | int event__synthesize_thread(pid_t pid, event__handler_t process, | ||
118 | struct perf_session *session); | ||
119 | void event__synthesize_threads(event__handler_t process, | ||
120 | struct perf_session *session); | ||
121 | int event__synthesize_kernel_mmap(event__handler_t process, | ||
122 | struct perf_session *session, | ||
123 | const char *symbol_name); | ||
124 | int event__synthesize_modules(event__handler_t process, | ||
125 | struct perf_session *session); | ||
93 | 126 | ||
94 | static inline u64 vdso__map_ip(struct map *map __used, u64 ip) | 127 | int event__process_comm(event_t *self, struct perf_session *session); |
95 | { | 128 | int event__process_lost(event_t *self, struct perf_session *session); |
96 | return ip; | 129 | int event__process_mmap(event_t *self, struct perf_session *session); |
97 | } | 130 | int event__process_task(event_t *self, struct perf_session *session); |
98 | 131 | ||
99 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen); | 132 | struct addr_location; |
100 | struct map *map__clone(struct map *self); | 133 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
101 | int map__overlap(struct map *l, struct map *r); | 134 | struct addr_location *al, symbol_filter_t filter); |
102 | size_t map__fprintf(struct map *self, FILE *fp); | 135 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data); |
103 | 136 | ||
104 | #endif | 137 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h index effe25eb1545..31647ac92ed1 100644 --- a/tools/perf/util/exec_cmd.h +++ b/tools/perf/util/exec_cmd.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef PERF_EXEC_CMD_H | 1 | #ifndef __PERF_EXEC_CMD_H |
2 | #define PERF_EXEC_CMD_H | 2 | #define __PERF_EXEC_CMD_H |
3 | 3 | ||
4 | extern void perf_set_argv_exec_path(const char *exec_path); | 4 | extern void perf_set_argv_exec_path(const char *exec_path); |
5 | extern const char *perf_extract_argv0_path(const char *path); | 5 | extern const char *perf_extract_argv0_path(const char *path); |
@@ -10,4 +10,4 @@ extern int execv_perf_cmd(const char **argv); /* NULL terminated */ | |||
10 | extern int execl_perf_cmd(const char *cmd, ...); | 10 | extern int execl_perf_cmd(const char *cmd, ...); |
11 | extern const char *system_path(const char *path); | 11 | extern const char *system_path(const char *path); |
12 | 12 | ||
13 | #endif /* PERF_EXEC_CMD_H */ | 13 | #endif /* __PERF_EXEC_CMD_H */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index e306857b2c2b..6c9aa16ee51f 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1,10 +1,20 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | ||
2 | |||
1 | #include <sys/types.h> | 3 | #include <sys/types.h> |
4 | #include <byteswap.h> | ||
2 | #include <unistd.h> | 5 | #include <unistd.h> |
3 | #include <stdio.h> | 6 | #include <stdio.h> |
4 | #include <stdlib.h> | 7 | #include <stdlib.h> |
8 | #include <linux/list.h> | ||
9 | #include <linux/kernel.h> | ||
5 | 10 | ||
6 | #include "util.h" | 11 | #include "util.h" |
7 | #include "header.h" | 12 | #include "header.h" |
13 | #include "../perf.h" | ||
14 | #include "trace-event.h" | ||
15 | #include "session.h" | ||
16 | #include "symbol.h" | ||
17 | #include "debug.h" | ||
8 | 18 | ||
9 | /* | 19 | /* |
10 | * Create new perf.data header attribute: | 20 | * Create new perf.data header attribute: |
@@ -13,75 +23,80 @@ struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr) | |||
13 | { | 23 | { |
14 | struct perf_header_attr *self = malloc(sizeof(*self)); | 24 | struct perf_header_attr *self = malloc(sizeof(*self)); |
15 | 25 | ||
16 | if (!self) | 26 | if (self != NULL) { |
17 | die("nomem"); | 27 | self->attr = *attr; |
18 | 28 | self->ids = 0; | |
19 | self->attr = *attr; | 29 | self->size = 1; |
20 | self->ids = 0; | 30 | self->id = malloc(sizeof(u64)); |
21 | self->size = 1; | 31 | if (self->id == NULL) { |
22 | self->id = malloc(sizeof(u64)); | 32 | free(self); |
23 | 33 | self = NULL; | |
24 | if (!self->id) | 34 | } |
25 | die("nomem"); | 35 | } |
26 | 36 | ||
27 | return self; | 37 | return self; |
28 | } | 38 | } |
29 | 39 | ||
30 | void perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | 40 | void perf_header_attr__delete(struct perf_header_attr *self) |
41 | { | ||
42 | free(self->id); | ||
43 | free(self); | ||
44 | } | ||
45 | |||
46 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | ||
31 | { | 47 | { |
32 | int pos = self->ids; | 48 | int pos = self->ids; |
33 | 49 | ||
34 | self->ids++; | 50 | self->ids++; |
35 | if (self->ids > self->size) { | 51 | if (self->ids > self->size) { |
36 | self->size *= 2; | 52 | int nsize = self->size * 2; |
37 | self->id = realloc(self->id, self->size * sizeof(u64)); | 53 | u64 *nid = realloc(self->id, nsize * sizeof(u64)); |
38 | if (!self->id) | 54 | |
39 | die("nomem"); | 55 | if (nid == NULL) |
56 | return -1; | ||
57 | |||
58 | self->size = nsize; | ||
59 | self->id = nid; | ||
40 | } | 60 | } |
41 | self->id[pos] = id; | 61 | self->id[pos] = id; |
62 | return 0; | ||
42 | } | 63 | } |
43 | 64 | ||
44 | /* | 65 | int perf_header__init(struct perf_header *self) |
45 | * Create new perf.data header: | ||
46 | */ | ||
47 | struct perf_header *perf_header__new(void) | ||
48 | { | 66 | { |
49 | struct perf_header *self = malloc(sizeof(*self)); | ||
50 | |||
51 | if (!self) | ||
52 | die("nomem"); | ||
53 | |||
54 | self->frozen = 0; | ||
55 | |||
56 | self->attrs = 0; | ||
57 | self->size = 1; | 67 | self->size = 1; |
58 | self->attr = malloc(sizeof(void *)); | 68 | self->attr = malloc(sizeof(void *)); |
59 | 69 | return self->attr == NULL ? -ENOMEM : 0; | |
60 | if (!self->attr) | ||
61 | die("nomem"); | ||
62 | |||
63 | self->data_offset = 0; | ||
64 | self->data_size = 0; | ||
65 | |||
66 | return self; | ||
67 | } | 70 | } |
68 | 71 | ||
69 | void perf_header__add_attr(struct perf_header *self, | 72 | void perf_header__exit(struct perf_header *self) |
70 | struct perf_header_attr *attr) | ||
71 | { | 73 | { |
72 | int pos = self->attrs; | 74 | int i; |
75 | for (i = 0; i < self->attrs; ++i) | ||
76 | perf_header_attr__delete(self->attr[i]); | ||
77 | free(self->attr); | ||
78 | } | ||
73 | 79 | ||
80 | int perf_header__add_attr(struct perf_header *self, | ||
81 | struct perf_header_attr *attr) | ||
82 | { | ||
74 | if (self->frozen) | 83 | if (self->frozen) |
75 | die("frozen"); | 84 | return -1; |
76 | 85 | ||
77 | self->attrs++; | 86 | if (self->attrs == self->size) { |
78 | if (self->attrs > self->size) { | 87 | int nsize = self->size * 2; |
79 | self->size *= 2; | 88 | struct perf_header_attr **nattr; |
80 | self->attr = realloc(self->attr, self->size * sizeof(void *)); | 89 | |
81 | if (!self->attr) | 90 | nattr = realloc(self->attr, nsize * sizeof(void *)); |
82 | die("nomem"); | 91 | if (nattr == NULL) |
92 | return -1; | ||
93 | |||
94 | self->size = nsize; | ||
95 | self->attr = nattr; | ||
83 | } | 96 | } |
84 | self->attr[pos] = attr; | 97 | |
98 | self->attr[self->attrs++] = attr; | ||
99 | return 0; | ||
85 | } | 100 | } |
86 | 101 | ||
87 | #define MAX_EVENT_NAME 64 | 102 | #define MAX_EVENT_NAME 64 |
@@ -94,24 +109,28 @@ struct perf_trace_event_type { | |||
94 | static int event_count; | 109 | static int event_count; |
95 | static struct perf_trace_event_type *events; | 110 | static struct perf_trace_event_type *events; |
96 | 111 | ||
97 | void perf_header__push_event(u64 id, const char *name) | 112 | int perf_header__push_event(u64 id, const char *name) |
98 | { | 113 | { |
99 | if (strlen(name) > MAX_EVENT_NAME) | 114 | if (strlen(name) > MAX_EVENT_NAME) |
100 | printf("Event %s will be truncated\n", name); | 115 | pr_warning("Event %s will be truncated\n", name); |
101 | 116 | ||
102 | if (!events) { | 117 | if (!events) { |
103 | events = malloc(sizeof(struct perf_trace_event_type)); | 118 | events = malloc(sizeof(struct perf_trace_event_type)); |
104 | if (!events) | 119 | if (events == NULL) |
105 | die("nomem"); | 120 | return -ENOMEM; |
106 | } else { | 121 | } else { |
107 | events = realloc(events, (event_count + 1) * sizeof(struct perf_trace_event_type)); | 122 | struct perf_trace_event_type *nevents; |
108 | if (!events) | 123 | |
109 | die("nomem"); | 124 | nevents = realloc(events, (event_count + 1) * sizeof(*events)); |
125 | if (nevents == NULL) | ||
126 | return -ENOMEM; | ||
127 | events = nevents; | ||
110 | } | 128 | } |
111 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); | 129 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); |
112 | events[event_count].event_id = id; | 130 | events[event_count].event_id = id; |
113 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); | 131 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); |
114 | event_count++; | 132 | event_count++; |
133 | return 0; | ||
115 | } | 134 | } |
116 | 135 | ||
117 | char *perf_header__find_event(u64 id) | 136 | char *perf_header__find_event(u64 id) |
@@ -128,44 +147,292 @@ static const char *__perf_magic = "PERFFILE"; | |||
128 | 147 | ||
129 | #define PERF_MAGIC (*(u64 *)__perf_magic) | 148 | #define PERF_MAGIC (*(u64 *)__perf_magic) |
130 | 149 | ||
131 | struct perf_file_section { | ||
132 | u64 offset; | ||
133 | u64 size; | ||
134 | }; | ||
135 | |||
136 | struct perf_file_attr { | 150 | struct perf_file_attr { |
137 | struct perf_event_attr attr; | 151 | struct perf_event_attr attr; |
138 | struct perf_file_section ids; | 152 | struct perf_file_section ids; |
139 | }; | 153 | }; |
140 | 154 | ||
141 | struct perf_file_header { | 155 | void perf_header__set_feat(struct perf_header *self, int feat) |
142 | u64 magic; | 156 | { |
143 | u64 size; | 157 | set_bit(feat, self->adds_features); |
144 | u64 attr_size; | 158 | } |
145 | struct perf_file_section attrs; | ||
146 | struct perf_file_section data; | ||
147 | struct perf_file_section event_types; | ||
148 | }; | ||
149 | 159 | ||
150 | static void do_write(int fd, void *buf, size_t size) | 160 | bool perf_header__has_feat(const struct perf_header *self, int feat) |
161 | { | ||
162 | return test_bit(feat, self->adds_features); | ||
163 | } | ||
164 | |||
165 | static int do_write(int fd, const void *buf, size_t size) | ||
151 | { | 166 | { |
152 | while (size) { | 167 | while (size) { |
153 | int ret = write(fd, buf, size); | 168 | int ret = write(fd, buf, size); |
154 | 169 | ||
155 | if (ret < 0) | 170 | if (ret < 0) |
156 | die("failed to write"); | 171 | return -errno; |
157 | 172 | ||
158 | size -= ret; | 173 | size -= ret; |
159 | buf += ret; | 174 | buf += ret; |
160 | } | 175 | } |
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | #define NAME_ALIGN 64 | ||
181 | |||
182 | static int write_padded(int fd, const void *bf, size_t count, | ||
183 | size_t count_aligned) | ||
184 | { | ||
185 | static const char zero_buf[NAME_ALIGN]; | ||
186 | int err = do_write(fd, bf, count); | ||
187 | |||
188 | if (!err) | ||
189 | err = do_write(fd, zero_buf, count_aligned - count); | ||
190 | |||
191 | return err; | ||
192 | } | ||
193 | |||
194 | #define dsos__for_each_with_build_id(pos, head) \ | ||
195 | list_for_each_entry(pos, head, node) \ | ||
196 | if (!pos->has_build_id) \ | ||
197 | continue; \ | ||
198 | else | ||
199 | |||
200 | static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) | ||
201 | { | ||
202 | struct dso *pos; | ||
203 | |||
204 | dsos__for_each_with_build_id(pos, head) { | ||
205 | int err; | ||
206 | struct build_id_event b; | ||
207 | size_t len; | ||
208 | |||
209 | if (!pos->hit) | ||
210 | continue; | ||
211 | len = pos->long_name_len + 1; | ||
212 | len = ALIGN(len, NAME_ALIGN); | ||
213 | memset(&b, 0, sizeof(b)); | ||
214 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); | ||
215 | b.header.misc = misc; | ||
216 | b.header.size = sizeof(b) + len; | ||
217 | err = do_write(fd, &b, sizeof(b)); | ||
218 | if (err < 0) | ||
219 | return err; | ||
220 | err = write_padded(fd, pos->long_name, | ||
221 | pos->long_name_len + 1, len); | ||
222 | if (err < 0) | ||
223 | return err; | ||
224 | } | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int dsos__write_buildid_table(int fd) | ||
230 | { | ||
231 | int err = __dsos__write_buildid_table(&dsos__kernel, | ||
232 | PERF_RECORD_MISC_KERNEL, fd); | ||
233 | if (err == 0) | ||
234 | err = __dsos__write_buildid_table(&dsos__user, | ||
235 | PERF_RECORD_MISC_USER, fd); | ||
236 | return err; | ||
237 | } | ||
238 | |||
239 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
240 | const char *name, bool is_kallsyms) | ||
241 | { | ||
242 | const size_t size = PATH_MAX; | ||
243 | char *filename = malloc(size), | ||
244 | *linkname = malloc(size), *targetname; | ||
245 | int len, err = -1; | ||
246 | |||
247 | if (filename == NULL || linkname == NULL) | ||
248 | goto out_free; | ||
249 | |||
250 | len = snprintf(filename, size, "%s%s%s", | ||
251 | debugdir, is_kallsyms ? "/" : "", name); | ||
252 | if (mkdir_p(filename, 0755)) | ||
253 | goto out_free; | ||
254 | |||
255 | snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id); | ||
256 | |||
257 | if (access(filename, F_OK)) { | ||
258 | if (is_kallsyms) { | ||
259 | if (copyfile("/proc/kallsyms", filename)) | ||
260 | goto out_free; | ||
261 | } else if (link(name, filename) && copyfile(name, filename)) | ||
262 | goto out_free; | ||
263 | } | ||
264 | |||
265 | len = snprintf(linkname, size, "%s/.build-id/%.2s", | ||
266 | debugdir, sbuild_id); | ||
267 | |||
268 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | ||
269 | goto out_free; | ||
270 | |||
271 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | ||
272 | targetname = filename + strlen(debugdir) - 5; | ||
273 | memcpy(targetname, "../..", 5); | ||
274 | |||
275 | if (symlink(targetname, linkname) == 0) | ||
276 | err = 0; | ||
277 | out_free: | ||
278 | free(filename); | ||
279 | free(linkname); | ||
280 | return err; | ||
281 | } | ||
282 | |||
283 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | ||
284 | const char *name, const char *debugdir, | ||
285 | bool is_kallsyms) | ||
286 | { | ||
287 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
288 | |||
289 | build_id__sprintf(build_id, build_id_size, sbuild_id); | ||
290 | |||
291 | return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); | ||
292 | } | ||
293 | |||
294 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | ||
295 | { | ||
296 | const size_t size = PATH_MAX; | ||
297 | char *filename = malloc(size), | ||
298 | *linkname = malloc(size); | ||
299 | int err = -1; | ||
300 | |||
301 | if (filename == NULL || linkname == NULL) | ||
302 | goto out_free; | ||
303 | |||
304 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
305 | debugdir, sbuild_id, sbuild_id + 2); | ||
306 | |||
307 | if (access(linkname, F_OK)) | ||
308 | goto out_free; | ||
309 | |||
310 | if (readlink(linkname, filename, size) < 0) | ||
311 | goto out_free; | ||
312 | |||
313 | if (unlink(linkname)) | ||
314 | goto out_free; | ||
315 | |||
316 | /* | ||
317 | * Since the link is relative, we must make it absolute: | ||
318 | */ | ||
319 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
320 | debugdir, sbuild_id, filename); | ||
321 | |||
322 | if (unlink(linkname)) | ||
323 | goto out_free; | ||
324 | |||
325 | err = 0; | ||
326 | out_free: | ||
327 | free(filename); | ||
328 | free(linkname); | ||
329 | return err; | ||
330 | } | ||
331 | |||
332 | static int dso__cache_build_id(struct dso *self, const char *debugdir) | ||
333 | { | ||
334 | bool is_kallsyms = self->kernel && self->long_name[0] != '/'; | ||
335 | |||
336 | return build_id_cache__add_b(self->build_id, sizeof(self->build_id), | ||
337 | self->long_name, debugdir, is_kallsyms); | ||
338 | } | ||
339 | |||
340 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | ||
341 | { | ||
342 | struct dso *pos; | ||
343 | int err = 0; | ||
344 | |||
345 | dsos__for_each_with_build_id(pos, head) | ||
346 | if (dso__cache_build_id(pos, debugdir)) | ||
347 | err = -1; | ||
348 | |||
349 | return err; | ||
350 | } | ||
351 | |||
352 | static int dsos__cache_build_ids(void) | ||
353 | { | ||
354 | int err_kernel, err_user; | ||
355 | char debugdir[PATH_MAX]; | ||
356 | |||
357 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), | ||
358 | DEBUG_CACHE_DIR); | ||
359 | |||
360 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | ||
361 | return -1; | ||
362 | |||
363 | err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); | ||
364 | err_user = __dsos__cache_build_ids(&dsos__user, debugdir); | ||
365 | return err_kernel || err_user ? -1 : 0; | ||
366 | } | ||
367 | |||
368 | static int perf_header__adds_write(struct perf_header *self, int fd) | ||
369 | { | ||
370 | int nr_sections; | ||
371 | struct perf_file_section *feat_sec; | ||
372 | int sec_size; | ||
373 | u64 sec_start; | ||
374 | int idx = 0, err; | ||
375 | |||
376 | if (dsos__read_build_ids(true)) | ||
377 | perf_header__set_feat(self, HEADER_BUILD_ID); | ||
378 | |||
379 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | ||
380 | if (!nr_sections) | ||
381 | return 0; | ||
382 | |||
383 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | ||
384 | if (feat_sec == NULL) | ||
385 | return -ENOMEM; | ||
386 | |||
387 | sec_size = sizeof(*feat_sec) * nr_sections; | ||
388 | |||
389 | sec_start = self->data_offset + self->data_size; | ||
390 | lseek(fd, sec_start + sec_size, SEEK_SET); | ||
391 | |||
392 | if (perf_header__has_feat(self, HEADER_TRACE_INFO)) { | ||
393 | struct perf_file_section *trace_sec; | ||
394 | |||
395 | trace_sec = &feat_sec[idx++]; | ||
396 | |||
397 | /* Write trace info */ | ||
398 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | ||
399 | read_tracing_data(fd, attrs, nr_counters); | ||
400 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | ||
401 | } | ||
402 | |||
403 | |||
404 | if (perf_header__has_feat(self, HEADER_BUILD_ID)) { | ||
405 | struct perf_file_section *buildid_sec; | ||
406 | |||
407 | buildid_sec = &feat_sec[idx++]; | ||
408 | |||
409 | /* Write build-ids */ | ||
410 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | ||
411 | err = dsos__write_buildid_table(fd); | ||
412 | if (err < 0) { | ||
413 | pr_debug("failed to write buildid table\n"); | ||
414 | goto out_free; | ||
415 | } | ||
416 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - | ||
417 | buildid_sec->offset; | ||
418 | dsos__cache_build_ids(); | ||
419 | } | ||
420 | |||
421 | lseek(fd, sec_start, SEEK_SET); | ||
422 | err = do_write(fd, feat_sec, sec_size); | ||
423 | if (err < 0) | ||
424 | pr_debug("failed to write feature section\n"); | ||
425 | out_free: | ||
426 | free(feat_sec); | ||
427 | return err; | ||
161 | } | 428 | } |
162 | 429 | ||
163 | void perf_header__write(struct perf_header *self, int fd) | 430 | int perf_header__write(struct perf_header *self, int fd, bool at_exit) |
164 | { | 431 | { |
165 | struct perf_file_header f_header; | 432 | struct perf_file_header f_header; |
166 | struct perf_file_attr f_attr; | 433 | struct perf_file_attr f_attr; |
167 | struct perf_header_attr *attr; | 434 | struct perf_header_attr *attr; |
168 | int i; | 435 | int i, err; |
169 | 436 | ||
170 | lseek(fd, sizeof(f_header), SEEK_SET); | 437 | lseek(fd, sizeof(f_header), SEEK_SET); |
171 | 438 | ||
@@ -174,7 +441,11 @@ void perf_header__write(struct perf_header *self, int fd) | |||
174 | attr = self->attr[i]; | 441 | attr = self->attr[i]; |
175 | 442 | ||
176 | attr->id_offset = lseek(fd, 0, SEEK_CUR); | 443 | attr->id_offset = lseek(fd, 0, SEEK_CUR); |
177 | do_write(fd, attr->id, attr->ids * sizeof(u64)); | 444 | err = do_write(fd, attr->id, attr->ids * sizeof(u64)); |
445 | if (err < 0) { | ||
446 | pr_debug("failed to write perf header\n"); | ||
447 | return err; | ||
448 | } | ||
178 | } | 449 | } |
179 | 450 | ||
180 | 451 | ||
@@ -190,17 +461,31 @@ void perf_header__write(struct perf_header *self, int fd) | |||
190 | .size = attr->ids * sizeof(u64), | 461 | .size = attr->ids * sizeof(u64), |
191 | } | 462 | } |
192 | }; | 463 | }; |
193 | do_write(fd, &f_attr, sizeof(f_attr)); | 464 | err = do_write(fd, &f_attr, sizeof(f_attr)); |
465 | if (err < 0) { | ||
466 | pr_debug("failed to write perf header attribute\n"); | ||
467 | return err; | ||
468 | } | ||
194 | } | 469 | } |
195 | 470 | ||
196 | self->event_offset = lseek(fd, 0, SEEK_CUR); | 471 | self->event_offset = lseek(fd, 0, SEEK_CUR); |
197 | self->event_size = event_count * sizeof(struct perf_trace_event_type); | 472 | self->event_size = event_count * sizeof(struct perf_trace_event_type); |
198 | if (events) | 473 | if (events) { |
199 | do_write(fd, events, self->event_size); | 474 | err = do_write(fd, events, self->event_size); |
200 | 475 | if (err < 0) { | |
476 | pr_debug("failed to write perf header events\n"); | ||
477 | return err; | ||
478 | } | ||
479 | } | ||
201 | 480 | ||
202 | self->data_offset = lseek(fd, 0, SEEK_CUR); | 481 | self->data_offset = lseek(fd, 0, SEEK_CUR); |
203 | 482 | ||
483 | if (at_exit) { | ||
484 | err = perf_header__adds_write(self, fd); | ||
485 | if (err < 0) | ||
486 | return err; | ||
487 | } | ||
488 | |||
204 | f_header = (struct perf_file_header){ | 489 | f_header = (struct perf_file_header){ |
205 | .magic = PERF_MAGIC, | 490 | .magic = PERF_MAGIC, |
206 | .size = sizeof(f_header), | 491 | .size = sizeof(f_header), |
@@ -219,44 +504,175 @@ void perf_header__write(struct perf_header *self, int fd) | |||
219 | }, | 504 | }, |
220 | }; | 505 | }; |
221 | 506 | ||
507 | memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features)); | ||
508 | |||
222 | lseek(fd, 0, SEEK_SET); | 509 | lseek(fd, 0, SEEK_SET); |
223 | do_write(fd, &f_header, sizeof(f_header)); | 510 | err = do_write(fd, &f_header, sizeof(f_header)); |
511 | if (err < 0) { | ||
512 | pr_debug("failed to write perf header\n"); | ||
513 | return err; | ||
514 | } | ||
224 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | 515 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); |
225 | 516 | ||
226 | self->frozen = 1; | 517 | self->frozen = 1; |
518 | return 0; | ||
227 | } | 519 | } |
228 | 520 | ||
229 | static void do_read(int fd, void *buf, size_t size) | 521 | static int do_read(int fd, void *buf, size_t size) |
230 | { | 522 | { |
231 | while (size) { | 523 | while (size) { |
232 | int ret = read(fd, buf, size); | 524 | int ret = read(fd, buf, size); |
233 | 525 | ||
234 | if (ret < 0) | 526 | if (ret <= 0) |
235 | die("failed to read"); | 527 | return -1; |
236 | if (ret == 0) | ||
237 | die("failed to read: missing data"); | ||
238 | 528 | ||
239 | size -= ret; | 529 | size -= ret; |
240 | buf += ret; | 530 | buf += ret; |
241 | } | 531 | } |
532 | |||
533 | return 0; | ||
242 | } | 534 | } |
243 | 535 | ||
244 | struct perf_header *perf_header__read(int fd) | 536 | static int perf_header__getbuffer64(struct perf_header *self, |
537 | int fd, void *buf, size_t size) | ||
245 | { | 538 | { |
246 | struct perf_header *self = perf_header__new(); | 539 | if (do_read(fd, buf, size)) |
247 | struct perf_file_header f_header; | 540 | return -1; |
248 | struct perf_file_attr f_attr; | ||
249 | u64 f_id; | ||
250 | 541 | ||
251 | int nr_attrs, nr_ids, i, j; | 542 | if (self->needs_swap) |
543 | mem_bswap_64(buf, size); | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | int perf_header__process_sections(struct perf_header *self, int fd, | ||
549 | int (*process)(struct perf_file_section *self, | ||
550 | struct perf_header *ph, | ||
551 | int feat, int fd)) | ||
552 | { | ||
553 | struct perf_file_section *feat_sec; | ||
554 | int nr_sections; | ||
555 | int sec_size; | ||
556 | int idx = 0; | ||
557 | int err = -1, feat = 1; | ||
558 | |||
559 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | ||
560 | if (!nr_sections) | ||
561 | return 0; | ||
562 | |||
563 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | ||
564 | if (!feat_sec) | ||
565 | return -1; | ||
566 | |||
567 | sec_size = sizeof(*feat_sec) * nr_sections; | ||
568 | |||
569 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | ||
570 | |||
571 | if (perf_header__getbuffer64(self, fd, feat_sec, sec_size)) | ||
572 | goto out_free; | ||
573 | |||
574 | err = 0; | ||
575 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { | ||
576 | if (perf_header__has_feat(self, feat)) { | ||
577 | struct perf_file_section *sec = &feat_sec[idx++]; | ||
578 | |||
579 | err = process(sec, self, feat, fd); | ||
580 | if (err < 0) | ||
581 | break; | ||
582 | } | ||
583 | ++feat; | ||
584 | } | ||
585 | out_free: | ||
586 | free(feat_sec); | ||
587 | return err; | ||
588 | } | ||
252 | 589 | ||
590 | int perf_file_header__read(struct perf_file_header *self, | ||
591 | struct perf_header *ph, int fd) | ||
592 | { | ||
253 | lseek(fd, 0, SEEK_SET); | 593 | lseek(fd, 0, SEEK_SET); |
254 | do_read(fd, &f_header, sizeof(f_header)); | ||
255 | 594 | ||
256 | if (f_header.magic != PERF_MAGIC || | 595 | if (do_read(fd, self, sizeof(*self)) || |
257 | f_header.size != sizeof(f_header) || | 596 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) |
258 | f_header.attr_size != sizeof(f_attr)) | 597 | return -1; |
259 | die("incompatible file format"); | 598 | |
599 | if (self->attr_size != sizeof(struct perf_file_attr)) { | ||
600 | u64 attr_size = bswap_64(self->attr_size); | ||
601 | |||
602 | if (attr_size != sizeof(struct perf_file_attr)) | ||
603 | return -1; | ||
604 | |||
605 | mem_bswap_64(self, offsetof(struct perf_file_header, | ||
606 | adds_features)); | ||
607 | ph->needs_swap = true; | ||
608 | } | ||
609 | |||
610 | if (self->size != sizeof(*self)) { | ||
611 | /* Support the previous format */ | ||
612 | if (self->size == offsetof(typeof(*self), adds_features)) | ||
613 | bitmap_zero(self->adds_features, HEADER_FEAT_BITS); | ||
614 | else | ||
615 | return -1; | ||
616 | } | ||
617 | |||
618 | memcpy(&ph->adds_features, &self->adds_features, | ||
619 | sizeof(ph->adds_features)); | ||
620 | /* | ||
621 | * FIXME: hack that assumes that if we need swap the perf.data file | ||
622 | * may be coming from an arch with a different word-size, ergo different | ||
623 | * DEFINE_BITMAP format, investigate more later, but for now its mostly | ||
624 | * safe to assume that we have a build-id section. Trace files probably | ||
625 | * have several other issues in this realm anyway... | ||
626 | */ | ||
627 | if (ph->needs_swap) { | ||
628 | memset(&ph->adds_features, 0, sizeof(ph->adds_features)); | ||
629 | perf_header__set_feat(ph, HEADER_BUILD_ID); | ||
630 | } | ||
631 | |||
632 | ph->event_offset = self->event_types.offset; | ||
633 | ph->event_size = self->event_types.size; | ||
634 | ph->data_offset = self->data.offset; | ||
635 | ph->data_size = self->data.size; | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static int perf_file_section__process(struct perf_file_section *self, | ||
640 | struct perf_header *ph, | ||
641 | int feat, int fd) | ||
642 | { | ||
643 | if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) { | ||
644 | pr_debug("Failed to lseek to %Ld offset for feature %d, " | ||
645 | "continuing...\n", self->offset, feat); | ||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | switch (feat) { | ||
650 | case HEADER_TRACE_INFO: | ||
651 | trace_report(fd); | ||
652 | break; | ||
653 | |||
654 | case HEADER_BUILD_ID: | ||
655 | if (perf_header__read_build_ids(ph, fd, self->offset, self->size)) | ||
656 | pr_debug("Failed to read buildids, continuing...\n"); | ||
657 | break; | ||
658 | default: | ||
659 | pr_debug("unknown feature %d, continuing...\n", feat); | ||
660 | } | ||
661 | |||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | int perf_header__read(struct perf_header *self, int fd) | ||
666 | { | ||
667 | struct perf_file_header f_header; | ||
668 | struct perf_file_attr f_attr; | ||
669 | u64 f_id; | ||
670 | int nr_attrs, nr_ids, i, j; | ||
671 | |||
672 | if (perf_file_header__read(&f_header, self, fd) < 0) { | ||
673 | pr_debug("incompatible file format\n"); | ||
674 | return -EINVAL; | ||
675 | } | ||
260 | 676 | ||
261 | nr_attrs = f_header.attrs.size / sizeof(f_attr); | 677 | nr_attrs = f_header.attrs.size / sizeof(f_attr); |
262 | lseek(fd, f_header.attrs.offset, SEEK_SET); | 678 | lseek(fd, f_header.attrs.offset, SEEK_SET); |
@@ -265,42 +681,54 @@ struct perf_header *perf_header__read(int fd) | |||
265 | struct perf_header_attr *attr; | 681 | struct perf_header_attr *attr; |
266 | off_t tmp; | 682 | off_t tmp; |
267 | 683 | ||
268 | do_read(fd, &f_attr, sizeof(f_attr)); | 684 | if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr))) |
685 | goto out_errno; | ||
686 | |||
269 | tmp = lseek(fd, 0, SEEK_CUR); | 687 | tmp = lseek(fd, 0, SEEK_CUR); |
270 | 688 | ||
271 | attr = perf_header_attr__new(&f_attr.attr); | 689 | attr = perf_header_attr__new(&f_attr.attr); |
690 | if (attr == NULL) | ||
691 | return -ENOMEM; | ||
272 | 692 | ||
273 | nr_ids = f_attr.ids.size / sizeof(u64); | 693 | nr_ids = f_attr.ids.size / sizeof(u64); |
274 | lseek(fd, f_attr.ids.offset, SEEK_SET); | 694 | lseek(fd, f_attr.ids.offset, SEEK_SET); |
275 | 695 | ||
276 | for (j = 0; j < nr_ids; j++) { | 696 | for (j = 0; j < nr_ids; j++) { |
277 | do_read(fd, &f_id, sizeof(f_id)); | 697 | if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id))) |
698 | goto out_errno; | ||
278 | 699 | ||
279 | perf_header_attr__add_id(attr, f_id); | 700 | if (perf_header_attr__add_id(attr, f_id) < 0) { |
701 | perf_header_attr__delete(attr); | ||
702 | return -ENOMEM; | ||
703 | } | ||
704 | } | ||
705 | if (perf_header__add_attr(self, attr) < 0) { | ||
706 | perf_header_attr__delete(attr); | ||
707 | return -ENOMEM; | ||
280 | } | 708 | } |
281 | perf_header__add_attr(self, attr); | 709 | |
282 | lseek(fd, tmp, SEEK_SET); | 710 | lseek(fd, tmp, SEEK_SET); |
283 | } | 711 | } |
284 | 712 | ||
285 | if (f_header.event_types.size) { | 713 | if (f_header.event_types.size) { |
286 | lseek(fd, f_header.event_types.offset, SEEK_SET); | 714 | lseek(fd, f_header.event_types.offset, SEEK_SET); |
287 | events = malloc(f_header.event_types.size); | 715 | events = malloc(f_header.event_types.size); |
288 | if (!events) | 716 | if (events == NULL) |
289 | die("nomem"); | 717 | return -ENOMEM; |
290 | do_read(fd, events, f_header.event_types.size); | 718 | if (perf_header__getbuffer64(self, fd, events, |
719 | f_header.event_types.size)) | ||
720 | goto out_errno; | ||
291 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 721 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
292 | } | 722 | } |
293 | self->event_offset = f_header.event_types.offset; | ||
294 | self->event_size = f_header.event_types.size; | ||
295 | 723 | ||
296 | self->data_offset = f_header.data.offset; | 724 | perf_header__process_sections(self, fd, perf_file_section__process); |
297 | self->data_size = f_header.data.size; | ||
298 | 725 | ||
299 | lseek(fd, self->data_offset, SEEK_SET); | 726 | lseek(fd, self->data_offset, SEEK_SET); |
300 | 727 | ||
301 | self->frozen = 1; | 728 | self->frozen = 1; |
302 | 729 | return 0; | |
303 | return self; | 730 | out_errno: |
731 | return -errno; | ||
304 | } | 732 | } |
305 | 733 | ||
306 | u64 perf_header__sample_type(struct perf_header *header) | 734 | u64 perf_header__sample_type(struct perf_header *header) |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index a0761bc7863c..82a6af72d4cc 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -1,9 +1,13 @@ | |||
1 | #ifndef _PERF_HEADER_H | 1 | #ifndef __PERF_HEADER_H |
2 | #define _PERF_HEADER_H | 2 | #define __PERF_HEADER_H |
3 | 3 | ||
4 | #include "../../../include/linux/perf_event.h" | 4 | #include "../../../include/linux/perf_event.h" |
5 | #include <sys/types.h> | 5 | #include <sys/types.h> |
6 | #include <stdbool.h> | ||
6 | #include "types.h" | 7 | #include "types.h" |
8 | #include "event.h" | ||
9 | |||
10 | #include <linux/bitmap.h> | ||
7 | 11 | ||
8 | struct perf_header_attr { | 12 | struct perf_header_attr { |
9 | struct perf_event_attr attr; | 13 | struct perf_event_attr attr; |
@@ -12,36 +16,77 @@ struct perf_header_attr { | |||
12 | off_t id_offset; | 16 | off_t id_offset; |
13 | }; | 17 | }; |
14 | 18 | ||
19 | enum { | ||
20 | HEADER_TRACE_INFO = 1, | ||
21 | HEADER_BUILD_ID, | ||
22 | HEADER_LAST_FEATURE, | ||
23 | }; | ||
24 | |||
25 | #define HEADER_FEAT_BITS 256 | ||
26 | |||
27 | struct perf_file_section { | ||
28 | u64 offset; | ||
29 | u64 size; | ||
30 | }; | ||
31 | |||
32 | struct perf_file_header { | ||
33 | u64 magic; | ||
34 | u64 size; | ||
35 | u64 attr_size; | ||
36 | struct perf_file_section attrs; | ||
37 | struct perf_file_section data; | ||
38 | struct perf_file_section event_types; | ||
39 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | ||
40 | }; | ||
41 | |||
42 | struct perf_header; | ||
43 | |||
44 | int perf_file_header__read(struct perf_file_header *self, | ||
45 | struct perf_header *ph, int fd); | ||
46 | |||
15 | struct perf_header { | 47 | struct perf_header { |
16 | int frozen; | 48 | int frozen; |
17 | int attrs, size; | 49 | int attrs, size; |
18 | struct perf_header_attr **attr; | 50 | struct perf_header_attr **attr; |
19 | s64 attr_offset; | 51 | s64 attr_offset; |
20 | u64 data_offset; | 52 | u64 data_offset; |
21 | u64 data_size; | 53 | u64 data_size; |
22 | u64 event_offset; | 54 | u64 event_offset; |
23 | u64 event_size; | 55 | u64 event_size; |
56 | bool needs_swap; | ||
57 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | ||
24 | }; | 58 | }; |
25 | 59 | ||
26 | struct perf_header *perf_header__read(int fd); | 60 | int perf_header__init(struct perf_header *self); |
27 | void perf_header__write(struct perf_header *self, int fd); | 61 | void perf_header__exit(struct perf_header *self); |
62 | |||
63 | int perf_header__read(struct perf_header *self, int fd); | ||
64 | int perf_header__write(struct perf_header *self, int fd, bool at_exit); | ||
28 | 65 | ||
29 | void perf_header__add_attr(struct perf_header *self, | 66 | int perf_header__add_attr(struct perf_header *self, |
30 | struct perf_header_attr *attr); | 67 | struct perf_header_attr *attr); |
31 | 68 | ||
32 | void perf_header__push_event(u64 id, const char *name); | 69 | int perf_header__push_event(u64 id, const char *name); |
33 | char *perf_header__find_event(u64 id); | 70 | char *perf_header__find_event(u64 id); |
34 | 71 | ||
72 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr); | ||
73 | void perf_header_attr__delete(struct perf_header_attr *self); | ||
35 | 74 | ||
36 | struct perf_header_attr * | 75 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); |
37 | perf_header_attr__new(struct perf_event_attr *attr); | ||
38 | void perf_header_attr__add_id(struct perf_header_attr *self, u64 id); | ||
39 | 76 | ||
40 | u64 perf_header__sample_type(struct perf_header *header); | 77 | u64 perf_header__sample_type(struct perf_header *header); |
41 | struct perf_event_attr * | 78 | struct perf_event_attr * |
42 | perf_header__find_attr(u64 id, struct perf_header *header); | 79 | perf_header__find_attr(u64 id, struct perf_header *header); |
80 | void perf_header__set_feat(struct perf_header *self, int feat); | ||
81 | bool perf_header__has_feat(const struct perf_header *self, int feat); | ||
43 | 82 | ||
83 | int perf_header__process_sections(struct perf_header *self, int fd, | ||
84 | int (*process)(struct perf_file_section *self, | ||
85 | struct perf_header *ph, | ||
86 | int feat, int fd)); | ||
44 | 87 | ||
45 | struct perf_header *perf_header__new(void); | 88 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, |
89 | const char *name, bool is_kallsyms); | ||
90 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | ||
46 | 91 | ||
47 | #endif /* _PERF_HEADER_H */ | 92 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h index 7128783637b4..7f5c6dedd714 100644 --- a/tools/perf/util/help.h +++ b/tools/perf/util/help.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef HELP_H | 1 | #ifndef __PERF_HELP_H |
2 | #define HELP_H | 2 | #define __PERF_HELP_H |
3 | 3 | ||
4 | struct cmdnames { | 4 | struct cmdnames { |
5 | size_t alloc; | 5 | size_t alloc; |
@@ -26,4 +26,4 @@ int is_in_cmdlist(struct cmdnames *c, const char *s); | |||
26 | void list_commands(const char *title, struct cmdnames *main_cmds, | 26 | void list_commands(const char *title, struct cmdnames *main_cmds, |
27 | struct cmdnames *other_cmds); | 27 | struct cmdnames *other_cmds); |
28 | 28 | ||
29 | #endif /* HELP_H */ | 29 | #endif /* __PERF_HELP_H */ |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c new file mode 100644 index 000000000000..2be33c7dbf03 --- /dev/null +++ b/tools/perf/util/hist.c | |||
@@ -0,0 +1,668 @@ | |||
1 | #include "hist.h" | ||
2 | #include "session.h" | ||
3 | #include "sort.h" | ||
4 | #include <math.h> | ||
5 | |||
6 | struct callchain_param callchain_param = { | ||
7 | .mode = CHAIN_GRAPH_REL, | ||
8 | .min_percent = 0.5 | ||
9 | }; | ||
10 | |||
11 | /* | ||
12 | * histogram, sorted on item, collects counts | ||
13 | */ | ||
14 | |||
15 | struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, | ||
16 | struct addr_location *al, | ||
17 | struct symbol *sym_parent, | ||
18 | u64 count, bool *hit) | ||
19 | { | ||
20 | struct rb_node **p = &hists->rb_node; | ||
21 | struct rb_node *parent = NULL; | ||
22 | struct hist_entry *he; | ||
23 | struct hist_entry entry = { | ||
24 | .thread = al->thread, | ||
25 | .map = al->map, | ||
26 | .sym = al->sym, | ||
27 | .ip = al->addr, | ||
28 | .level = al->level, | ||
29 | .count = count, | ||
30 | .parent = sym_parent, | ||
31 | }; | ||
32 | int cmp; | ||
33 | |||
34 | while (*p != NULL) { | ||
35 | parent = *p; | ||
36 | he = rb_entry(parent, struct hist_entry, rb_node); | ||
37 | |||
38 | cmp = hist_entry__cmp(&entry, he); | ||
39 | |||
40 | if (!cmp) { | ||
41 | *hit = true; | ||
42 | return he; | ||
43 | } | ||
44 | |||
45 | if (cmp < 0) | ||
46 | p = &(*p)->rb_left; | ||
47 | else | ||
48 | p = &(*p)->rb_right; | ||
49 | } | ||
50 | |||
51 | he = malloc(sizeof(*he)); | ||
52 | if (!he) | ||
53 | return NULL; | ||
54 | *he = entry; | ||
55 | rb_link_node(&he->rb_node, parent, p); | ||
56 | rb_insert_color(&he->rb_node, hists); | ||
57 | *hit = false; | ||
58 | return he; | ||
59 | } | ||
60 | |||
61 | int64_t | ||
62 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | ||
63 | { | ||
64 | struct sort_entry *se; | ||
65 | int64_t cmp = 0; | ||
66 | |||
67 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
68 | cmp = se->cmp(left, right); | ||
69 | if (cmp) | ||
70 | break; | ||
71 | } | ||
72 | |||
73 | return cmp; | ||
74 | } | ||
75 | |||
76 | int64_t | ||
77 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | ||
78 | { | ||
79 | struct sort_entry *se; | ||
80 | int64_t cmp = 0; | ||
81 | |||
82 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
83 | int64_t (*f)(struct hist_entry *, struct hist_entry *); | ||
84 | |||
85 | f = se->collapse ?: se->cmp; | ||
86 | |||
87 | cmp = f(left, right); | ||
88 | if (cmp) | ||
89 | break; | ||
90 | } | ||
91 | |||
92 | return cmp; | ||
93 | } | ||
94 | |||
95 | void hist_entry__free(struct hist_entry *he) | ||
96 | { | ||
97 | free(he); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * collapse the histogram | ||
102 | */ | ||
103 | |||
104 | static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | ||
105 | { | ||
106 | struct rb_node **p = &root->rb_node; | ||
107 | struct rb_node *parent = NULL; | ||
108 | struct hist_entry *iter; | ||
109 | int64_t cmp; | ||
110 | |||
111 | while (*p != NULL) { | ||
112 | parent = *p; | ||
113 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
114 | |||
115 | cmp = hist_entry__collapse(iter, he); | ||
116 | |||
117 | if (!cmp) { | ||
118 | iter->count += he->count; | ||
119 | hist_entry__free(he); | ||
120 | return; | ||
121 | } | ||
122 | |||
123 | if (cmp < 0) | ||
124 | p = &(*p)->rb_left; | ||
125 | else | ||
126 | p = &(*p)->rb_right; | ||
127 | } | ||
128 | |||
129 | rb_link_node(&he->rb_node, parent, p); | ||
130 | rb_insert_color(&he->rb_node, root); | ||
131 | } | ||
132 | |||
133 | void perf_session__collapse_resort(struct rb_root *hists) | ||
134 | { | ||
135 | struct rb_root tmp; | ||
136 | struct rb_node *next; | ||
137 | struct hist_entry *n; | ||
138 | |||
139 | if (!sort__need_collapse) | ||
140 | return; | ||
141 | |||
142 | tmp = RB_ROOT; | ||
143 | next = rb_first(hists); | ||
144 | |||
145 | while (next) { | ||
146 | n = rb_entry(next, struct hist_entry, rb_node); | ||
147 | next = rb_next(&n->rb_node); | ||
148 | |||
149 | rb_erase(&n->rb_node, hists); | ||
150 | collapse__insert_entry(&tmp, n); | ||
151 | } | ||
152 | |||
153 | *hists = tmp; | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * reverse the map, sort on count. | ||
158 | */ | ||
159 | |||
160 | static void perf_session__insert_output_hist_entry(struct rb_root *root, | ||
161 | struct hist_entry *he, | ||
162 | u64 min_callchain_hits) | ||
163 | { | ||
164 | struct rb_node **p = &root->rb_node; | ||
165 | struct rb_node *parent = NULL; | ||
166 | struct hist_entry *iter; | ||
167 | |||
168 | if (symbol_conf.use_callchain) | ||
169 | callchain_param.sort(&he->sorted_chain, &he->callchain, | ||
170 | min_callchain_hits, &callchain_param); | ||
171 | |||
172 | while (*p != NULL) { | ||
173 | parent = *p; | ||
174 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
175 | |||
176 | if (he->count > iter->count) | ||
177 | p = &(*p)->rb_left; | ||
178 | else | ||
179 | p = &(*p)->rb_right; | ||
180 | } | ||
181 | |||
182 | rb_link_node(&he->rb_node, parent, p); | ||
183 | rb_insert_color(&he->rb_node, root); | ||
184 | } | ||
185 | |||
186 | void perf_session__output_resort(struct rb_root *hists, u64 total_samples) | ||
187 | { | ||
188 | struct rb_root tmp; | ||
189 | struct rb_node *next; | ||
190 | struct hist_entry *n; | ||
191 | u64 min_callchain_hits; | ||
192 | |||
193 | min_callchain_hits = | ||
194 | total_samples * (callchain_param.min_percent / 100); | ||
195 | |||
196 | tmp = RB_ROOT; | ||
197 | next = rb_first(hists); | ||
198 | |||
199 | while (next) { | ||
200 | n = rb_entry(next, struct hist_entry, rb_node); | ||
201 | next = rb_next(&n->rb_node); | ||
202 | |||
203 | rb_erase(&n->rb_node, hists); | ||
204 | perf_session__insert_output_hist_entry(&tmp, n, | ||
205 | min_callchain_hits); | ||
206 | } | ||
207 | |||
208 | *hists = tmp; | ||
209 | } | ||
210 | |||
211 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | ||
212 | { | ||
213 | int i; | ||
214 | int ret = fprintf(fp, " "); | ||
215 | |||
216 | for (i = 0; i < left_margin; i++) | ||
217 | ret += fprintf(fp, " "); | ||
218 | |||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | ||
223 | int left_margin) | ||
224 | { | ||
225 | int i; | ||
226 | size_t ret = callchain__fprintf_left_margin(fp, left_margin); | ||
227 | |||
228 | for (i = 0; i < depth; i++) | ||
229 | if (depth_mask & (1 << i)) | ||
230 | ret += fprintf(fp, "| "); | ||
231 | else | ||
232 | ret += fprintf(fp, " "); | ||
233 | |||
234 | ret += fprintf(fp, "\n"); | ||
235 | |||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | ||
240 | int depth, int depth_mask, int count, | ||
241 | u64 total_samples, int hits, | ||
242 | int left_margin) | ||
243 | { | ||
244 | int i; | ||
245 | size_t ret = 0; | ||
246 | |||
247 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
248 | for (i = 0; i < depth; i++) { | ||
249 | if (depth_mask & (1 << i)) | ||
250 | ret += fprintf(fp, "|"); | ||
251 | else | ||
252 | ret += fprintf(fp, " "); | ||
253 | if (!count && i == depth - 1) { | ||
254 | double percent; | ||
255 | |||
256 | percent = hits * 100.0 / total_samples; | ||
257 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
258 | } else | ||
259 | ret += fprintf(fp, "%s", " "); | ||
260 | } | ||
261 | if (chain->sym) | ||
262 | ret += fprintf(fp, "%s\n", chain->sym->name); | ||
263 | else | ||
264 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | ||
265 | |||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | static struct symbol *rem_sq_bracket; | ||
270 | static struct callchain_list rem_hits; | ||
271 | |||
272 | static void init_rem_hits(void) | ||
273 | { | ||
274 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
275 | if (!rem_sq_bracket) { | ||
276 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
277 | return; | ||
278 | } | ||
279 | |||
280 | strcpy(rem_sq_bracket->name, "[...]"); | ||
281 | rem_hits.sym = rem_sq_bracket; | ||
282 | } | ||
283 | |||
284 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
285 | u64 total_samples, int depth, | ||
286 | int depth_mask, int left_margin) | ||
287 | { | ||
288 | struct rb_node *node, *next; | ||
289 | struct callchain_node *child; | ||
290 | struct callchain_list *chain; | ||
291 | int new_depth_mask = depth_mask; | ||
292 | u64 new_total; | ||
293 | u64 remaining; | ||
294 | size_t ret = 0; | ||
295 | int i; | ||
296 | |||
297 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
298 | new_total = self->children_hit; | ||
299 | else | ||
300 | new_total = total_samples; | ||
301 | |||
302 | remaining = new_total; | ||
303 | |||
304 | node = rb_first(&self->rb_root); | ||
305 | while (node) { | ||
306 | u64 cumul; | ||
307 | |||
308 | child = rb_entry(node, struct callchain_node, rb_node); | ||
309 | cumul = cumul_hits(child); | ||
310 | remaining -= cumul; | ||
311 | |||
312 | /* | ||
313 | * The depth mask manages the output of pipes that show | ||
314 | * the depth. We don't want to keep the pipes of the current | ||
315 | * level for the last child of this depth. | ||
316 | * Except if we have remaining filtered hits. They will | ||
317 | * supersede the last child | ||
318 | */ | ||
319 | next = rb_next(node); | ||
320 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | ||
321 | new_depth_mask &= ~(1 << (depth - 1)); | ||
322 | |||
323 | /* | ||
324 | * But we keep the older depth mask for the line separator | ||
325 | * to keep the level link until we reach the last child | ||
326 | */ | ||
327 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | ||
328 | left_margin); | ||
329 | i = 0; | ||
330 | list_for_each_entry(chain, &child->val, list) { | ||
331 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
332 | continue; | ||
333 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
334 | new_depth_mask, i++, | ||
335 | new_total, | ||
336 | cumul, | ||
337 | left_margin); | ||
338 | } | ||
339 | ret += __callchain__fprintf_graph(fp, child, new_total, | ||
340 | depth + 1, | ||
341 | new_depth_mask | (1 << depth), | ||
342 | left_margin); | ||
343 | node = next; | ||
344 | } | ||
345 | |||
346 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
347 | remaining && remaining != new_total) { | ||
348 | |||
349 | if (!rem_sq_bracket) | ||
350 | return ret; | ||
351 | |||
352 | new_depth_mask &= ~(1 << (depth - 1)); | ||
353 | |||
354 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
355 | new_depth_mask, 0, new_total, | ||
356 | remaining, left_margin); | ||
357 | } | ||
358 | |||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
363 | u64 total_samples, int left_margin) | ||
364 | { | ||
365 | struct callchain_list *chain; | ||
366 | bool printed = false; | ||
367 | int i = 0; | ||
368 | int ret = 0; | ||
369 | |||
370 | list_for_each_entry(chain, &self->val, list) { | ||
371 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
372 | continue; | ||
373 | |||
374 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
375 | continue; | ||
376 | |||
377 | if (!printed) { | ||
378 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
379 | ret += fprintf(fp, "|\n"); | ||
380 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
381 | ret += fprintf(fp, "---"); | ||
382 | |||
383 | left_margin += 3; | ||
384 | printed = true; | ||
385 | } else | ||
386 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
387 | |||
388 | if (chain->sym) | ||
389 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
390 | else | ||
391 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
392 | } | ||
393 | |||
394 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | ||
395 | |||
396 | return ret; | ||
397 | } | ||
398 | |||
399 | static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | ||
400 | u64 total_samples) | ||
401 | { | ||
402 | struct callchain_list *chain; | ||
403 | size_t ret = 0; | ||
404 | |||
405 | if (!self) | ||
406 | return 0; | ||
407 | |||
408 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | ||
409 | |||
410 | |||
411 | list_for_each_entry(chain, &self->val, list) { | ||
412 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
413 | continue; | ||
414 | if (chain->sym) | ||
415 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
416 | else | ||
417 | ret += fprintf(fp, " %p\n", | ||
418 | (void *)(long)chain->ip); | ||
419 | } | ||
420 | |||
421 | return ret; | ||
422 | } | ||
423 | |||
424 | static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | ||
425 | u64 total_samples, int left_margin) | ||
426 | { | ||
427 | struct rb_node *rb_node; | ||
428 | struct callchain_node *chain; | ||
429 | size_t ret = 0; | ||
430 | |||
431 | rb_node = rb_first(&self->sorted_chain); | ||
432 | while (rb_node) { | ||
433 | double percent; | ||
434 | |||
435 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
436 | percent = chain->hit * 100.0 / total_samples; | ||
437 | switch (callchain_param.mode) { | ||
438 | case CHAIN_FLAT: | ||
439 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | ||
440 | percent); | ||
441 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
442 | break; | ||
443 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
444 | case CHAIN_GRAPH_REL: | ||
445 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
446 | left_margin); | ||
447 | case CHAIN_NONE: | ||
448 | default: | ||
449 | break; | ||
450 | } | ||
451 | ret += fprintf(fp, "\n"); | ||
452 | rb_node = rb_next(rb_node); | ||
453 | } | ||
454 | |||
455 | return ret; | ||
456 | } | ||
457 | |||
458 | static size_t hist_entry__fprintf(struct hist_entry *self, | ||
459 | struct perf_session *pair_session, | ||
460 | bool show_displacement, | ||
461 | long displacement, FILE *fp, | ||
462 | u64 session_total) | ||
463 | { | ||
464 | struct sort_entry *se; | ||
465 | u64 count, total; | ||
466 | const char *sep = symbol_conf.field_sep; | ||
467 | size_t ret; | ||
468 | |||
469 | if (symbol_conf.exclude_other && !self->parent) | ||
470 | return 0; | ||
471 | |||
472 | if (pair_session) { | ||
473 | count = self->pair ? self->pair->count : 0; | ||
474 | total = pair_session->events_stats.total; | ||
475 | } else { | ||
476 | count = self->count; | ||
477 | total = session_total; | ||
478 | } | ||
479 | |||
480 | if (total) | ||
481 | ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%", | ||
482 | (count * 100.0) / total); | ||
483 | else | ||
484 | ret = fprintf(fp, sep ? "%lld" : "%12lld ", count); | ||
485 | |||
486 | if (symbol_conf.show_nr_samples) { | ||
487 | if (sep) | ||
488 | fprintf(fp, "%c%lld", *sep, count); | ||
489 | else | ||
490 | fprintf(fp, "%11lld", count); | ||
491 | } | ||
492 | |||
493 | if (pair_session) { | ||
494 | char bf[32]; | ||
495 | double old_percent = 0, new_percent = 0, diff; | ||
496 | |||
497 | if (total > 0) | ||
498 | old_percent = (count * 100.0) / total; | ||
499 | if (session_total > 0) | ||
500 | new_percent = (self->count * 100.0) / session_total; | ||
501 | |||
502 | diff = new_percent - old_percent; | ||
503 | |||
504 | if (fabs(diff) >= 0.01) | ||
505 | snprintf(bf, sizeof(bf), "%+4.2F%%", diff); | ||
506 | else | ||
507 | snprintf(bf, sizeof(bf), " "); | ||
508 | |||
509 | if (sep) | ||
510 | ret += fprintf(fp, "%c%s", *sep, bf); | ||
511 | else | ||
512 | ret += fprintf(fp, "%11.11s", bf); | ||
513 | |||
514 | if (show_displacement) { | ||
515 | if (displacement) | ||
516 | snprintf(bf, sizeof(bf), "%+4ld", displacement); | ||
517 | else | ||
518 | snprintf(bf, sizeof(bf), " "); | ||
519 | |||
520 | if (sep) | ||
521 | fprintf(fp, "%c%s", *sep, bf); | ||
522 | else | ||
523 | fprintf(fp, "%6.6s", bf); | ||
524 | } | ||
525 | } | ||
526 | |||
527 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
528 | if (se->elide) | ||
529 | continue; | ||
530 | |||
531 | fprintf(fp, "%s", sep ?: " "); | ||
532 | ret += se->print(fp, self, se->width ? *se->width : 0); | ||
533 | } | ||
534 | |||
535 | ret += fprintf(fp, "\n"); | ||
536 | |||
537 | if (symbol_conf.use_callchain) { | ||
538 | int left_margin = 0; | ||
539 | |||
540 | if (sort__first_dimension == SORT_COMM) { | ||
541 | se = list_first_entry(&hist_entry__sort_list, typeof(*se), | ||
542 | list); | ||
543 | left_margin = se->width ? *se->width : 0; | ||
544 | left_margin -= thread__comm_len(self->thread); | ||
545 | } | ||
546 | |||
547 | hist_entry_callchain__fprintf(fp, self, session_total, | ||
548 | left_margin); | ||
549 | } | ||
550 | |||
551 | return ret; | ||
552 | } | ||
553 | |||
554 | size_t perf_session__fprintf_hists(struct rb_root *hists, | ||
555 | struct perf_session *pair, | ||
556 | bool show_displacement, FILE *fp, | ||
557 | u64 session_total) | ||
558 | { | ||
559 | struct sort_entry *se; | ||
560 | struct rb_node *nd; | ||
561 | size_t ret = 0; | ||
562 | unsigned long position = 1; | ||
563 | long displacement = 0; | ||
564 | unsigned int width; | ||
565 | const char *sep = symbol_conf.field_sep; | ||
566 | char *col_width = symbol_conf.col_width_list_str; | ||
567 | |||
568 | init_rem_hits(); | ||
569 | |||
570 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); | ||
571 | |||
572 | if (symbol_conf.show_nr_samples) { | ||
573 | if (sep) | ||
574 | fprintf(fp, "%cSamples", *sep); | ||
575 | else | ||
576 | fputs(" Samples ", fp); | ||
577 | } | ||
578 | |||
579 | if (pair) { | ||
580 | if (sep) | ||
581 | ret += fprintf(fp, "%cDelta", *sep); | ||
582 | else | ||
583 | ret += fprintf(fp, " Delta "); | ||
584 | |||
585 | if (show_displacement) { | ||
586 | if (sep) | ||
587 | ret += fprintf(fp, "%cDisplacement", *sep); | ||
588 | else | ||
589 | ret += fprintf(fp, " Displ"); | ||
590 | } | ||
591 | } | ||
592 | |||
593 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
594 | if (se->elide) | ||
595 | continue; | ||
596 | if (sep) { | ||
597 | fprintf(fp, "%c%s", *sep, se->header); | ||
598 | continue; | ||
599 | } | ||
600 | width = strlen(se->header); | ||
601 | if (se->width) { | ||
602 | if (symbol_conf.col_width_list_str) { | ||
603 | if (col_width) { | ||
604 | *se->width = atoi(col_width); | ||
605 | col_width = strchr(col_width, ','); | ||
606 | if (col_width) | ||
607 | ++col_width; | ||
608 | } | ||
609 | } | ||
610 | width = *se->width = max(*se->width, width); | ||
611 | } | ||
612 | fprintf(fp, " %*s", width, se->header); | ||
613 | } | ||
614 | fprintf(fp, "\n"); | ||
615 | |||
616 | if (sep) | ||
617 | goto print_entries; | ||
618 | |||
619 | fprintf(fp, "# ........"); | ||
620 | if (symbol_conf.show_nr_samples) | ||
621 | fprintf(fp, " .........."); | ||
622 | if (pair) { | ||
623 | fprintf(fp, " .........."); | ||
624 | if (show_displacement) | ||
625 | fprintf(fp, " ....."); | ||
626 | } | ||
627 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
628 | unsigned int i; | ||
629 | |||
630 | if (se->elide) | ||
631 | continue; | ||
632 | |||
633 | fprintf(fp, " "); | ||
634 | if (se->width) | ||
635 | width = *se->width; | ||
636 | else | ||
637 | width = strlen(se->header); | ||
638 | for (i = 0; i < width; i++) | ||
639 | fprintf(fp, "."); | ||
640 | } | ||
641 | |||
642 | fprintf(fp, "\n#\n"); | ||
643 | |||
644 | print_entries: | ||
645 | for (nd = rb_first(hists); nd; nd = rb_next(nd)) { | ||
646 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
647 | |||
648 | if (show_displacement) { | ||
649 | if (h->pair != NULL) | ||
650 | displacement = ((long)h->pair->position - | ||
651 | (long)position); | ||
652 | else | ||
653 | displacement = 0; | ||
654 | ++position; | ||
655 | } | ||
656 | ret += hist_entry__fprintf(h, pair, show_displacement, | ||
657 | displacement, fp, session_total); | ||
658 | if (h->map == NULL && verbose > 1) { | ||
659 | __map_groups__fprintf_maps(&h->thread->mg, | ||
660 | MAP__FUNCTION, fp); | ||
661 | fprintf(fp, "%.10s end\n", graph_dotted_line); | ||
662 | } | ||
663 | } | ||
664 | |||
665 | free(rem_sq_bracket); | ||
666 | |||
667 | return ret; | ||
668 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h new file mode 100644 index 000000000000..16f360cce5bf --- /dev/null +++ b/tools/perf/util/hist.h | |||
@@ -0,0 +1,29 @@ | |||
1 | #ifndef __PERF_HIST_H | ||
2 | #define __PERF_HIST_H | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include "callchain.h" | ||
6 | |||
7 | extern struct callchain_param callchain_param; | ||
8 | |||
9 | struct perf_session; | ||
10 | struct hist_entry; | ||
11 | struct addr_location; | ||
12 | struct symbol; | ||
13 | struct rb_root; | ||
14 | |||
15 | struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, | ||
16 | struct addr_location *al, | ||
17 | struct symbol *parent, | ||
18 | u64 count, bool *hit); | ||
19 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | ||
20 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | ||
21 | void hist_entry__free(struct hist_entry *); | ||
22 | |||
23 | void perf_session__output_resort(struct rb_root *hists, u64 total_samples); | ||
24 | void perf_session__collapse_resort(struct rb_root *hists); | ||
25 | size_t perf_session__fprintf_hists(struct rb_root *hists, | ||
26 | struct perf_session *pair, | ||
27 | bool show_displacement, FILE *fp, | ||
28 | u64 session_total); | ||
29 | #endif /* __PERF_HIST_H */ | ||
diff --git a/tools/perf/util/include/asm/asm-offsets.h b/tools/perf/util/include/asm/asm-offsets.h new file mode 100644 index 000000000000..ed538942523d --- /dev/null +++ b/tools/perf/util/include/asm/asm-offsets.h | |||
@@ -0,0 +1 @@ | |||
/* stub */ | |||
diff --git a/tools/perf/util/include/asm/bitops.h b/tools/perf/util/include/asm/bitops.h new file mode 100644 index 000000000000..58e9817ffae0 --- /dev/null +++ b/tools/perf/util/include/asm/bitops.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #ifndef _PERF_ASM_BITOPS_H_ | ||
2 | #define _PERF_ASM_BITOPS_H_ | ||
3 | |||
4 | #include <sys/types.h> | ||
5 | #include "../../types.h" | ||
6 | #include <linux/compiler.h> | ||
7 | |||
8 | /* CHECKME: Not sure both always match */ | ||
9 | #define BITS_PER_LONG __WORDSIZE | ||
10 | |||
11 | #include "../../../../include/asm-generic/bitops/__fls.h" | ||
12 | #include "../../../../include/asm-generic/bitops/fls.h" | ||
13 | #include "../../../../include/asm-generic/bitops/fls64.h" | ||
14 | #include "../../../../include/asm-generic/bitops/__ffs.h" | ||
15 | #include "../../../../include/asm-generic/bitops/ffz.h" | ||
16 | #include "../../../../include/asm-generic/bitops/hweight.h" | ||
17 | |||
18 | #endif | ||
diff --git a/tools/perf/util/include/asm/bug.h b/tools/perf/util/include/asm/bug.h new file mode 100644 index 000000000000..7fcc6810adc2 --- /dev/null +++ b/tools/perf/util/include/asm/bug.h | |||
@@ -0,0 +1,22 @@ | |||
1 | #ifndef _PERF_ASM_GENERIC_BUG_H | ||
2 | #define _PERF_ASM_GENERIC_BUG_H | ||
3 | |||
4 | #define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0) | ||
5 | |||
6 | #define WARN(condition, format...) ({ \ | ||
7 | int __ret_warn_on = !!(condition); \ | ||
8 | if (unlikely(__ret_warn_on)) \ | ||
9 | __WARN_printf(format); \ | ||
10 | unlikely(__ret_warn_on); \ | ||
11 | }) | ||
12 | |||
13 | #define WARN_ONCE(condition, format...) ({ \ | ||
14 | static int __warned; \ | ||
15 | int __ret_warn_once = !!(condition); \ | ||
16 | \ | ||
17 | if (unlikely(__ret_warn_once)) \ | ||
18 | if (WARN(!__warned, format)) \ | ||
19 | __warned = 1; \ | ||
20 | unlikely(__ret_warn_once); \ | ||
21 | }) | ||
22 | #endif | ||
diff --git a/tools/perf/util/include/asm/byteorder.h b/tools/perf/util/include/asm/byteorder.h new file mode 100644 index 000000000000..b722abe3a626 --- /dev/null +++ b/tools/perf/util/include/asm/byteorder.h | |||
@@ -0,0 +1,2 @@ | |||
1 | #include <asm/types.h> | ||
2 | #include "../../../../include/linux/swab.h" | ||
diff --git a/tools/perf/util/include/asm/swab.h b/tools/perf/util/include/asm/swab.h new file mode 100644 index 000000000000..ed538942523d --- /dev/null +++ b/tools/perf/util/include/asm/swab.h | |||
@@ -0,0 +1 @@ | |||
/* stub */ | |||
diff --git a/tools/perf/util/include/asm/uaccess.h b/tools/perf/util/include/asm/uaccess.h new file mode 100644 index 000000000000..d0f72b8fcc35 --- /dev/null +++ b/tools/perf/util/include/asm/uaccess.h | |||
@@ -0,0 +1,14 @@ | |||
1 | #ifndef _PERF_ASM_UACCESS_H_ | ||
2 | #define _PERF_ASM_UACCESS_H_ | ||
3 | |||
4 | #define __get_user(src, dest) \ | ||
5 | ({ \ | ||
6 | (src) = *dest; \ | ||
7 | 0; \ | ||
8 | }) | ||
9 | |||
10 | #define get_user __get_user | ||
11 | |||
12 | #define access_ok(type, addr, size) 1 | ||
13 | |||
14 | #endif | ||
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h new file mode 100644 index 000000000000..94507639a8c4 --- /dev/null +++ b/tools/perf/util/include/linux/bitmap.h | |||
@@ -0,0 +1,3 @@ | |||
1 | #include "../../../../include/linux/bitmap.h" | ||
2 | #include "../../../../include/asm-generic/bitops/find.h" | ||
3 | #include <linux/errno.h> | ||
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h new file mode 100644 index 000000000000..8d63116e9435 --- /dev/null +++ b/tools/perf/util/include/linux/bitops.h | |||
@@ -0,0 +1,29 @@ | |||
1 | #ifndef _PERF_LINUX_BITOPS_H_ | ||
2 | #define _PERF_LINUX_BITOPS_H_ | ||
3 | |||
4 | #define __KERNEL__ | ||
5 | |||
6 | #define CONFIG_GENERIC_FIND_NEXT_BIT | ||
7 | #define CONFIG_GENERIC_FIND_FIRST_BIT | ||
8 | #include "../../../../include/linux/bitops.h" | ||
9 | |||
10 | #undef __KERNEL__ | ||
11 | |||
12 | static inline void set_bit(int nr, unsigned long *addr) | ||
13 | { | ||
14 | addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG); | ||
15 | } | ||
16 | |||
17 | static __always_inline int test_bit(unsigned int nr, const unsigned long *addr) | ||
18 | { | ||
19 | return ((1UL << (nr % BITS_PER_LONG)) & | ||
20 | (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0; | ||
21 | } | ||
22 | |||
23 | unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, unsigned | ||
24 | long size, unsigned long offset); | ||
25 | |||
26 | unsigned long generic_find_next_le_bit(const unsigned long *addr, unsigned | ||
27 | long size, unsigned long offset); | ||
28 | |||
29 | #endif | ||
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h new file mode 100644 index 000000000000..dfb0713ed47f --- /dev/null +++ b/tools/perf/util/include/linux/compiler.h | |||
@@ -0,0 +1,10 @@ | |||
1 | #ifndef _PERF_LINUX_COMPILER_H_ | ||
2 | #define _PERF_LINUX_COMPILER_H_ | ||
3 | |||
4 | #ifndef __always_inline | ||
5 | #define __always_inline inline | ||
6 | #endif | ||
7 | #define __user | ||
8 | #define __attribute_const__ | ||
9 | |||
10 | #endif | ||
diff --git a/tools/perf/util/include/linux/ctype.h b/tools/perf/util/include/linux/ctype.h new file mode 100644 index 000000000000..a53d4ee1e0b7 --- /dev/null +++ b/tools/perf/util/include/linux/ctype.h | |||
@@ -0,0 +1 @@ | |||
#include "../util.h" | |||
diff --git a/tools/perf/util/include/linux/hash.h b/tools/perf/util/include/linux/hash.h new file mode 100644 index 000000000000..201f57397997 --- /dev/null +++ b/tools/perf/util/include/linux/hash.h | |||
@@ -0,0 +1,5 @@ | |||
1 | #include "../../../../include/linux/hash.h" | ||
2 | |||
3 | #ifndef PERF_HASH_H | ||
4 | #define PERF_HASH_H | ||
5 | #endif | ||
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index a6b87390cb52..f2611655ab51 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h | |||
@@ -1,6 +1,16 @@ | |||
1 | #ifndef PERF_LINUX_KERNEL_H_ | 1 | #ifndef PERF_LINUX_KERNEL_H_ |
2 | #define PERF_LINUX_KERNEL_H_ | 2 | #define PERF_LINUX_KERNEL_H_ |
3 | 3 | ||
4 | #include <stdarg.h> | ||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <assert.h> | ||
8 | |||
9 | #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) | ||
10 | |||
11 | #define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) | ||
12 | #define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) | ||
13 | |||
4 | #ifndef offsetof | 14 | #ifndef offsetof |
5 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) | 15 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) |
6 | #endif | 16 | #endif |
@@ -26,4 +36,71 @@ | |||
26 | _max1 > _max2 ? _max1 : _max2; }) | 36 | _max1 > _max2 ? _max1 : _max2; }) |
27 | #endif | 37 | #endif |
28 | 38 | ||
39 | #ifndef min | ||
40 | #define min(x, y) ({ \ | ||
41 | typeof(x) _min1 = (x); \ | ||
42 | typeof(y) _min2 = (y); \ | ||
43 | (void) (&_min1 == &_min2); \ | ||
44 | _min1 < _min2 ? _min1 : _min2; }) | ||
45 | #endif | ||
46 | |||
47 | #ifndef BUG_ON | ||
48 | #define BUG_ON(cond) assert(!(cond)) | ||
49 | #endif | ||
50 | |||
51 | /* | ||
52 | * Both need more care to handle endianness | ||
53 | * (Don't use bitmap_copy_le() for now) | ||
54 | */ | ||
55 | #define cpu_to_le64(x) (x) | ||
56 | #define cpu_to_le32(x) (x) | ||
57 | |||
58 | static inline int | ||
59 | vscnprintf(char *buf, size_t size, const char *fmt, va_list args) | ||
60 | { | ||
61 | int i; | ||
62 | ssize_t ssize = size; | ||
63 | |||
64 | i = vsnprintf(buf, size, fmt, args); | ||
65 | |||
66 | return (i >= ssize) ? (ssize - 1) : i; | ||
67 | } | ||
68 | |||
69 | static inline int scnprintf(char * buf, size_t size, const char * fmt, ...) | ||
70 | { | ||
71 | va_list args; | ||
72 | ssize_t ssize = size; | ||
73 | int i; | ||
74 | |||
75 | va_start(args, fmt); | ||
76 | i = vsnprintf(buf, size, fmt, args); | ||
77 | va_end(args); | ||
78 | |||
79 | return (i >= ssize) ? (ssize - 1) : i; | ||
80 | } | ||
81 | |||
82 | static inline unsigned long | ||
83 | simple_strtoul(const char *nptr, char **endptr, int base) | ||
84 | { | ||
85 | return strtoul(nptr, endptr, base); | ||
86 | } | ||
87 | |||
88 | #ifndef pr_fmt | ||
89 | #define pr_fmt(fmt) fmt | ||
90 | #endif | ||
91 | |||
92 | #define pr_err(fmt, ...) \ | ||
93 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | ||
94 | #define pr_warning(fmt, ...) \ | ||
95 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | ||
96 | #define pr_info(fmt, ...) \ | ||
97 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | ||
98 | #define pr_debug(fmt, ...) \ | ||
99 | eprintf(1, pr_fmt(fmt), ##__VA_ARGS__) | ||
100 | #define pr_debugN(n, fmt, ...) \ | ||
101 | eprintf(n, pr_fmt(fmt), ##__VA_ARGS__) | ||
102 | #define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__) | ||
103 | #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) | ||
104 | #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) | ||
105 | |||
29 | #endif | 106 | #endif |
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h new file mode 100644 index 000000000000..3b2f5900276f --- /dev/null +++ b/tools/perf/util/include/linux/string.h | |||
@@ -0,0 +1 @@ | |||
#include <string.h> | |||
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h new file mode 100644 index 000000000000..196862a81a21 --- /dev/null +++ b/tools/perf/util/include/linux/types.h | |||
@@ -0,0 +1,9 @@ | |||
1 | #ifndef _PERF_LINUX_TYPES_H_ | ||
2 | #define _PERF_LINUX_TYPES_H_ | ||
3 | |||
4 | #include <asm/types.h> | ||
5 | |||
6 | #define DECLARE_BITMAP(name,bits) \ | ||
7 | unsigned long name[BITS_TO_LONGS(bits)] | ||
8 | |||
9 | #endif | ||
diff --git a/tools/perf/util/levenshtein.h b/tools/perf/util/levenshtein.h index 0173abeef52c..b0fcb6d8a881 100644 --- a/tools/perf/util/levenshtein.h +++ b/tools/perf/util/levenshtein.h | |||
@@ -1,8 +1,8 @@ | |||
1 | #ifndef LEVENSHTEIN_H | 1 | #ifndef __PERF_LEVENSHTEIN_H |
2 | #define LEVENSHTEIN_H | 2 | #define __PERF_LEVENSHTEIN_H |
3 | 3 | ||
4 | int levenshtein(const char *string1, const char *string2, | 4 | int levenshtein(const char *string1, const char *string2, |
5 | int swap_penalty, int substition_penalty, | 5 | int swap_penalty, int substition_penalty, |
6 | int insertion_penalty, int deletion_penalty); | 6 | int insertion_penalty, int deletion_penalty); |
7 | 7 | ||
8 | #endif | 8 | #endif /* __PERF_LEVENSHTEIN_H */ |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 804e02382739..e509cd59c67d 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -3,6 +3,12 @@ | |||
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <stdio.h> | 5 | #include <stdio.h> |
6 | #include "debug.h" | ||
7 | |||
8 | const char *map_type__name[MAP__NR_TYPES] = { | ||
9 | [MAP__FUNCTION] = "Functions", | ||
10 | [MAP__VARIABLE] = "Variables", | ||
11 | }; | ||
6 | 12 | ||
7 | static inline int is_anon_memory(const char *filename) | 13 | static inline int is_anon_memory(const char *filename) |
8 | { | 14 | { |
@@ -19,13 +25,28 @@ static int strcommon(const char *pathname, char *cwd, int cwdlen) | |||
19 | return n; | 25 | return n; |
20 | } | 26 | } |
21 | 27 | ||
22 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen) | 28 | void map__init(struct map *self, enum map_type type, |
29 | u64 start, u64 end, u64 pgoff, struct dso *dso) | ||
30 | { | ||
31 | self->type = type; | ||
32 | self->start = start; | ||
33 | self->end = end; | ||
34 | self->pgoff = pgoff; | ||
35 | self->dso = dso; | ||
36 | self->map_ip = map__map_ip; | ||
37 | self->unmap_ip = map__unmap_ip; | ||
38 | RB_CLEAR_NODE(&self->rb_node); | ||
39 | } | ||
40 | |||
41 | struct map *map__new(struct mmap_event *event, enum map_type type, | ||
42 | char *cwd, int cwdlen) | ||
23 | { | 43 | { |
24 | struct map *self = malloc(sizeof(*self)); | 44 | struct map *self = malloc(sizeof(*self)); |
25 | 45 | ||
26 | if (self != NULL) { | 46 | if (self != NULL) { |
27 | const char *filename = event->filename; | 47 | const char *filename = event->filename; |
28 | char newfilename[PATH_MAX]; | 48 | char newfilename[PATH_MAX]; |
49 | struct dso *dso; | ||
29 | int anon; | 50 | int anon; |
30 | 51 | ||
31 | if (cwd) { | 52 | if (cwd) { |
@@ -45,18 +66,20 @@ static int strcommon(const char *pathname, char *cwd, int cwdlen) | |||
45 | filename = newfilename; | 66 | filename = newfilename; |
46 | } | 67 | } |
47 | 68 | ||
48 | self->start = event->start; | 69 | dso = dsos__findnew(filename); |
49 | self->end = event->start + event->len; | 70 | if (dso == NULL) |
50 | self->pgoff = event->pgoff; | ||
51 | |||
52 | self->dso = dsos__findnew(filename); | ||
53 | if (self->dso == NULL) | ||
54 | goto out_delete; | 71 | goto out_delete; |
55 | 72 | ||
56 | if (self->dso == vdso || anon) | 73 | map__init(self, type, event->start, event->start + event->len, |
57 | self->map_ip = vdso__map_ip; | 74 | event->pgoff, dso); |
58 | else | 75 | |
59 | self->map_ip = map__map_ip; | 76 | if (anon) { |
77 | set_identity: | ||
78 | self->map_ip = self->unmap_ip = identity__map_ip; | ||
79 | } else if (strcmp(filename, "[vdso]") == 0) { | ||
80 | dso__set_loaded(dso, self->type); | ||
81 | goto set_identity; | ||
82 | } | ||
60 | } | 83 | } |
61 | return self; | 84 | return self; |
62 | out_delete: | 85 | out_delete: |
@@ -64,6 +87,103 @@ out_delete: | |||
64 | return NULL; | 87 | return NULL; |
65 | } | 88 | } |
66 | 89 | ||
90 | void map__delete(struct map *self) | ||
91 | { | ||
92 | free(self); | ||
93 | } | ||
94 | |||
95 | void map__fixup_start(struct map *self) | ||
96 | { | ||
97 | struct rb_root *symbols = &self->dso->symbols[self->type]; | ||
98 | struct rb_node *nd = rb_first(symbols); | ||
99 | if (nd != NULL) { | ||
100 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | ||
101 | self->start = sym->start; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | void map__fixup_end(struct map *self) | ||
106 | { | ||
107 | struct rb_root *symbols = &self->dso->symbols[self->type]; | ||
108 | struct rb_node *nd = rb_last(symbols); | ||
109 | if (nd != NULL) { | ||
110 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | ||
111 | self->end = sym->end; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | #define DSO__DELETED "(deleted)" | ||
116 | |||
117 | int map__load(struct map *self, symbol_filter_t filter) | ||
118 | { | ||
119 | const char *name = self->dso->long_name; | ||
120 | int nr; | ||
121 | |||
122 | if (dso__loaded(self->dso, self->type)) | ||
123 | return 0; | ||
124 | |||
125 | nr = dso__load(self->dso, self, filter); | ||
126 | if (nr < 0) { | ||
127 | if (self->dso->has_build_id) { | ||
128 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
129 | |||
130 | build_id__sprintf(self->dso->build_id, | ||
131 | sizeof(self->dso->build_id), | ||
132 | sbuild_id); | ||
133 | pr_warning("%s with build id %s not found", | ||
134 | name, sbuild_id); | ||
135 | } else | ||
136 | pr_warning("Failed to open %s", name); | ||
137 | |||
138 | pr_warning(", continuing without symbols\n"); | ||
139 | return -1; | ||
140 | } else if (nr == 0) { | ||
141 | const size_t len = strlen(name); | ||
142 | const size_t real_len = len - sizeof(DSO__DELETED); | ||
143 | |||
144 | if (len > sizeof(DSO__DELETED) && | ||
145 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { | ||
146 | pr_warning("%.*s was updated, restart the long " | ||
147 | "running apps that use it!\n", | ||
148 | (int)real_len, name); | ||
149 | } else { | ||
150 | pr_warning("no symbols found in %s, maybe install " | ||
151 | "a debug package?\n", name); | ||
152 | } | ||
153 | |||
154 | return -1; | ||
155 | } | ||
156 | /* | ||
157 | * Only applies to the kernel, as its symtabs aren't relative like the | ||
158 | * module ones. | ||
159 | */ | ||
160 | if (self->dso->kernel) | ||
161 | map__reloc_vmlinux(self); | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | struct symbol *map__find_symbol(struct map *self, u64 addr, | ||
167 | symbol_filter_t filter) | ||
168 | { | ||
169 | if (map__load(self, filter) < 0) | ||
170 | return NULL; | ||
171 | |||
172 | return dso__find_symbol(self->dso, self->type, addr); | ||
173 | } | ||
174 | |||
175 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
176 | symbol_filter_t filter) | ||
177 | { | ||
178 | if (map__load(self, filter) < 0) | ||
179 | return NULL; | ||
180 | |||
181 | if (!dso__sorted_by_name(self->dso, self->type)) | ||
182 | dso__sort_by_name(self->dso, self->type); | ||
183 | |||
184 | return dso__find_symbol_by_name(self->dso, self->type, name); | ||
185 | } | ||
186 | |||
67 | struct map *map__clone(struct map *self) | 187 | struct map *map__clone(struct map *self) |
68 | { | 188 | { |
69 | struct map *map = malloc(sizeof(*self)); | 189 | struct map *map = malloc(sizeof(*self)); |
@@ -95,3 +215,23 @@ size_t map__fprintf(struct map *self, FILE *fp) | |||
95 | return fprintf(fp, " %Lx-%Lx %Lx %s\n", | 215 | return fprintf(fp, " %Lx-%Lx %Lx %s\n", |
96 | self->start, self->end, self->pgoff, self->dso->name); | 216 | self->start, self->end, self->pgoff, self->dso->name); |
97 | } | 217 | } |
218 | |||
219 | /* | ||
220 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. | ||
221 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases. | ||
222 | */ | ||
223 | u64 map__rip_2objdump(struct map *map, u64 rip) | ||
224 | { | ||
225 | u64 addr = map->dso->adjust_symbols ? | ||
226 | map->unmap_ip(map, rip) : /* RIP -> IP */ | ||
227 | rip; | ||
228 | return addr; | ||
229 | } | ||
230 | |||
231 | u64 map__objdump_2ip(struct map *map, u64 addr) | ||
232 | { | ||
233 | u64 ip = map->dso->adjust_symbols ? | ||
234 | addr : | ||
235 | map->unmap_ip(map, addr); /* RIP -> IP */ | ||
236 | return ip; | ||
237 | } | ||
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h new file mode 100644 index 000000000000..b756368076c6 --- /dev/null +++ b/tools/perf/util/map.h | |||
@@ -0,0 +1,94 @@ | |||
1 | #ifndef __PERF_MAP_H | ||
2 | #define __PERF_MAP_H | ||
3 | |||
4 | #include <linux/compiler.h> | ||
5 | #include <linux/list.h> | ||
6 | #include <linux/rbtree.h> | ||
7 | #include <linux/types.h> | ||
8 | |||
9 | enum map_type { | ||
10 | MAP__FUNCTION = 0, | ||
11 | MAP__VARIABLE, | ||
12 | }; | ||
13 | |||
14 | #define MAP__NR_TYPES (MAP__VARIABLE + 1) | ||
15 | |||
16 | extern const char *map_type__name[MAP__NR_TYPES]; | ||
17 | |||
18 | struct dso; | ||
19 | struct ref_reloc_sym; | ||
20 | struct map_groups; | ||
21 | |||
22 | struct map { | ||
23 | union { | ||
24 | struct rb_node rb_node; | ||
25 | struct list_head node; | ||
26 | }; | ||
27 | u64 start; | ||
28 | u64 end; | ||
29 | enum map_type type; | ||
30 | u64 pgoff; | ||
31 | |||
32 | /* ip -> dso rip */ | ||
33 | u64 (*map_ip)(struct map *, u64); | ||
34 | /* dso rip -> ip */ | ||
35 | u64 (*unmap_ip)(struct map *, u64); | ||
36 | |||
37 | struct dso *dso; | ||
38 | }; | ||
39 | |||
40 | struct kmap { | ||
41 | struct ref_reloc_sym *ref_reloc_sym; | ||
42 | struct map_groups *kmaps; | ||
43 | }; | ||
44 | |||
45 | static inline struct kmap *map__kmap(struct map *self) | ||
46 | { | ||
47 | return (struct kmap *)(self + 1); | ||
48 | } | ||
49 | |||
50 | static inline u64 map__map_ip(struct map *map, u64 ip) | ||
51 | { | ||
52 | return ip - map->start + map->pgoff; | ||
53 | } | ||
54 | |||
55 | static inline u64 map__unmap_ip(struct map *map, u64 ip) | ||
56 | { | ||
57 | return ip + map->start - map->pgoff; | ||
58 | } | ||
59 | |||
60 | static inline u64 identity__map_ip(struct map *map __used, u64 ip) | ||
61 | { | ||
62 | return ip; | ||
63 | } | ||
64 | |||
65 | |||
66 | /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ | ||
67 | u64 map__rip_2objdump(struct map *map, u64 rip); | ||
68 | u64 map__objdump_2ip(struct map *map, u64 addr); | ||
69 | |||
70 | struct symbol; | ||
71 | struct mmap_event; | ||
72 | |||
73 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | ||
74 | |||
75 | void map__init(struct map *self, enum map_type type, | ||
76 | u64 start, u64 end, u64 pgoff, struct dso *dso); | ||
77 | struct map *map__new(struct mmap_event *event, enum map_type, | ||
78 | char *cwd, int cwdlen); | ||
79 | void map__delete(struct map *self); | ||
80 | struct map *map__clone(struct map *self); | ||
81 | int map__overlap(struct map *l, struct map *r); | ||
82 | size_t map__fprintf(struct map *self, FILE *fp); | ||
83 | |||
84 | int map__load(struct map *self, symbol_filter_t filter); | ||
85 | struct symbol *map__find_symbol(struct map *self, | ||
86 | u64 addr, symbol_filter_t filter); | ||
87 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
88 | symbol_filter_t filter); | ||
89 | void map__fixup_start(struct map *self); | ||
90 | void map__fixup_end(struct map *self); | ||
91 | |||
92 | void map__reloc_vmlinux(struct map *self); | ||
93 | |||
94 | #endif /* __PERF_MAP_H */ | ||
diff --git a/tools/perf/util/module.c b/tools/perf/util/module.c deleted file mode 100644 index 0d8c85defcd2..000000000000 --- a/tools/perf/util/module.c +++ /dev/null | |||
@@ -1,545 +0,0 @@ | |||
1 | #include "util.h" | ||
2 | #include "../perf.h" | ||
3 | #include "string.h" | ||
4 | #include "module.h" | ||
5 | |||
6 | #include <libelf.h> | ||
7 | #include <libgen.h> | ||
8 | #include <gelf.h> | ||
9 | #include <elf.h> | ||
10 | #include <dirent.h> | ||
11 | #include <sys/utsname.h> | ||
12 | |||
13 | static unsigned int crc32(const char *p, unsigned int len) | ||
14 | { | ||
15 | int i; | ||
16 | unsigned int crc = 0; | ||
17 | |||
18 | while (len--) { | ||
19 | crc ^= *p++; | ||
20 | for (i = 0; i < 8; i++) | ||
21 | crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0); | ||
22 | } | ||
23 | return crc; | ||
24 | } | ||
25 | |||
26 | /* module section methods */ | ||
27 | |||
28 | struct sec_dso *sec_dso__new_dso(const char *name) | ||
29 | { | ||
30 | struct sec_dso *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
31 | |||
32 | if (self != NULL) { | ||
33 | strcpy(self->name, name); | ||
34 | self->secs = RB_ROOT; | ||
35 | self->find_section = sec_dso__find_section; | ||
36 | } | ||
37 | |||
38 | return self; | ||
39 | } | ||
40 | |||
41 | static void sec_dso__delete_section(struct section *self) | ||
42 | { | ||
43 | free(((void *)self)); | ||
44 | } | ||
45 | |||
46 | void sec_dso__delete_sections(struct sec_dso *self) | ||
47 | { | ||
48 | struct section *pos; | ||
49 | struct rb_node *next = rb_first(&self->secs); | ||
50 | |||
51 | while (next) { | ||
52 | pos = rb_entry(next, struct section, rb_node); | ||
53 | next = rb_next(&pos->rb_node); | ||
54 | rb_erase(&pos->rb_node, &self->secs); | ||
55 | sec_dso__delete_section(pos); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | void sec_dso__delete_self(struct sec_dso *self) | ||
60 | { | ||
61 | sec_dso__delete_sections(self); | ||
62 | free(self); | ||
63 | } | ||
64 | |||
65 | static void sec_dso__insert_section(struct sec_dso *self, struct section *sec) | ||
66 | { | ||
67 | struct rb_node **p = &self->secs.rb_node; | ||
68 | struct rb_node *parent = NULL; | ||
69 | const u64 hash = sec->hash; | ||
70 | struct section *s; | ||
71 | |||
72 | while (*p != NULL) { | ||
73 | parent = *p; | ||
74 | s = rb_entry(parent, struct section, rb_node); | ||
75 | if (hash < s->hash) | ||
76 | p = &(*p)->rb_left; | ||
77 | else | ||
78 | p = &(*p)->rb_right; | ||
79 | } | ||
80 | rb_link_node(&sec->rb_node, parent, p); | ||
81 | rb_insert_color(&sec->rb_node, &self->secs); | ||
82 | } | ||
83 | |||
84 | struct section *sec_dso__find_section(struct sec_dso *self, const char *name) | ||
85 | { | ||
86 | struct rb_node *n; | ||
87 | u64 hash; | ||
88 | int len; | ||
89 | |||
90 | if (self == NULL) | ||
91 | return NULL; | ||
92 | |||
93 | len = strlen(name); | ||
94 | hash = crc32(name, len); | ||
95 | |||
96 | n = self->secs.rb_node; | ||
97 | |||
98 | while (n) { | ||
99 | struct section *s = rb_entry(n, struct section, rb_node); | ||
100 | |||
101 | if (hash < s->hash) | ||
102 | n = n->rb_left; | ||
103 | else if (hash > s->hash) | ||
104 | n = n->rb_right; | ||
105 | else { | ||
106 | if (!strcmp(name, s->name)) | ||
107 | return s; | ||
108 | else | ||
109 | n = rb_next(&s->rb_node); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | return NULL; | ||
114 | } | ||
115 | |||
116 | static size_t sec_dso__fprintf_section(struct section *self, FILE *fp) | ||
117 | { | ||
118 | return fprintf(fp, "name:%s vma:%llx path:%s\n", | ||
119 | self->name, self->vma, self->path); | ||
120 | } | ||
121 | |||
122 | size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp) | ||
123 | { | ||
124 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | ||
125 | |||
126 | struct rb_node *nd; | ||
127 | for (nd = rb_first(&self->secs); nd; nd = rb_next(nd)) { | ||
128 | struct section *pos = rb_entry(nd, struct section, rb_node); | ||
129 | ret += sec_dso__fprintf_section(pos, fp); | ||
130 | } | ||
131 | |||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | static struct section *section__new(const char *name, const char *path) | ||
136 | { | ||
137 | struct section *self = calloc(1, sizeof(*self)); | ||
138 | |||
139 | if (!self) | ||
140 | goto out_failure; | ||
141 | |||
142 | self->name = calloc(1, strlen(name) + 1); | ||
143 | if (!self->name) | ||
144 | goto out_failure; | ||
145 | |||
146 | self->path = calloc(1, strlen(path) + 1); | ||
147 | if (!self->path) | ||
148 | goto out_failure; | ||
149 | |||
150 | strcpy(self->name, name); | ||
151 | strcpy(self->path, path); | ||
152 | self->hash = crc32(self->name, strlen(name)); | ||
153 | |||
154 | return self; | ||
155 | |||
156 | out_failure: | ||
157 | if (self) { | ||
158 | if (self->name) | ||
159 | free(self->name); | ||
160 | if (self->path) | ||
161 | free(self->path); | ||
162 | free(self); | ||
163 | } | ||
164 | |||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | /* module methods */ | ||
169 | |||
170 | struct mod_dso *mod_dso__new_dso(const char *name) | ||
171 | { | ||
172 | struct mod_dso *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
173 | |||
174 | if (self != NULL) { | ||
175 | strcpy(self->name, name); | ||
176 | self->mods = RB_ROOT; | ||
177 | self->find_module = mod_dso__find_module; | ||
178 | } | ||
179 | |||
180 | return self; | ||
181 | } | ||
182 | |||
183 | static void mod_dso__delete_module(struct module *self) | ||
184 | { | ||
185 | free(((void *)self)); | ||
186 | } | ||
187 | |||
188 | void mod_dso__delete_modules(struct mod_dso *self) | ||
189 | { | ||
190 | struct module *pos; | ||
191 | struct rb_node *next = rb_first(&self->mods); | ||
192 | |||
193 | while (next) { | ||
194 | pos = rb_entry(next, struct module, rb_node); | ||
195 | next = rb_next(&pos->rb_node); | ||
196 | rb_erase(&pos->rb_node, &self->mods); | ||
197 | mod_dso__delete_module(pos); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | void mod_dso__delete_self(struct mod_dso *self) | ||
202 | { | ||
203 | mod_dso__delete_modules(self); | ||
204 | free(self); | ||
205 | } | ||
206 | |||
207 | static void mod_dso__insert_module(struct mod_dso *self, struct module *mod) | ||
208 | { | ||
209 | struct rb_node **p = &self->mods.rb_node; | ||
210 | struct rb_node *parent = NULL; | ||
211 | const u64 hash = mod->hash; | ||
212 | struct module *m; | ||
213 | |||
214 | while (*p != NULL) { | ||
215 | parent = *p; | ||
216 | m = rb_entry(parent, struct module, rb_node); | ||
217 | if (hash < m->hash) | ||
218 | p = &(*p)->rb_left; | ||
219 | else | ||
220 | p = &(*p)->rb_right; | ||
221 | } | ||
222 | rb_link_node(&mod->rb_node, parent, p); | ||
223 | rb_insert_color(&mod->rb_node, &self->mods); | ||
224 | } | ||
225 | |||
226 | struct module *mod_dso__find_module(struct mod_dso *self, const char *name) | ||
227 | { | ||
228 | struct rb_node *n; | ||
229 | u64 hash; | ||
230 | int len; | ||
231 | |||
232 | if (self == NULL) | ||
233 | return NULL; | ||
234 | |||
235 | len = strlen(name); | ||
236 | hash = crc32(name, len); | ||
237 | |||
238 | n = self->mods.rb_node; | ||
239 | |||
240 | while (n) { | ||
241 | struct module *m = rb_entry(n, struct module, rb_node); | ||
242 | |||
243 | if (hash < m->hash) | ||
244 | n = n->rb_left; | ||
245 | else if (hash > m->hash) | ||
246 | n = n->rb_right; | ||
247 | else { | ||
248 | if (!strcmp(name, m->name)) | ||
249 | return m; | ||
250 | else | ||
251 | n = rb_next(&m->rb_node); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | return NULL; | ||
256 | } | ||
257 | |||
258 | static size_t mod_dso__fprintf_module(struct module *self, FILE *fp) | ||
259 | { | ||
260 | return fprintf(fp, "name:%s path:%s\n", self->name, self->path); | ||
261 | } | ||
262 | |||
263 | size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp) | ||
264 | { | ||
265 | struct rb_node *nd; | ||
266 | size_t ret; | ||
267 | |||
268 | ret = fprintf(fp, "dso: %s\n", self->name); | ||
269 | |||
270 | for (nd = rb_first(&self->mods); nd; nd = rb_next(nd)) { | ||
271 | struct module *pos = rb_entry(nd, struct module, rb_node); | ||
272 | |||
273 | ret += mod_dso__fprintf_module(pos, fp); | ||
274 | } | ||
275 | |||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | static struct module *module__new(const char *name, const char *path) | ||
280 | { | ||
281 | struct module *self = calloc(1, sizeof(*self)); | ||
282 | |||
283 | if (!self) | ||
284 | goto out_failure; | ||
285 | |||
286 | self->name = calloc(1, strlen(name) + 1); | ||
287 | if (!self->name) | ||
288 | goto out_failure; | ||
289 | |||
290 | self->path = calloc(1, strlen(path) + 1); | ||
291 | if (!self->path) | ||
292 | goto out_failure; | ||
293 | |||
294 | strcpy(self->name, name); | ||
295 | strcpy(self->path, path); | ||
296 | self->hash = crc32(self->name, strlen(name)); | ||
297 | |||
298 | return self; | ||
299 | |||
300 | out_failure: | ||
301 | if (self) { | ||
302 | if (self->name) | ||
303 | free(self->name); | ||
304 | if (self->path) | ||
305 | free(self->path); | ||
306 | free(self); | ||
307 | } | ||
308 | |||
309 | return NULL; | ||
310 | } | ||
311 | |||
312 | static int mod_dso__load_sections(struct module *mod) | ||
313 | { | ||
314 | int count = 0, path_len; | ||
315 | struct dirent *entry; | ||
316 | char *line = NULL; | ||
317 | char *dir_path; | ||
318 | DIR *dir; | ||
319 | size_t n; | ||
320 | |||
321 | path_len = strlen("/sys/module/"); | ||
322 | path_len += strlen(mod->name); | ||
323 | path_len += strlen("/sections/"); | ||
324 | |||
325 | dir_path = calloc(1, path_len + 1); | ||
326 | if (dir_path == NULL) | ||
327 | goto out_failure; | ||
328 | |||
329 | strcat(dir_path, "/sys/module/"); | ||
330 | strcat(dir_path, mod->name); | ||
331 | strcat(dir_path, "/sections/"); | ||
332 | |||
333 | dir = opendir(dir_path); | ||
334 | if (dir == NULL) | ||
335 | goto out_free; | ||
336 | |||
337 | while ((entry = readdir(dir))) { | ||
338 | struct section *section; | ||
339 | char *path, *vma; | ||
340 | int line_len; | ||
341 | FILE *file; | ||
342 | |||
343 | if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) | ||
344 | continue; | ||
345 | |||
346 | path = calloc(1, path_len + strlen(entry->d_name) + 1); | ||
347 | if (path == NULL) | ||
348 | break; | ||
349 | strcat(path, dir_path); | ||
350 | strcat(path, entry->d_name); | ||
351 | |||
352 | file = fopen(path, "r"); | ||
353 | if (file == NULL) { | ||
354 | free(path); | ||
355 | break; | ||
356 | } | ||
357 | |||
358 | line_len = getline(&line, &n, file); | ||
359 | if (line_len < 0) { | ||
360 | free(path); | ||
361 | fclose(file); | ||
362 | break; | ||
363 | } | ||
364 | |||
365 | if (!line) { | ||
366 | free(path); | ||
367 | fclose(file); | ||
368 | break; | ||
369 | } | ||
370 | |||
371 | line[--line_len] = '\0'; /* \n */ | ||
372 | |||
373 | vma = strstr(line, "0x"); | ||
374 | if (!vma) { | ||
375 | free(path); | ||
376 | fclose(file); | ||
377 | break; | ||
378 | } | ||
379 | vma += 2; | ||
380 | |||
381 | section = section__new(entry->d_name, path); | ||
382 | if (!section) { | ||
383 | fprintf(stderr, "load_sections: allocation error\n"); | ||
384 | free(path); | ||
385 | fclose(file); | ||
386 | break; | ||
387 | } | ||
388 | |||
389 | hex2u64(vma, §ion->vma); | ||
390 | sec_dso__insert_section(mod->sections, section); | ||
391 | |||
392 | free(path); | ||
393 | fclose(file); | ||
394 | count++; | ||
395 | } | ||
396 | |||
397 | closedir(dir); | ||
398 | free(line); | ||
399 | free(dir_path); | ||
400 | |||
401 | return count; | ||
402 | |||
403 | out_free: | ||
404 | free(dir_path); | ||
405 | |||
406 | out_failure: | ||
407 | return count; | ||
408 | } | ||
409 | |||
410 | static int mod_dso__load_module_paths(struct mod_dso *self) | ||
411 | { | ||
412 | struct utsname uts; | ||
413 | int count = 0, len, err = -1; | ||
414 | char *line = NULL; | ||
415 | FILE *file; | ||
416 | char *dpath, *dir; | ||
417 | size_t n; | ||
418 | |||
419 | if (uname(&uts) < 0) | ||
420 | return err; | ||
421 | |||
422 | len = strlen("/lib/modules/"); | ||
423 | len += strlen(uts.release); | ||
424 | len += strlen("/modules.dep"); | ||
425 | |||
426 | dpath = calloc(1, len + 1); | ||
427 | if (dpath == NULL) | ||
428 | return err; | ||
429 | |||
430 | strcat(dpath, "/lib/modules/"); | ||
431 | strcat(dpath, uts.release); | ||
432 | strcat(dpath, "/modules.dep"); | ||
433 | |||
434 | file = fopen(dpath, "r"); | ||
435 | if (file == NULL) | ||
436 | goto out_failure; | ||
437 | |||
438 | dir = dirname(dpath); | ||
439 | if (!dir) | ||
440 | goto out_failure; | ||
441 | strcat(dir, "/"); | ||
442 | |||
443 | while (!feof(file)) { | ||
444 | struct module *module; | ||
445 | char *name, *path, *tmp; | ||
446 | FILE *modfile; | ||
447 | int line_len; | ||
448 | |||
449 | line_len = getline(&line, &n, file); | ||
450 | if (line_len < 0) | ||
451 | break; | ||
452 | |||
453 | if (!line) | ||
454 | break; | ||
455 | |||
456 | line[--line_len] = '\0'; /* \n */ | ||
457 | |||
458 | path = strchr(line, ':'); | ||
459 | if (!path) | ||
460 | break; | ||
461 | *path = '\0'; | ||
462 | |||
463 | path = strdup(line); | ||
464 | if (!path) | ||
465 | break; | ||
466 | |||
467 | if (!strstr(path, dir)) { | ||
468 | if (strncmp(path, "kernel/", 7)) | ||
469 | break; | ||
470 | |||
471 | free(path); | ||
472 | path = calloc(1, strlen(dir) + strlen(line) + 1); | ||
473 | if (!path) | ||
474 | break; | ||
475 | strcat(path, dir); | ||
476 | strcat(path, line); | ||
477 | } | ||
478 | |||
479 | modfile = fopen(path, "r"); | ||
480 | if (modfile == NULL) | ||
481 | break; | ||
482 | fclose(modfile); | ||
483 | |||
484 | name = strdup(path); | ||
485 | if (!name) | ||
486 | break; | ||
487 | |||
488 | name = strtok(name, "/"); | ||
489 | tmp = name; | ||
490 | |||
491 | while (tmp) { | ||
492 | tmp = strtok(NULL, "/"); | ||
493 | if (tmp) | ||
494 | name = tmp; | ||
495 | } | ||
496 | |||
497 | name = strsep(&name, "."); | ||
498 | if (!name) | ||
499 | break; | ||
500 | |||
501 | /* Quirk: replace '-' with '_' in all modules */ | ||
502 | for (len = strlen(name); len; len--) { | ||
503 | if (*(name+len) == '-') | ||
504 | *(name+len) = '_'; | ||
505 | } | ||
506 | |||
507 | module = module__new(name, path); | ||
508 | if (!module) | ||
509 | break; | ||
510 | mod_dso__insert_module(self, module); | ||
511 | |||
512 | module->sections = sec_dso__new_dso("sections"); | ||
513 | if (!module->sections) | ||
514 | break; | ||
515 | |||
516 | module->active = mod_dso__load_sections(module); | ||
517 | |||
518 | if (module->active > 0) | ||
519 | count++; | ||
520 | } | ||
521 | |||
522 | if (feof(file)) | ||
523 | err = count; | ||
524 | else | ||
525 | fprintf(stderr, "load_module_paths: modules.dep parsing failure!\n"); | ||
526 | |||
527 | out_failure: | ||
528 | if (dpath) | ||
529 | free(dpath); | ||
530 | if (file) | ||
531 | fclose(file); | ||
532 | if (line) | ||
533 | free(line); | ||
534 | |||
535 | return err; | ||
536 | } | ||
537 | |||
538 | int mod_dso__load_modules(struct mod_dso *dso) | ||
539 | { | ||
540 | int err; | ||
541 | |||
542 | err = mod_dso__load_module_paths(dso); | ||
543 | |||
544 | return err; | ||
545 | } | ||
diff --git a/tools/perf/util/module.h b/tools/perf/util/module.h deleted file mode 100644 index 8a592ef641ca..000000000000 --- a/tools/perf/util/module.h +++ /dev/null | |||
@@ -1,53 +0,0 @@ | |||
1 | #ifndef _PERF_MODULE_ | ||
2 | #define _PERF_MODULE_ 1 | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include "../types.h" | ||
6 | #include <linux/list.h> | ||
7 | #include <linux/rbtree.h> | ||
8 | |||
9 | struct section { | ||
10 | struct rb_node rb_node; | ||
11 | u64 hash; | ||
12 | u64 vma; | ||
13 | char *name; | ||
14 | char *path; | ||
15 | }; | ||
16 | |||
17 | struct sec_dso { | ||
18 | struct list_head node; | ||
19 | struct rb_root secs; | ||
20 | struct section *(*find_section)(struct sec_dso *, const char *name); | ||
21 | char name[0]; | ||
22 | }; | ||
23 | |||
24 | struct module { | ||
25 | struct rb_node rb_node; | ||
26 | u64 hash; | ||
27 | char *name; | ||
28 | char *path; | ||
29 | struct sec_dso *sections; | ||
30 | int active; | ||
31 | }; | ||
32 | |||
33 | struct mod_dso { | ||
34 | struct list_head node; | ||
35 | struct rb_root mods; | ||
36 | struct module *(*find_module)(struct mod_dso *, const char *name); | ||
37 | char name[0]; | ||
38 | }; | ||
39 | |||
40 | struct sec_dso *sec_dso__new_dso(const char *name); | ||
41 | void sec_dso__delete_sections(struct sec_dso *self); | ||
42 | void sec_dso__delete_self(struct sec_dso *self); | ||
43 | size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp); | ||
44 | struct section *sec_dso__find_section(struct sec_dso *self, const char *name); | ||
45 | |||
46 | struct mod_dso *mod_dso__new_dso(const char *name); | ||
47 | void mod_dso__delete_modules(struct mod_dso *self); | ||
48 | void mod_dso__delete_self(struct mod_dso *self); | ||
49 | size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp); | ||
50 | struct module *mod_dso__find_module(struct mod_dso *self, const char *name); | ||
51 | int mod_dso__load_modules(struct mod_dso *dso); | ||
52 | |||
53 | #endif /* _PERF_MODULE_ */ | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 8cfb48cbbea0..05d0c5c2030c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -1,4 +1,4 @@ | |||
1 | 1 | #include "../../../include/linux/hw_breakpoint.h" | |
2 | #include "util.h" | 2 | #include "util.h" |
3 | #include "../perf.h" | 3 | #include "../perf.h" |
4 | #include "parse-options.h" | 4 | #include "parse-options.h" |
@@ -7,10 +7,12 @@ | |||
7 | #include "string.h" | 7 | #include "string.h" |
8 | #include "cache.h" | 8 | #include "cache.h" |
9 | #include "header.h" | 9 | #include "header.h" |
10 | #include "debugfs.h" | ||
10 | 11 | ||
11 | int nr_counters; | 12 | int nr_counters; |
12 | 13 | ||
13 | struct perf_event_attr attrs[MAX_COUNTERS]; | 14 | struct perf_event_attr attrs[MAX_COUNTERS]; |
15 | char *filters[MAX_COUNTERS]; | ||
14 | 16 | ||
15 | struct event_symbol { | 17 | struct event_symbol { |
16 | u8 type; | 18 | u8 type; |
@@ -46,6 +48,8 @@ static struct event_symbol event_symbols[] = { | |||
46 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, | 48 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, |
47 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, | 49 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, |
48 | { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, | 50 | { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, |
51 | { CSW(ALIGNMENT_FAULTS), "alignment-faults", "" }, | ||
52 | { CSW(EMULATION_FAULTS), "emulation-faults", "" }, | ||
49 | }; | 53 | }; |
50 | 54 | ||
51 | #define __PERF_EVENT_FIELD(config, name) \ | 55 | #define __PERF_EVENT_FIELD(config, name) \ |
@@ -74,6 +78,8 @@ static const char *sw_event_names[] = { | |||
74 | "CPU-migrations", | 78 | "CPU-migrations", |
75 | "minor-faults", | 79 | "minor-faults", |
76 | "major-faults", | 80 | "major-faults", |
81 | "alignment-faults", | ||
82 | "emulation-faults", | ||
77 | }; | 83 | }; |
78 | 84 | ||
79 | #define MAX_ALIASES 8 | 85 | #define MAX_ALIASES 8 |
@@ -148,16 +154,6 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) | |||
148 | 154 | ||
149 | #define MAX_EVENT_LENGTH 512 | 155 | #define MAX_EVENT_LENGTH 512 |
150 | 156 | ||
151 | int valid_debugfs_mount(const char *debugfs) | ||
152 | { | ||
153 | struct statfs st_fs; | ||
154 | |||
155 | if (statfs(debugfs, &st_fs) < 0) | ||
156 | return -ENOENT; | ||
157 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
158 | return -ENOENT; | ||
159 | return 0; | ||
160 | } | ||
161 | 157 | ||
162 | struct tracepoint_path *tracepoint_id_to_path(u64 config) | 158 | struct tracepoint_path *tracepoint_id_to_path(u64 config) |
163 | { | 159 | { |
@@ -170,7 +166,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
170 | char evt_path[MAXPATHLEN]; | 166 | char evt_path[MAXPATHLEN]; |
171 | char dir_path[MAXPATHLEN]; | 167 | char dir_path[MAXPATHLEN]; |
172 | 168 | ||
173 | if (valid_debugfs_mount(debugfs_path)) | 169 | if (debugfs_valid_mountpoint(debugfs_path)) |
174 | return NULL; | 170 | return NULL; |
175 | 171 | ||
176 | sys_dir = opendir(debugfs_path); | 172 | sys_dir = opendir(debugfs_path); |
@@ -201,7 +197,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
201 | if (id == config) { | 197 | if (id == config) { |
202 | closedir(evt_dir); | 198 | closedir(evt_dir); |
203 | closedir(sys_dir); | 199 | closedir(sys_dir); |
204 | path = calloc(1, sizeof(path)); | 200 | path = zalloc(sizeof(*path)); |
205 | path->system = malloc(MAX_EVENT_LENGTH); | 201 | path->system = malloc(MAX_EVENT_LENGTH); |
206 | if (!path->system) { | 202 | if (!path->system) { |
207 | free(path); | 203 | free(path); |
@@ -454,7 +450,8 @@ parse_single_tracepoint_event(char *sys_name, | |||
454 | /* sys + ':' + event + ':' + flags*/ | 450 | /* sys + ':' + event + ':' + flags*/ |
455 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 451 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
456 | static enum event_result | 452 | static enum event_result |
457 | parse_subsystem_tracepoint_event(char *sys_name, char *flags) | 453 | parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, |
454 | char *flags) | ||
458 | { | 455 | { |
459 | char evt_path[MAXPATHLEN]; | 456 | char evt_path[MAXPATHLEN]; |
460 | struct dirent *evt_ent; | 457 | struct dirent *evt_ent; |
@@ -471,7 +468,6 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
471 | while ((evt_ent = readdir(evt_dir))) { | 468 | while ((evt_ent = readdir(evt_dir))) { |
472 | char event_opt[MAX_EVOPT_LEN + 1]; | 469 | char event_opt[MAX_EVOPT_LEN + 1]; |
473 | int len; | 470 | int len; |
474 | unsigned int rem = MAX_EVOPT_LEN; | ||
475 | 471 | ||
476 | if (!strcmp(evt_ent->d_name, ".") | 472 | if (!strcmp(evt_ent->d_name, ".") |
477 | || !strcmp(evt_ent->d_name, "..") | 473 | || !strcmp(evt_ent->d_name, "..") |
@@ -479,20 +475,15 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
479 | || !strcmp(evt_ent->d_name, "filter")) | 475 | || !strcmp(evt_ent->d_name, "filter")) |
480 | continue; | 476 | continue; |
481 | 477 | ||
482 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name, | 478 | if (!strglobmatch(evt_ent->d_name, evt_exp)) |
483 | evt_ent->d_name); | 479 | continue; |
480 | |||
481 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, | ||
482 | evt_ent->d_name, flags ? ":" : "", | ||
483 | flags ?: ""); | ||
484 | if (len < 0) | 484 | if (len < 0) |
485 | return EVT_FAILED; | 485 | return EVT_FAILED; |
486 | 486 | ||
487 | rem -= len; | ||
488 | if (flags) { | ||
489 | if (rem < strlen(flags) + 1) | ||
490 | return EVT_FAILED; | ||
491 | |||
492 | strcat(event_opt, ":"); | ||
493 | strcat(event_opt, flags); | ||
494 | } | ||
495 | |||
496 | if (parse_events(NULL, event_opt, 0)) | 487 | if (parse_events(NULL, event_opt, 0)) |
497 | return EVT_FAILED; | 488 | return EVT_FAILED; |
498 | } | 489 | } |
@@ -509,7 +500,7 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
509 | char sys_name[MAX_EVENT_LENGTH]; | 500 | char sys_name[MAX_EVENT_LENGTH]; |
510 | unsigned int sys_length, evt_length; | 501 | unsigned int sys_length, evt_length; |
511 | 502 | ||
512 | if (valid_debugfs_mount(debugfs_path)) | 503 | if (debugfs_valid_mountpoint(debugfs_path)) |
513 | return 0; | 504 | return 0; |
514 | 505 | ||
515 | evt_name = strchr(*strp, ':'); | 506 | evt_name = strchr(*strp, ':'); |
@@ -535,15 +526,91 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
535 | if (evt_length >= MAX_EVENT_LENGTH) | 526 | if (evt_length >= MAX_EVENT_LENGTH) |
536 | return EVT_FAILED; | 527 | return EVT_FAILED; |
537 | 528 | ||
538 | if (!strcmp(evt_name, "*")) { | 529 | if (strpbrk(evt_name, "*?")) { |
539 | *strp = evt_name + evt_length; | 530 | *strp = evt_name + evt_length; |
540 | return parse_subsystem_tracepoint_event(sys_name, flags); | 531 | return parse_multiple_tracepoint_event(sys_name, evt_name, |
532 | flags); | ||
541 | } else | 533 | } else |
542 | return parse_single_tracepoint_event(sys_name, evt_name, | 534 | return parse_single_tracepoint_event(sys_name, evt_name, |
543 | evt_length, flags, | 535 | evt_length, flags, |
544 | attr, strp); | 536 | attr, strp); |
545 | } | 537 | } |
546 | 538 | ||
539 | static enum event_result | ||
540 | parse_breakpoint_type(const char *type, const char **strp, | ||
541 | struct perf_event_attr *attr) | ||
542 | { | ||
543 | int i; | ||
544 | |||
545 | for (i = 0; i < 3; i++) { | ||
546 | if (!type[i]) | ||
547 | break; | ||
548 | |||
549 | switch (type[i]) { | ||
550 | case 'r': | ||
551 | attr->bp_type |= HW_BREAKPOINT_R; | ||
552 | break; | ||
553 | case 'w': | ||
554 | attr->bp_type |= HW_BREAKPOINT_W; | ||
555 | break; | ||
556 | case 'x': | ||
557 | attr->bp_type |= HW_BREAKPOINT_X; | ||
558 | break; | ||
559 | default: | ||
560 | return EVT_FAILED; | ||
561 | } | ||
562 | } | ||
563 | if (!attr->bp_type) /* Default */ | ||
564 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
565 | |||
566 | *strp = type + i; | ||
567 | |||
568 | return EVT_HANDLED; | ||
569 | } | ||
570 | |||
571 | static enum event_result | ||
572 | parse_breakpoint_event(const char **strp, struct perf_event_attr *attr) | ||
573 | { | ||
574 | const char *target; | ||
575 | const char *type; | ||
576 | char *endaddr; | ||
577 | u64 addr; | ||
578 | enum event_result err; | ||
579 | |||
580 | target = strchr(*strp, ':'); | ||
581 | if (!target) | ||
582 | return EVT_FAILED; | ||
583 | |||
584 | if (strncmp(*strp, "mem", target - *strp) != 0) | ||
585 | return EVT_FAILED; | ||
586 | |||
587 | target++; | ||
588 | |||
589 | addr = strtoull(target, &endaddr, 0); | ||
590 | if (target == endaddr) | ||
591 | return EVT_FAILED; | ||
592 | |||
593 | attr->bp_addr = addr; | ||
594 | *strp = endaddr; | ||
595 | |||
596 | type = strchr(target, ':'); | ||
597 | |||
598 | /* If no type is defined, just rw as default */ | ||
599 | if (!type) { | ||
600 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
601 | } else { | ||
602 | err = parse_breakpoint_type(++type, strp, attr); | ||
603 | if (err == EVT_FAILED) | ||
604 | return EVT_FAILED; | ||
605 | } | ||
606 | |||
607 | /* We should find a nice way to override the access type */ | ||
608 | attr->bp_len = HW_BREAKPOINT_LEN_4; | ||
609 | attr->type = PERF_TYPE_BREAKPOINT; | ||
610 | |||
611 | return EVT_HANDLED; | ||
612 | } | ||
613 | |||
547 | static int check_events(const char *str, unsigned int i) | 614 | static int check_events(const char *str, unsigned int i) |
548 | { | 615 | { |
549 | int n; | 616 | int n; |
@@ -677,6 +744,12 @@ parse_event_symbols(const char **str, struct perf_event_attr *attr) | |||
677 | if (ret != EVT_FAILED) | 744 | if (ret != EVT_FAILED) |
678 | goto modifier; | 745 | goto modifier; |
679 | 746 | ||
747 | ret = parse_breakpoint_event(str, attr); | ||
748 | if (ret != EVT_FAILED) | ||
749 | goto modifier; | ||
750 | |||
751 | fprintf(stderr, "invalid or unsupported event: '%s'\n", *str); | ||
752 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
680 | return EVT_FAILED; | 753 | return EVT_FAILED; |
681 | 754 | ||
682 | modifier: | 755 | modifier: |
@@ -685,11 +758,11 @@ modifier: | |||
685 | return ret; | 758 | return ret; |
686 | } | 759 | } |
687 | 760 | ||
688 | static void store_event_type(const char *orgname) | 761 | static int store_event_type(const char *orgname) |
689 | { | 762 | { |
690 | char filename[PATH_MAX], *c; | 763 | char filename[PATH_MAX], *c; |
691 | FILE *file; | 764 | FILE *file; |
692 | int id; | 765 | int id, n; |
693 | 766 | ||
694 | sprintf(filename, "%s/", debugfs_path); | 767 | sprintf(filename, "%s/", debugfs_path); |
695 | strncat(filename, orgname, strlen(orgname)); | 768 | strncat(filename, orgname, strlen(orgname)); |
@@ -701,21 +774,24 @@ static void store_event_type(const char *orgname) | |||
701 | 774 | ||
702 | file = fopen(filename, "r"); | 775 | file = fopen(filename, "r"); |
703 | if (!file) | 776 | if (!file) |
704 | return; | 777 | return 0; |
705 | if (fscanf(file, "%i", &id) < 1) | 778 | n = fscanf(file, "%i", &id); |
706 | die("cannot store event ID"); | ||
707 | fclose(file); | 779 | fclose(file); |
708 | perf_header__push_event(id, orgname); | 780 | if (n < 1) { |
781 | pr_err("cannot store event ID\n"); | ||
782 | return -EINVAL; | ||
783 | } | ||
784 | return perf_header__push_event(id, orgname); | ||
709 | } | 785 | } |
710 | 786 | ||
711 | |||
712 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 787 | int parse_events(const struct option *opt __used, const char *str, int unset __used) |
713 | { | 788 | { |
714 | struct perf_event_attr attr; | 789 | struct perf_event_attr attr; |
715 | enum event_result ret; | 790 | enum event_result ret; |
716 | 791 | ||
717 | if (strchr(str, ':')) | 792 | if (strchr(str, ':')) |
718 | store_event_type(str); | 793 | if (store_event_type(str) < 0) |
794 | return -1; | ||
719 | 795 | ||
720 | for (;;) { | 796 | for (;;) { |
721 | if (nr_counters == MAX_COUNTERS) | 797 | if (nr_counters == MAX_COUNTERS) |
@@ -745,12 +821,35 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
745 | return 0; | 821 | return 0; |
746 | } | 822 | } |
747 | 823 | ||
824 | int parse_filter(const struct option *opt __used, const char *str, | ||
825 | int unset __used) | ||
826 | { | ||
827 | int i = nr_counters - 1; | ||
828 | int len = strlen(str); | ||
829 | |||
830 | if (i < 0 || attrs[i].type != PERF_TYPE_TRACEPOINT) { | ||
831 | fprintf(stderr, | ||
832 | "-F option should follow a -e tracepoint option\n"); | ||
833 | return -1; | ||
834 | } | ||
835 | |||
836 | filters[i] = malloc(len + 1); | ||
837 | if (!filters[i]) { | ||
838 | fprintf(stderr, "not enough memory to hold filter string\n"); | ||
839 | return -1; | ||
840 | } | ||
841 | strcpy(filters[i], str); | ||
842 | |||
843 | return 0; | ||
844 | } | ||
845 | |||
748 | static const char * const event_type_descriptors[] = { | 846 | static const char * const event_type_descriptors[] = { |
749 | "", | ||
750 | "Hardware event", | 847 | "Hardware event", |
751 | "Software event", | 848 | "Software event", |
752 | "Tracepoint event", | 849 | "Tracepoint event", |
753 | "Hardware cache event", | 850 | "Hardware cache event", |
851 | "Raw hardware event descriptor", | ||
852 | "Hardware breakpoint", | ||
754 | }; | 853 | }; |
755 | 854 | ||
756 | /* | 855 | /* |
@@ -764,7 +863,7 @@ static void print_tracepoint_events(void) | |||
764 | char evt_path[MAXPATHLEN]; | 863 | char evt_path[MAXPATHLEN]; |
765 | char dir_path[MAXPATHLEN]; | 864 | char dir_path[MAXPATHLEN]; |
766 | 865 | ||
767 | if (valid_debugfs_mount(debugfs_path)) | 866 | if (debugfs_valid_mountpoint(debugfs_path)) |
768 | return; | 867 | return; |
769 | 868 | ||
770 | sys_dir = opendir(debugfs_path); | 869 | sys_dir = opendir(debugfs_path); |
@@ -782,8 +881,8 @@ static void print_tracepoint_events(void) | |||
782 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | 881 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { |
783 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | 882 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
784 | sys_dirent.d_name, evt_dirent.d_name); | 883 | sys_dirent.d_name, evt_dirent.d_name); |
785 | fprintf(stderr, " %-42s [%s]\n", evt_path, | 884 | printf(" %-42s [%s]\n", evt_path, |
786 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | 885 | event_type_descriptors[PERF_TYPE_TRACEPOINT]); |
787 | } | 886 | } |
788 | closedir(evt_dir); | 887 | closedir(evt_dir); |
789 | } | 888 | } |
@@ -799,28 +898,26 @@ void print_events(void) | |||
799 | unsigned int i, type, op, prev_type = -1; | 898 | unsigned int i, type, op, prev_type = -1; |
800 | char name[40]; | 899 | char name[40]; |
801 | 900 | ||
802 | fprintf(stderr, "\n"); | 901 | printf("\n"); |
803 | fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); | 902 | printf("List of pre-defined events (to be used in -e):\n"); |
804 | 903 | ||
805 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 904 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
806 | type = syms->type + 1; | 905 | type = syms->type; |
807 | if (type >= ARRAY_SIZE(event_type_descriptors)) | ||
808 | type = 0; | ||
809 | 906 | ||
810 | if (type != prev_type) | 907 | if (type != prev_type) |
811 | fprintf(stderr, "\n"); | 908 | printf("\n"); |
812 | 909 | ||
813 | if (strlen(syms->alias)) | 910 | if (strlen(syms->alias)) |
814 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); | 911 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); |
815 | else | 912 | else |
816 | strcpy(name, syms->symbol); | 913 | strcpy(name, syms->symbol); |
817 | fprintf(stderr, " %-42s [%s]\n", name, | 914 | printf(" %-42s [%s]\n", name, |
818 | event_type_descriptors[type]); | 915 | event_type_descriptors[type]); |
819 | 916 | ||
820 | prev_type = type; | 917 | prev_type = type; |
821 | } | 918 | } |
822 | 919 | ||
823 | fprintf(stderr, "\n"); | 920 | printf("\n"); |
824 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | 921 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { |
825 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | 922 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { |
826 | /* skip invalid cache type */ | 923 | /* skip invalid cache type */ |
@@ -828,17 +925,22 @@ void print_events(void) | |||
828 | continue; | 925 | continue; |
829 | 926 | ||
830 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | 927 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { |
831 | fprintf(stderr, " %-42s [%s]\n", | 928 | printf(" %-42s [%s]\n", |
832 | event_cache_name(type, op, i), | 929 | event_cache_name(type, op, i), |
833 | event_type_descriptors[4]); | 930 | event_type_descriptors[PERF_TYPE_HW_CACHE]); |
834 | } | 931 | } |
835 | } | 932 | } |
836 | } | 933 | } |
837 | 934 | ||
838 | fprintf(stderr, "\n"); | 935 | printf("\n"); |
839 | fprintf(stderr, " %-42s [raw hardware event descriptor]\n", | 936 | printf(" %-42s [%s]\n", |
840 | "rNNN"); | 937 | "rNNN", event_type_descriptors[PERF_TYPE_RAW]); |
841 | fprintf(stderr, "\n"); | 938 | printf("\n"); |
939 | |||
940 | printf(" %-42s [%s]\n", | ||
941 | "mem:<addr>[:access]", | ||
942 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); | ||
943 | printf("\n"); | ||
842 | 944 | ||
843 | print_tracepoint_events(); | 945 | print_tracepoint_events(); |
844 | 946 | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 30c608112845..b8c1f64bc935 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef _PARSE_EVENTS_H | 1 | #ifndef __PERF_PARSE_EVENTS_H |
2 | #define _PARSE_EVENTS_H | 2 | #define __PERF_PARSE_EVENTS_H |
3 | /* | 3 | /* |
4 | * Parse symbolic events/counts passed in as options: | 4 | * Parse symbolic events/counts passed in as options: |
5 | */ | 5 | */ |
@@ -17,11 +17,13 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config); | |||
17 | extern int nr_counters; | 17 | extern int nr_counters; |
18 | 18 | ||
19 | extern struct perf_event_attr attrs[MAX_COUNTERS]; | 19 | extern struct perf_event_attr attrs[MAX_COUNTERS]; |
20 | extern char *filters[MAX_COUNTERS]; | ||
20 | 21 | ||
21 | extern const char *event_name(int ctr); | 22 | extern const char *event_name(int ctr); |
22 | extern const char *__event_name(int type, u64 config); | 23 | extern const char *__event_name(int type, u64 config); |
23 | 24 | ||
24 | extern int parse_events(const struct option *opt, const char *str, int unset); | 25 | extern int parse_events(const struct option *opt, const char *str, int unset); |
26 | extern int parse_filter(const struct option *opt, const char *str, int unset); | ||
25 | 27 | ||
26 | #define EVENTS_HELP_MAX (128*1024) | 28 | #define EVENTS_HELP_MAX (128*1024) |
27 | 29 | ||
@@ -31,4 +33,4 @@ extern char debugfs_path[]; | |||
31 | extern int valid_debugfs_mount(const char *debugfs); | 33 | extern int valid_debugfs_mount(const char *debugfs); |
32 | 34 | ||
33 | 35 | ||
34 | #endif /* _PARSE_EVENTS_H */ | 36 | #endif /* __PERF_PARSE_EVENTS_H */ |
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 6d8af48c925e..efebd5b476b3 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c | |||
@@ -430,6 +430,9 @@ int usage_with_options_internal(const char * const *usagestr, | |||
430 | pos = fprintf(stderr, " "); | 430 | pos = fprintf(stderr, " "); |
431 | if (opts->short_name) | 431 | if (opts->short_name) |
432 | pos += fprintf(stderr, "-%c", opts->short_name); | 432 | pos += fprintf(stderr, "-%c", opts->short_name); |
433 | else | ||
434 | pos += fprintf(stderr, " "); | ||
435 | |||
433 | if (opts->long_name && opts->short_name) | 436 | if (opts->long_name && opts->short_name) |
434 | pos += fprintf(stderr, ", "); | 437 | pos += fprintf(stderr, ", "); |
435 | if (opts->long_name) | 438 | if (opts->long_name) |
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 2ee248ff27e5..948805af43c2 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef PARSE_OPTIONS_H | 1 | #ifndef __PERF_PARSE_OPTIONS_H |
2 | #define PARSE_OPTIONS_H | 2 | #define __PERF_PARSE_OPTIONS_H |
3 | 3 | ||
4 | enum parse_opt_type { | 4 | enum parse_opt_type { |
5 | /* special types */ | 5 | /* special types */ |
@@ -174,4 +174,4 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int); | |||
174 | 174 | ||
175 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); | 175 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); |
176 | 176 | ||
177 | #endif | 177 | #endif /* __PERF_PARSE_OPTIONS_H */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c new file mode 100644 index 000000000000..7c004b6ef24f --- /dev/null +++ b/tools/perf/util/probe-event.c | |||
@@ -0,0 +1,802 @@ | |||
1 | /* | ||
2 | * probe-event.c : perf-probe definition to kprobe_events format converter | ||
3 | * | ||
4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | ||
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 | #define _GNU_SOURCE | ||
23 | #include <sys/utsname.h> | ||
24 | #include <sys/types.h> | ||
25 | #include <sys/stat.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <errno.h> | ||
28 | #include <stdio.h> | ||
29 | #include <unistd.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <string.h> | ||
32 | #include <stdarg.h> | ||
33 | #include <limits.h> | ||
34 | |||
35 | #undef _GNU_SOURCE | ||
36 | #include "event.h" | ||
37 | #include "string.h" | ||
38 | #include "strlist.h" | ||
39 | #include "debug.h" | ||
40 | #include "cache.h" | ||
41 | #include "color.h" | ||
42 | #include "parse-events.h" /* For debugfs_path */ | ||
43 | #include "probe-event.h" | ||
44 | |||
45 | #define MAX_CMDLEN 256 | ||
46 | #define MAX_PROBE_ARGS 128 | ||
47 | #define PERFPROBE_GROUP "probe" | ||
48 | |||
49 | #define semantic_error(msg ...) die("Semantic error :" msg) | ||
50 | |||
51 | /* If there is no space to write, returns -E2BIG. */ | ||
52 | static int e_snprintf(char *str, size_t size, const char *format, ...) | ||
53 | __attribute__((format(printf, 3, 4))); | ||
54 | |||
55 | static int e_snprintf(char *str, size_t size, const char *format, ...) | ||
56 | { | ||
57 | int ret; | ||
58 | va_list ap; | ||
59 | va_start(ap, format); | ||
60 | ret = vsnprintf(str, size, format, ap); | ||
61 | va_end(ap); | ||
62 | if (ret >= (int)size) | ||
63 | ret = -E2BIG; | ||
64 | return ret; | ||
65 | } | ||
66 | |||
67 | void parse_line_range_desc(const char *arg, struct line_range *lr) | ||
68 | { | ||
69 | const char *ptr; | ||
70 | char *tmp; | ||
71 | /* | ||
72 | * <Syntax> | ||
73 | * SRC:SLN[+NUM|-ELN] | ||
74 | * FUNC[:SLN[+NUM|-ELN]] | ||
75 | */ | ||
76 | ptr = strchr(arg, ':'); | ||
77 | if (ptr) { | ||
78 | lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0); | ||
79 | if (*tmp == '+') | ||
80 | lr->end = lr->start + (unsigned int)strtoul(tmp + 1, | ||
81 | &tmp, 0); | ||
82 | else if (*tmp == '-') | ||
83 | lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0); | ||
84 | else | ||
85 | lr->end = 0; | ||
86 | pr_debug("Line range is %u to %u\n", lr->start, lr->end); | ||
87 | if (lr->end && lr->start > lr->end) | ||
88 | semantic_error("Start line must be smaller" | ||
89 | " than end line."); | ||
90 | if (*tmp != '\0') | ||
91 | semantic_error("Tailing with invalid character '%d'.", | ||
92 | *tmp); | ||
93 | tmp = strndup(arg, (ptr - arg)); | ||
94 | } else | ||
95 | tmp = strdup(arg); | ||
96 | |||
97 | if (strchr(tmp, '.')) | ||
98 | lr->file = tmp; | ||
99 | else | ||
100 | lr->function = tmp; | ||
101 | } | ||
102 | |||
103 | /* Check the name is good for event/group */ | ||
104 | static bool check_event_name(const char *name) | ||
105 | { | ||
106 | if (!isalpha(*name) && *name != '_') | ||
107 | return false; | ||
108 | while (*++name != '\0') { | ||
109 | if (!isalpha(*name) && !isdigit(*name) && *name != '_') | ||
110 | return false; | ||
111 | } | ||
112 | return true; | ||
113 | } | ||
114 | |||
115 | /* Parse probepoint definition. */ | ||
116 | static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | ||
117 | { | ||
118 | char *ptr, *tmp; | ||
119 | char c, nc = 0; | ||
120 | /* | ||
121 | * <Syntax> | ||
122 | * perf probe [EVENT=]SRC[:LN|;PTN] | ||
123 | * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT] | ||
124 | * | ||
125 | * TODO:Group name support | ||
126 | */ | ||
127 | |||
128 | ptr = strpbrk(arg, ";=@+%"); | ||
129 | if (ptr && *ptr == '=') { /* Event name */ | ||
130 | *ptr = '\0'; | ||
131 | tmp = ptr + 1; | ||
132 | ptr = strchr(arg, ':'); | ||
133 | if (ptr) /* Group name is not supported yet. */ | ||
134 | semantic_error("Group name is not supported yet."); | ||
135 | if (!check_event_name(arg)) | ||
136 | semantic_error("%s is bad for event name -it must " | ||
137 | "follow C symbol-naming rule.", arg); | ||
138 | pp->event = strdup(arg); | ||
139 | arg = tmp; | ||
140 | } | ||
141 | |||
142 | ptr = strpbrk(arg, ";:+@%"); | ||
143 | if (ptr) { | ||
144 | nc = *ptr; | ||
145 | *ptr++ = '\0'; | ||
146 | } | ||
147 | |||
148 | /* Check arg is function or file and copy it */ | ||
149 | if (strchr(arg, '.')) /* File */ | ||
150 | pp->file = strdup(arg); | ||
151 | else /* Function */ | ||
152 | pp->function = strdup(arg); | ||
153 | DIE_IF(pp->file == NULL && pp->function == NULL); | ||
154 | |||
155 | /* Parse other options */ | ||
156 | while (ptr) { | ||
157 | arg = ptr; | ||
158 | c = nc; | ||
159 | if (c == ';') { /* Lazy pattern must be the last part */ | ||
160 | pp->lazy_line = strdup(arg); | ||
161 | break; | ||
162 | } | ||
163 | ptr = strpbrk(arg, ";:+@%"); | ||
164 | if (ptr) { | ||
165 | nc = *ptr; | ||
166 | *ptr++ = '\0'; | ||
167 | } | ||
168 | switch (c) { | ||
169 | case ':': /* Line number */ | ||
170 | pp->line = strtoul(arg, &tmp, 0); | ||
171 | if (*tmp != '\0') | ||
172 | semantic_error("There is non-digit char" | ||
173 | " in line number."); | ||
174 | break; | ||
175 | case '+': /* Byte offset from a symbol */ | ||
176 | pp->offset = strtoul(arg, &tmp, 0); | ||
177 | if (*tmp != '\0') | ||
178 | semantic_error("There is non-digit character" | ||
179 | " in offset."); | ||
180 | break; | ||
181 | case '@': /* File name */ | ||
182 | if (pp->file) | ||
183 | semantic_error("SRC@SRC is not allowed."); | ||
184 | pp->file = strdup(arg); | ||
185 | DIE_IF(pp->file == NULL); | ||
186 | break; | ||
187 | case '%': /* Probe places */ | ||
188 | if (strcmp(arg, "return") == 0) { | ||
189 | pp->retprobe = 1; | ||
190 | } else /* Others not supported yet */ | ||
191 | semantic_error("%%%s is not supported.", arg); | ||
192 | break; | ||
193 | default: | ||
194 | DIE_IF("Program has a bug."); | ||
195 | break; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /* Exclusion check */ | ||
200 | if (pp->lazy_line && pp->line) | ||
201 | semantic_error("Lazy pattern can't be used with line number."); | ||
202 | |||
203 | if (pp->lazy_line && pp->offset) | ||
204 | semantic_error("Lazy pattern can't be used with offset."); | ||
205 | |||
206 | if (pp->line && pp->offset) | ||
207 | semantic_error("Offset can't be used with line number."); | ||
208 | |||
209 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) | ||
210 | semantic_error("File always requires line number or " | ||
211 | "lazy pattern."); | ||
212 | |||
213 | if (pp->offset && !pp->function) | ||
214 | semantic_error("Offset requires an entry function."); | ||
215 | |||
216 | if (pp->retprobe && !pp->function) | ||
217 | semantic_error("Return probe requires an entry function."); | ||
218 | |||
219 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) | ||
220 | semantic_error("Offset/Line/Lazy pattern can't be used with " | ||
221 | "return probe."); | ||
222 | |||
223 | pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n", | ||
224 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe, | ||
225 | pp->lazy_line); | ||
226 | } | ||
227 | |||
228 | /* Parse perf-probe event definition */ | ||
229 | void parse_perf_probe_event(const char *str, struct probe_point *pp, | ||
230 | bool *need_dwarf) | ||
231 | { | ||
232 | char **argv; | ||
233 | int argc, i; | ||
234 | |||
235 | *need_dwarf = false; | ||
236 | |||
237 | argv = argv_split(str, &argc); | ||
238 | if (!argv) | ||
239 | die("argv_split failed."); | ||
240 | if (argc > MAX_PROBE_ARGS + 1) | ||
241 | semantic_error("Too many arguments"); | ||
242 | |||
243 | /* Parse probe point */ | ||
244 | parse_perf_probe_probepoint(argv[0], pp); | ||
245 | if (pp->file || pp->line || pp->lazy_line) | ||
246 | *need_dwarf = true; | ||
247 | |||
248 | /* Copy arguments and ensure return probe has no C argument */ | ||
249 | pp->nr_args = argc - 1; | ||
250 | pp->args = zalloc(sizeof(char *) * pp->nr_args); | ||
251 | for (i = 0; i < pp->nr_args; i++) { | ||
252 | pp->args[i] = strdup(argv[i + 1]); | ||
253 | if (!pp->args[i]) | ||
254 | die("Failed to copy argument."); | ||
255 | if (is_c_varname(pp->args[i])) { | ||
256 | if (pp->retprobe) | ||
257 | semantic_error("You can't specify local" | ||
258 | " variable for kretprobe"); | ||
259 | *need_dwarf = true; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | argv_free(argv); | ||
264 | } | ||
265 | |||
266 | /* Parse kprobe_events event into struct probe_point */ | ||
267 | void parse_trace_kprobe_event(const char *str, struct probe_point *pp) | ||
268 | { | ||
269 | char pr; | ||
270 | char *p; | ||
271 | int ret, i, argc; | ||
272 | char **argv; | ||
273 | |||
274 | pr_debug("Parsing kprobe_events: %s\n", str); | ||
275 | argv = argv_split(str, &argc); | ||
276 | if (!argv) | ||
277 | die("argv_split failed."); | ||
278 | if (argc < 2) | ||
279 | semantic_error("Too less arguments."); | ||
280 | |||
281 | /* Scan event and group name. */ | ||
282 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", | ||
283 | &pr, (float *)(void *)&pp->group, | ||
284 | (float *)(void *)&pp->event); | ||
285 | if (ret != 3) | ||
286 | semantic_error("Failed to parse event name: %s", argv[0]); | ||
287 | pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); | ||
288 | |||
289 | pp->retprobe = (pr == 'r'); | ||
290 | |||
291 | /* Scan function name and offset */ | ||
292 | ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, | ||
293 | &pp->offset); | ||
294 | if (ret == 1) | ||
295 | pp->offset = 0; | ||
296 | |||
297 | /* kprobe_events doesn't have this information */ | ||
298 | pp->line = 0; | ||
299 | pp->file = NULL; | ||
300 | |||
301 | pp->nr_args = argc - 2; | ||
302 | pp->args = zalloc(sizeof(char *) * pp->nr_args); | ||
303 | for (i = 0; i < pp->nr_args; i++) { | ||
304 | p = strchr(argv[i + 2], '='); | ||
305 | if (p) /* We don't need which register is assigned. */ | ||
306 | *p = '\0'; | ||
307 | pp->args[i] = strdup(argv[i + 2]); | ||
308 | if (!pp->args[i]) | ||
309 | die("Failed to copy argument."); | ||
310 | } | ||
311 | |||
312 | argv_free(argv); | ||
313 | } | ||
314 | |||
315 | /* Synthesize only probe point (not argument) */ | ||
316 | int synthesize_perf_probe_point(struct probe_point *pp) | ||
317 | { | ||
318 | char *buf; | ||
319 | char offs[64] = "", line[64] = ""; | ||
320 | int ret; | ||
321 | |||
322 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | ||
323 | pp->found = 1; | ||
324 | if (!buf) | ||
325 | die("Failed to allocate memory by zalloc."); | ||
326 | if (pp->offset) { | ||
327 | ret = e_snprintf(offs, 64, "+%d", pp->offset); | ||
328 | if (ret <= 0) | ||
329 | goto error; | ||
330 | } | ||
331 | if (pp->line) { | ||
332 | ret = e_snprintf(line, 64, ":%d", pp->line); | ||
333 | if (ret <= 0) | ||
334 | goto error; | ||
335 | } | ||
336 | |||
337 | if (pp->function) | ||
338 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, | ||
339 | offs, pp->retprobe ? "%return" : "", line); | ||
340 | else | ||
341 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); | ||
342 | if (ret <= 0) { | ||
343 | error: | ||
344 | free(pp->probes[0]); | ||
345 | pp->probes[0] = NULL; | ||
346 | pp->found = 0; | ||
347 | } | ||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | int synthesize_perf_probe_event(struct probe_point *pp) | ||
352 | { | ||
353 | char *buf; | ||
354 | int i, len, ret; | ||
355 | |||
356 | len = synthesize_perf_probe_point(pp); | ||
357 | if (len < 0) | ||
358 | return 0; | ||
359 | |||
360 | buf = pp->probes[0]; | ||
361 | for (i = 0; i < pp->nr_args; i++) { | ||
362 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | ||
363 | pp->args[i]); | ||
364 | if (ret <= 0) | ||
365 | goto error; | ||
366 | len += ret; | ||
367 | } | ||
368 | pp->found = 1; | ||
369 | |||
370 | return pp->found; | ||
371 | error: | ||
372 | free(pp->probes[0]); | ||
373 | pp->probes[0] = NULL; | ||
374 | |||
375 | return ret; | ||
376 | } | ||
377 | |||
378 | int synthesize_trace_kprobe_event(struct probe_point *pp) | ||
379 | { | ||
380 | char *buf; | ||
381 | int i, len, ret; | ||
382 | |||
383 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | ||
384 | if (!buf) | ||
385 | die("Failed to allocate memory by zalloc."); | ||
386 | ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); | ||
387 | if (ret <= 0) | ||
388 | goto error; | ||
389 | len = ret; | ||
390 | |||
391 | for (i = 0; i < pp->nr_args; i++) { | ||
392 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | ||
393 | pp->args[i]); | ||
394 | if (ret <= 0) | ||
395 | goto error; | ||
396 | len += ret; | ||
397 | } | ||
398 | pp->found = 1; | ||
399 | |||
400 | return pp->found; | ||
401 | error: | ||
402 | free(pp->probes[0]); | ||
403 | pp->probes[0] = NULL; | ||
404 | |||
405 | return ret; | ||
406 | } | ||
407 | |||
408 | static int open_kprobe_events(int flags, int mode) | ||
409 | { | ||
410 | char buf[PATH_MAX]; | ||
411 | int ret; | ||
412 | |||
413 | ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path); | ||
414 | if (ret < 0) | ||
415 | die("Failed to make kprobe_events path."); | ||
416 | |||
417 | ret = open(buf, flags, mode); | ||
418 | if (ret < 0) { | ||
419 | if (errno == ENOENT) | ||
420 | die("kprobe_events file does not exist -" | ||
421 | " please rebuild with CONFIG_KPROBE_EVENT."); | ||
422 | else | ||
423 | die("Could not open kprobe_events file: %s", | ||
424 | strerror(errno)); | ||
425 | } | ||
426 | return ret; | ||
427 | } | ||
428 | |||
429 | /* Get raw string list of current kprobe_events */ | ||
430 | static struct strlist *get_trace_kprobe_event_rawlist(int fd) | ||
431 | { | ||
432 | int ret, idx; | ||
433 | FILE *fp; | ||
434 | char buf[MAX_CMDLEN]; | ||
435 | char *p; | ||
436 | struct strlist *sl; | ||
437 | |||
438 | sl = strlist__new(true, NULL); | ||
439 | |||
440 | fp = fdopen(dup(fd), "r"); | ||
441 | while (!feof(fp)) { | ||
442 | p = fgets(buf, MAX_CMDLEN, fp); | ||
443 | if (!p) | ||
444 | break; | ||
445 | |||
446 | idx = strlen(p) - 1; | ||
447 | if (p[idx] == '\n') | ||
448 | p[idx] = '\0'; | ||
449 | ret = strlist__add(sl, buf); | ||
450 | if (ret < 0) | ||
451 | die("strlist__add failed: %s", strerror(-ret)); | ||
452 | } | ||
453 | fclose(fp); | ||
454 | |||
455 | return sl; | ||
456 | } | ||
457 | |||
458 | /* Free and zero clear probe_point */ | ||
459 | static void clear_probe_point(struct probe_point *pp) | ||
460 | { | ||
461 | int i; | ||
462 | |||
463 | if (pp->event) | ||
464 | free(pp->event); | ||
465 | if (pp->group) | ||
466 | free(pp->group); | ||
467 | if (pp->function) | ||
468 | free(pp->function); | ||
469 | if (pp->file) | ||
470 | free(pp->file); | ||
471 | if (pp->lazy_line) | ||
472 | free(pp->lazy_line); | ||
473 | for (i = 0; i < pp->nr_args; i++) | ||
474 | free(pp->args[i]); | ||
475 | if (pp->args) | ||
476 | free(pp->args); | ||
477 | for (i = 0; i < pp->found; i++) | ||
478 | free(pp->probes[i]); | ||
479 | memset(pp, 0, sizeof(*pp)); | ||
480 | } | ||
481 | |||
482 | /* Show an event */ | ||
483 | static void show_perf_probe_event(const char *event, const char *place, | ||
484 | struct probe_point *pp) | ||
485 | { | ||
486 | int i, ret; | ||
487 | char buf[128]; | ||
488 | |||
489 | ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); | ||
490 | if (ret < 0) | ||
491 | die("Failed to copy event: %s", strerror(-ret)); | ||
492 | printf(" %-40s (on %s", buf, place); | ||
493 | |||
494 | if (pp->nr_args > 0) { | ||
495 | printf(" with"); | ||
496 | for (i = 0; i < pp->nr_args; i++) | ||
497 | printf(" %s", pp->args[i]); | ||
498 | } | ||
499 | printf(")\n"); | ||
500 | } | ||
501 | |||
502 | /* List up current perf-probe events */ | ||
503 | void show_perf_probe_events(void) | ||
504 | { | ||
505 | int fd; | ||
506 | struct probe_point pp; | ||
507 | struct strlist *rawlist; | ||
508 | struct str_node *ent; | ||
509 | |||
510 | setup_pager(); | ||
511 | memset(&pp, 0, sizeof(pp)); | ||
512 | |||
513 | fd = open_kprobe_events(O_RDONLY, 0); | ||
514 | rawlist = get_trace_kprobe_event_rawlist(fd); | ||
515 | close(fd); | ||
516 | |||
517 | strlist__for_each(ent, rawlist) { | ||
518 | parse_trace_kprobe_event(ent->s, &pp); | ||
519 | /* Synthesize only event probe point */ | ||
520 | synthesize_perf_probe_point(&pp); | ||
521 | /* Show an event */ | ||
522 | show_perf_probe_event(pp.event, pp.probes[0], &pp); | ||
523 | clear_probe_point(&pp); | ||
524 | } | ||
525 | |||
526 | strlist__delete(rawlist); | ||
527 | } | ||
528 | |||
529 | /* Get current perf-probe event names */ | ||
530 | static struct strlist *get_perf_event_names(int fd, bool include_group) | ||
531 | { | ||
532 | char buf[128]; | ||
533 | struct strlist *sl, *rawlist; | ||
534 | struct str_node *ent; | ||
535 | struct probe_point pp; | ||
536 | |||
537 | memset(&pp, 0, sizeof(pp)); | ||
538 | rawlist = get_trace_kprobe_event_rawlist(fd); | ||
539 | |||
540 | sl = strlist__new(true, NULL); | ||
541 | strlist__for_each(ent, rawlist) { | ||
542 | parse_trace_kprobe_event(ent->s, &pp); | ||
543 | if (include_group) { | ||
544 | if (e_snprintf(buf, 128, "%s:%s", pp.group, | ||
545 | pp.event) < 0) | ||
546 | die("Failed to copy group:event name."); | ||
547 | strlist__add(sl, buf); | ||
548 | } else | ||
549 | strlist__add(sl, pp.event); | ||
550 | clear_probe_point(&pp); | ||
551 | } | ||
552 | |||
553 | strlist__delete(rawlist); | ||
554 | |||
555 | return sl; | ||
556 | } | ||
557 | |||
558 | static void write_trace_kprobe_event(int fd, const char *buf) | ||
559 | { | ||
560 | int ret; | ||
561 | |||
562 | pr_debug("Writing event: %s\n", buf); | ||
563 | ret = write(fd, buf, strlen(buf)); | ||
564 | if (ret <= 0) | ||
565 | die("Failed to write event: %s", strerror(errno)); | ||
566 | } | ||
567 | |||
568 | static void get_new_event_name(char *buf, size_t len, const char *base, | ||
569 | struct strlist *namelist, bool allow_suffix) | ||
570 | { | ||
571 | int i, ret; | ||
572 | |||
573 | /* Try no suffix */ | ||
574 | ret = e_snprintf(buf, len, "%s", base); | ||
575 | if (ret < 0) | ||
576 | die("snprintf() failed: %s", strerror(-ret)); | ||
577 | if (!strlist__has_entry(namelist, buf)) | ||
578 | return; | ||
579 | |||
580 | if (!allow_suffix) { | ||
581 | pr_warning("Error: event \"%s\" already exists. " | ||
582 | "(Use -f to force duplicates.)\n", base); | ||
583 | die("Can't add new event."); | ||
584 | } | ||
585 | |||
586 | /* Try to add suffix */ | ||
587 | for (i = 1; i < MAX_EVENT_INDEX; i++) { | ||
588 | ret = e_snprintf(buf, len, "%s_%d", base, i); | ||
589 | if (ret < 0) | ||
590 | die("snprintf() failed: %s", strerror(-ret)); | ||
591 | if (!strlist__has_entry(namelist, buf)) | ||
592 | break; | ||
593 | } | ||
594 | if (i == MAX_EVENT_INDEX) | ||
595 | die("Too many events are on the same function."); | ||
596 | } | ||
597 | |||
598 | void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, | ||
599 | bool force_add) | ||
600 | { | ||
601 | int i, j, fd; | ||
602 | struct probe_point *pp; | ||
603 | char buf[MAX_CMDLEN]; | ||
604 | char event[64]; | ||
605 | struct strlist *namelist; | ||
606 | bool allow_suffix; | ||
607 | |||
608 | fd = open_kprobe_events(O_RDWR, O_APPEND); | ||
609 | /* Get current event names */ | ||
610 | namelist = get_perf_event_names(fd, false); | ||
611 | |||
612 | for (j = 0; j < nr_probes; j++) { | ||
613 | pp = probes + j; | ||
614 | if (!pp->event) | ||
615 | pp->event = strdup(pp->function); | ||
616 | if (!pp->group) | ||
617 | pp->group = strdup(PERFPROBE_GROUP); | ||
618 | DIE_IF(!pp->event || !pp->group); | ||
619 | /* If force_add is true, suffix search is allowed */ | ||
620 | allow_suffix = force_add; | ||
621 | for (i = 0; i < pp->found; i++) { | ||
622 | /* Get an unused new event name */ | ||
623 | get_new_event_name(event, 64, pp->event, namelist, | ||
624 | allow_suffix); | ||
625 | snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", | ||
626 | pp->retprobe ? 'r' : 'p', | ||
627 | pp->group, event, | ||
628 | pp->probes[i]); | ||
629 | write_trace_kprobe_event(fd, buf); | ||
630 | printf("Added new event:\n"); | ||
631 | /* Get the first parameter (probe-point) */ | ||
632 | sscanf(pp->probes[i], "%s", buf); | ||
633 | show_perf_probe_event(event, buf, pp); | ||
634 | /* Add added event name to namelist */ | ||
635 | strlist__add(namelist, event); | ||
636 | /* | ||
637 | * Probes after the first probe which comes from same | ||
638 | * user input are always allowed to add suffix, because | ||
639 | * there might be several addresses corresponding to | ||
640 | * one code line. | ||
641 | */ | ||
642 | allow_suffix = true; | ||
643 | } | ||
644 | } | ||
645 | /* Show how to use the event. */ | ||
646 | printf("\nYou can now use it on all perf tools, such as:\n\n"); | ||
647 | printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event); | ||
648 | |||
649 | strlist__delete(namelist); | ||
650 | close(fd); | ||
651 | } | ||
652 | |||
653 | static void __del_trace_kprobe_event(int fd, struct str_node *ent) | ||
654 | { | ||
655 | char *p; | ||
656 | char buf[128]; | ||
657 | |||
658 | /* Convert from perf-probe event to trace-kprobe event */ | ||
659 | if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) | ||
660 | die("Failed to copy event."); | ||
661 | p = strchr(buf + 2, ':'); | ||
662 | if (!p) | ||
663 | die("Internal error: %s should have ':' but not.", ent->s); | ||
664 | *p = '/'; | ||
665 | |||
666 | write_trace_kprobe_event(fd, buf); | ||
667 | printf("Remove event: %s\n", ent->s); | ||
668 | } | ||
669 | |||
670 | static void del_trace_kprobe_event(int fd, const char *group, | ||
671 | const char *event, struct strlist *namelist) | ||
672 | { | ||
673 | char buf[128]; | ||
674 | struct str_node *ent, *n; | ||
675 | int found = 0; | ||
676 | |||
677 | if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) | ||
678 | die("Failed to copy event."); | ||
679 | |||
680 | if (strpbrk(buf, "*?")) { /* Glob-exp */ | ||
681 | strlist__for_each_safe(ent, n, namelist) | ||
682 | if (strglobmatch(ent->s, buf)) { | ||
683 | found++; | ||
684 | __del_trace_kprobe_event(fd, ent); | ||
685 | strlist__remove(namelist, ent); | ||
686 | } | ||
687 | } else { | ||
688 | ent = strlist__find(namelist, buf); | ||
689 | if (ent) { | ||
690 | found++; | ||
691 | __del_trace_kprobe_event(fd, ent); | ||
692 | strlist__remove(namelist, ent); | ||
693 | } | ||
694 | } | ||
695 | if (found == 0) | ||
696 | pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); | ||
697 | } | ||
698 | |||
699 | void del_trace_kprobe_events(struct strlist *dellist) | ||
700 | { | ||
701 | int fd; | ||
702 | const char *group, *event; | ||
703 | char *p, *str; | ||
704 | struct str_node *ent; | ||
705 | struct strlist *namelist; | ||
706 | |||
707 | fd = open_kprobe_events(O_RDWR, O_APPEND); | ||
708 | /* Get current event names */ | ||
709 | namelist = get_perf_event_names(fd, true); | ||
710 | |||
711 | strlist__for_each(ent, dellist) { | ||
712 | str = strdup(ent->s); | ||
713 | if (!str) | ||
714 | die("Failed to copy event."); | ||
715 | pr_debug("Parsing: %s\n", str); | ||
716 | p = strchr(str, ':'); | ||
717 | if (p) { | ||
718 | group = str; | ||
719 | *p = '\0'; | ||
720 | event = p + 1; | ||
721 | } else { | ||
722 | group = "*"; | ||
723 | event = str; | ||
724 | } | ||
725 | pr_debug("Group: %s, Event: %s\n", group, event); | ||
726 | del_trace_kprobe_event(fd, group, event, namelist); | ||
727 | free(str); | ||
728 | } | ||
729 | strlist__delete(namelist); | ||
730 | close(fd); | ||
731 | } | ||
732 | |||
733 | #define LINEBUF_SIZE 256 | ||
734 | #define NR_ADDITIONAL_LINES 2 | ||
735 | |||
736 | static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) | ||
737 | { | ||
738 | char buf[LINEBUF_SIZE]; | ||
739 | const char *color = PERF_COLOR_BLUE; | ||
740 | |||
741 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
742 | goto error; | ||
743 | if (!skip) { | ||
744 | if (show_num) | ||
745 | fprintf(stdout, "%7u %s", l, buf); | ||
746 | else | ||
747 | color_fprintf(stdout, color, " %s", buf); | ||
748 | } | ||
749 | |||
750 | while (strlen(buf) == LINEBUF_SIZE - 1 && | ||
751 | buf[LINEBUF_SIZE - 2] != '\n') { | ||
752 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
753 | goto error; | ||
754 | if (!skip) { | ||
755 | if (show_num) | ||
756 | fprintf(stdout, "%s", buf); | ||
757 | else | ||
758 | color_fprintf(stdout, color, "%s", buf); | ||
759 | } | ||
760 | } | ||
761 | return; | ||
762 | error: | ||
763 | if (feof(fp)) | ||
764 | die("Source file is shorter than expected."); | ||
765 | else | ||
766 | die("File read error: %s", strerror(errno)); | ||
767 | } | ||
768 | |||
769 | void show_line_range(struct line_range *lr) | ||
770 | { | ||
771 | unsigned int l = 1; | ||
772 | struct line_node *ln; | ||
773 | FILE *fp; | ||
774 | |||
775 | setup_pager(); | ||
776 | |||
777 | if (lr->function) | ||
778 | fprintf(stdout, "<%s:%d>\n", lr->function, | ||
779 | lr->start - lr->offset); | ||
780 | else | ||
781 | fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); | ||
782 | |||
783 | fp = fopen(lr->path, "r"); | ||
784 | if (fp == NULL) | ||
785 | die("Failed to open %s: %s", lr->path, strerror(errno)); | ||
786 | /* Skip to starting line number */ | ||
787 | while (l < lr->start) | ||
788 | show_one_line(fp, l++, true, false); | ||
789 | |||
790 | list_for_each_entry(ln, &lr->line_list, list) { | ||
791 | while (ln->line > l) | ||
792 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
793 | show_one_line(fp, (l++) - lr->offset, false, true); | ||
794 | } | ||
795 | |||
796 | if (lr->end == INT_MAX) | ||
797 | lr->end = l + NR_ADDITIONAL_LINES; | ||
798 | while (l < lr->end && !feof(fp)) | ||
799 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
800 | |||
801 | fclose(fp); | ||
802 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h new file mode 100644 index 000000000000..711287d4baea --- /dev/null +++ b/tools/perf/util/probe-event.h | |||
@@ -0,0 +1,24 @@ | |||
1 | #ifndef _PROBE_EVENT_H | ||
2 | #define _PROBE_EVENT_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include "probe-finder.h" | ||
6 | #include "strlist.h" | ||
7 | |||
8 | extern void parse_line_range_desc(const char *arg, struct line_range *lr); | ||
9 | extern void parse_perf_probe_event(const char *str, struct probe_point *pp, | ||
10 | bool *need_dwarf); | ||
11 | extern int synthesize_perf_probe_point(struct probe_point *pp); | ||
12 | extern int synthesize_perf_probe_event(struct probe_point *pp); | ||
13 | extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp); | ||
14 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); | ||
15 | extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, | ||
16 | bool force_add); | ||
17 | extern void del_trace_kprobe_events(struct strlist *dellist); | ||
18 | extern void show_perf_probe_events(void); | ||
19 | extern void show_line_range(struct line_range *lr); | ||
20 | |||
21 | /* Maximum index number of event-name postfix */ | ||
22 | #define MAX_EVENT_INDEX 1024 | ||
23 | |||
24 | #endif /*_PROBE_EVENT_H */ | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c new file mode 100644 index 000000000000..c171a243d05b --- /dev/null +++ b/tools/perf/util/probe-finder.c | |||
@@ -0,0 +1,829 @@ | |||
1 | /* | ||
2 | * probe-finder.c : C expression to kprobe event converter | ||
3 | * | ||
4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | ||
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 <sys/utsname.h> | ||
23 | #include <sys/types.h> | ||
24 | #include <sys/stat.h> | ||
25 | #include <fcntl.h> | ||
26 | #include <errno.h> | ||
27 | #include <stdio.h> | ||
28 | #include <unistd.h> | ||
29 | #include <getopt.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <string.h> | ||
32 | #include <stdarg.h> | ||
33 | #include <ctype.h> | ||
34 | |||
35 | #include "string.h" | ||
36 | #include "event.h" | ||
37 | #include "debug.h" | ||
38 | #include "util.h" | ||
39 | #include "probe-finder.h" | ||
40 | |||
41 | |||
42 | /* | ||
43 | * Generic dwarf analysis helpers | ||
44 | */ | ||
45 | |||
46 | #define X86_32_MAX_REGS 8 | ||
47 | const char *x86_32_regs_table[X86_32_MAX_REGS] = { | ||
48 | "%ax", | ||
49 | "%cx", | ||
50 | "%dx", | ||
51 | "%bx", | ||
52 | "$stack", /* Stack address instead of %sp */ | ||
53 | "%bp", | ||
54 | "%si", | ||
55 | "%di", | ||
56 | }; | ||
57 | |||
58 | #define X86_64_MAX_REGS 16 | ||
59 | const char *x86_64_regs_table[X86_64_MAX_REGS] = { | ||
60 | "%ax", | ||
61 | "%dx", | ||
62 | "%cx", | ||
63 | "%bx", | ||
64 | "%si", | ||
65 | "%di", | ||
66 | "%bp", | ||
67 | "%sp", | ||
68 | "%r8", | ||
69 | "%r9", | ||
70 | "%r10", | ||
71 | "%r11", | ||
72 | "%r12", | ||
73 | "%r13", | ||
74 | "%r14", | ||
75 | "%r15", | ||
76 | }; | ||
77 | |||
78 | /* TODO: switching by dwarf address size */ | ||
79 | #ifdef __x86_64__ | ||
80 | #define ARCH_MAX_REGS X86_64_MAX_REGS | ||
81 | #define arch_regs_table x86_64_regs_table | ||
82 | #else | ||
83 | #define ARCH_MAX_REGS X86_32_MAX_REGS | ||
84 | #define arch_regs_table x86_32_regs_table | ||
85 | #endif | ||
86 | |||
87 | /* Return architecture dependent register string (for kprobe-tracer) */ | ||
88 | static const char *get_arch_regstr(unsigned int n) | ||
89 | { | ||
90 | return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL; | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * Compare the tail of two strings. | ||
95 | * Return 0 if whole of either string is same as another's tail part. | ||
96 | */ | ||
97 | static int strtailcmp(const char *s1, const char *s2) | ||
98 | { | ||
99 | int i1 = strlen(s1); | ||
100 | int i2 = strlen(s2); | ||
101 | while (--i1 >= 0 && --i2 >= 0) { | ||
102 | if (s1[i1] != s2[i2]) | ||
103 | return s1[i1] - s2[i2]; | ||
104 | } | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | /* Line number list operations */ | ||
109 | |||
110 | /* Add a line to line number list */ | ||
111 | static void line_list__add_line(struct list_head *head, unsigned int line) | ||
112 | { | ||
113 | struct line_node *ln; | ||
114 | struct list_head *p; | ||
115 | |||
116 | /* Reverse search, because new line will be the last one */ | ||
117 | list_for_each_entry_reverse(ln, head, list) { | ||
118 | if (ln->line < line) { | ||
119 | p = &ln->list; | ||
120 | goto found; | ||
121 | } else if (ln->line == line) /* Already exist */ | ||
122 | return ; | ||
123 | } | ||
124 | /* List is empty, or the smallest entry */ | ||
125 | p = head; | ||
126 | found: | ||
127 | pr_debug("line list: add a line %u\n", line); | ||
128 | ln = zalloc(sizeof(struct line_node)); | ||
129 | DIE_IF(ln == NULL); | ||
130 | ln->line = line; | ||
131 | INIT_LIST_HEAD(&ln->list); | ||
132 | list_add(&ln->list, p); | ||
133 | } | ||
134 | |||
135 | /* Check if the line in line number list */ | ||
136 | static int line_list__has_line(struct list_head *head, unsigned int line) | ||
137 | { | ||
138 | struct line_node *ln; | ||
139 | |||
140 | /* Reverse search, because new line will be the last one */ | ||
141 | list_for_each_entry(ln, head, list) | ||
142 | if (ln->line == line) | ||
143 | return 1; | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | /* Init line number list */ | ||
149 | static void line_list__init(struct list_head *head) | ||
150 | { | ||
151 | INIT_LIST_HEAD(head); | ||
152 | } | ||
153 | |||
154 | /* Free line number list */ | ||
155 | static void line_list__free(struct list_head *head) | ||
156 | { | ||
157 | struct line_node *ln; | ||
158 | while (!list_empty(head)) { | ||
159 | ln = list_first_entry(head, struct line_node, list); | ||
160 | list_del(&ln->list); | ||
161 | free(ln); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | /* Dwarf wrappers */ | ||
166 | |||
167 | /* Find the realpath of the target file. */ | ||
168 | static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
169 | { | ||
170 | Dwarf_Files *files; | ||
171 | size_t nfiles, i; | ||
172 | const char *src = NULL; | ||
173 | int ret; | ||
174 | |||
175 | if (!fname) | ||
176 | return NULL; | ||
177 | |||
178 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); | ||
179 | if (ret != 0) | ||
180 | return NULL; | ||
181 | |||
182 | for (i = 0; i < nfiles; i++) { | ||
183 | src = dwarf_filesrc(files, i, NULL, NULL); | ||
184 | if (strtailcmp(src, fname) == 0) | ||
185 | break; | ||
186 | } | ||
187 | return src; | ||
188 | } | ||
189 | |||
190 | struct __addr_die_search_param { | ||
191 | Dwarf_Addr addr; | ||
192 | Dwarf_Die *die_mem; | ||
193 | }; | ||
194 | |||
195 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
196 | { | ||
197 | struct __addr_die_search_param *ad = data; | ||
198 | |||
199 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && | ||
200 | dwarf_haspc(fn_die, ad->addr)) { | ||
201 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
202 | return DWARF_CB_ABORT; | ||
203 | } | ||
204 | return DWARF_CB_OK; | ||
205 | } | ||
206 | |||
207 | /* Search a real subprogram including this line, */ | ||
208 | static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
209 | Dwarf_Die *die_mem) | ||
210 | { | ||
211 | struct __addr_die_search_param ad; | ||
212 | ad.addr = addr; | ||
213 | ad.die_mem = die_mem; | ||
214 | /* dwarf_getscopes can't find subprogram. */ | ||
215 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) | ||
216 | return NULL; | ||
217 | else | ||
218 | return die_mem; | ||
219 | } | ||
220 | |||
221 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ | ||
222 | static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
223 | Dwarf_Die *die_mem) | ||
224 | { | ||
225 | Dwarf_Die child_die; | ||
226 | int ret; | ||
227 | |||
228 | ret = dwarf_child(sp_die, die_mem); | ||
229 | if (ret != 0) | ||
230 | return NULL; | ||
231 | |||
232 | do { | ||
233 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | ||
234 | dwarf_haspc(die_mem, addr)) | ||
235 | return die_mem; | ||
236 | |||
237 | if (die_get_inlinefunc(die_mem, addr, &child_die)) { | ||
238 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
239 | return die_mem; | ||
240 | } | ||
241 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
242 | |||
243 | return NULL; | ||
244 | } | ||
245 | |||
246 | /* Compare diename and tname */ | ||
247 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | ||
248 | { | ||
249 | const char *name; | ||
250 | name = dwarf_diename(dw_die); | ||
251 | DIE_IF(name == NULL); | ||
252 | return strcmp(tname, name); | ||
253 | } | ||
254 | |||
255 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ | ||
256 | static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) | ||
257 | { | ||
258 | Dwarf_Addr epc; | ||
259 | int ret; | ||
260 | |||
261 | ret = dwarf_entrypc(dw_die, &epc); | ||
262 | DIE_IF(ret == -1); | ||
263 | return epc; | ||
264 | } | ||
265 | |||
266 | /* Get a variable die */ | ||
267 | static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, | ||
268 | Dwarf_Die *die_mem) | ||
269 | { | ||
270 | Dwarf_Die child_die; | ||
271 | int tag; | ||
272 | int ret; | ||
273 | |||
274 | ret = dwarf_child(sp_die, die_mem); | ||
275 | if (ret != 0) | ||
276 | return NULL; | ||
277 | |||
278 | do { | ||
279 | tag = dwarf_tag(die_mem); | ||
280 | if ((tag == DW_TAG_formal_parameter || | ||
281 | tag == DW_TAG_variable) && | ||
282 | (die_compare_name(die_mem, name) == 0)) | ||
283 | return die_mem; | ||
284 | |||
285 | if (die_find_variable(die_mem, name, &child_die)) { | ||
286 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
287 | return die_mem; | ||
288 | } | ||
289 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
290 | |||
291 | return NULL; | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * Probe finder related functions | ||
296 | */ | ||
297 | |||
298 | /* Show a location */ | ||
299 | static void show_location(Dwarf_Op *op, struct probe_finder *pf) | ||
300 | { | ||
301 | unsigned int regn; | ||
302 | Dwarf_Word offs = 0; | ||
303 | int deref = 0, ret; | ||
304 | const char *regs; | ||
305 | |||
306 | /* TODO: support CFA */ | ||
307 | /* If this is based on frame buffer, set the offset */ | ||
308 | if (op->atom == DW_OP_fbreg) { | ||
309 | if (pf->fb_ops == NULL) | ||
310 | die("The attribute of frame base is not supported.\n"); | ||
311 | deref = 1; | ||
312 | offs = op->number; | ||
313 | op = &pf->fb_ops[0]; | ||
314 | } | ||
315 | |||
316 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { | ||
317 | regn = op->atom - DW_OP_breg0; | ||
318 | offs += op->number; | ||
319 | deref = 1; | ||
320 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { | ||
321 | regn = op->atom - DW_OP_reg0; | ||
322 | } else if (op->atom == DW_OP_bregx) { | ||
323 | regn = op->number; | ||
324 | offs += op->number2; | ||
325 | deref = 1; | ||
326 | } else if (op->atom == DW_OP_regx) { | ||
327 | regn = op->number; | ||
328 | } else | ||
329 | die("DW_OP %d is not supported.", op->atom); | ||
330 | |||
331 | regs = get_arch_regstr(regn); | ||
332 | if (!regs) | ||
333 | die("%u exceeds max register number.", regn); | ||
334 | |||
335 | if (deref) | ||
336 | ret = snprintf(pf->buf, pf->len, " %s=%+jd(%s)", | ||
337 | pf->var, (intmax_t)offs, regs); | ||
338 | else | ||
339 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); | ||
340 | DIE_IF(ret < 0); | ||
341 | DIE_IF(ret >= pf->len); | ||
342 | } | ||
343 | |||
344 | /* Show a variables in kprobe event format */ | ||
345 | static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | ||
346 | { | ||
347 | Dwarf_Attribute attr; | ||
348 | Dwarf_Op *expr; | ||
349 | size_t nexpr; | ||
350 | int ret; | ||
351 | |||
352 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) | ||
353 | goto error; | ||
354 | /* TODO: handle more than 1 exprs */ | ||
355 | ret = dwarf_getlocation_addr(&attr, pf->addr, &expr, &nexpr, 1); | ||
356 | if (ret <= 0 || nexpr == 0) | ||
357 | goto error; | ||
358 | |||
359 | show_location(expr, pf); | ||
360 | /* *expr will be cached in libdw. Don't free it. */ | ||
361 | return ; | ||
362 | error: | ||
363 | /* TODO: Support const_value */ | ||
364 | die("Failed to find the location of %s at this address.\n" | ||
365 | " Perhaps, it has been optimized out.", pf->var); | ||
366 | } | ||
367 | |||
368 | /* Find a variable in a subprogram die */ | ||
369 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
370 | { | ||
371 | int ret; | ||
372 | Dwarf_Die vr_die; | ||
373 | |||
374 | /* TODO: Support struct members and arrays */ | ||
375 | if (!is_c_varname(pf->var)) { | ||
376 | /* Output raw parameters */ | ||
377 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); | ||
378 | DIE_IF(ret < 0); | ||
379 | DIE_IF(ret >= pf->len); | ||
380 | return ; | ||
381 | } | ||
382 | |||
383 | pr_debug("Searching '%s' variable in context.\n", pf->var); | ||
384 | /* Search child die for local variables and parameters. */ | ||
385 | if (!die_find_variable(sp_die, pf->var, &vr_die)) | ||
386 | die("Failed to find '%s' in this function.", pf->var); | ||
387 | |||
388 | show_variable(&vr_die, pf); | ||
389 | } | ||
390 | |||
391 | /* Show a probe point to output buffer */ | ||
392 | static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
393 | { | ||
394 | struct probe_point *pp = pf->pp; | ||
395 | Dwarf_Addr eaddr; | ||
396 | Dwarf_Die die_mem; | ||
397 | const char *name; | ||
398 | char tmp[MAX_PROBE_BUFFER]; | ||
399 | int ret, i, len; | ||
400 | Dwarf_Attribute fb_attr; | ||
401 | size_t nops; | ||
402 | |||
403 | /* If no real subprogram, find a real one */ | ||
404 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | ||
405 | sp_die = die_get_real_subprogram(&pf->cu_die, | ||
406 | pf->addr, &die_mem); | ||
407 | if (!sp_die) | ||
408 | die("Probe point is not found in subprograms."); | ||
409 | } | ||
410 | |||
411 | /* Output name of probe point */ | ||
412 | name = dwarf_diename(sp_die); | ||
413 | if (name) { | ||
414 | dwarf_entrypc(sp_die, &eaddr); | ||
415 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, | ||
416 | (unsigned long)(pf->addr - eaddr)); | ||
417 | /* Copy the function name if possible */ | ||
418 | if (!pp->function) { | ||
419 | pp->function = strdup(name); | ||
420 | pp->offset = (size_t)(pf->addr - eaddr); | ||
421 | } | ||
422 | } else { | ||
423 | /* This function has no name. */ | ||
424 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", | ||
425 | (uintmax_t)pf->addr); | ||
426 | if (!pp->function) { | ||
427 | /* TODO: Use _stext */ | ||
428 | pp->function = strdup(""); | ||
429 | pp->offset = (size_t)pf->addr; | ||
430 | } | ||
431 | } | ||
432 | DIE_IF(ret < 0); | ||
433 | DIE_IF(ret >= MAX_PROBE_BUFFER); | ||
434 | len = ret; | ||
435 | pr_debug("Probe point found: %s\n", tmp); | ||
436 | |||
437 | /* Get the frame base attribute/ops */ | ||
438 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | ||
439 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); | ||
440 | if (ret <= 0 || nops == 0) | ||
441 | pf->fb_ops = NULL; | ||
442 | |||
443 | /* Find each argument */ | ||
444 | /* TODO: use dwarf_cfi_addrframe */ | ||
445 | for (i = 0; i < pp->nr_args; i++) { | ||
446 | pf->var = pp->args[i]; | ||
447 | pf->buf = &tmp[len]; | ||
448 | pf->len = MAX_PROBE_BUFFER - len; | ||
449 | find_variable(sp_die, pf); | ||
450 | len += strlen(pf->buf); | ||
451 | } | ||
452 | |||
453 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | ||
454 | pf->fb_ops = NULL; | ||
455 | |||
456 | if (pp->found == MAX_PROBES) | ||
457 | die("Too many( > %d) probe point found.\n", MAX_PROBES); | ||
458 | |||
459 | pp->probes[pp->found] = strdup(tmp); | ||
460 | pp->found++; | ||
461 | } | ||
462 | |||
463 | /* Find probe point from its line number */ | ||
464 | static void find_probe_point_by_line(struct probe_finder *pf) | ||
465 | { | ||
466 | Dwarf_Lines *lines; | ||
467 | Dwarf_Line *line; | ||
468 | size_t nlines, i; | ||
469 | Dwarf_Addr addr; | ||
470 | int lineno; | ||
471 | int ret; | ||
472 | |||
473 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); | ||
474 | DIE_IF(ret != 0); | ||
475 | |||
476 | for (i = 0; i < nlines; i++) { | ||
477 | line = dwarf_onesrcline(lines, i); | ||
478 | dwarf_lineno(line, &lineno); | ||
479 | if (lineno != pf->lno) | ||
480 | continue; | ||
481 | |||
482 | /* TODO: Get fileno from line, but how? */ | ||
483 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
484 | continue; | ||
485 | |||
486 | ret = dwarf_lineaddr(line, &addr); | ||
487 | DIE_IF(ret != 0); | ||
488 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | ||
489 | (int)i, lineno, (uintmax_t)addr); | ||
490 | pf->addr = addr; | ||
491 | |||
492 | show_probe_point(NULL, pf); | ||
493 | /* Continuing, because target line might be inlined. */ | ||
494 | } | ||
495 | } | ||
496 | |||
497 | /* Find lines which match lazy pattern */ | ||
498 | static int find_lazy_match_lines(struct list_head *head, | ||
499 | const char *fname, const char *pat) | ||
500 | { | ||
501 | char *fbuf, *p1, *p2; | ||
502 | int fd, line, nlines = 0; | ||
503 | struct stat st; | ||
504 | |||
505 | fd = open(fname, O_RDONLY); | ||
506 | if (fd < 0) | ||
507 | die("failed to open %s", fname); | ||
508 | DIE_IF(fstat(fd, &st) < 0); | ||
509 | fbuf = malloc(st.st_size + 2); | ||
510 | DIE_IF(fbuf == NULL); | ||
511 | DIE_IF(read(fd, fbuf, st.st_size) < 0); | ||
512 | close(fd); | ||
513 | fbuf[st.st_size] = '\n'; /* Dummy line */ | ||
514 | fbuf[st.st_size + 1] = '\0'; | ||
515 | p1 = fbuf; | ||
516 | line = 1; | ||
517 | while ((p2 = strchr(p1, '\n')) != NULL) { | ||
518 | *p2 = '\0'; | ||
519 | if (strlazymatch(p1, pat)) { | ||
520 | line_list__add_line(head, line); | ||
521 | nlines++; | ||
522 | } | ||
523 | line++; | ||
524 | p1 = p2 + 1; | ||
525 | } | ||
526 | free(fbuf); | ||
527 | return nlines; | ||
528 | } | ||
529 | |||
530 | /* Find probe points from lazy pattern */ | ||
531 | static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
532 | { | ||
533 | Dwarf_Lines *lines; | ||
534 | Dwarf_Line *line; | ||
535 | size_t nlines, i; | ||
536 | Dwarf_Addr addr; | ||
537 | Dwarf_Die die_mem; | ||
538 | int lineno; | ||
539 | int ret; | ||
540 | |||
541 | if (list_empty(&pf->lcache)) { | ||
542 | /* Matching lazy line pattern */ | ||
543 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, | ||
544 | pf->pp->lazy_line); | ||
545 | if (ret <= 0) | ||
546 | die("No matched lines found in %s.", pf->fname); | ||
547 | } | ||
548 | |||
549 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); | ||
550 | DIE_IF(ret != 0); | ||
551 | for (i = 0; i < nlines; i++) { | ||
552 | line = dwarf_onesrcline(lines, i); | ||
553 | |||
554 | dwarf_lineno(line, &lineno); | ||
555 | if (!line_list__has_line(&pf->lcache, lineno)) | ||
556 | continue; | ||
557 | |||
558 | /* TODO: Get fileno from line, but how? */ | ||
559 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
560 | continue; | ||
561 | |||
562 | ret = dwarf_lineaddr(line, &addr); | ||
563 | DIE_IF(ret != 0); | ||
564 | if (sp_die) { | ||
565 | /* Address filtering 1: does sp_die include addr? */ | ||
566 | if (!dwarf_haspc(sp_die, addr)) | ||
567 | continue; | ||
568 | /* Address filtering 2: No child include addr? */ | ||
569 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
570 | continue; | ||
571 | } | ||
572 | |||
573 | pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", | ||
574 | (int)i, lineno, (unsigned long long)addr); | ||
575 | pf->addr = addr; | ||
576 | |||
577 | show_probe_point(sp_die, pf); | ||
578 | /* Continuing, because target line might be inlined. */ | ||
579 | } | ||
580 | /* TODO: deallocate lines, but how? */ | ||
581 | } | ||
582 | |||
583 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | ||
584 | { | ||
585 | struct probe_finder *pf = (struct probe_finder *)data; | ||
586 | struct probe_point *pp = pf->pp; | ||
587 | |||
588 | if (pp->lazy_line) | ||
589 | find_probe_point_lazy(in_die, pf); | ||
590 | else { | ||
591 | /* Get probe address */ | ||
592 | pf->addr = die_get_entrypc(in_die); | ||
593 | pf->addr += pp->offset; | ||
594 | pr_debug("found inline addr: 0x%jx\n", | ||
595 | (uintmax_t)pf->addr); | ||
596 | |||
597 | show_probe_point(in_die, pf); | ||
598 | } | ||
599 | |||
600 | return DWARF_CB_OK; | ||
601 | } | ||
602 | |||
603 | /* Search function from function name */ | ||
604 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | ||
605 | { | ||
606 | struct probe_finder *pf = (struct probe_finder *)data; | ||
607 | struct probe_point *pp = pf->pp; | ||
608 | |||
609 | /* Check tag and diename */ | ||
610 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | ||
611 | die_compare_name(sp_die, pp->function) != 0) | ||
612 | return 0; | ||
613 | |||
614 | pf->fname = dwarf_decl_file(sp_die); | ||
615 | if (pp->line) { /* Function relative line */ | ||
616 | dwarf_decl_line(sp_die, &pf->lno); | ||
617 | pf->lno += pp->line; | ||
618 | find_probe_point_by_line(pf); | ||
619 | } else if (!dwarf_func_inline(sp_die)) { | ||
620 | /* Real function */ | ||
621 | if (pp->lazy_line) | ||
622 | find_probe_point_lazy(sp_die, pf); | ||
623 | else { | ||
624 | pf->addr = die_get_entrypc(sp_die); | ||
625 | pf->addr += pp->offset; | ||
626 | /* TODO: Check the address in this function */ | ||
627 | show_probe_point(sp_die, pf); | ||
628 | } | ||
629 | } else | ||
630 | /* Inlined function: search instances */ | ||
631 | dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf); | ||
632 | |||
633 | return 1; /* Exit; no same symbol in this CU. */ | ||
634 | } | ||
635 | |||
636 | static void find_probe_point_by_func(struct probe_finder *pf) | ||
637 | { | ||
638 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); | ||
639 | } | ||
640 | |||
641 | /* Find a probe point */ | ||
642 | int find_probe_point(int fd, struct probe_point *pp) | ||
643 | { | ||
644 | struct probe_finder pf = {.pp = pp}; | ||
645 | Dwarf_Off off, noff; | ||
646 | size_t cuhl; | ||
647 | Dwarf_Die *diep; | ||
648 | Dwarf *dbg; | ||
649 | |||
650 | dbg = dwarf_begin(fd, DWARF_C_READ); | ||
651 | if (!dbg) | ||
652 | return -ENOENT; | ||
653 | |||
654 | pp->found = 0; | ||
655 | off = 0; | ||
656 | line_list__init(&pf.lcache); | ||
657 | /* Loop on CUs (Compilation Unit) */ | ||
658 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { | ||
659 | /* Get the DIE(Debugging Information Entry) of this CU */ | ||
660 | diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); | ||
661 | if (!diep) | ||
662 | continue; | ||
663 | |||
664 | /* Check if target file is included. */ | ||
665 | if (pp->file) | ||
666 | pf.fname = cu_find_realpath(&pf.cu_die, pp->file); | ||
667 | else | ||
668 | pf.fname = NULL; | ||
669 | |||
670 | if (!pp->file || pf.fname) { | ||
671 | if (pp->function) | ||
672 | find_probe_point_by_func(&pf); | ||
673 | else if (pp->lazy_line) | ||
674 | find_probe_point_lazy(NULL, &pf); | ||
675 | else { | ||
676 | pf.lno = pp->line; | ||
677 | find_probe_point_by_line(&pf); | ||
678 | } | ||
679 | } | ||
680 | off = noff; | ||
681 | } | ||
682 | line_list__free(&pf.lcache); | ||
683 | dwarf_end(dbg); | ||
684 | |||
685 | return pp->found; | ||
686 | } | ||
687 | |||
688 | /* Find line range from its line number */ | ||
689 | static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | ||
690 | { | ||
691 | Dwarf_Lines *lines; | ||
692 | Dwarf_Line *line; | ||
693 | size_t nlines, i; | ||
694 | Dwarf_Addr addr; | ||
695 | int lineno; | ||
696 | int ret; | ||
697 | const char *src; | ||
698 | Dwarf_Die die_mem; | ||
699 | |||
700 | line_list__init(&lf->lr->line_list); | ||
701 | ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines); | ||
702 | DIE_IF(ret != 0); | ||
703 | |||
704 | for (i = 0; i < nlines; i++) { | ||
705 | line = dwarf_onesrcline(lines, i); | ||
706 | ret = dwarf_lineno(line, &lineno); | ||
707 | DIE_IF(ret != 0); | ||
708 | if (lf->lno_s > lineno || lf->lno_e < lineno) | ||
709 | continue; | ||
710 | |||
711 | if (sp_die) { | ||
712 | /* Address filtering 1: does sp_die include addr? */ | ||
713 | ret = dwarf_lineaddr(line, &addr); | ||
714 | DIE_IF(ret != 0); | ||
715 | if (!dwarf_haspc(sp_die, addr)) | ||
716 | continue; | ||
717 | |||
718 | /* Address filtering 2: No child include addr? */ | ||
719 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
720 | continue; | ||
721 | } | ||
722 | |||
723 | /* TODO: Get fileno from line, but how? */ | ||
724 | src = dwarf_linesrc(line, NULL, NULL); | ||
725 | if (strtailcmp(src, lf->fname) != 0) | ||
726 | continue; | ||
727 | |||
728 | /* Copy real path */ | ||
729 | if (!lf->lr->path) | ||
730 | lf->lr->path = strdup(src); | ||
731 | line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); | ||
732 | } | ||
733 | /* Update status */ | ||
734 | if (!list_empty(&lf->lr->line_list)) | ||
735 | lf->found = 1; | ||
736 | else { | ||
737 | free(lf->lr->path); | ||
738 | lf->lr->path = NULL; | ||
739 | } | ||
740 | } | ||
741 | |||
742 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | ||
743 | { | ||
744 | find_line_range_by_line(in_die, (struct line_finder *)data); | ||
745 | return DWARF_CB_ABORT; /* No need to find other instances */ | ||
746 | } | ||
747 | |||
748 | /* Search function from function name */ | ||
749 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | ||
750 | { | ||
751 | struct line_finder *lf = (struct line_finder *)data; | ||
752 | struct line_range *lr = lf->lr; | ||
753 | |||
754 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | ||
755 | die_compare_name(sp_die, lr->function) == 0) { | ||
756 | lf->fname = dwarf_decl_file(sp_die); | ||
757 | dwarf_decl_line(sp_die, &lr->offset); | ||
758 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); | ||
759 | lf->lno_s = lr->offset + lr->start; | ||
760 | if (!lr->end) | ||
761 | lf->lno_e = INT_MAX; | ||
762 | else | ||
763 | lf->lno_e = lr->offset + lr->end; | ||
764 | lr->start = lf->lno_s; | ||
765 | lr->end = lf->lno_e; | ||
766 | if (dwarf_func_inline(sp_die)) | ||
767 | dwarf_func_inline_instances(sp_die, | ||
768 | line_range_inline_cb, lf); | ||
769 | else | ||
770 | find_line_range_by_line(sp_die, lf); | ||
771 | return 1; | ||
772 | } | ||
773 | return 0; | ||
774 | } | ||
775 | |||
776 | static void find_line_range_by_func(struct line_finder *lf) | ||
777 | { | ||
778 | dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0); | ||
779 | } | ||
780 | |||
781 | int find_line_range(int fd, struct line_range *lr) | ||
782 | { | ||
783 | struct line_finder lf = {.lr = lr, .found = 0}; | ||
784 | int ret; | ||
785 | Dwarf_Off off = 0, noff; | ||
786 | size_t cuhl; | ||
787 | Dwarf_Die *diep; | ||
788 | Dwarf *dbg; | ||
789 | |||
790 | dbg = dwarf_begin(fd, DWARF_C_READ); | ||
791 | if (!dbg) | ||
792 | return -ENOENT; | ||
793 | |||
794 | /* Loop on CUs (Compilation Unit) */ | ||
795 | while (!lf.found) { | ||
796 | ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL); | ||
797 | if (ret != 0) | ||
798 | break; | ||
799 | |||
800 | /* Get the DIE(Debugging Information Entry) of this CU */ | ||
801 | diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); | ||
802 | if (!diep) | ||
803 | continue; | ||
804 | |||
805 | /* Check if target file is included. */ | ||
806 | if (lr->file) | ||
807 | lf.fname = cu_find_realpath(&lf.cu_die, lr->file); | ||
808 | else | ||
809 | lf.fname = 0; | ||
810 | |||
811 | if (!lr->file || lf.fname) { | ||
812 | if (lr->function) | ||
813 | find_line_range_by_func(&lf); | ||
814 | else { | ||
815 | lf.lno_s = lr->start; | ||
816 | if (!lr->end) | ||
817 | lf.lno_e = INT_MAX; | ||
818 | else | ||
819 | lf.lno_e = lr->end; | ||
820 | find_line_range_by_line(NULL, &lf); | ||
821 | } | ||
822 | } | ||
823 | off = noff; | ||
824 | } | ||
825 | pr_debug("path: %lx\n", (unsigned long)lr->path); | ||
826 | dwarf_end(dbg); | ||
827 | return lf.found; | ||
828 | } | ||
829 | |||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h new file mode 100644 index 000000000000..21f7354397b4 --- /dev/null +++ b/tools/perf/util/probe-finder.h | |||
@@ -0,0 +1,92 @@ | |||
1 | #ifndef _PROBE_FINDER_H | ||
2 | #define _PROBE_FINDER_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include "util.h" | ||
6 | |||
7 | #define MAX_PATH_LEN 256 | ||
8 | #define MAX_PROBE_BUFFER 1024 | ||
9 | #define MAX_PROBES 128 | ||
10 | |||
11 | static inline int is_c_varname(const char *name) | ||
12 | { | ||
13 | /* TODO */ | ||
14 | return isalpha(name[0]) || name[0] == '_'; | ||
15 | } | ||
16 | |||
17 | struct probe_point { | ||
18 | char *event; /* Event name */ | ||
19 | char *group; /* Event group */ | ||
20 | |||
21 | /* Inputs */ | ||
22 | char *file; /* File name */ | ||
23 | int line; /* Line number */ | ||
24 | char *lazy_line; /* Lazy line pattern */ | ||
25 | |||
26 | char *function; /* Function name */ | ||
27 | int offset; /* Offset bytes */ | ||
28 | |||
29 | int nr_args; /* Number of arguments */ | ||
30 | char **args; /* Arguments */ | ||
31 | |||
32 | int retprobe; /* Return probe */ | ||
33 | |||
34 | /* Output */ | ||
35 | int found; /* Number of found probe points */ | ||
36 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ | ||
37 | }; | ||
38 | |||
39 | /* Line number container */ | ||
40 | struct line_node { | ||
41 | struct list_head list; | ||
42 | unsigned int line; | ||
43 | }; | ||
44 | |||
45 | /* Line range */ | ||
46 | struct line_range { | ||
47 | char *file; /* File name */ | ||
48 | char *function; /* Function name */ | ||
49 | unsigned int start; /* Start line number */ | ||
50 | unsigned int end; /* End line number */ | ||
51 | int offset; /* Start line offset */ | ||
52 | char *path; /* Real path name */ | ||
53 | struct list_head line_list; /* Visible lines */ | ||
54 | }; | ||
55 | |||
56 | #ifndef NO_DWARF_SUPPORT | ||
57 | extern int find_probe_point(int fd, struct probe_point *pp); | ||
58 | extern int find_line_range(int fd, struct line_range *lr); | ||
59 | |||
60 | #include <dwarf.h> | ||
61 | #include <libdw.h> | ||
62 | |||
63 | struct probe_finder { | ||
64 | struct probe_point *pp; /* Target probe point */ | ||
65 | |||
66 | /* For function searching */ | ||
67 | Dwarf_Addr addr; /* Address */ | ||
68 | const char *fname; /* File name */ | ||
69 | int lno; /* Line number */ | ||
70 | Dwarf_Die cu_die; /* Current CU */ | ||
71 | |||
72 | /* For variable searching */ | ||
73 | Dwarf_Op *fb_ops; /* Frame base attribute */ | ||
74 | const char *var; /* Current variable name */ | ||
75 | char *buf; /* Current output buffer */ | ||
76 | int len; /* Length of output buffer */ | ||
77 | struct list_head lcache; /* Line cache for lazy match */ | ||
78 | }; | ||
79 | |||
80 | struct line_finder { | ||
81 | struct line_range *lr; /* Target line range */ | ||
82 | |||
83 | const char *fname; /* File name */ | ||
84 | int lno_s; /* Start line number */ | ||
85 | int lno_e; /* End line number */ | ||
86 | Dwarf_Die cu_die; /* Current CU */ | ||
87 | int found; | ||
88 | }; | ||
89 | |||
90 | #endif /* NO_DWARF_SUPPORT */ | ||
91 | |||
92 | #endif /*_PROBE_FINDER_H */ | ||
diff --git a/tools/perf/util/quote.h b/tools/perf/util/quote.h index a5454a1d1c13..b6a019733919 100644 --- a/tools/perf/util/quote.h +++ b/tools/perf/util/quote.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef QUOTE_H | 1 | #ifndef __PERF_QUOTE_H |
2 | #define QUOTE_H | 2 | #define __PERF_QUOTE_H |
3 | 3 | ||
4 | #include <stddef.h> | 4 | #include <stddef.h> |
5 | #include <stdio.h> | 5 | #include <stdio.h> |
@@ -65,4 +65,4 @@ extern void perl_quote_print(FILE *stream, const char *src); | |||
65 | extern void python_quote_print(FILE *stream, const char *src); | 65 | extern void python_quote_print(FILE *stream, const char *src); |
66 | extern void tcl_quote_print(FILE *stream, const char *src); | 66 | extern void tcl_quote_print(FILE *stream, const char *src); |
67 | 67 | ||
68 | #endif | 68 | #endif /* __PERF_QUOTE_H */ |
diff --git a/tools/perf/util/run-command.h b/tools/perf/util/run-command.h index cc1837deba88..d79028727ce2 100644 --- a/tools/perf/util/run-command.h +++ b/tools/perf/util/run-command.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef RUN_COMMAND_H | 1 | #ifndef __PERF_RUN_COMMAND_H |
2 | #define RUN_COMMAND_H | 2 | #define __PERF_RUN_COMMAND_H |
3 | 3 | ||
4 | enum { | 4 | enum { |
5 | ERR_RUN_COMMAND_FORK = 10000, | 5 | ERR_RUN_COMMAND_FORK = 10000, |
@@ -85,4 +85,4 @@ struct async { | |||
85 | int start_async(struct async *async); | 85 | int start_async(struct async *async); |
86 | int finish_async(struct async *async); | 86 | int finish_async(struct async *async); |
87 | 87 | ||
88 | #endif | 88 | #endif /* __PERF_RUN_COMMAND_H */ |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c new file mode 100644 index 000000000000..5376378e0cfc --- /dev/null +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -0,0 +1,568 @@ | |||
1 | /* | ||
2 | * trace-event-perl. Feed perf trace events to an embedded Perl interpreter. | ||
3 | * | ||
4 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> | ||
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 <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include <ctype.h> | ||
26 | #include <errno.h> | ||
27 | |||
28 | #include "../../perf.h" | ||
29 | #include "../util.h" | ||
30 | #include "../trace-event.h" | ||
31 | |||
32 | #include <EXTERN.h> | ||
33 | #include <perl.h> | ||
34 | |||
35 | void boot_Perf__Trace__Context(pTHX_ CV *cv); | ||
36 | void boot_DynaLoader(pTHX_ CV *cv); | ||
37 | typedef PerlInterpreter * INTERP; | ||
38 | |||
39 | void xs_init(pTHX); | ||
40 | |||
41 | void xs_init(pTHX) | ||
42 | { | ||
43 | const char *file = __FILE__; | ||
44 | dXSUB_SYS; | ||
45 | |||
46 | newXS("Perf::Trace::Context::bootstrap", boot_Perf__Trace__Context, | ||
47 | file); | ||
48 | newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); | ||
49 | } | ||
50 | |||
51 | INTERP my_perl; | ||
52 | |||
53 | #define FTRACE_MAX_EVENT \ | ||
54 | ((1 << (sizeof(unsigned short) * 8)) - 1) | ||
55 | |||
56 | struct event *events[FTRACE_MAX_EVENT]; | ||
57 | |||
58 | extern struct scripting_context *scripting_context; | ||
59 | |||
60 | static char *cur_field_name; | ||
61 | static int zero_flag_atom; | ||
62 | |||
63 | static void define_symbolic_value(const char *ev_name, | ||
64 | const char *field_name, | ||
65 | const char *field_value, | ||
66 | const char *field_str) | ||
67 | { | ||
68 | unsigned long long value; | ||
69 | dSP; | ||
70 | |||
71 | value = eval_flag(field_value); | ||
72 | |||
73 | ENTER; | ||
74 | SAVETMPS; | ||
75 | PUSHMARK(SP); | ||
76 | |||
77 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
78 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
79 | XPUSHs(sv_2mortal(newSVuv(value))); | ||
80 | XPUSHs(sv_2mortal(newSVpv(field_str, 0))); | ||
81 | |||
82 | PUTBACK; | ||
83 | if (get_cv("main::define_symbolic_value", 0)) | ||
84 | call_pv("main::define_symbolic_value", G_SCALAR); | ||
85 | SPAGAIN; | ||
86 | PUTBACK; | ||
87 | FREETMPS; | ||
88 | LEAVE; | ||
89 | } | ||
90 | |||
91 | static void define_symbolic_values(struct print_flag_sym *field, | ||
92 | const char *ev_name, | ||
93 | const char *field_name) | ||
94 | { | ||
95 | define_symbolic_value(ev_name, field_name, field->value, field->str); | ||
96 | if (field->next) | ||
97 | define_symbolic_values(field->next, ev_name, field_name); | ||
98 | } | ||
99 | |||
100 | static void define_symbolic_field(const char *ev_name, | ||
101 | const char *field_name) | ||
102 | { | ||
103 | dSP; | ||
104 | |||
105 | ENTER; | ||
106 | SAVETMPS; | ||
107 | PUSHMARK(SP); | ||
108 | |||
109 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
110 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
111 | |||
112 | PUTBACK; | ||
113 | if (get_cv("main::define_symbolic_field", 0)) | ||
114 | call_pv("main::define_symbolic_field", G_SCALAR); | ||
115 | SPAGAIN; | ||
116 | PUTBACK; | ||
117 | FREETMPS; | ||
118 | LEAVE; | ||
119 | } | ||
120 | |||
121 | static void define_flag_value(const char *ev_name, | ||
122 | const char *field_name, | ||
123 | const char *field_value, | ||
124 | const char *field_str) | ||
125 | { | ||
126 | unsigned long long value; | ||
127 | dSP; | ||
128 | |||
129 | value = eval_flag(field_value); | ||
130 | |||
131 | ENTER; | ||
132 | SAVETMPS; | ||
133 | PUSHMARK(SP); | ||
134 | |||
135 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
136 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
137 | XPUSHs(sv_2mortal(newSVuv(value))); | ||
138 | XPUSHs(sv_2mortal(newSVpv(field_str, 0))); | ||
139 | |||
140 | PUTBACK; | ||
141 | if (get_cv("main::define_flag_value", 0)) | ||
142 | call_pv("main::define_flag_value", G_SCALAR); | ||
143 | SPAGAIN; | ||
144 | PUTBACK; | ||
145 | FREETMPS; | ||
146 | LEAVE; | ||
147 | } | ||
148 | |||
149 | static void define_flag_values(struct print_flag_sym *field, | ||
150 | const char *ev_name, | ||
151 | const char *field_name) | ||
152 | { | ||
153 | define_flag_value(ev_name, field_name, field->value, field->str); | ||
154 | if (field->next) | ||
155 | define_flag_values(field->next, ev_name, field_name); | ||
156 | } | ||
157 | |||
158 | static void define_flag_field(const char *ev_name, | ||
159 | const char *field_name, | ||
160 | const char *delim) | ||
161 | { | ||
162 | dSP; | ||
163 | |||
164 | ENTER; | ||
165 | SAVETMPS; | ||
166 | PUSHMARK(SP); | ||
167 | |||
168 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
169 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
170 | XPUSHs(sv_2mortal(newSVpv(delim, 0))); | ||
171 | |||
172 | PUTBACK; | ||
173 | if (get_cv("main::define_flag_field", 0)) | ||
174 | call_pv("main::define_flag_field", G_SCALAR); | ||
175 | SPAGAIN; | ||
176 | PUTBACK; | ||
177 | FREETMPS; | ||
178 | LEAVE; | ||
179 | } | ||
180 | |||
181 | static void define_event_symbols(struct event *event, | ||
182 | const char *ev_name, | ||
183 | struct print_arg *args) | ||
184 | { | ||
185 | switch (args->type) { | ||
186 | case PRINT_NULL: | ||
187 | break; | ||
188 | case PRINT_ATOM: | ||
189 | define_flag_value(ev_name, cur_field_name, "0", | ||
190 | args->atom.atom); | ||
191 | zero_flag_atom = 0; | ||
192 | break; | ||
193 | case PRINT_FIELD: | ||
194 | if (cur_field_name) | ||
195 | free(cur_field_name); | ||
196 | cur_field_name = strdup(args->field.name); | ||
197 | break; | ||
198 | case PRINT_FLAGS: | ||
199 | define_event_symbols(event, ev_name, args->flags.field); | ||
200 | define_flag_field(ev_name, cur_field_name, args->flags.delim); | ||
201 | define_flag_values(args->flags.flags, ev_name, cur_field_name); | ||
202 | break; | ||
203 | case PRINT_SYMBOL: | ||
204 | define_event_symbols(event, ev_name, args->symbol.field); | ||
205 | define_symbolic_field(ev_name, cur_field_name); | ||
206 | define_symbolic_values(args->symbol.symbols, ev_name, | ||
207 | cur_field_name); | ||
208 | break; | ||
209 | case PRINT_STRING: | ||
210 | break; | ||
211 | case PRINT_TYPE: | ||
212 | define_event_symbols(event, ev_name, args->typecast.item); | ||
213 | break; | ||
214 | case PRINT_OP: | ||
215 | if (strcmp(args->op.op, ":") == 0) | ||
216 | zero_flag_atom = 1; | ||
217 | define_event_symbols(event, ev_name, args->op.left); | ||
218 | define_event_symbols(event, ev_name, args->op.right); | ||
219 | break; | ||
220 | default: | ||
221 | /* we should warn... */ | ||
222 | return; | ||
223 | } | ||
224 | |||
225 | if (args->next) | ||
226 | define_event_symbols(event, ev_name, args->next); | ||
227 | } | ||
228 | |||
229 | static inline struct event *find_cache_event(int type) | ||
230 | { | ||
231 | static char ev_name[256]; | ||
232 | struct event *event; | ||
233 | |||
234 | if (events[type]) | ||
235 | return events[type]; | ||
236 | |||
237 | events[type] = event = trace_find_event(type); | ||
238 | if (!event) | ||
239 | return NULL; | ||
240 | |||
241 | sprintf(ev_name, "%s::%s", event->system, event->name); | ||
242 | |||
243 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
244 | |||
245 | return event; | ||
246 | } | ||
247 | |||
248 | static void perl_process_event(int cpu, void *data, | ||
249 | int size __unused, | ||
250 | unsigned long long nsecs, char *comm) | ||
251 | { | ||
252 | struct format_field *field; | ||
253 | static char handler[256]; | ||
254 | unsigned long long val; | ||
255 | unsigned long s, ns; | ||
256 | struct event *event; | ||
257 | int type; | ||
258 | int pid; | ||
259 | |||
260 | dSP; | ||
261 | |||
262 | type = trace_parse_common_type(data); | ||
263 | |||
264 | event = find_cache_event(type); | ||
265 | if (!event) | ||
266 | die("ug! no event found for type %d", type); | ||
267 | |||
268 | pid = trace_parse_common_pid(data); | ||
269 | |||
270 | sprintf(handler, "%s::%s", event->system, event->name); | ||
271 | |||
272 | s = nsecs / NSECS_PER_SEC; | ||
273 | ns = nsecs - s * NSECS_PER_SEC; | ||
274 | |||
275 | scripting_context->event_data = data; | ||
276 | |||
277 | ENTER; | ||
278 | SAVETMPS; | ||
279 | PUSHMARK(SP); | ||
280 | |||
281 | XPUSHs(sv_2mortal(newSVpv(handler, 0))); | ||
282 | XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context)))); | ||
283 | XPUSHs(sv_2mortal(newSVuv(cpu))); | ||
284 | XPUSHs(sv_2mortal(newSVuv(s))); | ||
285 | XPUSHs(sv_2mortal(newSVuv(ns))); | ||
286 | XPUSHs(sv_2mortal(newSViv(pid))); | ||
287 | XPUSHs(sv_2mortal(newSVpv(comm, 0))); | ||
288 | |||
289 | /* common fields other than pid can be accessed via xsub fns */ | ||
290 | |||
291 | for (field = event->format.fields; field; field = field->next) { | ||
292 | if (field->flags & FIELD_IS_STRING) { | ||
293 | int offset; | ||
294 | if (field->flags & FIELD_IS_DYNAMIC) { | ||
295 | offset = *(int *)(data + field->offset); | ||
296 | offset &= 0xffff; | ||
297 | } else | ||
298 | offset = field->offset; | ||
299 | XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0))); | ||
300 | } else { /* FIELD_IS_NUMERIC */ | ||
301 | val = read_size(data + field->offset, field->size); | ||
302 | if (field->flags & FIELD_IS_SIGNED) { | ||
303 | XPUSHs(sv_2mortal(newSViv(val))); | ||
304 | } else { | ||
305 | XPUSHs(sv_2mortal(newSVuv(val))); | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | |||
310 | PUTBACK; | ||
311 | |||
312 | if (get_cv(handler, 0)) | ||
313 | call_pv(handler, G_SCALAR); | ||
314 | else if (get_cv("main::trace_unhandled", 0)) { | ||
315 | XPUSHs(sv_2mortal(newSVpv(handler, 0))); | ||
316 | XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context)))); | ||
317 | XPUSHs(sv_2mortal(newSVuv(cpu))); | ||
318 | XPUSHs(sv_2mortal(newSVuv(nsecs))); | ||
319 | XPUSHs(sv_2mortal(newSViv(pid))); | ||
320 | XPUSHs(sv_2mortal(newSVpv(comm, 0))); | ||
321 | call_pv("main::trace_unhandled", G_SCALAR); | ||
322 | } | ||
323 | SPAGAIN; | ||
324 | PUTBACK; | ||
325 | FREETMPS; | ||
326 | LEAVE; | ||
327 | } | ||
328 | |||
329 | static void run_start_sub(void) | ||
330 | { | ||
331 | dSP; /* access to Perl stack */ | ||
332 | PUSHMARK(SP); | ||
333 | |||
334 | if (get_cv("main::trace_begin", 0)) | ||
335 | call_pv("main::trace_begin", G_DISCARD | G_NOARGS); | ||
336 | } | ||
337 | |||
338 | /* | ||
339 | * Start trace script | ||
340 | */ | ||
341 | static int perl_start_script(const char *script, int argc, const char **argv) | ||
342 | { | ||
343 | const char **command_line; | ||
344 | int i, err = 0; | ||
345 | |||
346 | command_line = malloc((argc + 2) * sizeof(const char *)); | ||
347 | command_line[0] = ""; | ||
348 | command_line[1] = script; | ||
349 | for (i = 2; i < argc + 2; i++) | ||
350 | command_line[i] = argv[i - 2]; | ||
351 | |||
352 | my_perl = perl_alloc(); | ||
353 | perl_construct(my_perl); | ||
354 | |||
355 | if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line, | ||
356 | (char **)NULL)) { | ||
357 | err = -1; | ||
358 | goto error; | ||
359 | } | ||
360 | |||
361 | if (perl_run(my_perl)) { | ||
362 | err = -1; | ||
363 | goto error; | ||
364 | } | ||
365 | |||
366 | if (SvTRUE(ERRSV)) { | ||
367 | err = -1; | ||
368 | goto error; | ||
369 | } | ||
370 | |||
371 | run_start_sub(); | ||
372 | |||
373 | free(command_line); | ||
374 | fprintf(stderr, "perf trace started with Perl script %s\n\n", script); | ||
375 | return 0; | ||
376 | error: | ||
377 | perl_free(my_perl); | ||
378 | free(command_line); | ||
379 | |||
380 | return err; | ||
381 | } | ||
382 | |||
383 | /* | ||
384 | * Stop trace script | ||
385 | */ | ||
386 | static int perl_stop_script(void) | ||
387 | { | ||
388 | dSP; /* access to Perl stack */ | ||
389 | PUSHMARK(SP); | ||
390 | |||
391 | if (get_cv("main::trace_end", 0)) | ||
392 | call_pv("main::trace_end", G_DISCARD | G_NOARGS); | ||
393 | |||
394 | perl_destruct(my_perl); | ||
395 | perl_free(my_perl); | ||
396 | |||
397 | fprintf(stderr, "\nperf trace Perl script stopped\n"); | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static int perl_generate_script(const char *outfile) | ||
403 | { | ||
404 | struct event *event = NULL; | ||
405 | struct format_field *f; | ||
406 | char fname[PATH_MAX]; | ||
407 | int not_first, count; | ||
408 | FILE *ofp; | ||
409 | |||
410 | sprintf(fname, "%s.pl", outfile); | ||
411 | ofp = fopen(fname, "w"); | ||
412 | if (ofp == NULL) { | ||
413 | fprintf(stderr, "couldn't open %s\n", fname); | ||
414 | return -1; | ||
415 | } | ||
416 | |||
417 | fprintf(ofp, "# perf trace event handlers, " | ||
418 | "generated by perf trace -g perl\n"); | ||
419 | |||
420 | fprintf(ofp, "# Licensed under the terms of the GNU GPL" | ||
421 | " License version 2\n\n"); | ||
422 | |||
423 | fprintf(ofp, "# The common_* event handler fields are the most useful " | ||
424 | "fields common to\n"); | ||
425 | |||
426 | fprintf(ofp, "# all events. They don't necessarily correspond to " | ||
427 | "the 'common_*' fields\n"); | ||
428 | |||
429 | fprintf(ofp, "# in the format files. Those fields not available as " | ||
430 | "handler params can\n"); | ||
431 | |||
432 | fprintf(ofp, "# be retrieved using Perl functions of the form " | ||
433 | "common_*($context).\n"); | ||
434 | |||
435 | fprintf(ofp, "# See Context.pm for the list of available " | ||
436 | "functions.\n\n"); | ||
437 | |||
438 | fprintf(ofp, "use lib \"$ENV{'PERF_EXEC_PATH'}/scripts/perl/" | ||
439 | "Perf-Trace-Util/lib\";\n"); | ||
440 | |||
441 | fprintf(ofp, "use lib \"./Perf-Trace-Util/lib\";\n"); | ||
442 | fprintf(ofp, "use Perf::Trace::Core;\n"); | ||
443 | fprintf(ofp, "use Perf::Trace::Context;\n"); | ||
444 | fprintf(ofp, "use Perf::Trace::Util;\n\n"); | ||
445 | |||
446 | fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n"); | ||
447 | fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n"); | ||
448 | |||
449 | while ((event = trace_find_next_event(event))) { | ||
450 | fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name); | ||
451 | fprintf(ofp, "\tmy ("); | ||
452 | |||
453 | fprintf(ofp, "$event_name, "); | ||
454 | fprintf(ofp, "$context, "); | ||
455 | fprintf(ofp, "$common_cpu, "); | ||
456 | fprintf(ofp, "$common_secs, "); | ||
457 | fprintf(ofp, "$common_nsecs,\n"); | ||
458 | fprintf(ofp, "\t $common_pid, "); | ||
459 | fprintf(ofp, "$common_comm,\n\t "); | ||
460 | |||
461 | not_first = 0; | ||
462 | count = 0; | ||
463 | |||
464 | for (f = event->format.fields; f; f = f->next) { | ||
465 | if (not_first++) | ||
466 | fprintf(ofp, ", "); | ||
467 | if (++count % 5 == 0) | ||
468 | fprintf(ofp, "\n\t "); | ||
469 | |||
470 | fprintf(ofp, "$%s", f->name); | ||
471 | } | ||
472 | fprintf(ofp, ") = @_;\n\n"); | ||
473 | |||
474 | fprintf(ofp, "\tprint_header($event_name, $common_cpu, " | ||
475 | "$common_secs, $common_nsecs,\n\t " | ||
476 | "$common_pid, $common_comm);\n\n"); | ||
477 | |||
478 | fprintf(ofp, "\tprintf(\""); | ||
479 | |||
480 | not_first = 0; | ||
481 | count = 0; | ||
482 | |||
483 | for (f = event->format.fields; f; f = f->next) { | ||
484 | if (not_first++) | ||
485 | fprintf(ofp, ", "); | ||
486 | if (count && count % 4 == 0) { | ||
487 | fprintf(ofp, "\".\n\t \""); | ||
488 | } | ||
489 | count++; | ||
490 | |||
491 | fprintf(ofp, "%s=", f->name); | ||
492 | if (f->flags & FIELD_IS_STRING || | ||
493 | f->flags & FIELD_IS_FLAG || | ||
494 | f->flags & FIELD_IS_SYMBOLIC) | ||
495 | fprintf(ofp, "%%s"); | ||
496 | else if (f->flags & FIELD_IS_SIGNED) | ||
497 | fprintf(ofp, "%%d"); | ||
498 | else | ||
499 | fprintf(ofp, "%%u"); | ||
500 | } | ||
501 | |||
502 | fprintf(ofp, "\\n\",\n\t "); | ||
503 | |||
504 | not_first = 0; | ||
505 | count = 0; | ||
506 | |||
507 | for (f = event->format.fields; f; f = f->next) { | ||
508 | if (not_first++) | ||
509 | fprintf(ofp, ", "); | ||
510 | |||
511 | if (++count % 5 == 0) | ||
512 | fprintf(ofp, "\n\t "); | ||
513 | |||
514 | if (f->flags & FIELD_IS_FLAG) { | ||
515 | if ((count - 1) % 5 != 0) { | ||
516 | fprintf(ofp, "\n\t "); | ||
517 | count = 4; | ||
518 | } | ||
519 | fprintf(ofp, "flag_str(\""); | ||
520 | fprintf(ofp, "%s::%s\", ", event->system, | ||
521 | event->name); | ||
522 | fprintf(ofp, "\"%s\", $%s)", f->name, | ||
523 | f->name); | ||
524 | } else if (f->flags & FIELD_IS_SYMBOLIC) { | ||
525 | if ((count - 1) % 5 != 0) { | ||
526 | fprintf(ofp, "\n\t "); | ||
527 | count = 4; | ||
528 | } | ||
529 | fprintf(ofp, "symbol_str(\""); | ||
530 | fprintf(ofp, "%s::%s\", ", event->system, | ||
531 | event->name); | ||
532 | fprintf(ofp, "\"%s\", $%s)", f->name, | ||
533 | f->name); | ||
534 | } else | ||
535 | fprintf(ofp, "$%s", f->name); | ||
536 | } | ||
537 | |||
538 | fprintf(ofp, ");\n"); | ||
539 | fprintf(ofp, "}\n\n"); | ||
540 | } | ||
541 | |||
542 | fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, " | ||
543 | "$common_cpu, $common_secs, $common_nsecs,\n\t " | ||
544 | "$common_pid, $common_comm) = @_;\n\n"); | ||
545 | |||
546 | fprintf(ofp, "\tprint_header($event_name, $common_cpu, " | ||
547 | "$common_secs, $common_nsecs,\n\t $common_pid, " | ||
548 | "$common_comm);\n}\n\n"); | ||
549 | |||
550 | fprintf(ofp, "sub print_header\n{\n" | ||
551 | "\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n" | ||
552 | "\tprintf(\"%%-20s %%5u %%05u.%%09u %%8u %%-20s \",\n\t " | ||
553 | "$event_name, $cpu, $secs, $nsecs, $pid, $comm);\n}"); | ||
554 | |||
555 | fclose(ofp); | ||
556 | |||
557 | fprintf(stderr, "generated Perl script: %s\n", fname); | ||
558 | |||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | struct scripting_ops perl_scripting_ops = { | ||
563 | .name = "Perl", | ||
564 | .start_script = perl_start_script, | ||
565 | .stop_script = perl_stop_script, | ||
566 | .process_event = perl_process_event, | ||
567 | .generate_script = perl_generate_script, | ||
568 | }; | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c new file mode 100644 index 000000000000..6a72f14c5986 --- /dev/null +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -0,0 +1,580 @@ | |||
1 | /* | ||
2 | * trace-event-python. Feed trace events to an embedded Python interpreter. | ||
3 | * | ||
4 | * Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com> | ||
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 <Python.h> | ||
23 | |||
24 | #include <stdio.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | #include <ctype.h> | ||
28 | #include <errno.h> | ||
29 | |||
30 | #include "../../perf.h" | ||
31 | #include "../util.h" | ||
32 | #include "../trace-event.h" | ||
33 | |||
34 | PyMODINIT_FUNC initperf_trace_context(void); | ||
35 | |||
36 | #define FTRACE_MAX_EVENT \ | ||
37 | ((1 << (sizeof(unsigned short) * 8)) - 1) | ||
38 | |||
39 | struct event *events[FTRACE_MAX_EVENT]; | ||
40 | |||
41 | #define MAX_FIELDS 64 | ||
42 | #define N_COMMON_FIELDS 7 | ||
43 | |||
44 | extern struct scripting_context *scripting_context; | ||
45 | |||
46 | static char *cur_field_name; | ||
47 | static int zero_flag_atom; | ||
48 | |||
49 | static PyObject *main_module, *main_dict; | ||
50 | |||
51 | static void handler_call_die(const char *handler_name) | ||
52 | { | ||
53 | PyErr_Print(); | ||
54 | Py_FatalError("problem in Python trace event handler"); | ||
55 | } | ||
56 | |||
57 | static void define_value(enum print_arg_type field_type, | ||
58 | const char *ev_name, | ||
59 | const char *field_name, | ||
60 | const char *field_value, | ||
61 | const char *field_str) | ||
62 | { | ||
63 | const char *handler_name = "define_flag_value"; | ||
64 | PyObject *handler, *t, *retval; | ||
65 | unsigned long long value; | ||
66 | unsigned n = 0; | ||
67 | |||
68 | if (field_type == PRINT_SYMBOL) | ||
69 | handler_name = "define_symbolic_value"; | ||
70 | |||
71 | t = PyTuple_New(4); | ||
72 | if (!t) | ||
73 | Py_FatalError("couldn't create Python tuple"); | ||
74 | |||
75 | value = eval_flag(field_value); | ||
76 | |||
77 | PyTuple_SetItem(t, n++, PyString_FromString(ev_name)); | ||
78 | PyTuple_SetItem(t, n++, PyString_FromString(field_name)); | ||
79 | PyTuple_SetItem(t, n++, PyInt_FromLong(value)); | ||
80 | PyTuple_SetItem(t, n++, PyString_FromString(field_str)); | ||
81 | |||
82 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
83 | if (handler && PyCallable_Check(handler)) { | ||
84 | retval = PyObject_CallObject(handler, t); | ||
85 | if (retval == NULL) | ||
86 | handler_call_die(handler_name); | ||
87 | } | ||
88 | |||
89 | Py_DECREF(t); | ||
90 | } | ||
91 | |||
92 | static void define_values(enum print_arg_type field_type, | ||
93 | struct print_flag_sym *field, | ||
94 | const char *ev_name, | ||
95 | const char *field_name) | ||
96 | { | ||
97 | define_value(field_type, ev_name, field_name, field->value, | ||
98 | field->str); | ||
99 | |||
100 | if (field->next) | ||
101 | define_values(field_type, field->next, ev_name, field_name); | ||
102 | } | ||
103 | |||
104 | static void define_field(enum print_arg_type field_type, | ||
105 | const char *ev_name, | ||
106 | const char *field_name, | ||
107 | const char *delim) | ||
108 | { | ||
109 | const char *handler_name = "define_flag_field"; | ||
110 | PyObject *handler, *t, *retval; | ||
111 | unsigned n = 0; | ||
112 | |||
113 | if (field_type == PRINT_SYMBOL) | ||
114 | handler_name = "define_symbolic_field"; | ||
115 | |||
116 | if (field_type == PRINT_FLAGS) | ||
117 | t = PyTuple_New(3); | ||
118 | else | ||
119 | t = PyTuple_New(2); | ||
120 | if (!t) | ||
121 | Py_FatalError("couldn't create Python tuple"); | ||
122 | |||
123 | PyTuple_SetItem(t, n++, PyString_FromString(ev_name)); | ||
124 | PyTuple_SetItem(t, n++, PyString_FromString(field_name)); | ||
125 | if (field_type == PRINT_FLAGS) | ||
126 | PyTuple_SetItem(t, n++, PyString_FromString(delim)); | ||
127 | |||
128 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
129 | if (handler && PyCallable_Check(handler)) { | ||
130 | retval = PyObject_CallObject(handler, t); | ||
131 | if (retval == NULL) | ||
132 | handler_call_die(handler_name); | ||
133 | } | ||
134 | |||
135 | Py_DECREF(t); | ||
136 | } | ||
137 | |||
138 | static void define_event_symbols(struct event *event, | ||
139 | const char *ev_name, | ||
140 | struct print_arg *args) | ||
141 | { | ||
142 | switch (args->type) { | ||
143 | case PRINT_NULL: | ||
144 | break; | ||
145 | case PRINT_ATOM: | ||
146 | define_value(PRINT_FLAGS, ev_name, cur_field_name, "0", | ||
147 | args->atom.atom); | ||
148 | zero_flag_atom = 0; | ||
149 | break; | ||
150 | case PRINT_FIELD: | ||
151 | if (cur_field_name) | ||
152 | free(cur_field_name); | ||
153 | cur_field_name = strdup(args->field.name); | ||
154 | break; | ||
155 | case PRINT_FLAGS: | ||
156 | define_event_symbols(event, ev_name, args->flags.field); | ||
157 | define_field(PRINT_FLAGS, ev_name, cur_field_name, | ||
158 | args->flags.delim); | ||
159 | define_values(PRINT_FLAGS, args->flags.flags, ev_name, | ||
160 | cur_field_name); | ||
161 | break; | ||
162 | case PRINT_SYMBOL: | ||
163 | define_event_symbols(event, ev_name, args->symbol.field); | ||
164 | define_field(PRINT_SYMBOL, ev_name, cur_field_name, NULL); | ||
165 | define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name, | ||
166 | cur_field_name); | ||
167 | break; | ||
168 | case PRINT_STRING: | ||
169 | break; | ||
170 | case PRINT_TYPE: | ||
171 | define_event_symbols(event, ev_name, args->typecast.item); | ||
172 | break; | ||
173 | case PRINT_OP: | ||
174 | if (strcmp(args->op.op, ":") == 0) | ||
175 | zero_flag_atom = 1; | ||
176 | define_event_symbols(event, ev_name, args->op.left); | ||
177 | define_event_symbols(event, ev_name, args->op.right); | ||
178 | break; | ||
179 | default: | ||
180 | /* we should warn... */ | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | if (args->next) | ||
185 | define_event_symbols(event, ev_name, args->next); | ||
186 | } | ||
187 | |||
188 | static inline struct event *find_cache_event(int type) | ||
189 | { | ||
190 | static char ev_name[256]; | ||
191 | struct event *event; | ||
192 | |||
193 | if (events[type]) | ||
194 | return events[type]; | ||
195 | |||
196 | events[type] = event = trace_find_event(type); | ||
197 | if (!event) | ||
198 | return NULL; | ||
199 | |||
200 | sprintf(ev_name, "%s__%s", event->system, event->name); | ||
201 | |||
202 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
203 | |||
204 | return event; | ||
205 | } | ||
206 | |||
207 | static void python_process_event(int cpu, void *data, | ||
208 | int size __unused, | ||
209 | unsigned long long nsecs, char *comm) | ||
210 | { | ||
211 | PyObject *handler, *retval, *context, *t, *obj; | ||
212 | static char handler_name[256]; | ||
213 | struct format_field *field; | ||
214 | unsigned long long val; | ||
215 | unsigned long s, ns; | ||
216 | struct event *event; | ||
217 | unsigned n = 0; | ||
218 | int type; | ||
219 | int pid; | ||
220 | |||
221 | t = PyTuple_New(MAX_FIELDS); | ||
222 | if (!t) | ||
223 | Py_FatalError("couldn't create Python tuple"); | ||
224 | |||
225 | type = trace_parse_common_type(data); | ||
226 | |||
227 | event = find_cache_event(type); | ||
228 | if (!event) | ||
229 | die("ug! no event found for type %d", type); | ||
230 | |||
231 | pid = trace_parse_common_pid(data); | ||
232 | |||
233 | sprintf(handler_name, "%s__%s", event->system, event->name); | ||
234 | |||
235 | s = nsecs / NSECS_PER_SEC; | ||
236 | ns = nsecs - s * NSECS_PER_SEC; | ||
237 | |||
238 | scripting_context->event_data = data; | ||
239 | |||
240 | context = PyCObject_FromVoidPtr(scripting_context, NULL); | ||
241 | |||
242 | PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); | ||
243 | PyTuple_SetItem(t, n++, | ||
244 | PyCObject_FromVoidPtr(scripting_context, NULL)); | ||
245 | PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); | ||
246 | PyTuple_SetItem(t, n++, PyInt_FromLong(s)); | ||
247 | PyTuple_SetItem(t, n++, PyInt_FromLong(ns)); | ||
248 | PyTuple_SetItem(t, n++, PyInt_FromLong(pid)); | ||
249 | PyTuple_SetItem(t, n++, PyString_FromString(comm)); | ||
250 | |||
251 | for (field = event->format.fields; field; field = field->next) { | ||
252 | if (field->flags & FIELD_IS_STRING) { | ||
253 | int offset; | ||
254 | if (field->flags & FIELD_IS_DYNAMIC) { | ||
255 | offset = *(int *)(data + field->offset); | ||
256 | offset &= 0xffff; | ||
257 | } else | ||
258 | offset = field->offset; | ||
259 | obj = PyString_FromString((char *)data + offset); | ||
260 | } else { /* FIELD_IS_NUMERIC */ | ||
261 | val = read_size(data + field->offset, field->size); | ||
262 | if (field->flags & FIELD_IS_SIGNED) { | ||
263 | if ((long long)val >= LONG_MIN && | ||
264 | (long long)val <= LONG_MAX) | ||
265 | obj = PyInt_FromLong(val); | ||
266 | else | ||
267 | obj = PyLong_FromLongLong(val); | ||
268 | } else { | ||
269 | if (val <= LONG_MAX) | ||
270 | obj = PyInt_FromLong(val); | ||
271 | else | ||
272 | obj = PyLong_FromUnsignedLongLong(val); | ||
273 | } | ||
274 | } | ||
275 | PyTuple_SetItem(t, n++, obj); | ||
276 | } | ||
277 | |||
278 | if (_PyTuple_Resize(&t, n) == -1) | ||
279 | Py_FatalError("error resizing Python tuple"); | ||
280 | |||
281 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
282 | if (handler && PyCallable_Check(handler)) { | ||
283 | retval = PyObject_CallObject(handler, t); | ||
284 | if (retval == NULL) | ||
285 | handler_call_die(handler_name); | ||
286 | } else { | ||
287 | handler = PyDict_GetItemString(main_dict, "trace_unhandled"); | ||
288 | if (handler && PyCallable_Check(handler)) { | ||
289 | if (_PyTuple_Resize(&t, N_COMMON_FIELDS) == -1) | ||
290 | Py_FatalError("error resizing Python tuple"); | ||
291 | |||
292 | retval = PyObject_CallObject(handler, t); | ||
293 | if (retval == NULL) | ||
294 | handler_call_die("trace_unhandled"); | ||
295 | } | ||
296 | } | ||
297 | |||
298 | Py_DECREF(t); | ||
299 | } | ||
300 | |||
301 | static int run_start_sub(void) | ||
302 | { | ||
303 | PyObject *handler, *retval; | ||
304 | int err = 0; | ||
305 | |||
306 | main_module = PyImport_AddModule("__main__"); | ||
307 | if (main_module == NULL) | ||
308 | return -1; | ||
309 | Py_INCREF(main_module); | ||
310 | |||
311 | main_dict = PyModule_GetDict(main_module); | ||
312 | if (main_dict == NULL) { | ||
313 | err = -1; | ||
314 | goto error; | ||
315 | } | ||
316 | Py_INCREF(main_dict); | ||
317 | |||
318 | handler = PyDict_GetItemString(main_dict, "trace_begin"); | ||
319 | if (handler == NULL || !PyCallable_Check(handler)) | ||
320 | goto out; | ||
321 | |||
322 | retval = PyObject_CallObject(handler, NULL); | ||
323 | if (retval == NULL) | ||
324 | handler_call_die("trace_begin"); | ||
325 | |||
326 | Py_DECREF(retval); | ||
327 | return err; | ||
328 | error: | ||
329 | Py_XDECREF(main_dict); | ||
330 | Py_XDECREF(main_module); | ||
331 | out: | ||
332 | return err; | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * Start trace script | ||
337 | */ | ||
338 | static int python_start_script(const char *script, int argc, const char **argv) | ||
339 | { | ||
340 | const char **command_line; | ||
341 | char buf[PATH_MAX]; | ||
342 | int i, err = 0; | ||
343 | FILE *fp; | ||
344 | |||
345 | command_line = malloc((argc + 1) * sizeof(const char *)); | ||
346 | command_line[0] = script; | ||
347 | for (i = 1; i < argc + 1; i++) | ||
348 | command_line[i] = argv[i - 1]; | ||
349 | |||
350 | Py_Initialize(); | ||
351 | |||
352 | initperf_trace_context(); | ||
353 | |||
354 | PySys_SetArgv(argc + 1, (char **)command_line); | ||
355 | |||
356 | fp = fopen(script, "r"); | ||
357 | if (!fp) { | ||
358 | sprintf(buf, "Can't open python script \"%s\"", script); | ||
359 | perror(buf); | ||
360 | err = -1; | ||
361 | goto error; | ||
362 | } | ||
363 | |||
364 | err = PyRun_SimpleFile(fp, script); | ||
365 | if (err) { | ||
366 | fprintf(stderr, "Error running python script %s\n", script); | ||
367 | goto error; | ||
368 | } | ||
369 | |||
370 | err = run_start_sub(); | ||
371 | if (err) { | ||
372 | fprintf(stderr, "Error starting python script %s\n", script); | ||
373 | goto error; | ||
374 | } | ||
375 | |||
376 | free(command_line); | ||
377 | fprintf(stderr, "perf trace started with Python script %s\n\n", | ||
378 | script); | ||
379 | |||
380 | return err; | ||
381 | error: | ||
382 | Py_Finalize(); | ||
383 | free(command_line); | ||
384 | |||
385 | return err; | ||
386 | } | ||
387 | |||
388 | /* | ||
389 | * Stop trace script | ||
390 | */ | ||
391 | static int python_stop_script(void) | ||
392 | { | ||
393 | PyObject *handler, *retval; | ||
394 | int err = 0; | ||
395 | |||
396 | handler = PyDict_GetItemString(main_dict, "trace_end"); | ||
397 | if (handler == NULL || !PyCallable_Check(handler)) | ||
398 | goto out; | ||
399 | |||
400 | retval = PyObject_CallObject(handler, NULL); | ||
401 | if (retval == NULL) | ||
402 | handler_call_die("trace_end"); | ||
403 | else | ||
404 | Py_DECREF(retval); | ||
405 | out: | ||
406 | Py_XDECREF(main_dict); | ||
407 | Py_XDECREF(main_module); | ||
408 | Py_Finalize(); | ||
409 | |||
410 | fprintf(stderr, "\nperf trace Python script stopped\n"); | ||
411 | |||
412 | return err; | ||
413 | } | ||
414 | |||
415 | static int python_generate_script(const char *outfile) | ||
416 | { | ||
417 | struct event *event = NULL; | ||
418 | struct format_field *f; | ||
419 | char fname[PATH_MAX]; | ||
420 | int not_first, count; | ||
421 | FILE *ofp; | ||
422 | |||
423 | sprintf(fname, "%s.py", outfile); | ||
424 | ofp = fopen(fname, "w"); | ||
425 | if (ofp == NULL) { | ||
426 | fprintf(stderr, "couldn't open %s\n", fname); | ||
427 | return -1; | ||
428 | } | ||
429 | fprintf(ofp, "# perf trace event handlers, " | ||
430 | "generated by perf trace -g python\n"); | ||
431 | |||
432 | fprintf(ofp, "# Licensed under the terms of the GNU GPL" | ||
433 | " License version 2\n\n"); | ||
434 | |||
435 | fprintf(ofp, "# The common_* event handler fields are the most useful " | ||
436 | "fields common to\n"); | ||
437 | |||
438 | fprintf(ofp, "# all events. They don't necessarily correspond to " | ||
439 | "the 'common_*' fields\n"); | ||
440 | |||
441 | fprintf(ofp, "# in the format files. Those fields not available as " | ||
442 | "handler params can\n"); | ||
443 | |||
444 | fprintf(ofp, "# be retrieved using Python functions of the form " | ||
445 | "common_*(context).\n"); | ||
446 | |||
447 | fprintf(ofp, "# See the perf-trace-python Documentation for the list " | ||
448 | "of available functions.\n\n"); | ||
449 | |||
450 | fprintf(ofp, "import os\n"); | ||
451 | fprintf(ofp, "import sys\n\n"); | ||
452 | |||
453 | fprintf(ofp, "sys.path.append(os.environ['PERF_EXEC_PATH'] + \\\n"); | ||
454 | fprintf(ofp, "\t'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')\n"); | ||
455 | fprintf(ofp, "\nfrom perf_trace_context import *\n"); | ||
456 | fprintf(ofp, "from Core import *\n\n\n"); | ||
457 | |||
458 | fprintf(ofp, "def trace_begin():\n"); | ||
459 | fprintf(ofp, "\tprint \"in trace_begin\"\n\n"); | ||
460 | |||
461 | fprintf(ofp, "def trace_end():\n"); | ||
462 | fprintf(ofp, "\tprint \"in trace_end\"\n\n"); | ||
463 | |||
464 | while ((event = trace_find_next_event(event))) { | ||
465 | fprintf(ofp, "def %s__%s(", event->system, event->name); | ||
466 | fprintf(ofp, "event_name, "); | ||
467 | fprintf(ofp, "context, "); | ||
468 | fprintf(ofp, "common_cpu,\n"); | ||
469 | fprintf(ofp, "\tcommon_secs, "); | ||
470 | fprintf(ofp, "common_nsecs, "); | ||
471 | fprintf(ofp, "common_pid, "); | ||
472 | fprintf(ofp, "common_comm,\n\t"); | ||
473 | |||
474 | not_first = 0; | ||
475 | count = 0; | ||
476 | |||
477 | for (f = event->format.fields; f; f = f->next) { | ||
478 | if (not_first++) | ||
479 | fprintf(ofp, ", "); | ||
480 | if (++count % 5 == 0) | ||
481 | fprintf(ofp, "\n\t"); | ||
482 | |||
483 | fprintf(ofp, "%s", f->name); | ||
484 | } | ||
485 | fprintf(ofp, "):\n"); | ||
486 | |||
487 | fprintf(ofp, "\t\tprint_header(event_name, common_cpu, " | ||
488 | "common_secs, common_nsecs,\n\t\t\t" | ||
489 | "common_pid, common_comm)\n\n"); | ||
490 | |||
491 | fprintf(ofp, "\t\tprint \""); | ||
492 | |||
493 | not_first = 0; | ||
494 | count = 0; | ||
495 | |||
496 | for (f = event->format.fields; f; f = f->next) { | ||
497 | if (not_first++) | ||
498 | fprintf(ofp, ", "); | ||
499 | if (count && count % 3 == 0) { | ||
500 | fprintf(ofp, "\" \\\n\t\t\""); | ||
501 | } | ||
502 | count++; | ||
503 | |||
504 | fprintf(ofp, "%s=", f->name); | ||
505 | if (f->flags & FIELD_IS_STRING || | ||
506 | f->flags & FIELD_IS_FLAG || | ||
507 | f->flags & FIELD_IS_SYMBOLIC) | ||
508 | fprintf(ofp, "%%s"); | ||
509 | else if (f->flags & FIELD_IS_SIGNED) | ||
510 | fprintf(ofp, "%%d"); | ||
511 | else | ||
512 | fprintf(ofp, "%%u"); | ||
513 | } | ||
514 | |||
515 | fprintf(ofp, "\\n\" %% \\\n\t\t("); | ||
516 | |||
517 | not_first = 0; | ||
518 | count = 0; | ||
519 | |||
520 | for (f = event->format.fields; f; f = f->next) { | ||
521 | if (not_first++) | ||
522 | fprintf(ofp, ", "); | ||
523 | |||
524 | if (++count % 5 == 0) | ||
525 | fprintf(ofp, "\n\t\t"); | ||
526 | |||
527 | if (f->flags & FIELD_IS_FLAG) { | ||
528 | if ((count - 1) % 5 != 0) { | ||
529 | fprintf(ofp, "\n\t\t"); | ||
530 | count = 4; | ||
531 | } | ||
532 | fprintf(ofp, "flag_str(\""); | ||
533 | fprintf(ofp, "%s__%s\", ", event->system, | ||
534 | event->name); | ||
535 | fprintf(ofp, "\"%s\", %s)", f->name, | ||
536 | f->name); | ||
537 | } else if (f->flags & FIELD_IS_SYMBOLIC) { | ||
538 | if ((count - 1) % 5 != 0) { | ||
539 | fprintf(ofp, "\n\t\t"); | ||
540 | count = 4; | ||
541 | } | ||
542 | fprintf(ofp, "symbol_str(\""); | ||
543 | fprintf(ofp, "%s__%s\", ", event->system, | ||
544 | event->name); | ||
545 | fprintf(ofp, "\"%s\", %s)", f->name, | ||
546 | f->name); | ||
547 | } else | ||
548 | fprintf(ofp, "%s", f->name); | ||
549 | } | ||
550 | |||
551 | fprintf(ofp, "),\n\n"); | ||
552 | } | ||
553 | |||
554 | fprintf(ofp, "def trace_unhandled(event_name, context, " | ||
555 | "common_cpu, common_secs, common_nsecs,\n\t\t" | ||
556 | "common_pid, common_comm):\n"); | ||
557 | |||
558 | fprintf(ofp, "\t\tprint_header(event_name, common_cpu, " | ||
559 | "common_secs, common_nsecs,\n\t\tcommon_pid, " | ||
560 | "common_comm)\n\n"); | ||
561 | |||
562 | fprintf(ofp, "def print_header(" | ||
563 | "event_name, cpu, secs, nsecs, pid, comm):\n" | ||
564 | "\tprint \"%%-20s %%5u %%05u.%%09u %%8u %%-20s \" %% \\\n\t" | ||
565 | "(event_name, cpu, secs, nsecs, pid, comm),\n"); | ||
566 | |||
567 | fclose(ofp); | ||
568 | |||
569 | fprintf(stderr, "generated Python script: %s\n", fname); | ||
570 | |||
571 | return 0; | ||
572 | } | ||
573 | |||
574 | struct scripting_ops python_scripting_ops = { | ||
575 | .name = "Python", | ||
576 | .start_script = python_start_script, | ||
577 | .stop_script = python_stop_script, | ||
578 | .process_event = python_process_event, | ||
579 | .generate_script = python_generate_script, | ||
580 | }; | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c new file mode 100644 index 000000000000..eed1cb889008 --- /dev/null +++ b/tools/perf/util/session.c | |||
@@ -0,0 +1,574 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | ||
2 | |||
3 | #include <linux/kernel.h> | ||
4 | |||
5 | #include <byteswap.h> | ||
6 | #include <unistd.h> | ||
7 | #include <sys/types.h> | ||
8 | |||
9 | #include "session.h" | ||
10 | #include "sort.h" | ||
11 | #include "util.h" | ||
12 | |||
13 | static int perf_session__open(struct perf_session *self, bool force) | ||
14 | { | ||
15 | struct stat input_stat; | ||
16 | |||
17 | self->fd = open(self->filename, O_RDONLY); | ||
18 | if (self->fd < 0) { | ||
19 | pr_err("failed to open file: %s", self->filename); | ||
20 | if (!strcmp(self->filename, "perf.data")) | ||
21 | pr_err(" (try 'perf record' first)"); | ||
22 | pr_err("\n"); | ||
23 | return -errno; | ||
24 | } | ||
25 | |||
26 | if (fstat(self->fd, &input_stat) < 0) | ||
27 | goto out_close; | ||
28 | |||
29 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | ||
30 | pr_err("file %s not owned by current user or root\n", | ||
31 | self->filename); | ||
32 | goto out_close; | ||
33 | } | ||
34 | |||
35 | if (!input_stat.st_size) { | ||
36 | pr_info("zero-sized file (%s), nothing to do!\n", | ||
37 | self->filename); | ||
38 | goto out_close; | ||
39 | } | ||
40 | |||
41 | if (perf_header__read(&self->header, self->fd) < 0) { | ||
42 | pr_err("incompatible file format"); | ||
43 | goto out_close; | ||
44 | } | ||
45 | |||
46 | self->size = input_stat.st_size; | ||
47 | return 0; | ||
48 | |||
49 | out_close: | ||
50 | close(self->fd); | ||
51 | self->fd = -1; | ||
52 | return -1; | ||
53 | } | ||
54 | |||
55 | static inline int perf_session__create_kernel_maps(struct perf_session *self) | ||
56 | { | ||
57 | return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps); | ||
58 | } | ||
59 | |||
60 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) | ||
61 | { | ||
62 | size_t len = filename ? strlen(filename) + 1 : 0; | ||
63 | struct perf_session *self = zalloc(sizeof(*self) + len); | ||
64 | |||
65 | if (self == NULL) | ||
66 | goto out; | ||
67 | |||
68 | if (perf_header__init(&self->header) < 0) | ||
69 | goto out_free; | ||
70 | |||
71 | memcpy(self->filename, filename, len); | ||
72 | self->threads = RB_ROOT; | ||
73 | self->stats_by_id = RB_ROOT; | ||
74 | self->last_match = NULL; | ||
75 | self->mmap_window = 32; | ||
76 | self->cwd = NULL; | ||
77 | self->cwdlen = 0; | ||
78 | self->unknown_events = 0; | ||
79 | map_groups__init(&self->kmaps); | ||
80 | |||
81 | if (mode == O_RDONLY) { | ||
82 | if (perf_session__open(self, force) < 0) | ||
83 | goto out_delete; | ||
84 | } else if (mode == O_WRONLY) { | ||
85 | /* | ||
86 | * In O_RDONLY mode this will be performed when reading the | ||
87 | * kernel MMAP event, in event__process_mmap(). | ||
88 | */ | ||
89 | if (perf_session__create_kernel_maps(self) < 0) | ||
90 | goto out_delete; | ||
91 | } | ||
92 | |||
93 | self->sample_type = perf_header__sample_type(&self->header); | ||
94 | out: | ||
95 | return self; | ||
96 | out_free: | ||
97 | free(self); | ||
98 | return NULL; | ||
99 | out_delete: | ||
100 | perf_session__delete(self); | ||
101 | return NULL; | ||
102 | } | ||
103 | |||
104 | void perf_session__delete(struct perf_session *self) | ||
105 | { | ||
106 | perf_header__exit(&self->header); | ||
107 | close(self->fd); | ||
108 | free(self->cwd); | ||
109 | free(self); | ||
110 | } | ||
111 | |||
112 | static bool symbol__match_parent_regex(struct symbol *sym) | ||
113 | { | ||
114 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
115 | return 1; | ||
116 | |||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | struct symbol **perf_session__resolve_callchain(struct perf_session *self, | ||
121 | struct thread *thread, | ||
122 | struct ip_callchain *chain, | ||
123 | struct symbol **parent) | ||
124 | { | ||
125 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
126 | struct symbol **syms = NULL; | ||
127 | unsigned int i; | ||
128 | |||
129 | if (symbol_conf.use_callchain) { | ||
130 | syms = calloc(chain->nr, sizeof(*syms)); | ||
131 | if (!syms) { | ||
132 | fprintf(stderr, "Can't allocate memory for symbols\n"); | ||
133 | exit(-1); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | for (i = 0; i < chain->nr; i++) { | ||
138 | u64 ip = chain->ips[i]; | ||
139 | struct addr_location al; | ||
140 | |||
141 | if (ip >= PERF_CONTEXT_MAX) { | ||
142 | switch (ip) { | ||
143 | case PERF_CONTEXT_HV: | ||
144 | cpumode = PERF_RECORD_MISC_HYPERVISOR; break; | ||
145 | case PERF_CONTEXT_KERNEL: | ||
146 | cpumode = PERF_RECORD_MISC_KERNEL; break; | ||
147 | case PERF_CONTEXT_USER: | ||
148 | cpumode = PERF_RECORD_MISC_USER; break; | ||
149 | default: | ||
150 | break; | ||
151 | } | ||
152 | continue; | ||
153 | } | ||
154 | |||
155 | thread__find_addr_location(thread, self, cpumode, | ||
156 | MAP__FUNCTION, ip, &al, NULL); | ||
157 | if (al.sym != NULL) { | ||
158 | if (sort__has_parent && !*parent && | ||
159 | symbol__match_parent_regex(al.sym)) | ||
160 | *parent = al.sym; | ||
161 | if (!symbol_conf.use_callchain) | ||
162 | break; | ||
163 | syms[i] = al.sym; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | return syms; | ||
168 | } | ||
169 | |||
170 | static int process_event_stub(event_t *event __used, | ||
171 | struct perf_session *session __used) | ||
172 | { | ||
173 | dump_printf(": unhandled!\n"); | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | ||
178 | { | ||
179 | if (handler->sample == NULL) | ||
180 | handler->sample = process_event_stub; | ||
181 | if (handler->mmap == NULL) | ||
182 | handler->mmap = process_event_stub; | ||
183 | if (handler->comm == NULL) | ||
184 | handler->comm = process_event_stub; | ||
185 | if (handler->fork == NULL) | ||
186 | handler->fork = process_event_stub; | ||
187 | if (handler->exit == NULL) | ||
188 | handler->exit = process_event_stub; | ||
189 | if (handler->lost == NULL) | ||
190 | handler->lost = process_event_stub; | ||
191 | if (handler->read == NULL) | ||
192 | handler->read = process_event_stub; | ||
193 | if (handler->throttle == NULL) | ||
194 | handler->throttle = process_event_stub; | ||
195 | if (handler->unthrottle == NULL) | ||
196 | handler->unthrottle = process_event_stub; | ||
197 | } | ||
198 | |||
199 | static const char *event__name[] = { | ||
200 | [0] = "TOTAL", | ||
201 | [PERF_RECORD_MMAP] = "MMAP", | ||
202 | [PERF_RECORD_LOST] = "LOST", | ||
203 | [PERF_RECORD_COMM] = "COMM", | ||
204 | [PERF_RECORD_EXIT] = "EXIT", | ||
205 | [PERF_RECORD_THROTTLE] = "THROTTLE", | ||
206 | [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", | ||
207 | [PERF_RECORD_FORK] = "FORK", | ||
208 | [PERF_RECORD_READ] = "READ", | ||
209 | [PERF_RECORD_SAMPLE] = "SAMPLE", | ||
210 | }; | ||
211 | |||
212 | unsigned long event__total[PERF_RECORD_MAX]; | ||
213 | |||
214 | void event__print_totals(void) | ||
215 | { | ||
216 | int i; | ||
217 | for (i = 0; i < PERF_RECORD_MAX; ++i) | ||
218 | pr_info("%10s events: %10ld\n", | ||
219 | event__name[i], event__total[i]); | ||
220 | } | ||
221 | |||
222 | void mem_bswap_64(void *src, int byte_size) | ||
223 | { | ||
224 | u64 *m = src; | ||
225 | |||
226 | while (byte_size > 0) { | ||
227 | *m = bswap_64(*m); | ||
228 | byte_size -= sizeof(u64); | ||
229 | ++m; | ||
230 | } | ||
231 | } | ||
232 | |||
233 | static void event__all64_swap(event_t *self) | ||
234 | { | ||
235 | struct perf_event_header *hdr = &self->header; | ||
236 | mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr)); | ||
237 | } | ||
238 | |||
239 | static void event__comm_swap(event_t *self) | ||
240 | { | ||
241 | self->comm.pid = bswap_32(self->comm.pid); | ||
242 | self->comm.tid = bswap_32(self->comm.tid); | ||
243 | } | ||
244 | |||
245 | static void event__mmap_swap(event_t *self) | ||
246 | { | ||
247 | self->mmap.pid = bswap_32(self->mmap.pid); | ||
248 | self->mmap.tid = bswap_32(self->mmap.tid); | ||
249 | self->mmap.start = bswap_64(self->mmap.start); | ||
250 | self->mmap.len = bswap_64(self->mmap.len); | ||
251 | self->mmap.pgoff = bswap_64(self->mmap.pgoff); | ||
252 | } | ||
253 | |||
254 | static void event__task_swap(event_t *self) | ||
255 | { | ||
256 | self->fork.pid = bswap_32(self->fork.pid); | ||
257 | self->fork.tid = bswap_32(self->fork.tid); | ||
258 | self->fork.ppid = bswap_32(self->fork.ppid); | ||
259 | self->fork.ptid = bswap_32(self->fork.ptid); | ||
260 | self->fork.time = bswap_64(self->fork.time); | ||
261 | } | ||
262 | |||
263 | static void event__read_swap(event_t *self) | ||
264 | { | ||
265 | self->read.pid = bswap_32(self->read.pid); | ||
266 | self->read.tid = bswap_32(self->read.tid); | ||
267 | self->read.value = bswap_64(self->read.value); | ||
268 | self->read.time_enabled = bswap_64(self->read.time_enabled); | ||
269 | self->read.time_running = bswap_64(self->read.time_running); | ||
270 | self->read.id = bswap_64(self->read.id); | ||
271 | } | ||
272 | |||
273 | typedef void (*event__swap_op)(event_t *self); | ||
274 | |||
275 | static event__swap_op event__swap_ops[] = { | ||
276 | [PERF_RECORD_MMAP] = event__mmap_swap, | ||
277 | [PERF_RECORD_COMM] = event__comm_swap, | ||
278 | [PERF_RECORD_FORK] = event__task_swap, | ||
279 | [PERF_RECORD_EXIT] = event__task_swap, | ||
280 | [PERF_RECORD_LOST] = event__all64_swap, | ||
281 | [PERF_RECORD_READ] = event__read_swap, | ||
282 | [PERF_RECORD_SAMPLE] = event__all64_swap, | ||
283 | [PERF_RECORD_MAX] = NULL, | ||
284 | }; | ||
285 | |||
286 | static int perf_session__process_event(struct perf_session *self, | ||
287 | event_t *event, | ||
288 | struct perf_event_ops *ops, | ||
289 | u64 offset, u64 head) | ||
290 | { | ||
291 | trace_event(event); | ||
292 | |||
293 | if (event->header.type < PERF_RECORD_MAX) { | ||
294 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | ||
295 | offset + head, event->header.size, | ||
296 | event__name[event->header.type]); | ||
297 | ++event__total[0]; | ||
298 | ++event__total[event->header.type]; | ||
299 | } | ||
300 | |||
301 | if (self->header.needs_swap && event__swap_ops[event->header.type]) | ||
302 | event__swap_ops[event->header.type](event); | ||
303 | |||
304 | switch (event->header.type) { | ||
305 | case PERF_RECORD_SAMPLE: | ||
306 | return ops->sample(event, self); | ||
307 | case PERF_RECORD_MMAP: | ||
308 | return ops->mmap(event, self); | ||
309 | case PERF_RECORD_COMM: | ||
310 | return ops->comm(event, self); | ||
311 | case PERF_RECORD_FORK: | ||
312 | return ops->fork(event, self); | ||
313 | case PERF_RECORD_EXIT: | ||
314 | return ops->exit(event, self); | ||
315 | case PERF_RECORD_LOST: | ||
316 | return ops->lost(event, self); | ||
317 | case PERF_RECORD_READ: | ||
318 | return ops->read(event, self); | ||
319 | case PERF_RECORD_THROTTLE: | ||
320 | return ops->throttle(event, self); | ||
321 | case PERF_RECORD_UNTHROTTLE: | ||
322 | return ops->unthrottle(event, self); | ||
323 | default: | ||
324 | self->unknown_events++; | ||
325 | return -1; | ||
326 | } | ||
327 | } | ||
328 | |||
329 | void perf_event_header__bswap(struct perf_event_header *self) | ||
330 | { | ||
331 | self->type = bswap_32(self->type); | ||
332 | self->misc = bswap_16(self->misc); | ||
333 | self->size = bswap_16(self->size); | ||
334 | } | ||
335 | |||
336 | int perf_header__read_build_ids(struct perf_header *self, | ||
337 | int input, u64 offset, u64 size) | ||
338 | { | ||
339 | struct build_id_event bev; | ||
340 | char filename[PATH_MAX]; | ||
341 | u64 limit = offset + size; | ||
342 | int err = -1; | ||
343 | |||
344 | while (offset < limit) { | ||
345 | struct dso *dso; | ||
346 | ssize_t len; | ||
347 | struct list_head *head = &dsos__user; | ||
348 | |||
349 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
350 | goto out; | ||
351 | |||
352 | if (self->needs_swap) | ||
353 | perf_event_header__bswap(&bev.header); | ||
354 | |||
355 | len = bev.header.size - sizeof(bev); | ||
356 | if (read(input, filename, len) != len) | ||
357 | goto out; | ||
358 | |||
359 | if (bev.header.misc & PERF_RECORD_MISC_KERNEL) | ||
360 | head = &dsos__kernel; | ||
361 | |||
362 | dso = __dsos__findnew(head, filename); | ||
363 | if (dso != NULL) { | ||
364 | dso__set_build_id(dso, &bev.build_id); | ||
365 | if (head == &dsos__kernel && filename[0] == '[') | ||
366 | dso->kernel = 1; | ||
367 | } | ||
368 | |||
369 | offset += bev.header.size; | ||
370 | } | ||
371 | err = 0; | ||
372 | out: | ||
373 | return err; | ||
374 | } | ||
375 | |||
376 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | ||
377 | { | ||
378 | struct thread *thread = perf_session__findnew(self, 0); | ||
379 | |||
380 | if (thread == NULL || thread__set_comm(thread, "swapper")) { | ||
381 | pr_err("problem inserting idle task.\n"); | ||
382 | thread = NULL; | ||
383 | } | ||
384 | |||
385 | return thread; | ||
386 | } | ||
387 | |||
388 | int __perf_session__process_events(struct perf_session *self, | ||
389 | u64 data_offset, u64 data_size, | ||
390 | u64 file_size, struct perf_event_ops *ops) | ||
391 | { | ||
392 | int err, mmap_prot, mmap_flags; | ||
393 | u64 head, shift; | ||
394 | u64 offset = 0; | ||
395 | size_t page_size; | ||
396 | event_t *event; | ||
397 | uint32_t size; | ||
398 | char *buf; | ||
399 | |||
400 | perf_event_ops__fill_defaults(ops); | ||
401 | |||
402 | page_size = sysconf(_SC_PAGESIZE); | ||
403 | |||
404 | head = data_offset; | ||
405 | shift = page_size * (head / page_size); | ||
406 | offset += shift; | ||
407 | head -= shift; | ||
408 | |||
409 | mmap_prot = PROT_READ; | ||
410 | mmap_flags = MAP_SHARED; | ||
411 | |||
412 | if (self->header.needs_swap) { | ||
413 | mmap_prot |= PROT_WRITE; | ||
414 | mmap_flags = MAP_PRIVATE; | ||
415 | } | ||
416 | remap: | ||
417 | buf = mmap(NULL, page_size * self->mmap_window, mmap_prot, | ||
418 | mmap_flags, self->fd, offset); | ||
419 | if (buf == MAP_FAILED) { | ||
420 | pr_err("failed to mmap file\n"); | ||
421 | err = -errno; | ||
422 | goto out_err; | ||
423 | } | ||
424 | |||
425 | more: | ||
426 | event = (event_t *)(buf + head); | ||
427 | |||
428 | if (self->header.needs_swap) | ||
429 | perf_event_header__bswap(&event->header); | ||
430 | size = event->header.size; | ||
431 | if (size == 0) | ||
432 | size = 8; | ||
433 | |||
434 | if (head + event->header.size >= page_size * self->mmap_window) { | ||
435 | int munmap_ret; | ||
436 | |||
437 | shift = page_size * (head / page_size); | ||
438 | |||
439 | munmap_ret = munmap(buf, page_size * self->mmap_window); | ||
440 | assert(munmap_ret == 0); | ||
441 | |||
442 | offset += shift; | ||
443 | head -= shift; | ||
444 | goto remap; | ||
445 | } | ||
446 | |||
447 | size = event->header.size; | ||
448 | |||
449 | dump_printf("\n%#Lx [%#x]: event: %d\n", | ||
450 | offset + head, event->header.size, event->header.type); | ||
451 | |||
452 | if (size == 0 || | ||
453 | perf_session__process_event(self, event, ops, offset, head) < 0) { | ||
454 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | ||
455 | offset + head, event->header.size, | ||
456 | event->header.type); | ||
457 | /* | ||
458 | * assume we lost track of the stream, check alignment, and | ||
459 | * increment a single u64 in the hope to catch on again 'soon'. | ||
460 | */ | ||
461 | if (unlikely(head & 7)) | ||
462 | head &= ~7ULL; | ||
463 | |||
464 | size = 8; | ||
465 | } | ||
466 | |||
467 | head += size; | ||
468 | |||
469 | if (offset + head >= data_offset + data_size) | ||
470 | goto done; | ||
471 | |||
472 | if (offset + head < file_size) | ||
473 | goto more; | ||
474 | done: | ||
475 | err = 0; | ||
476 | out_err: | ||
477 | return err; | ||
478 | } | ||
479 | |||
480 | int perf_session__process_events(struct perf_session *self, | ||
481 | struct perf_event_ops *ops) | ||
482 | { | ||
483 | int err; | ||
484 | |||
485 | if (perf_session__register_idle_thread(self) == NULL) | ||
486 | return -ENOMEM; | ||
487 | |||
488 | if (!symbol_conf.full_paths) { | ||
489 | char bf[PATH_MAX]; | ||
490 | |||
491 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
492 | err = -errno; | ||
493 | out_getcwd_err: | ||
494 | pr_err("failed to get the current directory\n"); | ||
495 | goto out_err; | ||
496 | } | ||
497 | self->cwd = strdup(bf); | ||
498 | if (self->cwd == NULL) { | ||
499 | err = -ENOMEM; | ||
500 | goto out_getcwd_err; | ||
501 | } | ||
502 | self->cwdlen = strlen(self->cwd); | ||
503 | } | ||
504 | |||
505 | err = __perf_session__process_events(self, self->header.data_offset, | ||
506 | self->header.data_size, | ||
507 | self->size, ops); | ||
508 | out_err: | ||
509 | return err; | ||
510 | } | ||
511 | |||
512 | bool perf_session__has_traces(struct perf_session *self, const char *msg) | ||
513 | { | ||
514 | if (!(self->sample_type & PERF_SAMPLE_RAW)) { | ||
515 | pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg); | ||
516 | return false; | ||
517 | } | ||
518 | |||
519 | return true; | ||
520 | } | ||
521 | |||
522 | int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | ||
523 | const char *symbol_name, | ||
524 | u64 addr) | ||
525 | { | ||
526 | char *bracket; | ||
527 | enum map_type i; | ||
528 | |||
529 | self->ref_reloc_sym.name = strdup(symbol_name); | ||
530 | if (self->ref_reloc_sym.name == NULL) | ||
531 | return -ENOMEM; | ||
532 | |||
533 | bracket = strchr(self->ref_reloc_sym.name, ']'); | ||
534 | if (bracket) | ||
535 | *bracket = '\0'; | ||
536 | |||
537 | self->ref_reloc_sym.addr = addr; | ||
538 | |||
539 | for (i = 0; i < MAP__NR_TYPES; ++i) { | ||
540 | struct kmap *kmap = map__kmap(self->vmlinux_maps[i]); | ||
541 | kmap->ref_reloc_sym = &self->ref_reloc_sym; | ||
542 | } | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | static u64 map__reloc_map_ip(struct map *map, u64 ip) | ||
548 | { | ||
549 | return ip + (s64)map->pgoff; | ||
550 | } | ||
551 | |||
552 | static u64 map__reloc_unmap_ip(struct map *map, u64 ip) | ||
553 | { | ||
554 | return ip - (s64)map->pgoff; | ||
555 | } | ||
556 | |||
557 | void map__reloc_vmlinux(struct map *self) | ||
558 | { | ||
559 | struct kmap *kmap = map__kmap(self); | ||
560 | s64 reloc; | ||
561 | |||
562 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) | ||
563 | return; | ||
564 | |||
565 | reloc = (kmap->ref_reloc_sym->unrelocated_addr - | ||
566 | kmap->ref_reloc_sym->addr); | ||
567 | |||
568 | if (!reloc) | ||
569 | return; | ||
570 | |||
571 | self->map_ip = map__reloc_map_ip; | ||
572 | self->unmap_ip = map__reloc_unmap_ip; | ||
573 | self->pgoff = reloc; | ||
574 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h new file mode 100644 index 000000000000..5c33417eebb3 --- /dev/null +++ b/tools/perf/util/session.h | |||
@@ -0,0 +1,89 @@ | |||
1 | #ifndef __PERF_SESSION_H | ||
2 | #define __PERF_SESSION_H | ||
3 | |||
4 | #include "event.h" | ||
5 | #include "header.h" | ||
6 | #include "symbol.h" | ||
7 | #include "thread.h" | ||
8 | #include <linux/rbtree.h> | ||
9 | #include "../../../include/linux/perf_event.h" | ||
10 | |||
11 | struct ip_callchain; | ||
12 | struct thread; | ||
13 | |||
14 | struct perf_session { | ||
15 | struct perf_header header; | ||
16 | unsigned long size; | ||
17 | unsigned long mmap_window; | ||
18 | struct map_groups kmaps; | ||
19 | struct rb_root threads; | ||
20 | struct thread *last_match; | ||
21 | struct map *vmlinux_maps[MAP__NR_TYPES]; | ||
22 | struct events_stats events_stats; | ||
23 | struct rb_root stats_by_id; | ||
24 | unsigned long event_total[PERF_RECORD_MAX]; | ||
25 | unsigned long unknown_events; | ||
26 | struct rb_root hists; | ||
27 | u64 sample_type; | ||
28 | struct ref_reloc_sym ref_reloc_sym; | ||
29 | int fd; | ||
30 | int cwdlen; | ||
31 | char *cwd; | ||
32 | char filename[0]; | ||
33 | }; | ||
34 | |||
35 | typedef int (*event_op)(event_t *self, struct perf_session *session); | ||
36 | |||
37 | struct perf_event_ops { | ||
38 | event_op sample, | ||
39 | mmap, | ||
40 | comm, | ||
41 | fork, | ||
42 | exit, | ||
43 | lost, | ||
44 | read, | ||
45 | throttle, | ||
46 | unthrottle; | ||
47 | }; | ||
48 | |||
49 | struct perf_session *perf_session__new(const char *filename, int mode, bool force); | ||
50 | void perf_session__delete(struct perf_session *self); | ||
51 | |||
52 | void perf_event_header__bswap(struct perf_event_header *self); | ||
53 | |||
54 | int __perf_session__process_events(struct perf_session *self, | ||
55 | u64 data_offset, u64 data_size, u64 size, | ||
56 | struct perf_event_ops *ops); | ||
57 | int perf_session__process_events(struct perf_session *self, | ||
58 | struct perf_event_ops *event_ops); | ||
59 | |||
60 | struct symbol **perf_session__resolve_callchain(struct perf_session *self, | ||
61 | struct thread *thread, | ||
62 | struct ip_callchain *chain, | ||
63 | struct symbol **parent); | ||
64 | |||
65 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | ||
66 | |||
67 | int perf_header__read_build_ids(struct perf_header *self, int input, | ||
68 | u64 offset, u64 file_size); | ||
69 | |||
70 | int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | ||
71 | const char *symbol_name, | ||
72 | u64 addr); | ||
73 | |||
74 | void mem_bswap_64(void *src, int byte_size); | ||
75 | |||
76 | static inline int __perf_session__create_kernel_maps(struct perf_session *self, | ||
77 | struct dso *kernel) | ||
78 | { | ||
79 | return __map_groups__create_kernel_maps(&self->kmaps, | ||
80 | self->vmlinux_maps, kernel); | ||
81 | } | ||
82 | |||
83 | static inline struct map * | ||
84 | perf_session__new_module_map(struct perf_session *self, | ||
85 | u64 start, const char *filename) | ||
86 | { | ||
87 | return map_groups__new_module(&self->kmaps, start, filename); | ||
88 | } | ||
89 | #endif /* __PERF_SESSION_H */ | ||
diff --git a/tools/perf/util/sigchain.h b/tools/perf/util/sigchain.h index 618083bce0c6..1a53c11265fd 100644 --- a/tools/perf/util/sigchain.h +++ b/tools/perf/util/sigchain.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef SIGCHAIN_H | 1 | #ifndef __PERF_SIGCHAIN_H |
2 | #define SIGCHAIN_H | 2 | #define __PERF_SIGCHAIN_H |
3 | 3 | ||
4 | typedef void (*sigchain_fun)(int); | 4 | typedef void (*sigchain_fun)(int); |
5 | 5 | ||
@@ -8,4 +8,4 @@ int sigchain_pop(int sig); | |||
8 | 8 | ||
9 | void sigchain_push_common(sigchain_fun f); | 9 | void sigchain_push_common(sigchain_fun f); |
10 | 10 | ||
11 | #endif /* SIGCHAIN_H */ | 11 | #endif /* __PERF_SIGCHAIN_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c new file mode 100644 index 000000000000..cb0f327de9e8 --- /dev/null +++ b/tools/perf/util/sort.c | |||
@@ -0,0 +1,316 @@ | |||
1 | #include "sort.h" | ||
2 | |||
3 | regex_t parent_regex; | ||
4 | char default_parent_pattern[] = "^sys_|^do_page_fault"; | ||
5 | char *parent_pattern = default_parent_pattern; | ||
6 | char default_sort_order[] = "comm,dso,symbol"; | ||
7 | char *sort_order = default_sort_order; | ||
8 | int sort__need_collapse = 0; | ||
9 | int sort__has_parent = 0; | ||
10 | |||
11 | enum sort_type sort__first_dimension; | ||
12 | |||
13 | unsigned int dsos__col_width; | ||
14 | unsigned int comms__col_width; | ||
15 | unsigned int threads__col_width; | ||
16 | static unsigned int parent_symbol__col_width; | ||
17 | char * field_sep; | ||
18 | |||
19 | LIST_HEAD(hist_entry__sort_list); | ||
20 | |||
21 | struct sort_entry sort_thread = { | ||
22 | .header = "Command: Pid", | ||
23 | .cmp = sort__thread_cmp, | ||
24 | .print = sort__thread_print, | ||
25 | .width = &threads__col_width, | ||
26 | }; | ||
27 | |||
28 | struct sort_entry sort_comm = { | ||
29 | .header = "Command", | ||
30 | .cmp = sort__comm_cmp, | ||
31 | .collapse = sort__comm_collapse, | ||
32 | .print = sort__comm_print, | ||
33 | .width = &comms__col_width, | ||
34 | }; | ||
35 | |||
36 | struct sort_entry sort_dso = { | ||
37 | .header = "Shared Object", | ||
38 | .cmp = sort__dso_cmp, | ||
39 | .print = sort__dso_print, | ||
40 | .width = &dsos__col_width, | ||
41 | }; | ||
42 | |||
43 | struct sort_entry sort_sym = { | ||
44 | .header = "Symbol", | ||
45 | .cmp = sort__sym_cmp, | ||
46 | .print = sort__sym_print, | ||
47 | }; | ||
48 | |||
49 | struct sort_entry sort_parent = { | ||
50 | .header = "Parent symbol", | ||
51 | .cmp = sort__parent_cmp, | ||
52 | .print = sort__parent_print, | ||
53 | .width = &parent_symbol__col_width, | ||
54 | }; | ||
55 | |||
56 | struct sort_dimension { | ||
57 | const char *name; | ||
58 | struct sort_entry *entry; | ||
59 | int taken; | ||
60 | }; | ||
61 | |||
62 | static struct sort_dimension sort_dimensions[] = { | ||
63 | { .name = "pid", .entry = &sort_thread, }, | ||
64 | { .name = "comm", .entry = &sort_comm, }, | ||
65 | { .name = "dso", .entry = &sort_dso, }, | ||
66 | { .name = "symbol", .entry = &sort_sym, }, | ||
67 | { .name = "parent", .entry = &sort_parent, }, | ||
68 | }; | ||
69 | |||
70 | int64_t cmp_null(void *l, void *r) | ||
71 | { | ||
72 | if (!l && !r) | ||
73 | return 0; | ||
74 | else if (!l) | ||
75 | return -1; | ||
76 | else | ||
77 | return 1; | ||
78 | } | ||
79 | |||
80 | /* --sort pid */ | ||
81 | |||
82 | int64_t | ||
83 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
84 | { | ||
85 | return right->thread->pid - left->thread->pid; | ||
86 | } | ||
87 | |||
88 | int repsep_fprintf(FILE *fp, const char *fmt, ...) | ||
89 | { | ||
90 | int n; | ||
91 | va_list ap; | ||
92 | |||
93 | va_start(ap, fmt); | ||
94 | if (!field_sep) | ||
95 | n = vfprintf(fp, fmt, ap); | ||
96 | else { | ||
97 | char *bf = NULL; | ||
98 | n = vasprintf(&bf, fmt, ap); | ||
99 | if (n > 0) { | ||
100 | char *sep = bf; | ||
101 | |||
102 | while (1) { | ||
103 | sep = strchr(sep, *field_sep); | ||
104 | if (sep == NULL) | ||
105 | break; | ||
106 | *sep = '.'; | ||
107 | } | ||
108 | } | ||
109 | fputs(bf, fp); | ||
110 | free(bf); | ||
111 | } | ||
112 | va_end(ap); | ||
113 | return n; | ||
114 | } | ||
115 | |||
116 | size_t | ||
117 | sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
118 | { | ||
119 | return repsep_fprintf(fp, "%*s:%5d", width - 6, | ||
120 | self->thread->comm ?: "", self->thread->pid); | ||
121 | } | ||
122 | |||
123 | size_t | ||
124 | sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
125 | { | ||
126 | return repsep_fprintf(fp, "%*s", width, self->thread->comm); | ||
127 | } | ||
128 | |||
129 | /* --sort dso */ | ||
130 | |||
131 | int64_t | ||
132 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | ||
133 | { | ||
134 | struct dso *dso_l = left->map ? left->map->dso : NULL; | ||
135 | struct dso *dso_r = right->map ? right->map->dso : NULL; | ||
136 | const char *dso_name_l, *dso_name_r; | ||
137 | |||
138 | if (!dso_l || !dso_r) | ||
139 | return cmp_null(dso_l, dso_r); | ||
140 | |||
141 | if (verbose) { | ||
142 | dso_name_l = dso_l->long_name; | ||
143 | dso_name_r = dso_r->long_name; | ||
144 | } else { | ||
145 | dso_name_l = dso_l->short_name; | ||
146 | dso_name_r = dso_r->short_name; | ||
147 | } | ||
148 | |||
149 | return strcmp(dso_name_l, dso_name_r); | ||
150 | } | ||
151 | |||
152 | size_t | ||
153 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
154 | { | ||
155 | if (self->map && self->map->dso) { | ||
156 | const char *dso_name = !verbose ? self->map->dso->short_name : | ||
157 | self->map->dso->long_name; | ||
158 | return repsep_fprintf(fp, "%-*s", width, dso_name); | ||
159 | } | ||
160 | |||
161 | return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); | ||
162 | } | ||
163 | |||
164 | /* --sort symbol */ | ||
165 | |||
166 | int64_t | ||
167 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
168 | { | ||
169 | u64 ip_l, ip_r; | ||
170 | |||
171 | if (left->sym == right->sym) | ||
172 | return 0; | ||
173 | |||
174 | ip_l = left->sym ? left->sym->start : left->ip; | ||
175 | ip_r = right->sym ? right->sym->start : right->ip; | ||
176 | |||
177 | return (int64_t)(ip_r - ip_l); | ||
178 | } | ||
179 | |||
180 | |||
181 | size_t | ||
182 | sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) | ||
183 | { | ||
184 | size_t ret = 0; | ||
185 | |||
186 | if (verbose) { | ||
187 | char o = self->map ? dso__symtab_origin(self->map->dso) : '!'; | ||
188 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); | ||
189 | } | ||
190 | |||
191 | ret += repsep_fprintf(fp, "[%c] ", self->level); | ||
192 | if (self->sym) | ||
193 | ret += repsep_fprintf(fp, "%s", self->sym->name); | ||
194 | else | ||
195 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); | ||
196 | |||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | /* --sort comm */ | ||
201 | |||
202 | int64_t | ||
203 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | ||
204 | { | ||
205 | return right->thread->pid - left->thread->pid; | ||
206 | } | ||
207 | |||
208 | int64_t | ||
209 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
210 | { | ||
211 | char *comm_l = left->thread->comm; | ||
212 | char *comm_r = right->thread->comm; | ||
213 | |||
214 | if (!comm_l || !comm_r) | ||
215 | return cmp_null(comm_l, comm_r); | ||
216 | |||
217 | return strcmp(comm_l, comm_r); | ||
218 | } | ||
219 | |||
220 | /* --sort parent */ | ||
221 | |||
222 | int64_t | ||
223 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | ||
224 | { | ||
225 | struct symbol *sym_l = left->parent; | ||
226 | struct symbol *sym_r = right->parent; | ||
227 | |||
228 | if (!sym_l || !sym_r) | ||
229 | return cmp_null(sym_l, sym_r); | ||
230 | |||
231 | return strcmp(sym_l->name, sym_r->name); | ||
232 | } | ||
233 | |||
234 | size_t | ||
235 | sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
236 | { | ||
237 | return repsep_fprintf(fp, "%-*s", width, | ||
238 | self->parent ? self->parent->name : "[other]"); | ||
239 | } | ||
240 | |||
241 | int sort_dimension__add(const char *tok) | ||
242 | { | ||
243 | unsigned int i; | ||
244 | |||
245 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | ||
246 | struct sort_dimension *sd = &sort_dimensions[i]; | ||
247 | |||
248 | if (sd->taken) | ||
249 | continue; | ||
250 | |||
251 | if (strncasecmp(tok, sd->name, strlen(tok))) | ||
252 | continue; | ||
253 | |||
254 | if (sd->entry->collapse) | ||
255 | sort__need_collapse = 1; | ||
256 | |||
257 | if (sd->entry == &sort_parent) { | ||
258 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | ||
259 | if (ret) { | ||
260 | char err[BUFSIZ]; | ||
261 | |||
262 | regerror(ret, &parent_regex, err, sizeof(err)); | ||
263 | fprintf(stderr, "Invalid regex: %s\n%s", | ||
264 | parent_pattern, err); | ||
265 | exit(-1); | ||
266 | } | ||
267 | sort__has_parent = 1; | ||
268 | } | ||
269 | |||
270 | if (list_empty(&hist_entry__sort_list)) { | ||
271 | if (!strcmp(sd->name, "pid")) | ||
272 | sort__first_dimension = SORT_PID; | ||
273 | else if (!strcmp(sd->name, "comm")) | ||
274 | sort__first_dimension = SORT_COMM; | ||
275 | else if (!strcmp(sd->name, "dso")) | ||
276 | sort__first_dimension = SORT_DSO; | ||
277 | else if (!strcmp(sd->name, "symbol")) | ||
278 | sort__first_dimension = SORT_SYM; | ||
279 | else if (!strcmp(sd->name, "parent")) | ||
280 | sort__first_dimension = SORT_PARENT; | ||
281 | } | ||
282 | |||
283 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | ||
284 | sd->taken = 1; | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | return -ESRCH; | ||
290 | } | ||
291 | |||
292 | void setup_sorting(const char * const usagestr[], const struct option *opts) | ||
293 | { | ||
294 | char *tmp, *tok, *str = strdup(sort_order); | ||
295 | |||
296 | for (tok = strtok_r(str, ", ", &tmp); | ||
297 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
298 | if (sort_dimension__add(tok) < 0) { | ||
299 | error("Unknown --sort key: `%s'", tok); | ||
300 | usage_with_options(usagestr, opts); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | free(str); | ||
305 | } | ||
306 | |||
307 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | ||
308 | const char *list_name, FILE *fp) | ||
309 | { | ||
310 | if (list && strlist__nr_entries(list) == 1) { | ||
311 | if (fp != NULL) | ||
312 | fprintf(fp, "# %s: %s\n", list_name, | ||
313 | strlist__entry(list, 0)->s); | ||
314 | self->elide = true; | ||
315 | } | ||
316 | } | ||
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h new file mode 100644 index 000000000000..753f9ea99fb0 --- /dev/null +++ b/tools/perf/util/sort.h | |||
@@ -0,0 +1,107 @@ | |||
1 | #ifndef __PERF_SORT_H | ||
2 | #define __PERF_SORT_H | ||
3 | #include "../builtin.h" | ||
4 | |||
5 | #include "util.h" | ||
6 | |||
7 | #include "color.h" | ||
8 | #include <linux/list.h> | ||
9 | #include "cache.h" | ||
10 | #include <linux/rbtree.h> | ||
11 | #include "symbol.h" | ||
12 | #include "string.h" | ||
13 | #include "callchain.h" | ||
14 | #include "strlist.h" | ||
15 | #include "values.h" | ||
16 | |||
17 | #include "../perf.h" | ||
18 | #include "debug.h" | ||
19 | #include "header.h" | ||
20 | |||
21 | #include "parse-options.h" | ||
22 | #include "parse-events.h" | ||
23 | |||
24 | #include "thread.h" | ||
25 | #include "sort.h" | ||
26 | |||
27 | extern regex_t parent_regex; | ||
28 | extern char *sort_order; | ||
29 | extern char default_parent_pattern[]; | ||
30 | extern char *parent_pattern; | ||
31 | extern char default_sort_order[]; | ||
32 | extern int sort__need_collapse; | ||
33 | extern int sort__has_parent; | ||
34 | extern char *field_sep; | ||
35 | extern struct sort_entry sort_comm; | ||
36 | extern struct sort_entry sort_dso; | ||
37 | extern struct sort_entry sort_sym; | ||
38 | extern struct sort_entry sort_parent; | ||
39 | extern unsigned int dsos__col_width; | ||
40 | extern unsigned int comms__col_width; | ||
41 | extern unsigned int threads__col_width; | ||
42 | extern enum sort_type sort__first_dimension; | ||
43 | |||
44 | struct hist_entry { | ||
45 | struct rb_node rb_node; | ||
46 | u64 count; | ||
47 | struct thread *thread; | ||
48 | struct map *map; | ||
49 | struct symbol *sym; | ||
50 | u64 ip; | ||
51 | char level; | ||
52 | struct symbol *parent; | ||
53 | struct callchain_node callchain; | ||
54 | union { | ||
55 | unsigned long position; | ||
56 | struct hist_entry *pair; | ||
57 | struct rb_root sorted_chain; | ||
58 | }; | ||
59 | }; | ||
60 | |||
61 | enum sort_type { | ||
62 | SORT_PID, | ||
63 | SORT_COMM, | ||
64 | SORT_DSO, | ||
65 | SORT_SYM, | ||
66 | SORT_PARENT | ||
67 | }; | ||
68 | |||
69 | /* | ||
70 | * configurable sorting bits | ||
71 | */ | ||
72 | |||
73 | struct sort_entry { | ||
74 | struct list_head list; | ||
75 | |||
76 | const char *header; | ||
77 | |||
78 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); | ||
79 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); | ||
80 | size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); | ||
81 | unsigned int *width; | ||
82 | bool elide; | ||
83 | }; | ||
84 | |||
85 | extern struct sort_entry sort_thread; | ||
86 | extern struct list_head hist_entry__sort_list; | ||
87 | |||
88 | void setup_sorting(const char * const usagestr[], const struct option *opts); | ||
89 | |||
90 | extern int repsep_fprintf(FILE *fp, const char *fmt, ...); | ||
91 | extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); | ||
92 | extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); | ||
93 | extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int); | ||
94 | extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used); | ||
95 | extern int64_t cmp_null(void *, void *); | ||
96 | extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *); | ||
97 | extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *); | ||
98 | extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *); | ||
99 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); | ||
100 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | ||
101 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | ||
102 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | ||
103 | extern int sort_dimension__add(const char *); | ||
104 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | ||
105 | const char *list_name, FILE *fp); | ||
106 | |||
107 | #endif /* __PERF_SORT_H */ | ||
diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h index d2aa86c014c1..a3d121d6c83e 100644 --- a/tools/perf/util/strbuf.h +++ b/tools/perf/util/strbuf.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef STRBUF_H | 1 | #ifndef __PERF_STRBUF_H |
2 | #define STRBUF_H | 2 | #define __PERF_STRBUF_H |
3 | 3 | ||
4 | /* | 4 | /* |
5 | * Strbuf's can be use in many ways: as a byte array, or to store arbitrary | 5 | * Strbuf's can be use in many ways: as a byte array, or to store arbitrary |
@@ -134,4 +134,4 @@ extern int launch_editor(const char *path, struct strbuf *buffer, const char *co | |||
134 | extern int strbuf_branchname(struct strbuf *sb, const char *name); | 134 | extern int strbuf_branchname(struct strbuf *sb, const char *name); |
135 | extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name); | 135 | extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name); |
136 | 136 | ||
137 | #endif /* STRBUF_H */ | 137 | #endif /* __PERF_STRBUF_H */ |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index c93eca9a7be3..a175949ed216 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include "string.h" | 1 | #include "string.h" |
2 | #include "util.h" | ||
2 | 3 | ||
3 | static int hex(char ch) | 4 | static int hex(char ch) |
4 | { | 5 | { |
@@ -32,3 +33,307 @@ int hex2u64(const char *ptr, u64 *long_val) | |||
32 | 33 | ||
33 | return p - ptr; | 34 | return p - ptr; |
34 | } | 35 | } |
36 | |||
37 | char *strxfrchar(char *s, char from, char to) | ||
38 | { | ||
39 | char *p = s; | ||
40 | |||
41 | while ((p = strchr(p, from)) != NULL) | ||
42 | *p++ = to; | ||
43 | |||
44 | return s; | ||
45 | } | ||
46 | |||
47 | #define K 1024LL | ||
48 | /* | ||
49 | * perf_atoll() | ||
50 | * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB") | ||
51 | * and return its numeric value | ||
52 | */ | ||
53 | s64 perf_atoll(const char *str) | ||
54 | { | ||
55 | unsigned int i; | ||
56 | s64 length = -1, unit = 1; | ||
57 | |||
58 | if (!isdigit(str[0])) | ||
59 | goto out_err; | ||
60 | |||
61 | for (i = 1; i < strlen(str); i++) { | ||
62 | switch (str[i]) { | ||
63 | case 'B': | ||
64 | case 'b': | ||
65 | break; | ||
66 | case 'K': | ||
67 | if (str[i + 1] != 'B') | ||
68 | goto out_err; | ||
69 | else | ||
70 | goto kilo; | ||
71 | case 'k': | ||
72 | if (str[i + 1] != 'b') | ||
73 | goto out_err; | ||
74 | kilo: | ||
75 | unit = K; | ||
76 | break; | ||
77 | case 'M': | ||
78 | if (str[i + 1] != 'B') | ||
79 | goto out_err; | ||
80 | else | ||
81 | goto mega; | ||
82 | case 'm': | ||
83 | if (str[i + 1] != 'b') | ||
84 | goto out_err; | ||
85 | mega: | ||
86 | unit = K * K; | ||
87 | break; | ||
88 | case 'G': | ||
89 | if (str[i + 1] != 'B') | ||
90 | goto out_err; | ||
91 | else | ||
92 | goto giga; | ||
93 | case 'g': | ||
94 | if (str[i + 1] != 'b') | ||
95 | goto out_err; | ||
96 | giga: | ||
97 | unit = K * K * K; | ||
98 | break; | ||
99 | case 'T': | ||
100 | if (str[i + 1] != 'B') | ||
101 | goto out_err; | ||
102 | else | ||
103 | goto tera; | ||
104 | case 't': | ||
105 | if (str[i + 1] != 'b') | ||
106 | goto out_err; | ||
107 | tera: | ||
108 | unit = K * K * K * K; | ||
109 | break; | ||
110 | case '\0': /* only specified figures */ | ||
111 | unit = 1; | ||
112 | break; | ||
113 | default: | ||
114 | if (!isdigit(str[i])) | ||
115 | goto out_err; | ||
116 | break; | ||
117 | } | ||
118 | } | ||
119 | |||
120 | length = atoll(str) * unit; | ||
121 | goto out; | ||
122 | |||
123 | out_err: | ||
124 | length = -1; | ||
125 | out: | ||
126 | return length; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * Helper function for splitting a string into an argv-like array. | ||
131 | * originaly copied from lib/argv_split.c | ||
132 | */ | ||
133 | static const char *skip_sep(const char *cp) | ||
134 | { | ||
135 | while (*cp && isspace(*cp)) | ||
136 | cp++; | ||
137 | |||
138 | return cp; | ||
139 | } | ||
140 | |||
141 | static const char *skip_arg(const char *cp) | ||
142 | { | ||
143 | while (*cp && !isspace(*cp)) | ||
144 | cp++; | ||
145 | |||
146 | return cp; | ||
147 | } | ||
148 | |||
149 | static int count_argc(const char *str) | ||
150 | { | ||
151 | int count = 0; | ||
152 | |||
153 | while (*str) { | ||
154 | str = skip_sep(str); | ||
155 | if (*str) { | ||
156 | count++; | ||
157 | str = skip_arg(str); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | return count; | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * argv_free - free an argv | ||
166 | * @argv - the argument vector to be freed | ||
167 | * | ||
168 | * Frees an argv and the strings it points to. | ||
169 | */ | ||
170 | void argv_free(char **argv) | ||
171 | { | ||
172 | char **p; | ||
173 | for (p = argv; *p; p++) | ||
174 | free(*p); | ||
175 | |||
176 | free(argv); | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * argv_split - split a string at whitespace, returning an argv | ||
181 | * @str: the string to be split | ||
182 | * @argcp: returned argument count | ||
183 | * | ||
184 | * Returns an array of pointers to strings which are split out from | ||
185 | * @str. This is performed by strictly splitting on white-space; no | ||
186 | * quote processing is performed. Multiple whitespace characters are | ||
187 | * considered to be a single argument separator. The returned array | ||
188 | * is always NULL-terminated. Returns NULL on memory allocation | ||
189 | * failure. | ||
190 | */ | ||
191 | char **argv_split(const char *str, int *argcp) | ||
192 | { | ||
193 | int argc = count_argc(str); | ||
194 | char **argv = zalloc(sizeof(*argv) * (argc+1)); | ||
195 | char **argvp; | ||
196 | |||
197 | if (argv == NULL) | ||
198 | goto out; | ||
199 | |||
200 | if (argcp) | ||
201 | *argcp = argc; | ||
202 | |||
203 | argvp = argv; | ||
204 | |||
205 | while (*str) { | ||
206 | str = skip_sep(str); | ||
207 | |||
208 | if (*str) { | ||
209 | const char *p = str; | ||
210 | char *t; | ||
211 | |||
212 | str = skip_arg(str); | ||
213 | |||
214 | t = strndup(p, str-p); | ||
215 | if (t == NULL) | ||
216 | goto fail; | ||
217 | *argvp++ = t; | ||
218 | } | ||
219 | } | ||
220 | *argvp = NULL; | ||
221 | |||
222 | out: | ||
223 | return argv; | ||
224 | |||
225 | fail: | ||
226 | argv_free(argv); | ||
227 | return NULL; | ||
228 | } | ||
229 | |||
230 | /* Character class matching */ | ||
231 | static bool __match_charclass(const char *pat, char c, const char **npat) | ||
232 | { | ||
233 | bool complement = false, ret = true; | ||
234 | |||
235 | if (*pat == '!') { | ||
236 | complement = true; | ||
237 | pat++; | ||
238 | } | ||
239 | if (*pat++ == c) /* First character is special */ | ||
240 | goto end; | ||
241 | |||
242 | while (*pat && *pat != ']') { /* Matching */ | ||
243 | if (*pat == '-' && *(pat + 1) != ']') { /* Range */ | ||
244 | if (*(pat - 1) <= c && c <= *(pat + 1)) | ||
245 | goto end; | ||
246 | if (*(pat - 1) > *(pat + 1)) | ||
247 | goto error; | ||
248 | pat += 2; | ||
249 | } else if (*pat++ == c) | ||
250 | goto end; | ||
251 | } | ||
252 | if (!*pat) | ||
253 | goto error; | ||
254 | ret = false; | ||
255 | |||
256 | end: | ||
257 | while (*pat && *pat != ']') /* Searching closing */ | ||
258 | pat++; | ||
259 | if (!*pat) | ||
260 | goto error; | ||
261 | *npat = pat + 1; | ||
262 | return complement ? !ret : ret; | ||
263 | |||
264 | error: | ||
265 | return false; | ||
266 | } | ||
267 | |||
268 | /* Glob/lazy pattern matching */ | ||
269 | static bool __match_glob(const char *str, const char *pat, bool ignore_space) | ||
270 | { | ||
271 | while (*str && *pat && *pat != '*') { | ||
272 | if (ignore_space) { | ||
273 | /* Ignore spaces for lazy matching */ | ||
274 | if (isspace(*str)) { | ||
275 | str++; | ||
276 | continue; | ||
277 | } | ||
278 | if (isspace(*pat)) { | ||
279 | pat++; | ||
280 | continue; | ||
281 | } | ||
282 | } | ||
283 | if (*pat == '?') { /* Matches any single character */ | ||
284 | str++; | ||
285 | pat++; | ||
286 | continue; | ||
287 | } else if (*pat == '[') /* Character classes/Ranges */ | ||
288 | if (__match_charclass(pat + 1, *str, &pat)) { | ||
289 | str++; | ||
290 | continue; | ||
291 | } else | ||
292 | return false; | ||
293 | else if (*pat == '\\') /* Escaped char match as normal char */ | ||
294 | pat++; | ||
295 | if (*str++ != *pat++) | ||
296 | return false; | ||
297 | } | ||
298 | /* Check wild card */ | ||
299 | if (*pat == '*') { | ||
300 | while (*pat == '*') | ||
301 | pat++; | ||
302 | if (!*pat) /* Tail wild card matches all */ | ||
303 | return true; | ||
304 | while (*str) | ||
305 | if (strglobmatch(str++, pat)) | ||
306 | return true; | ||
307 | } | ||
308 | return !*str && !*pat; | ||
309 | } | ||
310 | |||
311 | /** | ||
312 | * strglobmatch - glob expression pattern matching | ||
313 | * @str: the target string to match | ||
314 | * @pat: the pattern string to match | ||
315 | * | ||
316 | * This returns true if the @str matches @pat. @pat can includes wildcards | ||
317 | * ('*','?') and character classes ([CHARS], complementation and ranges are | ||
318 | * also supported). Also, this supports escape character ('\') to use special | ||
319 | * characters as normal character. | ||
320 | * | ||
321 | * Note: if @pat syntax is broken, this always returns false. | ||
322 | */ | ||
323 | bool strglobmatch(const char *str, const char *pat) | ||
324 | { | ||
325 | return __match_glob(str, pat, false); | ||
326 | } | ||
327 | |||
328 | /** | ||
329 | * strlazymatch - matching pattern strings lazily with glob pattern | ||
330 | * @str: the target string to match | ||
331 | * @pat: the pattern string to match | ||
332 | * | ||
333 | * This is similar to strglobmatch, except this ignores spaces in | ||
334 | * the target string. | ||
335 | */ | ||
336 | bool strlazymatch(const char *str, const char *pat) | ||
337 | { | ||
338 | return __match_glob(str, pat, true); | ||
339 | } | ||
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index bf39dfadfd24..542e44de3719 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
@@ -1,11 +1,18 @@ | |||
1 | #ifndef _PERF_STRING_H_ | 1 | #ifndef __PERF_STRING_H_ |
2 | #define _PERF_STRING_H_ | 2 | #define __PERF_STRING_H_ |
3 | 3 | ||
4 | #include <stdbool.h> | ||
4 | #include "types.h" | 5 | #include "types.h" |
5 | 6 | ||
6 | int hex2u64(const char *ptr, u64 *val); | 7 | int hex2u64(const char *ptr, u64 *val); |
8 | char *strxfrchar(char *s, char from, char to); | ||
9 | s64 perf_atoll(const char *str); | ||
10 | char **argv_split(const char *str, int *argcp); | ||
11 | void argv_free(char **argv); | ||
12 | bool strglobmatch(const char *str, const char *pat); | ||
13 | bool strlazymatch(const char *str, const char *pat); | ||
7 | 14 | ||
8 | #define _STR(x) #x | 15 | #define _STR(x) #x |
9 | #define STR(x) _STR(x) | 16 | #define STR(x) _STR(x) |
10 | 17 | ||
11 | #endif | 18 | #endif /* __PERF_STRING_H */ |
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 7ad38171dc2b..6783a2043555 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c | |||
@@ -102,7 +102,7 @@ void strlist__remove(struct strlist *self, struct str_node *sn) | |||
102 | str_node__delete(sn, self->dupstr); | 102 | str_node__delete(sn, self->dupstr); |
103 | } | 103 | } |
104 | 104 | ||
105 | bool strlist__has_entry(struct strlist *self, const char *entry) | 105 | struct str_node *strlist__find(struct strlist *self, const char *entry) |
106 | { | 106 | { |
107 | struct rb_node **p = &self->entries.rb_node; | 107 | struct rb_node **p = &self->entries.rb_node; |
108 | struct rb_node *parent = NULL; | 108 | struct rb_node *parent = NULL; |
@@ -120,10 +120,10 @@ bool strlist__has_entry(struct strlist *self, const char *entry) | |||
120 | else if (rc < 0) | 120 | else if (rc < 0) |
121 | p = &(*p)->rb_right; | 121 | p = &(*p)->rb_right; |
122 | else | 122 | else |
123 | return true; | 123 | return sn; |
124 | } | 124 | } |
125 | 125 | ||
126 | return false; | 126 | return NULL; |
127 | } | 127 | } |
128 | 128 | ||
129 | static int strlist__parse_list_entry(struct strlist *self, const char *s) | 129 | static int strlist__parse_list_entry(struct strlist *self, const char *s) |
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index 921818e44a54..3ba839007d2c 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef STRLIST_H_ | 1 | #ifndef __PERF_STRLIST_H |
2 | #define STRLIST_H_ | 2 | #define __PERF_STRLIST_H |
3 | 3 | ||
4 | #include <linux/rbtree.h> | 4 | #include <linux/rbtree.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
@@ -23,7 +23,12 @@ int strlist__load(struct strlist *self, const char *filename); | |||
23 | int strlist__add(struct strlist *self, const char *str); | 23 | int strlist__add(struct strlist *self, const char *str); |
24 | 24 | ||
25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); | 25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); |
26 | bool strlist__has_entry(struct strlist *self, const char *entry); | 26 | struct str_node *strlist__find(struct strlist *self, const char *entry); |
27 | |||
28 | static inline bool strlist__has_entry(struct strlist *self, const char *entry) | ||
29 | { | ||
30 | return strlist__find(self, entry) != NULL; | ||
31 | } | ||
27 | 32 | ||
28 | static inline bool strlist__empty(const struct strlist *self) | 33 | static inline bool strlist__empty(const struct strlist *self) |
29 | { | 34 | { |
@@ -35,5 +40,39 @@ static inline unsigned int strlist__nr_entries(const struct strlist *self) | |||
35 | return self->nr_entries; | 40 | return self->nr_entries; |
36 | } | 41 | } |
37 | 42 | ||
43 | /* For strlist iteration */ | ||
44 | static inline struct str_node *strlist__first(struct strlist *self) | ||
45 | { | ||
46 | struct rb_node *rn = rb_first(&self->entries); | ||
47 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; | ||
48 | } | ||
49 | static inline struct str_node *strlist__next(struct str_node *sn) | ||
50 | { | ||
51 | struct rb_node *rn; | ||
52 | if (!sn) | ||
53 | return NULL; | ||
54 | rn = rb_next(&sn->rb_node); | ||
55 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * strlist_for_each - iterate over a strlist | ||
60 | * @pos: the &struct str_node to use as a loop cursor. | ||
61 | * @self: the &struct strlist for loop. | ||
62 | */ | ||
63 | #define strlist__for_each(pos, self) \ | ||
64 | for (pos = strlist__first(self); pos; pos = strlist__next(pos)) | ||
65 | |||
66 | /** | ||
67 | * strlist_for_each_safe - iterate over a strlist safe against removal of | ||
68 | * str_node | ||
69 | * @pos: the &struct str_node to use as a loop cursor. | ||
70 | * @n: another &struct str_node to use as temporary storage. | ||
71 | * @self: the &struct strlist for loop. | ||
72 | */ | ||
73 | #define strlist__for_each_safe(pos, n, self) \ | ||
74 | for (pos = strlist__first(self), n = strlist__next(pos); pos;\ | ||
75 | pos = n, n = strlist__next(n)) | ||
76 | |||
38 | int strlist__parse_list(struct strlist *self, const char *s); | 77 | int strlist__parse_list(struct strlist *self, const char *s); |
39 | #endif /* STRLIST_H_ */ | 78 | #endif /* __PERF_STRLIST_H */ |
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index cd93195aedb3..e0781989cc31 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef _INCLUDE_GUARD_SVG_HELPER_ | 1 | #ifndef __PERF_SVGHELPER_H |
2 | #define _INCLUDE_GUARD_SVG_HELPER_ | 2 | #define __PERF_SVGHELPER_H |
3 | 3 | ||
4 | #include "types.h" | 4 | #include "types.h" |
5 | 5 | ||
@@ -25,4 +25,4 @@ extern void svg_close(void); | |||
25 | 25 | ||
26 | extern int svg_page_width; | 26 | extern int svg_page_width; |
27 | 27 | ||
28 | #endif | 28 | #endif /* __PERF_SVGHELPER_H */ |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 226f44a2357d..c458c4a371d1 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -1,111 +1,234 @@ | |||
1 | #include "util.h" | 1 | #include "util.h" |
2 | #include "../perf.h" | 2 | #include "../perf.h" |
3 | #include "sort.h" | ||
3 | #include "string.h" | 4 | #include "string.h" |
4 | #include "symbol.h" | 5 | #include "symbol.h" |
6 | #include "thread.h" | ||
5 | 7 | ||
6 | #include "debug.h" | 8 | #include "debug.h" |
7 | 9 | ||
10 | #include <asm/bug.h> | ||
8 | #include <libelf.h> | 11 | #include <libelf.h> |
9 | #include <gelf.h> | 12 | #include <gelf.h> |
10 | #include <elf.h> | 13 | #include <elf.h> |
14 | #include <limits.h> | ||
15 | #include <sys/utsname.h> | ||
11 | 16 | ||
12 | const char *sym_hist_filter; | 17 | #ifndef NT_GNU_BUILD_ID |
18 | #define NT_GNU_BUILD_ID 3 | ||
19 | #endif | ||
13 | 20 | ||
14 | enum dso_origin { | 21 | enum dso_origin { |
15 | DSO__ORIG_KERNEL = 0, | 22 | DSO__ORIG_KERNEL = 0, |
16 | DSO__ORIG_JAVA_JIT, | 23 | DSO__ORIG_JAVA_JIT, |
24 | DSO__ORIG_BUILD_ID_CACHE, | ||
17 | DSO__ORIG_FEDORA, | 25 | DSO__ORIG_FEDORA, |
18 | DSO__ORIG_UBUNTU, | 26 | DSO__ORIG_UBUNTU, |
19 | DSO__ORIG_BUILDID, | 27 | DSO__ORIG_BUILDID, |
20 | DSO__ORIG_DSO, | 28 | DSO__ORIG_DSO, |
29 | DSO__ORIG_KMODULE, | ||
21 | DSO__ORIG_NOT_FOUND, | 30 | DSO__ORIG_NOT_FOUND, |
22 | }; | 31 | }; |
23 | 32 | ||
24 | static struct symbol *symbol__new(u64 start, u64 len, | 33 | static void dsos__add(struct list_head *head, struct dso *dso); |
25 | const char *name, unsigned int priv_size, | 34 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
26 | u64 obj_start, int v) | 35 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
36 | symbol_filter_t filter); | ||
37 | static int vmlinux_path__nr_entries; | ||
38 | static char **vmlinux_path; | ||
39 | |||
40 | struct symbol_conf symbol_conf = { | ||
41 | .exclude_other = true, | ||
42 | .use_modules = true, | ||
43 | .try_vmlinux_path = true, | ||
44 | }; | ||
45 | |||
46 | bool dso__loaded(const struct dso *self, enum map_type type) | ||
27 | { | 47 | { |
28 | size_t namelen = strlen(name) + 1; | 48 | return self->loaded & (1 << type); |
29 | struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); | 49 | } |
30 | 50 | ||
31 | if (!self) | 51 | bool dso__sorted_by_name(const struct dso *self, enum map_type type) |
32 | return NULL; | 52 | { |
53 | return self->sorted_by_name & (1 << type); | ||
54 | } | ||
55 | |||
56 | static void dso__set_sorted_by_name(struct dso *self, enum map_type type) | ||
57 | { | ||
58 | self->sorted_by_name |= (1 << type); | ||
59 | } | ||
60 | |||
61 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) | ||
62 | { | ||
63 | switch (map_type) { | ||
64 | case MAP__FUNCTION: | ||
65 | return symbol_type == 'T' || symbol_type == 'W'; | ||
66 | case MAP__VARIABLE: | ||
67 | return symbol_type == 'D' || symbol_type == 'd'; | ||
68 | default: | ||
69 | return false; | ||
70 | } | ||
71 | } | ||
33 | 72 | ||
34 | if (v >= 2) | 73 | static void symbols__fixup_end(struct rb_root *self) |
35 | printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n", | 74 | { |
36 | (u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start); | 75 | struct rb_node *nd, *prevnd = rb_first(self); |
76 | struct symbol *curr, *prev; | ||
37 | 77 | ||
38 | self->obj_start= obj_start; | 78 | if (prevnd == NULL) |
39 | self->hist = NULL; | 79 | return; |
40 | self->hist_sum = 0; | ||
41 | 80 | ||
42 | if (sym_hist_filter && !strcmp(name, sym_hist_filter)) | 81 | curr = rb_entry(prevnd, struct symbol, rb_node); |
43 | self->hist = calloc(sizeof(u64), len); | ||
44 | 82 | ||
45 | if (priv_size) { | 83 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { |
46 | memset(self, 0, priv_size); | 84 | prev = curr; |
47 | self = ((void *)self) + priv_size; | 85 | curr = rb_entry(nd, struct symbol, rb_node); |
86 | |||
87 | if (prev->end == prev->start) | ||
88 | prev->end = curr->start - 1; | ||
48 | } | 89 | } |
90 | |||
91 | /* Last entry */ | ||
92 | if (curr->end == curr->start) | ||
93 | curr->end = roundup(curr->start, 4096); | ||
94 | } | ||
95 | |||
96 | static void __map_groups__fixup_end(struct map_groups *self, enum map_type type) | ||
97 | { | ||
98 | struct map *prev, *curr; | ||
99 | struct rb_node *nd, *prevnd = rb_first(&self->maps[type]); | ||
100 | |||
101 | if (prevnd == NULL) | ||
102 | return; | ||
103 | |||
104 | curr = rb_entry(prevnd, struct map, rb_node); | ||
105 | |||
106 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | ||
107 | prev = curr; | ||
108 | curr = rb_entry(nd, struct map, rb_node); | ||
109 | prev->end = curr->start - 1; | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * We still haven't the actual symbols, so guess the | ||
114 | * last map final address. | ||
115 | */ | ||
116 | curr->end = ~0UL; | ||
117 | } | ||
118 | |||
119 | static void map_groups__fixup_end(struct map_groups *self) | ||
120 | { | ||
121 | int i; | ||
122 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
123 | __map_groups__fixup_end(self, i); | ||
124 | } | ||
125 | |||
126 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) | ||
127 | { | ||
128 | size_t namelen = strlen(name) + 1; | ||
129 | struct symbol *self = zalloc(symbol_conf.priv_size + | ||
130 | sizeof(*self) + namelen); | ||
131 | if (self == NULL) | ||
132 | return NULL; | ||
133 | |||
134 | if (symbol_conf.priv_size) | ||
135 | self = ((void *)self) + symbol_conf.priv_size; | ||
136 | |||
49 | self->start = start; | 137 | self->start = start; |
50 | self->end = len ? start + len - 1 : start; | 138 | self->end = len ? start + len - 1 : start; |
139 | |||
140 | pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); | ||
141 | |||
51 | memcpy(self->name, name, namelen); | 142 | memcpy(self->name, name, namelen); |
52 | 143 | ||
53 | return self; | 144 | return self; |
54 | } | 145 | } |
55 | 146 | ||
56 | static void symbol__delete(struct symbol *self, unsigned int priv_size) | 147 | void symbol__delete(struct symbol *self) |
57 | { | 148 | { |
58 | free(((void *)self) - priv_size); | 149 | free(((void *)self) - symbol_conf.priv_size); |
59 | } | 150 | } |
60 | 151 | ||
61 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | 152 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) |
62 | { | 153 | { |
63 | if (!self->module) | 154 | return fprintf(fp, " %llx-%llx %s\n", |
64 | return fprintf(fp, " %llx-%llx %s\n", | ||
65 | self->start, self->end, self->name); | 155 | self->start, self->end, self->name); |
66 | else | ||
67 | return fprintf(fp, " %llx-%llx %s \t[%s]\n", | ||
68 | self->start, self->end, self->name, self->module->name); | ||
69 | } | 156 | } |
70 | 157 | ||
71 | struct dso *dso__new(const char *name, unsigned int sym_priv_size) | 158 | void dso__set_long_name(struct dso *self, char *name) |
72 | { | 159 | { |
73 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); | 160 | if (name == NULL) |
161 | return; | ||
162 | self->long_name = name; | ||
163 | self->long_name_len = strlen(name); | ||
164 | } | ||
165 | |||
166 | static void dso__set_short_name(struct dso *self, const char *name) | ||
167 | { | ||
168 | if (name == NULL) | ||
169 | return; | ||
170 | self->short_name = name; | ||
171 | self->short_name_len = strlen(name); | ||
172 | } | ||
173 | |||
174 | static void dso__set_basename(struct dso *self) | ||
175 | { | ||
176 | dso__set_short_name(self, basename(self->long_name)); | ||
177 | } | ||
178 | |||
179 | struct dso *dso__new(const char *name) | ||
180 | { | ||
181 | struct dso *self = zalloc(sizeof(*self) + strlen(name) + 1); | ||
74 | 182 | ||
75 | if (self != NULL) { | 183 | if (self != NULL) { |
184 | int i; | ||
76 | strcpy(self->name, name); | 185 | strcpy(self->name, name); |
77 | self->syms = RB_ROOT; | 186 | dso__set_long_name(self, self->name); |
78 | self->sym_priv_size = sym_priv_size; | 187 | dso__set_short_name(self, self->name); |
79 | self->find_symbol = dso__find_symbol; | 188 | for (i = 0; i < MAP__NR_TYPES; ++i) |
189 | self->symbols[i] = self->symbol_names[i] = RB_ROOT; | ||
80 | self->slen_calculated = 0; | 190 | self->slen_calculated = 0; |
81 | self->origin = DSO__ORIG_NOT_FOUND; | 191 | self->origin = DSO__ORIG_NOT_FOUND; |
192 | self->loaded = 0; | ||
193 | self->sorted_by_name = 0; | ||
194 | self->has_build_id = 0; | ||
82 | } | 195 | } |
83 | 196 | ||
84 | return self; | 197 | return self; |
85 | } | 198 | } |
86 | 199 | ||
87 | static void dso__delete_symbols(struct dso *self) | 200 | static void symbols__delete(struct rb_root *self) |
88 | { | 201 | { |
89 | struct symbol *pos; | 202 | struct symbol *pos; |
90 | struct rb_node *next = rb_first(&self->syms); | 203 | struct rb_node *next = rb_first(self); |
91 | 204 | ||
92 | while (next) { | 205 | while (next) { |
93 | pos = rb_entry(next, struct symbol, rb_node); | 206 | pos = rb_entry(next, struct symbol, rb_node); |
94 | next = rb_next(&pos->rb_node); | 207 | next = rb_next(&pos->rb_node); |
95 | rb_erase(&pos->rb_node, &self->syms); | 208 | rb_erase(&pos->rb_node, self); |
96 | symbol__delete(pos, self->sym_priv_size); | 209 | symbol__delete(pos); |
97 | } | 210 | } |
98 | } | 211 | } |
99 | 212 | ||
100 | void dso__delete(struct dso *self) | 213 | void dso__delete(struct dso *self) |
101 | { | 214 | { |
102 | dso__delete_symbols(self); | 215 | int i; |
216 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
217 | symbols__delete(&self->symbols[i]); | ||
218 | if (self->long_name != self->name) | ||
219 | free(self->long_name); | ||
103 | free(self); | 220 | free(self); |
104 | } | 221 | } |
105 | 222 | ||
106 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) | 223 | void dso__set_build_id(struct dso *self, void *build_id) |
107 | { | 224 | { |
108 | struct rb_node **p = &self->syms.rb_node; | 225 | memcpy(self->build_id, build_id, sizeof(self->build_id)); |
226 | self->has_build_id = 1; | ||
227 | } | ||
228 | |||
229 | static void symbols__insert(struct rb_root *self, struct symbol *sym) | ||
230 | { | ||
231 | struct rb_node **p = &self->rb_node; | ||
109 | struct rb_node *parent = NULL; | 232 | struct rb_node *parent = NULL; |
110 | const u64 ip = sym->start; | 233 | const u64 ip = sym->start; |
111 | struct symbol *s; | 234 | struct symbol *s; |
@@ -119,17 +242,17 @@ static void dso__insert_symbol(struct dso *self, struct symbol *sym) | |||
119 | p = &(*p)->rb_right; | 242 | p = &(*p)->rb_right; |
120 | } | 243 | } |
121 | rb_link_node(&sym->rb_node, parent, p); | 244 | rb_link_node(&sym->rb_node, parent, p); |
122 | rb_insert_color(&sym->rb_node, &self->syms); | 245 | rb_insert_color(&sym->rb_node, self); |
123 | } | 246 | } |
124 | 247 | ||
125 | struct symbol *dso__find_symbol(struct dso *self, u64 ip) | 248 | static struct symbol *symbols__find(struct rb_root *self, u64 ip) |
126 | { | 249 | { |
127 | struct rb_node *n; | 250 | struct rb_node *n; |
128 | 251 | ||
129 | if (self == NULL) | 252 | if (self == NULL) |
130 | return NULL; | 253 | return NULL; |
131 | 254 | ||
132 | n = self->syms.rb_node; | 255 | n = self->rb_node; |
133 | 256 | ||
134 | while (n) { | 257 | while (n) { |
135 | struct symbol *s = rb_entry(n, struct symbol, rb_node); | 258 | struct symbol *s = rb_entry(n, struct symbol, rb_node); |
@@ -145,12 +268,120 @@ struct symbol *dso__find_symbol(struct dso *self, u64 ip) | |||
145 | return NULL; | 268 | return NULL; |
146 | } | 269 | } |
147 | 270 | ||
148 | size_t dso__fprintf(struct dso *self, FILE *fp) | 271 | struct symbol_name_rb_node { |
272 | struct rb_node rb_node; | ||
273 | struct symbol sym; | ||
274 | }; | ||
275 | |||
276 | static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym) | ||
277 | { | ||
278 | struct rb_node **p = &self->rb_node; | ||
279 | struct rb_node *parent = NULL; | ||
280 | struct symbol_name_rb_node *symn = ((void *)sym) - sizeof(*parent), *s; | ||
281 | |||
282 | while (*p != NULL) { | ||
283 | parent = *p; | ||
284 | s = rb_entry(parent, struct symbol_name_rb_node, rb_node); | ||
285 | if (strcmp(sym->name, s->sym.name) < 0) | ||
286 | p = &(*p)->rb_left; | ||
287 | else | ||
288 | p = &(*p)->rb_right; | ||
289 | } | ||
290 | rb_link_node(&symn->rb_node, parent, p); | ||
291 | rb_insert_color(&symn->rb_node, self); | ||
292 | } | ||
293 | |||
294 | static void symbols__sort_by_name(struct rb_root *self, struct rb_root *source) | ||
295 | { | ||
296 | struct rb_node *nd; | ||
297 | |||
298 | for (nd = rb_first(source); nd; nd = rb_next(nd)) { | ||
299 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
300 | symbols__insert_by_name(self, pos); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | static struct symbol *symbols__find_by_name(struct rb_root *self, const char *name) | ||
305 | { | ||
306 | struct rb_node *n; | ||
307 | |||
308 | if (self == NULL) | ||
309 | return NULL; | ||
310 | |||
311 | n = self->rb_node; | ||
312 | |||
313 | while (n) { | ||
314 | struct symbol_name_rb_node *s; | ||
315 | int cmp; | ||
316 | |||
317 | s = rb_entry(n, struct symbol_name_rb_node, rb_node); | ||
318 | cmp = strcmp(name, s->sym.name); | ||
319 | |||
320 | if (cmp < 0) | ||
321 | n = n->rb_left; | ||
322 | else if (cmp > 0) | ||
323 | n = n->rb_right; | ||
324 | else | ||
325 | return &s->sym; | ||
326 | } | ||
327 | |||
328 | return NULL; | ||
329 | } | ||
330 | |||
331 | struct symbol *dso__find_symbol(struct dso *self, | ||
332 | enum map_type type, u64 addr) | ||
333 | { | ||
334 | return symbols__find(&self->symbols[type], addr); | ||
335 | } | ||
336 | |||
337 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | ||
338 | const char *name) | ||
339 | { | ||
340 | return symbols__find_by_name(&self->symbol_names[type], name); | ||
341 | } | ||
342 | |||
343 | void dso__sort_by_name(struct dso *self, enum map_type type) | ||
344 | { | ||
345 | dso__set_sorted_by_name(self, type); | ||
346 | return symbols__sort_by_name(&self->symbol_names[type], | ||
347 | &self->symbols[type]); | ||
348 | } | ||
349 | |||
350 | int build_id__sprintf(const u8 *self, int len, char *bf) | ||
351 | { | ||
352 | char *bid = bf; | ||
353 | const u8 *raw = self; | ||
354 | int i; | ||
355 | |||
356 | for (i = 0; i < len; ++i) { | ||
357 | sprintf(bid, "%02x", *raw); | ||
358 | ++raw; | ||
359 | bid += 2; | ||
360 | } | ||
361 | |||
362 | return raw - self; | ||
363 | } | ||
364 | |||
365 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp) | ||
149 | { | 366 | { |
150 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | 367 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
368 | |||
369 | build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id); | ||
370 | return fprintf(fp, "%s", sbuild_id); | ||
371 | } | ||
151 | 372 | ||
373 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp) | ||
374 | { | ||
152 | struct rb_node *nd; | 375 | struct rb_node *nd; |
153 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { | 376 | size_t ret = fprintf(fp, "dso: %s (", self->short_name); |
377 | |||
378 | if (self->short_name != self->long_name) | ||
379 | ret += fprintf(fp, "%s, ", self->long_name); | ||
380 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], | ||
381 | self->loaded ? "" : "NOT "); | ||
382 | ret += dso__fprintf_buildid(self, fp); | ||
383 | ret += fprintf(fp, ")\n"); | ||
384 | for (nd = rb_first(&self->symbols[type]); nd; nd = rb_next(nd)) { | ||
154 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 385 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); |
155 | ret += symbol__fprintf(pos, fp); | 386 | ret += symbol__fprintf(pos, fp); |
156 | } | 387 | } |
@@ -158,22 +389,23 @@ size_t dso__fprintf(struct dso *self, FILE *fp) | |||
158 | return ret; | 389 | return ret; |
159 | } | 390 | } |
160 | 391 | ||
161 | static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v) | 392 | int kallsyms__parse(const char *filename, void *arg, |
393 | int (*process_symbol)(void *arg, const char *name, | ||
394 | char type, u64 start)) | ||
162 | { | 395 | { |
163 | struct rb_node *nd, *prevnd; | ||
164 | char *line = NULL; | 396 | char *line = NULL; |
165 | size_t n; | 397 | size_t n; |
166 | FILE *file = fopen("/proc/kallsyms", "r"); | 398 | int err = 0; |
167 | int count = 0; | 399 | FILE *file = fopen(filename, "r"); |
168 | 400 | ||
169 | if (file == NULL) | 401 | if (file == NULL) |
170 | goto out_failure; | 402 | goto out_failure; |
171 | 403 | ||
172 | while (!feof(file)) { | 404 | while (!feof(file)) { |
173 | u64 start; | 405 | u64 start; |
174 | struct symbol *sym; | ||
175 | int line_len, len; | 406 | int line_len, len; |
176 | char symbol_type; | 407 | char symbol_type; |
408 | char *symbol_name; | ||
177 | 409 | ||
178 | line_len = getline(&line, &n, file); | 410 | line_len = getline(&line, &n, file); |
179 | if (line_len < 0) | 411 | if (line_len < 0) |
@@ -191,64 +423,168 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v) | |||
191 | continue; | 423 | continue; |
192 | 424 | ||
193 | symbol_type = toupper(line[len]); | 425 | symbol_type = toupper(line[len]); |
194 | /* | 426 | symbol_name = line + len + 2; |
195 | * We're interested only in code ('T'ext) | ||
196 | */ | ||
197 | if (symbol_type != 'T' && symbol_type != 'W') | ||
198 | continue; | ||
199 | /* | ||
200 | * Well fix up the end later, when we have all sorted. | ||
201 | */ | ||
202 | sym = symbol__new(start, 0xdead, line + len + 2, | ||
203 | self->sym_priv_size, 0, v); | ||
204 | 427 | ||
205 | if (sym == NULL) | 428 | err = process_symbol(arg, symbol_name, symbol_type, start); |
206 | goto out_delete_line; | 429 | if (err) |
207 | 430 | break; | |
208 | if (filter && filter(self, sym)) | ||
209 | symbol__delete(sym, self->sym_priv_size); | ||
210 | else { | ||
211 | dso__insert_symbol(self, sym); | ||
212 | count++; | ||
213 | } | ||
214 | } | 431 | } |
215 | 432 | ||
433 | free(line); | ||
434 | fclose(file); | ||
435 | return err; | ||
436 | |||
437 | out_failure: | ||
438 | return -1; | ||
439 | } | ||
440 | |||
441 | struct process_kallsyms_args { | ||
442 | struct map *map; | ||
443 | struct dso *dso; | ||
444 | }; | ||
445 | |||
446 | static int map__process_kallsym_symbol(void *arg, const char *name, | ||
447 | char type, u64 start) | ||
448 | { | ||
449 | struct symbol *sym; | ||
450 | struct process_kallsyms_args *a = arg; | ||
451 | struct rb_root *root = &a->dso->symbols[a->map->type]; | ||
452 | |||
453 | if (!symbol_type__is_a(type, a->map->type)) | ||
454 | return 0; | ||
455 | |||
216 | /* | 456 | /* |
217 | * Now that we have all sorted out, just set the ->end of all | 457 | * Will fix up the end later, when we have all symbols sorted. |
218 | * symbols | ||
219 | */ | 458 | */ |
220 | prevnd = rb_first(&self->syms); | 459 | sym = symbol__new(start, 0, name); |
221 | 460 | ||
222 | if (prevnd == NULL) | 461 | if (sym == NULL) |
223 | goto out_delete_line; | 462 | return -ENOMEM; |
463 | /* | ||
464 | * We will pass the symbols to the filter later, in | ||
465 | * map__split_kallsyms, when we have split the maps per module | ||
466 | */ | ||
467 | symbols__insert(root, sym); | ||
468 | return 0; | ||
469 | } | ||
224 | 470 | ||
225 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | 471 | /* |
226 | struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node), | 472 | * Loads the function entries in /proc/kallsyms into kernel_map->dso, |
227 | *curr = rb_entry(nd, struct symbol, rb_node); | 473 | * so that we can in the next step set the symbol ->end address and then |
474 | * call kernel_maps__split_kallsyms. | ||
475 | */ | ||
476 | static int dso__load_all_kallsyms(struct dso *self, const char *filename, | ||
477 | struct map *map) | ||
478 | { | ||
479 | struct process_kallsyms_args args = { .map = map, .dso = self, }; | ||
480 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); | ||
481 | } | ||
228 | 482 | ||
229 | prev->end = curr->start - 1; | 483 | /* |
230 | prevnd = nd; | 484 | * Split the symbols into maps, making sure there are no overlaps, i.e. the |
231 | } | 485 | * kernel range is broken in several maps, named [kernel].N, as we don't have |
486 | * the original ELF section names vmlinux have. | ||
487 | */ | ||
488 | static int dso__split_kallsyms(struct dso *self, struct map *map, | ||
489 | symbol_filter_t filter) | ||
490 | { | ||
491 | struct map_groups *kmaps = map__kmap(map)->kmaps; | ||
492 | struct map *curr_map = map; | ||
493 | struct symbol *pos; | ||
494 | int count = 0; | ||
495 | struct rb_root *root = &self->symbols[map->type]; | ||
496 | struct rb_node *next = rb_first(root); | ||
497 | int kernel_range = 0; | ||
232 | 498 | ||
233 | free(line); | 499 | while (next) { |
234 | fclose(file); | 500 | char *module; |
501 | |||
502 | pos = rb_entry(next, struct symbol, rb_node); | ||
503 | next = rb_next(&pos->rb_node); | ||
504 | |||
505 | module = strchr(pos->name, '\t'); | ||
506 | if (module) { | ||
507 | if (!symbol_conf.use_modules) | ||
508 | goto discard_symbol; | ||
509 | |||
510 | *module++ = '\0'; | ||
511 | |||
512 | if (strcmp(curr_map->dso->short_name, module)) { | ||
513 | curr_map = map_groups__find_by_name(kmaps, map->type, module); | ||
514 | if (curr_map == NULL) { | ||
515 | pr_debug("/proc/{kallsyms,modules} " | ||
516 | "inconsistency while looking " | ||
517 | "for \"%s\" module!\n", module); | ||
518 | return -1; | ||
519 | } | ||
520 | |||
521 | if (curr_map->dso->loaded) | ||
522 | goto discard_symbol; | ||
523 | } | ||
524 | /* | ||
525 | * So that we look just like we get from .ko files, | ||
526 | * i.e. not prelinked, relative to map->start. | ||
527 | */ | ||
528 | pos->start = curr_map->map_ip(curr_map, pos->start); | ||
529 | pos->end = curr_map->map_ip(curr_map, pos->end); | ||
530 | } else if (curr_map != map) { | ||
531 | char dso_name[PATH_MAX]; | ||
532 | struct dso *dso; | ||
533 | |||
534 | snprintf(dso_name, sizeof(dso_name), "[kernel].%d", | ||
535 | kernel_range++); | ||
536 | |||
537 | dso = dso__new(dso_name); | ||
538 | if (dso == NULL) | ||
539 | return -1; | ||
540 | |||
541 | curr_map = map__new2(pos->start, dso, map->type); | ||
542 | if (curr_map == NULL) { | ||
543 | dso__delete(dso); | ||
544 | return -1; | ||
545 | } | ||
546 | |||
547 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; | ||
548 | map_groups__insert(kmaps, curr_map); | ||
549 | ++kernel_range; | ||
550 | } | ||
551 | |||
552 | if (filter && filter(curr_map, pos)) { | ||
553 | discard_symbol: rb_erase(&pos->rb_node, root); | ||
554 | symbol__delete(pos); | ||
555 | } else { | ||
556 | if (curr_map != map) { | ||
557 | rb_erase(&pos->rb_node, root); | ||
558 | symbols__insert(&curr_map->dso->symbols[curr_map->type], pos); | ||
559 | } | ||
560 | count++; | ||
561 | } | ||
562 | } | ||
235 | 563 | ||
236 | return count; | 564 | return count; |
565 | } | ||
237 | 566 | ||
238 | out_delete_line: | 567 | int dso__load_kallsyms(struct dso *self, const char *filename, |
239 | free(line); | 568 | struct map *map, symbol_filter_t filter) |
240 | out_failure: | 569 | { |
241 | return -1; | 570 | if (dso__load_all_kallsyms(self, filename, map) < 0) |
571 | return -1; | ||
572 | |||
573 | symbols__fixup_end(&self->symbols[map->type]); | ||
574 | self->origin = DSO__ORIG_KERNEL; | ||
575 | |||
576 | return dso__split_kallsyms(self, map, filter); | ||
242 | } | 577 | } |
243 | 578 | ||
244 | static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int v) | 579 | static int dso__load_perf_map(struct dso *self, struct map *map, |
580 | symbol_filter_t filter) | ||
245 | { | 581 | { |
246 | char *line = NULL; | 582 | char *line = NULL; |
247 | size_t n; | 583 | size_t n; |
248 | FILE *file; | 584 | FILE *file; |
249 | int nr_syms = 0; | 585 | int nr_syms = 0; |
250 | 586 | ||
251 | file = fopen(self->name, "r"); | 587 | file = fopen(self->long_name, "r"); |
252 | if (file == NULL) | 588 | if (file == NULL) |
253 | goto out_failure; | 589 | goto out_failure; |
254 | 590 | ||
@@ -278,16 +614,15 @@ static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int v) | |||
278 | if (len + 2 >= line_len) | 614 | if (len + 2 >= line_len) |
279 | continue; | 615 | continue; |
280 | 616 | ||
281 | sym = symbol__new(start, size, line + len, | 617 | sym = symbol__new(start, size, line + len); |
282 | self->sym_priv_size, start, v); | ||
283 | 618 | ||
284 | if (sym == NULL) | 619 | if (sym == NULL) |
285 | goto out_delete_line; | 620 | goto out_delete_line; |
286 | 621 | ||
287 | if (filter && filter(self, sym)) | 622 | if (filter && filter(map, sym)) |
288 | symbol__delete(sym, self->sym_priv_size); | 623 | symbol__delete(sym); |
289 | else { | 624 | else { |
290 | dso__insert_symbol(self, sym); | 625 | symbols__insert(&self->symbols[map->type], sym); |
291 | nr_syms++; | 626 | nr_syms++; |
292 | } | 627 | } |
293 | } | 628 | } |
@@ -327,6 +662,13 @@ static inline int elf_sym__is_function(const GElf_Sym *sym) | |||
327 | sym->st_shndx != SHN_UNDEF; | 662 | sym->st_shndx != SHN_UNDEF; |
328 | } | 663 | } |
329 | 664 | ||
665 | static inline bool elf_sym__is_object(const GElf_Sym *sym) | ||
666 | { | ||
667 | return elf_sym__type(sym) == STT_OBJECT && | ||
668 | sym->st_name != 0 && | ||
669 | sym->st_shndx != SHN_UNDEF; | ||
670 | } | ||
671 | |||
330 | static inline int elf_sym__is_label(const GElf_Sym *sym) | 672 | static inline int elf_sym__is_label(const GElf_Sym *sym) |
331 | { | 673 | { |
332 | return elf_sym__type(sym) == STT_NOTYPE && | 674 | return elf_sym__type(sym) == STT_NOTYPE && |
@@ -347,6 +689,12 @@ static inline int elf_sec__is_text(const GElf_Shdr *shdr, | |||
347 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | 689 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; |
348 | } | 690 | } |
349 | 691 | ||
692 | static inline bool elf_sec__is_data(const GElf_Shdr *shdr, | ||
693 | const Elf_Data *secstrs) | ||
694 | { | ||
695 | return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; | ||
696 | } | ||
697 | |||
350 | static inline const char *elf_sym__name(const GElf_Sym *sym, | 698 | static inline const char *elf_sym__name(const GElf_Sym *sym, |
351 | const Elf_Data *symstrs) | 699 | const Elf_Data *symstrs) |
352 | { | 700 | { |
@@ -393,7 +741,8 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
393 | * And always look at the original dso, not at debuginfo packages, that | 741 | * And always look at the original dso, not at debuginfo packages, that |
394 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | 742 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). |
395 | */ | 743 | */ |
396 | static int dso__synthesize_plt_symbols(struct dso *self, int v) | 744 | static int dso__synthesize_plt_symbols(struct dso *self, struct map *map, |
745 | symbol_filter_t filter) | ||
397 | { | 746 | { |
398 | uint32_t nr_rel_entries, idx; | 747 | uint32_t nr_rel_entries, idx; |
399 | GElf_Sym sym; | 748 | GElf_Sym sym; |
@@ -409,7 +758,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
409 | Elf *elf; | 758 | Elf *elf; |
410 | int nr = 0, symidx, fd, err = 0; | 759 | int nr = 0, symidx, fd, err = 0; |
411 | 760 | ||
412 | fd = open(self->name, O_RDONLY); | 761 | fd = open(self->long_name, O_RDONLY); |
413 | if (fd < 0) | 762 | if (fd < 0) |
414 | goto out; | 763 | goto out; |
415 | 764 | ||
@@ -477,12 +826,16 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
477 | "%s@plt", elf_sym__name(&sym, symstrs)); | 826 | "%s@plt", elf_sym__name(&sym, symstrs)); |
478 | 827 | ||
479 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 828 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
480 | sympltname, self->sym_priv_size, 0, v); | 829 | sympltname); |
481 | if (!f) | 830 | if (!f) |
482 | goto out_elf_end; | 831 | goto out_elf_end; |
483 | 832 | ||
484 | dso__insert_symbol(self, f); | 833 | if (filter && filter(map, f)) |
485 | ++nr; | 834 | symbol__delete(f); |
835 | else { | ||
836 | symbols__insert(&self->symbols[map->type], f); | ||
837 | ++nr; | ||
838 | } | ||
486 | } | 839 | } |
487 | } else if (shdr_rel_plt.sh_type == SHT_REL) { | 840 | } else if (shdr_rel_plt.sh_type == SHT_REL) { |
488 | GElf_Rel pos_mem, *pos; | 841 | GElf_Rel pos_mem, *pos; |
@@ -495,12 +848,16 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
495 | "%s@plt", elf_sym__name(&sym, symstrs)); | 848 | "%s@plt", elf_sym__name(&sym, symstrs)); |
496 | 849 | ||
497 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 850 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
498 | sympltname, self->sym_priv_size, 0, v); | 851 | sympltname); |
499 | if (!f) | 852 | if (!f) |
500 | goto out_elf_end; | 853 | goto out_elf_end; |
501 | 854 | ||
502 | dso__insert_symbol(self, f); | 855 | if (filter && filter(map, f)) |
503 | ++nr; | 856 | symbol__delete(f); |
857 | else { | ||
858 | symbols__insert(&self->symbols[map->type], f); | ||
859 | ++nr; | ||
860 | } | ||
504 | } | 861 | } |
505 | } | 862 | } |
506 | 863 | ||
@@ -513,14 +870,41 @@ out_close: | |||
513 | if (err == 0) | 870 | if (err == 0) |
514 | return nr; | 871 | return nr; |
515 | out: | 872 | out: |
516 | fprintf(stderr, "%s: problems reading %s PLT info.\n", | 873 | pr_warning("%s: problems reading %s PLT info.\n", |
517 | __func__, self->name); | 874 | __func__, self->long_name); |
518 | return 0; | 875 | return 0; |
519 | } | 876 | } |
520 | 877 | ||
521 | static int dso__load_sym(struct dso *self, int fd, const char *name, | 878 | static bool elf_sym__is_a(GElf_Sym *self, enum map_type type) |
522 | symbol_filter_t filter, int v, struct module *mod) | ||
523 | { | 879 | { |
880 | switch (type) { | ||
881 | case MAP__FUNCTION: | ||
882 | return elf_sym__is_function(self); | ||
883 | case MAP__VARIABLE: | ||
884 | return elf_sym__is_object(self); | ||
885 | default: | ||
886 | return false; | ||
887 | } | ||
888 | } | ||
889 | |||
890 | static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type) | ||
891 | { | ||
892 | switch (type) { | ||
893 | case MAP__FUNCTION: | ||
894 | return elf_sec__is_text(self, secstrs); | ||
895 | case MAP__VARIABLE: | ||
896 | return elf_sec__is_data(self, secstrs); | ||
897 | default: | ||
898 | return false; | ||
899 | } | ||
900 | } | ||
901 | |||
902 | static int dso__load_sym(struct dso *self, struct map *map, const char *name, | ||
903 | int fd, symbol_filter_t filter, int kmodule) | ||
904 | { | ||
905 | struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; | ||
906 | struct map *curr_map = map; | ||
907 | struct dso *curr_dso = self; | ||
524 | Elf_Data *symstrs, *secstrs; | 908 | Elf_Data *symstrs, *secstrs; |
525 | uint32_t nr_syms; | 909 | uint32_t nr_syms; |
526 | int err = -1; | 910 | int err = -1; |
@@ -531,19 +915,16 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
531 | GElf_Sym sym; | 915 | GElf_Sym sym; |
532 | Elf_Scn *sec, *sec_strndx; | 916 | Elf_Scn *sec, *sec_strndx; |
533 | Elf *elf; | 917 | Elf *elf; |
534 | int nr = 0, kernel = !strcmp("[kernel]", self->name); | 918 | int nr = 0; |
535 | 919 | ||
536 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 920 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
537 | if (elf == NULL) { | 921 | if (elf == NULL) { |
538 | if (v) | 922 | pr_err("%s: cannot read %s ELF file.\n", __func__, name); |
539 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
540 | __func__, name); | ||
541 | goto out_close; | 923 | goto out_close; |
542 | } | 924 | } |
543 | 925 | ||
544 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 926 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
545 | if (v) | 927 | pr_err("%s: cannot get elf header.\n", __func__); |
546 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
547 | goto out_elf_end; | 928 | goto out_elf_end; |
548 | } | 929 | } |
549 | 930 | ||
@@ -577,7 +958,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
577 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 958 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
578 | 959 | ||
579 | memset(&sym, 0, sizeof(sym)); | 960 | memset(&sym, 0, sizeof(sym)); |
580 | if (!kernel) { | 961 | if (!self->kernel) { |
581 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | 962 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || |
582 | elf_section_by_name(elf, &ehdr, &shdr, | 963 | elf_section_by_name(elf, &ehdr, &shdr, |
583 | ".gnu.prelink_undo", | 964 | ".gnu.prelink_undo", |
@@ -586,14 +967,16 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
586 | 967 | ||
587 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | 968 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { |
588 | struct symbol *f; | 969 | struct symbol *f; |
589 | const char *elf_name; | 970 | const char *elf_name = elf_sym__name(&sym, symstrs); |
590 | char *demangled; | 971 | char *demangled = NULL; |
591 | u64 obj_start; | ||
592 | struct section *section = NULL; | ||
593 | int is_label = elf_sym__is_label(&sym); | 972 | int is_label = elf_sym__is_label(&sym); |
594 | const char *section_name; | 973 | const char *section_name; |
595 | 974 | ||
596 | if (!is_label && !elf_sym__is_function(&sym)) | 975 | if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && |
976 | strcmp(elf_name, kmap->ref_reloc_sym->name) == 0) | ||
977 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; | ||
978 | |||
979 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | ||
597 | continue; | 980 | continue; |
598 | 981 | ||
599 | sec = elf_getscn(elf, sym.st_shndx); | 982 | sec = elf_getscn(elf, sym.st_shndx); |
@@ -602,55 +985,98 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
602 | 985 | ||
603 | gelf_getshdr(sec, &shdr); | 986 | gelf_getshdr(sec, &shdr); |
604 | 987 | ||
605 | if (is_label && !elf_sec__is_text(&shdr, secstrs)) | 988 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) |
606 | continue; | 989 | continue; |
607 | 990 | ||
608 | section_name = elf_sec__name(&shdr, secstrs); | 991 | section_name = elf_sec__name(&shdr, secstrs); |
609 | obj_start = sym.st_value; | ||
610 | 992 | ||
611 | if (self->adjust_symbols) { | 993 | if (self->kernel || kmodule) { |
612 | if (v >= 2) | 994 | char dso_name[PATH_MAX]; |
613 | printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n", | ||
614 | (u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset); | ||
615 | 995 | ||
616 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | 996 | if (strcmp(section_name, |
617 | } | 997 | (curr_dso->short_name + |
998 | self->short_name_len)) == 0) | ||
999 | goto new_symbol; | ||
618 | 1000 | ||
619 | if (mod) { | 1001 | if (strcmp(section_name, ".text") == 0) { |
620 | section = mod->sections->find_section(mod->sections, section_name); | 1002 | curr_map = map; |
621 | if (section) | 1003 | curr_dso = self; |
622 | sym.st_value += section->vma; | 1004 | goto new_symbol; |
623 | else { | ||
624 | fprintf(stderr, "dso__load_sym() module %s lookup of %s failed\n", | ||
625 | mod->name, section_name); | ||
626 | goto out_elf_end; | ||
627 | } | 1005 | } |
1006 | |||
1007 | snprintf(dso_name, sizeof(dso_name), | ||
1008 | "%s%s", self->short_name, section_name); | ||
1009 | |||
1010 | curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name); | ||
1011 | if (curr_map == NULL) { | ||
1012 | u64 start = sym.st_value; | ||
1013 | |||
1014 | if (kmodule) | ||
1015 | start += map->start + shdr.sh_offset; | ||
1016 | |||
1017 | curr_dso = dso__new(dso_name); | ||
1018 | if (curr_dso == NULL) | ||
1019 | goto out_elf_end; | ||
1020 | curr_map = map__new2(start, curr_dso, | ||
1021 | map->type); | ||
1022 | if (curr_map == NULL) { | ||
1023 | dso__delete(curr_dso); | ||
1024 | goto out_elf_end; | ||
1025 | } | ||
1026 | curr_map->map_ip = identity__map_ip; | ||
1027 | curr_map->unmap_ip = identity__map_ip; | ||
1028 | curr_dso->origin = DSO__ORIG_KERNEL; | ||
1029 | map_groups__insert(kmap->kmaps, curr_map); | ||
1030 | dsos__add(&dsos__kernel, curr_dso); | ||
1031 | dso__set_loaded(curr_dso, map->type); | ||
1032 | } else | ||
1033 | curr_dso = curr_map->dso; | ||
1034 | |||
1035 | goto new_symbol; | ||
1036 | } | ||
1037 | |||
1038 | if (curr_dso->adjust_symbols) { | ||
1039 | pr_debug4("%s: adjusting symbol: st_value: %#Lx " | ||
1040 | "sh_addr: %#Lx sh_offset: %#Lx\n", __func__, | ||
1041 | (u64)sym.st_value, (u64)shdr.sh_addr, | ||
1042 | (u64)shdr.sh_offset); | ||
1043 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | ||
628 | } | 1044 | } |
629 | /* | 1045 | /* |
630 | * We need to figure out if the object was created from C++ sources | 1046 | * We need to figure out if the object was created from C++ sources |
631 | * DWARF DW_compile_unit has this, but we don't always have access | 1047 | * DWARF DW_compile_unit has this, but we don't always have access |
632 | * to it... | 1048 | * to it... |
633 | */ | 1049 | */ |
634 | elf_name = elf_sym__name(&sym, symstrs); | ||
635 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); | 1050 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); |
636 | if (demangled != NULL) | 1051 | if (demangled != NULL) |
637 | elf_name = demangled; | 1052 | elf_name = demangled; |
638 | 1053 | new_symbol: | |
639 | f = symbol__new(sym.st_value, sym.st_size, elf_name, | 1054 | f = symbol__new(sym.st_value, sym.st_size, elf_name); |
640 | self->sym_priv_size, obj_start, v); | ||
641 | free(demangled); | 1055 | free(demangled); |
642 | if (!f) | 1056 | if (!f) |
643 | goto out_elf_end; | 1057 | goto out_elf_end; |
644 | 1058 | ||
645 | if (filter && filter(self, f)) | 1059 | if (filter && filter(curr_map, f)) |
646 | symbol__delete(f, self->sym_priv_size); | 1060 | symbol__delete(f); |
647 | else { | 1061 | else { |
648 | f->module = mod; | 1062 | symbols__insert(&curr_dso->symbols[curr_map->type], f); |
649 | dso__insert_symbol(self, f); | ||
650 | nr++; | 1063 | nr++; |
651 | } | 1064 | } |
652 | } | 1065 | } |
653 | 1066 | ||
1067 | /* | ||
1068 | * For misannotated, zeroed, ASM function sizes. | ||
1069 | */ | ||
1070 | if (nr > 0) { | ||
1071 | symbols__fixup_end(&self->symbols[map->type]); | ||
1072 | if (kmap) { | ||
1073 | /* | ||
1074 | * We need to fixup this here too because we create new | ||
1075 | * maps here, for things like vsyscall sections. | ||
1076 | */ | ||
1077 | __map_groups__fixup_end(kmap->kmaps, map->type); | ||
1078 | } | ||
1079 | } | ||
654 | err = nr; | 1080 | err = nr; |
655 | out_elf_end: | 1081 | out_elf_end: |
656 | elf_end(elf); | 1082 | elf_end(elf); |
@@ -658,63 +1084,157 @@ out_close: | |||
658 | return err; | 1084 | return err; |
659 | } | 1085 | } |
660 | 1086 | ||
661 | #define BUILD_ID_SIZE 128 | 1087 | static bool dso__build_id_equal(const struct dso *self, u8 *build_id) |
1088 | { | ||
1089 | return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0; | ||
1090 | } | ||
662 | 1091 | ||
663 | static char *dso__read_build_id(struct dso *self, int v) | 1092 | static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) |
664 | { | 1093 | { |
665 | int i; | 1094 | bool have_build_id = false; |
1095 | struct dso *pos; | ||
1096 | |||
1097 | list_for_each_entry(pos, head, node) { | ||
1098 | if (with_hits && !pos->hit) | ||
1099 | continue; | ||
1100 | if (filename__read_build_id(pos->long_name, pos->build_id, | ||
1101 | sizeof(pos->build_id)) > 0) { | ||
1102 | have_build_id = true; | ||
1103 | pos->has_build_id = true; | ||
1104 | } | ||
1105 | } | ||
1106 | |||
1107 | return have_build_id; | ||
1108 | } | ||
1109 | |||
1110 | bool dsos__read_build_ids(bool with_hits) | ||
1111 | { | ||
1112 | bool kbuildids = __dsos__read_build_ids(&dsos__kernel, with_hits), | ||
1113 | ubuildids = __dsos__read_build_ids(&dsos__user, with_hits); | ||
1114 | return kbuildids || ubuildids; | ||
1115 | } | ||
1116 | |||
1117 | /* | ||
1118 | * Align offset to 4 bytes as needed for note name and descriptor data. | ||
1119 | */ | ||
1120 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) | ||
1121 | |||
1122 | int filename__read_build_id(const char *filename, void *bf, size_t size) | ||
1123 | { | ||
1124 | int fd, err = -1; | ||
666 | GElf_Ehdr ehdr; | 1125 | GElf_Ehdr ehdr; |
667 | GElf_Shdr shdr; | 1126 | GElf_Shdr shdr; |
668 | Elf_Data *build_id_data; | 1127 | Elf_Data *data; |
669 | Elf_Scn *sec; | 1128 | Elf_Scn *sec; |
670 | char *build_id = NULL, *bid; | 1129 | Elf_Kind ek; |
671 | unsigned char *raw; | 1130 | void *ptr; |
672 | Elf *elf; | 1131 | Elf *elf; |
673 | int fd = open(self->name, O_RDONLY); | ||
674 | 1132 | ||
1133 | if (size < BUILD_ID_SIZE) | ||
1134 | goto out; | ||
1135 | |||
1136 | fd = open(filename, O_RDONLY); | ||
675 | if (fd < 0) | 1137 | if (fd < 0) |
676 | goto out; | 1138 | goto out; |
677 | 1139 | ||
678 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 1140 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
679 | if (elf == NULL) { | 1141 | if (elf == NULL) { |
680 | if (v) | 1142 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); |
681 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
682 | __func__, self->name); | ||
683 | goto out_close; | 1143 | goto out_close; |
684 | } | 1144 | } |
685 | 1145 | ||
1146 | ek = elf_kind(elf); | ||
1147 | if (ek != ELF_K_ELF) | ||
1148 | goto out_elf_end; | ||
1149 | |||
686 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 1150 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
687 | if (v) | 1151 | pr_err("%s: cannot get elf header.\n", __func__); |
688 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
689 | goto out_elf_end; | 1152 | goto out_elf_end; |
690 | } | 1153 | } |
691 | 1154 | ||
692 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); | 1155 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
693 | if (sec == NULL) | 1156 | ".note.gnu.build-id", NULL); |
694 | goto out_elf_end; | 1157 | if (sec == NULL) { |
1158 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
1159 | ".notes", NULL); | ||
1160 | if (sec == NULL) | ||
1161 | goto out_elf_end; | ||
1162 | } | ||
695 | 1163 | ||
696 | build_id_data = elf_getdata(sec, NULL); | 1164 | data = elf_getdata(sec, NULL); |
697 | if (build_id_data == NULL) | 1165 | if (data == NULL) |
698 | goto out_elf_end; | 1166 | goto out_elf_end; |
699 | build_id = malloc(BUILD_ID_SIZE); | ||
700 | if (build_id == NULL) | ||
701 | goto out_elf_end; | ||
702 | raw = build_id_data->d_buf + 16; | ||
703 | bid = build_id; | ||
704 | 1167 | ||
705 | for (i = 0; i < 20; ++i) { | 1168 | ptr = data->d_buf; |
706 | sprintf(bid, "%02x", *raw); | 1169 | while (ptr < (data->d_buf + data->d_size)) { |
707 | ++raw; | 1170 | GElf_Nhdr *nhdr = ptr; |
708 | bid += 2; | 1171 | int namesz = NOTE_ALIGN(nhdr->n_namesz), |
1172 | descsz = NOTE_ALIGN(nhdr->n_descsz); | ||
1173 | const char *name; | ||
1174 | |||
1175 | ptr += sizeof(*nhdr); | ||
1176 | name = ptr; | ||
1177 | ptr += namesz; | ||
1178 | if (nhdr->n_type == NT_GNU_BUILD_ID && | ||
1179 | nhdr->n_namesz == sizeof("GNU")) { | ||
1180 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | ||
1181 | memcpy(bf, ptr, BUILD_ID_SIZE); | ||
1182 | err = BUILD_ID_SIZE; | ||
1183 | break; | ||
1184 | } | ||
1185 | } | ||
1186 | ptr += descsz; | ||
709 | } | 1187 | } |
710 | if (v >= 2) | ||
711 | printf("%s(%s): %s\n", __func__, self->name, build_id); | ||
712 | out_elf_end: | 1188 | out_elf_end: |
713 | elf_end(elf); | 1189 | elf_end(elf); |
714 | out_close: | 1190 | out_close: |
715 | close(fd); | 1191 | close(fd); |
716 | out: | 1192 | out: |
717 | return build_id; | 1193 | return err; |
1194 | } | ||
1195 | |||
1196 | int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | ||
1197 | { | ||
1198 | int fd, err = -1; | ||
1199 | |||
1200 | if (size < BUILD_ID_SIZE) | ||
1201 | goto out; | ||
1202 | |||
1203 | fd = open(filename, O_RDONLY); | ||
1204 | if (fd < 0) | ||
1205 | goto out; | ||
1206 | |||
1207 | while (1) { | ||
1208 | char bf[BUFSIZ]; | ||
1209 | GElf_Nhdr nhdr; | ||
1210 | int namesz, descsz; | ||
1211 | |||
1212 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) | ||
1213 | break; | ||
1214 | |||
1215 | namesz = NOTE_ALIGN(nhdr.n_namesz); | ||
1216 | descsz = NOTE_ALIGN(nhdr.n_descsz); | ||
1217 | if (nhdr.n_type == NT_GNU_BUILD_ID && | ||
1218 | nhdr.n_namesz == sizeof("GNU")) { | ||
1219 | if (read(fd, bf, namesz) != namesz) | ||
1220 | break; | ||
1221 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { | ||
1222 | if (read(fd, build_id, | ||
1223 | BUILD_ID_SIZE) == BUILD_ID_SIZE) { | ||
1224 | err = 0; | ||
1225 | break; | ||
1226 | } | ||
1227 | } else if (read(fd, bf, descsz) != descsz) | ||
1228 | break; | ||
1229 | } else { | ||
1230 | int n = namesz + descsz; | ||
1231 | if (read(fd, bf, n) != n) | ||
1232 | break; | ||
1233 | } | ||
1234 | } | ||
1235 | close(fd); | ||
1236 | out: | ||
1237 | return err; | ||
718 | } | 1238 | } |
719 | 1239 | ||
720 | char dso__symtab_origin(const struct dso *self) | 1240 | char dso__symtab_origin(const struct dso *self) |
@@ -722,10 +1242,12 @@ char dso__symtab_origin(const struct dso *self) | |||
722 | static const char origin[] = { | 1242 | static const char origin[] = { |
723 | [DSO__ORIG_KERNEL] = 'k', | 1243 | [DSO__ORIG_KERNEL] = 'k', |
724 | [DSO__ORIG_JAVA_JIT] = 'j', | 1244 | [DSO__ORIG_JAVA_JIT] = 'j', |
1245 | [DSO__ORIG_BUILD_ID_CACHE] = 'B', | ||
725 | [DSO__ORIG_FEDORA] = 'f', | 1246 | [DSO__ORIG_FEDORA] = 'f', |
726 | [DSO__ORIG_UBUNTU] = 'u', | 1247 | [DSO__ORIG_UBUNTU] = 'u', |
727 | [DSO__ORIG_BUILDID] = 'b', | 1248 | [DSO__ORIG_BUILDID] = 'b', |
728 | [DSO__ORIG_DSO] = 'd', | 1249 | [DSO__ORIG_DSO] = 'd', |
1250 | [DSO__ORIG_KMODULE] = 'K', | ||
729 | }; | 1251 | }; |
730 | 1252 | ||
731 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | 1253 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) |
@@ -733,60 +1255,90 @@ char dso__symtab_origin(const struct dso *self) | |||
733 | return origin[self->origin]; | 1255 | return origin[self->origin]; |
734 | } | 1256 | } |
735 | 1257 | ||
736 | int dso__load(struct dso *self, symbol_filter_t filter, int v) | 1258 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) |
737 | { | 1259 | { |
738 | int size = PATH_MAX; | 1260 | int size = PATH_MAX; |
739 | char *name = malloc(size), *build_id = NULL; | 1261 | char *name; |
1262 | u8 build_id[BUILD_ID_SIZE]; | ||
1263 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
740 | int ret = -1; | 1264 | int ret = -1; |
741 | int fd; | 1265 | int fd; |
742 | 1266 | ||
1267 | dso__set_loaded(self, map->type); | ||
1268 | |||
1269 | if (self->kernel) | ||
1270 | return dso__load_kernel_sym(self, map, filter); | ||
1271 | |||
1272 | name = malloc(size); | ||
743 | if (!name) | 1273 | if (!name) |
744 | return -1; | 1274 | return -1; |
745 | 1275 | ||
746 | self->adjust_symbols = 0; | 1276 | self->adjust_symbols = 0; |
747 | 1277 | ||
748 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { | 1278 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { |
749 | ret = dso__load_perf_map(self, filter, v); | 1279 | ret = dso__load_perf_map(self, map, filter); |
750 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | 1280 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : |
751 | DSO__ORIG_NOT_FOUND; | 1281 | DSO__ORIG_NOT_FOUND; |
752 | return ret; | 1282 | return ret; |
753 | } | 1283 | } |
754 | 1284 | ||
755 | self->origin = DSO__ORIG_FEDORA - 1; | 1285 | self->origin = DSO__ORIG_BUILD_ID_CACHE; |
756 | 1286 | ||
1287 | if (self->has_build_id) { | ||
1288 | build_id__sprintf(self->build_id, sizeof(self->build_id), | ||
1289 | build_id_hex); | ||
1290 | snprintf(name, size, "%s/%s/.build-id/%.2s/%s", | ||
1291 | getenv("HOME"), DEBUG_CACHE_DIR, | ||
1292 | build_id_hex, build_id_hex + 2); | ||
1293 | goto open_file; | ||
1294 | } | ||
757 | more: | 1295 | more: |
758 | do { | 1296 | do { |
759 | self->origin++; | 1297 | self->origin++; |
760 | switch (self->origin) { | 1298 | switch (self->origin) { |
761 | case DSO__ORIG_FEDORA: | 1299 | case DSO__ORIG_FEDORA: |
762 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); | 1300 | snprintf(name, size, "/usr/lib/debug%s.debug", |
1301 | self->long_name); | ||
763 | break; | 1302 | break; |
764 | case DSO__ORIG_UBUNTU: | 1303 | case DSO__ORIG_UBUNTU: |
765 | snprintf(name, size, "/usr/lib/debug%s", self->name); | 1304 | snprintf(name, size, "/usr/lib/debug%s", |
1305 | self->long_name); | ||
766 | break; | 1306 | break; |
767 | case DSO__ORIG_BUILDID: | 1307 | case DSO__ORIG_BUILDID: |
768 | build_id = dso__read_build_id(self, v); | 1308 | if (filename__read_build_id(self->long_name, build_id, |
769 | if (build_id != NULL) { | 1309 | sizeof(build_id))) { |
1310 | build_id__sprintf(build_id, sizeof(build_id), | ||
1311 | build_id_hex); | ||
770 | snprintf(name, size, | 1312 | snprintf(name, size, |
771 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | 1313 | "/usr/lib/debug/.build-id/%.2s/%s.debug", |
772 | build_id, build_id + 2); | 1314 | build_id_hex, build_id_hex + 2); |
773 | free(build_id); | 1315 | if (self->has_build_id) |
1316 | goto compare_build_id; | ||
774 | break; | 1317 | break; |
775 | } | 1318 | } |
776 | self->origin++; | 1319 | self->origin++; |
777 | /* Fall thru */ | 1320 | /* Fall thru */ |
778 | case DSO__ORIG_DSO: | 1321 | case DSO__ORIG_DSO: |
779 | snprintf(name, size, "%s", self->name); | 1322 | snprintf(name, size, "%s", self->long_name); |
780 | break; | 1323 | break; |
781 | 1324 | ||
782 | default: | 1325 | default: |
783 | goto out; | 1326 | goto out; |
784 | } | 1327 | } |
785 | 1328 | ||
1329 | if (self->has_build_id) { | ||
1330 | if (filename__read_build_id(name, build_id, | ||
1331 | sizeof(build_id)) < 0) | ||
1332 | goto more; | ||
1333 | compare_build_id: | ||
1334 | if (!dso__build_id_equal(self, build_id)) | ||
1335 | goto more; | ||
1336 | } | ||
1337 | open_file: | ||
786 | fd = open(name, O_RDONLY); | 1338 | fd = open(name, O_RDONLY); |
787 | } while (fd < 0); | 1339 | } while (fd < 0); |
788 | 1340 | ||
789 | ret = dso__load_sym(self, fd, name, filter, v, NULL); | 1341 | ret = dso__load_sym(self, map, name, fd, filter, 0); |
790 | close(fd); | 1342 | close(fd); |
791 | 1343 | ||
792 | /* | 1344 | /* |
@@ -796,7 +1348,7 @@ more: | |||
796 | goto more; | 1348 | goto more; |
797 | 1349 | ||
798 | if (ret > 0) { | 1350 | if (ret > 0) { |
799 | int nr_plt = dso__synthesize_plt_symbols(self, v); | 1351 | int nr_plt = dso__synthesize_plt_symbols(self, map, filter); |
800 | if (nr_plt > 0) | 1352 | if (nr_plt > 0) |
801 | ret += nr_plt; | 1353 | ret += nr_plt; |
802 | } | 1354 | } |
@@ -807,231 +1359,608 @@ out: | |||
807 | return ret; | 1359 | return ret; |
808 | } | 1360 | } |
809 | 1361 | ||
810 | static int dso__load_module(struct dso *self, struct mod_dso *mods, const char *name, | 1362 | struct map *map_groups__find_by_name(struct map_groups *self, |
811 | symbol_filter_t filter, int v) | 1363 | enum map_type type, const char *name) |
812 | { | 1364 | { |
813 | struct module *mod = mod_dso__find_module(mods, name); | 1365 | struct rb_node *nd; |
814 | int err = 0, fd; | ||
815 | 1366 | ||
816 | if (mod == NULL || !mod->active) | 1367 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { |
817 | return err; | 1368 | struct map *map = rb_entry(nd, struct map, rb_node); |
818 | 1369 | ||
819 | fd = open(mod->path, O_RDONLY); | 1370 | if (map->dso && strcmp(map->dso->short_name, name) == 0) |
1371 | return map; | ||
1372 | } | ||
820 | 1373 | ||
821 | if (fd < 0) | 1374 | return NULL; |
822 | return err; | 1375 | } |
823 | 1376 | ||
824 | err = dso__load_sym(self, fd, name, filter, v, mod); | 1377 | static int dso__kernel_module_get_build_id(struct dso *self) |
825 | close(fd); | 1378 | { |
1379 | char filename[PATH_MAX]; | ||
1380 | /* | ||
1381 | * kernel module short names are of the form "[module]" and | ||
1382 | * we need just "module" here. | ||
1383 | */ | ||
1384 | const char *name = self->short_name + 1; | ||
826 | 1385 | ||
827 | return err; | 1386 | snprintf(filename, sizeof(filename), |
1387 | "/sys/module/%.*s/notes/.note.gnu.build-id", | ||
1388 | (int)strlen(name - 1), name); | ||
1389 | |||
1390 | if (sysfs__read_build_id(filename, self->build_id, | ||
1391 | sizeof(self->build_id)) == 0) | ||
1392 | self->has_build_id = true; | ||
1393 | |||
1394 | return 0; | ||
828 | } | 1395 | } |
829 | 1396 | ||
830 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int v) | 1397 | static int map_groups__set_modules_path_dir(struct map_groups *self, char *dirname) |
831 | { | 1398 | { |
832 | struct mod_dso *mods = mod_dso__new_dso("modules"); | 1399 | struct dirent *dent; |
833 | struct module *pos; | 1400 | DIR *dir = opendir(dirname); |
834 | struct rb_node *next; | ||
835 | int err, count = 0; | ||
836 | 1401 | ||
837 | err = mod_dso__load_modules(mods); | 1402 | if (!dir) { |
1403 | pr_debug("%s: cannot open %s dir\n", __func__, dirname); | ||
1404 | return -1; | ||
1405 | } | ||
838 | 1406 | ||
839 | if (err <= 0) | 1407 | while ((dent = readdir(dir)) != NULL) { |
840 | return err; | 1408 | char path[PATH_MAX]; |
1409 | |||
1410 | if (dent->d_type == DT_DIR) { | ||
1411 | if (!strcmp(dent->d_name, ".") || | ||
1412 | !strcmp(dent->d_name, "..")) | ||
1413 | continue; | ||
1414 | |||
1415 | snprintf(path, sizeof(path), "%s/%s", | ||
1416 | dirname, dent->d_name); | ||
1417 | if (map_groups__set_modules_path_dir(self, path) < 0) | ||
1418 | goto failure; | ||
1419 | } else { | ||
1420 | char *dot = strrchr(dent->d_name, '.'), | ||
1421 | dso_name[PATH_MAX]; | ||
1422 | struct map *map; | ||
1423 | char *long_name; | ||
1424 | |||
1425 | if (dot == NULL || strcmp(dot, ".ko")) | ||
1426 | continue; | ||
1427 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | ||
1428 | (int)(dot - dent->d_name), dent->d_name); | ||
1429 | |||
1430 | strxfrchar(dso_name, '-', '_'); | ||
1431 | map = map_groups__find_by_name(self, MAP__FUNCTION, dso_name); | ||
1432 | if (map == NULL) | ||
1433 | continue; | ||
1434 | |||
1435 | snprintf(path, sizeof(path), "%s/%s", | ||
1436 | dirname, dent->d_name); | ||
1437 | |||
1438 | long_name = strdup(path); | ||
1439 | if (long_name == NULL) | ||
1440 | goto failure; | ||
1441 | dso__set_long_name(map->dso, long_name); | ||
1442 | dso__kernel_module_get_build_id(map->dso); | ||
1443 | } | ||
1444 | } | ||
841 | 1445 | ||
842 | /* | 1446 | return 0; |
843 | * Iterate over modules, and load active symbols. | 1447 | failure: |
844 | */ | 1448 | closedir(dir); |
845 | next = rb_first(&mods->mods); | 1449 | return -1; |
846 | while (next) { | 1450 | } |
847 | pos = rb_entry(next, struct module, rb_node); | ||
848 | err = dso__load_module(self, mods, pos->name, filter, v); | ||
849 | 1451 | ||
850 | if (err < 0) | 1452 | static int map_groups__set_modules_path(struct map_groups *self) |
851 | break; | 1453 | { |
1454 | struct utsname uts; | ||
1455 | char modules_path[PATH_MAX]; | ||
852 | 1456 | ||
853 | next = rb_next(&pos->rb_node); | 1457 | if (uname(&uts) < 0) |
854 | count += err; | 1458 | return -1; |
855 | } | 1459 | |
1460 | snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", | ||
1461 | uts.release); | ||
1462 | |||
1463 | return map_groups__set_modules_path_dir(self, modules_path); | ||
1464 | } | ||
856 | 1465 | ||
857 | if (err < 0) { | 1466 | /* |
858 | mod_dso__delete_modules(mods); | 1467 | * Constructor variant for modules (where we know from /proc/modules where |
859 | mod_dso__delete_self(mods); | 1468 | * they are loaded) and for vmlinux, where only after we load all the |
860 | return err; | 1469 | * symbols we'll know where it starts and ends. |
1470 | */ | ||
1471 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | ||
1472 | { | ||
1473 | struct map *self = zalloc(sizeof(*self) + | ||
1474 | (dso->kernel ? sizeof(struct kmap) : 0)); | ||
1475 | if (self != NULL) { | ||
1476 | /* | ||
1477 | * ->end will be filled after we load all the symbols | ||
1478 | */ | ||
1479 | map__init(self, type, start, 0, 0, dso); | ||
861 | } | 1480 | } |
862 | 1481 | ||
863 | return count; | 1482 | return self; |
864 | } | 1483 | } |
865 | 1484 | ||
866 | static inline void dso__fill_symbol_holes(struct dso *self) | 1485 | struct map *map_groups__new_module(struct map_groups *self, u64 start, |
1486 | const char *filename) | ||
867 | { | 1487 | { |
868 | struct symbol *prev = NULL; | 1488 | struct map *map; |
869 | struct rb_node *nd; | 1489 | struct dso *dso = __dsos__findnew(&dsos__kernel, filename); |
870 | 1490 | ||
871 | for (nd = rb_last(&self->syms); nd; nd = rb_prev(nd)) { | 1491 | if (dso == NULL) |
872 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 1492 | return NULL; |
873 | 1493 | ||
874 | if (prev) { | 1494 | map = map__new2(start, dso, MAP__FUNCTION); |
875 | u64 hole = 0; | 1495 | if (map == NULL) |
876 | int alias = pos->start == prev->start; | 1496 | return NULL; |
877 | 1497 | ||
878 | if (!alias) | 1498 | dso->origin = DSO__ORIG_KMODULE; |
879 | hole = prev->start - pos->end - 1; | 1499 | map_groups__insert(self, map); |
1500 | return map; | ||
1501 | } | ||
880 | 1502 | ||
881 | if (hole || alias) { | 1503 | static int map_groups__create_modules(struct map_groups *self) |
882 | if (alias) | 1504 | { |
883 | pos->end = prev->end; | 1505 | char *line = NULL; |
884 | else if (hole) | 1506 | size_t n; |
885 | pos->end = prev->start - 1; | 1507 | FILE *file = fopen("/proc/modules", "r"); |
886 | } | 1508 | struct map *map; |
887 | } | 1509 | |
888 | prev = pos; | 1510 | if (file == NULL) |
1511 | return -1; | ||
1512 | |||
1513 | while (!feof(file)) { | ||
1514 | char name[PATH_MAX]; | ||
1515 | u64 start; | ||
1516 | char *sep; | ||
1517 | int line_len; | ||
1518 | |||
1519 | line_len = getline(&line, &n, file); | ||
1520 | if (line_len < 0) | ||
1521 | break; | ||
1522 | |||
1523 | if (!line) | ||
1524 | goto out_failure; | ||
1525 | |||
1526 | line[--line_len] = '\0'; /* \n */ | ||
1527 | |||
1528 | sep = strrchr(line, 'x'); | ||
1529 | if (sep == NULL) | ||
1530 | continue; | ||
1531 | |||
1532 | hex2u64(sep + 1, &start); | ||
1533 | |||
1534 | sep = strchr(line, ' '); | ||
1535 | if (sep == NULL) | ||
1536 | continue; | ||
1537 | |||
1538 | *sep = '\0'; | ||
1539 | |||
1540 | snprintf(name, sizeof(name), "[%s]", line); | ||
1541 | map = map_groups__new_module(self, start, name); | ||
1542 | if (map == NULL) | ||
1543 | goto out_delete_line; | ||
1544 | dso__kernel_module_get_build_id(map->dso); | ||
889 | } | 1545 | } |
1546 | |||
1547 | free(line); | ||
1548 | fclose(file); | ||
1549 | |||
1550 | return map_groups__set_modules_path(self); | ||
1551 | |||
1552 | out_delete_line: | ||
1553 | free(line); | ||
1554 | out_failure: | ||
1555 | return -1; | ||
890 | } | 1556 | } |
891 | 1557 | ||
892 | static int dso__load_vmlinux(struct dso *self, const char *vmlinux, | 1558 | static int dso__load_vmlinux(struct dso *self, struct map *map, |
893 | symbol_filter_t filter, int v) | 1559 | const char *vmlinux, symbol_filter_t filter) |
894 | { | 1560 | { |
895 | int err, fd = open(vmlinux, O_RDONLY); | 1561 | int err = -1, fd; |
1562 | |||
1563 | if (self->has_build_id) { | ||
1564 | u8 build_id[BUILD_ID_SIZE]; | ||
1565 | |||
1566 | if (filename__read_build_id(vmlinux, build_id, | ||
1567 | sizeof(build_id)) < 0) { | ||
1568 | pr_debug("No build_id in %s, ignoring it\n", vmlinux); | ||
1569 | return -1; | ||
1570 | } | ||
1571 | if (!dso__build_id_equal(self, build_id)) { | ||
1572 | char expected_build_id[BUILD_ID_SIZE * 2 + 1], | ||
1573 | vmlinux_build_id[BUILD_ID_SIZE * 2 + 1]; | ||
1574 | |||
1575 | build_id__sprintf(self->build_id, | ||
1576 | sizeof(self->build_id), | ||
1577 | expected_build_id); | ||
1578 | build_id__sprintf(build_id, sizeof(build_id), | ||
1579 | vmlinux_build_id); | ||
1580 | pr_debug("build_id in %s is %s while expected is %s, " | ||
1581 | "ignoring it\n", vmlinux, vmlinux_build_id, | ||
1582 | expected_build_id); | ||
1583 | return -1; | ||
1584 | } | ||
1585 | } | ||
896 | 1586 | ||
1587 | fd = open(vmlinux, O_RDONLY); | ||
897 | if (fd < 0) | 1588 | if (fd < 0) |
898 | return -1; | 1589 | return -1; |
899 | 1590 | ||
900 | err = dso__load_sym(self, fd, vmlinux, filter, v, NULL); | 1591 | dso__set_loaded(self, map->type); |
1592 | err = dso__load_sym(self, map, vmlinux, fd, filter, 0); | ||
1593 | close(fd); | ||
901 | 1594 | ||
902 | if (err > 0) | 1595 | if (err > 0) |
903 | dso__fill_symbol_holes(self); | 1596 | pr_debug("Using %s for symbols\n", vmlinux); |
904 | 1597 | ||
905 | close(fd); | 1598 | return err; |
1599 | } | ||
1600 | |||
1601 | int dso__load_vmlinux_path(struct dso *self, struct map *map, | ||
1602 | symbol_filter_t filter) | ||
1603 | { | ||
1604 | int i, err = 0; | ||
1605 | |||
1606 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | ||
1607 | vmlinux_path__nr_entries); | ||
1608 | |||
1609 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | ||
1610 | err = dso__load_vmlinux(self, map, vmlinux_path[i], filter); | ||
1611 | if (err > 0) { | ||
1612 | dso__set_long_name(self, strdup(vmlinux_path[i])); | ||
1613 | break; | ||
1614 | } | ||
1615 | } | ||
906 | 1616 | ||
907 | return err; | 1617 | return err; |
908 | } | 1618 | } |
909 | 1619 | ||
910 | int dso__load_kernel(struct dso *self, const char *vmlinux, | 1620 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
911 | symbol_filter_t filter, int v, int use_modules) | 1621 | symbol_filter_t filter) |
912 | { | 1622 | { |
913 | int err = -1; | 1623 | int err; |
1624 | const char *kallsyms_filename = NULL; | ||
1625 | char *kallsyms_allocated_filename = NULL; | ||
1626 | /* | ||
1627 | * Step 1: if the user specified a vmlinux filename, use it and only | ||
1628 | * it, reporting errors to the user if it cannot be used. | ||
1629 | * | ||
1630 | * For instance, try to analyse an ARM perf.data file _without_ a | ||
1631 | * build-id, or if the user specifies the wrong path to the right | ||
1632 | * vmlinux file, obviously we can't fallback to another vmlinux (a | ||
1633 | * x86_86 one, on the machine where analysis is being performed, say), | ||
1634 | * or worse, /proc/kallsyms. | ||
1635 | * | ||
1636 | * If the specified file _has_ a build-id and there is a build-id | ||
1637 | * section in the perf.data file, we will still do the expected | ||
1638 | * validation in dso__load_vmlinux and will bail out if they don't | ||
1639 | * match. | ||
1640 | */ | ||
1641 | if (symbol_conf.vmlinux_name != NULL) { | ||
1642 | err = dso__load_vmlinux(self, map, | ||
1643 | symbol_conf.vmlinux_name, filter); | ||
1644 | goto out_try_fixup; | ||
1645 | } | ||
914 | 1646 | ||
915 | if (vmlinux) { | 1647 | if (vmlinux_path != NULL) { |
916 | err = dso__load_vmlinux(self, vmlinux, filter, v); | 1648 | err = dso__load_vmlinux_path(self, map, filter); |
917 | if (err > 0 && use_modules) { | 1649 | if (err > 0) |
918 | int syms = dso__load_modules(self, filter, v); | 1650 | goto out_fixup; |
1651 | } | ||
919 | 1652 | ||
920 | if (syms < 0) { | 1653 | /* |
921 | fprintf(stderr, "dso__load_modules failed!\n"); | 1654 | * Say the kernel DSO was created when processing the build-id header table, |
922 | return syms; | 1655 | * we have a build-id, so check if it is the same as the running kernel, |
1656 | * using it if it is. | ||
1657 | */ | ||
1658 | if (self->has_build_id) { | ||
1659 | u8 kallsyms_build_id[BUILD_ID_SIZE]; | ||
1660 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1661 | |||
1662 | if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, | ||
1663 | sizeof(kallsyms_build_id)) == 0) { | ||
1664 | if (dso__build_id_equal(self, kallsyms_build_id)) { | ||
1665 | kallsyms_filename = "/proc/kallsyms"; | ||
1666 | goto do_kallsyms; | ||
923 | } | 1667 | } |
924 | err += syms; | ||
925 | } | 1668 | } |
926 | } | 1669 | /* |
1670 | * Now look if we have it on the build-id cache in | ||
1671 | * $HOME/.debug/[kernel.kallsyms]. | ||
1672 | */ | ||
1673 | build_id__sprintf(self->build_id, sizeof(self->build_id), | ||
1674 | sbuild_id); | ||
1675 | |||
1676 | if (asprintf(&kallsyms_allocated_filename, | ||
1677 | "%s/.debug/[kernel.kallsyms]/%s", | ||
1678 | getenv("HOME"), sbuild_id) == -1) { | ||
1679 | pr_err("Not enough memory for kallsyms file lookup\n"); | ||
1680 | return -1; | ||
1681 | } | ||
927 | 1682 | ||
928 | if (err <= 0) | 1683 | kallsyms_filename = kallsyms_allocated_filename; |
929 | err = dso__load_kallsyms(self, filter, v); | ||
930 | 1684 | ||
1685 | if (access(kallsyms_filename, F_OK)) { | ||
1686 | pr_err("No kallsyms or vmlinux with build-id %s " | ||
1687 | "was found\n", sbuild_id); | ||
1688 | free(kallsyms_allocated_filename); | ||
1689 | return -1; | ||
1690 | } | ||
1691 | } else { | ||
1692 | /* | ||
1693 | * Last resort, if we don't have a build-id and couldn't find | ||
1694 | * any vmlinux file, try the running kernel kallsyms table. | ||
1695 | */ | ||
1696 | kallsyms_filename = "/proc/kallsyms"; | ||
1697 | } | ||
1698 | |||
1699 | do_kallsyms: | ||
1700 | err = dso__load_kallsyms(self, kallsyms_filename, map, filter); | ||
931 | if (err > 0) | 1701 | if (err > 0) |
932 | self->origin = DSO__ORIG_KERNEL; | 1702 | pr_debug("Using %s for symbols\n", kallsyms_filename); |
1703 | free(kallsyms_allocated_filename); | ||
1704 | |||
1705 | out_try_fixup: | ||
1706 | if (err > 0) { | ||
1707 | out_fixup: | ||
1708 | if (kallsyms_filename != NULL) | ||
1709 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); | ||
1710 | map__fixup_start(map); | ||
1711 | map__fixup_end(map); | ||
1712 | } | ||
933 | 1713 | ||
934 | return err; | 1714 | return err; |
935 | } | 1715 | } |
936 | 1716 | ||
937 | LIST_HEAD(dsos); | 1717 | LIST_HEAD(dsos__user); |
938 | struct dso *kernel_dso; | 1718 | LIST_HEAD(dsos__kernel); |
939 | struct dso *vdso; | ||
940 | struct dso *hypervisor_dso; | ||
941 | |||
942 | const char *vmlinux_name = "vmlinux"; | ||
943 | int modules; | ||
944 | 1719 | ||
945 | static void dsos__add(struct dso *dso) | 1720 | static void dsos__add(struct list_head *head, struct dso *dso) |
946 | { | 1721 | { |
947 | list_add_tail(&dso->node, &dsos); | 1722 | list_add_tail(&dso->node, head); |
948 | } | 1723 | } |
949 | 1724 | ||
950 | static struct dso *dsos__find(const char *name) | 1725 | static struct dso *dsos__find(struct list_head *head, const char *name) |
951 | { | 1726 | { |
952 | struct dso *pos; | 1727 | struct dso *pos; |
953 | 1728 | ||
954 | list_for_each_entry(pos, &dsos, node) | 1729 | list_for_each_entry(pos, head, node) |
955 | if (strcmp(pos->name, name) == 0) | 1730 | if (strcmp(pos->long_name, name) == 0) |
956 | return pos; | 1731 | return pos; |
957 | return NULL; | 1732 | return NULL; |
958 | } | 1733 | } |
959 | 1734 | ||
960 | struct dso *dsos__findnew(const char *name) | 1735 | struct dso *__dsos__findnew(struct list_head *head, const char *name) |
961 | { | 1736 | { |
962 | struct dso *dso = dsos__find(name); | 1737 | struct dso *dso = dsos__find(head, name); |
963 | int nr; | ||
964 | |||
965 | if (dso) | ||
966 | return dso; | ||
967 | |||
968 | dso = dso__new(name, 0); | ||
969 | if (!dso) | ||
970 | goto out_delete_dso; | ||
971 | 1738 | ||
972 | nr = dso__load(dso, NULL, verbose); | 1739 | if (!dso) { |
973 | if (nr < 0) { | 1740 | dso = dso__new(name); |
974 | eprintf("Failed to open: %s\n", name); | 1741 | if (dso != NULL) { |
975 | goto out_delete_dso; | 1742 | dsos__add(head, dso); |
1743 | dso__set_basename(dso); | ||
1744 | } | ||
976 | } | 1745 | } |
977 | if (!nr) | ||
978 | eprintf("No symbols found in: %s, maybe install a debug package?\n", name); | ||
979 | |||
980 | dsos__add(dso); | ||
981 | 1746 | ||
982 | return dso; | 1747 | return dso; |
1748 | } | ||
983 | 1749 | ||
984 | out_delete_dso: | 1750 | static void __dsos__fprintf(struct list_head *head, FILE *fp) |
985 | dso__delete(dso); | 1751 | { |
986 | return NULL; | 1752 | struct dso *pos; |
1753 | |||
1754 | list_for_each_entry(pos, head, node) { | ||
1755 | int i; | ||
1756 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
1757 | dso__fprintf(pos, i, fp); | ||
1758 | } | ||
987 | } | 1759 | } |
988 | 1760 | ||
989 | void dsos__fprintf(FILE *fp) | 1761 | void dsos__fprintf(FILE *fp) |
990 | { | 1762 | { |
1763 | __dsos__fprintf(&dsos__kernel, fp); | ||
1764 | __dsos__fprintf(&dsos__user, fp); | ||
1765 | } | ||
1766 | |||
1767 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | ||
1768 | bool with_hits) | ||
1769 | { | ||
991 | struct dso *pos; | 1770 | struct dso *pos; |
1771 | size_t ret = 0; | ||
992 | 1772 | ||
993 | list_for_each_entry(pos, &dsos, node) | 1773 | list_for_each_entry(pos, head, node) { |
994 | dso__fprintf(pos, fp); | 1774 | if (with_hits && !pos->hit) |
1775 | continue; | ||
1776 | ret += dso__fprintf_buildid(pos, fp); | ||
1777 | ret += fprintf(fp, " %s\n", pos->long_name); | ||
1778 | } | ||
1779 | return ret; | ||
995 | } | 1780 | } |
996 | 1781 | ||
997 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) | 1782 | size_t dsos__fprintf_buildid(FILE *fp, bool with_hits) |
998 | { | 1783 | { |
999 | return dso__find_symbol(dso, ip); | 1784 | return (__dsos__fprintf_buildid(&dsos__kernel, fp, with_hits) + |
1785 | __dsos__fprintf_buildid(&dsos__user, fp, with_hits)); | ||
1000 | } | 1786 | } |
1001 | 1787 | ||
1002 | int load_kernel(void) | 1788 | struct dso *dso__new_kernel(const char *name) |
1003 | { | 1789 | { |
1004 | int err; | 1790 | struct dso *self = dso__new(name ?: "[kernel.kallsyms]"); |
1005 | 1791 | ||
1006 | kernel_dso = dso__new("[kernel]", 0); | 1792 | if (self != NULL) { |
1007 | if (!kernel_dso) | 1793 | dso__set_short_name(self, "[kernel]"); |
1008 | return -1; | 1794 | self->kernel = 1; |
1795 | } | ||
1009 | 1796 | ||
1010 | err = dso__load_kernel(kernel_dso, vmlinux_name, NULL, verbose, modules); | 1797 | return self; |
1011 | if (err <= 0) { | 1798 | } |
1012 | dso__delete(kernel_dso); | ||
1013 | kernel_dso = NULL; | ||
1014 | } else | ||
1015 | dsos__add(kernel_dso); | ||
1016 | 1799 | ||
1017 | vdso = dso__new("[vdso]", 0); | 1800 | void dso__read_running_kernel_build_id(struct dso *self) |
1018 | if (!vdso) | 1801 | { |
1019 | return -1; | 1802 | if (sysfs__read_build_id("/sys/kernel/notes", self->build_id, |
1803 | sizeof(self->build_id)) == 0) | ||
1804 | self->has_build_id = true; | ||
1805 | } | ||
1806 | |||
1807 | static struct dso *dsos__create_kernel(const char *vmlinux) | ||
1808 | { | ||
1809 | struct dso *kernel = dso__new_kernel(vmlinux); | ||
1810 | |||
1811 | if (kernel != NULL) { | ||
1812 | dso__read_running_kernel_build_id(kernel); | ||
1813 | dsos__add(&dsos__kernel, kernel); | ||
1814 | } | ||
1815 | |||
1816 | return kernel; | ||
1817 | } | ||
1818 | |||
1819 | int __map_groups__create_kernel_maps(struct map_groups *self, | ||
1820 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
1821 | struct dso *kernel) | ||
1822 | { | ||
1823 | enum map_type type; | ||
1824 | |||
1825 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
1826 | struct kmap *kmap; | ||
1827 | |||
1828 | vmlinux_maps[type] = map__new2(0, kernel, type); | ||
1829 | if (vmlinux_maps[type] == NULL) | ||
1830 | return -1; | ||
1831 | |||
1832 | vmlinux_maps[type]->map_ip = | ||
1833 | vmlinux_maps[type]->unmap_ip = identity__map_ip; | ||
1834 | |||
1835 | kmap = map__kmap(vmlinux_maps[type]); | ||
1836 | kmap->kmaps = self; | ||
1837 | map_groups__insert(self, vmlinux_maps[type]); | ||
1838 | } | ||
1839 | |||
1840 | return 0; | ||
1841 | } | ||
1842 | |||
1843 | static void vmlinux_path__exit(void) | ||
1844 | { | ||
1845 | while (--vmlinux_path__nr_entries >= 0) { | ||
1846 | free(vmlinux_path[vmlinux_path__nr_entries]); | ||
1847 | vmlinux_path[vmlinux_path__nr_entries] = NULL; | ||
1848 | } | ||
1020 | 1849 | ||
1021 | vdso->find_symbol = vdso__find_symbol; | 1850 | free(vmlinux_path); |
1851 | vmlinux_path = NULL; | ||
1852 | } | ||
1022 | 1853 | ||
1023 | dsos__add(vdso); | 1854 | static int vmlinux_path__init(void) |
1855 | { | ||
1856 | struct utsname uts; | ||
1857 | char bf[PATH_MAX]; | ||
1024 | 1858 | ||
1025 | hypervisor_dso = dso__new("[hypervisor]", 0); | 1859 | if (uname(&uts) < 0) |
1026 | if (!hypervisor_dso) | ||
1027 | return -1; | 1860 | return -1; |
1028 | dsos__add(hypervisor_dso); | ||
1029 | 1861 | ||
1030 | return err; | 1862 | vmlinux_path = malloc(sizeof(char *) * 5); |
1863 | if (vmlinux_path == NULL) | ||
1864 | return -1; | ||
1865 | |||
1866 | vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux"); | ||
1867 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1868 | goto out_fail; | ||
1869 | ++vmlinux_path__nr_entries; | ||
1870 | vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux"); | ||
1871 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1872 | goto out_fail; | ||
1873 | ++vmlinux_path__nr_entries; | ||
1874 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release); | ||
1875 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
1876 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1877 | goto out_fail; | ||
1878 | ++vmlinux_path__nr_entries; | ||
1879 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release); | ||
1880 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
1881 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1882 | goto out_fail; | ||
1883 | ++vmlinux_path__nr_entries; | ||
1884 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", | ||
1885 | uts.release); | ||
1886 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
1887 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1888 | goto out_fail; | ||
1889 | ++vmlinux_path__nr_entries; | ||
1890 | |||
1891 | return 0; | ||
1892 | |||
1893 | out_fail: | ||
1894 | vmlinux_path__exit(); | ||
1895 | return -1; | ||
1031 | } | 1896 | } |
1032 | 1897 | ||
1898 | static int setup_list(struct strlist **list, const char *list_str, | ||
1899 | const char *list_name) | ||
1900 | { | ||
1901 | if (list_str == NULL) | ||
1902 | return 0; | ||
1903 | |||
1904 | *list = strlist__new(true, list_str); | ||
1905 | if (!*list) { | ||
1906 | pr_err("problems parsing %s list\n", list_name); | ||
1907 | return -1; | ||
1908 | } | ||
1909 | return 0; | ||
1910 | } | ||
1033 | 1911 | ||
1034 | void symbol__init(void) | 1912 | int symbol__init(void) |
1035 | { | 1913 | { |
1036 | elf_version(EV_CURRENT); | 1914 | elf_version(EV_CURRENT); |
1915 | if (symbol_conf.sort_by_name) | ||
1916 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - | ||
1917 | sizeof(struct symbol)); | ||
1918 | |||
1919 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) | ||
1920 | return -1; | ||
1921 | |||
1922 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { | ||
1923 | pr_err("'.' is the only non valid --field-separator argument\n"); | ||
1924 | return -1; | ||
1925 | } | ||
1926 | |||
1927 | if (setup_list(&symbol_conf.dso_list, | ||
1928 | symbol_conf.dso_list_str, "dso") < 0) | ||
1929 | return -1; | ||
1930 | |||
1931 | if (setup_list(&symbol_conf.comm_list, | ||
1932 | symbol_conf.comm_list_str, "comm") < 0) | ||
1933 | goto out_free_dso_list; | ||
1934 | |||
1935 | if (setup_list(&symbol_conf.sym_list, | ||
1936 | symbol_conf.sym_list_str, "symbol") < 0) | ||
1937 | goto out_free_comm_list; | ||
1938 | |||
1939 | return 0; | ||
1940 | |||
1941 | out_free_dso_list: | ||
1942 | strlist__delete(symbol_conf.dso_list); | ||
1943 | out_free_comm_list: | ||
1944 | strlist__delete(symbol_conf.comm_list); | ||
1945 | return -1; | ||
1946 | } | ||
1947 | |||
1948 | int map_groups__create_kernel_maps(struct map_groups *self, | ||
1949 | struct map *vmlinux_maps[MAP__NR_TYPES]) | ||
1950 | { | ||
1951 | struct dso *kernel = dsos__create_kernel(symbol_conf.vmlinux_name); | ||
1952 | |||
1953 | if (kernel == NULL) | ||
1954 | return -1; | ||
1955 | |||
1956 | if (__map_groups__create_kernel_maps(self, vmlinux_maps, kernel) < 0) | ||
1957 | return -1; | ||
1958 | |||
1959 | if (symbol_conf.use_modules && map_groups__create_modules(self) < 0) | ||
1960 | pr_debug("Problems creating module maps, continuing anyway...\n"); | ||
1961 | /* | ||
1962 | * Now that we have all the maps created, just set the ->end of them: | ||
1963 | */ | ||
1964 | map_groups__fixup_end(self); | ||
1965 | return 0; | ||
1037 | } | 1966 | } |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 829da9edba64..f30a37428919 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -1,13 +1,15 @@ | |||
1 | #ifndef _PERF_SYMBOL_ | 1 | #ifndef __PERF_SYMBOL |
2 | #define _PERF_SYMBOL_ 1 | 2 | #define __PERF_SYMBOL 1 |
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <stdbool.h> | ||
5 | #include "types.h" | 6 | #include "types.h" |
6 | #include <linux/list.h> | 7 | #include <linux/list.h> |
7 | #include <linux/rbtree.h> | 8 | #include <linux/rbtree.h> |
8 | #include "module.h" | ||
9 | #include "event.h" | 9 | #include "event.h" |
10 | 10 | ||
11 | #define DEBUG_CACHE_DIR ".debug" | ||
12 | |||
11 | #ifdef HAVE_CPLUS_DEMANGLE | 13 | #ifdef HAVE_CPLUS_DEMANGLE |
12 | extern char *cplus_demangle(const char *, int); | 14 | extern char *cplus_demangle(const char *, int); |
13 | 15 | ||
@@ -46,57 +48,125 @@ struct symbol { | |||
46 | struct rb_node rb_node; | 48 | struct rb_node rb_node; |
47 | u64 start; | 49 | u64 start; |
48 | u64 end; | 50 | u64 end; |
49 | u64 obj_start; | ||
50 | u64 hist_sum; | ||
51 | u64 *hist; | ||
52 | struct module *module; | ||
53 | void *priv; | ||
54 | char name[0]; | 51 | char name[0]; |
55 | }; | 52 | }; |
56 | 53 | ||
54 | void symbol__delete(struct symbol *self); | ||
55 | |||
56 | struct strlist; | ||
57 | |||
58 | struct symbol_conf { | ||
59 | unsigned short priv_size; | ||
60 | bool try_vmlinux_path, | ||
61 | use_modules, | ||
62 | sort_by_name, | ||
63 | show_nr_samples, | ||
64 | use_callchain, | ||
65 | exclude_other, | ||
66 | full_paths; | ||
67 | const char *vmlinux_name, | ||
68 | *field_sep; | ||
69 | char *dso_list_str, | ||
70 | *comm_list_str, | ||
71 | *sym_list_str, | ||
72 | *col_width_list_str; | ||
73 | struct strlist *dso_list, | ||
74 | *comm_list, | ||
75 | *sym_list; | ||
76 | }; | ||
77 | |||
78 | extern struct symbol_conf symbol_conf; | ||
79 | |||
80 | static inline void *symbol__priv(struct symbol *self) | ||
81 | { | ||
82 | return ((void *)self) - symbol_conf.priv_size; | ||
83 | } | ||
84 | |||
85 | struct ref_reloc_sym { | ||
86 | const char *name; | ||
87 | u64 addr; | ||
88 | u64 unrelocated_addr; | ||
89 | }; | ||
90 | |||
91 | struct addr_location { | ||
92 | struct thread *thread; | ||
93 | struct map *map; | ||
94 | struct symbol *sym; | ||
95 | u64 addr; | ||
96 | char level; | ||
97 | bool filtered; | ||
98 | }; | ||
99 | |||
57 | struct dso { | 100 | struct dso { |
58 | struct list_head node; | 101 | struct list_head node; |
59 | struct rb_root syms; | 102 | struct rb_root symbols[MAP__NR_TYPES]; |
60 | struct symbol *(*find_symbol)(struct dso *, u64 ip); | 103 | struct rb_root symbol_names[MAP__NR_TYPES]; |
61 | unsigned int sym_priv_size; | 104 | u8 adjust_symbols:1; |
62 | unsigned char adjust_symbols; | 105 | u8 slen_calculated:1; |
63 | unsigned char slen_calculated; | 106 | u8 has_build_id:1; |
107 | u8 kernel:1; | ||
108 | u8 hit:1; | ||
64 | unsigned char origin; | 109 | unsigned char origin; |
110 | u8 sorted_by_name; | ||
111 | u8 loaded; | ||
112 | u8 build_id[BUILD_ID_SIZE]; | ||
113 | const char *short_name; | ||
114 | char *long_name; | ||
115 | u16 long_name_len; | ||
116 | u16 short_name_len; | ||
65 | char name[0]; | 117 | char name[0]; |
66 | }; | 118 | }; |
67 | 119 | ||
68 | extern const char *sym_hist_filter; | 120 | struct dso *dso__new(const char *name); |
69 | 121 | struct dso *dso__new_kernel(const char *name); | |
70 | typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym); | ||
71 | |||
72 | struct dso *dso__new(const char *name, unsigned int sym_priv_size); | ||
73 | void dso__delete(struct dso *self); | 122 | void dso__delete(struct dso *self); |
74 | 123 | ||
75 | static inline void *dso__sym_priv(struct dso *self, struct symbol *sym) | 124 | bool dso__loaded(const struct dso *self, enum map_type type); |
125 | bool dso__sorted_by_name(const struct dso *self, enum map_type type); | ||
126 | |||
127 | static inline void dso__set_loaded(struct dso *self, enum map_type type) | ||
76 | { | 128 | { |
77 | return ((void *)sym) - self->sym_priv_size; | 129 | self->loaded |= (1 << type); |
78 | } | 130 | } |
79 | 131 | ||
80 | struct symbol *dso__find_symbol(struct dso *self, u64 ip); | 132 | void dso__sort_by_name(struct dso *self, enum map_type type); |
81 | 133 | ||
82 | int dso__load_kernel(struct dso *self, const char *vmlinux, | 134 | extern struct list_head dsos__user, dsos__kernel; |
83 | symbol_filter_t filter, int verbose, int modules); | ||
84 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose); | ||
85 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose); | ||
86 | struct dso *dsos__findnew(const char *name); | ||
87 | void dsos__fprintf(FILE *fp); | ||
88 | 135 | ||
89 | size_t dso__fprintf(struct dso *self, FILE *fp); | 136 | struct dso *__dsos__findnew(struct list_head *head, const char *name); |
90 | char dso__symtab_origin(const struct dso *self); | ||
91 | 137 | ||
92 | int load_kernel(void); | 138 | static inline struct dso *dsos__findnew(const char *name) |
139 | { | ||
140 | return __dsos__findnew(&dsos__user, name); | ||
141 | } | ||
93 | 142 | ||
94 | void symbol__init(void); | 143 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); |
144 | int dso__load_vmlinux_path(struct dso *self, struct map *map, | ||
145 | symbol_filter_t filter); | ||
146 | int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, | ||
147 | symbol_filter_t filter); | ||
148 | void dsos__fprintf(FILE *fp); | ||
149 | size_t dsos__fprintf_buildid(FILE *fp, bool with_hits); | ||
95 | 150 | ||
96 | extern struct list_head dsos; | 151 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); |
97 | extern struct dso *kernel_dso; | 152 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); |
98 | extern struct dso *vdso; | 153 | char dso__symtab_origin(const struct dso *self); |
99 | extern struct dso *hypervisor_dso; | 154 | void dso__set_long_name(struct dso *self, char *name); |
100 | extern const char *vmlinux_name; | 155 | void dso__set_build_id(struct dso *self, void *build_id); |
101 | extern int modules; | 156 | void dso__read_running_kernel_build_id(struct dso *self); |
102 | #endif /* _PERF_SYMBOL_ */ | 157 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); |
158 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | ||
159 | const char *name); | ||
160 | |||
161 | int filename__read_build_id(const char *filename, void *bf, size_t size); | ||
162 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); | ||
163 | bool dsos__read_build_ids(bool with_hits); | ||
164 | int build_id__sprintf(const u8 *self, int len, char *bf); | ||
165 | int kallsyms__parse(const char *filename, void *arg, | ||
166 | int (*process_symbol)(void *arg, const char *name, | ||
167 | char type, u64 start)); | ||
168 | |||
169 | int symbol__init(void); | ||
170 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | ||
171 | |||
172 | #endif /* __PERF_SYMBOL */ | ||
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 45efb5db0d19..fa968312ee7d 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -2,48 +2,151 @@ | |||
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include <stdio.h> | 3 | #include <stdio.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include "session.h" | ||
5 | #include "thread.h" | 6 | #include "thread.h" |
6 | #include "util.h" | 7 | #include "util.h" |
7 | #include "debug.h" | 8 | #include "debug.h" |
8 | 9 | ||
10 | void map_groups__init(struct map_groups *self) | ||
11 | { | ||
12 | int i; | ||
13 | for (i = 0; i < MAP__NR_TYPES; ++i) { | ||
14 | self->maps[i] = RB_ROOT; | ||
15 | INIT_LIST_HEAD(&self->removed_maps[i]); | ||
16 | } | ||
17 | } | ||
18 | |||
9 | static struct thread *thread__new(pid_t pid) | 19 | static struct thread *thread__new(pid_t pid) |
10 | { | 20 | { |
11 | struct thread *self = calloc(1, sizeof(*self)); | 21 | struct thread *self = zalloc(sizeof(*self)); |
12 | 22 | ||
13 | if (self != NULL) { | 23 | if (self != NULL) { |
24 | map_groups__init(&self->mg); | ||
14 | self->pid = pid; | 25 | self->pid = pid; |
15 | self->comm = malloc(32); | 26 | self->comm = malloc(32); |
16 | if (self->comm) | 27 | if (self->comm) |
17 | snprintf(self->comm, 32, ":%d", self->pid); | 28 | snprintf(self->comm, 32, ":%d", self->pid); |
18 | INIT_LIST_HEAD(&self->maps); | ||
19 | } | 29 | } |
20 | 30 | ||
21 | return self; | 31 | return self; |
22 | } | 32 | } |
23 | 33 | ||
34 | static void map_groups__flush(struct map_groups *self) | ||
35 | { | ||
36 | int type; | ||
37 | |||
38 | for (type = 0; type < MAP__NR_TYPES; type++) { | ||
39 | struct rb_root *root = &self->maps[type]; | ||
40 | struct rb_node *next = rb_first(root); | ||
41 | |||
42 | while (next) { | ||
43 | struct map *pos = rb_entry(next, struct map, rb_node); | ||
44 | next = rb_next(&pos->rb_node); | ||
45 | rb_erase(&pos->rb_node, root); | ||
46 | /* | ||
47 | * We may have references to this map, for | ||
48 | * instance in some hist_entry instances, so | ||
49 | * just move them to a separate list. | ||
50 | */ | ||
51 | list_add_tail(&pos->node, &self->removed_maps[pos->type]); | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | |||
24 | int thread__set_comm(struct thread *self, const char *comm) | 56 | int thread__set_comm(struct thread *self, const char *comm) |
25 | { | 57 | { |
58 | int err; | ||
59 | |||
26 | if (self->comm) | 60 | if (self->comm) |
27 | free(self->comm); | 61 | free(self->comm); |
28 | self->comm = strdup(comm); | 62 | self->comm = strdup(comm); |
29 | return self->comm ? 0 : -ENOMEM; | 63 | err = self->comm == NULL ? -ENOMEM : 0; |
64 | if (!err) { | ||
65 | self->comm_set = true; | ||
66 | map_groups__flush(&self->mg); | ||
67 | } | ||
68 | return err; | ||
30 | } | 69 | } |
31 | 70 | ||
32 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 71 | int thread__comm_len(struct thread *self) |
72 | { | ||
73 | if (!self->comm_len) { | ||
74 | if (!self->comm) | ||
75 | return 0; | ||
76 | self->comm_len = strlen(self->comm); | ||
77 | } | ||
78 | |||
79 | return self->comm_len; | ||
80 | } | ||
81 | |||
82 | size_t __map_groups__fprintf_maps(struct map_groups *self, | ||
83 | enum map_type type, FILE *fp) | ||
84 | { | ||
85 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); | ||
86 | struct rb_node *nd; | ||
87 | |||
88 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { | ||
89 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
90 | printed += fprintf(fp, "Map:"); | ||
91 | printed += map__fprintf(pos, fp); | ||
92 | if (verbose > 2) { | ||
93 | printed += dso__fprintf(pos->dso, type, fp); | ||
94 | printed += fprintf(fp, "--\n"); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | return printed; | ||
99 | } | ||
100 | |||
101 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp) | ||
102 | { | ||
103 | size_t printed = 0, i; | ||
104 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
105 | printed += __map_groups__fprintf_maps(self, i, fp); | ||
106 | return printed; | ||
107 | } | ||
108 | |||
109 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, | ||
110 | enum map_type type, FILE *fp) | ||
33 | { | 111 | { |
34 | struct map *pos; | 112 | struct map *pos; |
35 | size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); | 113 | size_t printed = 0; |
114 | |||
115 | list_for_each_entry(pos, &self->removed_maps[type], node) { | ||
116 | printed += fprintf(fp, "Map:"); | ||
117 | printed += map__fprintf(pos, fp); | ||
118 | if (verbose > 1) { | ||
119 | printed += dso__fprintf(pos->dso, type, fp); | ||
120 | printed += fprintf(fp, "--\n"); | ||
121 | } | ||
122 | } | ||
123 | return printed; | ||
124 | } | ||
36 | 125 | ||
37 | list_for_each_entry(pos, &self->maps, node) | 126 | static size_t map_groups__fprintf_removed_maps(struct map_groups *self, FILE *fp) |
38 | ret += map__fprintf(pos, fp); | 127 | { |
128 | size_t printed = 0, i; | ||
129 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
130 | printed += __map_groups__fprintf_removed_maps(self, i, fp); | ||
131 | return printed; | ||
132 | } | ||
39 | 133 | ||
40 | return ret; | 134 | static size_t map_groups__fprintf(struct map_groups *self, FILE *fp) |
135 | { | ||
136 | size_t printed = map_groups__fprintf_maps(self, fp); | ||
137 | printed += fprintf(fp, "Removed maps:\n"); | ||
138 | return printed + map_groups__fprintf_removed_maps(self, fp); | ||
41 | } | 139 | } |
42 | 140 | ||
43 | struct thread * | 141 | static size_t thread__fprintf(struct thread *self, FILE *fp) |
44 | threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match) | 142 | { |
143 | return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) + | ||
144 | map_groups__fprintf(&self->mg, fp); | ||
145 | } | ||
146 | |||
147 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) | ||
45 | { | 148 | { |
46 | struct rb_node **p = &threads->rb_node; | 149 | struct rb_node **p = &self->threads.rb_node; |
47 | struct rb_node *parent = NULL; | 150 | struct rb_node *parent = NULL; |
48 | struct thread *th; | 151 | struct thread *th; |
49 | 152 | ||
@@ -52,15 +155,15 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match) | |||
52 | * so most of the time we dont have to look up | 155 | * so most of the time we dont have to look up |
53 | * the full rbtree: | 156 | * the full rbtree: |
54 | */ | 157 | */ |
55 | if (*last_match && (*last_match)->pid == pid) | 158 | if (self->last_match && self->last_match->pid == pid) |
56 | return *last_match; | 159 | return self->last_match; |
57 | 160 | ||
58 | while (*p != NULL) { | 161 | while (*p != NULL) { |
59 | parent = *p; | 162 | parent = *p; |
60 | th = rb_entry(parent, struct thread, rb_node); | 163 | th = rb_entry(parent, struct thread, rb_node); |
61 | 164 | ||
62 | if (th->pid == pid) { | 165 | if (th->pid == pid) { |
63 | *last_match = th; | 166 | self->last_match = th; |
64 | return th; | 167 | return th; |
65 | } | 168 | } |
66 | 169 | ||
@@ -73,99 +176,159 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match) | |||
73 | th = thread__new(pid); | 176 | th = thread__new(pid); |
74 | if (th != NULL) { | 177 | if (th != NULL) { |
75 | rb_link_node(&th->rb_node, parent, p); | 178 | rb_link_node(&th->rb_node, parent, p); |
76 | rb_insert_color(&th->rb_node, threads); | 179 | rb_insert_color(&th->rb_node, &self->threads); |
77 | *last_match = th; | 180 | self->last_match = th; |
78 | } | 181 | } |
79 | 182 | ||
80 | return th; | 183 | return th; |
81 | } | 184 | } |
82 | 185 | ||
83 | struct thread * | 186 | static int map_groups__fixup_overlappings(struct map_groups *self, |
84 | register_idle_thread(struct rb_root *threads, struct thread **last_match) | 187 | struct map *map) |
85 | { | 188 | { |
86 | struct thread *thread = threads__findnew(0, threads, last_match); | 189 | struct rb_root *root = &self->maps[map->type]; |
190 | struct rb_node *next = rb_first(root); | ||
191 | |||
192 | while (next) { | ||
193 | struct map *pos = rb_entry(next, struct map, rb_node); | ||
194 | next = rb_next(&pos->rb_node); | ||
195 | |||
196 | if (!map__overlap(pos, map)) | ||
197 | continue; | ||
198 | |||
199 | if (verbose >= 2) { | ||
200 | fputs("overlapping maps:\n", stderr); | ||
201 | map__fprintf(map, stderr); | ||
202 | map__fprintf(pos, stderr); | ||
203 | } | ||
204 | |||
205 | rb_erase(&pos->rb_node, root); | ||
206 | /* | ||
207 | * We may have references to this map, for instance in some | ||
208 | * hist_entry instances, so just move them to a separate | ||
209 | * list. | ||
210 | */ | ||
211 | list_add_tail(&pos->node, &self->removed_maps[map->type]); | ||
212 | /* | ||
213 | * Now check if we need to create new maps for areas not | ||
214 | * overlapped by the new map: | ||
215 | */ | ||
216 | if (map->start > pos->start) { | ||
217 | struct map *before = map__clone(pos); | ||
218 | |||
219 | if (before == NULL) | ||
220 | return -ENOMEM; | ||
221 | |||
222 | before->end = map->start - 1; | ||
223 | map_groups__insert(self, before); | ||
224 | if (verbose >= 2) | ||
225 | map__fprintf(before, stderr); | ||
226 | } | ||
227 | |||
228 | if (map->end < pos->end) { | ||
229 | struct map *after = map__clone(pos); | ||
87 | 230 | ||
88 | if (!thread || thread__set_comm(thread, "swapper")) { | 231 | if (after == NULL) |
89 | fprintf(stderr, "problem inserting idle task.\n"); | 232 | return -ENOMEM; |
90 | exit(-1); | 233 | |
234 | after->start = map->end + 1; | ||
235 | map_groups__insert(self, after); | ||
236 | if (verbose >= 2) | ||
237 | map__fprintf(after, stderr); | ||
238 | } | ||
91 | } | 239 | } |
92 | 240 | ||
93 | return thread; | 241 | return 0; |
94 | } | 242 | } |
95 | 243 | ||
96 | void thread__insert_map(struct thread *self, struct map *map) | 244 | void maps__insert(struct rb_root *maps, struct map *map) |
97 | { | 245 | { |
98 | struct map *pos, *tmp; | 246 | struct rb_node **p = &maps->rb_node; |
99 | 247 | struct rb_node *parent = NULL; | |
100 | list_for_each_entry_safe(pos, tmp, &self->maps, node) { | 248 | const u64 ip = map->start; |
101 | if (map__overlap(pos, map)) { | 249 | struct map *m; |
102 | if (verbose >= 2) { | ||
103 | printf("overlapping maps:\n"); | ||
104 | map__fprintf(map, stdout); | ||
105 | map__fprintf(pos, stdout); | ||
106 | } | ||
107 | 250 | ||
108 | if (map->start <= pos->start && map->end > pos->start) | 251 | while (*p != NULL) { |
109 | pos->start = map->end; | 252 | parent = *p; |
253 | m = rb_entry(parent, struct map, rb_node); | ||
254 | if (ip < m->start) | ||
255 | p = &(*p)->rb_left; | ||
256 | else | ||
257 | p = &(*p)->rb_right; | ||
258 | } | ||
110 | 259 | ||
111 | if (map->end >= pos->end && map->start < pos->end) | 260 | rb_link_node(&map->rb_node, parent, p); |
112 | pos->end = map->start; | 261 | rb_insert_color(&map->rb_node, maps); |
262 | } | ||
113 | 263 | ||
114 | if (verbose >= 2) { | 264 | struct map *maps__find(struct rb_root *maps, u64 ip) |
115 | printf("after collision:\n"); | 265 | { |
116 | map__fprintf(pos, stdout); | 266 | struct rb_node **p = &maps->rb_node; |
117 | } | 267 | struct rb_node *parent = NULL; |
268 | struct map *m; | ||
118 | 269 | ||
119 | if (pos->start >= pos->end) { | 270 | while (*p != NULL) { |
120 | list_del_init(&pos->node); | 271 | parent = *p; |
121 | free(pos); | 272 | m = rb_entry(parent, struct map, rb_node); |
122 | } | 273 | if (ip < m->start) |
123 | } | 274 | p = &(*p)->rb_left; |
275 | else if (ip > m->end) | ||
276 | p = &(*p)->rb_right; | ||
277 | else | ||
278 | return m; | ||
124 | } | 279 | } |
125 | 280 | ||
126 | list_add_tail(&map->node, &self->maps); | 281 | return NULL; |
127 | } | 282 | } |
128 | 283 | ||
129 | int thread__fork(struct thread *self, struct thread *parent) | 284 | void thread__insert_map(struct thread *self, struct map *map) |
130 | { | 285 | { |
131 | struct map *map; | 286 | map_groups__fixup_overlappings(&self->mg, map); |
132 | 287 | map_groups__insert(&self->mg, map); | |
133 | if (self->comm) | 288 | } |
134 | free(self->comm); | ||
135 | self->comm = strdup(parent->comm); | ||
136 | if (!self->comm) | ||
137 | return -ENOMEM; | ||
138 | 289 | ||
139 | list_for_each_entry(map, &parent->maps, node) { | 290 | /* |
291 | * XXX This should not really _copy_ te maps, but refcount them. | ||
292 | */ | ||
293 | static int map_groups__clone(struct map_groups *self, | ||
294 | struct map_groups *parent, enum map_type type) | ||
295 | { | ||
296 | struct rb_node *nd; | ||
297 | for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { | ||
298 | struct map *map = rb_entry(nd, struct map, rb_node); | ||
140 | struct map *new = map__clone(map); | 299 | struct map *new = map__clone(map); |
141 | if (!new) | 300 | if (new == NULL) |
142 | return -ENOMEM; | 301 | return -ENOMEM; |
143 | thread__insert_map(self, new); | 302 | map_groups__insert(self, new); |
144 | } | 303 | } |
145 | |||
146 | return 0; | 304 | return 0; |
147 | } | 305 | } |
148 | 306 | ||
149 | struct map *thread__find_map(struct thread *self, u64 ip) | 307 | int thread__fork(struct thread *self, struct thread *parent) |
150 | { | 308 | { |
151 | struct map *pos; | 309 | int i; |
152 | |||
153 | if (self == NULL) | ||
154 | return NULL; | ||
155 | 310 | ||
156 | list_for_each_entry(pos, &self->maps, node) | 311 | if (parent->comm_set) { |
157 | if (ip >= pos->start && ip <= pos->end) | 312 | if (self->comm) |
158 | return pos; | 313 | free(self->comm); |
314 | self->comm = strdup(parent->comm); | ||
315 | if (!self->comm) | ||
316 | return -ENOMEM; | ||
317 | self->comm_set = true; | ||
318 | } | ||
159 | 319 | ||
160 | return NULL; | 320 | for (i = 0; i < MAP__NR_TYPES; ++i) |
321 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) | ||
322 | return -ENOMEM; | ||
323 | return 0; | ||
161 | } | 324 | } |
162 | 325 | ||
163 | size_t threads__fprintf(FILE *fp, struct rb_root *threads) | 326 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp) |
164 | { | 327 | { |
165 | size_t ret = 0; | 328 | size_t ret = 0; |
166 | struct rb_node *nd; | 329 | struct rb_node *nd; |
167 | 330 | ||
168 | for (nd = rb_first(threads); nd; nd = rb_next(nd)) { | 331 | for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) { |
169 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | 332 | struct thread *pos = rb_entry(nd, struct thread, rb_node); |
170 | 333 | ||
171 | ret += thread__fprintf(pos, fp); | 334 | ret += thread__fprintf(pos, fp); |
@@ -173,3 +336,15 @@ size_t threads__fprintf(FILE *fp, struct rb_root *threads) | |||
173 | 336 | ||
174 | return ret; | 337 | return ret; |
175 | } | 338 | } |
339 | |||
340 | struct symbol *map_groups__find_symbol(struct map_groups *self, | ||
341 | enum map_type type, u64 addr, | ||
342 | symbol_filter_t filter) | ||
343 | { | ||
344 | struct map *map = map_groups__find(self, type, addr); | ||
345 | |||
346 | if (map != NULL) | ||
347 | return map__find_symbol(map, map->map_ip(map, addr), filter); | ||
348 | |||
349 | return NULL; | ||
350 | } | ||
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 32aea3c1c2ad..dcf70303e58e 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -1,22 +1,87 @@ | |||
1 | #ifndef __PERF_THREAD_H | ||
2 | #define __PERF_THREAD_H | ||
3 | |||
1 | #include <linux/rbtree.h> | 4 | #include <linux/rbtree.h> |
2 | #include <linux/list.h> | ||
3 | #include <unistd.h> | 5 | #include <unistd.h> |
4 | #include "symbol.h" | 6 | #include "symbol.h" |
5 | 7 | ||
8 | struct map_groups { | ||
9 | struct rb_root maps[MAP__NR_TYPES]; | ||
10 | struct list_head removed_maps[MAP__NR_TYPES]; | ||
11 | }; | ||
12 | |||
13 | size_t __map_groups__fprintf_maps(struct map_groups *self, | ||
14 | enum map_type type, FILE *fp); | ||
15 | |||
6 | struct thread { | 16 | struct thread { |
7 | struct rb_node rb_node; | 17 | struct rb_node rb_node; |
8 | struct list_head maps; | 18 | struct map_groups mg; |
9 | pid_t pid; | 19 | pid_t pid; |
10 | char shortname[3]; | 20 | char shortname[3]; |
21 | bool comm_set; | ||
11 | char *comm; | 22 | char *comm; |
23 | int comm_len; | ||
12 | }; | 24 | }; |
13 | 25 | ||
26 | void map_groups__init(struct map_groups *self); | ||
14 | int thread__set_comm(struct thread *self, const char *comm); | 27 | int thread__set_comm(struct thread *self, const char *comm); |
15 | struct thread * | 28 | int thread__comm_len(struct thread *self); |
16 | threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match); | 29 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
17 | struct thread * | ||
18 | register_idle_thread(struct rb_root *threads, struct thread **last_match); | ||
19 | void thread__insert_map(struct thread *self, struct map *map); | 30 | void thread__insert_map(struct thread *self, struct map *map); |
20 | int thread__fork(struct thread *self, struct thread *parent); | 31 | int thread__fork(struct thread *self, struct thread *parent); |
21 | struct map *thread__find_map(struct thread *self, u64 ip); | 32 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); |
22 | size_t threads__fprintf(FILE *fp, struct rb_root *threads); | 33 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); |
34 | |||
35 | void maps__insert(struct rb_root *maps, struct map *map); | ||
36 | struct map *maps__find(struct rb_root *maps, u64 addr); | ||
37 | |||
38 | static inline void map_groups__insert(struct map_groups *self, struct map *map) | ||
39 | { | ||
40 | maps__insert(&self->maps[map->type], map); | ||
41 | } | ||
42 | |||
43 | static inline struct map *map_groups__find(struct map_groups *self, | ||
44 | enum map_type type, u64 addr) | ||
45 | { | ||
46 | return maps__find(&self->maps[type], addr); | ||
47 | } | ||
48 | |||
49 | static inline struct map *thread__find_map(struct thread *self, | ||
50 | enum map_type type, u64 addr) | ||
51 | { | ||
52 | return self ? map_groups__find(&self->mg, type, addr) : NULL; | ||
53 | } | ||
54 | |||
55 | void thread__find_addr_map(struct thread *self, | ||
56 | struct perf_session *session, u8 cpumode, | ||
57 | enum map_type type, u64 addr, | ||
58 | struct addr_location *al); | ||
59 | |||
60 | void thread__find_addr_location(struct thread *self, | ||
61 | struct perf_session *session, u8 cpumode, | ||
62 | enum map_type type, u64 addr, | ||
63 | struct addr_location *al, | ||
64 | symbol_filter_t filter); | ||
65 | struct symbol *map_groups__find_symbol(struct map_groups *self, | ||
66 | enum map_type type, u64 addr, | ||
67 | symbol_filter_t filter); | ||
68 | |||
69 | static inline struct symbol *map_groups__find_function(struct map_groups *self, | ||
70 | u64 addr, | ||
71 | symbol_filter_t filter) | ||
72 | { | ||
73 | return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); | ||
74 | } | ||
75 | |||
76 | struct map *map_groups__find_by_name(struct map_groups *self, | ||
77 | enum map_type type, const char *name); | ||
78 | |||
79 | int __map_groups__create_kernel_maps(struct map_groups *self, | ||
80 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
81 | struct dso *kernel); | ||
82 | int map_groups__create_kernel_maps(struct map_groups *self, | ||
83 | struct map *vmlinux_maps[MAP__NR_TYPES]); | ||
84 | |||
85 | struct map *map_groups__new_module(struct map_groups *self, u64 start, | ||
86 | const char *filename); | ||
87 | #endif /* __PERF_THREAD_H */ | ||
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index af4b0573b37f..5ea8973ad331 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -20,6 +20,7 @@ | |||
20 | */ | 20 | */ |
21 | #define _GNU_SOURCE | 21 | #define _GNU_SOURCE |
22 | #include <dirent.h> | 22 | #include <dirent.h> |
23 | #include <mntent.h> | ||
23 | #include <stdio.h> | 24 | #include <stdio.h> |
24 | #include <stdlib.h> | 25 | #include <stdlib.h> |
25 | #include <string.h> | 26 | #include <string.h> |
@@ -33,10 +34,11 @@ | |||
33 | #include <ctype.h> | 34 | #include <ctype.h> |
34 | #include <errno.h> | 35 | #include <errno.h> |
35 | #include <stdbool.h> | 36 | #include <stdbool.h> |
37 | #include <linux/kernel.h> | ||
36 | 38 | ||
37 | #include "../perf.h" | 39 | #include "../perf.h" |
38 | #include "trace-event.h" | 40 | #include "trace-event.h" |
39 | 41 | #include "debugfs.h" | |
40 | 42 | ||
41 | #define VERSION "0.5" | 43 | #define VERSION "0.5" |
42 | 44 | ||
@@ -101,32 +103,12 @@ void *malloc_or_die(unsigned int size) | |||
101 | 103 | ||
102 | static const char *find_debugfs(void) | 104 | static const char *find_debugfs(void) |
103 | { | 105 | { |
104 | static char debugfs[MAX_PATH+1]; | 106 | const char *path = debugfs_mount(NULL); |
105 | static int debugfs_found; | ||
106 | char type[100]; | ||
107 | FILE *fp; | ||
108 | |||
109 | if (debugfs_found) | ||
110 | return debugfs; | ||
111 | |||
112 | if ((fp = fopen("/proc/mounts","r")) == NULL) | ||
113 | die("Can't open /proc/mounts for read"); | ||
114 | |||
115 | while (fscanf(fp, "%*s %" | ||
116 | STR(MAX_PATH) | ||
117 | "s %99s %*s %*d %*d\n", | ||
118 | debugfs, type) == 2) { | ||
119 | if (strcmp(type, "debugfs") == 0) | ||
120 | break; | ||
121 | } | ||
122 | fclose(fp); | ||
123 | |||
124 | if (strcmp(type, "debugfs") != 0) | ||
125 | die("debugfs not mounted, please mount"); | ||
126 | 107 | ||
127 | debugfs_found = 1; | 108 | if (!path) |
109 | die("Your kernel not support debugfs filesystem"); | ||
128 | 110 | ||
129 | return debugfs; | 111 | return path; |
130 | } | 112 | } |
131 | 113 | ||
132 | /* | 114 | /* |
@@ -271,6 +253,8 @@ static void read_header_files(void) | |||
271 | write_or_die("header_page", 12); | 253 | write_or_die("header_page", 12); |
272 | write_or_die(&size, 8); | 254 | write_or_die(&size, 8); |
273 | check_size = copy_file_fd(fd); | 255 | check_size = copy_file_fd(fd); |
256 | close(fd); | ||
257 | |||
274 | if (size != check_size) | 258 | if (size != check_size) |
275 | die("wrong size for '%s' size=%lld read=%lld", | 259 | die("wrong size for '%s' size=%lld read=%lld", |
276 | path, size, check_size); | 260 | path, size, check_size); |
@@ -289,6 +273,7 @@ static void read_header_files(void) | |||
289 | if (size != check_size) | 273 | if (size != check_size) |
290 | die("wrong size for '%s'", path); | 274 | die("wrong size for '%s'", path); |
291 | put_tracing_file(path); | 275 | put_tracing_file(path); |
276 | close(fd); | ||
292 | } | 277 | } |
293 | 278 | ||
294 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | 279 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) |
@@ -317,7 +302,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
317 | die("can't read directory '%s'", sys); | 302 | die("can't read directory '%s'", sys); |
318 | 303 | ||
319 | while ((dent = readdir(dir))) { | 304 | while ((dent = readdir(dir))) { |
320 | if (strcmp(dent->d_name, ".") == 0 || | 305 | if (dent->d_type != DT_DIR || |
306 | strcmp(dent->d_name, ".") == 0 || | ||
321 | strcmp(dent->d_name, "..") == 0 || | 307 | strcmp(dent->d_name, "..") == 0 || |
322 | !name_in_tp_list(dent->d_name, tps)) | 308 | !name_in_tp_list(dent->d_name, tps)) |
323 | continue; | 309 | continue; |
@@ -334,7 +320,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
334 | 320 | ||
335 | rewinddir(dir); | 321 | rewinddir(dir); |
336 | while ((dent = readdir(dir))) { | 322 | while ((dent = readdir(dir))) { |
337 | if (strcmp(dent->d_name, ".") == 0 || | 323 | if (dent->d_type != DT_DIR || |
324 | strcmp(dent->d_name, ".") == 0 || | ||
338 | strcmp(dent->d_name, "..") == 0 || | 325 | strcmp(dent->d_name, "..") == 0 || |
339 | !name_in_tp_list(dent->d_name, tps)) | 326 | !name_in_tp_list(dent->d_name, tps)) |
340 | continue; | 327 | continue; |
@@ -353,6 +340,7 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
353 | 340 | ||
354 | free(format); | 341 | free(format); |
355 | } | 342 | } |
343 | closedir(dir); | ||
356 | } | 344 | } |
357 | 345 | ||
358 | static void read_ftrace_files(struct tracepoint_path *tps) | 346 | static void read_ftrace_files(struct tracepoint_path *tps) |
@@ -394,26 +382,21 @@ static void read_event_files(struct tracepoint_path *tps) | |||
394 | die("can't read directory '%s'", path); | 382 | die("can't read directory '%s'", path); |
395 | 383 | ||
396 | while ((dent = readdir(dir))) { | 384 | while ((dent = readdir(dir))) { |
397 | if (strcmp(dent->d_name, ".") == 0 || | 385 | if (dent->d_type != DT_DIR || |
386 | strcmp(dent->d_name, ".") == 0 || | ||
398 | strcmp(dent->d_name, "..") == 0 || | 387 | strcmp(dent->d_name, "..") == 0 || |
399 | strcmp(dent->d_name, "ftrace") == 0 || | 388 | strcmp(dent->d_name, "ftrace") == 0 || |
400 | !system_in_tp_list(dent->d_name, tps)) | 389 | !system_in_tp_list(dent->d_name, tps)) |
401 | continue; | 390 | continue; |
402 | sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); | 391 | count++; |
403 | sprintf(sys, "%s/%s", path, dent->d_name); | ||
404 | ret = stat(sys, &st); | ||
405 | free(sys); | ||
406 | if (ret < 0) | ||
407 | continue; | ||
408 | if (S_ISDIR(st.st_mode)) | ||
409 | count++; | ||
410 | } | 392 | } |
411 | 393 | ||
412 | write_or_die(&count, 4); | 394 | write_or_die(&count, 4); |
413 | 395 | ||
414 | rewinddir(dir); | 396 | rewinddir(dir); |
415 | while ((dent = readdir(dir))) { | 397 | while ((dent = readdir(dir))) { |
416 | if (strcmp(dent->d_name, ".") == 0 || | 398 | if (dent->d_type != DT_DIR || |
399 | strcmp(dent->d_name, ".") == 0 || | ||
417 | strcmp(dent->d_name, "..") == 0 || | 400 | strcmp(dent->d_name, "..") == 0 || |
418 | strcmp(dent->d_name, "ftrace") == 0 || | 401 | strcmp(dent->d_name, "ftrace") == 0 || |
419 | !system_in_tp_list(dent->d_name, tps)) | 402 | !system_in_tp_list(dent->d_name, tps)) |
@@ -422,14 +405,13 @@ static void read_event_files(struct tracepoint_path *tps) | |||
422 | sprintf(sys, "%s/%s", path, dent->d_name); | 405 | sprintf(sys, "%s/%s", path, dent->d_name); |
423 | ret = stat(sys, &st); | 406 | ret = stat(sys, &st); |
424 | if (ret >= 0) { | 407 | if (ret >= 0) { |
425 | if (S_ISDIR(st.st_mode)) { | 408 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); |
426 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); | 409 | copy_event_system(sys, tps); |
427 | copy_event_system(sys, tps); | ||
428 | } | ||
429 | } | 410 | } |
430 | free(sys); | 411 | free(sys); |
431 | } | 412 | } |
432 | 413 | ||
414 | closedir(dir); | ||
433 | put_tracing_file(path); | 415 | put_tracing_file(path); |
434 | } | 416 | } |
435 | 417 | ||
@@ -483,27 +465,33 @@ static struct tracepoint_path * | |||
483 | get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) | 465 | get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) |
484 | { | 466 | { |
485 | struct tracepoint_path path, *ppath = &path; | 467 | struct tracepoint_path path, *ppath = &path; |
486 | int i; | 468 | int i, nr_tracepoints = 0; |
487 | 469 | ||
488 | for (i = 0; i < nb_events; i++) { | 470 | for (i = 0; i < nb_events; i++) { |
489 | if (pattrs[i].type != PERF_TYPE_TRACEPOINT) | 471 | if (pattrs[i].type != PERF_TYPE_TRACEPOINT) |
490 | continue; | 472 | continue; |
473 | ++nr_tracepoints; | ||
491 | ppath->next = tracepoint_id_to_path(pattrs[i].config); | 474 | ppath->next = tracepoint_id_to_path(pattrs[i].config); |
492 | if (!ppath->next) | 475 | if (!ppath->next) |
493 | die("%s\n", "No memory to alloc tracepoints list"); | 476 | die("%s\n", "No memory to alloc tracepoints list"); |
494 | ppath = ppath->next; | 477 | ppath = ppath->next; |
495 | } | 478 | } |
496 | 479 | ||
497 | return path.next; | 480 | return nr_tracepoints > 0 ? path.next : NULL; |
498 | } | 481 | } |
499 | void read_tracing_data(struct perf_event_attr *pattrs, int nb_events) | 482 | |
483 | int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) | ||
500 | { | 484 | { |
501 | char buf[BUFSIZ]; | 485 | char buf[BUFSIZ]; |
502 | struct tracepoint_path *tps; | 486 | struct tracepoint_path *tps = get_tracepoints_path(pattrs, nb_events); |
487 | |||
488 | /* | ||
489 | * What? No tracepoints? No sense writing anything here, bail out. | ||
490 | */ | ||
491 | if (tps == NULL) | ||
492 | return -1; | ||
503 | 493 | ||
504 | output_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); | 494 | output_fd = fd; |
505 | if (output_fd < 0) | ||
506 | die("creating file '%s'", output_file); | ||
507 | 495 | ||
508 | buf[0] = 23; | 496 | buf[0] = 23; |
509 | buf[1] = 8; | 497 | buf[1] = 8; |
@@ -527,14 +515,14 @@ void read_tracing_data(struct perf_event_attr *pattrs, int nb_events) | |||
527 | write_or_die(buf, 1); | 515 | write_or_die(buf, 1); |
528 | 516 | ||
529 | /* save page_size */ | 517 | /* save page_size */ |
530 | page_size = getpagesize(); | 518 | page_size = sysconf(_SC_PAGESIZE); |
531 | write_or_die(&page_size, 4); | 519 | write_or_die(&page_size, 4); |
532 | 520 | ||
533 | tps = get_tracepoints_path(pattrs, nb_events); | ||
534 | |||
535 | read_header_files(); | 521 | read_header_files(); |
536 | read_ftrace_files(tps); | 522 | read_ftrace_files(tps); |
537 | read_event_files(tps); | 523 | read_event_files(tps); |
538 | read_proc_kallsyms(); | 524 | read_proc_kallsyms(); |
539 | read_ftrace_printk(); | 525 | read_ftrace_printk(); |
526 | |||
527 | return 0; | ||
540 | } | 528 | } |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 55c9659a56e2..613c9cc90570 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -40,12 +40,19 @@ int header_page_size_size; | |||
40 | int header_page_data_offset; | 40 | int header_page_data_offset; |
41 | int header_page_data_size; | 41 | int header_page_data_size; |
42 | 42 | ||
43 | int latency_format; | ||
44 | |||
43 | static char *input_buf; | 45 | static char *input_buf; |
44 | static unsigned long long input_buf_ptr; | 46 | static unsigned long long input_buf_ptr; |
45 | static unsigned long long input_buf_siz; | 47 | static unsigned long long input_buf_siz; |
46 | 48 | ||
47 | static int cpus; | 49 | static int cpus; |
48 | static int long_size; | 50 | static int long_size; |
51 | static int is_flag_field; | ||
52 | static int is_symbolic_field; | ||
53 | |||
54 | static struct format_field * | ||
55 | find_any_field(struct event *event, const char *name); | ||
49 | 56 | ||
50 | static void init_input_buf(char *buf, unsigned long long size) | 57 | static void init_input_buf(char *buf, unsigned long long size) |
51 | { | 58 | { |
@@ -170,7 +177,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused) | |||
170 | func_count++; | 177 | func_count++; |
171 | } | 178 | } |
172 | 179 | ||
173 | func_list = malloc_or_die(sizeof(*func_list) * func_count + 1); | 180 | func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1)); |
174 | 181 | ||
175 | i = 0; | 182 | i = 0; |
176 | while (list) { | 183 | while (list) { |
@@ -284,18 +291,19 @@ void parse_ftrace_printk(char *file, unsigned int size __unused) | |||
284 | char *line; | 291 | char *line; |
285 | char *next = NULL; | 292 | char *next = NULL; |
286 | char *addr_str; | 293 | char *addr_str; |
287 | int ret; | ||
288 | int i; | 294 | int i; |
289 | 295 | ||
290 | line = strtok_r(file, "\n", &next); | 296 | line = strtok_r(file, "\n", &next); |
291 | while (line) { | 297 | while (line) { |
298 | addr_str = strsep(&line, ":"); | ||
299 | if (!line) { | ||
300 | warning("error parsing print strings"); | ||
301 | break; | ||
302 | } | ||
292 | item = malloc_or_die(sizeof(*item)); | 303 | item = malloc_or_die(sizeof(*item)); |
293 | ret = sscanf(line, "%as : %as", | ||
294 | (float *)(void *)&addr_str, /* workaround gcc warning */ | ||
295 | (float *)(void *)&item->printk); | ||
296 | item->addr = strtoull(addr_str, NULL, 16); | 304 | item->addr = strtoull(addr_str, NULL, 16); |
297 | free(addr_str); | 305 | /* fmt still has a space, skip it */ |
298 | 306 | item->printk = strdup(line+1); | |
299 | item->next = list; | 307 | item->next = list; |
300 | list = item; | 308 | list = item; |
301 | line = strtok_r(NULL, "\n", &next); | 309 | line = strtok_r(NULL, "\n", &next); |
@@ -522,7 +530,10 @@ static enum event_type __read_token(char **tok) | |||
522 | last_ch = ch; | 530 | last_ch = ch; |
523 | ch = __read_char(); | 531 | ch = __read_char(); |
524 | buf[i++] = ch; | 532 | buf[i++] = ch; |
525 | } while (ch != quote_ch && last_ch != '\\'); | 533 | /* the '\' '\' will cancel itself */ |
534 | if (ch == '\\' && last_ch == '\\') | ||
535 | last_ch = 0; | ||
536 | } while (ch != quote_ch || last_ch == '\\'); | ||
526 | /* remove the last quote */ | 537 | /* remove the last quote */ |
527 | i--; | 538 | i--; |
528 | goto out; | 539 | goto out; |
@@ -610,7 +621,7 @@ static enum event_type read_token_item(char **tok) | |||
610 | static int test_type(enum event_type type, enum event_type expect) | 621 | static int test_type(enum event_type type, enum event_type expect) |
611 | { | 622 | { |
612 | if (type != expect) { | 623 | if (type != expect) { |
613 | die("Error: expected type %d but read %d", | 624 | warning("Error: expected type %d but read %d", |
614 | expect, type); | 625 | expect, type); |
615 | return -1; | 626 | return -1; |
616 | } | 627 | } |
@@ -621,13 +632,13 @@ static int test_type_token(enum event_type type, char *token, | |||
621 | enum event_type expect, const char *expect_tok) | 632 | enum event_type expect, const char *expect_tok) |
622 | { | 633 | { |
623 | if (type != expect) { | 634 | if (type != expect) { |
624 | die("Error: expected type %d but read %d", | 635 | warning("Error: expected type %d but read %d", |
625 | expect, type); | 636 | expect, type); |
626 | return -1; | 637 | return -1; |
627 | } | 638 | } |
628 | 639 | ||
629 | if (strcmp(token, expect_tok) != 0) { | 640 | if (strcmp(token, expect_tok) != 0) { |
630 | die("Error: expected '%s' but read '%s'", | 641 | warning("Error: expected '%s' but read '%s'", |
631 | expect_tok, token); | 642 | expect_tok, token); |
632 | return -1; | 643 | return -1; |
633 | } | 644 | } |
@@ -665,7 +676,7 @@ static int __read_expected(enum event_type expect, const char *str, int newline_ | |||
665 | 676 | ||
666 | free_token(token); | 677 | free_token(token); |
667 | 678 | ||
668 | return 0; | 679 | return ret; |
669 | } | 680 | } |
670 | 681 | ||
671 | static int read_expected(enum event_type expect, const char *str) | 682 | static int read_expected(enum event_type expect, const char *str) |
@@ -682,10 +693,10 @@ static char *event_read_name(void) | |||
682 | { | 693 | { |
683 | char *token; | 694 | char *token; |
684 | 695 | ||
685 | if (read_expected(EVENT_ITEM, (char *)"name") < 0) | 696 | if (read_expected(EVENT_ITEM, "name") < 0) |
686 | return NULL; | 697 | return NULL; |
687 | 698 | ||
688 | if (read_expected(EVENT_OP, (char *)":") < 0) | 699 | if (read_expected(EVENT_OP, ":") < 0) |
689 | return NULL; | 700 | return NULL; |
690 | 701 | ||
691 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 702 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
@@ -703,10 +714,10 @@ static int event_read_id(void) | |||
703 | char *token; | 714 | char *token; |
704 | int id; | 715 | int id; |
705 | 716 | ||
706 | if (read_expected_item(EVENT_ITEM, (char *)"ID") < 0) | 717 | if (read_expected_item(EVENT_ITEM, "ID") < 0) |
707 | return -1; | 718 | return -1; |
708 | 719 | ||
709 | if (read_expected(EVENT_OP, (char *)":") < 0) | 720 | if (read_expected(EVENT_OP, ":") < 0) |
710 | return -1; | 721 | return -1; |
711 | 722 | ||
712 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 723 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
@@ -721,6 +732,24 @@ static int event_read_id(void) | |||
721 | return -1; | 732 | return -1; |
722 | } | 733 | } |
723 | 734 | ||
735 | static int field_is_string(struct format_field *field) | ||
736 | { | ||
737 | if ((field->flags & FIELD_IS_ARRAY) && | ||
738 | (!strstr(field->type, "char") || !strstr(field->type, "u8") || | ||
739 | !strstr(field->type, "s8"))) | ||
740 | return 1; | ||
741 | |||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | static int field_is_dynamic(struct format_field *field) | ||
746 | { | ||
747 | if (!strcmp(field->type, "__data_loc")) | ||
748 | return 1; | ||
749 | |||
750 | return 0; | ||
751 | } | ||
752 | |||
724 | static int event_read_fields(struct event *event, struct format_field **fields) | 753 | static int event_read_fields(struct event *event, struct format_field **fields) |
725 | { | 754 | { |
726 | struct format_field *field = NULL; | 755 | struct format_field *field = NULL; |
@@ -738,7 +767,7 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
738 | 767 | ||
739 | count++; | 768 | count++; |
740 | 769 | ||
741 | if (test_type_token(type, token, EVENT_ITEM, (char *)"field")) | 770 | if (test_type_token(type, token, EVENT_ITEM, "field")) |
742 | goto fail; | 771 | goto fail; |
743 | free_token(token); | 772 | free_token(token); |
744 | 773 | ||
@@ -753,7 +782,7 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
753 | type = read_token(&token); | 782 | type = read_token(&token); |
754 | } | 783 | } |
755 | 784 | ||
756 | if (test_type_token(type, token, EVENT_OP, (char *)":") < 0) | 785 | if (test_type_token(type, token, EVENT_OP, ":") < 0) |
757 | return -1; | 786 | return -1; |
758 | 787 | ||
759 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 788 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
@@ -865,14 +894,20 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
865 | free(brackets); | 894 | free(brackets); |
866 | } | 895 | } |
867 | 896 | ||
868 | if (test_type_token(type, token, EVENT_OP, (char *)";")) | 897 | if (field_is_string(field)) { |
898 | field->flags |= FIELD_IS_STRING; | ||
899 | if (field_is_dynamic(field)) | ||
900 | field->flags |= FIELD_IS_DYNAMIC; | ||
901 | } | ||
902 | |||
903 | if (test_type_token(type, token, EVENT_OP, ";")) | ||
869 | goto fail; | 904 | goto fail; |
870 | free_token(token); | 905 | free_token(token); |
871 | 906 | ||
872 | if (read_expected(EVENT_ITEM, (char *)"offset") < 0) | 907 | if (read_expected(EVENT_ITEM, "offset") < 0) |
873 | goto fail_expect; | 908 | goto fail_expect; |
874 | 909 | ||
875 | if (read_expected(EVENT_OP, (char *)":") < 0) | 910 | if (read_expected(EVENT_OP, ":") < 0) |
876 | goto fail_expect; | 911 | goto fail_expect; |
877 | 912 | ||
878 | if (read_expect_type(EVENT_ITEM, &token)) | 913 | if (read_expect_type(EVENT_ITEM, &token)) |
@@ -880,13 +915,13 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
880 | field->offset = strtoul(token, NULL, 0); | 915 | field->offset = strtoul(token, NULL, 0); |
881 | free_token(token); | 916 | free_token(token); |
882 | 917 | ||
883 | if (read_expected(EVENT_OP, (char *)";") < 0) | 918 | if (read_expected(EVENT_OP, ";") < 0) |
884 | goto fail_expect; | 919 | goto fail_expect; |
885 | 920 | ||
886 | if (read_expected(EVENT_ITEM, (char *)"size") < 0) | 921 | if (read_expected(EVENT_ITEM, "size") < 0) |
887 | goto fail_expect; | 922 | goto fail_expect; |
888 | 923 | ||
889 | if (read_expected(EVENT_OP, (char *)":") < 0) | 924 | if (read_expected(EVENT_OP, ":") < 0) |
890 | goto fail_expect; | 925 | goto fail_expect; |
891 | 926 | ||
892 | if (read_expect_type(EVENT_ITEM, &token)) | 927 | if (read_expect_type(EVENT_ITEM, &token)) |
@@ -894,11 +929,34 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
894 | field->size = strtoul(token, NULL, 0); | 929 | field->size = strtoul(token, NULL, 0); |
895 | free_token(token); | 930 | free_token(token); |
896 | 931 | ||
897 | if (read_expected(EVENT_OP, (char *)";") < 0) | 932 | if (read_expected(EVENT_OP, ";") < 0) |
898 | goto fail_expect; | 933 | goto fail_expect; |
899 | 934 | ||
900 | if (read_expect_type(EVENT_NEWLINE, &token) < 0) | 935 | type = read_token(&token); |
901 | goto fail; | 936 | if (type != EVENT_NEWLINE) { |
937 | /* newer versions of the kernel have a "signed" type */ | ||
938 | if (test_type_token(type, token, EVENT_ITEM, "signed")) | ||
939 | goto fail; | ||
940 | |||
941 | free_token(token); | ||
942 | |||
943 | if (read_expected(EVENT_OP, ":") < 0) | ||
944 | goto fail_expect; | ||
945 | |||
946 | if (read_expect_type(EVENT_ITEM, &token)) | ||
947 | goto fail; | ||
948 | |||
949 | if (strtoul(token, NULL, 0)) | ||
950 | field->flags |= FIELD_IS_SIGNED; | ||
951 | |||
952 | free_token(token); | ||
953 | if (read_expected(EVENT_OP, ";") < 0) | ||
954 | goto fail_expect; | ||
955 | |||
956 | if (read_expect_type(EVENT_NEWLINE, &token)) | ||
957 | goto fail; | ||
958 | } | ||
959 | |||
902 | free_token(token); | 960 | free_token(token); |
903 | 961 | ||
904 | *fields = field; | 962 | *fields = field; |
@@ -921,10 +979,10 @@ static int event_read_format(struct event *event) | |||
921 | char *token; | 979 | char *token; |
922 | int ret; | 980 | int ret; |
923 | 981 | ||
924 | if (read_expected_item(EVENT_ITEM, (char *)"format") < 0) | 982 | if (read_expected_item(EVENT_ITEM, "format") < 0) |
925 | return -1; | 983 | return -1; |
926 | 984 | ||
927 | if (read_expected(EVENT_OP, (char *)":") < 0) | 985 | if (read_expected(EVENT_OP, ":") < 0) |
928 | return -1; | 986 | return -1; |
929 | 987 | ||
930 | if (read_expect_type(EVENT_NEWLINE, &token)) | 988 | if (read_expect_type(EVENT_NEWLINE, &token)) |
@@ -984,7 +1042,7 @@ process_cond(struct event *event, struct print_arg *top, char **tok) | |||
984 | 1042 | ||
985 | *tok = NULL; | 1043 | *tok = NULL; |
986 | type = process_arg(event, left, &token); | 1044 | type = process_arg(event, left, &token); |
987 | if (test_type_token(type, token, EVENT_OP, (char *)":")) | 1045 | if (test_type_token(type, token, EVENT_OP, ":")) |
988 | goto out_free; | 1046 | goto out_free; |
989 | 1047 | ||
990 | arg->op.op = token; | 1048 | arg->op.op = token; |
@@ -1004,6 +1062,35 @@ out_free: | |||
1004 | return EVENT_ERROR; | 1062 | return EVENT_ERROR; |
1005 | } | 1063 | } |
1006 | 1064 | ||
1065 | static enum event_type | ||
1066 | process_array(struct event *event, struct print_arg *top, char **tok) | ||
1067 | { | ||
1068 | struct print_arg *arg; | ||
1069 | enum event_type type; | ||
1070 | char *token = NULL; | ||
1071 | |||
1072 | arg = malloc_or_die(sizeof(*arg)); | ||
1073 | memset(arg, 0, sizeof(*arg)); | ||
1074 | |||
1075 | *tok = NULL; | ||
1076 | type = process_arg(event, arg, &token); | ||
1077 | if (test_type_token(type, token, EVENT_OP, "]")) | ||
1078 | goto out_free; | ||
1079 | |||
1080 | top->op.right = arg; | ||
1081 | |||
1082 | free_token(token); | ||
1083 | type = read_token_item(&token); | ||
1084 | *tok = token; | ||
1085 | |||
1086 | return type; | ||
1087 | |||
1088 | out_free: | ||
1089 | free_token(*tok); | ||
1090 | free_arg(arg); | ||
1091 | return EVENT_ERROR; | ||
1092 | } | ||
1093 | |||
1007 | static int get_op_prio(char *op) | 1094 | static int get_op_prio(char *op) |
1008 | { | 1095 | { |
1009 | if (!op[1]) { | 1096 | if (!op[1]) { |
@@ -1128,6 +1215,8 @@ process_op(struct event *event, struct print_arg *arg, char **tok) | |||
1128 | strcmp(token, "*") == 0 || | 1215 | strcmp(token, "*") == 0 || |
1129 | strcmp(token, "^") == 0 || | 1216 | strcmp(token, "^") == 0 || |
1130 | strcmp(token, "/") == 0 || | 1217 | strcmp(token, "/") == 0 || |
1218 | strcmp(token, "<") == 0 || | ||
1219 | strcmp(token, ">") == 0 || | ||
1131 | strcmp(token, "==") == 0 || | 1220 | strcmp(token, "==") == 0 || |
1132 | strcmp(token, "!=") == 0) { | 1221 | strcmp(token, "!=") == 0) { |
1133 | 1222 | ||
@@ -1144,17 +1233,46 @@ process_op(struct event *event, struct print_arg *arg, char **tok) | |||
1144 | 1233 | ||
1145 | right = malloc_or_die(sizeof(*right)); | 1234 | right = malloc_or_die(sizeof(*right)); |
1146 | 1235 | ||
1147 | type = process_arg(event, right, tok); | 1236 | type = read_token_item(&token); |
1237 | *tok = token; | ||
1238 | |||
1239 | /* could just be a type pointer */ | ||
1240 | if ((strcmp(arg->op.op, "*") == 0) && | ||
1241 | type == EVENT_DELIM && (strcmp(token, ")") == 0)) { | ||
1242 | if (left->type != PRINT_ATOM) | ||
1243 | die("bad pointer type"); | ||
1244 | left->atom.atom = realloc(left->atom.atom, | ||
1245 | sizeof(left->atom.atom) + 3); | ||
1246 | strcat(left->atom.atom, " *"); | ||
1247 | *arg = *left; | ||
1248 | free(arg); | ||
1249 | |||
1250 | return type; | ||
1251 | } | ||
1252 | |||
1253 | type = process_arg_token(event, right, tok, type); | ||
1148 | 1254 | ||
1149 | arg->op.right = right; | 1255 | arg->op.right = right; |
1150 | 1256 | ||
1257 | } else if (strcmp(token, "[") == 0) { | ||
1258 | |||
1259 | left = malloc_or_die(sizeof(*left)); | ||
1260 | *left = *arg; | ||
1261 | |||
1262 | arg->type = PRINT_OP; | ||
1263 | arg->op.op = token; | ||
1264 | arg->op.left = left; | ||
1265 | |||
1266 | arg->op.prio = 0; | ||
1267 | type = process_array(event, arg, tok); | ||
1268 | |||
1151 | } else { | 1269 | } else { |
1152 | die("unknown op '%s'", token); | 1270 | warning("unknown op '%s'", token); |
1271 | event->flags |= EVENT_FL_FAILED; | ||
1153 | /* the arg is now the left side */ | 1272 | /* the arg is now the left side */ |
1154 | return EVENT_NONE; | 1273 | return EVENT_NONE; |
1155 | } | 1274 | } |
1156 | 1275 | ||
1157 | |||
1158 | if (type == EVENT_OP) { | 1276 | if (type == EVENT_OP) { |
1159 | int prio; | 1277 | int prio; |
1160 | 1278 | ||
@@ -1178,7 +1296,7 @@ process_entry(struct event *event __unused, struct print_arg *arg, | |||
1178 | char *field; | 1296 | char *field; |
1179 | char *token; | 1297 | char *token; |
1180 | 1298 | ||
1181 | if (read_expected(EVENT_OP, (char *)"->") < 0) | 1299 | if (read_expected(EVENT_OP, "->") < 0) |
1182 | return EVENT_ERROR; | 1300 | return EVENT_ERROR; |
1183 | 1301 | ||
1184 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 1302 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
@@ -1188,6 +1306,16 @@ process_entry(struct event *event __unused, struct print_arg *arg, | |||
1188 | arg->type = PRINT_FIELD; | 1306 | arg->type = PRINT_FIELD; |
1189 | arg->field.name = field; | 1307 | arg->field.name = field; |
1190 | 1308 | ||
1309 | if (is_flag_field) { | ||
1310 | arg->field.field = find_any_field(event, arg->field.name); | ||
1311 | arg->field.field->flags |= FIELD_IS_FLAG; | ||
1312 | is_flag_field = 0; | ||
1313 | } else if (is_symbolic_field) { | ||
1314 | arg->field.field = find_any_field(event, arg->field.name); | ||
1315 | arg->field.field->flags |= FIELD_IS_SYMBOLIC; | ||
1316 | is_symbolic_field = 0; | ||
1317 | } | ||
1318 | |||
1191 | type = read_token(&token); | 1319 | type = read_token(&token); |
1192 | *tok = token; | 1320 | *tok = token; |
1193 | 1321 | ||
@@ -1338,25 +1466,25 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok) | |||
1338 | do { | 1466 | do { |
1339 | free_token(token); | 1467 | free_token(token); |
1340 | type = read_token_item(&token); | 1468 | type = read_token_item(&token); |
1341 | if (test_type_token(type, token, EVENT_OP, (char *)"{")) | 1469 | if (test_type_token(type, token, EVENT_OP, "{")) |
1342 | break; | 1470 | break; |
1343 | 1471 | ||
1344 | arg = malloc_or_die(sizeof(*arg)); | 1472 | arg = malloc_or_die(sizeof(*arg)); |
1345 | 1473 | ||
1346 | free_token(token); | 1474 | free_token(token); |
1347 | type = process_arg(event, arg, &token); | 1475 | type = process_arg(event, arg, &token); |
1348 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1476 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1349 | goto out_free; | 1477 | goto out_free; |
1350 | 1478 | ||
1351 | field = malloc_or_die(sizeof(*field)); | 1479 | field = malloc_or_die(sizeof(*field)); |
1352 | memset(field, 0, sizeof(field)); | 1480 | memset(field, 0, sizeof(*field)); |
1353 | 1481 | ||
1354 | value = arg_eval(arg); | 1482 | value = arg_eval(arg); |
1355 | field->value = strdup(value); | 1483 | field->value = strdup(value); |
1356 | 1484 | ||
1357 | free_token(token); | 1485 | free_token(token); |
1358 | type = process_arg(event, arg, &token); | 1486 | type = process_arg(event, arg, &token); |
1359 | if (test_type_token(type, token, EVENT_OP, (char *)"}")) | 1487 | if (test_type_token(type, token, EVENT_OP, "}")) |
1360 | goto out_free; | 1488 | goto out_free; |
1361 | 1489 | ||
1362 | value = arg_eval(arg); | 1490 | value = arg_eval(arg); |
@@ -1391,13 +1519,13 @@ process_flags(struct event *event, struct print_arg *arg, char **tok) | |||
1391 | memset(arg, 0, sizeof(*arg)); | 1519 | memset(arg, 0, sizeof(*arg)); |
1392 | arg->type = PRINT_FLAGS; | 1520 | arg->type = PRINT_FLAGS; |
1393 | 1521 | ||
1394 | if (read_expected_item(EVENT_DELIM, (char *)"(") < 0) | 1522 | if (read_expected_item(EVENT_DELIM, "(") < 0) |
1395 | return EVENT_ERROR; | 1523 | return EVENT_ERROR; |
1396 | 1524 | ||
1397 | field = malloc_or_die(sizeof(*field)); | 1525 | field = malloc_or_die(sizeof(*field)); |
1398 | 1526 | ||
1399 | type = process_arg(event, field, &token); | 1527 | type = process_arg(event, field, &token); |
1400 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1528 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1401 | goto out_free; | 1529 | goto out_free; |
1402 | 1530 | ||
1403 | arg->flags.field = field; | 1531 | arg->flags.field = field; |
@@ -1408,11 +1536,11 @@ process_flags(struct event *event, struct print_arg *arg, char **tok) | |||
1408 | type = read_token_item(&token); | 1536 | type = read_token_item(&token); |
1409 | } | 1537 | } |
1410 | 1538 | ||
1411 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1539 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1412 | goto out_free; | 1540 | goto out_free; |
1413 | 1541 | ||
1414 | type = process_fields(event, &arg->flags.flags, &token); | 1542 | type = process_fields(event, &arg->flags.flags, &token); |
1415 | if (test_type_token(type, token, EVENT_DELIM, (char *)")")) | 1543 | if (test_type_token(type, token, EVENT_DELIM, ")")) |
1416 | goto out_free; | 1544 | goto out_free; |
1417 | 1545 | ||
1418 | free_token(token); | 1546 | free_token(token); |
@@ -1434,19 +1562,19 @@ process_symbols(struct event *event, struct print_arg *arg, char **tok) | |||
1434 | memset(arg, 0, sizeof(*arg)); | 1562 | memset(arg, 0, sizeof(*arg)); |
1435 | arg->type = PRINT_SYMBOL; | 1563 | arg->type = PRINT_SYMBOL; |
1436 | 1564 | ||
1437 | if (read_expected_item(EVENT_DELIM, (char *)"(") < 0) | 1565 | if (read_expected_item(EVENT_DELIM, "(") < 0) |
1438 | return EVENT_ERROR; | 1566 | return EVENT_ERROR; |
1439 | 1567 | ||
1440 | field = malloc_or_die(sizeof(*field)); | 1568 | field = malloc_or_die(sizeof(*field)); |
1441 | 1569 | ||
1442 | type = process_arg(event, field, &token); | 1570 | type = process_arg(event, field, &token); |
1443 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1571 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1444 | goto out_free; | 1572 | goto out_free; |
1445 | 1573 | ||
1446 | arg->symbol.field = field; | 1574 | arg->symbol.field = field; |
1447 | 1575 | ||
1448 | type = process_fields(event, &arg->symbol.symbols, &token); | 1576 | type = process_fields(event, &arg->symbol.symbols, &token); |
1449 | if (test_type_token(type, token, EVENT_DELIM, (char *)")")) | 1577 | if (test_type_token(type, token, EVENT_DELIM, ")")) |
1450 | goto out_free; | 1578 | goto out_free; |
1451 | 1579 | ||
1452 | free_token(token); | 1580 | free_token(token); |
@@ -1463,7 +1591,6 @@ process_paren(struct event *event, struct print_arg *arg, char **tok) | |||
1463 | { | 1591 | { |
1464 | struct print_arg *item_arg; | 1592 | struct print_arg *item_arg; |
1465 | enum event_type type; | 1593 | enum event_type type; |
1466 | int ptr_cast = 0; | ||
1467 | char *token; | 1594 | char *token; |
1468 | 1595 | ||
1469 | type = process_arg(event, arg, &token); | 1596 | type = process_arg(event, arg, &token); |
@@ -1471,28 +1598,13 @@ process_paren(struct event *event, struct print_arg *arg, char **tok) | |||
1471 | if (type == EVENT_ERROR) | 1598 | if (type == EVENT_ERROR) |
1472 | return EVENT_ERROR; | 1599 | return EVENT_ERROR; |
1473 | 1600 | ||
1474 | if (type == EVENT_OP) { | 1601 | if (type == EVENT_OP) |
1475 | /* handle the ptr casts */ | 1602 | type = process_op(event, arg, &token); |
1476 | if (!strcmp(token, "*")) { | ||
1477 | /* | ||
1478 | * FIXME: should we zapp whitespaces before ')' ? | ||
1479 | * (may require a peek_token_item()) | ||
1480 | */ | ||
1481 | if (__peek_char() == ')') { | ||
1482 | ptr_cast = 1; | ||
1483 | free_token(token); | ||
1484 | type = read_token_item(&token); | ||
1485 | } | ||
1486 | } | ||
1487 | if (!ptr_cast) { | ||
1488 | type = process_op(event, arg, &token); | ||
1489 | 1603 | ||
1490 | if (type == EVENT_ERROR) | 1604 | if (type == EVENT_ERROR) |
1491 | return EVENT_ERROR; | 1605 | return EVENT_ERROR; |
1492 | } | ||
1493 | } | ||
1494 | 1606 | ||
1495 | if (test_type_token(type, token, EVENT_DELIM, (char *)")")) { | 1607 | if (test_type_token(type, token, EVENT_DELIM, ")")) { |
1496 | free_token(token); | 1608 | free_token(token); |
1497 | return EVENT_ERROR; | 1609 | return EVENT_ERROR; |
1498 | } | 1610 | } |
@@ -1516,13 +1628,6 @@ process_paren(struct event *event, struct print_arg *arg, char **tok) | |||
1516 | item_arg = malloc_or_die(sizeof(*item_arg)); | 1628 | item_arg = malloc_or_die(sizeof(*item_arg)); |
1517 | 1629 | ||
1518 | arg->type = PRINT_TYPE; | 1630 | arg->type = PRINT_TYPE; |
1519 | if (ptr_cast) { | ||
1520 | char *old = arg->atom.atom; | ||
1521 | |||
1522 | arg->atom.atom = malloc_or_die(strlen(old + 3)); | ||
1523 | sprintf(arg->atom.atom, "%s *", old); | ||
1524 | free(old); | ||
1525 | } | ||
1526 | arg->typecast.type = arg->atom.atom; | 1631 | arg->typecast.type = arg->atom.atom; |
1527 | arg->typecast.item = item_arg; | 1632 | arg->typecast.item = item_arg; |
1528 | type = process_arg_token(event, item_arg, &token, type); | 1633 | type = process_arg_token(event, item_arg, &token, type); |
@@ -1540,7 +1645,7 @@ process_str(struct event *event __unused, struct print_arg *arg, char **tok) | |||
1540 | enum event_type type; | 1645 | enum event_type type; |
1541 | char *token; | 1646 | char *token; |
1542 | 1647 | ||
1543 | if (read_expected(EVENT_DELIM, (char *)"(") < 0) | 1648 | if (read_expected(EVENT_DELIM, "(") < 0) |
1544 | return EVENT_ERROR; | 1649 | return EVENT_ERROR; |
1545 | 1650 | ||
1546 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 1651 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
@@ -1550,7 +1655,7 @@ process_str(struct event *event __unused, struct print_arg *arg, char **tok) | |||
1550 | arg->string.string = token; | 1655 | arg->string.string = token; |
1551 | arg->string.offset = -1; | 1656 | arg->string.offset = -1; |
1552 | 1657 | ||
1553 | if (read_expected(EVENT_DELIM, (char *)")") < 0) | 1658 | if (read_expected(EVENT_DELIM, ")") < 0) |
1554 | return EVENT_ERROR; | 1659 | return EVENT_ERROR; |
1555 | 1660 | ||
1556 | type = read_token(&token); | 1661 | type = read_token(&token); |
@@ -1578,9 +1683,11 @@ process_arg_token(struct event *event, struct print_arg *arg, | |||
1578 | type = process_entry(event, arg, &token); | 1683 | type = process_entry(event, arg, &token); |
1579 | } else if (strcmp(token, "__print_flags") == 0) { | 1684 | } else if (strcmp(token, "__print_flags") == 0) { |
1580 | free_token(token); | 1685 | free_token(token); |
1686 | is_flag_field = 1; | ||
1581 | type = process_flags(event, arg, &token); | 1687 | type = process_flags(event, arg, &token); |
1582 | } else if (strcmp(token, "__print_symbolic") == 0) { | 1688 | } else if (strcmp(token, "__print_symbolic") == 0) { |
1583 | free_token(token); | 1689 | free_token(token); |
1690 | is_symbolic_field = 1; | ||
1584 | type = process_symbols(event, arg, &token); | 1691 | type = process_symbols(event, arg, &token); |
1585 | } else if (strcmp(token, "__get_str") == 0) { | 1692 | } else if (strcmp(token, "__get_str") == 0) { |
1586 | free_token(token); | 1693 | free_token(token); |
@@ -1637,12 +1744,18 @@ process_arg_token(struct event *event, struct print_arg *arg, | |||
1637 | 1744 | ||
1638 | static int event_read_print_args(struct event *event, struct print_arg **list) | 1745 | static int event_read_print_args(struct event *event, struct print_arg **list) |
1639 | { | 1746 | { |
1640 | enum event_type type; | 1747 | enum event_type type = EVENT_ERROR; |
1641 | struct print_arg *arg; | 1748 | struct print_arg *arg; |
1642 | char *token; | 1749 | char *token; |
1643 | int args = 0; | 1750 | int args = 0; |
1644 | 1751 | ||
1645 | do { | 1752 | do { |
1753 | if (type == EVENT_NEWLINE) { | ||
1754 | free_token(token); | ||
1755 | type = read_token_item(&token); | ||
1756 | continue; | ||
1757 | } | ||
1758 | |||
1646 | arg = malloc_or_die(sizeof(*arg)); | 1759 | arg = malloc_or_die(sizeof(*arg)); |
1647 | memset(arg, 0, sizeof(*arg)); | 1760 | memset(arg, 0, sizeof(*arg)); |
1648 | 1761 | ||
@@ -1683,18 +1796,19 @@ static int event_read_print(struct event *event) | |||
1683 | char *token; | 1796 | char *token; |
1684 | int ret; | 1797 | int ret; |
1685 | 1798 | ||
1686 | if (read_expected_item(EVENT_ITEM, (char *)"print") < 0) | 1799 | if (read_expected_item(EVENT_ITEM, "print") < 0) |
1687 | return -1; | 1800 | return -1; |
1688 | 1801 | ||
1689 | if (read_expected(EVENT_ITEM, (char *)"fmt") < 0) | 1802 | if (read_expected(EVENT_ITEM, "fmt") < 0) |
1690 | return -1; | 1803 | return -1; |
1691 | 1804 | ||
1692 | if (read_expected(EVENT_OP, (char *)":") < 0) | 1805 | if (read_expected(EVENT_OP, ":") < 0) |
1693 | return -1; | 1806 | return -1; |
1694 | 1807 | ||
1695 | if (read_expect_type(EVENT_DQUOTE, &token) < 0) | 1808 | if (read_expect_type(EVENT_DQUOTE, &token) < 0) |
1696 | goto fail; | 1809 | goto fail; |
1697 | 1810 | ||
1811 | concat: | ||
1698 | event->print_fmt.format = token; | 1812 | event->print_fmt.format = token; |
1699 | event->print_fmt.args = NULL; | 1813 | event->print_fmt.args = NULL; |
1700 | 1814 | ||
@@ -1704,7 +1818,22 @@ static int event_read_print(struct event *event) | |||
1704 | if (type == EVENT_NONE) | 1818 | if (type == EVENT_NONE) |
1705 | return 0; | 1819 | return 0; |
1706 | 1820 | ||
1707 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1821 | /* Handle concatination of print lines */ |
1822 | if (type == EVENT_DQUOTE) { | ||
1823 | char *cat; | ||
1824 | |||
1825 | cat = malloc_or_die(strlen(event->print_fmt.format) + | ||
1826 | strlen(token) + 1); | ||
1827 | strcpy(cat, event->print_fmt.format); | ||
1828 | strcat(cat, token); | ||
1829 | free_token(token); | ||
1830 | free_token(event->print_fmt.format); | ||
1831 | event->print_fmt.format = NULL; | ||
1832 | token = cat; | ||
1833 | goto concat; | ||
1834 | } | ||
1835 | |||
1836 | if (test_type_token(type, token, EVENT_DELIM, ",")) | ||
1708 | goto fail; | 1837 | goto fail; |
1709 | 1838 | ||
1710 | free_token(token); | 1839 | free_token(token); |
@@ -1713,7 +1842,7 @@ static int event_read_print(struct event *event) | |||
1713 | if (ret < 0) | 1842 | if (ret < 0) |
1714 | return -1; | 1843 | return -1; |
1715 | 1844 | ||
1716 | return 0; | 1845 | return ret; |
1717 | 1846 | ||
1718 | fail: | 1847 | fail: |
1719 | free_token(token); | 1848 | free_token(token); |
@@ -1759,7 +1888,7 @@ find_any_field(struct event *event, const char *name) | |||
1759 | return find_field(event, name); | 1888 | return find_field(event, name); |
1760 | } | 1889 | } |
1761 | 1890 | ||
1762 | static unsigned long long read_size(void *ptr, int size) | 1891 | unsigned long long read_size(void *ptr, int size) |
1763 | { | 1892 | { |
1764 | switch (size) { | 1893 | switch (size) { |
1765 | case 1: | 1894 | case 1: |
@@ -1796,6 +1925,15 @@ void *raw_field_ptr(struct event *event, const char *name, void *data) | |||
1796 | if (!field) | 1925 | if (!field) |
1797 | return NULL; | 1926 | return NULL; |
1798 | 1927 | ||
1928 | if (field->flags & FIELD_IS_DYNAMIC) { | ||
1929 | int offset; | ||
1930 | |||
1931 | offset = *(int *)(data + field->offset); | ||
1932 | offset &= 0xffff; | ||
1933 | |||
1934 | return data + offset; | ||
1935 | } | ||
1936 | |||
1799 | return data + field->offset; | 1937 | return data + field->offset; |
1800 | } | 1938 | } |
1801 | 1939 | ||
@@ -1822,37 +1960,67 @@ static int get_common_info(const char *type, int *offset, int *size) | |||
1822 | return 0; | 1960 | return 0; |
1823 | } | 1961 | } |
1824 | 1962 | ||
1825 | int trace_parse_common_type(void *data) | 1963 | static int __parse_common(void *data, int *size, int *offset, |
1964 | const char *name) | ||
1826 | { | 1965 | { |
1827 | static int type_offset; | ||
1828 | static int type_size; | ||
1829 | int ret; | 1966 | int ret; |
1830 | 1967 | ||
1831 | if (!type_size) { | 1968 | if (!*size) { |
1832 | ret = get_common_info("common_type", | 1969 | ret = get_common_info(name, offset, size); |
1833 | &type_offset, | ||
1834 | &type_size); | ||
1835 | if (ret < 0) | 1970 | if (ret < 0) |
1836 | return ret; | 1971 | return ret; |
1837 | } | 1972 | } |
1838 | return read_size(data + type_offset, type_size); | 1973 | return read_size(data + *offset, *size); |
1839 | } | 1974 | } |
1840 | 1975 | ||
1841 | static int parse_common_pid(void *data) | 1976 | int trace_parse_common_type(void *data) |
1977 | { | ||
1978 | static int type_offset; | ||
1979 | static int type_size; | ||
1980 | |||
1981 | return __parse_common(data, &type_size, &type_offset, | ||
1982 | "common_type"); | ||
1983 | } | ||
1984 | |||
1985 | int trace_parse_common_pid(void *data) | ||
1842 | { | 1986 | { |
1843 | static int pid_offset; | 1987 | static int pid_offset; |
1844 | static int pid_size; | 1988 | static int pid_size; |
1989 | |||
1990 | return __parse_common(data, &pid_size, &pid_offset, | ||
1991 | "common_pid"); | ||
1992 | } | ||
1993 | |||
1994 | int parse_common_pc(void *data) | ||
1995 | { | ||
1996 | static int pc_offset; | ||
1997 | static int pc_size; | ||
1998 | |||
1999 | return __parse_common(data, &pc_size, &pc_offset, | ||
2000 | "common_preempt_count"); | ||
2001 | } | ||
2002 | |||
2003 | int parse_common_flags(void *data) | ||
2004 | { | ||
2005 | static int flags_offset; | ||
2006 | static int flags_size; | ||
2007 | |||
2008 | return __parse_common(data, &flags_size, &flags_offset, | ||
2009 | "common_flags"); | ||
2010 | } | ||
2011 | |||
2012 | int parse_common_lock_depth(void *data) | ||
2013 | { | ||
2014 | static int ld_offset; | ||
2015 | static int ld_size; | ||
1845 | int ret; | 2016 | int ret; |
1846 | 2017 | ||
1847 | if (!pid_size) { | 2018 | ret = __parse_common(data, &ld_size, &ld_offset, |
1848 | ret = get_common_info("common_pid", | 2019 | "common_lock_depth"); |
1849 | &pid_offset, | 2020 | if (ret < 0) |
1850 | &pid_size); | 2021 | return -1; |
1851 | if (ret < 0) | ||
1852 | return ret; | ||
1853 | } | ||
1854 | 2022 | ||
1855 | return read_size(data + pid_offset, pid_size); | 2023 | return ret; |
1856 | } | 2024 | } |
1857 | 2025 | ||
1858 | struct event *trace_find_event(int id) | 2026 | struct event *trace_find_event(int id) |
@@ -1866,11 +2034,20 @@ struct event *trace_find_event(int id) | |||
1866 | return event; | 2034 | return event; |
1867 | } | 2035 | } |
1868 | 2036 | ||
2037 | struct event *trace_find_next_event(struct event *event) | ||
2038 | { | ||
2039 | if (!event) | ||
2040 | return event_list; | ||
2041 | |||
2042 | return event->next; | ||
2043 | } | ||
2044 | |||
1869 | static unsigned long long eval_num_arg(void *data, int size, | 2045 | static unsigned long long eval_num_arg(void *data, int size, |
1870 | struct event *event, struct print_arg *arg) | 2046 | struct event *event, struct print_arg *arg) |
1871 | { | 2047 | { |
1872 | unsigned long long val = 0; | 2048 | unsigned long long val = 0; |
1873 | unsigned long long left, right; | 2049 | unsigned long long left, right; |
2050 | struct print_arg *larg; | ||
1874 | 2051 | ||
1875 | switch (arg->type) { | 2052 | switch (arg->type) { |
1876 | case PRINT_NULL: | 2053 | case PRINT_NULL: |
@@ -1897,6 +2074,26 @@ static unsigned long long eval_num_arg(void *data, int size, | |||
1897 | return 0; | 2074 | return 0; |
1898 | break; | 2075 | break; |
1899 | case PRINT_OP: | 2076 | case PRINT_OP: |
2077 | if (strcmp(arg->op.op, "[") == 0) { | ||
2078 | /* | ||
2079 | * Arrays are special, since we don't want | ||
2080 | * to read the arg as is. | ||
2081 | */ | ||
2082 | if (arg->op.left->type != PRINT_FIELD) | ||
2083 | goto default_op; /* oops, all bets off */ | ||
2084 | larg = arg->op.left; | ||
2085 | if (!larg->field.field) { | ||
2086 | larg->field.field = | ||
2087 | find_any_field(event, larg->field.name); | ||
2088 | if (!larg->field.field) | ||
2089 | die("field %s not found", larg->field.name); | ||
2090 | } | ||
2091 | right = eval_num_arg(data, size, event, arg->op.right); | ||
2092 | val = read_size(data + larg->field.field->offset + | ||
2093 | right * long_size, long_size); | ||
2094 | break; | ||
2095 | } | ||
2096 | default_op: | ||
1900 | left = eval_num_arg(data, size, event, arg->op.left); | 2097 | left = eval_num_arg(data, size, event, arg->op.left); |
1901 | right = eval_num_arg(data, size, event, arg->op.right); | 2098 | right = eval_num_arg(data, size, event, arg->op.right); |
1902 | switch (arg->op.op[0]) { | 2099 | switch (arg->op.op[0]) { |
@@ -1947,6 +2144,12 @@ static unsigned long long eval_num_arg(void *data, int size, | |||
1947 | die("unknown op '%s'", arg->op.op); | 2144 | die("unknown op '%s'", arg->op.op); |
1948 | val = left == right; | 2145 | val = left == right; |
1949 | break; | 2146 | break; |
2147 | case '-': | ||
2148 | val = left - right; | ||
2149 | break; | ||
2150 | case '+': | ||
2151 | val = left + right; | ||
2152 | break; | ||
1950 | default: | 2153 | default: |
1951 | die("unknown op '%s'", arg->op.op); | 2154 | die("unknown op '%s'", arg->op.op); |
1952 | } | 2155 | } |
@@ -1978,7 +2181,7 @@ static const struct flag flags[] = { | |||
1978 | { "HRTIMER_RESTART", 1 }, | 2181 | { "HRTIMER_RESTART", 1 }, |
1979 | }; | 2182 | }; |
1980 | 2183 | ||
1981 | static unsigned long long eval_flag(const char *flag) | 2184 | unsigned long long eval_flag(const char *flag) |
1982 | { | 2185 | { |
1983 | int i; | 2186 | int i; |
1984 | 2187 | ||
@@ -2145,8 +2348,9 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc | |||
2145 | case 'u': | 2348 | case 'u': |
2146 | case 'x': | 2349 | case 'x': |
2147 | case 'i': | 2350 | case 'i': |
2148 | bptr = (void *)(((unsigned long)bptr + (long_size - 1)) & | 2351 | /* the pointers are always 4 bytes aligned */ |
2149 | ~(long_size - 1)); | 2352 | bptr = (void *)(((unsigned long)bptr + 3) & |
2353 | ~3); | ||
2150 | switch (ls) { | 2354 | switch (ls) { |
2151 | case 0: | 2355 | case 0: |
2152 | case 1: | 2356 | case 1: |
@@ -2270,7 +2474,27 @@ static void pretty_print(void *data, int size, struct event *event) | |||
2270 | 2474 | ||
2271 | for (; *ptr; ptr++) { | 2475 | for (; *ptr; ptr++) { |
2272 | ls = 0; | 2476 | ls = 0; |
2273 | if (*ptr == '%') { | 2477 | if (*ptr == '\\') { |
2478 | ptr++; | ||
2479 | switch (*ptr) { | ||
2480 | case 'n': | ||
2481 | printf("\n"); | ||
2482 | break; | ||
2483 | case 't': | ||
2484 | printf("\t"); | ||
2485 | break; | ||
2486 | case 'r': | ||
2487 | printf("\r"); | ||
2488 | break; | ||
2489 | case '\\': | ||
2490 | printf("\\"); | ||
2491 | break; | ||
2492 | default: | ||
2493 | printf("%c", *ptr); | ||
2494 | break; | ||
2495 | } | ||
2496 | |||
2497 | } else if (*ptr == '%') { | ||
2274 | saveptr = ptr; | 2498 | saveptr = ptr; |
2275 | show_func = 0; | 2499 | show_func = 0; |
2276 | cont_process: | 2500 | cont_process: |
@@ -2377,6 +2601,41 @@ static inline int log10_cpu(int nb) | |||
2377 | return 1; | 2601 | return 1; |
2378 | } | 2602 | } |
2379 | 2603 | ||
2604 | static void print_lat_fmt(void *data, int size __unused) | ||
2605 | { | ||
2606 | unsigned int lat_flags; | ||
2607 | unsigned int pc; | ||
2608 | int lock_depth; | ||
2609 | int hardirq; | ||
2610 | int softirq; | ||
2611 | |||
2612 | lat_flags = parse_common_flags(data); | ||
2613 | pc = parse_common_pc(data); | ||
2614 | lock_depth = parse_common_lock_depth(data); | ||
2615 | |||
2616 | hardirq = lat_flags & TRACE_FLAG_HARDIRQ; | ||
2617 | softirq = lat_flags & TRACE_FLAG_SOFTIRQ; | ||
2618 | |||
2619 | printf("%c%c%c", | ||
2620 | (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' : | ||
2621 | (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ? | ||
2622 | 'X' : '.', | ||
2623 | (lat_flags & TRACE_FLAG_NEED_RESCHED) ? | ||
2624 | 'N' : '.', | ||
2625 | (hardirq && softirq) ? 'H' : | ||
2626 | hardirq ? 'h' : softirq ? 's' : '.'); | ||
2627 | |||
2628 | if (pc) | ||
2629 | printf("%x", pc); | ||
2630 | else | ||
2631 | printf("."); | ||
2632 | |||
2633 | if (lock_depth < 0) | ||
2634 | printf("."); | ||
2635 | else | ||
2636 | printf("%d", lock_depth); | ||
2637 | } | ||
2638 | |||
2380 | /* taken from Linux, written by Frederic Weisbecker */ | 2639 | /* taken from Linux, written by Frederic Weisbecker */ |
2381 | static void print_graph_cpu(int cpu) | 2640 | static void print_graph_cpu(int cpu) |
2382 | { | 2641 | { |
@@ -2452,7 +2711,7 @@ get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func, | |||
2452 | if (!(event->flags & EVENT_FL_ISFUNCRET)) | 2711 | if (!(event->flags & EVENT_FL_ISFUNCRET)) |
2453 | return NULL; | 2712 | return NULL; |
2454 | 2713 | ||
2455 | pid = parse_common_pid(next->data); | 2714 | pid = trace_parse_common_pid(next->data); |
2456 | field = find_field(event, "func"); | 2715 | field = find_field(event, "func"); |
2457 | if (!field) | 2716 | if (!field) |
2458 | die("function return does not have field func"); | 2717 | die("function return does not have field func"); |
@@ -2620,6 +2879,11 @@ pretty_print_func_ent(void *data, int size, struct event *event, | |||
2620 | 2879 | ||
2621 | printf(" | "); | 2880 | printf(" | "); |
2622 | 2881 | ||
2882 | if (latency_format) { | ||
2883 | print_lat_fmt(data, size); | ||
2884 | printf(" | "); | ||
2885 | } | ||
2886 | |||
2623 | field = find_field(event, "func"); | 2887 | field = find_field(event, "func"); |
2624 | if (!field) | 2888 | if (!field) |
2625 | die("function entry does not have func field"); | 2889 | die("function entry does not have func field"); |
@@ -2663,6 +2927,11 @@ pretty_print_func_ret(void *data, int size __unused, struct event *event, | |||
2663 | 2927 | ||
2664 | printf(" | "); | 2928 | printf(" | "); |
2665 | 2929 | ||
2930 | if (latency_format) { | ||
2931 | print_lat_fmt(data, size); | ||
2932 | printf(" | "); | ||
2933 | } | ||
2934 | |||
2666 | field = find_field(event, "rettime"); | 2935 | field = find_field(event, "rettime"); |
2667 | if (!field) | 2936 | if (!field) |
2668 | die("can't find rettime in return graph"); | 2937 | die("can't find rettime in return graph"); |
@@ -2724,19 +2993,30 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs, | |||
2724 | 2993 | ||
2725 | event = trace_find_event(type); | 2994 | event = trace_find_event(type); |
2726 | if (!event) { | 2995 | if (!event) { |
2727 | printf("ug! no event found for type %d\n", type); | 2996 | warning("ug! no event found for type %d", type); |
2728 | return; | 2997 | return; |
2729 | } | 2998 | } |
2730 | 2999 | ||
2731 | pid = parse_common_pid(data); | 3000 | pid = trace_parse_common_pid(data); |
2732 | 3001 | ||
2733 | if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) | 3002 | if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) |
2734 | return pretty_print_func_graph(data, size, event, cpu, | 3003 | return pretty_print_func_graph(data, size, event, cpu, |
2735 | pid, comm, secs, usecs); | 3004 | pid, comm, secs, usecs); |
2736 | 3005 | ||
2737 | printf("%16s-%-5d [%03d] %5lu.%09Lu: %s: ", | 3006 | if (latency_format) { |
2738 | comm, pid, cpu, | 3007 | printf("%8.8s-%-5d %3d", |
2739 | secs, nsecs, event->name); | 3008 | comm, pid, cpu); |
3009 | print_lat_fmt(data, size); | ||
3010 | } else | ||
3011 | printf("%16s-%-5d [%03d]", comm, pid, cpu); | ||
3012 | |||
3013 | printf(" %5lu.%06lu: %s: ", secs, usecs, event->name); | ||
3014 | |||
3015 | if (event->flags & EVENT_FL_FAILED) { | ||
3016 | printf("EVENT '%s' FAILED TO PARSE\n", | ||
3017 | event->name); | ||
3018 | return; | ||
3019 | } | ||
2740 | 3020 | ||
2741 | pretty_print(data, size, event); | 3021 | pretty_print(data, size, event); |
2742 | printf("\n"); | 3022 | printf("\n"); |
@@ -2807,46 +3087,71 @@ static void print_args(struct print_arg *args) | |||
2807 | } | 3087 | } |
2808 | } | 3088 | } |
2809 | 3089 | ||
2810 | static void parse_header_field(char *type, | 3090 | static void parse_header_field(const char *field, |
2811 | int *offset, int *size) | 3091 | int *offset, int *size) |
2812 | { | 3092 | { |
2813 | char *token; | 3093 | char *token; |
3094 | int type; | ||
2814 | 3095 | ||
2815 | if (read_expected(EVENT_ITEM, (char *)"field") < 0) | 3096 | if (read_expected(EVENT_ITEM, "field") < 0) |
2816 | return; | 3097 | return; |
2817 | if (read_expected(EVENT_OP, (char *)":") < 0) | 3098 | if (read_expected(EVENT_OP, ":") < 0) |
2818 | return; | 3099 | return; |
3100 | |||
2819 | /* type */ | 3101 | /* type */ |
2820 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 3102 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
2821 | return; | 3103 | goto fail; |
2822 | free_token(token); | 3104 | free_token(token); |
2823 | 3105 | ||
2824 | if (read_expected(EVENT_ITEM, type) < 0) | 3106 | if (read_expected(EVENT_ITEM, field) < 0) |
2825 | return; | 3107 | return; |
2826 | if (read_expected(EVENT_OP, (char *)";") < 0) | 3108 | if (read_expected(EVENT_OP, ";") < 0) |
2827 | return; | 3109 | return; |
2828 | if (read_expected(EVENT_ITEM, (char *)"offset") < 0) | 3110 | if (read_expected(EVENT_ITEM, "offset") < 0) |
2829 | return; | 3111 | return; |
2830 | if (read_expected(EVENT_OP, (char *)":") < 0) | 3112 | if (read_expected(EVENT_OP, ":") < 0) |
2831 | return; | 3113 | return; |
2832 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 3114 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
2833 | return; | 3115 | goto fail; |
2834 | *offset = atoi(token); | 3116 | *offset = atoi(token); |
2835 | free_token(token); | 3117 | free_token(token); |
2836 | if (read_expected(EVENT_OP, (char *)";") < 0) | 3118 | if (read_expected(EVENT_OP, ";") < 0) |
2837 | return; | 3119 | return; |
2838 | if (read_expected(EVENT_ITEM, (char *)"size") < 0) | 3120 | if (read_expected(EVENT_ITEM, "size") < 0) |
2839 | return; | 3121 | return; |
2840 | if (read_expected(EVENT_OP, (char *)":") < 0) | 3122 | if (read_expected(EVENT_OP, ":") < 0) |
2841 | return; | 3123 | return; |
2842 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 3124 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
2843 | return; | 3125 | goto fail; |
2844 | *size = atoi(token); | 3126 | *size = atoi(token); |
2845 | free_token(token); | 3127 | free_token(token); |
2846 | if (read_expected(EVENT_OP, (char *)";") < 0) | 3128 | if (read_expected(EVENT_OP, ";") < 0) |
2847 | return; | ||
2848 | if (read_expect_type(EVENT_NEWLINE, &token) < 0) | ||
2849 | return; | 3129 | return; |
3130 | type = read_token(&token); | ||
3131 | if (type != EVENT_NEWLINE) { | ||
3132 | /* newer versions of the kernel have a "signed" type */ | ||
3133 | if (type != EVENT_ITEM) | ||
3134 | goto fail; | ||
3135 | |||
3136 | if (strcmp(token, "signed") != 0) | ||
3137 | goto fail; | ||
3138 | |||
3139 | free_token(token); | ||
3140 | |||
3141 | if (read_expected(EVENT_OP, ":") < 0) | ||
3142 | return; | ||
3143 | |||
3144 | if (read_expect_type(EVENT_ITEM, &token)) | ||
3145 | goto fail; | ||
3146 | |||
3147 | free_token(token); | ||
3148 | if (read_expected(EVENT_OP, ";") < 0) | ||
3149 | return; | ||
3150 | |||
3151 | if (read_expect_type(EVENT_NEWLINE, &token)) | ||
3152 | goto fail; | ||
3153 | } | ||
3154 | fail: | ||
2850 | free_token(token); | 3155 | free_token(token); |
2851 | } | 3156 | } |
2852 | 3157 | ||
@@ -2854,11 +3159,11 @@ int parse_header_page(char *buf, unsigned long size) | |||
2854 | { | 3159 | { |
2855 | init_input_buf(buf, size); | 3160 | init_input_buf(buf, size); |
2856 | 3161 | ||
2857 | parse_header_field((char *)"timestamp", &header_page_ts_offset, | 3162 | parse_header_field("timestamp", &header_page_ts_offset, |
2858 | &header_page_ts_size); | 3163 | &header_page_ts_size); |
2859 | parse_header_field((char *)"commit", &header_page_size_offset, | 3164 | parse_header_field("commit", &header_page_size_offset, |
2860 | &header_page_size_size); | 3165 | &header_page_size_size); |
2861 | parse_header_field((char *)"data", &header_page_data_offset, | 3166 | parse_header_field("data", &header_page_data_offset, |
2862 | &header_page_data_size); | 3167 | &header_page_data_size); |
2863 | 3168 | ||
2864 | return 0; | 3169 | return 0; |
@@ -2909,6 +3214,9 @@ int parse_ftrace_file(char *buf, unsigned long size) | |||
2909 | if (ret < 0) | 3214 | if (ret < 0) |
2910 | die("failed to read ftrace event print fmt"); | 3215 | die("failed to read ftrace event print fmt"); |
2911 | 3216 | ||
3217 | /* New ftrace handles args */ | ||
3218 | if (ret > 0) | ||
3219 | return 0; | ||
2912 | /* | 3220 | /* |
2913 | * The arguments for ftrace files are parsed by the fields. | 3221 | * The arguments for ftrace files are parsed by the fields. |
2914 | * Set up the fields as their arguments. | 3222 | * Set up the fields as their arguments. |
@@ -2926,7 +3234,7 @@ int parse_ftrace_file(char *buf, unsigned long size) | |||
2926 | return 0; | 3234 | return 0; |
2927 | } | 3235 | } |
2928 | 3236 | ||
2929 | int parse_event_file(char *buf, unsigned long size, char *system__unused __unused) | 3237 | int parse_event_file(char *buf, unsigned long size, char *sys) |
2930 | { | 3238 | { |
2931 | struct event *event; | 3239 | struct event *event; |
2932 | int ret; | 3240 | int ret; |
@@ -2946,12 +3254,18 @@ int parse_event_file(char *buf, unsigned long size, char *system__unused __unuse | |||
2946 | die("failed to read event id"); | 3254 | die("failed to read event id"); |
2947 | 3255 | ||
2948 | ret = event_read_format(event); | 3256 | ret = event_read_format(event); |
2949 | if (ret < 0) | 3257 | if (ret < 0) { |
2950 | die("failed to read event format"); | 3258 | warning("failed to read event format for %s", event->name); |
3259 | goto event_failed; | ||
3260 | } | ||
2951 | 3261 | ||
2952 | ret = event_read_print(event); | 3262 | ret = event_read_print(event); |
2953 | if (ret < 0) | 3263 | if (ret < 0) { |
2954 | die("failed to read event print fmt"); | 3264 | warning("failed to read event print fmt for %s", event->name); |
3265 | goto event_failed; | ||
3266 | } | ||
3267 | |||
3268 | event->system = strdup(sys); | ||
2955 | 3269 | ||
2956 | #define PRINT_ARGS 0 | 3270 | #define PRINT_ARGS 0 |
2957 | if (PRINT_ARGS && event->print_fmt.args) | 3271 | if (PRINT_ARGS && event->print_fmt.args) |
@@ -2959,6 +3273,12 @@ int parse_event_file(char *buf, unsigned long size, char *system__unused __unuse | |||
2959 | 3273 | ||
2960 | add_event(event); | 3274 | add_event(event); |
2961 | return 0; | 3275 | return 0; |
3276 | |||
3277 | event_failed: | ||
3278 | event->flags |= EVENT_FL_FAILED; | ||
3279 | /* still add it even if it failed */ | ||
3280 | add_event(event); | ||
3281 | return -1; | ||
2962 | } | 3282 | } |
2963 | 3283 | ||
2964 | void parse_set_info(int nr_cpus, int long_sz) | 3284 | void parse_set_info(int nr_cpus, int long_sz) |
@@ -2966,3 +3286,18 @@ void parse_set_info(int nr_cpus, int long_sz) | |||
2966 | cpus = nr_cpus; | 3286 | cpus = nr_cpus; |
2967 | long_size = long_sz; | 3287 | long_size = long_sz; |
2968 | } | 3288 | } |
3289 | |||
3290 | int common_pc(struct scripting_context *context) | ||
3291 | { | ||
3292 | return parse_common_pc(context->event_data); | ||
3293 | } | ||
3294 | |||
3295 | int common_flags(struct scripting_context *context) | ||
3296 | { | ||
3297 | return parse_common_flags(context->event_data); | ||
3298 | } | ||
3299 | |||
3300 | int common_lock_depth(struct scripting_context *context) | ||
3301 | { | ||
3302 | return parse_common_lock_depth(context->event_data); | ||
3303 | } | ||
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 1b5c847d2c22..7cd1193918c7 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
@@ -18,7 +18,7 @@ | |||
18 | * | 18 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ | 20 | */ |
21 | #define _LARGEFILE64_SOURCE | 21 | #define _FILE_OFFSET_BITS 64 |
22 | 22 | ||
23 | #include <dirent.h> | 23 | #include <dirent.h> |
24 | #include <stdio.h> | 24 | #include <stdio.h> |
@@ -83,7 +83,7 @@ static char *read_string(void) | |||
83 | char *str = NULL; | 83 | char *str = NULL; |
84 | int size = 0; | 84 | int size = 0; |
85 | int i; | 85 | int i; |
86 | int r; | 86 | off_t r; |
87 | 87 | ||
88 | for (;;) { | 88 | for (;;) { |
89 | r = read(input_fd, buf, BUFSIZ); | 89 | r = read(input_fd, buf, BUFSIZ); |
@@ -118,7 +118,7 @@ static char *read_string(void) | |||
118 | 118 | ||
119 | /* move the file descriptor to the end of the string */ | 119 | /* move the file descriptor to the end of the string */ |
120 | r = lseek(input_fd, -(r - i), SEEK_CUR); | 120 | r = lseek(input_fd, -(r - i), SEEK_CUR); |
121 | if (r < 0) | 121 | if (r == (off_t)-1) |
122 | die("lseek"); | 122 | die("lseek"); |
123 | 123 | ||
124 | if (str) { | 124 | if (str) { |
@@ -145,8 +145,9 @@ static void read_proc_kallsyms(void) | |||
145 | if (!size) | 145 | if (!size) |
146 | return; | 146 | return; |
147 | 147 | ||
148 | buf = malloc_or_die(size); | 148 | buf = malloc_or_die(size + 1); |
149 | read_or_die(buf, size); | 149 | read_or_die(buf, size); |
150 | buf[size] = '\0'; | ||
150 | 151 | ||
151 | parse_proc_kallsyms(buf, size); | 152 | parse_proc_kallsyms(buf, size); |
152 | 153 | ||
@@ -281,8 +282,8 @@ static void update_cpu_data_index(int cpu) | |||
281 | 282 | ||
282 | static void get_next_page(int cpu) | 283 | static void get_next_page(int cpu) |
283 | { | 284 | { |
284 | off64_t save_seek; | 285 | off_t save_seek; |
285 | off64_t ret; | 286 | off_t ret; |
286 | 287 | ||
287 | if (!cpu_data[cpu].page) | 288 | if (!cpu_data[cpu].page) |
288 | return; | 289 | return; |
@@ -297,17 +298,17 @@ static void get_next_page(int cpu) | |||
297 | update_cpu_data_index(cpu); | 298 | update_cpu_data_index(cpu); |
298 | 299 | ||
299 | /* other parts of the code may expect the pointer to not move */ | 300 | /* other parts of the code may expect the pointer to not move */ |
300 | save_seek = lseek64(input_fd, 0, SEEK_CUR); | 301 | save_seek = lseek(input_fd, 0, SEEK_CUR); |
301 | 302 | ||
302 | ret = lseek64(input_fd, cpu_data[cpu].offset, SEEK_SET); | 303 | ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET); |
303 | if (ret < 0) | 304 | if (ret == (off_t)-1) |
304 | die("failed to lseek"); | 305 | die("failed to lseek"); |
305 | ret = read(input_fd, cpu_data[cpu].page, page_size); | 306 | ret = read(input_fd, cpu_data[cpu].page, page_size); |
306 | if (ret < 0) | 307 | if (ret < 0) |
307 | die("failed to read page"); | 308 | die("failed to read page"); |
308 | 309 | ||
309 | /* reset the file pointer back */ | 310 | /* reset the file pointer back */ |
310 | lseek64(input_fd, save_seek, SEEK_SET); | 311 | lseek(input_fd, save_seek, SEEK_SET); |
311 | 312 | ||
312 | return; | 313 | return; |
313 | } | 314 | } |
@@ -458,9 +459,8 @@ struct record *trace_read_data(int cpu) | |||
458 | return data; | 459 | return data; |
459 | } | 460 | } |
460 | 461 | ||
461 | void trace_report(void) | 462 | void trace_report(int fd) |
462 | { | 463 | { |
463 | const char *input_file = "trace.info"; | ||
464 | char buf[BUFSIZ]; | 464 | char buf[BUFSIZ]; |
465 | char test[] = { 23, 8, 68 }; | 465 | char test[] = { 23, 8, 68 }; |
466 | char *version; | 466 | char *version; |
@@ -468,17 +468,15 @@ void trace_report(void) | |||
468 | int show_funcs = 0; | 468 | int show_funcs = 0; |
469 | int show_printk = 0; | 469 | int show_printk = 0; |
470 | 470 | ||
471 | input_fd = open(input_file, O_RDONLY); | 471 | input_fd = fd; |
472 | if (input_fd < 0) | ||
473 | die("opening '%s'\n", input_file); | ||
474 | 472 | ||
475 | read_or_die(buf, 3); | 473 | read_or_die(buf, 3); |
476 | if (memcmp(buf, test, 3) != 0) | 474 | if (memcmp(buf, test, 3) != 0) |
477 | die("not an trace data file"); | 475 | die("no trace data in the file"); |
478 | 476 | ||
479 | read_or_die(buf, 7); | 477 | read_or_die(buf, 7); |
480 | if (memcmp(buf, "tracing", 7) != 0) | 478 | if (memcmp(buf, "tracing", 7) != 0) |
481 | die("not a trace file (missing tracing)"); | 479 | die("not a trace file (missing 'tracing' tag)"); |
482 | 480 | ||
483 | version = read_string(); | 481 | version = read_string(); |
484 | if (show_version) | 482 | if (show_version) |
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c new file mode 100644 index 000000000000..7ea983acfaea --- /dev/null +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * trace-event-scripting. Scripting engine common and initialization code. | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Tom Zanussi <tzanussi@gmail.com> | ||
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 <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include <ctype.h> | ||
26 | #include <errno.h> | ||
27 | |||
28 | #include "../perf.h" | ||
29 | #include "util.h" | ||
30 | #include "trace-event.h" | ||
31 | |||
32 | struct scripting_context *scripting_context; | ||
33 | |||
34 | static int stop_script_unsupported(void) | ||
35 | { | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static void process_event_unsupported(int cpu __unused, | ||
40 | void *data __unused, | ||
41 | int size __unused, | ||
42 | unsigned long long nsecs __unused, | ||
43 | char *comm __unused) | ||
44 | { | ||
45 | } | ||
46 | |||
47 | static void print_python_unsupported_msg(void) | ||
48 | { | ||
49 | fprintf(stderr, "Python scripting not supported." | ||
50 | " Install libpython and rebuild perf to enable it.\n" | ||
51 | "For example:\n # apt-get install python-dev (ubuntu)" | ||
52 | "\n # yum install python-devel (Fedora)" | ||
53 | "\n etc.\n"); | ||
54 | } | ||
55 | |||
56 | static int python_start_script_unsupported(const char *script __unused, | ||
57 | int argc __unused, | ||
58 | const char **argv __unused) | ||
59 | { | ||
60 | print_python_unsupported_msg(); | ||
61 | |||
62 | return -1; | ||
63 | } | ||
64 | |||
65 | static int python_generate_script_unsupported(const char *outfile __unused) | ||
66 | { | ||
67 | print_python_unsupported_msg(); | ||
68 | |||
69 | return -1; | ||
70 | } | ||
71 | |||
72 | struct scripting_ops python_scripting_unsupported_ops = { | ||
73 | .name = "Python", | ||
74 | .start_script = python_start_script_unsupported, | ||
75 | .stop_script = stop_script_unsupported, | ||
76 | .process_event = process_event_unsupported, | ||
77 | .generate_script = python_generate_script_unsupported, | ||
78 | }; | ||
79 | |||
80 | static void register_python_scripting(struct scripting_ops *scripting_ops) | ||
81 | { | ||
82 | int err; | ||
83 | err = script_spec_register("Python", scripting_ops); | ||
84 | if (err) | ||
85 | die("error registering Python script extension"); | ||
86 | |||
87 | err = script_spec_register("py", scripting_ops); | ||
88 | if (err) | ||
89 | die("error registering py script extension"); | ||
90 | |||
91 | scripting_context = malloc(sizeof(struct scripting_context)); | ||
92 | } | ||
93 | |||
94 | #ifdef NO_LIBPYTHON | ||
95 | void setup_python_scripting(void) | ||
96 | { | ||
97 | register_python_scripting(&python_scripting_unsupported_ops); | ||
98 | } | ||
99 | #else | ||
100 | struct scripting_ops python_scripting_ops; | ||
101 | |||
102 | void setup_python_scripting(void) | ||
103 | { | ||
104 | register_python_scripting(&python_scripting_ops); | ||
105 | } | ||
106 | #endif | ||
107 | |||
108 | static void print_perl_unsupported_msg(void) | ||
109 | { | ||
110 | fprintf(stderr, "Perl scripting not supported." | ||
111 | " Install libperl and rebuild perf to enable it.\n" | ||
112 | "For example:\n # apt-get install libperl-dev (ubuntu)" | ||
113 | "\n # yum install 'perl(ExtUtils::Embed)' (Fedora)" | ||
114 | "\n etc.\n"); | ||
115 | } | ||
116 | |||
117 | static int perl_start_script_unsupported(const char *script __unused, | ||
118 | int argc __unused, | ||
119 | const char **argv __unused) | ||
120 | { | ||
121 | print_perl_unsupported_msg(); | ||
122 | |||
123 | return -1; | ||
124 | } | ||
125 | |||
126 | static int perl_generate_script_unsupported(const char *outfile __unused) | ||
127 | { | ||
128 | print_perl_unsupported_msg(); | ||
129 | |||
130 | return -1; | ||
131 | } | ||
132 | |||
133 | struct scripting_ops perl_scripting_unsupported_ops = { | ||
134 | .name = "Perl", | ||
135 | .start_script = perl_start_script_unsupported, | ||
136 | .stop_script = stop_script_unsupported, | ||
137 | .process_event = process_event_unsupported, | ||
138 | .generate_script = perl_generate_script_unsupported, | ||
139 | }; | ||
140 | |||
141 | static void register_perl_scripting(struct scripting_ops *scripting_ops) | ||
142 | { | ||
143 | int err; | ||
144 | err = script_spec_register("Perl", scripting_ops); | ||
145 | if (err) | ||
146 | die("error registering Perl script extension"); | ||
147 | |||
148 | err = script_spec_register("pl", scripting_ops); | ||
149 | if (err) | ||
150 | die("error registering pl script extension"); | ||
151 | |||
152 | scripting_context = malloc(sizeof(struct scripting_context)); | ||
153 | } | ||
154 | |||
155 | #ifdef NO_LIBPERL | ||
156 | void setup_perl_scripting(void) | ||
157 | { | ||
158 | register_perl_scripting(&perl_scripting_unsupported_ops); | ||
159 | } | ||
160 | #else | ||
161 | struct scripting_ops perl_scripting_ops; | ||
162 | |||
163 | void setup_perl_scripting(void) | ||
164 | { | ||
165 | register_perl_scripting(&perl_scripting_ops); | ||
166 | } | ||
167 | #endif | ||
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 693f815c9429..c3269b937db4 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef _TRACE_EVENTS_H | 1 | #ifndef __PERF_TRACE_EVENTS_H |
2 | #define _TRACE_EVENTS_H | 2 | #define __PERF_TRACE_EVENTS_H |
3 | 3 | ||
4 | #include "parse-events.h" | 4 | #include "parse-events.h" |
5 | 5 | ||
@@ -26,6 +26,11 @@ enum { | |||
26 | enum format_flags { | 26 | enum format_flags { |
27 | FIELD_IS_ARRAY = 1, | 27 | FIELD_IS_ARRAY = 1, |
28 | FIELD_IS_POINTER = 2, | 28 | FIELD_IS_POINTER = 2, |
29 | FIELD_IS_SIGNED = 4, | ||
30 | FIELD_IS_STRING = 8, | ||
31 | FIELD_IS_DYNAMIC = 16, | ||
32 | FIELD_IS_FLAG = 32, | ||
33 | FIELD_IS_SYMBOLIC = 64, | ||
29 | }; | 34 | }; |
30 | 35 | ||
31 | struct format_field { | 36 | struct format_field { |
@@ -132,15 +137,18 @@ struct event { | |||
132 | int flags; | 137 | int flags; |
133 | struct format format; | 138 | struct format format; |
134 | struct print_fmt print_fmt; | 139 | struct print_fmt print_fmt; |
140 | char *system; | ||
135 | }; | 141 | }; |
136 | 142 | ||
137 | enum { | 143 | enum { |
138 | EVENT_FL_ISFTRACE = 1, | 144 | EVENT_FL_ISFTRACE = 0x01, |
139 | EVENT_FL_ISPRINT = 2, | 145 | EVENT_FL_ISPRINT = 0x02, |
140 | EVENT_FL_ISBPRINT = 4, | 146 | EVENT_FL_ISBPRINT = 0x04, |
141 | EVENT_FL_ISFUNC = 8, | 147 | EVENT_FL_ISFUNC = 0x08, |
142 | EVENT_FL_ISFUNCENT = 16, | 148 | EVENT_FL_ISFUNCENT = 0x10, |
143 | EVENT_FL_ISFUNCRET = 32, | 149 | EVENT_FL_ISFUNCRET = 0x20, |
150 | |||
151 | EVENT_FL_FAILED = 0x80000000 | ||
144 | }; | 152 | }; |
145 | 153 | ||
146 | struct record { | 154 | struct record { |
@@ -154,7 +162,7 @@ struct record *trace_read_data(int cpu); | |||
154 | 162 | ||
155 | void parse_set_info(int nr_cpus, int long_sz); | 163 | void parse_set_info(int nr_cpus, int long_sz); |
156 | 164 | ||
157 | void trace_report(void); | 165 | void trace_report(int fd); |
158 | 166 | ||
159 | void *malloc_or_die(unsigned int size); | 167 | void *malloc_or_die(unsigned int size); |
160 | 168 | ||
@@ -166,7 +174,7 @@ void print_funcs(void); | |||
166 | void print_printk(void); | 174 | void print_printk(void); |
167 | 175 | ||
168 | int parse_ftrace_file(char *buf, unsigned long size); | 176 | int parse_ftrace_file(char *buf, unsigned long size); |
169 | int parse_event_file(char *buf, unsigned long size, char *system); | 177 | int parse_event_file(char *buf, unsigned long size, char *sys); |
170 | void print_event(int cpu, void *data, int size, unsigned long long nsecs, | 178 | void print_event(int cpu, void *data, int size, unsigned long long nsecs, |
171 | char *comm); | 179 | char *comm); |
172 | 180 | ||
@@ -233,13 +241,53 @@ extern int header_page_size_size; | |||
233 | extern int header_page_data_offset; | 241 | extern int header_page_data_offset; |
234 | extern int header_page_data_size; | 242 | extern int header_page_data_size; |
235 | 243 | ||
244 | extern int latency_format; | ||
245 | |||
236 | int parse_header_page(char *buf, unsigned long size); | 246 | int parse_header_page(char *buf, unsigned long size); |
237 | int trace_parse_common_type(void *data); | 247 | int trace_parse_common_type(void *data); |
248 | int trace_parse_common_pid(void *data); | ||
249 | int parse_common_pc(void *data); | ||
250 | int parse_common_flags(void *data); | ||
251 | int parse_common_lock_depth(void *data); | ||
238 | struct event *trace_find_event(int id); | 252 | struct event *trace_find_event(int id); |
253 | struct event *trace_find_next_event(struct event *event); | ||
254 | unsigned long long read_size(void *ptr, int size); | ||
239 | unsigned long long | 255 | unsigned long long |
240 | raw_field_value(struct event *event, const char *name, void *data); | 256 | raw_field_value(struct event *event, const char *name, void *data); |
241 | void *raw_field_ptr(struct event *event, const char *name, void *data); | 257 | void *raw_field_ptr(struct event *event, const char *name, void *data); |
258 | unsigned long long eval_flag(const char *flag); | ||
259 | |||
260 | int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events); | ||
261 | |||
262 | /* taken from kernel/trace/trace.h */ | ||
263 | enum trace_flag_type { | ||
264 | TRACE_FLAG_IRQS_OFF = 0x01, | ||
265 | TRACE_FLAG_IRQS_NOSUPPORT = 0x02, | ||
266 | TRACE_FLAG_NEED_RESCHED = 0x04, | ||
267 | TRACE_FLAG_HARDIRQ = 0x08, | ||
268 | TRACE_FLAG_SOFTIRQ = 0x10, | ||
269 | }; | ||
270 | |||
271 | struct scripting_ops { | ||
272 | const char *name; | ||
273 | int (*start_script) (const char *script, int argc, const char **argv); | ||
274 | int (*stop_script) (void); | ||
275 | void (*process_event) (int cpu, void *data, int size, | ||
276 | unsigned long long nsecs, char *comm); | ||
277 | int (*generate_script) (const char *outfile); | ||
278 | }; | ||
279 | |||
280 | int script_spec_register(const char *spec, struct scripting_ops *ops); | ||
281 | |||
282 | void setup_perl_scripting(void); | ||
283 | void setup_python_scripting(void); | ||
284 | |||
285 | struct scripting_context { | ||
286 | void *event_data; | ||
287 | }; | ||
242 | 288 | ||
243 | void read_tracing_data(struct perf_event_attr *pattrs, int nb_events); | 289 | int common_pc(struct scripting_context *context); |
290 | int common_flags(struct scripting_context *context); | ||
291 | int common_lock_depth(struct scripting_context *context); | ||
244 | 292 | ||
245 | #endif /* _TRACE_EVENTS_H */ | 293 | #endif /* __PERF_TRACE_EVENTS_H */ |
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h index 5e75f9005940..7d6b8331f898 100644 --- a/tools/perf/util/types.h +++ b/tools/perf/util/types.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef _PERF_TYPES_H | 1 | #ifndef __PERF_TYPES_H |
2 | #define _PERF_TYPES_H | 2 | #define __PERF_TYPES_H |
3 | 3 | ||
4 | /* | 4 | /* |
5 | * We define u64 as unsigned long long for every architecture | 5 | * We define u64 as unsigned long long for every architecture |
@@ -14,4 +14,4 @@ typedef signed short s16; | |||
14 | typedef unsigned char u8; | 14 | typedef unsigned char u8; |
15 | typedef signed char s8; | 15 | typedef signed char s8; |
16 | 16 | ||
17 | #endif /* _PERF_TYPES_H */ | 17 | #endif /* __PERF_TYPES_H */ |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c new file mode 100644 index 000000000000..f9b890fde681 --- /dev/null +++ b/tools/perf/util/util.c | |||
@@ -0,0 +1,94 @@ | |||
1 | #include "util.h" | ||
2 | #include <sys/mman.h> | ||
3 | |||
4 | int mkdir_p(char *path, mode_t mode) | ||
5 | { | ||
6 | struct stat st; | ||
7 | int err; | ||
8 | char *d = path; | ||
9 | |||
10 | if (*d != '/') | ||
11 | return -1; | ||
12 | |||
13 | if (stat(path, &st) == 0) | ||
14 | return 0; | ||
15 | |||
16 | while (*++d == '/'); | ||
17 | |||
18 | while ((d = strchr(d, '/'))) { | ||
19 | *d = '\0'; | ||
20 | err = stat(path, &st) && mkdir(path, mode); | ||
21 | *d++ = '/'; | ||
22 | if (err) | ||
23 | return -1; | ||
24 | while (*d == '/') | ||
25 | ++d; | ||
26 | } | ||
27 | return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; | ||
28 | } | ||
29 | |||
30 | static int slow_copyfile(const char *from, const char *to) | ||
31 | { | ||
32 | int err = 0; | ||
33 | char *line = NULL; | ||
34 | size_t n; | ||
35 | FILE *from_fp = fopen(from, "r"), *to_fp; | ||
36 | |||
37 | if (from_fp == NULL) | ||
38 | goto out; | ||
39 | |||
40 | to_fp = fopen(to, "w"); | ||
41 | if (to_fp == NULL) | ||
42 | goto out_fclose_from; | ||
43 | |||
44 | while (getline(&line, &n, from_fp) > 0) | ||
45 | if (fputs(line, to_fp) == EOF) | ||
46 | goto out_fclose_to; | ||
47 | err = 0; | ||
48 | out_fclose_to: | ||
49 | fclose(to_fp); | ||
50 | free(line); | ||
51 | out_fclose_from: | ||
52 | fclose(from_fp); | ||
53 | out: | ||
54 | return err; | ||
55 | } | ||
56 | |||
57 | int copyfile(const char *from, const char *to) | ||
58 | { | ||
59 | int fromfd, tofd; | ||
60 | struct stat st; | ||
61 | void *addr; | ||
62 | int err = -1; | ||
63 | |||
64 | if (stat(from, &st)) | ||
65 | goto out; | ||
66 | |||
67 | if (st.st_size == 0) /* /proc? do it slowly... */ | ||
68 | return slow_copyfile(from, to); | ||
69 | |||
70 | fromfd = open(from, O_RDONLY); | ||
71 | if (fromfd < 0) | ||
72 | goto out; | ||
73 | |||
74 | tofd = creat(to, 0755); | ||
75 | if (tofd < 0) | ||
76 | goto out_close_from; | ||
77 | |||
78 | addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0); | ||
79 | if (addr == MAP_FAILED) | ||
80 | goto out_close_to; | ||
81 | |||
82 | if (write(tofd, addr, st.st_size) == st.st_size) | ||
83 | err = 0; | ||
84 | |||
85 | munmap(addr, st.st_size); | ||
86 | out_close_to: | ||
87 | close(tofd); | ||
88 | if (err) | ||
89 | unlink(to); | ||
90 | out_close_from: | ||
91 | close(fromfd); | ||
92 | out: | ||
93 | return err; | ||
94 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 9de2329dd44d..0f5b2a6f1080 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -84,6 +84,9 @@ | |||
84 | #include <iconv.h> | 84 | #include <iconv.h> |
85 | #endif | 85 | #endif |
86 | 86 | ||
87 | extern const char *graph_line; | ||
88 | extern const char *graph_dotted_line; | ||
89 | |||
87 | /* On most systems <limits.h> would have given us this, but | 90 | /* On most systems <limits.h> would have given us this, but |
88 | * not on some systems (e.g. GNU/Hurd). | 91 | * not on some systems (e.g. GNU/Hurd). |
89 | */ | 92 | */ |
@@ -134,6 +137,15 @@ extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, | |||
134 | extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); | 137 | extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); |
135 | extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); | 138 | extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); |
136 | 139 | ||
140 | #include "../../../include/linux/stringify.h" | ||
141 | |||
142 | #define DIE_IF(cnd) \ | ||
143 | do { if (cnd) \ | ||
144 | die(" at (" __FILE__ ":" __stringify(__LINE__) "): " \ | ||
145 | __stringify(cnd) "\n"); \ | ||
146 | } while (0) | ||
147 | |||
148 | |||
137 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); | 149 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); |
138 | 150 | ||
139 | extern int prefixcmp(const char *str, const char *prefix); | 151 | extern int prefixcmp(const char *str, const char *prefix); |
@@ -278,17 +290,15 @@ static inline char *gitstrchrnul(const char *s, int c) | |||
278 | * Wrappers: | 290 | * Wrappers: |
279 | */ | 291 | */ |
280 | extern char *xstrdup(const char *str); | 292 | extern char *xstrdup(const char *str); |
281 | extern void *xmalloc(size_t size); | 293 | extern void *xmalloc(size_t size) __attribute__((weak)); |
282 | extern void *xmemdupz(const void *data, size_t len); | 294 | extern void *xmemdupz(const void *data, size_t len); |
283 | extern char *xstrndup(const char *str, size_t len); | 295 | extern char *xstrndup(const char *str, size_t len); |
284 | extern void *xrealloc(void *ptr, size_t size); | 296 | extern void *xrealloc(void *ptr, size_t size) __attribute__((weak)); |
285 | extern void *xcalloc(size_t nmemb, size_t size); | 297 | |
286 | extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); | 298 | static inline void *zalloc(size_t size) |
287 | extern ssize_t xread(int fd, void *buf, size_t len); | 299 | { |
288 | extern ssize_t xwrite(int fd, const void *buf, size_t len); | 300 | return calloc(1, size); |
289 | extern int xdup(int fd); | 301 | } |
290 | extern FILE *xfdopen(int fd, const char *mode); | ||
291 | extern int xmkstemp(char *template); | ||
292 | 302 | ||
293 | static inline size_t xsize_t(off_t len) | 303 | static inline size_t xsize_t(off_t len) |
294 | { | 304 | { |
@@ -306,6 +316,7 @@ static inline int has_extension(const char *filename, const char *ext) | |||
306 | #undef isascii | 316 | #undef isascii |
307 | #undef isspace | 317 | #undef isspace |
308 | #undef isdigit | 318 | #undef isdigit |
319 | #undef isxdigit | ||
309 | #undef isalpha | 320 | #undef isalpha |
310 | #undef isprint | 321 | #undef isprint |
311 | #undef isalnum | 322 | #undef isalnum |
@@ -323,6 +334,8 @@ extern unsigned char sane_ctype[256]; | |||
323 | #define isascii(x) (((x) & ~0x7f) == 0) | 334 | #define isascii(x) (((x) & ~0x7f) == 0) |
324 | #define isspace(x) sane_istest(x,GIT_SPACE) | 335 | #define isspace(x) sane_istest(x,GIT_SPACE) |
325 | #define isdigit(x) sane_istest(x,GIT_DIGIT) | 336 | #define isdigit(x) sane_istest(x,GIT_DIGIT) |
337 | #define isxdigit(x) \ | ||
338 | (sane_istest(toupper(x), GIT_ALPHA | GIT_DIGIT) && toupper(x) < 'G') | ||
326 | #define isalpha(x) sane_istest(x,GIT_ALPHA) | 339 | #define isalpha(x) sane_istest(x,GIT_ALPHA) |
327 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) | 340 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) |
328 | #define isprint(x) sane_istest(x,GIT_PRINT) | 341 | #define isprint(x) sane_istest(x,GIT_PRINT) |
@@ -390,4 +403,7 @@ void git_qsort(void *base, size_t nmemb, size_t size, | |||
390 | #endif | 403 | #endif |
391 | #endif | 404 | #endif |
392 | 405 | ||
406 | int mkdir_p(char *path, mode_t mode); | ||
407 | int copyfile(const char *from, const char *to); | ||
408 | |||
393 | #endif | 409 | #endif |
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index 1c15e39f99e3..cfa55d686e3b 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c | |||
@@ -169,6 +169,7 @@ static void perf_read_values__display_pretty(FILE *fp, | |||
169 | counterwidth[j], values->value[i][j]); | 169 | counterwidth[j], values->value[i][j]); |
170 | fprintf(fp, "\n"); | 170 | fprintf(fp, "\n"); |
171 | } | 171 | } |
172 | free(counterwidth); | ||
172 | } | 173 | } |
173 | 174 | ||
174 | static void perf_read_values__display_raw(FILE *fp, | 175 | static void perf_read_values__display_raw(FILE *fp, |
diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h index cadf8cf2a590..2fa967e1a88a 100644 --- a/tools/perf/util/values.h +++ b/tools/perf/util/values.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef _PERF_VALUES_H | 1 | #ifndef __PERF_VALUES_H |
2 | #define _PERF_VALUES_H | 2 | #define __PERF_VALUES_H |
3 | 3 | ||
4 | #include "types.h" | 4 | #include "types.h" |
5 | 5 | ||
@@ -24,4 +24,4 @@ void perf_read_values_add_value(struct perf_read_values *values, | |||
24 | void perf_read_values_display(FILE *fp, struct perf_read_values *values, | 24 | void perf_read_values_display(FILE *fp, struct perf_read_values *values, |
25 | int raw); | 25 | int raw); |
26 | 26 | ||
27 | #endif /* _PERF_VALUES_H */ | 27 | #endif /* __PERF_VALUES_H */ |
diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c index 4574ac28396f..bf44ca85d23b 100644 --- a/tools/perf/util/wrapper.c +++ b/tools/perf/util/wrapper.c | |||
@@ -79,43 +79,12 @@ void *xrealloc(void *ptr, size_t size) | |||
79 | return ret; | 79 | return ret; |
80 | } | 80 | } |
81 | 81 | ||
82 | void *xcalloc(size_t nmemb, size_t size) | ||
83 | { | ||
84 | void *ret = calloc(nmemb, size); | ||
85 | if (!ret && (!nmemb || !size)) | ||
86 | ret = calloc(1, 1); | ||
87 | if (!ret) { | ||
88 | release_pack_memory(nmemb * size, -1); | ||
89 | ret = calloc(nmemb, size); | ||
90 | if (!ret && (!nmemb || !size)) | ||
91 | ret = calloc(1, 1); | ||
92 | if (!ret) | ||
93 | die("Out of memory, calloc failed"); | ||
94 | } | ||
95 | return ret; | ||
96 | } | ||
97 | |||
98 | void *xmmap(void *start, size_t length, | ||
99 | int prot, int flags, int fd, off_t offset) | ||
100 | { | ||
101 | void *ret = mmap(start, length, prot, flags, fd, offset); | ||
102 | if (ret == MAP_FAILED) { | ||
103 | if (!length) | ||
104 | return NULL; | ||
105 | release_pack_memory(length, fd); | ||
106 | ret = mmap(start, length, prot, flags, fd, offset); | ||
107 | if (ret == MAP_FAILED) | ||
108 | die("Out of memory? mmap failed: %s", strerror(errno)); | ||
109 | } | ||
110 | return ret; | ||
111 | } | ||
112 | |||
113 | /* | 82 | /* |
114 | * xread() is the same a read(), but it automatically restarts read() | 83 | * xread() is the same a read(), but it automatically restarts read() |
115 | * operations with a recoverable error (EAGAIN and EINTR). xread() | 84 | * operations with a recoverable error (EAGAIN and EINTR). xread() |
116 | * DOES NOT GUARANTEE that "len" bytes is read even if the data is available. | 85 | * DOES NOT GUARANTEE that "len" bytes is read even if the data is available. |
117 | */ | 86 | */ |
118 | ssize_t xread(int fd, void *buf, size_t len) | 87 | static ssize_t xread(int fd, void *buf, size_t len) |
119 | { | 88 | { |
120 | ssize_t nr; | 89 | ssize_t nr; |
121 | while (1) { | 90 | while (1) { |
@@ -131,7 +100,7 @@ ssize_t xread(int fd, void *buf, size_t len) | |||
131 | * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT | 100 | * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT |
132 | * GUARANTEE that "len" bytes is written even if the operation is successful. | 101 | * GUARANTEE that "len" bytes is written even if the operation is successful. |
133 | */ | 102 | */ |
134 | ssize_t xwrite(int fd, const void *buf, size_t len) | 103 | static ssize_t xwrite(int fd, const void *buf, size_t len) |
135 | { | 104 | { |
136 | ssize_t nr; | 105 | ssize_t nr; |
137 | while (1) { | 106 | while (1) { |
@@ -179,29 +148,3 @@ ssize_t write_in_full(int fd, const void *buf, size_t count) | |||
179 | 148 | ||
180 | return total; | 149 | return total; |
181 | } | 150 | } |
182 | |||
183 | int xdup(int fd) | ||
184 | { | ||
185 | int ret = dup(fd); | ||
186 | if (ret < 0) | ||
187 | die("dup failed: %s", strerror(errno)); | ||
188 | return ret; | ||
189 | } | ||
190 | |||
191 | FILE *xfdopen(int fd, const char *mode) | ||
192 | { | ||
193 | FILE *stream = fdopen(fd, mode); | ||
194 | if (stream == NULL) | ||
195 | die("Out of memory? fdopen failed: %s", strerror(errno)); | ||
196 | return stream; | ||
197 | } | ||
198 | |||
199 | int xmkstemp(char *template) | ||
200 | { | ||
201 | int fd; | ||
202 | |||
203 | fd = mkstemp(template); | ||
204 | if (fd < 0) | ||
205 | die("Unable to create temporary file: %s", strerror(errno)); | ||
206 | return fd; | ||
207 | } | ||