diff --git a/usr.bin/tpfmt/Makefile b/usr.bin/tpfmt/Makefile new file mode 100644 index 000000000000..3b9743f97593 --- /dev/null +++ b/usr.bin/tpfmt/Makefile @@ -0,0 +1,15 @@ +# $NetBSD: Makefile,v 1.1 2010/11/23 20:48:40 yamt Exp $ + +PROG= tpfmt +NOMAN= + +CPPFLAGS+= -I${NETBSDSRCDIR}/sys/ +SRCS= tpfmt.c sym.c + +LDADD+= -lpthread +LDADD+= -lelf +DPADD+= ${LIBPTHREAD} +DPADD+= ${LIBELF} + +.include +.include diff --git a/usr.bin/tpfmt/README b/usr.bin/tpfmt/README new file mode 100644 index 000000000000..da0f9f99db70 --- /dev/null +++ b/usr.bin/tpfmt/README @@ -0,0 +1,3 @@ +$Id: README,v 1.1 2010/11/23 20:48:40 yamt Exp $ + +it's a C-version of tpfmt.sh. see usr.sbin/tprof/README for the usage. diff --git a/usr.bin/tpfmt/sym.c b/usr.bin/tpfmt/sym.c new file mode 100644 index 000000000000..57fd6b48f0f6 --- /dev/null +++ b/usr.bin/tpfmt/sym.c @@ -0,0 +1,155 @@ +/* $NetBSD: sym.c,v 1.1 2010/11/23 20:48:40 yamt Exp $ */ + +/*- + * Copyright (c)2010 YAMAMOTO Takashi, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: sym.c,v 1.1 2010/11/23 20:48:40 yamt Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sym.h" + +struct sym { + char *name; + uint64_t value; + uint64_t size; +}; + +static struct sym **syms = NULL; +static unsigned int nsyms = 0; + +static int +compare_value(const void *p1, const void *p2) +{ + const struct sym *s1 = *(const struct sym * const *)p1; + const struct sym *s2 = *(const struct sym * const *)p2; + + if (s1->value > s2->value) { + return -1; + } else if (s1->value < s2->value) { + return 1; + } + return 0; +} + +void +ksymload(const char *ksyms) +{ + Elf *e; + Elf_Scn *s; + GElf_Shdr sh_store; + GElf_Shdr *sh; + Elf_Data *d; + int fd; + size_t size; + unsigned int i; + + fd = open(ksyms, O_RDONLY); + if (fd == -1) { + err(EXIT_FAILURE, "open"); + } + if (elf_version(EV_CURRENT) == EV_NONE) { + goto elffail; + } + e = elf_begin(fd, ELF_C_READ, NULL); + if (e == NULL) { + goto elffail; + } + for (s = elf_nextscn(e, NULL); s != NULL; s = elf_nextscn(e, s)) { + sh = gelf_getshdr(s, &sh_store); + if (sh == NULL) { + goto elffail; + } + if (sh->sh_type == SHT_SYMTAB) { + break; + } + } + if (s == NULL) { + errx(EXIT_FAILURE, "no symtab"); + } + d = elf_getdata(s, NULL); + if (d == NULL) { + goto elffail; + } + assert(sh->sh_size == d->d_size); + size = sh->sh_size / sh->sh_entsize; + for (i = 1; i < size; i++) { + GElf_Sym st_store; + GElf_Sym *st; + struct sym *sym; + + st = gelf_getsym(d, i, &st_store); + if (st == NULL) { + goto elffail; + } + if (ELF_ST_TYPE(st->st_info) != STT_FUNC) { + continue; + } + sym = malloc(sizeof(*sym)); + sym->name = strdup(elf_strptr(e, sh->sh_link, st->st_name)); + sym->value = (uint64_t)st->st_value; + sym->size = st->st_size; + nsyms++; + syms = realloc(syms, sizeof(*syms) * nsyms); + if (syms == NULL) { + err(EXIT_FAILURE, "realloc"); + } + syms[nsyms - 1] = sym; + } + qsort(syms, nsyms, sizeof(*syms), compare_value); + return; +elffail: + errx(EXIT_FAILURE, "libelf: %s", elf_errmsg(elf_errno())); +} + +const char * +ksymlookup(uint64_t value, uint64_t *offset) +{ + unsigned int i; + + for (i = 0; i < nsyms; i++) { + const struct sym *sym = syms[i]; + + if (sym->value <= value) { + *offset = value - sym->value; + return sym->name; + } + if (sym->size != 0 && sym->value + sym->size < value) { + break; + } + } + return NULL; +} diff --git a/usr.bin/tpfmt/sym.h b/usr.bin/tpfmt/sym.h new file mode 100644 index 000000000000..382476ac4c78 --- /dev/null +++ b/usr.bin/tpfmt/sym.h @@ -0,0 +1,30 @@ +/* $NetBSD: sym.h,v 1.1 2010/11/23 20:48:40 yamt Exp $ */ + +/*- + * Copyright (c)2010 YAMAMOTO Takashi, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +void ksymload(const char *); +const char *ksymlookup(uint64_t, uint64_t *); diff --git a/usr.bin/tpfmt/tpfmt.c b/usr.bin/tpfmt/tpfmt.c new file mode 100644 index 000000000000..bf5e33ad6c62 --- /dev/null +++ b/usr.bin/tpfmt/tpfmt.c @@ -0,0 +1,174 @@ +/* $NetBSD: tpfmt.c,v 1.1 2010/11/23 20:48:40 yamt Exp $ */ + +/*- + * Copyright (c)2010 YAMAMOTO Takashi, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: tpfmt.c,v 1.1 2010/11/23 20:48:40 yamt Exp $"); +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sym.h" + +const char *ksyms = "/dev/ksyms"; + +struct addr { + struct rb_node node; + uint64_t addr; /* address */ + unsigned int nsamples; /* number of samples taken for the address */ +}; + +rb_tree_t addrtree; + +static signed int +addrtree_compare_key(void *ctx, const void *n1, const void *keyp) +{ + const struct addr *a1 = n1; + const uint64_t key = *(const uint64_t *)keyp; + + if (a1->addr > key) { + return 1; + } else if (a1->addr < key) { + return -1; + } + return 0; +} + +static signed int +addrtree_compare_nodes(void *ctx, const void *n1, const void *n2) +{ + const struct addr *a2 = n2; + + return addrtree_compare_key(ctx, n1, &a2->addr); +} + +static const rb_tree_ops_t addrtree_ops = { + .rbto_compare_nodes = addrtree_compare_nodes, + .rbto_compare_key = addrtree_compare_key, +}; + +static int +compare_nsamples(const void *p1, const void *p2) +{ + const struct addr *a1 = *(const struct addr * const *)p1; + const struct addr *a2 = *(const struct addr * const *)p2; + + if (a1->nsamples > a2->nsamples) { + return -1; + } else if (a1->nsamples < a2->nsamples) { + return 1; + } + return 0; +} + +int +main(int argc, char *argv[]) +{ + struct addr *a; + struct addr **l; + struct addr **p; + unsigned int naddrs; + unsigned int i; + + ksymload(ksyms); + rb_tree_init(&addrtree, &addrtree_ops); + + /* + * read and count samples. + */ + + naddrs = 0; + while (/*CONSTCOND*/true) { + struct addr *o; + uintptr_t sample; + size_t n = fread(&sample, sizeof(sample), 1, stdin); + + if (n == 0) { + if (feof(stdin)) { + break; + } + if (ferror(stdin)) { + err(EXIT_FAILURE, "fread"); + } + } + a = malloc(sizeof(*a)); + a->addr = (uint64_t)sample; + a->nsamples = 1; + o = rb_tree_insert_node(&addrtree, a); + if (o != a) { + free(a); + o->nsamples++; + } else { + naddrs++; + } + } + + /* + * sort samples by addresses. + */ + + l = malloc(naddrs * sizeof(*l)); + p = l; + RB_TREE_FOREACH(a, &addrtree) { + *p++ = a; + } + assert(l + naddrs == p); + qsort(l, naddrs, sizeof(*l), compare_nsamples); + + /* + * print addresses and number of samples, preferably with + * resolved symbol names. + */ + + for (i = 0; i < naddrs; i++) { + const char *name; + char buf[100]; + uint64_t offset; + + a = l[i]; + name = ksymlookup(a->addr, &offset); + if (name == NULL) { + snprintf(buf, sizeof(buf), "<%016" PRIx64 ">", a->addr); + name = buf; + } else if (offset != 0) { + snprintf(buf, sizeof(buf), "%s+0x%" PRIx64, name, + offset); + name = buf; + } + printf("%8u %016" PRIx64 " %s\n", a->nsamples, a->addr, name); + } + exit(EXIT_SUCCESS); +}