2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#include "lint.h"
2N/A#include "thr_uberdata.h"
2N/A
2N/Aconst char *panicstr;
2N/Aulwp_t *panic_thread;
2N/A
2N/Astatic mutex_t assert_lock = DEFAULTMUTEX;
2N/Astatic ulwp_t *assert_thread = NULL;
2N/A
2N/A/*
2N/A * Called from __assert() to set panicstr and panic_thread.
2N/A */
2N/Avoid
2N/A__set_panicstr(const char *msg)
2N/A{
2N/A panicstr = msg;
2N/A panic_thread = __curthread();
2N/A}
2N/A
2N/A/*
2N/A * Called from exit() (atexit function) to give precedence
2N/A * to assertion failures and a core dump over _exit().
2N/A */
2N/Avoid
2N/Agrab_assert_lock()
2N/A{
2N/A (void) _lwp_mutex_lock(&assert_lock);
2N/A}
2N/A
2N/Astatic void
2N/AAbort(const char *msg)
2N/A{
2N/A ulwp_t *self;
2N/A struct sigaction act;
2N/A sigset_t sigmask;
2N/A lwpid_t lwpid;
2N/A
2N/A /* to help with core file debugging */
2N/A panicstr = msg;
2N/A if ((self = __curthread()) != NULL) {
2N/A panic_thread = self;
2N/A lwpid = self->ul_lwpid;
2N/A } else {
2N/A lwpid = _lwp_self();
2N/A }
2N/A
2N/A /* set SIGABRT signal handler to SIG_DFL w/o grabbing any locks */
2N/A (void) memset(&act, 0, sizeof (act));
2N/A act.sa_sigaction = SIG_DFL;
2N/A (void) __sigaction(SIGABRT, &act, NULL);
2N/A
2N/A /* delete SIGABRT from the signal mask */
2N/A (void) sigemptyset(&sigmask);
2N/A (void) sigaddset(&sigmask, SIGABRT);
2N/A (void) __lwp_sigmask(SIG_UNBLOCK, &sigmask);
2N/A
2N/A (void) _lwp_kill(lwpid, SIGABRT); /* never returns */
2N/A (void) kill(getpid(), SIGABRT); /* if it does, try harder */
2N/A _exit(127);
2N/A}
2N/A
2N/A/*
2N/A * Write a panic message w/o grabbing any locks other than assert_lock.
2N/A * We have no idea what locks are held at this point.
2N/A */
2N/Astatic void
2N/Acommon_panic(const char *head, const char *why)
2N/A{
2N/A char msg[400]; /* no panic() message in the library is this long */
2N/A ulwp_t *self;
2N/A size_t len1, len2;
2N/A
2N/A if ((self = __curthread()) != NULL)
2N/A enter_critical(self);
2N/A (void) _lwp_mutex_lock(&assert_lock);
2N/A
2N/A (void) memset(msg, 0, sizeof (msg));
2N/A (void) strcpy(msg, head);
2N/A len1 = strlen(msg);
2N/A len2 = strlen(why);
2N/A if (len1 + len2 >= sizeof (msg))
2N/A len2 = sizeof (msg) - len1 - 1;
2N/A (void) strncat(msg, why, len2);
2N/A len1 = strlen(msg);
2N/A if (msg[len1 - 1] != '\n')
2N/A msg[len1++] = '\n';
2N/A (void) __write(2, msg, len1);
2N/A Abort(msg);
2N/A}
2N/A
2N/Avoid
2N/Athr_panic(const char *why)
2N/A{
2N/A common_panic("*** libc thread failure: ", why);
2N/A}
2N/A
2N/Avoid
2N/Aaio_panic(const char *why)
2N/A{
2N/A common_panic("*** libc aio system failure: ", why);
2N/A}
2N/A
2N/A/*
2N/A * Utility function for converting a long integer to a string, avoiding stdio.
2N/A * 'base' must be one of 10 or 16
2N/A */
2N/Avoid
2N/Aultos(uint64_t n, int base, char *s)
2N/A{
2N/A char lbuf[24]; /* 64 bits fits in 16 hex digits, 20 decimal */
2N/A char *cp = lbuf;
2N/A
2N/A do {
2N/A *cp++ = "0123456789abcdef"[n%base];
2N/A n /= base;
2N/A } while (n);
2N/A if (base == 16) {
2N/A *s++ = '0';
2N/A *s++ = 'x';
2N/A }
2N/A do {
2N/A *s++ = *--cp;
2N/A } while (cp > lbuf);
2N/A *s = '\0';
2N/A}
2N/A
2N/A/*
2N/A * Report application lock usage error for mutexes and condvars.
2N/A * Not called if _THREAD_ERROR_DETECTION=0.
2N/A * Continue execution if _THREAD_ERROR_DETECTION=1.
2N/A * Dump core if _THREAD_ERROR_DETECTION=2.
2N/A */
2N/Avoid
2N/Alock_error(const mutex_t *mp, const char *who, void *cv, const char *msg)
2N/A{
2N/A mutex_t mcopy;
2N/A char buf[800];
2N/A uberdata_t *udp;
2N/A ulwp_t *self;
2N/A lwpid_t lwpid;
2N/A pid_t pid;
2N/A
2N/A /*
2N/A * Take a snapshot of the mutex before it changes (we hope!).
2N/A * Use memcpy() rather than 'mcopy = *mp' in case mp is unaligned.
2N/A */
2N/A (void) memcpy(&mcopy, mp, sizeof (mcopy));
2N/A
2N/A /* avoid recursion deadlock */
2N/A if ((self = __curthread()) != NULL) {
2N/A if (assert_thread == self)
2N/A _exit(127);
2N/A enter_critical(self);
2N/A (void) _lwp_mutex_lock(&assert_lock);
2N/A assert_thread = self;
2N/A lwpid = self->ul_lwpid;
2N/A udp = self->ul_uberdata;
2N/A pid = udp->pid;
2N/A } else {
2N/A self = NULL;
2N/A (void) _lwp_mutex_lock(&assert_lock);
2N/A lwpid = _lwp_self();
2N/A udp = &__uberdata;
2N/A pid = getpid();
2N/A }
2N/A
2N/A (void) strcpy(buf,
2N/A "\n*** _THREAD_ERROR_DETECTION: lock usage error detected ***\n");
2N/A (void) strcat(buf, who);
2N/A (void) strcat(buf, "(");
2N/A if (cv != NULL) {
2N/A ultos((uint64_t)(uintptr_t)cv, 16, buf + strlen(buf));
2N/A (void) strcat(buf, ", ");
2N/A }
2N/A ultos((uint64_t)(uintptr_t)mp, 16, buf + strlen(buf));
2N/A (void) strcat(buf, ")");
2N/A if (msg != NULL) {
2N/A (void) strcat(buf, ": ");
2N/A (void) strcat(buf, msg);
2N/A } else if (!mutex_held(&mcopy)) {
2N/A (void) strcat(buf, ": calling thread does not own the lock");
2N/A } else if (mcopy.mutex_rcount) {
2N/A (void) strcat(buf, ": mutex rcount = ");
2N/A ultos((uint64_t)mcopy.mutex_rcount, 10, buf + strlen(buf));
2N/A } else {
2N/A (void) strcat(buf, ": calling thread already owns the lock");
2N/A }
2N/A (void) strcat(buf, "\ncalling thread is ");
2N/A ultos((uint64_t)(uintptr_t)self, 16, buf + strlen(buf));
2N/A (void) strcat(buf, " thread-id ");
2N/A ultos((uint64_t)lwpid, 10, buf + strlen(buf));
2N/A if (msg != NULL || mutex_held(&mcopy))
2N/A /* EMPTY */;
2N/A else if (mcopy.mutex_lockw == 0)
2N/A (void) strcat(buf, "\nthe lock is unowned");
2N/A else if (!(mcopy.mutex_type & USYNC_PROCESS)) {
2N/A (void) strcat(buf, "\nthe lock owner is ");
2N/A ultos((uint64_t)mcopy.mutex_owner, 16, buf + strlen(buf));
2N/A } else {
2N/A (void) strcat(buf, " in process ");
2N/A ultos((uint64_t)pid, 10, buf + strlen(buf));
2N/A (void) strcat(buf, "\nthe lock owner is ");
2N/A ultos((uint64_t)mcopy.mutex_owner, 16, buf + strlen(buf));
2N/A (void) strcat(buf, " in process ");
2N/A ultos((uint64_t)mcopy.mutex_ownerpid, 10, buf + strlen(buf));
2N/A }
2N/A (void) strcat(buf, "\n\n");
2N/A (void) __write(2, buf, strlen(buf));
2N/A if (udp->uberflags.uf_thread_error_detection >= 2)
2N/A Abort(buf);
2N/A assert_thread = NULL;
2N/A (void) _lwp_mutex_unlock(&assert_lock);
2N/A if (self != NULL)
2N/A exit_critical(self);
2N/A}
2N/A
2N/A/*
2N/A * Report application lock usage error for rwlocks.
2N/A * Not called if _THREAD_ERROR_DETECTION=0.
2N/A * Continue execution if _THREAD_ERROR_DETECTION=1.
2N/A * Dump core if _THREAD_ERROR_DETECTION=2.
2N/A */
2N/Avoid
2N/Arwlock_error(const rwlock_t *rp, const char *who, const char *msg)
2N/A{
2N/A rwlock_t rcopy;
2N/A uint32_t rwstate;
2N/A char buf[800];
2N/A uberdata_t *udp;
2N/A ulwp_t *self;
2N/A lwpid_t lwpid;
2N/A pid_t pid;
2N/A int process;
2N/A
2N/A /*
2N/A * Take a snapshot of the rwlock before it changes (we hope!).
2N/A * Use memcpy() rather than 'rcopy = *rp' in case rp is unaligned.
2N/A */
2N/A (void) memcpy(&rcopy, rp, sizeof (rcopy));
2N/A
2N/A /* avoid recursion deadlock */
2N/A if ((self = __curthread()) != NULL) {
2N/A if (assert_thread == self)
2N/A _exit(127);
2N/A enter_critical(self);
2N/A (void) _lwp_mutex_lock(&assert_lock);
2N/A assert_thread = self;
2N/A lwpid = self->ul_lwpid;
2N/A udp = self->ul_uberdata;
2N/A pid = udp->pid;
2N/A } else {
2N/A self = NULL;
2N/A (void) _lwp_mutex_lock(&assert_lock);
2N/A lwpid = _lwp_self();
2N/A udp = &__uberdata;
2N/A pid = getpid();
2N/A }
2N/A
2N/A rwstate = (uint32_t)rcopy.rwlock_readers;
2N/A process = (rcopy.rwlock_type & USYNC_PROCESS);
2N/A
2N/A (void) strcpy(buf,
2N/A "\n*** _THREAD_ERROR_DETECTION: lock usage error detected ***\n");
2N/A (void) strcat(buf, who);
2N/A (void) strcat(buf, "(");
2N/A ultos((uint64_t)(uintptr_t)rp, 16, buf + strlen(buf));
2N/A (void) strcat(buf, "): ");
2N/A (void) strcat(buf, msg);
2N/A (void) strcat(buf, "\ncalling thread is ");
2N/A ultos((uint64_t)(uintptr_t)self, 16, buf + strlen(buf));
2N/A (void) strcat(buf, " thread-id ");
2N/A ultos((uint64_t)lwpid, 10, buf + strlen(buf));
2N/A if (process) {
2N/A (void) strcat(buf, " in process ");
2N/A ultos((uint64_t)pid, 10, buf + strlen(buf));
2N/A }
2N/A if (rwstate & URW_WRITE_LOCKED) {
2N/A (void) strcat(buf, "\nthe writer lock owner is ");
2N/A ultos((uint64_t)rcopy.rwlock_owner, 16,
2N/A buf + strlen(buf));
2N/A if (process) {
2N/A (void) strcat(buf, " in process ");
2N/A ultos((uint64_t)rcopy.rwlock_ownerpid, 10,
2N/A buf + strlen(buf));
2N/A }
2N/A } else if (rwstate & URW_READERS_MASK) {
2N/A (void) strcat(buf, "\nthe reader lock is held by ");
2N/A ultos((uint64_t)(rwstate & URW_READERS_MASK), 10,
2N/A buf + strlen(buf));
2N/A (void) strcat(buf, " readers");
2N/A } else {
2N/A (void) strcat(buf, "\nthe lock is unowned");
2N/A }
2N/A if (rwstate & URW_HAS_WAITERS)
2N/A (void) strcat(buf, "\nand the lock appears to have waiters");
2N/A (void) strcat(buf, "\n\n");
2N/A (void) __write(2, buf, strlen(buf));
2N/A if (udp->uberflags.uf_thread_error_detection >= 2)
2N/A Abort(buf);
2N/A assert_thread = NULL;
2N/A (void) _lwp_mutex_unlock(&assert_lock);
2N/A if (self != NULL)
2N/A exit_critical(self);
2N/A}
2N/A
2N/A/*
2N/A * Report a thread usage error.
2N/A * Not called if _THREAD_ERROR_DETECTION=0.
2N/A * Writes message and continues execution if _THREAD_ERROR_DETECTION=1.
2N/A * Writes message and dumps core if _THREAD_ERROR_DETECTION=2.
2N/A */
2N/Avoid
2N/Athread_error(const char *msg)
2N/A{
2N/A char buf[800];
2N/A uberdata_t *udp;
2N/A ulwp_t *self;
2N/A lwpid_t lwpid;
2N/A
2N/A /* avoid recursion deadlock */
2N/A if ((self = __curthread()) != NULL) {
2N/A if (assert_thread == self)
2N/A _exit(127);
2N/A enter_critical(self);
2N/A (void) _lwp_mutex_lock(&assert_lock);
2N/A assert_thread = self;
2N/A lwpid = self->ul_lwpid;
2N/A udp = self->ul_uberdata;
2N/A } else {
2N/A self = NULL;
2N/A (void) _lwp_mutex_lock(&assert_lock);
2N/A lwpid = _lwp_self();
2N/A udp = &__uberdata;
2N/A }
2N/A
2N/A (void) strcpy(buf, "\n*** _THREAD_ERROR_DETECTION: "
2N/A "thread usage error detected ***\n*** ");
2N/A (void) strcat(buf, msg);
2N/A
2N/A (void) strcat(buf, "\n*** calling thread is ");
2N/A ultos((uint64_t)(uintptr_t)self, 16, buf + strlen(buf));
2N/A (void) strcat(buf, " thread-id ");
2N/A ultos((uint64_t)lwpid, 10, buf + strlen(buf));
2N/A (void) strcat(buf, "\n\n");
2N/A (void) __write(2, buf, strlen(buf));
2N/A if (udp->uberflags.uf_thread_error_detection >= 2)
2N/A Abort(buf);
2N/A assert_thread = NULL;
2N/A (void) _lwp_mutex_unlock(&assert_lock);
2N/A if (self != NULL)
2N/A exit_critical(self);
2N/A}
2N/A
2N/A/*
2N/A * We use __assfail() because the libc __assert() calls
2N/A * gettext() which calls malloc() which grabs a mutex.
2N/A * We do everything without calling standard i/o.
2N/A * assfail() and _assfail() are exported functions;
2N/A * __assfail() is private to libc.
2N/A */
2N/A#pragma weak _assfail = __assfail
2N/Avoid
2N/A__assfail(const char *assertion, const char *filename, int line_num)
2N/A{
2N/A char buf[800]; /* no assert() message in the library is this long */
2N/A ulwp_t *self;
2N/A lwpid_t lwpid;
2N/A
2N/A /* avoid recursion deadlock */
2N/A if ((self = __curthread()) != NULL) {
2N/A if (assert_thread == self)
2N/A _exit(127);
2N/A enter_critical(self);
2N/A (void) _lwp_mutex_lock(&assert_lock);
2N/A assert_thread = self;
2N/A lwpid = self->ul_lwpid;
2N/A } else {
2N/A self = NULL;
2N/A (void) _lwp_mutex_lock(&assert_lock);
2N/A lwpid = _lwp_self();
2N/A }
2N/A
2N/A (void) strcpy(buf, "assertion failed for thread ");
2N/A ultos((uint64_t)(uintptr_t)self, 16, buf + strlen(buf));
2N/A (void) strcat(buf, ", thread-id ");
2N/A ultos((uint64_t)lwpid, 10, buf + strlen(buf));
2N/A (void) strcat(buf, ": ");
2N/A (void) strcat(buf, assertion);
2N/A (void) strcat(buf, ", file ");
2N/A (void) strcat(buf, filename);
2N/A (void) strcat(buf, ", line ");
2N/A ultos((uint64_t)line_num, 10, buf + strlen(buf));
2N/A (void) strcat(buf, "\n");
2N/A (void) __write(2, buf, strlen(buf));
2N/A /*
2N/A * We could replace the call to Abort() with the following code
2N/A * if we want just to issue a warning message and not die.
2N/A * assert_thread = NULL;
2N/A * _lwp_mutex_unlock(&assert_lock);
2N/A * if (self != NULL)
2N/A * exit_critical(self);
2N/A */
2N/A Abort(buf);
2N/A}
2N/A
2N/A/*
2N/A * We define and export this version of assfail() just because libaio
2N/A * used to define and export it, needlessly. Now that libaio is folded
2N/A * into libc, we need to continue this for ABI/version reasons.
2N/A * We don't use "#pragma weak assfail __assfail" in order to avoid
2N/A * warnings from the check_fnames utility at build time for libraries
2N/A * that define their own version of assfail().
2N/A */
2N/Avoid
2N/Aassfail(const char *assertion, const char *filename, int line_num)
2N/A{
2N/A __assfail(assertion, filename, line_num);
2N/A}