root/usr/src/lib/libumem/common/misc.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <unistd.h>
#include <dlfcn.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#include <sys/machelf.h>

#include <umem_impl.h>
#include "misc.h"

#define UMEM_ERRFD      2       /* goes to standard error */
#define UMEM_MAX_ERROR_SIZE 4096 /* error messages are truncated to this */

/*
 * This is a circular buffer for holding error messages.
 * umem_error_enter appends to the buffer, adding "..." to the beginning
 * if data has been lost.
 */

#define ERR_SIZE 8192           /* must be a power of 2 */

static mutex_t umem_error_lock = DEFAULTMUTEX;

char umem_error_buffer[ERR_SIZE] = "";
uint_t umem_error_begin = 0;
uint_t umem_error_end = 0;

#define WRITE_AND_INC(var, value) { \
        umem_error_buffer[(var)++] = (value); \
        var = P2PHASE((var), ERR_SIZE); \
}

static void
log_enter(const char *error_str)
{
        int looped;
        char c;

        looped = 0;

        (void) mutex_lock(&umem_error_lock);

        while ((c = *error_str++) != '\0') {
                WRITE_AND_INC(umem_error_end, c);
                if (umem_error_end == umem_error_begin)
                        looped = 1;
        }

        umem_error_buffer[umem_error_end] = 0;

        if (looped) {
                uint_t idx;
                umem_error_begin = P2PHASE(umem_error_end + 1, ERR_SIZE);

                idx = umem_error_begin;
                WRITE_AND_INC(idx, '.');
                WRITE_AND_INC(idx, '.');
                WRITE_AND_INC(idx, '.');
        }

        (void) mutex_unlock(&umem_error_lock);
}

void
umem_error_enter(const char *error_str)
{
#ifndef UMEM_STANDALONE
        if (umem_output && !issetugid())
                (void) write(UMEM_ERRFD, error_str, strlen(error_str));
#endif

        log_enter(error_str);
}

int
highbit(ulong_t i)
{
        register int h = 1;

        if (i == 0)
                return (0);
#ifdef _LP64
        if (i & 0xffffffff00000000ul) {
                h += 32; i >>= 32;
        }
#endif
        if (i & 0xffff0000) {
                h += 16; i >>= 16;
        }
        if (i & 0xff00) {
                h += 8; i >>= 8;
        }
        if (i & 0xf0) {
                h += 4; i >>= 4;
        }
        if (i & 0xc) {
                h += 2; i >>= 2;
        }
        if (i & 0x2) {
                h += 1;
        }
        return (h);
}

int
lowbit(ulong_t i)
{
        register int h = 1;

        if (i == 0)
                return (0);
#ifdef _LP64
        if (!(i & 0xffffffff)) {
                h += 32; i >>= 32;
        }
#endif
        if (!(i & 0xffff)) {
                h += 16; i >>= 16;
        }
        if (!(i & 0xff)) {
                h += 8; i >>= 8;
        }
        if (!(i & 0xf)) {
                h += 4; i >>= 4;
        }
        if (!(i & 0x3)) {
                h += 2; i >>= 2;
        }
        if (!(i & 0x1)) {
                h += 1;
        }
        return (h);
}

void
hrt2ts(hrtime_t hrt, timestruc_t *tsp)
{
        tsp->tv_sec = hrt / NANOSEC;
        tsp->tv_nsec = hrt % NANOSEC;
}

void
log_message(const char *format, ...)
{
        char buf[UMEM_MAX_ERROR_SIZE] = "";

        va_list va;

        va_start(va, format);
        (void) vsnprintf(buf, UMEM_MAX_ERROR_SIZE-1, format, va);
        va_end(va);

#ifndef UMEM_STANDALONE
        if (umem_output > 1)
                (void) write(UMEM_ERRFD, buf, strlen(buf));
#endif

        log_enter(buf);
}

#ifndef UMEM_STANDALONE
void
debug_printf(const char *format, ...)
{
        char buf[UMEM_MAX_ERROR_SIZE] = "";

        va_list va;

        va_start(va, format);
        (void) vsnprintf(buf, UMEM_MAX_ERROR_SIZE-1, format, va);
        va_end(va);

        (void) write(UMEM_ERRFD, buf, strlen(buf));
}
#endif

void
umem_vprintf(const char *format, va_list va)
{
        char buf[UMEM_MAX_ERROR_SIZE] = "";

        (void) vsnprintf(buf, UMEM_MAX_ERROR_SIZE-1, format, va);

        umem_error_enter(buf);
}

void
umem_printf(const char *format, ...)
{
        va_list va;

        va_start(va, format);
        umem_vprintf(format, va);
        va_end(va);
}

/*ARGSUSED*/
void
umem_printf_warn(void *ignored, const char *format, ...)
{
        va_list va;

        va_start(va, format);
        umem_vprintf(format, va);
        va_end(va);
}

/*
 * print_sym tries to print out the symbol and offset of a pointer
 */
int
print_sym(void *pointer)
{
        int result;
        Dl_info sym_info;

        uintptr_t end = (uintptr_t)NULL;

        Sym *ext_info = NULL;

        result = dladdr1(pointer, &sym_info, (void **)&ext_info,
            RTLD_DL_SYMENT);

        if (result != 0) {
                const char *endpath;

                end = (uintptr_t)sym_info.dli_saddr + ext_info->st_size;

                endpath = strrchr(sym_info.dli_fname, '/');
                if (endpath)
                        endpath++;
                else
                        endpath = sym_info.dli_fname;
                umem_printf("%s'", endpath);
        }

        if (result == 0 || (uintptr_t)pointer > end) {
                umem_printf("?? (0x%p)", pointer);
                return (0);
        } else {
                umem_printf("%s+0x%p", sym_info.dli_sname,
                    (char *)pointer - (char *)sym_info.dli_saddr);
                return (1);
        }
}