mirror of https://gitee.com/openkylin/linux.git
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: Paul Mackerras <paulus@samba.org> Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Acked-by: Mike 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: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
parent
81cdbe0509
commit
cbe46555dc
|
@ -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 @@
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue