Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -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
+45 −67
Original line number Original line Diff line number Diff line
@@ -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 @@
  *   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 @@
#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 @@
#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 @@
#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 @@
#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(
        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;
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;
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[])
			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[])
			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 {
	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)
	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)
		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)
			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)
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)


	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[])
	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[])
			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[])
		}
		}
	}
	}


	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);