blob: cba5cb0a97f977b7e3566fc083332406ed3216dd [file] [log] [blame]
Ingo Molnare0143ba2009-03-23 21:29:59 +01001/*
2 * kerneltop.c: show top kernel functions - performance counters showcase
3
4 Build with:
5
6 cc -O6 -Wall `pkg-config --cflags --libs glib-2.0` -o kerneltop kerneltop.c
7
8 Sample output:
9
10------------------------------------------------------------------------------
11 KernelTop: 2669 irqs/sec [NMI, cache-misses/cache-refs], (all, cpu: 2)
12------------------------------------------------------------------------------
13
14 weight RIP kernel function
15 ______ ________________ _______________
16
17 35.20 - ffffffff804ce74b : skb_copy_and_csum_dev
18 33.00 - ffffffff804cb740 : sock_alloc_send_skb
19 31.26 - ffffffff804ce808 : skb_push
20 22.43 - ffffffff80510004 : tcp_established_options
21 19.00 - ffffffff8027d250 : find_get_page
22 15.76 - ffffffff804e4fc9 : eth_type_trans
23 15.20 - ffffffff804d8baa : dst_release
24 14.86 - ffffffff804cf5d8 : skb_release_head_state
25 14.00 - ffffffff802217d5 : read_hpet
26 12.00 - ffffffff804ffb7f : __ip_local_out
27 11.97 - ffffffff804fc0c8 : ip_local_deliver_finish
28 8.54 - ffffffff805001a3 : ip_queue_xmit
29
30 Started by Ingo Molnar <mingo@redhat.com>
31
32 Improvements and fixes by:
33
34 Arjan van de Ven <arjan@linux.intel.com>
35 Yanmin Zhang <yanmin.zhang@intel.com>
36 Mike Galbraith <efault@gmx.de>
37
38 Released under the GPL v2. (and only v2, not any later version)
39
40 */
41#define _GNU_SOURCE
42#include <sys/types.h>
43#include <sys/stat.h>
44#include <sys/time.h>
45#include <unistd.h>
46#include <stdint.h>
47#include <stdlib.h>
48#include <string.h>
49#include <getopt.h>
50#include <assert.h>
51#include <fcntl.h>
52#include <stdio.h>
53#include <errno.h>
54#include <ctype.h>
55#include <time.h>
56
57#include <glib.h>
58
59#include <sys/syscall.h>
60#include <sys/ioctl.h>
61#include <sys/poll.h>
62#include <sys/prctl.h>
63#include <sys/wait.h>
64#include <sys/uio.h>
65
66#include <linux/unistd.h>
67
Wu Fengguangcea92ce2009-03-20 10:08:02 +080068#include "perfcounters.h"
Ingo Molnare0143ba2009-03-23 21:29:59 +010069
Ingo Molnare0143ba2009-03-23 21:29:59 +010070const unsigned int default_count[] = {
71 1000000,
72 1000000,
73 10000,
74 10000,
75 1000000,
76 10000,
77};
78
Ingo Molnare0143ba2009-03-23 21:29:59 +010079static __u64 count_filter = 100;
80
Ingo Molnare0143ba2009-03-23 21:29:59 +010081static int event_count[MAX_COUNTERS];
Ingo Molnare0143ba2009-03-23 21:29:59 +010082
83static int tid = -1;
84static int profile_cpu = -1;
85static int nr_cpus = 0;
86static int nmi = 1;
87static int group = 0;
88
89static char *vmlinux;
90
91static char *sym_filter;
92static unsigned long filter_start;
93static unsigned long filter_end;
94
95static int delay_secs = 2;
96static int zero;
97static int dump_symtab;
98
99struct source_line {
100 uint64_t EIP;
101 unsigned long count;
102 char *line;
103};
104
105static GList *lines;
106
107static void display_help(void)
108{
109 printf(
110 "Usage: kerneltop [<options>]\n\n"
111 "KernelTop Options (up to %d event types can be specified at once):\n\n",
112 MAX_COUNTERS);
113 printf(
Wu Fengguang95bb3be2009-03-20 10:08:04 +0800114 " -e EID --event=EID # event type ID [default: 0]\n"
Ingo Molnare0143ba2009-03-23 21:29:59 +0100115 " 0: CPU cycles\n"
116 " 1: instructions\n"
117 " 2: cache accesses\n"
118 " 3: cache misses\n"
119 " 4: branch instructions\n"
120 " 5: branch prediction misses\n"
121 " 6: bus cycles\n\n"
122 " rNNN: raw PMU events (eventsel+umask)\n\n"
123 " -c CNT --count=CNT # event period to sample\n\n"
124 " -C CPU --cpu=CPU # CPU (-1 for all) [default: -1]\n"
125 " -p PID --pid=PID # PID of sampled task (-1 for all) [default: -1]\n\n"
126 " -d delay --delay=<seconds> # sampling/display delay [default: 2]\n"
127 " -f CNT --filter=CNT # min-event-count filter [default: 100]\n\n"
128 " -s symbol --symbol=<symbol> # function to be showed annotated one-shot\n"
129 " -x path --vmlinux=<path> # the vmlinux binary, required for -s use:\n"
130 " -z --zero # zero counts after display\n"
131 " -D --dump_symtab # dump symbol table to stderr on startup\n"
132 "\n");
133
134 exit(0);
135}
136
137static void process_options(int argc, char *argv[])
138{
139 int error = 0, counter;
140
141 for (;;) {
142 int option_index = 0;
143 /** Options for getopt */
144 static struct option long_options[] = {
145 {"count", required_argument, NULL, 'c'},
146 {"cpu", required_argument, NULL, 'C'},
147 {"delay", required_argument, NULL, 'd'},
148 {"dump_symtab", no_argument, NULL, 'D'},
Wu Fengguang95bb3be2009-03-20 10:08:04 +0800149 {"event", required_argument, NULL, 'e'},
Ingo Molnare0143ba2009-03-23 21:29:59 +0100150 {"filter", required_argument, NULL, 'f'},
151 {"group", required_argument, NULL, 'g'},
152 {"help", no_argument, NULL, 'h'},
153 {"nmi", required_argument, NULL, 'n'},
154 {"pid", required_argument, NULL, 'p'},
155 {"vmlinux", required_argument, NULL, 'x'},
156 {"symbol", required_argument, NULL, 's'},
157 {"zero", no_argument, NULL, 'z'},
158 {NULL, 0, NULL, 0 }
159 };
160 int c = getopt_long(argc, argv, "c:C:d:De:f:g:hn:p:s:x:z",
161 long_options, &option_index);
162 if (c == -1)
163 break;
164
165 switch (c) {
166 case 'c':
Ingo Molnare0143ba2009-03-23 21:29:59 +0100167 event_count[nr_counters] = atoi(optarg); break;
168 case 'C':
169 /* CPU and PID are mutually exclusive */
170 if (tid != -1) {
171 printf("WARNING: CPU switch overriding PID\n");
172 sleep(1);
173 tid = -1;
174 }
175 profile_cpu = atoi(optarg); break;
176 case 'd': delay_secs = atoi(optarg); break;
177 case 'D': dump_symtab = 1; break;
178
Wu Fengguang95bb3be2009-03-20 10:08:04 +0800179 case 'e': error = parse_events(optarg); break;
Ingo Molnare0143ba2009-03-23 21:29:59 +0100180
181 case 'f': count_filter = atoi(optarg); break;
182 case 'g': group = atoi(optarg); break;
183 case 'h': display_help(); break;
184 case 'n': nmi = atoi(optarg); break;
185 case 'p':
186 /* CPU and PID are mutually exclusive */
187 if (profile_cpu != -1) {
188 printf("WARNING: PID switch overriding CPU\n");
189 sleep(1);
190 profile_cpu = -1;
191 }
192 tid = atoi(optarg); break;
193 case 's': sym_filter = strdup(optarg); break;
194 case 'x': vmlinux = strdup(optarg); break;
195 case 'z': zero = 1; break;
196 default: error = 1; break;
197 }
198 }
199 if (error)
200 display_help();
201
Wu Fengguang95bb3be2009-03-20 10:08:04 +0800202 if (!nr_counters) {
Ingo Molnare0143ba2009-03-23 21:29:59 +0100203 nr_counters = 1;
Wu Fengguang95bb3be2009-03-20 10:08:04 +0800204 event_id[0] = 0;
205 }
Ingo Molnare0143ba2009-03-23 21:29:59 +0100206
207 for (counter = 0; counter < nr_counters; counter++) {
208 if (event_count[counter])
209 continue;
210
211 if (event_id[counter] < PERF_HW_EVENTS_MAX)
212 event_count[counter] = default_count[event_id[counter]];
213 else
214 event_count[counter] = 100000;
215 }
216}
217
218static uint64_t min_ip;
219static uint64_t max_ip = -1ll;
220
221struct sym_entry {
222 unsigned long long addr;
223 char *sym;
224 unsigned long count[MAX_COUNTERS];
225 int skip;
226 GList *source;
227};
228
229#define MAX_SYMS 100000
230
231static int sym_table_count;
232
233struct sym_entry *sym_filter_entry;
234
235static struct sym_entry sym_table[MAX_SYMS];
236
237static void show_details(struct sym_entry *sym);
238
239/*
240 * Ordering weight: count-1 * count-1 * ... / count-n
241 */
242static double sym_weight(const struct sym_entry *sym)
243{
244 double weight;
245 int counter;
246
247 weight = sym->count[0];
248
249 for (counter = 1; counter < nr_counters-1; counter++)
250 weight *= sym->count[counter];
251
252 weight /= (sym->count[counter] + 1);
253
254 return weight;
255}
256
257static int compare(const void *__sym1, const void *__sym2)
258{
259 const struct sym_entry *sym1 = __sym1, *sym2 = __sym2;
260
261 return sym_weight(sym1) < sym_weight(sym2);
262}
263
264static time_t last_refresh;
265static long events;
266static long userspace_events;
267static const char CONSOLE_CLEAR[] = "";
268
269static struct sym_entry tmp[MAX_SYMS];
270
271static void print_sym_table(void)
272{
273 int i, printed;
274 int counter;
275 float events_per_sec = events/delay_secs;
276 float kevents_per_sec = (events-userspace_events)/delay_secs;
277
278 memcpy(tmp, sym_table, sizeof(sym_table[0])*sym_table_count);
279 qsort(tmp, sym_table_count, sizeof(tmp[0]), compare);
280
281 write(1, CONSOLE_CLEAR, strlen(CONSOLE_CLEAR));
282
283 printf(
284"------------------------------------------------------------------------------\n");
285 printf( " KernelTop:%8.0f irqs/sec kernel:%3.1f%% [%s, ",
286 events_per_sec,
287 100.0 - (100.0*((events_per_sec-kevents_per_sec)/events_per_sec)),
288 nmi ? "NMI" : "IRQ");
289
290 if (nr_counters == 1)
291 printf("%d ", event_count[0]);
292
293 for (counter = 0; counter < nr_counters; counter++) {
294 if (counter)
295 printf("/");
296
Wu Fengguange3908612009-03-20 10:08:05 +0800297 printf("%s", event_name(counter));
Ingo Molnare0143ba2009-03-23 21:29:59 +0100298 }
299
300 printf( "], ");
301
302 if (tid != -1)
303 printf(" (tid: %d", tid);
304 else
305 printf(" (all");
306
307 if (profile_cpu != -1)
308 printf(", cpu: %d)\n", profile_cpu);
309 else {
310 if (tid != -1)
311 printf(")\n");
312 else
313 printf(", %d CPUs)\n", nr_cpus);
314 }
315
316 printf("------------------------------------------------------------------------------\n\n");
317
318 if (nr_counters == 1)
319 printf(" events");
320 else
321 printf(" weight events");
322
323 printf(" RIP kernel function\n"
324 " ______ ______ ________________ _______________\n\n"
325 );
326
327 printed = 0;
328 for (i = 0; i < sym_table_count; i++) {
329 int count;
330
331 if (nr_counters == 1) {
332 if (printed <= 18 &&
333 tmp[i].count[0] >= count_filter) {
334 printf("%19.2f - %016llx : %s\n",
335 sym_weight(tmp + i), tmp[i].addr, tmp[i].sym);
336 printed++;
337 }
338 } else {
339 if (printed <= 18 &&
340 tmp[i].count[0] >= count_filter) {
341 printf("%8.1f %10ld - %016llx : %s\n",
342 sym_weight(tmp + i),
343 tmp[i].count[0],
344 tmp[i].addr, tmp[i].sym);
345 printed++;
346 }
347 }
348 /*
349 * Add decay to the counts:
350 */
351 for (count = 0; count < nr_counters; count++)
352 sym_table[i].count[count] = zero ? 0 : sym_table[i].count[count] * 7 / 8;
353 }
354
355 if (sym_filter_entry)
356 show_details(sym_filter_entry);
357
358 last_refresh = time(NULL);
359
360 {
361 struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
362
363 if (poll(&stdin_poll, 1, 0) == 1) {
364 printf("key pressed - exiting.\n");
365 exit(0);
366 }
367 }
368}
369
370static int read_symbol(FILE *in, struct sym_entry *s)
371{
372 static int filter_match = 0;
373 char *sym, stype;
374 char str[500];
375 int rc, pos;
376
377 rc = fscanf(in, "%llx %c %499s", &s->addr, &stype, str);
378 if (rc == EOF)
379 return -1;
380
381 assert(rc == 3);
382
383 /* skip until end of line: */
384 pos = strlen(str);
385 do {
386 rc = fgetc(in);
387 if (rc == '\n' || rc == EOF || pos >= 499)
388 break;
389 str[pos] = rc;
390 pos++;
391 } while (1);
392 str[pos] = 0;
393
394 sym = str;
395
396 /* Filter out known duplicates and non-text symbols. */
397 if (!strcmp(sym, "_text"))
398 return 1;
399 if (!min_ip && !strcmp(sym, "_stext"))
400 return 1;
401 if (!strcmp(sym, "_etext") || !strcmp(sym, "_sinittext"))
402 return 1;
403 if (stype != 'T' && stype != 't')
404 return 1;
405 if (!strncmp("init_module", sym, 11) || !strncmp("cleanup_module", sym, 14))
406 return 1;
407 if (strstr(sym, "_text_start") || strstr(sym, "_text_end"))
408 return 1;
409
410 s->sym = malloc(strlen(str));
411 assert(s->sym);
412
413 strcpy((char *)s->sym, str);
414 s->skip = 0;
415
416 /* Tag events to be skipped. */
417 if (!strcmp("default_idle", s->sym) || !strcmp("cpu_idle", s->sym))
418 s->skip = 1;
419 if (!strcmp("enter_idle", s->sym) || !strcmp("exit_idle", s->sym))
420 s->skip = 1;
421
422 if (filter_match == 1) {
423 filter_end = s->addr;
424 filter_match = -1;
425 if (filter_end - filter_start > 10000) {
426 printf("hm, too large filter symbol <%s> - skipping.\n",
427 sym_filter);
428 printf("symbol filter start: %016lx\n", filter_start);
429 printf(" end: %016lx\n", filter_end);
430 filter_end = filter_start = 0;
431 sym_filter = NULL;
432 sleep(1);
433 }
434 }
435 if (filter_match == 0 && sym_filter && !strcmp(s->sym, sym_filter)) {
436 filter_match = 1;
437 filter_start = s->addr;
438 }
439
440 return 0;
441}
442
443int compare_addr(const void *__sym1, const void *__sym2)
444{
445 const struct sym_entry *sym1 = __sym1, *sym2 = __sym2;
446
447 return sym1->addr > sym2->addr;
448}
449
450static void sort_symbol_table(void)
451{
452 int i, dups;
453
454 do {
455 qsort(sym_table, sym_table_count, sizeof(sym_table[0]), compare_addr);
456 for (i = 0, dups = 0; i < sym_table_count; i++) {
457 if (sym_table[i].addr == sym_table[i+1].addr) {
458 sym_table[i+1].addr = -1ll;
459 dups++;
460 }
461 }
462 sym_table_count -= dups;
463 } while(dups);
464}
465
466static void parse_symbols(void)
467{
468 struct sym_entry *last;
469
470 FILE *kallsyms = fopen("/proc/kallsyms", "r");
471
472 if (!kallsyms) {
473 printf("Could not open /proc/kallsyms - no CONFIG_KALLSYMS_ALL=y?\n");
474 exit(-1);
475 }
476
477 while (!feof(kallsyms)) {
478 if (read_symbol(kallsyms, &sym_table[sym_table_count]) == 0) {
479 sym_table_count++;
480 assert(sym_table_count <= MAX_SYMS);
481 }
482 }
483
484 sort_symbol_table();
485 min_ip = sym_table[0].addr;
486 max_ip = sym_table[sym_table_count-1].addr;
487 last = sym_table + sym_table_count++;
488
489 last->addr = -1ll;
490 last->sym = "<end>";
491
492 if (filter_end) {
493 int count;
494 for (count=0; count < sym_table_count; count ++) {
495 if (!strcmp(sym_table[count].sym, sym_filter)) {
496 sym_filter_entry = &sym_table[count];
497 break;
498 }
499 }
500 }
501 if (dump_symtab) {
502 int i;
503
504 for (i = 0; i < sym_table_count; i++)
505 fprintf(stderr, "%llx %s\n",
506 sym_table[i].addr, sym_table[i].sym);
507 }
508}
509
510
511static void parse_vmlinux(char *filename)
512{
513 FILE *file;
514 char command[PATH_MAX*2];
515 if (!filename)
516 return;
517
518 sprintf(command, "objdump --start-address=0x%016lx --stop-address=0x%016lx -dS %s", filter_start, filter_end, filename);
519
520 file = popen(command, "r");
521 if (!file)
522 return;
523
524 while (!feof(file)) {
525 struct source_line *src;
526 size_t dummy = 0;
527 char *c;
528
529 src = malloc(sizeof(struct source_line));
530 assert(src != NULL);
531 memset(src, 0, sizeof(struct source_line));
532
533 if (getline(&src->line, &dummy, file) < 0)
534 break;
535 if (!src->line)
536 break;
537
538 c = strchr(src->line, '\n');
539 if (c)
540 *c = 0;
541
542 lines = g_list_prepend(lines, src);
543
544 if (strlen(src->line)>8 && src->line[8] == ':')
545 src->EIP = strtoull(src->line, NULL, 16);
546 if (strlen(src->line)>8 && src->line[16] == ':')
547 src->EIP = strtoull(src->line, NULL, 16);
548 }
549 pclose(file);
550 lines = g_list_reverse(lines);
551}
552
553static void record_precise_ip(uint64_t ip)
554{
555 struct source_line *line;
556 GList *item;
557
558 item = g_list_first(lines);
559 while (item) {
560 line = item->data;
561 if (line->EIP == ip)
562 line->count++;
563 if (line->EIP > ip)
564 break;
565 item = g_list_next(item);
566 }
567}
568
569static void lookup_sym_in_vmlinux(struct sym_entry *sym)
570{
571 struct source_line *line;
572 GList *item;
573 char pattern[PATH_MAX];
574 sprintf(pattern, "<%s>:", sym->sym);
575
576 item = g_list_first(lines);
577 while (item) {
578 line = item->data;
579 if (strstr(line->line, pattern)) {
580 sym->source = item;
581 break;
582 }
583 item = g_list_next(item);
584 }
585}
586
587void show_lines(GList *item_queue, int item_queue_count)
588{
589 int i;
590 struct source_line *line;
591
592 for (i = 0; i < item_queue_count; i++) {
593 line = item_queue->data;
594 printf("%8li\t%s\n", line->count, line->line);
595 item_queue = g_list_next(item_queue);
596 }
597}
598
599#define TRACE_COUNT 3
600
601static void show_details(struct sym_entry *sym)
602{
603 struct source_line *line;
604 GList *item;
605 int displayed = 0;
606 GList *item_queue = NULL;
607 int item_queue_count = 0;
608
609 if (!sym->source)
610 lookup_sym_in_vmlinux(sym);
611 if (!sym->source)
612 return;
613
614 printf("Showing details for %s\n", sym->sym);
615
616 item = sym->source;
617 while (item) {
618 line = item->data;
619 if (displayed && strstr(line->line, ">:"))
620 break;
621
622 if (!item_queue_count)
623 item_queue = item;
624 item_queue_count ++;
625
626 if (line->count >= count_filter) {
627 show_lines(item_queue, item_queue_count);
628 item_queue_count = 0;
629 item_queue = NULL;
630 } else if (item_queue_count > TRACE_COUNT) {
631 item_queue = g_list_next(item_queue);
632 item_queue_count --;
633 }
634
635 line->count = 0;
636 displayed++;
637 if (displayed > 300)
638 break;
639 item = g_list_next(item);
640 }
641}
642
643/*
644 * Binary search in the histogram table and record the hit:
645 */
646static void record_ip(uint64_t ip, int counter)
647{
648 int left_idx, middle_idx, right_idx, idx;
649 unsigned long left, middle, right;
650
651 record_precise_ip(ip);
652
653 left_idx = 0;
654 right_idx = sym_table_count-1;
655 assert(ip <= max_ip && ip >= min_ip);
656
657 while (left_idx + 1 < right_idx) {
658 middle_idx = (left_idx + right_idx) / 2;
659
660 left = sym_table[ left_idx].addr;
661 middle = sym_table[middle_idx].addr;
662 right = sym_table[ right_idx].addr;
663
664 if (!(left <= middle && middle <= right)) {
665 printf("%016lx...\n%016lx...\n%016lx\n", left, middle, right);
666 printf("%d %d %d\n", left_idx, middle_idx, right_idx);
667 }
668 assert(left <= middle && middle <= right);
669 if (!(left <= ip && ip <= right)) {
670 printf(" left: %016lx\n", left);
671 printf(" ip: %016lx\n", ip);
672 printf("right: %016lx\n", right);
673 }
674 assert(left <= ip && ip <= right);
675 /*
676 * [ left .... target .... middle .... right ]
677 * => right := middle
678 */
679 if (ip < middle) {
680 right_idx = middle_idx;
681 continue;
682 }
683 /*
684 * [ left .... middle ... target ... right ]
685 * => left := middle
686 */
687 left_idx = middle_idx;
688 }
689
690 idx = left_idx;
691
692 if (!sym_table[idx].skip)
693 sym_table[idx].count[counter]++;
694 else events--;
695}
696
697static void process_event(uint64_t ip, int counter)
698{
699 events++;
700
701 if (ip < min_ip || ip > max_ip) {
702 userspace_events++;
703 return;
704 }
705
706 record_ip(ip, counter);
707}
708
709int main(int argc, char *argv[])
710{
711 struct pollfd event_array[MAX_NR_CPUS][MAX_COUNTERS];
712 struct perf_counter_hw_event hw_event;
713 int fd[MAX_NR_CPUS][MAX_COUNTERS];
714 int i, counter, group_fd;
715 unsigned int cpu;
716 uint64_t ip;
717 ssize_t res;
718 int ret;
719
720 process_options(argc, argv);
721
722 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
723 if (tid != -1 || profile_cpu != -1)
724 nr_cpus = 1;
725
726 assert(nr_cpus <= MAX_NR_CPUS);
727
728 for (i = 0; i < nr_cpus; i++) {
729 group_fd = -1;
730 for (counter = 0; counter < nr_counters; counter++) {
731
732 cpu = profile_cpu;
733 if (tid == -1 && profile_cpu == -1)
734 cpu = i;
735
736 memset(&hw_event, 0, sizeof(hw_event));
737 hw_event.type = event_id[counter];
738 hw_event.raw = event_raw[counter];
739 hw_event.irq_period = event_count[counter];
740 hw_event.record_type = PERF_RECORD_IRQ;
741 hw_event.nmi = nmi;
742
743 fd[i][counter] = sys_perf_counter_open(&hw_event, tid, cpu, group_fd, 0);
744 fcntl(fd[i][counter], F_SETFL, O_NONBLOCK);
745 if (fd[i][counter] < 0) {
746 printf("kerneltop error: syscall returned with %d (%s)\n",
747 fd[i][counter], strerror(-fd[i][counter]));
748 if (fd[i][counter] == -1)
749 printf("Are you root?\n");
750 exit(-1);
751 }
752 assert(fd[i][counter] >= 0);
753
754 /*
755 * First counter acts as the group leader:
756 */
757 if (group && group_fd == -1)
758 group_fd = fd[i][counter];
759
760 event_array[i][counter].fd = fd[i][counter];
761 event_array[i][counter].events = POLLIN;
762 }
763 }
764
765 parse_symbols();
766 if (vmlinux && sym_filter_entry)
767 parse_vmlinux(vmlinux);
768
769 printf("KernelTop refresh period: %d seconds\n", delay_secs);
770 last_refresh = time(NULL);
771
772 while (1) {
773 int hits = events;
774
775 for (i = 0; i < nr_cpus; i++) {
776 for (counter = 0; counter < nr_counters; counter++) {
777 res = read(fd[i][counter], (char *) &ip, sizeof(ip));
778 if (res > 0) {
779 assert(res == sizeof(ip));
780
781 process_event(ip, counter);
782 }
783 }
784 }
785
786 if (time(NULL) >= last_refresh + delay_secs) {
787 print_sym_table();
788 events = userspace_events = 0;
789 }
790
791 if (hits == events)
792 ret = poll(event_array[0], nr_cpus, 1000);
793 hits = events;
794 }
795
796 return 0;
797}