dcethread-exception.c   [plain text]


/*
 * Copyright (c) 2010 Apple Inc. All rights reserved.
 * Copyright (c) 2008 Likewise Software, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * 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.  Neither the name of Apple Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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.
 *
 * Portions of this software have been released under the following terms:
 *
 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
 *
 * To anyone who acknowledges that this file is provided "AS IS"
 * without any express or implied warranty:
 * permission to use, copy, modify, and distribute this file for any
 * purpose is hereby granted without fee, provided that the above
 * copyright notices and this notice appears in all source code copies,
 * and that none of the names of Open Software Foundation, Inc., Hewlett-
 * Packard Company or Digital Equipment Corporation be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Neither Open Software
 * Foundation, Inc., Hewlett-Packard Company nor Digital
 * Equipment Corporation makes any representations about the suitability
 * of this software for any purpose.
 *
 * Copyright (c) 2007, Novell, Inc. 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.  Neither the name of Novell Inc. nor the names of its contributors
 *     may be used to endorse or promote products derived from this
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

#include <config.h>
#include <string.h>
#include <setjmp.h>
#include <errno.h>
#include <stdio.h>
#ifdef HAVE_EXECINFO_H
#    include <execinfo.h>
#endif
#include <assert.h>

#include "dcethread-exception.h"
#include "dcethread-debug.h"
#include "dcethread-private.h"

static pthread_key_t frame_key;
static void (*uncaught_handler) (dcethread_exc* exc, const char* file, unsigned int line, void* data);
static void* uncaught_handler_data;

dcethread_exc dcethread_uninitexc_e;
dcethread_exc dcethread_exquota_e;
dcethread_exc dcethread_insfmem_e;
dcethread_exc dcethread_nopriv_e;
dcethread_exc dcethread_illaddr_e;
dcethread_exc dcethread_illinstr_e;
dcethread_exc dcethread_resaddr_e;
dcethread_exc dcethread_privinst_e;
dcethread_exc dcethread_resoper_e;
dcethread_exc dcethread_aritherr_e;
dcethread_exc dcethread_intovf_e;
dcethread_exc dcethread_intdiv_e;
dcethread_exc dcethread_fltovf_e;
dcethread_exc dcethread_fltdiv_e;
dcethread_exc dcethread_fltund_e;
dcethread_exc dcethread_decovf_e;
dcethread_exc dcethread_subrng_e;
dcethread_exc dcethread_excpu_e;
dcethread_exc dcethread_exfilsiz_e;
dcethread_exc dcethread_SIGTRAP_e;
dcethread_exc dcethread_SIGIOT_e;
dcethread_exc dcethread_SIGEMT_e;
dcethread_exc dcethread_SIGSYS_e;
dcethread_exc dcethread_SIGPIPE_e;
dcethread_exc dcethread_unksyncsig_e;
dcethread_exc dcethread_interrupt_e;
dcethread_exc dcethread_badparam_e;           /* Bad parameter */
dcethread_exc dcethread_existence_e;          /* Object does not exist */
dcethread_exc dcethread_in_use_e;             /* Object is in use */
dcethread_exc dcethread_use_error_e;          /* Object inappropriate for operation */
dcethread_exc dcethread_nostackmem_e;         /* No memory to allocate stack */
dcethread_exc dcethread_exit_thread_e;        /* Used to terminate a thread */

static void
default_uncaught_handler(dcethread_exc* exc, const char* file,
	unsigned int line, void* data ATTRIBUTE_UNUSED)
{
    if (!dcethread__exc_matches(exc, &dcethread_interrupt_e) &&
        !dcethread__exc_matches(exc, &dcethread_exit_thread_e))
    {
        const char* name = dcethread__exc_getname(exc);
        if (name)
        {
            fprintf(stderr, "%s:%i: uncaught exception %s in thread %p\n", file, line, name, dcethread__self());
        }
        else
        {
            fprintf(stderr, "%s:%i: uncaught exception %p (%i) in thread %p\n",
                    file, line, exc, dcethread__exc_getstatus(exc), dcethread__self());
        }

#ifdef HAVE_BACKTRACE_SYMBOLS_FD
        void* buffer[256];
        int size;

        size = backtrace(buffer, 256);

        fprintf(stderr, "Backtrace:\n");
        backtrace_symbols_fd(buffer, size, fileno(stderr));
#endif
        abort();
    }

    pthread_exit(0);
}

void
dcethread__init_exceptions(void)
{
    pthread_key_create(&frame_key, NULL);
    uncaught_handler = default_uncaught_handler;

    DCETHREAD_EXC_INIT(dcethread_uninitexc_e);
    DCETHREAD_EXC_INIT(dcethread_exquota_e);
    DCETHREAD_EXC_INIT(dcethread_insfmem_e);
    DCETHREAD_EXC_INIT(dcethread_nopriv_e);
    DCETHREAD_EXC_INIT(dcethread_illaddr_e);
    DCETHREAD_EXC_INIT(dcethread_illinstr_e);
    DCETHREAD_EXC_INIT(dcethread_resaddr_e);
    DCETHREAD_EXC_INIT(dcethread_privinst_e);
    DCETHREAD_EXC_INIT(dcethread_resoper_e);
    DCETHREAD_EXC_INIT(dcethread_aritherr_e);
    DCETHREAD_EXC_INIT(dcethread_intovf_e);
    DCETHREAD_EXC_INIT(dcethread_intdiv_e);
    DCETHREAD_EXC_INIT(dcethread_fltovf_e);
    DCETHREAD_EXC_INIT(dcethread_fltdiv_e);
    DCETHREAD_EXC_INIT(dcethread_fltund_e);
    DCETHREAD_EXC_INIT(dcethread_decovf_e);
    DCETHREAD_EXC_INIT(dcethread_subrng_e);
    DCETHREAD_EXC_INIT(dcethread_excpu_e);
    DCETHREAD_EXC_INIT(dcethread_exfilsiz_e);
    DCETHREAD_EXC_INIT(dcethread_SIGTRAP_e);
    DCETHREAD_EXC_INIT(dcethread_SIGIOT_e);
    DCETHREAD_EXC_INIT(dcethread_SIGEMT_e);
    DCETHREAD_EXC_INIT(dcethread_SIGSYS_e);
    DCETHREAD_EXC_INIT(dcethread_SIGPIPE_e);
    DCETHREAD_EXC_INIT(dcethread_unksyncsig_e);
    DCETHREAD_EXC_INIT(dcethread_interrupt_e);
    DCETHREAD_EXC_INIT(dcethread_badparam_e);
    DCETHREAD_EXC_INIT(dcethread_existence_e);
    DCETHREAD_EXC_INIT(dcethread_in_use_e);
    DCETHREAD_EXC_INIT(dcethread_use_error_e);
    DCETHREAD_EXC_INIT(dcethread_nostackmem_e);
    DCETHREAD_EXC_INIT(dcethread_exit_thread_e);
}

void
dcethread__frame_push(dcethread_frame* frame)
{
    dcethread_frame* cur = pthread_getspecific(frame_key);
    void *pframe = (void*)(struct _dcethread_frame*) frame;

    memset(pframe, 0, sizeof(*frame));

    frame->parent = cur;

    pthread_setspecific(frame_key, (void*) frame);
}

void
dcethread__frame_pop(dcethread_frame* frame)
{
    dcethread_frame* cur = pthread_getspecific(frame_key);

    if (cur == frame)
    {
	pthread_setspecific(frame_key, (void*) frame->parent);
    }
    else
    {
	DCETHREAD_ERROR("Attempted to pop exception frame in incorrect order");
    }
}

void
dcethread__exc_init(dcethread_exc* exc, const char* name)
{
    exc->kind = DCETHREAD_EXC_KIND_ADDRESS;
    exc->match.address = exc;
    exc->name = name;
}

void
dcethread__exc_setstatus(dcethread_exc* exc, int value)
{
    exc->kind = DCETHREAD_EXC_KIND_STATUS;
    exc->match.value = value;
}

int
dcethread__exc_getstatus(dcethread_exc* exc)
{
    if (exc->kind == DCETHREAD_EXC_KIND_STATUS)
	return exc->match.value;
    else
	return -1;
}

const char*
dcethread__exc_getname(dcethread_exc* exc)
{
    if (exc->kind == DCETHREAD_EXC_KIND_STATUS)
    {
        return exc->name;
    }
    else
    {
        return ((dcethread_exc*) exc->match.address)->name;
    }
}

int
dcethread__exc_matches(dcethread_exc* exc, dcethread_exc* pattern)
{
    assert (exc != NULL);
    assert (pattern != NULL);

    return (exc->kind == pattern->kind &&
	    (exc->kind == DCETHREAD_EXC_KIND_STATUS ?
	     exc->match.value == pattern->match.value :
	     exc->match.address == pattern->match.address));
}

void
dcethread__exc_raise(dcethread_exc* exc, const char* file, unsigned int line)
{
    dcethread_frame* cur;

    /* Ensure thread system is initialized */
    dcethread__init();

    cur = pthread_getspecific(frame_key);

    if (cur)
    {
        cur->exc = *exc;
        cur->file = file;
        cur->line = line;
        siglongjmp(((struct _dcethread_frame*) cur)->jmpbuf, 1);
    }
    else
    {
        uncaught_handler(exc, file, line, uncaught_handler_data);
        abort();
    }
}

void
dcethread__exc_handle_interrupt(dcethread* thread ATTRIBUTE_UNUSED, void* data)
{
    dcethread__exc_raise((dcethread_exc*) data, NULL, 0);
}

dcethread_exc*
dcethread__exc_from_errno(int err)
{
    switch (err)
    {
    case EINVAL:    return &dcethread_badparam_e;
    case ERANGE:    return &dcethread_badparam_e;
    case EDEADLK:   return &dcethread_in_use_e;
    case EBUSY:     return &dcethread_in_use_e;
    case EAGAIN:    return &dcethread_in_use_e;
    case ENOMEM:    return &dcethread_insfmem_e;
    case EPERM:     return &dcethread_nopriv_e;
    case -1:        return &dcethread_interrupt_e; /* XXX */
    default:        return &dcethread_use_error_e;
    }
}

void
dcethread__exc_set_uncaught_handler(void (*handler) (dcethread_exc*, const char*, unsigned int, void*), void* data)
{
    uncaught_handler = handler;
    uncaught_handler_data = data;
}