/* * readprofile(1) implementation. * (C) 2004 William Irwin, Oracle * Licensed under GPL, or any DFSG-free license of the user's choice. */ #include #include #include #include #include #include #include #include #include #include #include struct sym { unsigned long long vaddr; char name[64]; unsigned long hits; }; struct profile_state { int fd, shift; uint32_t *buf; size_t bufsz; struct sym syms[2], *last, *this; unsigned long long stext, vaddr; unsigned long total; char type, sym[64]; }; static int digits(void) { static int ret = 0; unsigned long n = ULONG_MAX; if (ret) return ret; for (n = ULONG_MAX; n >= 10; n /= 10) ++ret; ret += !!n; return ret; } static void shift_syms(struct sym *syms, struct sym **last, struct sym **this) { *last = *this; *this = &syms[!(*this - syms)]; } static int is_text(char c) { return c == 'T' || c == 't' || c == 'W' || c == 'w'; } static long profile_off(unsigned long long vaddr, struct profile_state *state) { return (((vaddr - state->stext) >> state->shift) + 1)*sizeof(uint32_t); } static int state_transition(struct profile_state *state) { int ret = 0; long page_mask, start, end; unsigned off; if (!state->stext) { if (!strcmp(state->sym, "_stext")) state->stext = state->vaddr; goto out; } else if ((!state->last || !state->this) && !is_text(state->type)) { ret = 1; goto out; } shift_syms(state->syms, &state->last, &state->this); state->this->vaddr = state->vaddr; state->this->hits = 0; if (is_text(state->type)) { strcpy(state->this->name, state->sym); if (!state->last) goto out; } else { strcpy(state->this->name, "*unknown*"); ret = 1; } start = profile_off(state->last->vaddr, state); end = profile_off(state->this->vaddr, state) + !!state->shift*sizeof(uint32_t); if (lseek(state->fd, start, SEEK_SET) != start) { fprintf(stderr, "fseek() failed\n"); exit(EXIT_FAILURE); } if (state->bufsz < (size_t)(end - start)) { page_mask = getpagesize() - 1; state->bufsz = (end - start + page_mask) & ~page_mask; free(state->buf); if (!(state->buf = malloc(state->bufsz))) { fprintf(stderr, "out of memory\n"); exit(EXIT_FAILURE); } } if (read(state->fd, state->buf, end - start) == end - start) { for (off = 0; off < (end - start)/sizeof(uint32_t); ++off) state->last->hits += state->buf[off]; } else { ret = 1; strcpy(state->last->name, "*unknown*"); } if (state->last->hits) printf("%*lu %s\n", digits(), state->last->hits, state->last->name); state->total += state->last->hits; out: return ret; } static int readprofile(FILE *system_map, int profile_buffer) { char *buf = NULL; ssize_t nchar, bufsz; uint32_t step; struct profile_state state = { .fd = profile_buffer, .last = NULL, .this = NULL, .total = 0, .stext = 0, }; if (read(profile_buffer, &step, sizeof(uint32_t)) != sizeof(uint32_t)) { fprintf(stderr, "read() failed\n"); return EXIT_FAILURE; } state.shift = ffs(step) - 1; if (!(state.buf = malloc(getpagesize()))) { fprintf(stderr, "out of memory\n"); return EXIT_FAILURE; } state.bufsz = getpagesize(); while ((nchar = getline(&buf, &bufsz, system_map)) > 0) { if (sscanf(buf, "%Lx %c %63s", &state.vaddr, &state.type, state.sym) != 3) continue; if (state_transition(&state)) break; } printf("%*lu total\n", digits(), state.total); if (state.buf) free(state.buf); return 0; } static FILE *open_system_map(void) { struct utsname buf; char s[256]; if (!access("/boot/System.map", R_OK)) return fopen("/boot/System.map", "r"); if (uname(&buf)) return NULL; snprintf(s, sizeof(s), "/boot/System.map-%s", buf.release); return fopen(s, "r"); } int main(int argc, char * const argv[]) { FILE *system_map = NULL; int c, ret, profile_buffer = -1; static const char optstr[] = "m:p:"; while ((c = getopt(argc, argv, optstr)) != EOF) { switch (c) { case 'm': if (!strcmp(optarg, "-")) system_map = fdopen(STDIN_FILENO, "r"); else if (!access(optarg, R_OK)) system_map = fopen(optarg, "r"); else { fprintf(stderr, "mapfile %s is " "inaccessible\n", optarg); exit(EXIT_FAILURE); } break; case 'p': if (!strcmp(optarg, "-")) profile_buffer = STDIN_FILENO; else if (!access(optarg, R_OK)) profile_buffer = open(optarg, O_RDONLY); else { fprintf(stderr, "profile %s is " "inaccessible\n", optarg); exit(EXIT_FAILURE); } break; case '?': default: fprintf(stderr, "usage: %s [ -m mapfile ] " "[ -p profile ]\n", argv[0]); exit(EXIT_FAILURE); } } if (!system_map) system_map = open_system_map(); if (profile_buffer < 0) profile_buffer = open("/proc/profile", O_RDONLY); ret = readprofile(system_map, profile_buffer); fclose(system_map); close(profile_buffer); return ret; }