Add the lockstat command: displays a summary of kernel locking events
recorded over the lifetime of a called program.
This commit is contained in:
parent
cd9bde5343
commit
297f461929
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: mi,v 1.647 2006/08/26 19:31:53 matt Exp $
|
||||
# $NetBSD: mi,v 1.648 2006/09/07 00:50:45 ad Exp $
|
||||
. base-sys-root
|
||||
./altroot base-sys-root
|
||||
./bin base-sys-root
|
||||
|
@ -1080,6 +1080,7 @@
|
|||
./usr/sbin/linkfarm base-pkgutil-bin
|
||||
./usr/sbin/lmcconfig base-netutil-bin
|
||||
./usr/sbin/lmtp base-obsolete obsolete
|
||||
./usr/sbin/lockstat base-sysutil-bin
|
||||
./usr/sbin/lpc base-lpr-bin
|
||||
./usr/sbin/lpd base-lpr-bin
|
||||
./usr/sbin/lptest base-lpr-bin
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: mi,v 1.926 2006/09/02 23:39:56 wiz Exp $
|
||||
# $NetBSD: mi,v 1.927 2006/09/07 00:50:45 ad Exp $
|
||||
./etc/mtree/set.man man-sys-root
|
||||
./usr/share/info/am-utils.info man-amd-info info
|
||||
./usr/share/info/as.info man-computil-info bfd,info
|
||||
|
@ -1972,6 +1972,7 @@
|
|||
./usr/share/man/cat8/local.0 man-postfix-catman postfix,.cat
|
||||
./usr/share/man/cat8/locate.updatedb.0 man-sysutil-catman .cat
|
||||
./usr/share/man/cat8/lockd.0 man-nfsserver-catman .cat
|
||||
./usr/share/man/cat8/lockstat.0 man-sysutil-catman .cat
|
||||
./usr/share/man/cat8/lpc.0 man-lpr-catman .cat
|
||||
./usr/share/man/cat8/lpd.0 man-lpr-catman .cat
|
||||
./usr/share/man/cat8/luna68k/MAKEDEV.0 man-obsolete obsolete
|
||||
|
@ -4341,6 +4342,7 @@
|
|||
./usr/share/man/man8/local.8 man-postfix-man postfix,.man
|
||||
./usr/share/man/man8/locate.updatedb.8 man-sysutil-man .man
|
||||
./usr/share/man/man8/lockd.8 man-nfsserver-man .man
|
||||
./usr/share/man/man8/lockstat.8 man-sysutil-man .man
|
||||
./usr/share/man/man8/lpc.8 man-lpr-man .man
|
||||
./usr/share/man/man8/lpd.8 man-lpr-man .man
|
||||
./usr/share/man/man8/luna68k/MAKEDEV.8 man-obsolete obsolete
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: Makefile,v 1.218 2006/08/26 17:26:00 matt Exp $
|
||||
# $NetBSD: Makefile,v 1.219 2006/09/07 00:50:45 ad Exp $
|
||||
# from: @(#)Makefile 5.20 (Berkeley) 6/12/93
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
@ -10,7 +10,7 @@ SUBDIR= ac accton altq amd apm apmd arp bad144 bind bootp \
|
|||
envstat eshconfig etcupdate extattrctl fssconfig fwctl gpioctl \
|
||||
grfconfig grfinfo gspa hilinfo ifwatchd inetd installboot \
|
||||
iopctl iostat ipwctl irdaattach isdn iteconfig iwictl\
|
||||
kgmon lastlogin link lmcconfig lpr mailwrapper makefs \
|
||||
kgmon lastlogin link lmcconfig lockstat lpr mailwrapper makefs \
|
||||
map-mbone mdconfig memswitch mlxctl mopd mountd moused \
|
||||
mrinfo mrouted mscdlabel mtrace \
|
||||
mtree ndbootd ndiscvt netgroup_mkdb nfsd ntp pcictl pkg_install pppd \
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# $NetBSD: Makefile,v 1.1 2006/09/07 00:50:45 ad Exp $
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
SRCS= elf32.c elf64.c main.c
|
||||
PROG= lockstat
|
||||
MAN= lockstat.8
|
||||
|
||||
DPADD= ${LIBUTIL}
|
||||
LDADD= -lutil
|
||||
|
||||
elf64.o: elf32.c
|
||||
|
||||
.include <bsd.prog.mk>
|
|
@ -0,0 +1,241 @@
|
|||
/* $NetBSD: elf32.c,v 1.1 2006/09/07 00:50:45 ad Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2006 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Andrew Doran.
|
||||
*
|
||||
* 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the NetBSD
|
||||
* Foundation, Inc. and its contributors.
|
||||
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 1996 Christopher G. Demetriou
|
||||
* 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed for the
|
||||
* NetBSD Project. See http://www.NetBSD.org/ for
|
||||
* information about NetBSD.
|
||||
* 4. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*
|
||||
* <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#if !defined(lint)
|
||||
__RCSID("$NetBSD: elf32.c,v 1.1 2006/09/07 00:50:45 ad Exp $");
|
||||
#endif
|
||||
|
||||
#ifndef ELFSIZE
|
||||
#define ELFSIZE 32
|
||||
#endif
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/exec_elf.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/lockstat.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "extern.h"
|
||||
|
||||
#if (ELFSIZE == 32)
|
||||
#define NAME(x) x##32
|
||||
#elif (ELFSIZE == 64)
|
||||
#define NAME(x) x##64
|
||||
#endif
|
||||
|
||||
static int nsyms;
|
||||
static Elf_Sym *symp;
|
||||
static char *strp;
|
||||
|
||||
int
|
||||
NAME(loadsym)(int fd)
|
||||
{
|
||||
Elf_Shdr symhdr, strhdr;
|
||||
Elf_Ehdr ehdr;
|
||||
size_t sz;
|
||||
off_t off;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Read the ELF header and make sure it's OK.
|
||||
*/
|
||||
if (pread(fd, &ehdr, sizeof(ehdr), 0) != sizeof(ehdr))
|
||||
return -1;
|
||||
|
||||
if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
|
||||
ehdr.e_ident[EI_CLASS] != ELFCLASS)
|
||||
return -1;
|
||||
|
||||
switch (ehdr.e_machine) {
|
||||
ELFDEFNNAME(MACHDEP_ID_CASES)
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the symbol table header, and make sure the binary isn't
|
||||
* stripped.
|
||||
*/
|
||||
off = ehdr.e_shoff;
|
||||
for (i = 0; i < ehdr.e_shnum; i++, off += sizeof(symhdr)) {
|
||||
sz = pread(fd, &symhdr, sizeof(symhdr), off);
|
||||
if (sz != sizeof(symhdr))
|
||||
err(EXIT_FAILURE, "pread (section headers)");
|
||||
if (symhdr.sh_type == SHT_SYMTAB)
|
||||
break;
|
||||
}
|
||||
if (i == ehdr.e_shnum || symhdr.sh_offset == 0)
|
||||
err(EXIT_FAILURE, "namelist is stripped");
|
||||
|
||||
/*
|
||||
* Pull in the string table header, and then read in both the symbol
|
||||
* table and string table proper.
|
||||
*
|
||||
* XXX We can't use mmap(), as /dev/ksyms doesn't support mmap yet.
|
||||
*/
|
||||
off = ehdr.e_shoff + symhdr.sh_link * sizeof(symhdr);
|
||||
if (pread(fd, &strhdr, sizeof(strhdr), off) != sizeof(strhdr))
|
||||
err(EXIT_FAILURE, "pread");
|
||||
|
||||
if ((symp = malloc(symhdr.sh_size)) == NULL)
|
||||
err(EXIT_FAILURE, "malloc (symbol table)");
|
||||
sz = pread(fd, symp, symhdr.sh_size, symhdr.sh_offset );
|
||||
if (sz != symhdr.sh_size)
|
||||
err(EXIT_FAILURE, "pread (symbol table)");
|
||||
|
||||
if ((strp = malloc(strhdr.sh_size)) == NULL)
|
||||
err(EXIT_FAILURE, "malloc (string table)");
|
||||
sz = pread(fd, strp, strhdr.sh_size, strhdr.sh_offset);
|
||||
if (sz != strhdr.sh_size)
|
||||
err(EXIT_FAILURE, "pread (string table)");
|
||||
|
||||
nsyms = (int)(symhdr.sh_size / sizeof(Elf_Sym));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NAME(findsym)(findsym_t find, char *name, uintptr_t *start, uintptr_t *end)
|
||||
{
|
||||
static int lastptr[FIND_MAX];
|
||||
uintptr_t sa, ea;
|
||||
int i, rv;
|
||||
|
||||
rv = -1;
|
||||
|
||||
#ifdef dump_core
|
||||
for (i = lastptr[find];;) {
|
||||
#else
|
||||
for (i = 0; i < nsyms; i++) {
|
||||
#endif
|
||||
switch (find) {
|
||||
case LOCK_BYNAME:
|
||||
if (ELF_ST_TYPE(symp[i].st_info) != STT_OBJECT)
|
||||
break;
|
||||
if (strcmp(&strp[symp[i].st_name], name) != 0)
|
||||
break;
|
||||
*start = (uintptr_t)symp[i].st_value;
|
||||
*end = *start + (uintptr_t)symp[i].st_size;
|
||||
goto found;
|
||||
|
||||
case LOCK_BYADDR:
|
||||
if (ELF_ST_TYPE(symp[i].st_info) != STT_OBJECT)
|
||||
break;
|
||||
if (*start != (uintptr_t)symp[i].st_value)
|
||||
break;
|
||||
strcpy(name, &strp[symp[i].st_name]);
|
||||
goto found;
|
||||
|
||||
case FUNC_BYNAME:
|
||||
if (ELF_ST_TYPE(symp[i].st_info) != STT_FUNC)
|
||||
break;
|
||||
if (strcmp(&strp[symp[i].st_name], name) != 0)
|
||||
break;
|
||||
*start = (uintptr_t)symp[i].st_value;
|
||||
*end = *start + (uintptr_t)symp[i].st_size;
|
||||
goto found;
|
||||
|
||||
case FUNC_BYADDR:
|
||||
if (ELF_ST_TYPE(symp[i].st_info) != STT_FUNC)
|
||||
break;
|
||||
sa = (uintptr_t)symp[i].st_value;
|
||||
ea = sa + (uintptr_t)symp[i].st_size - 1;
|
||||
if (*start < sa || *start > ea)
|
||||
break;
|
||||
sprintf(name, "%s+0x%x",
|
||||
&strp[symp[i].st_name], (int)(*start - sa));
|
||||
goto found;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef dump_core
|
||||
if (++i >= nsyms)
|
||||
i = 0;
|
||||
if (i == lastptr[find])
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
found:
|
||||
lastptr[find] = i;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/* $NetBSD: elf64.c,v 1.1 2006/09/07 00:50:45 ad Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1996 Christopher G. Demetriou
|
||||
* 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed for the
|
||||
* NetBSD Project. See http://www.NetBSD.org/ for
|
||||
* information about NetBSD.
|
||||
* 4. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*
|
||||
* <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#if !defined(lint)
|
||||
__RCSID("$NetBSD: elf64.c,v 1.1 2006/09/07 00:50:45 ad Exp $");
|
||||
#endif /* not lint */
|
||||
|
||||
#define ELFSIZE 64
|
||||
|
||||
#include "elf32.c"
|
|
@ -0,0 +1,54 @@
|
|||
/* $NetBSD: extern.h,v 1.1 2006/09/07 00:50:45 ad Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2006 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Andrew Doran.
|
||||
*
|
||||
* 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the NetBSD
|
||||
* Foundation, Inc. and its contributors.
|
||||
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* elf32.c, elf64.c
|
||||
*/
|
||||
|
||||
typedef enum findsym_enum {
|
||||
LOCK_BYNAME,
|
||||
FUNC_BYNAME,
|
||||
LOCK_BYADDR,
|
||||
FUNC_BYADDR,
|
||||
FIND_MAX
|
||||
} findsym_t;
|
||||
|
||||
int loadsym32(int);
|
||||
int loadsym64(int);
|
||||
int findsym32(findsym_t, char *, uintptr_t *, uintptr_t *);
|
||||
int findsym64(findsym_t, char *, uintptr_t *, uintptr_t *);
|
|
@ -0,0 +1,166 @@
|
|||
.\" $NetBSD: lockstat.8,v 1.1 2006/09/07 00:50:45 ad Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" This code is derived from software contributed to The NetBSD Foundation
|
||||
.\" by Andrew Doran.
|
||||
.\"
|
||||
.\" 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.
|
||||
.\" 3. All advertising materials mentioning features or use of this software
|
||||
.\" must display the following acknowledgement:
|
||||
.\" This product includes software developed by the NetBSD
|
||||
.\" Foundation, Inc. and its contributors.
|
||||
.\" 4. Neither the name of The NetBSD Foundation nor the names of its
|
||||
.\" contributors may be used to endorse or promote products derived
|
||||
.\" from this software without specific prior written permission.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
|
||||
.\"
|
||||
.Dd September 1, 2006
|
||||
.Dt LOCKSTAT 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm lockstat
|
||||
.Nd display kernel locking statistics
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl celpst
|
||||
.Op Fl E Ar event
|
||||
.Op Fl F Ar func
|
||||
.Op Fl L Ar lock
|
||||
.Op Fl N Ar nlist
|
||||
.Op Fl T Ar type
|
||||
.Op Fl b Ar nbuf
|
||||
.Op Fl o Ar file
|
||||
.Ar command ...
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
command enables system wide tracing of kernel lock events, executes
|
||||
the specified command, and when finished reports statistics to the user.
|
||||
.Pp
|
||||
Tracing may be ended early by sending
|
||||
.Dv SIGINT
|
||||
(Ctrl-C) to the process being executed by lockstat.
|
||||
.Pp
|
||||
The
|
||||
.Nm lockstat
|
||||
pseudo-device
|
||||
driver must be present in the kernel, and the
|
||||
.Nm
|
||||
command may only be used by the root user.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width wellhello
|
||||
.It Fl E Ar event
|
||||
Limit tracing to one type of event.
|
||||
Use the
|
||||
.Fl e
|
||||
option to list valid events.
|
||||
.It Fl F Ar func
|
||||
Limit tracing to locking operations performed within the specified function.
|
||||
.Ar func
|
||||
must be the name of a valid function in the kernel.
|
||||
.It Fl L Ar lock
|
||||
Limit tracing to one lock.
|
||||
.Ar lock
|
||||
may either be the name of a lock object in the kernel, or a kernel virtual
|
||||
address.
|
||||
.It Fl N Ar nlist
|
||||
Extract symbol information from the
|
||||
.Ar nlist
|
||||
file.
|
||||
.It Fl T Ar type
|
||||
Limit tracing to one type of lock.
|
||||
Use the
|
||||
.Fl t
|
||||
option to list valid lock types.
|
||||
.It Fl b Ar nbuf
|
||||
Adjust the number of trace buffers allocated by the kernel to
|
||||
.Ar nbuf .
|
||||
.It Fl c
|
||||
Report percentage of total events by count, and sort the output by number
|
||||
of events.
|
||||
The default is to key on event timings.
|
||||
.It Fl e
|
||||
List valid event types for the
|
||||
.Fl E
|
||||
option and exit.
|
||||
.It Fl l
|
||||
Trace only by lock; do not report on calling functions.
|
||||
.It Fl o Ar file
|
||||
Send output to the file named by
|
||||
.Ar file ,
|
||||
instead of the standard output (the default).
|
||||
.It Fl p
|
||||
Show the average number of events and time spent per CPU.
|
||||
The default is to show the total values.
|
||||
May be used in conjunction with the
|
||||
.Fl s
|
||||
option.
|
||||
.It Fl s
|
||||
Show the average number of events per second, and the average time spent per
|
||||
second.
|
||||
The accuracy will naturally increase with longer run times.
|
||||
The default is to show the total values.
|
||||
.It Fl t
|
||||
List valid lock types for the
|
||||
.Fl T
|
||||
option and exit.
|
||||
.El
|
||||
.Sh DIAGNOSTICS
|
||||
.Bl -diag
|
||||
.It "lockstat: incompatible lockstat interface version"
|
||||
.Pp
|
||||
The kernel device driver does not match the version of the
|
||||
.Nm
|
||||
command.
|
||||
.It "lockstat: overflowed available kernel trace buffers"
|
||||
.Pp
|
||||
Increase the number of buffers using the
|
||||
.Fl b
|
||||
option.
|
||||
.It "lockstat: ioctl: Invalid argument"
|
||||
.Pp
|
||||
The number of trace buffers is outside the minimum and maximum
|
||||
bounds set by the kernel.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width /dev/lockstat -compact
|
||||
.It Pa /dev/lockstat
|
||||
.Nm lockstat
|
||||
control device
|
||||
.It Pa /dev/ksyms
|
||||
default namelist
|
||||
.It Pa /netbsd
|
||||
namelist
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr ps 1 ,
|
||||
.Xr pstat 1 ,
|
||||
.Xr systat 1 ,
|
||||
.Xr vmstat 1 ,
|
||||
.Xr iostat 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
command appeared in
|
||||
.Nx 5.0 .
|
|
@ -0,0 +1,697 @@
|
|||
/* $NetBSD: main.c,v 1.1 2006/09/07 00:50:45 ad Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2006 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Andrew Doran.
|
||||
*
|
||||
* 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the NetBSD
|
||||
* Foundation, Inc. and its contributors.
|
||||
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
*
|
||||
* - Need better analysis and tracking of events.
|
||||
* - Should be binary format agnostic, but given that we're likely to be using
|
||||
* ELF for quite a while that's not a big problem.
|
||||
* - Shouldn't have to parse the namelist here. We should use something like
|
||||
* FreeBSD's libelf.
|
||||
* - The way the namelist is searched sucks, is it worth doing something
|
||||
* better?
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: main.c,v 1.1 2006/09/07 00:50:45 ad Exp $");
|
||||
#endif /* not lint */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/lockstat.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <err.h>
|
||||
#include <paths.h>
|
||||
#include <util.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "extern.h"
|
||||
|
||||
#define _PATH_DEV_LOCKSTAT "/dev/lockstat"
|
||||
|
||||
#define MILLI 1000.0
|
||||
#define MICRO 1000000.0
|
||||
#define NANO 1000000000.0
|
||||
#define PICO 1000000000000.0
|
||||
|
||||
TAILQ_HEAD(lock_head, lockstruct);
|
||||
typedef struct lock_head locklist_t;
|
||||
TAILQ_HEAD(buf_head, lsbuf);
|
||||
typedef struct buf_head buflist_t;
|
||||
|
||||
typedef struct lockstruct {
|
||||
TAILQ_ENTRY(lockstruct) chain;
|
||||
buflist_t bufs;
|
||||
uintptr_t lock;
|
||||
double times[LB_NEVENT];
|
||||
uint32_t counts[LB_NEVENT];
|
||||
u_int flags;
|
||||
} lock_t;
|
||||
|
||||
typedef struct name {
|
||||
const char *name;
|
||||
int mask;
|
||||
} name_t;
|
||||
|
||||
const name_t locknames[] = {
|
||||
{ "adaptive_mutex", LB_ADAPTIVE_MUTEX },
|
||||
{ "adaptive_rwlock", LB_ADAPTIVE_RWLOCK },
|
||||
{ "spin_mutex", LB_SPIN_MUTEX },
|
||||
{ "spin_rwlock", LB_SPIN_RWLOCK },
|
||||
{ "lockmgr", LB_LOCKMGR },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
const name_t eventnames[] = {
|
||||
{ "spin", LB_SPIN },
|
||||
{ "sleep", LB_SLEEP },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
const name_t alltypes[] = {
|
||||
{ "Adaptive mutex spin", LB_ADAPTIVE_MUTEX | LB_SPIN },
|
||||
{ "Adaptive mutex sleep", LB_ADAPTIVE_MUTEX | LB_SLEEP },
|
||||
{ "Adaptive RW lock spin", LB_ADAPTIVE_RWLOCK | LB_SPIN },
|
||||
{ "Adaptive RW lock sleep", LB_ADAPTIVE_RWLOCK | LB_SPIN },
|
||||
{ "Spin mutex spin", LB_SPIN_MUTEX | LB_SPIN },
|
||||
{ "Spin RW lock spin", LB_SPIN_RWLOCK | LB_SPIN },
|
||||
{ "lockmgr sleep", LB_LOCKMGR | LB_SLEEP },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
locklist_t locklist[LB_NLOCK];
|
||||
|
||||
lsbuf_t *bufs;
|
||||
lsdisable_t ld;
|
||||
int lflag;
|
||||
int nbufs;
|
||||
int cflag;
|
||||
int lsfd;
|
||||
int displayed;
|
||||
int bin64;
|
||||
double tscale;
|
||||
double cscale;
|
||||
double cpuscale[sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0])];
|
||||
FILE *outfp;
|
||||
|
||||
void findsym(findsym_t, char *, uintptr_t *, uintptr_t *);
|
||||
void spawn(int, char **);
|
||||
void display(int, const char *name);
|
||||
void listnames(const name_t *);
|
||||
int matchname(const name_t *, const char *);
|
||||
void makelists(void);
|
||||
void nullsig(int);
|
||||
void usage(void);
|
||||
void resort(int, int);
|
||||
int ncpu(void);
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int eventtype, locktype, ch, nlfd, sflag, fd, i, pflag;
|
||||
const char *nlistf, *outf;
|
||||
char *lockname, *funcname;
|
||||
const name_t *name;
|
||||
lsenable_t le;
|
||||
double ms;
|
||||
char *p;
|
||||
|
||||
nlistf = NULL;
|
||||
outf = NULL;
|
||||
lockname = NULL;
|
||||
funcname = NULL;
|
||||
eventtype = -1;
|
||||
locktype = -1;
|
||||
nbufs = 0;
|
||||
sflag = 0;
|
||||
pflag = 0;
|
||||
|
||||
while ((ch = getopt(argc, argv, "E:F:L:M:N:T:b:ceflo:pst")) != -1)
|
||||
switch (ch) {
|
||||
case 'E':
|
||||
eventtype = matchname(eventnames, optarg);
|
||||
break;
|
||||
case 'F':
|
||||
funcname = optarg;
|
||||
break;
|
||||
case 'L':
|
||||
lockname = optarg;
|
||||
break;
|
||||
case 'N':
|
||||
nlistf = optarg;
|
||||
break;
|
||||
case 'T':
|
||||
locktype = matchname(locknames, optarg);
|
||||
break;
|
||||
case 'b':
|
||||
nbufs = (int)strtol(optarg, &p, 0);
|
||||
if (!isdigit((u_int)*optarg) || *p != '\0')
|
||||
usage();
|
||||
break;
|
||||
case 'c':
|
||||
cflag = 1;
|
||||
break;
|
||||
case 'e':
|
||||
listnames(eventnames);
|
||||
break;
|
||||
case 'l':
|
||||
lflag = 1;
|
||||
break;
|
||||
case 'o':
|
||||
outf = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
pflag = 1;
|
||||
break;
|
||||
case 's':
|
||||
sflag = 1;
|
||||
break;
|
||||
case 't':
|
||||
listnames(locknames);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (*argv == NULL)
|
||||
usage();
|
||||
|
||||
if (outf) {
|
||||
if ((fd = open(outf, O_WRONLY | O_CREAT, 0600)) == -1)
|
||||
err(EXIT_FAILURE, "opening %s", outf);
|
||||
outfp = fdopen(fd, "w");
|
||||
} else
|
||||
outfp = stdout;
|
||||
|
||||
/*
|
||||
* Find the name list for resolving symbol names, and load it into
|
||||
* memory.
|
||||
*/
|
||||
if (nlistf == NULL) {
|
||||
nlfd = open(_PATH_KSYMS, O_RDONLY);
|
||||
nlistf = getbootfile();
|
||||
} else
|
||||
nlfd = -1;
|
||||
if (nlfd == -1) {
|
||||
if ((nlfd = open(nlistf, O_RDONLY)) < 0)
|
||||
err(EXIT_FAILURE, "cannot open " _PATH_KSYMS " or %s",
|
||||
nlistf);
|
||||
}
|
||||
if (loadsym32(nlfd) != 0) {
|
||||
if (loadsym64(nlfd) != 0)
|
||||
errx(EXIT_FAILURE, "unable to load symbol table");
|
||||
bin64 = 1;
|
||||
}
|
||||
close(nlfd);
|
||||
|
||||
memset(&le, 0, sizeof(le));
|
||||
le.le_nbufs = nbufs;
|
||||
|
||||
/*
|
||||
* Set up initial filtering.
|
||||
*/
|
||||
if (lockname != NULL) {
|
||||
findsym(LOCK_BYNAME, lockname, &le.le_lock, NULL);
|
||||
le.le_flags |= LE_ONE_LOCK;
|
||||
}
|
||||
if (!lflag)
|
||||
le.le_flags |= LE_CALLSITE;
|
||||
if (funcname != NULL) {
|
||||
if (lflag)
|
||||
usage();
|
||||
findsym(FUNC_BYNAME, funcname, &le.le_csstart, &le.le_csend);
|
||||
le.le_flags |= LE_ONE_CALLSITE;
|
||||
}
|
||||
le.le_mask = (eventtype & LB_EVENT_MASK) | (locktype & LB_LOCK_MASK);
|
||||
|
||||
/*
|
||||
* Start tracing.
|
||||
*/
|
||||
if ((lsfd = open(_PATH_DEV_LOCKSTAT, O_RDONLY)) < 0)
|
||||
err(EXIT_FAILURE, "cannot open " _PATH_DEV_LOCKSTAT);
|
||||
if (ioctl(lsfd, IOC_LOCKSTAT_GVERSION, &ch) < 0)
|
||||
err(EXIT_FAILURE, "ioctl");
|
||||
if (ch != LS_VERSION)
|
||||
errx(EXIT_FAILURE, "incompatible lockstat interface version");
|
||||
if (ioctl(lsfd, IOC_LOCKSTAT_ENABLE, &le))
|
||||
err(EXIT_FAILURE, "cannot enable tracing");
|
||||
|
||||
/*
|
||||
* Execute the traced program.
|
||||
*/
|
||||
spawn(argc, argv);
|
||||
|
||||
/*
|
||||
* Stop tracing, and read the trace buffers from the kernel.
|
||||
*/
|
||||
if (ioctl(lsfd, IOC_LOCKSTAT_DISABLE, &ld) == -1) {
|
||||
if (errno == EOVERFLOW) {
|
||||
warnx("overflowed available kernel trace buffers");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
err(EXIT_FAILURE, "cannot disable tracing");
|
||||
}
|
||||
if ((bufs = malloc(ld.ld_size)) == NULL)
|
||||
err(EXIT_FAILURE, "cannot allocate memory for user buffers");
|
||||
if (read(lsfd, bufs, ld.ld_size) != ld.ld_size)
|
||||
err(EXIT_FAILURE, "reading from " _PATH_DEV_LOCKSTAT);
|
||||
if (close(lsfd))
|
||||
err(EXIT_FAILURE, "close(" _PATH_DEV_LOCKSTAT ")");
|
||||
|
||||
/*
|
||||
* Figure out how to scale the results, and build the lists. For
|
||||
* internal use we convert all times from CPU frequency based to
|
||||
* picoseconds, and values are eventually displayed in ms.
|
||||
*/
|
||||
for (i = 0; i < sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0]); i++)
|
||||
if (ld.ld_freq[i] != 0)
|
||||
cpuscale[i] = PICO / ld.ld_freq[i];
|
||||
ms = ld.ld_time.tv_sec * MILLI + ld.ld_time.tv_nsec / MICRO;
|
||||
if (pflag)
|
||||
cscale = 1.0 / ncpu();
|
||||
else
|
||||
cscale = 1.0;
|
||||
cscale *= (sflag ? MILLI / ms : 1.0);
|
||||
tscale = cscale / NANO;
|
||||
nbufs = (int)(ld.ld_size / sizeof(lsbuf_t));
|
||||
makelists();
|
||||
|
||||
/*
|
||||
* Display the results.
|
||||
*/
|
||||
fprintf(outfp, "Elapsed time: %.2f seconds.", ms / MILLI);
|
||||
if (sflag || pflag) {
|
||||
fprintf(outfp, " Displaying ");
|
||||
if (pflag)
|
||||
fprintf(outfp, "per-CPU ");
|
||||
if (sflag)
|
||||
fprintf(outfp, "per-second ");
|
||||
fprintf(outfp, "averages.");
|
||||
}
|
||||
putc('\n', outfp);
|
||||
|
||||
for (name = alltypes; name->name != NULL; name++) {
|
||||
if (eventtype != -1 &&
|
||||
(name->mask & LB_EVENT_MASK) != eventtype)
|
||||
continue;
|
||||
if (locktype != -1 &&
|
||||
(name->mask & LB_LOCK_MASK) != locktype)
|
||||
continue;
|
||||
|
||||
display(name->mask, name->name);
|
||||
}
|
||||
|
||||
if (displayed == 0)
|
||||
fprintf(outfp, "None of the selected events were recorded.\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
|
||||
fprintf(stderr,
|
||||
"%s: usage:\n"
|
||||
"%s [options] <command>\n\n"
|
||||
"-F func\t\tlimit trace to one function\n"
|
||||
"-E evt\t\tdisplay only one type of event\n"
|
||||
"-L lock\t\tlimit trace to one lock (name, or address)\n"
|
||||
"-N nlist\tspecify name list file\n"
|
||||
"-T type\t\tdisplay only one type of lock\n"
|
||||
"-b nbuf\t\tset number of event buffers to allocate\n"
|
||||
"-c\t\treport percentage of total events by count, not time\n"
|
||||
"-e\t\tlist event types\n"
|
||||
"-l\t\ttrace only by lock\n"
|
||||
"-o file\t\tsend output to named file, not stdout\n"
|
||||
"-p\t\tshow average count/time per CPU, not total\n"
|
||||
"-s\t\tshow average count/time per second, not total\n"
|
||||
"-t\t\tlist lock types\n",
|
||||
getprogname(), getprogname());
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void
|
||||
nullsig(int junk)
|
||||
{
|
||||
|
||||
(void)junk;
|
||||
}
|
||||
|
||||
void
|
||||
listnames(const name_t *name)
|
||||
{
|
||||
|
||||
for (; name->name != NULL; name++)
|
||||
printf("%s\n", name->name);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int
|
||||
matchname(const name_t *name, const char *string)
|
||||
{
|
||||
|
||||
for (; name->name != NULL; name++)
|
||||
if (strcasecmp(name->name, string) == 0)
|
||||
return name->mask;
|
||||
|
||||
warnx("unknown type `%s'", string);
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of CPUs in the running system.
|
||||
*/
|
||||
int
|
||||
ncpu(void)
|
||||
{
|
||||
int rv, mib[2];
|
||||
size_t varlen;
|
||||
|
||||
mib[0] = CTL_HW;
|
||||
mib[1] = HW_NCPU;
|
||||
varlen = sizeof(rv);
|
||||
if (sysctl(mib, 2, &rv, &varlen, NULL, (size_t)0) < 0)
|
||||
rv = 1;
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call into the ELF parser and look up a symbol by name or by address.
|
||||
*/
|
||||
void
|
||||
findsym(findsym_t find, char *name, uintptr_t *start, uintptr_t *end)
|
||||
{
|
||||
uintptr_t tend;
|
||||
char *p;
|
||||
int rv;
|
||||
|
||||
if (end == NULL)
|
||||
end = &tend;
|
||||
|
||||
if (find == LOCK_BYNAME) {
|
||||
if (isdigit((u_int)name[0])) {
|
||||
*start = (uintptr_t)strtoul(name, &p, 0);
|
||||
if (*p == '\0')
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (bin64)
|
||||
rv = findsym64(find, name, start, end);
|
||||
else
|
||||
rv = findsym32(find, name, start, end);
|
||||
|
||||
if (find == FUNC_BYNAME || find == LOCK_BYNAME) {
|
||||
if (rv == -1)
|
||||
errx(EXIT_FAILURE, "unable to find symbol `%s'", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rv == -1)
|
||||
sprintf(name, "0x%016lx", (long)*start);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fork off the child process and wait for it to complete. We trap SIGINT
|
||||
* so that the caller can use Ctrl-C to stop tracing early and still get
|
||||
* useful results.
|
||||
*/
|
||||
void
|
||||
spawn(int argc, char **argv)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
switch (pid = fork()) {
|
||||
case 0:
|
||||
close(lsfd);
|
||||
if (execvp(argv[0], argv) == -1)
|
||||
err(EXIT_FAILURE, "cannot exec");
|
||||
break;
|
||||
case -1:
|
||||
err(EXIT_FAILURE, "cannot fork to exec");
|
||||
break;
|
||||
default:
|
||||
signal(SIGINT, nullsig);
|
||||
wait(NULL);
|
||||
signal(SIGINT, SIG_DFL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* From the kernel supplied data, construct two dimensional lists of locks
|
||||
* and event buffers, indexed by lock type.
|
||||
*/
|
||||
void
|
||||
makelists(void)
|
||||
{
|
||||
lsbuf_t *lb, *lb2, *max;
|
||||
int i, type;
|
||||
lock_t *l;
|
||||
|
||||
for (i = 0; i < LB_NLOCK; i++)
|
||||
TAILQ_INIT(&locklist[i]);
|
||||
|
||||
for (lb = bufs, max = bufs + nbufs; lb < max; lb++) {
|
||||
if (lb->lb_flags == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Look for a record descibing this lock, and allocate a
|
||||
* new one if needed.
|
||||
*/
|
||||
type = ((lb->lb_flags & LB_LOCK_MASK) >> LB_LOCK_SHIFT) - 1;
|
||||
TAILQ_FOREACH(l, &locklist[type], chain) {
|
||||
if (l->lock == lb->lb_lock)
|
||||
break;
|
||||
}
|
||||
if (l == NULL) {
|
||||
l = (lock_t *)malloc(sizeof(*l));
|
||||
l->flags = lb->lb_flags;
|
||||
l->lock = lb->lb_lock;
|
||||
memset(&l->counts, 0, sizeof(l->counts));
|
||||
memset(&l->times, 0, sizeof(l->times));
|
||||
TAILQ_INIT(&l->bufs);
|
||||
TAILQ_INSERT_TAIL(&locklist[type], l, chain);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scale the time values per buffer and summarise
|
||||
* times+counts per lock.
|
||||
*/
|
||||
for (i = 0; i < LB_NEVENT; i++) {
|
||||
lb->lb_times[i] *= cpuscale[lb->lb_cpu];
|
||||
l->counts[i] += lb->lb_counts[i];
|
||||
l->times[i] += lb->lb_times[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* Merge same lock+callsite pairs from multiple CPUs
|
||||
* together.
|
||||
*/
|
||||
TAILQ_FOREACH(lb2, &l->bufs, lb_chain.tailq) {
|
||||
if (lb->lb_callsite == lb2->lb_callsite)
|
||||
break;
|
||||
}
|
||||
if (lb2 != NULL) {
|
||||
for (i = 0; i < LB_NEVENT; i++) {
|
||||
lb2->lb_counts[i] += lb->lb_counts[i];
|
||||
lb2->lb_times[i] += lb->lb_times[i];
|
||||
}
|
||||
} else
|
||||
TAILQ_INSERT_HEAD(&l->bufs, lb, lb_chain.tailq);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-sort one list of locks / lock buffers by event type.
|
||||
*/
|
||||
void
|
||||
resort(int type, int event)
|
||||
{
|
||||
lsbuf_t *lb, *lb2;
|
||||
locklist_t llist;
|
||||
buflist_t blist;
|
||||
lock_t *l, *l2;
|
||||
|
||||
TAILQ_INIT(&llist);
|
||||
while ((l = TAILQ_FIRST(&locklist[type])) != NULL) {
|
||||
TAILQ_REMOVE(&locklist[type], l, chain);
|
||||
|
||||
/*
|
||||
* Sort the buffers into the per-lock list.
|
||||
*/
|
||||
TAILQ_INIT(&blist);
|
||||
while ((lb = TAILQ_FIRST(&l->bufs)) != NULL) {
|
||||
TAILQ_REMOVE(&l->bufs, lb, lb_chain.tailq);
|
||||
|
||||
lb2 = TAILQ_FIRST(&blist);
|
||||
while (lb2 != NULL) {
|
||||
if (cflag) {
|
||||
if (lb->lb_counts[event] >
|
||||
lb2->lb_counts[event])
|
||||
break;
|
||||
} else if (lb->lb_times[event] >
|
||||
lb2->lb_times[event])
|
||||
break;
|
||||
lb2 = TAILQ_NEXT(lb2, lb_chain.tailq);
|
||||
}
|
||||
if (lb2 == NULL)
|
||||
TAILQ_INSERT_TAIL(&blist, lb, lb_chain.tailq);
|
||||
else
|
||||
TAILQ_INSERT_BEFORE(lb2, lb, lb_chain.tailq);
|
||||
}
|
||||
l->bufs = blist;
|
||||
|
||||
/*
|
||||
* Sort this lock into the per-type list, based on the
|
||||
* totals per lock.
|
||||
*/
|
||||
l2 = TAILQ_FIRST(&llist);
|
||||
while (l2 != NULL) {
|
||||
if (cflag) {
|
||||
if (l->counts[event] > l2->counts[event])
|
||||
break;
|
||||
} else if (l->times[event] > l2->times[event])
|
||||
break;
|
||||
l2 = TAILQ_NEXT(l2, chain);
|
||||
}
|
||||
if (l2 == NULL)
|
||||
TAILQ_INSERT_TAIL(&llist, l, chain);
|
||||
else
|
||||
TAILQ_INSERT_BEFORE(l2, l, chain);
|
||||
}
|
||||
locklist[type] = llist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Display a summary table for one lock type / event type pair.
|
||||
*/
|
||||
void
|
||||
display(int mask, const char *name)
|
||||
{
|
||||
lock_t *l;
|
||||
lsbuf_t *lb;
|
||||
int event, type;
|
||||
double pcscale, metric;
|
||||
char lname[256], fname[256];
|
||||
|
||||
type = ((mask & LB_LOCK_MASK) >> LB_LOCK_SHIFT) - 1;
|
||||
if (TAILQ_FIRST(&locklist[type]) == NULL)
|
||||
return;
|
||||
|
||||
event = (mask & LB_EVENT_MASK) - 1;
|
||||
resort(type, event);
|
||||
|
||||
fprintf(outfp, "\n-- %s\n\n"
|
||||
"Total%% Count Time/ms Lock Caller\n"
|
||||
"------ ------- --------- ------------------ ----------------------------------\n",
|
||||
name);
|
||||
|
||||
/*
|
||||
* Sum up all events for this type of lock + event.
|
||||
*/
|
||||
pcscale = 0;
|
||||
TAILQ_FOREACH(l, &locklist[type], chain) {
|
||||
if (cflag)
|
||||
pcscale += l->counts[event];
|
||||
else
|
||||
pcscale += l->times[event];
|
||||
displayed++;
|
||||
}
|
||||
if (pcscale == 0)
|
||||
pcscale = 100;
|
||||
else
|
||||
pcscale = (100.0 / pcscale);
|
||||
|
||||
/*
|
||||
* For each lock, print a summary total, followed by a breakdown by
|
||||
* caller.
|
||||
*/
|
||||
TAILQ_FOREACH(l, &locklist[type], chain) {
|
||||
if (cflag)
|
||||
metric = l->counts[event];
|
||||
else
|
||||
metric = l->times[event];
|
||||
metric *= pcscale;
|
||||
|
||||
findsym(LOCK_BYADDR, lname, &l->lock, NULL);
|
||||
|
||||
fprintf(outfp, "%6.2f %7d %9.2f %-18s <all>\n", metric,
|
||||
(int)(l->counts[event] * cscale),
|
||||
l->times[event] * tscale, lname);
|
||||
|
||||
if (lflag)
|
||||
continue;
|
||||
|
||||
TAILQ_FOREACH(lb, &l->bufs, lb_chain.tailq) {
|
||||
if (cflag)
|
||||
metric = lb->lb_counts[event];
|
||||
else
|
||||
metric = lb->lb_times[event];
|
||||
metric *= pcscale;
|
||||
|
||||
findsym(LOCK_BYADDR, lname, &lb->lb_lock, NULL);
|
||||
findsym(FUNC_BYADDR, fname, &lb->lb_callsite, NULL);
|
||||
fprintf(outfp, "%6.2f %7d %9.2f %-18s %s\n", metric,
|
||||
(int)(lb->lb_counts[event] * cscale),
|
||||
lb->lb_times[event] * tscale,
|
||||
lname, fname);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue