/* $NetBSD: pthread_debug.c,v 1.11 2007/01/20 18:57:41 christos Exp $ */ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Nathan J. Williams. * * 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. */ #include __RCSID("$NetBSD: pthread_debug.c,v 1.11 2007/01/20 18:57:41 christos Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include "pthread.h" #include "pthread_int.h" int pthread__dbg; #ifdef PTHREAD__DEBUG int pthread__debug_counters[PTHREADD_NCOUNTERS]; static struct pthread_msgbuf *debugbuf; #define MAXLINELEN 1000 struct linebuf { char buf[MAXLINELEN]; int len; }; static struct linebuf *linebuf; static void pthread__debug_printcounters(void); static const char *pthread__counternames[] = PTHREADD_INITCOUNTERNAMES; extern int pthread__maxconcurrency, pthread__started; void pthread__debug_init(int ncpu) { time_t t; int i; if (getenv("PTHREAD_DEBUGCOUNTERS") != NULL) atexit(pthread__debug_printcounters); if (getenv("PTHREAD_DEBUGLOG") != NULL) { t = time(NULL); debugbuf = pthread__debuglog_init(0); linebuf = malloc(ncpu * sizeof(struct linebuf)); if (linebuf == NULL) err(1, "Couldn't allocate linebuf"); for (i = 0; i < ncpu; i++) linebuf[i].len = 0; DPRINTF(("Started debugging %s (pid %d) at %s\n", getprogname(), getpid(), ctime(&t))); } } static void pthread__debug_printcounters(void) { int i; int counts[PTHREADD_NCOUNTERS]; /* * Copy the counters before printing anything to so that we don't see * the effect of printing the counters. * There will still be one more mutex lock than unlock, because * atexit() handling itself will call us back with a mutex locked. */ for (i = 0; i < PTHREADD_NCOUNTERS; i++) counts[i] = pthread__debug_counters[i]; printf("Pthread event counters\n"); for (i = 0; i < PTHREADD_NCOUNTERS; i++) printf("%2d %-20s %9d\n", i, pthread__counternames[i], counts[i]); printf("\n"); } struct pthread_msgbuf * pthread__debuglog_init(int force) { int debugshmid; void *debuglog; struct pthread_msgbuf* buf; debugshmid = shmget((key_t)PTHREAD__DEBUG_SHMKEY, PTHREAD__DEBUG_SHMSIZE, IPC_CREAT | S_IRWXU | S_IRWXG | S_IRWXO); if (debugshmid == -1) err(1, "Couldn't get shared debug log"); debuglog = shmat(debugshmid, 0, 0); if (debuglog == (void *)-1) err(1, "Couldn't map shared debug log (ID %d)", debugshmid); buf = (struct pthread_msgbuf *)debuglog; if (force || buf->msg_magic != BUF_MAGIC) { /* Initialize */ buf->msg_magic = BUF_MAGIC; buf->msg_bufw = 0; buf->msg_bufr = 0; buf->msg_bufs = PTHREAD__DEBUG_SHMSIZE - sizeof (struct pthread_msgbuf); } return buf; } void pthread__debuglog_printf(const char *fmt, ...) { char *tmpbuf; size_t len, cplen, r, w, s; long diff1, diff2; int vpid; va_list ap; if (debugbuf == NULL || linebuf == NULL) return; #ifdef PTHREAD_SA if (pthread__maxconcurrency > 1) { vpid = pthread_self()->pt_vpid; } else vpid = 0; #else vpid = (int)_lwp_self(); #endif tmpbuf = linebuf[vpid].buf; len = linebuf[vpid].len; #if defined(PTHREAD_PID_DEBUG) || defined(PTHREAD_VP_DEBUG) if (len == 0) { #ifdef PTHREAD_PID_DEBUG len += sprintf(tmpbuf, "[%05d]", getpid()); #endif #ifdef PTHREAD_VP_DEBUG if (pthread__maxconcurrency > 1) len += sprintf(tmpbuf + len, "[%d]", vpid); #endif } #endif va_start(ap, fmt); len += vsnprintf(tmpbuf + len, (unsigned int)(MAXLINELEN - len), fmt, ap); va_end(ap); linebuf[vpid].len = len; if (tmpbuf[len - 1] != '\n') return; r = debugbuf->msg_bufr; w = debugbuf->msg_bufw; s = debugbuf->msg_bufs; diff1 = (long)w - (long)r; if (w + len >= s) { cplen = s - w; debugbuf->msg_bufw = len - cplen; (void)memcpy(&debugbuf->msg_bufc[w], tmpbuf, cplen); (void)memcpy(&debugbuf->msg_bufc[0], tmpbuf + cplen, len - cplen); w = len - cplen; /* Check for lapping the read pointer. */ diff2 = (long)w - (long)r; if (((diff1 < 0) && (diff2 <= 0)) || ((diff1 > 0) && (diff2 >= 0))) debugbuf->msg_bufr = (w + 1) % s; } else { debugbuf->msg_bufw = w + len; (void)memcpy(&debugbuf->msg_bufc[w], tmpbuf, len); w += len; /* Check for lapping the read pointer. */ diff2 = (long)w - (long)r; if ((diff1 < 0) && (diff2 >= 0)) debugbuf->msg_bufr = (w + 1) % s; } linebuf[vpid].len = 0; } int pthread__debuglog_newline(void) { int vpid; if (debugbuf == NULL) return 1; #ifdef PTHREAD_SA if (pthread__maxconcurrency > 1) vpid = pthread_self()->pt_vpid; else vpid = 0; #else vpid = (int)_lwp_self(); #endif return (linebuf[vpid].len == 0); } #endif