/* * kmonparse by Davide Libenzi (linux kernel kmon parser) * Version 0.12 - Copyright (C) 2007 Davide Libenzi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Davide Libenzi * * Assuming you have your kmon-patched kernel tree in $LNXPATH, to * build kmonparse simply run: * * gcc -I $LNXPATH/include/ -o kmonparse kmonparse.c * */ #include #include #include #include #include #include #include #include #include #include #include #define READ_BUFSIZ (1024 * 128) #define KMON_HDRSIZE 11 static unsigned long tscale = 1000; static u_int16_t get_16(const unsigned char *p) { return ((u_int16_t) p[0]) | (((u_int16_t) p[1]) << 8); } static u_int32_t get_32(const unsigned char *p) { return ((u_int32_t) p[0]) | (((u_int32_t) p[1]) << 8) | (((u_int32_t) p[2]) << 16) | (((u_int32_t) p[3]) << 24); } static u_int64_t get_64(const unsigned char *p) { return ((u_int64_t) p[0]) | (((u_int64_t) p[1]) << 8) | (((u_int64_t) p[2]) << 16) | (((u_int64_t) p[3]) << 24) | (((u_int64_t) p[4]) << 32) | (((u_int64_t) p[5]) << 40) | (((u_int64_t) p[6]) << 48) | (((u_int64_t) p[7]) << 56); } static void record_parse(const unsigned char *buf, size_t count, FILE *outf) { u_int16_t pid, pid2; switch (buf[0]) { case KMON_DT_TSKQUEUE: pid = get_16(buf + 1); fprintf(outf, "op=QUE,pid=%u\n", (unsigned int) pid); break; case KMON_DT_TSKDEQUEUE: pid = get_16(buf + 1); fprintf(outf, "op=DEQ,pid=%u\n", (unsigned int) pid); break; case KMON_DT_TSKSWITCH: pid = get_16(buf + 1); pid2 = get_16(buf + 3); fprintf(outf, "op=SWC,prev=%u,next=%u\n", (unsigned int) pid, (unsigned int) pid2); break; case KMON_DT_TSKWAKEUP: pid = get_16(buf + 1); pid2 = get_16(buf + 3); fprintf(outf, "op=WAK,from=%u,to=%u\n", (unsigned int) pid, (unsigned int) pid2); break; default: fprintf(outf, "(unknown record %u)\n", (unsigned int) buf[0]); } } static void buffer_process(const unsigned char *buf, size_t count, size_t *rem, FILE *outf) { size_t i; u_int16_t cpu, rsize; u_int64_t nstime; static u_int64_t tbase; for (i = 0; i < count;) { if (count - i < KMON_HDRSIZE) break; cpu = buf[i]; nstime = get_64(buf + i + 1); rsize = get_16(buf + i + 9); if (count - i < KMON_HDRSIZE + rsize) break; if (tbase == 0) tbase = nstime; if (nstime < tbase) fprintf(stderr, "backward time: base=%llu curr=%llu\n", (unsigned long long) tbase, (unsigned long long) nstime); nstime = (nstime - tbase) / tscale; fprintf(outf, "cpu=%u,ts=%llu,", (unsigned int) cpu, (unsigned long long) nstime); record_parse(buf + i + KMON_HDRSIZE, rsize, outf); i += rsize + KMON_HDRSIZE; } *rem = count - i; } static void usage(const char *prg) { fprintf(stderr, "use: %s [-i INFILE] [-o OUTFIL] [-s TSCALE (%llu)] [-h]\n", prg, tscale); exit(99); } int main(int ac, char **av) { int i, ifd = 0; ssize_t rdb; size_t size, rem; const char *infile = NULL, *outfile = NULL; FILE *outf = stdout; void *rdbuf; while ((i = getopt(ac, av, "o:i:s:h")) != -1) { switch (i) { case 'i': infile = optarg; break; case 'o': outfile = optarg; break; case 's': tscale = atol(optarg); break; case 'h': usage(av[0]); } } if ((rdbuf = malloc(READ_BUFSIZ)) == NULL) { perror("allocating read buffer"); return 2; } if (infile != NULL && (ifd = open(infile, O_RDONLY)) == -1) { perror(infile); return 3; } if (outfile != NULL && (outf = fopen(outfile, "w")) == NULL) { perror(outfile); return 4; } fprintf(outf, "KMON log v1.0\n"); for (size = READ_BUFSIZ, rem = 0;;) { size = READ_BUFSIZ - rem; rdb = read(ifd, rdbuf + rem, size); if (rdb == -1) { perror("reading kmon log data"); break; } if (rdb == 0) break; size = rem + rdb; buffer_process(rdbuf, size, &rem, outf); if (rem) memmove(rdbuf, rdbuf + size - rem, rem); } if (infile != NULL) close(ifd); if (outfile != NULL) fclose(outf); return 0; }