root/usr/src/cmd/mdb/common/mdb/mdb_err.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <mdb/mdb_signal.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb.h>

#include <thread_db.h>
#include <rtld_db.h>
#include <libctf.h>
#include <strings.h>
#include <stdlib.h>

static const char *const _mdb_errlist[] = {
        "unknown symbol name",                          /* EMDB_NOSYM */
        "unknown object file name",                     /* EMDB_NOOBJ */
        "no mapping for address",                       /* EMDB_NOMAP */
        "unknown dcmd name",                            /* EMDB_NODCMD */
        "unknown walk name",                            /* EMDB_NOWALK */
        "dcmd name already in use",                     /* EMDB_DCMDEXISTS */
        "walk name already in use",                     /* EMDB_WALKEXISTS */
        "no support for platform",                      /* EMDB_NOPLAT */
        "no process active",                            /* EMDB_NOPROC */
        "specified name is too long",                   /* EMDB_NAME2BIG */
        "specified name contains illegal characters",   /* EMDB_NAMEBAD */
        "failed to allocate needed memory",             /* EMDB_ALLOC */
        "specified module is not loaded",               /* EMDB_NOMOD */
        "cannot unload built-in module",                /* EMDB_BUILTINMOD */
        "no walk is currently active",                  /* EMDB_NOWCB */
        "invalid walk state argument",                  /* EMDB_BADWCB */
        "walker does not accept starting address",      /* EMDB_NOWALKLOC */
        "walker requires starting address",             /* EMDB_NOWALKGLOB */
        "failed to initialize walk",                    /* EMDB_WALKINIT */
        "walker cannot be layered on itself",           /* EMDB_WALKLOOP */
        "i/o stream is read-only",                      /* EMDB_IORO */
        "i/o stream is write-only",                     /* EMDB_IOWO */
        "no symbol corresponds to address",             /* EMDB_NOSYMADDR */
        "unknown disassembler name",                    /* EMDB_NODIS */
        "disassembler name already in use",             /* EMDB_DISEXISTS */
        "no such software event specifier",             /* EMDB_NOSESPEC */
        "no such xdata available",                      /* EMDB_NOXD */
        "xdata name already in use",                    /* EMDB_XDEXISTS */
        "operation not supported by target",            /* EMDB_TGTNOTSUP */
        "target is not open for writing",               /* EMDB_TGTRDONLY */
        "invalid register name",                        /* EMDB_BADREG */
        "no register set available for thread",         /* EMDB_NOREGS */
        "stack address is not properly aligned",        /* EMDB_STKALIGN */
        "no executable file is open",                   /* EMDB_NOEXEC */
        "failed to evaluate command",                   /* EMDB_EVAL */
        "command cancelled by user",                    /* EMDB_CANCEL */
        "only %lu of %lu bytes could be read",          /* EMDB_PARTIAL */
        "dcmd failed",                                  /* EMDB_DCFAIL */
        "improper dcmd usage",                          /* EMDB_DCUSAGE */
        "target error",                                 /* EMDB_TGT */
        "invalid system call number",                   /* EMDB_BADSYSNUM */
        "invalid signal number",                        /* EMDB_BADSIGNUM */
        "invalid fault number",                         /* EMDB_BADFLTNUM */
        "target is currently executing",                /* EMDB_TGTBUSY */
        "target has completed execution",               /* EMDB_TGTZOMB */
        "target is a core file",                        /* EMDB_TGTCORE */
        "debugger lost control of target",              /* EMDB_TGTLOST */
        "libthread_db call failed unexpectedly",        /* EMDB_TDB */
        "failed to dlopen library",                     /* EMDB_RTLD */
        "librtld_db call failed unexpectedly",          /* EMDB_RTLD_DB */
        "runtime linker data not available",            /* EMDB_NORTLD */
        "invalid thread identifier",                    /* EMDB_NOTHREAD */
        "event specifier disabled",                     /* EMDB_SPECDIS */
        "unknown link map id",                          /* EMDB_NOLMID */
        "failed to determine return address",           /* EMDB_NORETADDR */
        "watchpoint size exceeds address space limit",  /* EMDB_WPRANGE */
        "conflict with existing watchpoint",            /* EMDB_WPDUP */
        "address not aligned on an instruction boundary", /* EMDB_BPALIGN */
        "library is missing demangler entry point",     /* EMDB_NODEM */
        "cannot read past current end of file",         /* EMDB_EOF */
        "no symbolic debug information available for module", /* EMDB_NOCTF */
        "libctf call failed unexpectedly",              /* EMDB_CTF */
        "thread local storage has not yet been allocated", /* EMDB_TLS */
        "object does not support thread local storage", /* EMDB_NOTLS */
        "no such member of structure or union",         /* EMDB_CTFNOMEMB */
        "inappropriate context for action",             /* EMDB_CTX */
        "module incompatible with target",              /* EMDB_INCOMPAT */
        "operation not supported by target on this platform",
                                                        /* EMDB_TGTHWNOTSUP */
        "kmdb is not loaded",                           /* EMDB_KINACTIVE */
        "kmdb is loading",                              /* EMDB_KACTIVATING */
        "kmdb is already loaded",                       /* EMDB_KACTIVE */
        "kmdb is unloading",                            /* EMDB_KDEACTIVATING */
        "kmdb could not be loaded",                     /* EMDB_KNOLOAD */
        "boot-loaded kmdb cannot be unloaded",          /* EMDB_KNOUNLOAD */
        "too many enabled watchpoints for this machine", /* EMDB_WPTOOMANY */
        "DTrace is active",                             /* EMDB_DTACTIVE */
        "boot-loaded module cannot be unloaded",        /* EMDB_KMODNOUNLOAD */
        "stack frame pointer is invalid",               /* EMDB_STKFRAME */
        "unexpected short write"                        /* EMDB_SHORTWRITE */

};

static const int _mdb_nerr = sizeof (_mdb_errlist) / sizeof (_mdb_errlist[0]);

static size_t errno_rbytes;     /* EMDB_PARTIAL actual bytes read */
static size_t errno_nbytes;     /* EMDB_PARTIAL total bytes requested */
static int errno_libctf;        /* EMDB_CTF underlying error code */
#ifndef _KMDB
static int errno_rtld_db;       /* EMDB_RTLD_DB underlying error code */
#endif

const char *
mdb_strerror(int err)
{
        static char buf[256];
        const char *str;

        if (err >= EMDB_BASE && (err - EMDB_BASE) < _mdb_nerr)
                str = _mdb_errlist[err - EMDB_BASE];
        else
                str = strerror(err);

        switch (err) {
        case EMDB_PARTIAL:
                (void) mdb_iob_snprintf(buf, sizeof (buf), str,
                    errno_rbytes, errno_nbytes);
                str = buf;
                break;

#ifndef _KMDB
        case EMDB_RTLD_DB:
                if (rd_errstr(errno_rtld_db) != NULL)
                        str = rd_errstr(errno_rtld_db);
                break;
#endif

        case EMDB_CTF:
                if (ctf_errmsg(errno_libctf) != NULL)
                        str = ctf_errmsg(errno_libctf);
                break;
        }

        return (str ? str : "unknown error");
}

void
vwarn(const char *format, va_list alist)
{
        int err = errno;

        mdb_iob_printf(mdb.m_err, "%s: ", mdb.m_pname);
        mdb_iob_vprintf(mdb.m_err, format, alist);

        if (strchr(format, '\n') == NULL)
                mdb_iob_printf(mdb.m_err, ": %s\n", mdb_strerror(err));
}

void
vdie(const char *format, va_list alist)
{
        vwarn(format, alist);
        mdb_destroy();
        exit(1);
}

void
vfail(const char *format, va_list alist)
{
        extern const char *volatile _mdb_abort_str;
        static char buf[256];
        static int nfail;

        if (_mdb_abort_str == NULL) {
                _mdb_abort_str = buf; /* Do this first so we don't recurse */
                (void) mdb_iob_vsnprintf(buf, sizeof (buf), format, alist);

                nfail = 1;
        }

        /*
         * We'll try to print failure messages twice.  Any more than that,
         * and we're probably hitting an assertion or some other problem in
         * the printing routines, and will recurse until we run out of stack.
         */
        if (nfail++ < 3) {
                mdb_iob_printf(mdb.m_err, "%s ABORT: ", mdb.m_pname);
                mdb_iob_vprintf(mdb.m_err, format, alist);
                mdb_iob_flush(mdb.m_err);

                (void) mdb_signal_blockall();
                (void) mdb_signal_raise(SIGABRT);
                (void) mdb_signal_unblock(SIGABRT);
        }

        exit(1);
}

/*PRINTFLIKE1*/
void
warn(const char *format, ...)
{
        va_list alist;

        va_start(alist, format);
        vwarn(format, alist);
        va_end(alist);
}

/*PRINTFLIKE1*/
void
die(const char *format, ...)
{
        va_list alist;

        va_start(alist, format);
        vdie(format, alist);
        va_end(alist);
}

/*PRINTFLIKE1*/
void
fail(const char *format, ...)
{
        va_list alist;

        va_start(alist, format);
        vfail(format, alist);
        va_end(alist);
}

int
set_errbytes(size_t rbytes, size_t nbytes)
{
        errno_rbytes = rbytes;
        errno_nbytes = nbytes;
        errno = EMDB_PARTIAL;
        return (-1);
}

int
set_errno(int err)
{
        errno = err;
        return (-1);
}

int
ctf_to_errno(int err)
{
        errno_libctf = err;
        return (EMDB_CTF);
}

#ifndef _KMDB
/*
 * The libthread_db interface is a superfund site and provides no strerror
 * equivalent for us to call: we try to provide some sensible handling for its
 * garbage bin of error return codes here.  First of all, we don't bother
 * interpreting all of the possibilities, since many of them aren't even used
 * in the implementation anymore.  We try to map thread_db errors we may see
 * to UNIX errnos or mdb errnos as appropriate.
 */
int
tdb_to_errno(int err)
{
        switch (err) {
        case TD_OK:
        case TD_PARTIALREG:
                return (0);
        case TD_NOCAPAB:
                return (ENOTSUP);
        case TD_BADPH:
        case TD_BADTH:
        case TD_BADSH:
        case TD_BADTA:
        case TD_BADKEY:
        case TD_NOEVENT:
                return (EINVAL);
        case TD_NOFPREGS:
        case TD_NOXREGS:
                return (EMDB_NOREGS);
        case TD_NOTHR:
                return (EMDB_NOTHREAD);
        case TD_MALLOC:
                return (EMDB_ALLOC);
        case TD_TLSDEFER:
                return (EMDB_TLS);
        case TD_NOTLS:
                return (EMDB_NOTLS);
        case TD_DBERR:
        case TD_ERR:
        default:
                return (EMDB_TDB);
        }
}

int
rdb_to_errno(int err)
{
        errno_rtld_db = err;
        return (EMDB_RTLD_DB);
}
#endif /* _KMDB */