diff options
Diffstat (limited to 'tools/perf/util/event.c')
-rw-r--r-- | tools/perf/util/event.c | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c new file mode 100644 index 000000000000..414b89d1bde9 --- /dev/null +++ b/tools/perf/util/event.c | |||
@@ -0,0 +1,312 @@ | |||
1 | #include <linux/types.h> | ||
2 | #include "event.h" | ||
3 | #include "debug.h" | ||
4 | #include "string.h" | ||
5 | #include "thread.h" | ||
6 | |||
7 | static pid_t event__synthesize_comm(pid_t pid, int full, | ||
8 | int (*process)(event_t *event)) | ||
9 | { | ||
10 | event_t ev; | ||
11 | char filename[PATH_MAX]; | ||
12 | char bf[BUFSIZ]; | ||
13 | FILE *fp; | ||
14 | size_t size = 0; | ||
15 | DIR *tasks; | ||
16 | struct dirent dirent, *next; | ||
17 | pid_t tgid = 0; | ||
18 | |||
19 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); | ||
20 | |||
21 | fp = fopen(filename, "r"); | ||
22 | if (fp == NULL) { | ||
23 | out_race: | ||
24 | /* | ||
25 | * We raced with a task exiting - just return: | ||
26 | */ | ||
27 | pr_debug("couldn't open %s\n", filename); | ||
28 | return 0; | ||
29 | } | ||
30 | |||
31 | memset(&ev.comm, 0, sizeof(ev.comm)); | ||
32 | while (!ev.comm.comm[0] || !ev.comm.pid) { | ||
33 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
34 | goto out_failure; | ||
35 | |||
36 | if (memcmp(bf, "Name:", 5) == 0) { | ||
37 | char *name = bf + 5; | ||
38 | while (*name && isspace(*name)) | ||
39 | ++name; | ||
40 | size = strlen(name) - 1; | ||
41 | memcpy(ev.comm.comm, name, size++); | ||
42 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | ||
43 | char *tgids = bf + 5; | ||
44 | while (*tgids && isspace(*tgids)) | ||
45 | ++tgids; | ||
46 | tgid = ev.comm.pid = atoi(tgids); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | ev.comm.header.type = PERF_RECORD_COMM; | ||
51 | size = ALIGN(size, sizeof(u64)); | ||
52 | ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size); | ||
53 | |||
54 | if (!full) { | ||
55 | ev.comm.tid = pid; | ||
56 | |||
57 | process(&ev); | ||
58 | goto out_fclose; | ||
59 | } | ||
60 | |||
61 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | ||
62 | |||
63 | tasks = opendir(filename); | ||
64 | if (tasks == NULL) | ||
65 | goto out_race; | ||
66 | |||
67 | while (!readdir_r(tasks, &dirent, &next) && next) { | ||
68 | char *end; | ||
69 | pid = strtol(dirent.d_name, &end, 10); | ||
70 | if (*end) | ||
71 | continue; | ||
72 | |||
73 | ev.comm.tid = pid; | ||
74 | |||
75 | process(&ev); | ||
76 | } | ||
77 | closedir(tasks); | ||
78 | |||
79 | out_fclose: | ||
80 | fclose(fp); | ||
81 | return tgid; | ||
82 | |||
83 | out_failure: | ||
84 | pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); | ||
85 | return -1; | ||
86 | } | ||
87 | |||
88 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | ||
89 | int (*process)(event_t *event)) | ||
90 | { | ||
91 | char filename[PATH_MAX]; | ||
92 | FILE *fp; | ||
93 | |||
94 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); | ||
95 | |||
96 | fp = fopen(filename, "r"); | ||
97 | if (fp == NULL) { | ||
98 | /* | ||
99 | * We raced with a task exiting - just return: | ||
100 | */ | ||
101 | pr_debug("couldn't open %s\n", filename); | ||
102 | return -1; | ||
103 | } | ||
104 | |||
105 | while (1) { | ||
106 | char bf[BUFSIZ], *pbf = bf; | ||
107 | event_t ev = { | ||
108 | .header = { .type = PERF_RECORD_MMAP }, | ||
109 | }; | ||
110 | int n; | ||
111 | size_t size; | ||
112 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
113 | break; | ||
114 | |||
115 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | ||
116 | n = hex2u64(pbf, &ev.mmap.start); | ||
117 | if (n < 0) | ||
118 | continue; | ||
119 | pbf += n + 1; | ||
120 | n = hex2u64(pbf, &ev.mmap.len); | ||
121 | if (n < 0) | ||
122 | continue; | ||
123 | pbf += n + 3; | ||
124 | if (*pbf == 'x') { /* vm_exec */ | ||
125 | char *execname = strchr(bf, '/'); | ||
126 | |||
127 | /* Catch VDSO */ | ||
128 | if (execname == NULL) | ||
129 | execname = strstr(bf, "[vdso]"); | ||
130 | |||
131 | if (execname == NULL) | ||
132 | continue; | ||
133 | |||
134 | size = strlen(execname); | ||
135 | execname[size - 1] = '\0'; /* Remove \n */ | ||
136 | memcpy(ev.mmap.filename, execname, size); | ||
137 | size = ALIGN(size, sizeof(u64)); | ||
138 | ev.mmap.len -= ev.mmap.start; | ||
139 | ev.mmap.header.size = (sizeof(ev.mmap) - | ||
140 | (sizeof(ev.mmap.filename) - size)); | ||
141 | ev.mmap.pid = tgid; | ||
142 | ev.mmap.tid = pid; | ||
143 | |||
144 | process(&ev); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | fclose(fp); | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | int event__synthesize_thread(pid_t pid, int (*process)(event_t *event)) | ||
153 | { | ||
154 | pid_t tgid = event__synthesize_comm(pid, 1, process); | ||
155 | if (tgid == -1) | ||
156 | return -1; | ||
157 | return event__synthesize_mmap_events(pid, tgid, process); | ||
158 | } | ||
159 | |||
160 | void event__synthesize_threads(int (*process)(event_t *event)) | ||
161 | { | ||
162 | DIR *proc; | ||
163 | struct dirent dirent, *next; | ||
164 | |||
165 | proc = opendir("/proc"); | ||
166 | |||
167 | while (!readdir_r(proc, &dirent, &next) && next) { | ||
168 | char *end; | ||
169 | pid_t pid = strtol(dirent.d_name, &end, 10); | ||
170 | |||
171 | if (*end) /* only interested in proper numerical dirents */ | ||
172 | continue; | ||
173 | |||
174 | event__synthesize_thread(pid, process); | ||
175 | } | ||
176 | |||
177 | closedir(proc); | ||
178 | } | ||
179 | |||
180 | char *event__cwd; | ||
181 | int event__cwdlen; | ||
182 | |||
183 | struct events_stats event__stats; | ||
184 | |||
185 | int event__process_comm(event_t *self) | ||
186 | { | ||
187 | struct thread *thread = threads__findnew(self->comm.pid); | ||
188 | |||
189 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); | ||
190 | |||
191 | if (thread == NULL || thread__set_comm(thread, self->comm.comm)) { | ||
192 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
193 | return -1; | ||
194 | } | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | int event__process_lost(event_t *self) | ||
200 | { | ||
201 | dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); | ||
202 | event__stats.lost += self->lost.lost; | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | int event__process_mmap(event_t *self) | ||
207 | { | ||
208 | struct thread *thread = threads__findnew(self->mmap.pid); | ||
209 | struct map *map = map__new(&self->mmap, MAP__FUNCTION, | ||
210 | event__cwd, event__cwdlen); | ||
211 | |||
212 | dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", | ||
213 | self->mmap.pid, self->mmap.tid, | ||
214 | (void *)(long)self->mmap.start, | ||
215 | (void *)(long)self->mmap.len, | ||
216 | (void *)(long)self->mmap.pgoff, | ||
217 | self->mmap.filename); | ||
218 | |||
219 | if (thread == NULL || map == NULL) | ||
220 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
221 | else | ||
222 | thread__insert_map(thread, map); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | int event__process_task(event_t *self) | ||
228 | { | ||
229 | struct thread *thread = threads__findnew(self->fork.pid); | ||
230 | struct thread *parent = threads__findnew(self->fork.ppid); | ||
231 | |||
232 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, | ||
233 | self->fork.ppid, self->fork.ptid); | ||
234 | /* | ||
235 | * A thread clone will have the same PID for both parent and child. | ||
236 | */ | ||
237 | if (thread == parent) | ||
238 | return 0; | ||
239 | |||
240 | if (self->header.type == PERF_RECORD_EXIT) | ||
241 | return 0; | ||
242 | |||
243 | if (thread == NULL || parent == NULL || | ||
244 | thread__fork(thread, parent) < 0) { | ||
245 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | ||
246 | return -1; | ||
247 | } | ||
248 | |||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | void thread__find_addr_location(struct thread *self, u8 cpumode, | ||
253 | enum map_type type, u64 addr, | ||
254 | struct addr_location *al, | ||
255 | symbol_filter_t filter) | ||
256 | { | ||
257 | struct thread *thread = al->thread = self; | ||
258 | |||
259 | al->addr = addr; | ||
260 | |||
261 | if (cpumode & PERF_RECORD_MISC_KERNEL) { | ||
262 | al->level = 'k'; | ||
263 | thread = kthread; | ||
264 | } else if (cpumode & PERF_RECORD_MISC_USER) | ||
265 | al->level = '.'; | ||
266 | else { | ||
267 | al->level = 'H'; | ||
268 | al->map = NULL; | ||
269 | al->sym = NULL; | ||
270 | return; | ||
271 | } | ||
272 | try_again: | ||
273 | al->map = thread__find_map(thread, type, al->addr); | ||
274 | if (al->map == NULL) { | ||
275 | /* | ||
276 | * If this is outside of all known maps, and is a negative | ||
277 | * address, try to look it up in the kernel dso, as it might be | ||
278 | * a vsyscall or vdso (which executes in user-mode). | ||
279 | * | ||
280 | * XXX This is nasty, we should have a symbol list in the | ||
281 | * "[vdso]" dso, but for now lets use the old trick of looking | ||
282 | * in the whole kernel symbol list. | ||
283 | */ | ||
284 | if ((long long)al->addr < 0 && thread != kthread) { | ||
285 | thread = kthread; | ||
286 | goto try_again; | ||
287 | } | ||
288 | al->sym = NULL; | ||
289 | } else { | ||
290 | al->addr = al->map->map_ip(al->map, al->addr); | ||
291 | al->sym = map__find_symbol(al->map, al->addr, filter); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | int event__preprocess_sample(const event_t *self, struct addr_location *al, | ||
296 | symbol_filter_t filter) | ||
297 | { | ||
298 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
299 | struct thread *thread = threads__findnew(self->ip.pid); | ||
300 | |||
301 | if (thread == NULL) | ||
302 | return -1; | ||
303 | |||
304 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | ||
305 | |||
306 | thread__find_addr_location(thread, cpumode, MAP__FUNCTION, | ||
307 | self->ip.ip, al, filter); | ||
308 | dump_printf(" ...... dso: %s\n", | ||
309 | al->map ? al->map->dso->long_name : | ||
310 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | ||
311 | return 0; | ||
312 | } | ||