Commit cbe46555 authored by Paul Mackerras's avatar Paul Mackerras Committed by Ingo Molnar

perf_counter tools: remove glib dependency and fix bugs in kerneltop.c

The glib dependency in kerneltop.c is only for a little bit of list
manipulation, and I find it inconvenient.  This adds a 'next' field to
struct source_line, which lets us link them together into a list.  The
code to do the linking ourselves turns out to be no longer or more
difficult than using glib.

This also fixes a few other problems:

- We need to #include <limits.h> to get PATH_MAX on powerpc.

- We need to #include <linux/types.h> rather than have our own
  definitions of __u64 and __s64; on powerpc the installed headers
  define them to be unsigned long and long respectively, and if we
  have our own, different definition here that causes a compile error.

- This takes out the x86 setting of errno from -ret in
  sys_perf_counter_open.  My experiments on x86 indicate that the
  glibc syscall() does this for us already.

- We had two CPU migration counters in the default set, which seems
  unnecessary; I changed one of them to a context switch counter.

- In perfstat mode we were printing CPU cycles and instructions as
  milliseconds, and the cpu clock and task clock counters as events.
  This fixes that.

- In perfstat mode we were still printing a blank line after the first
  counter, which was a holdover from when a task clock counter was
  automatically included as the first counter.  This removes the blank
  line.

- On a test machine here, parse_symbols() and parse_vmlinux() were
  taking long enough (almost 0.5 seconds) for the mmap buffer to
  overflow before we got to the first mmap_read() call, so this moves
  them before we open all the counters.

- The error message if sys_perf_counter_open fails needs to use errno,
  not -fd[i][counter].
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Acked-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
Acked-by: default avatarMike Galbraith <efault@gmx.de>
Cc: Arjan van de Ven <arjan@linux.intel.com>
Orig-LKML-Reference: <18888.29986.340328.540512@cargo.ozlabs.ibm.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 81cdbe05
...@@ -3,7 +3,7 @@ BINS = kerneltop perfstat ...@@ -3,7 +3,7 @@ BINS = kerneltop perfstat
all: $(BINS) all: $(BINS)
kerneltop: kerneltop.c ../../include/linux/perf_counter.h kerneltop: kerneltop.c ../../include/linux/perf_counter.h
cc -O6 -Wall -lrt `pkg-config --cflags --libs glib-2.0` -o $@ $< cc -O6 -Wall -lrt -o $@ $<
perfstat: kerneltop perfstat: kerneltop
ln -sf kerneltop perfstat ln -sf kerneltop perfstat
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
Build with: Build with:
cc -O6 -Wall -lrt `pkg-config --cflags --libs glib-2.0` -o kerneltop kerneltop.c cc -O6 -Wall -c -o kerneltop.o kerneltop.c -lrt
Sample output: Sample output:
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
* Yanmin Zhang <yanmin.zhang@intel.com> * Yanmin Zhang <yanmin.zhang@intel.com>
* Wu Fengguang <fengguang.wu@intel.com> * Wu Fengguang <fengguang.wu@intel.com>
* Mike Galbraith <efault@gmx.de> * Mike Galbraith <efault@gmx.de>
* Paul Mackerras <paulus@samba.org>
* *
* Released under the GPL v2. (and only v2, not any later version) * Released under the GPL v2. (and only v2, not any later version)
*/ */
...@@ -68,6 +69,7 @@ ...@@ -68,6 +69,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <limits.h>
#include <getopt.h> #include <getopt.h>
#include <assert.h> #include <assert.h>
#include <fcntl.h> #include <fcntl.h>
...@@ -76,8 +78,6 @@ ...@@ -76,8 +78,6 @@
#include <ctype.h> #include <ctype.h>
#include <time.h> #include <time.h>
#include <glib.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/poll.h> #include <sys/poll.h>
...@@ -87,6 +87,7 @@ ...@@ -87,6 +87,7 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/types.h>
#include "../../include/linux/perf_counter.h" #include "../../include/linux/perf_counter.h"
...@@ -114,11 +115,6 @@ ...@@ -114,11 +115,6 @@
#define __user #define __user
#define asmlinkage #define asmlinkage
typedef unsigned int __u32;
typedef unsigned long long __u64;
typedef long long __s64;
#ifdef __x86_64__ #ifdef __x86_64__
#define __NR_perf_counter_open 295 #define __NR_perf_counter_open 295
#define rmb() asm volatile("lfence" ::: "memory") #define rmb() asm volatile("lfence" ::: "memory")
...@@ -146,17 +142,8 @@ asmlinkage int sys_perf_counter_open( ...@@ -146,17 +142,8 @@ asmlinkage int sys_perf_counter_open(
int group_fd, int group_fd,
unsigned long flags) unsigned long flags)
{ {
int ret; return syscall(
ret = syscall(
__NR_perf_counter_open, hw_event_uptr, pid, cpu, group_fd, flags); __NR_perf_counter_open, hw_event_uptr, pid, cpu, group_fd, flags);
#if defined(__x86_64__) || defined(__i386__)
if (ret < 0 && ret > -4096) {
errno = -ret;
ret = -1;
}
#endif
return ret;
} }
#define MAX_COUNTERS 64 #define MAX_COUNTERS 64
...@@ -170,7 +157,7 @@ static int system_wide = 0; ...@@ -170,7 +157,7 @@ static int system_wide = 0;
static int nr_counters = 0; static int nr_counters = 0;
static __u64 event_id[MAX_COUNTERS] = { static __u64 event_id[MAX_COUNTERS] = {
EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK), EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK),
EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS), EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES),
EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS), EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS),
EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS), EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS),
...@@ -202,14 +189,15 @@ static int delay_secs = 2; ...@@ -202,14 +189,15 @@ static int delay_secs = 2;
static int zero; static int zero;
static int dump_symtab; static int dump_symtab;
static GList *lines;
struct source_line { struct source_line {
uint64_t EIP; uint64_t EIP;
unsigned long count; unsigned long count;
char *line; char *line;
struct source_line *next;
}; };
static struct source_line *lines;
static struct source_line **lines_tail;
const unsigned int default_count[] = { const unsigned int default_count[] = {
1000000, 1000000,
...@@ -519,9 +507,8 @@ int do_perfstat(int argc, char *argv[]) ...@@ -519,9 +507,8 @@ int do_perfstat(int argc, char *argv[])
count += single_count; count += single_count;
} }
if (!PERF_COUNTER_RAW(event_id[counter]) && if (event_id[counter] == EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_CLOCK) ||
(event_id[counter] == PERF_COUNT_CPU_CLOCK || event_id[counter] == EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK)) {
event_id[counter] == PERF_COUNT_TASK_CLOCK)) {
double msecs = (double)count / 1000000; double msecs = (double)count / 1000000;
...@@ -531,8 +518,6 @@ int do_perfstat(int argc, char *argv[]) ...@@ -531,8 +518,6 @@ int do_perfstat(int argc, char *argv[])
fprintf(stderr, " %14Ld %-20s (events)\n", fprintf(stderr, " %14Ld %-20s (events)\n",
count, event_name(counter)); count, event_name(counter));
} }
if (!counter)
fprintf(stderr, "\n");
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");
fprintf(stderr, " Wall-clock time elapsed: %12.6f msecs\n", fprintf(stderr, " Wall-clock time elapsed: %12.6f msecs\n",
...@@ -554,7 +539,7 @@ struct sym_entry { ...@@ -554,7 +539,7 @@ struct sym_entry {
char *sym; char *sym;
unsigned long count[MAX_COUNTERS]; unsigned long count[MAX_COUNTERS];
int skip; int skip;
GList *source; struct source_line *source;
}; };
#define MAX_SYMS 100000 #define MAX_SYMS 100000
...@@ -855,6 +840,7 @@ static void parse_vmlinux(char *filename) ...@@ -855,6 +840,7 @@ static void parse_vmlinux(char *filename)
if (!file) if (!file)
return; return;
lines_tail = &lines;
while (!feof(file)) { while (!feof(file)) {
struct source_line *src; struct source_line *src;
size_t dummy = 0; size_t dummy = 0;
...@@ -873,7 +859,9 @@ static void parse_vmlinux(char *filename) ...@@ -873,7 +859,9 @@ static void parse_vmlinux(char *filename)
if (c) if (c)
*c = 0; *c = 0;
lines = g_list_prepend(lines, src); src->next = NULL;
*lines_tail = src;
lines_tail = &src->next;
if (strlen(src->line)>8 && src->line[8] == ':') if (strlen(src->line)>8 && src->line[8] == ':')
src->EIP = strtoull(src->line, NULL, 16); src->EIP = strtoull(src->line, NULL, 16);
...@@ -881,52 +869,43 @@ static void parse_vmlinux(char *filename) ...@@ -881,52 +869,43 @@ static void parse_vmlinux(char *filename)
src->EIP = strtoull(src->line, NULL, 16); src->EIP = strtoull(src->line, NULL, 16);
} }
pclose(file); pclose(file);
lines = g_list_reverse(lines);
} }
static void record_precise_ip(uint64_t ip) static void record_precise_ip(uint64_t ip)
{ {
struct source_line *line; struct source_line *line;
GList *item;
item = g_list_first(lines); for (line = lines; line; line = line->next) {
while (item) {
line = item->data;
if (line->EIP == ip) if (line->EIP == ip)
line->count++; line->count++;
if (line->EIP > ip) if (line->EIP > ip)
break; break;
item = g_list_next(item);
} }
} }
static void lookup_sym_in_vmlinux(struct sym_entry *sym) static void lookup_sym_in_vmlinux(struct sym_entry *sym)
{ {
struct source_line *line; struct source_line *line;
GList *item;
char pattern[PATH_MAX]; char pattern[PATH_MAX];
sprintf(pattern, "<%s>:", sym->sym); sprintf(pattern, "<%s>:", sym->sym);
item = g_list_first(lines); for (line = lines; line; line = line->next) {
while (item) {
line = item->data;
if (strstr(line->line, pattern)) { if (strstr(line->line, pattern)) {
sym->source = item; sym->source = line;
break; break;
} }
item = g_list_next(item);
} }
} }
void show_lines(GList *item_queue, int item_queue_count) static void show_lines(struct source_line *line_queue, int line_queue_count)
{ {
int i; int i;
struct source_line *line; struct source_line *line;
for (i = 0; i < item_queue_count; i++) { line = line_queue;
line = item_queue->data; for (i = 0; i < line_queue_count; i++) {
printf("%8li\t%s\n", line->count, line->line); printf("%8li\t%s\n", line->count, line->line);
item_queue = g_list_next(item_queue); line = line->next;
} }
} }
...@@ -935,10 +914,9 @@ void show_lines(GList *item_queue, int item_queue_count) ...@@ -935,10 +914,9 @@ void show_lines(GList *item_queue, int item_queue_count)
static void show_details(struct sym_entry *sym) static void show_details(struct sym_entry *sym)
{ {
struct source_line *line; struct source_line *line;
GList *item; struct source_line *line_queue = NULL;
int displayed = 0; int displayed = 0;
GList *item_queue = NULL; int line_queue_count = 0;
int item_queue_count = 0;
if (!sym->source) if (!sym->source)
lookup_sym_in_vmlinux(sym); lookup_sym_in_vmlinux(sym);
...@@ -947,30 +925,29 @@ static void show_details(struct sym_entry *sym) ...@@ -947,30 +925,29 @@ static void show_details(struct sym_entry *sym)
printf("Showing details for %s\n", sym->sym); printf("Showing details for %s\n", sym->sym);
item = sym->source; line = sym->source;
while (item) { while (line) {
line = item->data;
if (displayed && strstr(line->line, ">:")) if (displayed && strstr(line->line, ">:"))
break; break;
if (!item_queue_count) if (!line_queue_count)
item_queue = item; line_queue = line;
item_queue_count ++; line_queue_count ++;
if (line->count >= count_filter) { if (line->count >= count_filter) {
show_lines(item_queue, item_queue_count); show_lines(line_queue, line_queue_count);
item_queue_count = 0; line_queue_count = 0;
item_queue = NULL; line_queue = NULL;
} else if (item_queue_count > TRACE_COUNT) { } else if (line_queue_count > TRACE_COUNT) {
item_queue = g_list_next(item_queue); line_queue = line_queue->next;
item_queue_count --; line_queue_count --;
} }
line->count = 0; line->count = 0;
displayed++; displayed++;
if (displayed > 300) if (displayed > 300)
break; break;
item = g_list_next(item); line = line->next;
} }
} }
...@@ -1201,6 +1178,10 @@ int main(int argc, char *argv[]) ...@@ -1201,6 +1178,10 @@ int main(int argc, char *argv[])
if (tid != -1 || profile_cpu != -1) if (tid != -1 || profile_cpu != -1)
nr_cpus = 1; nr_cpus = 1;
parse_symbols();
if (vmlinux && sym_filter_entry)
parse_vmlinux(vmlinux);
for (i = 0; i < nr_cpus; i++) { for (i = 0; i < nr_cpus; i++) {
group_fd = -1; group_fd = -1;
for (counter = 0; counter < nr_counters; counter++) { for (counter = 0; counter < nr_counters; counter++) {
...@@ -1216,15 +1197,16 @@ int main(int argc, char *argv[]) ...@@ -1216,15 +1197,16 @@ int main(int argc, char *argv[])
hw_event.nmi = nmi; hw_event.nmi = nmi;
fd[i][counter] = sys_perf_counter_open(&hw_event, tid, cpu, group_fd, 0); fd[i][counter] = sys_perf_counter_open(&hw_event, tid, cpu, group_fd, 0);
fcntl(fd[i][counter], F_SETFL, O_NONBLOCK);
if (fd[i][counter] < 0) { if (fd[i][counter] < 0) {
int err = errno;
printf("kerneltop error: syscall returned with %d (%s)\n", printf("kerneltop error: syscall returned with %d (%s)\n",
fd[i][counter], strerror(-fd[i][counter])); fd[i][counter], strerror(err));
if (fd[i][counter] == -1) if (err == EPERM)
printf("Are you root?\n"); printf("Are you root?\n");
exit(-1); exit(-1);
} }
assert(fd[i][counter] >= 0); assert(fd[i][counter] >= 0);
fcntl(fd[i][counter], F_SETFL, O_NONBLOCK);
/* /*
* First counter acts as the group leader: * First counter acts as the group leader:
...@@ -1248,10 +1230,6 @@ int main(int argc, char *argv[]) ...@@ -1248,10 +1230,6 @@ int main(int argc, char *argv[])
} }
} }
parse_symbols();
if (vmlinux && sym_filter_entry)
parse_vmlinux(vmlinux);
printf("KernelTop refresh period: %d seconds\n", delay_secs); printf("KernelTop refresh period: %d seconds\n", delay_secs);
last_refresh = time(NULL); last_refresh = time(NULL);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment