Newer
Older
* builtin-top.c
*
* Builtin top command: Display a continuously updated profile of
* any workload, CPU or specific PID.
*
* Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com>
*
* Improvements and fixes by:
*
* Arjan van de Ven <arjan@linux.intel.com>
* Yanmin Zhang <yanmin.zhang@intel.com>
* Wu Fengguang <fengguang.wu@intel.com>
* Mike Galbraith <efault@gmx.de>
* Paul Mackerras <paulus@samba.org>
*
* Released under the GPL v2. (and only v2, not any later version)
#include "builtin.h"

Arnaldo Carvalho de Melo
committed
#include "util/symbol.h"
#include "util/color.h"
#include <linux/rbtree.h>
#include "util/parse-options.h"
#include "util/parse-events.h"
#include "util/debug.h"
#include <assert.h>
#include <fcntl.h>

Mike Galbraith
committed
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sched.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <sys/uio.h>
#include <sys/mman.h>
#include <linux/unistd.h>
#include <linux/types.h>
static int fd[MAX_NR_CPUS][MAX_COUNTERS];
static int system_wide = 0;
static int default_interval = 100000;

Mike Galbraith
committed
static int count_filter = 5;
static int inherit = 0;
static int profile_cpu = -1;
static int nr_cpus = 0;
static unsigned int realtime_prio = 0;
static int group = 0;
static unsigned int page_size;
static unsigned int mmap_pages = 16;
static int freq = 0;
static int delay_secs = 2;
static int zero;
static int dump_symtab;

Mike Galbraith
committed
/*
* Source
*/
struct source_line {
u64 eip;
unsigned long count[MAX_COUNTERS];
char *line;
struct source_line *next;
};
static char *sym_filter = NULL;
struct sym_entry *sym_filter_entry = NULL;
static int sym_pcnt_filter = 5;
static int sym_counter = 0;

Mike Galbraith
committed
static int display_weighted = -1;

Mike Galbraith
committed
static u64 min_ip;
static u64 max_ip = -1ll;

Arnaldo Carvalho de Melo
committed
struct rb_node rb_node;
struct list_head node;
unsigned long count[MAX_COUNTERS];
unsigned long snap_count;
double weight;

Mike Galbraith
committed
struct source_line *source;
struct source_line *lines;
struct source_line **lines_tail;
pthread_mutex_t source_lock;

Mike Galbraith
committed
/*
* Source functions
*/
static void parse_source(struct sym_entry *syme)
{
struct symbol *sym;
struct module *module;
struct section *section = NULL;
FILE *file;
char command[PATH_MAX*2];
const char *path = vmlinux_name;

Mike Galbraith
committed
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
u64 start, end, len;
if (!syme)
return;
if (syme->lines) {
pthread_mutex_lock(&syme->source_lock);
goto out_assign;
}
sym = (struct symbol *)(syme + 1);
module = sym->module;
if (module)
path = module->path;
if (!path)
return;
start = sym->obj_start;
if (!start)
start = sym->start;
if (module) {
section = module->sections->find_section(module->sections, ".text");
if (section)
start -= section->vma;
}
end = start + sym->end - sym->start + 1;
len = sym->end - sym->start;
sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", start, end, path);
file = popen(command, "r");
if (!file)
return;
pthread_mutex_lock(&syme->source_lock);
syme->lines_tail = &syme->lines;
while (!feof(file)) {
struct source_line *src;
size_t dummy = 0;
char *c;
src = malloc(sizeof(struct source_line));
assert(src != NULL);
memset(src, 0, sizeof(struct source_line));
if (getline(&src->line, &dummy, file) < 0)
break;
if (!src->line)
break;
c = strchr(src->line, '\n');
if (c)
*c = 0;
src->next = NULL;
*syme->lines_tail = src;
syme->lines_tail = &src->next;
if (strlen(src->line)>8 && src->line[8] == ':') {
src->eip = strtoull(src->line, NULL, 16);
if (section)
src->eip += section->vma;
}
if (strlen(src->line)>8 && src->line[16] == ':') {
src->eip = strtoull(src->line, NULL, 16);
if (section)
src->eip += section->vma;
}
}
pclose(file);
out_assign:
sym_filter_entry = syme;
pthread_mutex_unlock(&syme->source_lock);
}
static void __zero_source_counters(struct sym_entry *syme)
{
int i;
struct source_line *line;
line = syme->lines;
while (line) {
for (i = 0; i < nr_counters; i++)
line->count[i] = 0;
line = line->next;
}
}
static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
{
struct source_line *line;
if (syme != sym_filter_entry)
return;
if (pthread_mutex_trylock(&syme->source_lock))
return;
if (!syme->source)
goto out_unlock;
for (line = syme->lines; line; line = line->next) {
if (line->eip == ip) {
line->count[counter]++;
break;
}
if (line->eip > ip)
break;
}
out_unlock:
pthread_mutex_unlock(&syme->source_lock);
}
static void lookup_sym_source(struct sym_entry *syme)
{
struct symbol *symbol = (struct symbol *)(syme + 1);
struct source_line *line;
char pattern[PATH_MAX];
char *idx;
sprintf(pattern, "<%s>:", symbol->name);
if (symbol->module) {
idx = strstr(pattern, "\t");
if (idx)
*idx = 0;
}
pthread_mutex_lock(&syme->source_lock);
for (line = syme->lines; line; line = line->next) {
if (strstr(line->line, pattern)) {
syme->source = line;
break;
}
}
pthread_mutex_unlock(&syme->source_lock);
}
static void show_lines(struct source_line *queue, int count, int total)
{
int i;
struct source_line *line;
line = queue;
for (i = 0; i < count; i++) {
float pcnt = 100.0*(float)line->count[sym_counter]/(float)total;
printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line);
line = line->next;
}
}
#define TRACE_COUNT 3
static void show_details(struct sym_entry *syme)
{
struct symbol *symbol;
struct source_line *line;
struct source_line *line_queue = NULL;
int displayed = 0;
int line_queue_count = 0, total = 0, more = 0;
if (!syme)
return;
if (!syme->source)
lookup_sym_source(syme);
if (!syme->source)
return;
symbol = (struct symbol *)(syme + 1);
printf("Showing %s for %s\n", event_name(sym_counter), symbol->name);
printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter);
pthread_mutex_lock(&syme->source_lock);
line = syme->source;
while (line) {
total += line->count[sym_counter];
line = line->next;
}
line = syme->source;
while (line) {
float pcnt = 0.0;
if (!line_queue_count)
line_queue = line;
line_queue_count++;
if (line->count[sym_counter])
pcnt = 100.0 * line->count[sym_counter] / (float)total;
if (pcnt >= (float)sym_pcnt_filter) {
if (displayed <= print_entries)
show_lines(line_queue, line_queue_count, total);
else more++;
displayed += line_queue_count;
line_queue_count = 0;
line_queue = NULL;
} else if (line_queue_count > TRACE_COUNT) {
line_queue = line_queue->next;
line_queue_count--;
}
line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8;
line = line->next;
}
pthread_mutex_unlock(&syme->source_lock);
if (more)
printf("%d lines not displayed, maybe increase display entries [e]\n", more);
}

Arnaldo Carvalho de Melo
committed
/*
* Symbols will be added here in record_ip and will get out
* after decayed.
*/
static LIST_HEAD(active_symbols);
static pthread_mutex_t active_symbols_lock = PTHREAD_MUTEX_INITIALIZER;
/*
* Ordering weight: count-1 * count-2 * ... / count-n
*/
static double sym_weight(const struct sym_entry *sym)
{
double weight = sym->snap_count;

Mike Galbraith
committed
if (!display_weighted)
return weight;
for (counter = 1; counter < nr_counters-1; counter++)
weight *= sym->count[counter];
weight /= (sym->count[counter] + 1);
return weight;
}
static long samples;
static long userspace_samples;
static const char CONSOLE_CLEAR[] = "[H[2J";
static void __list_insert_active_sym(struct sym_entry *syme)

Arnaldo Carvalho de Melo
committed
{
list_add(&syme->node, &active_symbols);
}
static void list_remove_active_sym(struct sym_entry *syme)
{
pthread_mutex_lock(&active_symbols_lock);
list_del_init(&syme->node);
pthread_mutex_unlock(&active_symbols_lock);
}

Arnaldo Carvalho de Melo
committed
static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)
{
struct rb_node **p = &tree->rb_node;
struct rb_node *parent = NULL;
struct sym_entry *iter;
while (*p != NULL) {
parent = *p;
iter = rb_entry(parent, struct sym_entry, rb_node);
if (se->weight > iter->weight)

Arnaldo Carvalho de Melo
committed
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&se->rb_node, parent, p);
rb_insert_color(&se->rb_node, tree);
}
static void print_sym_table(void)
{
int printed = 0, j;

Mike Galbraith
committed
int counter, snap = !display_weighted ? sym_counter : 0;
float samples_per_sec = samples/delay_secs;
float ksamples_per_sec = (samples-userspace_samples)/delay_secs;
float sum_ksamples = 0.0;

Arnaldo Carvalho de Melo
committed
struct sym_entry *syme, *n;
struct rb_root tmp = RB_ROOT;
struct rb_node *nd;
samples = userspace_samples = 0;

Arnaldo Carvalho de Melo
committed
/* Sort the active symbols */
pthread_mutex_lock(&active_symbols_lock);
syme = list_entry(active_symbols.next, struct sym_entry, node);
pthread_mutex_unlock(&active_symbols_lock);
list_for_each_entry_safe_from(syme, n, &active_symbols, node) {

Mike Galbraith
committed
syme->snap_count = syme->count[snap];
if (syme->snap_count != 0) {
syme->weight = sym_weight(syme);

Arnaldo Carvalho de Melo
committed
rb_insert_active_sym(&tmp, syme);
sum_ksamples += syme->snap_count;
for (j = 0; j < nr_counters; j++)

Arnaldo Carvalho de Melo
committed
syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8;
} else
list_remove_active_sym(syme);
puts(CONSOLE_CLEAR);
printf(
"------------------------------------------------------------------------------\n");
printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [",
samples_per_sec,
100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec)));

Mike Galbraith
committed
if (nr_counters == 1 || !display_weighted) {
printf("%Ld", (u64)attrs[0].sample_period);
if (freq)
printf("Hz ");
else
printf(" ");
}

Mike Galbraith
committed
if (!display_weighted)
printf("%s", event_name(sym_counter));
else for (counter = 0; counter < nr_counters; counter++) {
if (counter)
printf("/");
printf("%s", event_name(counter));
}
printf( "], ");
if (target_pid != -1)
printf(" (target_pid: %d", target_pid);
else
printf(" (all");
if (profile_cpu != -1)
printf(", cpu: %d)\n", profile_cpu);
else {
printf(")\n");
else
printf(", %d CPUs)\n", nr_cpus);
}
printf("------------------------------------------------------------------------------\n\n");

Mike Galbraith
committed
if (sym_filter_entry) {
show_details(sym_filter_entry);
return;
}
printf(" samples pcnt");
printf(" weight samples pcnt");
if (verbose)
printf(" RIP ");
printf(" kernel function\n");
printf(" %s _______ _____",
nr_counters == 1 ? " " : "______");
if (verbose)
printf(" ________________");
printf(" _______________\n\n");

Arnaldo Carvalho de Melo
committed
for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {
syme = rb_entry(nd, struct sym_entry, rb_node);
sym = (struct symbol *)(syme + 1);

Mike Galbraith
committed
if (++printed > print_entries || (int)syme->snap_count < count_filter)
pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /
sum_ksamples));

Mike Galbraith
committed
if (nr_counters == 1 || !display_weighted)
printf("%20.2f - ", syme->weight);
printf("%9.1f %10ld - ", syme->weight, syme->snap_count);
percent_color_fprintf(stdout, "%4.1f%%", pcnt);
if (verbose)
printf(" - %016llx", sym->start);
printf(" : %s", sym->name);
if (sym->module)
printf("\t[%s]", sym->module->name);
printf("\n");

Mike Galbraith
committed
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
static void prompt_integer(int *target, const char *msg)
{
char *buf = malloc(0), *p;
size_t dummy = 0;
int tmp;
fprintf(stdout, "\n%s: ", msg);
if (getline(&buf, &dummy, stdin) < 0)
return;
p = strchr(buf, '\n');
if (p)
*p = 0;
p = buf;
while(*p) {
if (!isdigit(*p))
goto out_free;
p++;
}
tmp = strtoul(buf, NULL, 10);
*target = tmp;
out_free:
free(buf);
}
static void prompt_percent(int *target, const char *msg)
{
int tmp = 0;
prompt_integer(&tmp, msg);
if (tmp >= 0 && tmp <= 100)
*target = tmp;
}
static void prompt_symbol(struct sym_entry **target, const char *msg)
{
char *buf = malloc(0), *p;
struct sym_entry *syme = *target, *n, *found = NULL;
size_t dummy = 0;
/* zero counters of active symbol */
if (syme) {
pthread_mutex_lock(&syme->source_lock);
__zero_source_counters(syme);
*target = NULL;
pthread_mutex_unlock(&syme->source_lock);
}
fprintf(stdout, "\n%s: ", msg);
if (getline(&buf, &dummy, stdin) < 0)
goto out_free;
p = strchr(buf, '\n');
if (p)
*p = 0;
pthread_mutex_lock(&active_symbols_lock);
syme = list_entry(active_symbols.next, struct sym_entry, node);
pthread_mutex_unlock(&active_symbols_lock);
list_for_each_entry_safe_from(syme, n, &active_symbols, node) {
struct symbol *sym = (struct symbol *)(syme + 1);
if (!strcmp(buf, sym->name)) {
found = syme;
break;
}
}
if (!found) {
fprintf(stderr, "Sorry, %s is not active.\n", sym_filter);
sleep(1);
return;
} else
parse_source(found);
out_free:
free(buf);
}
static void print_mapped_keys(void)

Mike Galbraith
committed
{
char *name = NULL;
if (sym_filter_entry) {
struct symbol *sym = (struct symbol *)(sym_filter_entry+1);
name = sym->name;
}
fprintf(stdout, "\nMapped keys:\n");
fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs);
fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries);
if (nr_counters > 1)
fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_counter));
fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter);
fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter);
fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
fprintf(stdout, "\t[S] stop annotation.\n");
}
if (nr_counters > 1)
fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0);

Mike Galbraith
committed
fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0);
fprintf(stdout, "\t[qQ] quit.\n");
}
static int key_mapped(int c)
{
switch (c) {
case 'd':
case 'e':
case 'f':
case 'z':
case 'q':
case 'Q':
return 1;
case 'E':
case 'w':
return nr_counters > 1 ? 1 : 0;
case 'F':
case 's':
case 'S':
return vmlinux_name ? 1 : 0;
default:
break;

Mike Galbraith
committed
}
static void handle_keypress(int c)
{
if (!key_mapped(c)) {
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
struct termios tc, save;
print_mapped_keys();
fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
fflush(stdout);
tcgetattr(0, &save);
tc = save;
tc.c_lflag &= ~(ICANON | ECHO);
tc.c_cc[VMIN] = 0;
tc.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &tc);
poll(&stdin_poll, 1, -1);
c = getc(stdin);
tcsetattr(0, TCSAFLUSH, &save);
if (!key_mapped(c))
return;
}

Mike Galbraith
committed
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
switch (c) {
case 'd':
prompt_integer(&delay_secs, "Enter display delay");
break;
case 'e':
prompt_integer(&print_entries, "Enter display entries (lines)");
break;
case 'E':
if (nr_counters > 1) {
int i;
fprintf(stderr, "\nAvailable events:");
for (i = 0; i < nr_counters; i++)
fprintf(stderr, "\n\t%d %s", i, event_name(i));
prompt_integer(&sym_counter, "Enter details event counter");
if (sym_counter >= nr_counters) {
fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(0));
sym_counter = 0;
sleep(1);
}
} else sym_counter = 0;
break;
case 'f':
prompt_integer(&count_filter, "Enter display event count filter");
break;
case 'F':
prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)");
break;
case 'q':
case 'Q':
printf("exiting.\n");
exit(0);
case 's':
prompt_symbol(&sym_filter_entry, "Enter details symbol");
break;
case 'S':
if (!sym_filter_entry)
break;
else {
struct sym_entry *syme = sym_filter_entry;
pthread_mutex_lock(&syme->source_lock);
sym_filter_entry = NULL;
__zero_source_counters(syme);
pthread_mutex_unlock(&syme->source_lock);
}
break;

Mike Galbraith
committed
case 'w':
display_weighted = ~display_weighted;
break;

Mike Galbraith
committed
case 'z':
zero = ~zero;
break;

Mike Galbraith
committed
}
}
static void *display_thread(void *arg __used)
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };

Mike Galbraith
committed
struct termios tc, save;
int delay_msecs, c;
tcgetattr(0, &save);
tc = save;
tc.c_lflag &= ~(ICANON | ECHO);
tc.c_cc[VMIN] = 0;
tc.c_cc[VTIME] = 0;

Mike Galbraith
committed
repeat:
delay_msecs = delay_secs * 1000;
tcsetattr(0, TCSANOW, &tc);
/* trash return*/
getc(stdin);
} while (!poll(&stdin_poll, 1, delay_msecs) == 1);

Mike Galbraith
committed
c = getc(stdin);
tcsetattr(0, TCSAFLUSH, &save);
handle_keypress(c);
goto repeat;
/* Tag samples to be skipped. */
static const char *skip_symbols[] = {
"default_idle",
"cpu_idle",
"enter_idle",
"exit_idle",
"mwait_idle",
"mwait_idle_with_hints",
"ppc64_runlatch_off",
"pseries_dedicated_idle_sleep",

Arnaldo Carvalho de Melo
committed
static int symbol_filter(struct dso *self, struct symbol *sym)

Arnaldo Carvalho de Melo
committed
struct sym_entry *syme;
const char *name = sym->name;

Arnaldo Carvalho de Melo
committed
/*
* ppc64 uses function descriptors and appends a '.' to the
* start of every instruction address. Remove it.
*/
if (name[0] == '.')
name++;

Arnaldo Carvalho de Melo
committed
if (!strcmp(name, "_text") ||
!strcmp(name, "_etext") ||
!strcmp(name, "_sinittext") ||
!strncmp("init_module", name, 11) ||
!strncmp("cleanup_module", name, 14) ||
strstr(name, "_text_start") ||
strstr(name, "_text_end"))

Arnaldo Carvalho de Melo
committed
syme = dso__sym_priv(self, sym);

Mike Galbraith
committed
pthread_mutex_init(&syme->source_lock, NULL);
if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter))
sym_filter_entry = syme;
for (i = 0; skip_symbols[i]; i++) {
if (!strcmp(skip_symbols[i], name)) {
syme->skip = 1;
break;
}
}

Arnaldo Carvalho de Melo
committed
static int parse_symbols(void)

Arnaldo Carvalho de Melo
committed
struct rb_node *node;
struct symbol *sym;
int use_modules = vmlinux_name ? 1 : 0;

Arnaldo Carvalho de Melo
committed
kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry));
if (kernel_dso == NULL)
return -1;
if (dso__load_kernel(kernel_dso, vmlinux_name, symbol_filter, verbose, use_modules) <= 0)

Arnaldo Carvalho de Melo
committed
goto out_delete_dso;

Arnaldo Carvalho de Melo
committed
node = rb_first(&kernel_dso->syms);
sym = rb_entry(node, struct symbol, rb_node);
min_ip = sym->start;

Arnaldo Carvalho de Melo
committed
node = rb_last(&kernel_dso->syms);
sym = rb_entry(node, struct symbol, rb_node);
max_ip = sym->end;

Arnaldo Carvalho de Melo
committed
if (dump_symtab)
dso__fprintf(kernel_dso, stderr);

Arnaldo Carvalho de Melo
committed
return 0;

Arnaldo Carvalho de Melo
committed
out_delete_dso:
dso__delete(kernel_dso);
kernel_dso = NULL;
return -1;
}
/*
* Binary search in the histogram table and record the hit:
*/
static void record_ip(u64 ip, int counter)

Arnaldo Carvalho de Melo
committed
struct symbol *sym = dso__find_symbol(kernel_dso, ip);

Arnaldo Carvalho de Melo
committed
if (sym != NULL) {
struct sym_entry *syme = dso__sym_priv(kernel_dso, sym);

Arnaldo Carvalho de Melo
committed
if (!syme->skip) {
syme->count[counter]++;

Mike Galbraith
committed
record_precise_ip(syme, counter, ip);
pthread_mutex_lock(&active_symbols_lock);

Arnaldo Carvalho de Melo
committed
if (list_empty(&syme->node) || !syme->node.next)
__list_insert_active_sym(syme);
pthread_mutex_unlock(&active_symbols_lock);

Arnaldo Carvalho de Melo
committed
return;
static void process_event(u64 ip, int counter, int user)
return;
}
record_ip(ip, counter);
}
struct mmap_data {
int counter;
void *base;
unsigned int prev;
};
static unsigned int mmap_read_head(struct mmap_data *md)
{
struct perf_event_mmap_page *pc = md->base;
int head;
head = pc->data_head;
rmb();
return head;
}
struct timeval last_read, this_read;

Frederic Weisbecker
committed
static void mmap_read_counter(struct mmap_data *md)
{
unsigned int head = mmap_read_head(md);
unsigned int old = md->prev;
unsigned char *data = md->base + page_size;
int diff;
gettimeofday(&this_read, NULL);
/*
* If we're further behind than half the buffer, there's a chance
* the writer will bite our tail and mess up the samples under us.
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
*
* If we somehow ended up ahead of the head, we got messed up.
*
* In either case, truncate and restart at head.
*/
diff = head - old;
if (diff > md->mask / 2 || diff < 0) {
struct timeval iv;
unsigned long msecs;
timersub(&this_read, &last_read, &iv);
msecs = iv.tv_sec*1000 + iv.tv_usec/1000;
fprintf(stderr, "WARNING: failed to keep up with mmap data."
" Last read %lu msecs ago.\n", msecs);
/*
* head points to a known good entry, start there.
*/
old = head;
}
last_read = this_read;
for (; old != head;) {
event_t *event = (event_t *)&data[old & md->mask];
event_t event_copy;
size_t size = event->header.size;
/*
* Event straddles the mmap boundary -- header should always
* be inside due to u64 alignment of output.
*/
if ((old & md->mask) + size != ((old + size) & md->mask)) {
unsigned int offset = old;
unsigned int len = min(sizeof(*event), size), cpy;
void *dst = &event_copy;
do {
cpy = min(md->mask + 1 - (offset & md->mask), len);
memcpy(dst, &data[offset & md->mask], cpy);
offset += cpy;
dst += cpy;
len -= cpy;
} while (len);
event = &event_copy;
}
old += size;
if (event->header.type == PERF_RECORD_SAMPLE) {
(event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_USER;
process_event(event->ip.ip, md->counter, user);
}
}
md->prev = old;
}
static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS];
static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];

Frederic Weisbecker
committed
static void mmap_read(void)
{
int i, counter;
for (i = 0; i < nr_cpus; i++) {
for (counter = 0; counter < nr_counters; counter++)
mmap_read_counter(&mmap_array[i][counter]);
}