perf session: Register the idle thread in perf_session__process_events
[linux-2.6-block.git] / tools / perf / util / event.c
CommitLineData
234fbbf5
ACM
1#include <linux/types.h>
2#include "event.h"
3#include "debug.h"
4#include "string.h"
62daacb5 5#include "thread.h"
234fbbf5
ACM
6
7static pid_t event__synthesize_comm(pid_t pid, int full,
d8f66248
ACM
8 int (*process)(event_t *event,
9 struct perf_session *session),
10 struct perf_session *session)
234fbbf5
ACM
11{
12 event_t ev;
13 char filename[PATH_MAX];
14 char bf[BUFSIZ];
15 FILE *fp;
16 size_t size = 0;
17 DIR *tasks;
18 struct dirent dirent, *next;
19 pid_t tgid = 0;
20
21 snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
22
23 fp = fopen(filename, "r");
24 if (fp == NULL) {
25out_race:
26 /*
27 * We raced with a task exiting - just return:
28 */
29 pr_debug("couldn't open %s\n", filename);
30 return 0;
31 }
32
33 memset(&ev.comm, 0, sizeof(ev.comm));
34 while (!ev.comm.comm[0] || !ev.comm.pid) {
35 if (fgets(bf, sizeof(bf), fp) == NULL)
36 goto out_failure;
37
38 if (memcmp(bf, "Name:", 5) == 0) {
39 char *name = bf + 5;
40 while (*name && isspace(*name))
41 ++name;
42 size = strlen(name) - 1;
43 memcpy(ev.comm.comm, name, size++);
44 } else if (memcmp(bf, "Tgid:", 5) == 0) {
45 char *tgids = bf + 5;
46 while (*tgids && isspace(*tgids))
47 ++tgids;
48 tgid = ev.comm.pid = atoi(tgids);
49 }
50 }
51
52 ev.comm.header.type = PERF_RECORD_COMM;
53 size = ALIGN(size, sizeof(u64));
54 ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size);
55
56 if (!full) {
57 ev.comm.tid = pid;
58
d8f66248 59 process(&ev, session);
234fbbf5
ACM
60 goto out_fclose;
61 }
62
63 snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
64
65 tasks = opendir(filename);
66 if (tasks == NULL)
67 goto out_race;
68
69 while (!readdir_r(tasks, &dirent, &next) && next) {
70 char *end;
71 pid = strtol(dirent.d_name, &end, 10);
72 if (*end)
73 continue;
74
75 ev.comm.tid = pid;
76
d8f66248 77 process(&ev, session);
234fbbf5
ACM
78 }
79 closedir(tasks);
80
81out_fclose:
82 fclose(fp);
83 return tgid;
84
85out_failure:
86 pr_warning("couldn't get COMM and pgid, malformed %s\n", filename);
87 return -1;
88}
89
90static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
d8f66248
ACM
91 int (*process)(event_t *event,
92 struct perf_session *session),
93 struct perf_session *session)
234fbbf5
ACM
94{
95 char filename[PATH_MAX];
96 FILE *fp;
97
98 snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
99
100 fp = fopen(filename, "r");
101 if (fp == NULL) {
102 /*
103 * We raced with a task exiting - just return:
104 */
105 pr_debug("couldn't open %s\n", filename);
106 return -1;
107 }
108
109 while (1) {
110 char bf[BUFSIZ], *pbf = bf;
111 event_t ev = {
112 .header = { .type = PERF_RECORD_MMAP },
113 };
114 int n;
115 size_t size;
116 if (fgets(bf, sizeof(bf), fp) == NULL)
117 break;
118
119 /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
120 n = hex2u64(pbf, &ev.mmap.start);
121 if (n < 0)
122 continue;
123 pbf += n + 1;
124 n = hex2u64(pbf, &ev.mmap.len);
125 if (n < 0)
126 continue;
127 pbf += n + 3;
128 if (*pbf == 'x') { /* vm_exec */
129 char *execname = strchr(bf, '/');
130
131 /* Catch VDSO */
132 if (execname == NULL)
133 execname = strstr(bf, "[vdso]");
134
135 if (execname == NULL)
136 continue;
137
138 size = strlen(execname);
139 execname[size - 1] = '\0'; /* Remove \n */
140 memcpy(ev.mmap.filename, execname, size);
141 size = ALIGN(size, sizeof(u64));
142 ev.mmap.len -= ev.mmap.start;
143 ev.mmap.header.size = (sizeof(ev.mmap) -
144 (sizeof(ev.mmap.filename) - size));
145 ev.mmap.pid = tgid;
146 ev.mmap.tid = pid;
147
d8f66248 148 process(&ev, session);
234fbbf5
ACM
149 }
150 }
151
152 fclose(fp);
153 return 0;
154}
155
d8f66248
ACM
156int event__synthesize_thread(pid_t pid,
157 int (*process)(event_t *event,
158 struct perf_session *session),
159 struct perf_session *session)
234fbbf5 160{
d8f66248 161 pid_t tgid = event__synthesize_comm(pid, 1, process, session);
234fbbf5
ACM
162 if (tgid == -1)
163 return -1;
d8f66248 164 return event__synthesize_mmap_events(pid, tgid, process, session);
234fbbf5
ACM
165}
166
d8f66248
ACM
167void event__synthesize_threads(int (*process)(event_t *event,
168 struct perf_session *session),
169 struct perf_session *session)
234fbbf5
ACM
170{
171 DIR *proc;
172 struct dirent dirent, *next;
173
174 proc = opendir("/proc");
175
176 while (!readdir_r(proc, &dirent, &next) && next) {
177 char *end;
178 pid_t pid = strtol(dirent.d_name, &end, 10);
179
180 if (*end) /* only interested in proper numerical dirents */
181 continue;
182
d8f66248 183 event__synthesize_thread(pid, process, session);
234fbbf5
ACM
184 }
185
186 closedir(proc);
187}
62daacb5
ACM
188
189char *event__cwd;
190int event__cwdlen;
191
192struct events_stats event__stats;
193
d8f66248 194int event__process_comm(event_t *self, struct perf_session *session __used)
62daacb5
ACM
195{
196 struct thread *thread = threads__findnew(self->comm.pid);
197
bab81b62 198 dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid);
62daacb5
ACM
199
200 if (thread == NULL || thread__set_comm(thread, self->comm.comm)) {
201 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
202 return -1;
203 }
204
205 return 0;
206}
207
d8f66248 208int event__process_lost(event_t *self, struct perf_session *session __used)
62daacb5
ACM
209{
210 dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost);
211 event__stats.lost += self->lost.lost;
212 return 0;
213}
214
d8f66248 215int event__process_mmap(event_t *self, struct perf_session *session __used)
62daacb5
ACM
216{
217 struct thread *thread = threads__findnew(self->mmap.pid);
218 struct map *map = map__new(&self->mmap, MAP__FUNCTION,
219 event__cwd, event__cwdlen);
220
221 dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n",
222 self->mmap.pid, self->mmap.tid,
223 (void *)(long)self->mmap.start,
224 (void *)(long)self->mmap.len,
225 (void *)(long)self->mmap.pgoff,
226 self->mmap.filename);
227
228 if (thread == NULL || map == NULL)
229 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
230 else
231 thread__insert_map(thread, map);
232
233 return 0;
234}
235
d8f66248 236int event__process_task(event_t *self, struct perf_session *session __used)
62daacb5
ACM
237{
238 struct thread *thread = threads__findnew(self->fork.pid);
239 struct thread *parent = threads__findnew(self->fork.ppid);
240
241 dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
242 self->fork.ppid, self->fork.ptid);
243 /*
244 * A thread clone will have the same PID for both parent and child.
245 */
246 if (thread == parent)
247 return 0;
248
249 if (self->header.type == PERF_RECORD_EXIT)
250 return 0;
251
252 if (thread == NULL || parent == NULL ||
253 thread__fork(thread, parent) < 0) {
254 dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
255 return -1;
256 }
257
258 return 0;
259}
1ed091c4
ACM
260
261void thread__find_addr_location(struct thread *self, u8 cpumode,
262 enum map_type type, u64 addr,
263 struct addr_location *al,
264 symbol_filter_t filter)
265{
9958e1f0 266 struct map_groups *mg = &self->mg;
1ed091c4 267
9958e1f0 268 al->thread = self;
1ed091c4
ACM
269 al->addr = addr;
270
271 if (cpumode & PERF_RECORD_MISC_KERNEL) {
272 al->level = 'k';
9958e1f0 273 mg = kmaps;
1ed091c4
ACM
274 } else if (cpumode & PERF_RECORD_MISC_USER)
275 al->level = '.';
276 else {
277 al->level = 'H';
278 al->map = NULL;
279 al->sym = NULL;
280 return;
281 }
282try_again:
9958e1f0 283 al->map = map_groups__find(mg, type, al->addr);
1ed091c4
ACM
284 if (al->map == NULL) {
285 /*
286 * If this is outside of all known maps, and is a negative
287 * address, try to look it up in the kernel dso, as it might be
288 * a vsyscall or vdso (which executes in user-mode).
289 *
290 * XXX This is nasty, we should have a symbol list in the
291 * "[vdso]" dso, but for now lets use the old trick of looking
292 * in the whole kernel symbol list.
293 */
9958e1f0
ACM
294 if ((long long)al->addr < 0 && mg != kmaps) {
295 mg = kmaps;
1ed091c4
ACM
296 goto try_again;
297 }
298 al->sym = NULL;
299 } else {
300 al->addr = al->map->map_ip(al->map, al->addr);
301 al->sym = map__find_symbol(al->map, al->addr, filter);
302 }
303}
304
305int event__preprocess_sample(const event_t *self, struct addr_location *al,
306 symbol_filter_t filter)
307{
308 u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
309 struct thread *thread = threads__findnew(self->ip.pid);
310
311 if (thread == NULL)
312 return -1;
313
314 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
315
316 thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
317 self->ip.ip, al, filter);
318 dump_printf(" ...... dso: %s\n",
319 al->map ? al->map->dso->long_name :
320 al->level == 'H' ? "[hypervisor]" : "<not found>");
321 return 0;
322}
180f95e2
OH
323
324int event__parse_sample(event_t *event, u64 type, struct sample_data *data)
325{
326 u64 *array = event->sample.array;
327
328 if (type & PERF_SAMPLE_IP) {
329 data->ip = event->ip.ip;
330 array++;
331 }
332
333 if (type & PERF_SAMPLE_TID) {
334 u32 *p = (u32 *)array;
335 data->pid = p[0];
336 data->tid = p[1];
337 array++;
338 }
339
340 if (type & PERF_SAMPLE_TIME) {
341 data->time = *array;
342 array++;
343 }
344
345 if (type & PERF_SAMPLE_ADDR) {
346 data->addr = *array;
347 array++;
348 }
349
350 if (type & PERF_SAMPLE_ID) {
351 data->id = *array;
352 array++;
353 }
354
355 if (type & PERF_SAMPLE_STREAM_ID) {
356 data->stream_id = *array;
357 array++;
358 }
359
360 if (type & PERF_SAMPLE_CPU) {
361 u32 *p = (u32 *)array;
362 data->cpu = *p;
363 array++;
364 }
365
366 if (type & PERF_SAMPLE_PERIOD) {
367 data->period = *array;
368 array++;
369 }
370
371 if (type & PERF_SAMPLE_READ) {
372 pr_debug("PERF_SAMPLE_READ is unsuported for now\n");
373 return -1;
374 }
375
376 if (type & PERF_SAMPLE_CALLCHAIN) {
377 data->callchain = (struct ip_callchain *)array;
378 array += 1 + data->callchain->nr;
379 }
380
381 if (type & PERF_SAMPLE_RAW) {
382 u32 *p = (u32 *)array;
383 data->raw_size = *p;
384 p++;
385 data->raw_data = p;
386 }
387
388 return 0;
389}