1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <unistd.h>
  30 #include <dlfcn.h>
  31 #include <signal.h>
  32 #include <stdarg.h>
  33 #include <stdio.h>
  34 #include <string.h>
  35 
  36 #include <sys/machelf.h>
  37 
  38 #include <umem_impl.h>
  39 #include "misc.h"
  40 
  41 #define UMEM_ERRFD      2       /* goes to standard error */
  42 #define UMEM_MAX_ERROR_SIZE 4096 /* error messages are truncated to this */
  43 
  44 /*
  45  * This is a circular buffer for holding error messages.
  46  * umem_error_enter appends to the buffer, adding "..." to the beginning
  47  * if data has been lost.
  48  */
  49 
  50 #define ERR_SIZE 8192           /* must be a power of 2 */
  51 
  52 static mutex_t umem_error_lock = DEFAULTMUTEX;
  53 
  54 static char umem_error_buffer[ERR_SIZE] = "";
  55 static uint_t umem_error_begin = 0;
  56 static uint_t umem_error_end = 0;
  57 
  58 #define WRITE_AND_INC(var, value) { \
  59         umem_error_buffer[(var)++] = (value); \
  60         var = P2PHASE((var), ERR_SIZE); \
  61 }
  62 
  63 static void
  64 umem_log_enter(const char *error_str)
  65 {
  66         int looped;
  67         char c;
  68 
  69         looped = 0;
  70 
  71         (void) mutex_lock(&umem_error_lock);
  72 
  73         while ((c = *error_str++) != '\0') {
  74                 WRITE_AND_INC(umem_error_end, c);
  75                 if (umem_error_end == umem_error_begin)
  76                         looped = 1;
  77         }
  78 
  79         umem_error_buffer[umem_error_end] = 0;
  80 
  81         if (looped) {
  82                 uint_t idx;
  83                 umem_error_begin = P2PHASE(umem_error_end + 1, ERR_SIZE);
  84 
  85                 idx = umem_error_begin;
  86                 WRITE_AND_INC(idx, '.');
  87                 WRITE_AND_INC(idx, '.');
  88                 WRITE_AND_INC(idx, '.');
  89         }
  90 
  91         (void) mutex_unlock(&umem_error_lock);
  92 }
  93 
  94 void
  95 umem_error_enter(const char *error_str)
  96 {
  97 #ifndef UMEM_STANDALONE
  98         if (umem_output && !issetugid())
  99                 (void) write(UMEM_ERRFD, error_str, strlen(error_str));
 100 #endif
 101 
 102         umem_log_enter(error_str);
 103 }
 104 
 105 int
 106 highbit(ulong_t i)
 107 {
 108         register int h = 1;
 109 
 110         if (i == 0)
 111                 return (0);
 112 #ifdef _LP64
 113         if (i & 0xffffffff00000000ul) {
 114                 h += 32; i >>= 32;
 115         }
 116 #endif
 117         if (i & 0xffff0000) {
 118                 h += 16; i >>= 16;
 119         }
 120         if (i & 0xff00) {
 121                 h += 8; i >>= 8;
 122         }
 123         if (i & 0xf0) {
 124                 h += 4; i >>= 4;
 125         }
 126         if (i & 0xc) {
 127                 h += 2; i >>= 2;
 128         }
 129         if (i & 0x2) {
 130                 h += 1;
 131         }
 132         return (h);
 133 }
 134 
 135 int
 136 lowbit(ulong_t i)
 137 {
 138         register int h = 1;
 139 
 140         if (i == 0)
 141                 return (0);
 142 #ifdef _LP64
 143         if (!(i & 0xffffffff)) {
 144                 h += 32; i >>= 32;
 145         }
 146 #endif
 147         if (!(i & 0xffff)) {
 148                 h += 16; i >>= 16;
 149         }
 150         if (!(i & 0xff)) {
 151                 h += 8; i >>= 8;
 152         }
 153         if (!(i & 0xf)) {
 154                 h += 4; i >>= 4;
 155         }
 156         if (!(i & 0x3)) {
 157                 h += 2; i >>= 2;
 158         }
 159         if (!(i & 0x1)) {
 160                 h += 1;
 161         }
 162         return (h);
 163 }
 164 
 165 void
 166 hrt2ts(hrtime_t hrt, timestruc_t *tsp)
 167 {
 168         tsp->tv_sec = hrt / NANOSEC;
 169         tsp->tv_nsec = hrt % NANOSEC;
 170 }
 171 
 172 void
 173 log_message(const char *format, ...)
 174 {
 175         char buf[UMEM_MAX_ERROR_SIZE] = "";
 176 
 177         va_list va;
 178 
 179         va_start(va, format);
 180         (void) vsnprintf(buf, UMEM_MAX_ERROR_SIZE-1, format, va);
 181         va_end(va);
 182 
 183 #ifndef UMEM_STANDALONE
 184         if (umem_output > 1)
 185                 (void) write(UMEM_ERRFD, buf, strlen(buf));
 186 #endif
 187 
 188         umem_log_enter(buf);
 189 }
 190 
 191 #ifndef UMEM_STANDALONE
 192 void
 193 debug_printf(const char *format, ...)
 194 {
 195         char buf[UMEM_MAX_ERROR_SIZE] = "";
 196 
 197         va_list va;
 198 
 199         va_start(va, format);
 200         (void) vsnprintf(buf, UMEM_MAX_ERROR_SIZE-1, format, va);
 201         va_end(va);
 202 
 203         (void) write(UMEM_ERRFD, buf, strlen(buf));
 204 }
 205 #endif
 206 
 207 void
 208 umem_vprintf(const char *format, va_list va)
 209 {
 210         char buf[UMEM_MAX_ERROR_SIZE] = "";
 211 
 212         (void) vsnprintf(buf, UMEM_MAX_ERROR_SIZE-1, format, va);
 213 
 214         umem_error_enter(buf);
 215 }
 216 
 217 void
 218 umem_printf(const char *format, ...)
 219 {
 220         va_list va;
 221 
 222         va_start(va, format);
 223         umem_vprintf(format, va);
 224         va_end(va);
 225 }
 226 
 227 /*ARGSUSED*/
 228 void
 229 umem_printf_warn(void *ignored, const char *format, ...)
 230 {
 231         va_list va;
 232 
 233         va_start(va, format);
 234         umem_vprintf(format, va);
 235         va_end(va);
 236 }
 237 
 238 /*
 239  * print_sym tries to print out the symbol and offset of a pointer
 240  */
 241 int
 242 print_sym(void *pointer)
 243 {
 244         int result;
 245         Dl_info sym_info;
 246 
 247         uintptr_t end = NULL;
 248 
 249         Sym *ext_info = NULL;
 250 
 251         result = dladdr1(pointer, &sym_info, (void **)&ext_info,
 252             RTLD_DL_SYMENT);
 253 
 254         if (result != 0) {
 255                 const char *endpath;
 256 
 257                 end = (uintptr_t)sym_info.dli_saddr + ext_info->st_size;
 258 
 259                 endpath = strrchr(sym_info.dli_fname, '/');
 260                 if (endpath)
 261                         endpath++;
 262                 else
 263                         endpath = sym_info.dli_fname;
 264                 umem_printf("%s'", endpath);
 265         }
 266 
 267         if (result == 0 || (uintptr_t)pointer > end) {
 268                 umem_printf("?? (0x%p)", pointer);
 269                 return (0);
 270         } else {
 271                 umem_printf("%s+0x%p", sym_info.dli_sname,
 272                     (char *)pointer - (char *)sym_info.dli_saddr);
 273                 return (1);
 274         }
 275 }