/*
 * This is the kernel profiler originally written by Linus,
 * this version is a bit faster and gives sorted output.
 *
 * Before you can use this program you must compile the kernel
 * with profiling enabled (only for Configure, you don't have to
 * compile with -p).
 */

#define __KERNEL__
#include <sys/types.h>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <sys/ioctl.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <sys/stat.h>
#include "ps.h"
#include "psdata.h"

unsigned long prof_len = 0;
unsigned long prof_buffer = 0;
unsigned long jiffies = 0;

unsigned long *buffer;

extern int optind;
extern char *optarg;

char *oldname = NULL;

struct ts {
    int count;
    char *name;
} tbl[1024];

int
sortf(const void *p1, const void *p2)
{
    struct ts *a = (struct ts *) p1;
    struct ts *b = (struct ts *) p2;

    return b->count - a->count;
}

int
main(int argc, char *argv[])
{
    unsigned long total = 0;
    unsigned long sum = 0;
    unsigned long pct;
    unsigned long addr, nextaddr;
    char *fname, *nextname, *lname = NULL;
    int i=0, j, n;

    if (argc > 1)
	lname = argv[1];
    if (open_psdb()) {
	perror("cannot open psdatabase");
	exit(2);
    }
    jiffies = get_kword(k_addr("_jiffies"));
    prof_len = get_kword(k_addr("_prof_len"));
    prof_buffer = get_kword(k_addr("_prof_buffer"));
    buffer = malloc(prof_len * sizeof(unsigned long));
    if (!buffer) {
    	fprintf(stderr,"out of memory\n");
    	exit(1);
    }
    kmemread(buffer, (unsigned long) prof_buffer,
	     prof_len * sizeof(unsigned long));
    fname = first_func(&addr);
    addr >>= 2;
    while (1) {
	nextname = next_func(&nextaddr);
	if (nextname == NULL) {
	    n = prof_len - addr;
	} else {
	    nextaddr >>= 2;
	    n = nextaddr - addr;
	}
	sum = 0;
	if (lname && !strcmp(fname, lname)) {
	    int i;
	    for (i = 0; i < n; ++i)
		printf("%08lx: %5ld\n", (addr + i) << 2, buffer[addr + i]);
	}
	while (--n >= 0)
	    sum += buffer[addr++];
	if (sum) {
	    total += sum;
	    tbl[i].name = fname;
	    tbl[i].count = sum;
	    if (++i == 1024) {
		fprintf(stderr, "too many functions\n");
		break;
	    }
	}
	if (nextname == NULL)
	    break;
	fname = nextname;
	addr = nextaddr;
    }
    qsort(tbl, i, sizeof *tbl, sortf);
    for (j = 0; j < i; ++j) {
	pct = tbl[j].count * 10000 / total;
	printf("%32s %8d %3ld.%02ld\n",
		tbl[j].name,tbl[j].count,pct/100, pct%100);
    }
    /**
    printf("Linux has spent a total of %.2f seconds in kernel mode (%.2f%% of the time since bootup)\n",
	total/100.0,total*100.0/jiffies);
	**/
    exit(0);
}
