lib_trace.c revision 1.8
1/* $OpenBSD: lib_trace.c,v 1.8 2010/01/12 23:22:06 nicm Exp $ */ 2 3/**************************************************************************** 4 * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. * 5 * * 6 * Permission is hereby granted, free of charge, to any person obtaining a * 7 * copy of this software and associated documentation files (the * 8 * "Software"), to deal in the Software without restriction, including * 9 * without limitation the rights to use, copy, modify, merge, publish, * 10 * distribute, distribute with modifications, sublicense, and/or sell * 11 * copies of the Software, and to permit persons to whom the Software is * 12 * furnished to do so, subject to the following conditions: * 13 * * 14 * The above copyright notice and this permission notice shall be included * 15 * in all copies or substantial portions of the Software. * 16 * * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 24 * * 25 * Except as contained in this notice, the name(s) of the above copyright * 26 * holders shall not be used in advertising or otherwise to promote the * 27 * sale, use or other dealings in this Software without prior written * 28 * authorization. * 29 ****************************************************************************/ 30 31/**************************************************************************** 32 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 33 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 34 * and: Thomas E. Dickey 1996-on * 35 ****************************************************************************/ 36 37/* 38 * lib_trace.c - Tracing/Debugging routines 39 * 40 * The _tracef() function is originally from pcurses (by Pavel Curtis) in 1982. 41 * pcurses allowed one to enable/disable tracing using traceon() and traceoff() 42 * functions. ncurses provides a trace() function which allows one to 43 * selectively enable or disable several tracing features. 44 */ 45 46#include <curses.priv.h> 47#include <tic.h> 48 49#include <ctype.h> 50 51MODULE_ID("$Id: lib_trace.c,v 1.8 2010/01/12 23:22:06 nicm Exp $") 52 53NCURSES_EXPORT_VAR(unsigned) _nc_tracing = 0; /* always define this */ 54 55#ifdef TRACE 56 57#if USE_REENTRANT 58NCURSES_EXPORT(const char *) 59NCURSES_PUBLIC_VAR(_nc_tputs_trace) (void) 60{ 61 return SP ? SP->_tputs_trace : _nc_prescreen._tputs_trace; 62} 63NCURSES_EXPORT(long) 64NCURSES_PUBLIC_VAR(_nc_outchars) (void) 65{ 66 return SP ? SP->_outchars : _nc_prescreen._outchars; 67} 68NCURSES_EXPORT(void) 69_nc_set_tputs_trace(const char *s) 70{ 71 if (SP) 72 SP->_tputs_trace = s; 73 else 74 _nc_prescreen._tputs_trace = s; 75} 76NCURSES_EXPORT(void) 77_nc_count_outchars(long increment) 78{ 79 if (SP) 80 SP->_outchars += increment; 81 else 82 _nc_prescreen._outchars += increment; 83} 84#else 85NCURSES_EXPORT_VAR(const char *) _nc_tputs_trace = ""; 86NCURSES_EXPORT_VAR(long) _nc_outchars = 0; 87#endif 88 89#define TraceFP _nc_globals.trace_fp 90#define TracePath _nc_globals.trace_fname 91#define TraceLevel _nc_globals.trace_level 92 93NCURSES_EXPORT(void) 94trace(const unsigned int tracelevel) 95{ 96 if ((TraceFP == 0) && tracelevel) { 97 const char *mode = _nc_globals.init_trace ? "ab" : "wb"; 98 99 if (TracePath[0] == '\0') { 100 int size = sizeof(TracePath) - 12; 101 if (getcwd(TracePath, size) == 0) { 102 perror("curses: Can't get working directory"); 103 exit(EXIT_FAILURE); 104 } 105 TracePath[size] = '\0'; 106 assert(strlen(TracePath) <= size); 107 strlcat(TracePath, "/trace", sizeof(TracePath)); 108 if (_nc_is_dir_path(TracePath)) { 109 strlcat(TracePath, ".log", sizeof(TracePath)); 110 } 111 } 112 113 _nc_globals.init_trace = TRUE; 114 _nc_tracing = tracelevel; 115 if (_nc_access(TracePath, W_OK) < 0 116 || (TraceFP = fopen(TracePath, mode)) == 0) { 117 perror("curses: Can't open 'trace' file"); 118 exit(EXIT_FAILURE); 119 } 120 /* Try to set line-buffered mode, or (failing that) unbuffered, 121 * so that the trace-output gets flushed automatically at the 122 * end of each line. This is useful in case the program dies. 123 */ 124#if HAVE_SETVBUF /* ANSI */ 125 (void) setvbuf(TraceFP, (char *) 0, _IOLBF, 0); 126#elif HAVE_SETBUF /* POSIX */ 127 (void) setbuffer(TraceFP, (char *) 0); 128#endif 129 _tracef("TRACING NCURSES version %s.%d (tracelevel=%#x)", 130 NCURSES_VERSION, 131 NCURSES_VERSION_PATCH, 132 tracelevel); 133 } else if (tracelevel == 0) { 134 if (TraceFP != 0) { 135 fclose(TraceFP); 136 TraceFP = 0; 137 } 138 _nc_tracing = tracelevel; 139 } else if (_nc_tracing != tracelevel) { 140 _nc_tracing = tracelevel; 141 _tracef("tracelevel=%#x", tracelevel); 142 } 143} 144 145static void 146_nc_va_tracef(const char *fmt, va_list ap) 147{ 148 static const char Called[] = T_CALLED(""); 149 static const char Return[] = T_RETURN(""); 150 151 bool before = FALSE; 152 bool after = FALSE; 153 unsigned doit = _nc_tracing; 154 int save_err = errno; 155 156 if (strlen(fmt) >= sizeof(Called) - 1) { 157 if (!strncmp(fmt, Called, sizeof(Called) - 1)) { 158 before = TRUE; 159 TraceLevel++; 160 } else if (!strncmp(fmt, Return, sizeof(Return) - 1)) { 161 after = TRUE; 162 } 163 if (before || after) { 164 if ((TraceLevel <= 1) 165 || (doit & TRACE_ICALLS) != 0) 166 doit &= (TRACE_CALLS | TRACE_CCALLS); 167 else 168 doit = 0; 169 } 170 } 171 172 if (doit != 0) { 173 if (TraceFP == 0) 174 TraceFP = stderr; 175#ifdef USE_PTHREADS 176 /* 177 * TRACE_ICALLS is "really" needed to show normal use with threaded 178 * applications, since anything can be running during a napms(), 179 * making it appear in the hierarchical trace as it other functions 180 * are being called. 181 * 182 * Rather than add the complication of a per-thread stack, just 183 * show the thread-id in each line of the trace. 184 */ 185# if USE_WEAK_SYMBOLS 186 if ((pthread_self)) 187# endif 188 fprintf(TraceFP, "%#lx:", (long) (void *) pthread_self()); 189#endif 190 if (before || after) { 191 int n; 192 for (n = 1; n < TraceLevel; n++) 193 fputs("+ ", TraceFP); 194 } 195 vfprintf(TraceFP, fmt, ap); 196 fputc('\n', TraceFP); 197 fflush(TraceFP); 198 } 199 200 if (after && TraceLevel) 201 TraceLevel--; 202 203 errno = save_err; 204} 205 206NCURSES_EXPORT(void) 207_tracef(const char *fmt,...) 208{ 209 va_list ap; 210 211 va_start(ap, fmt); 212 _nc_va_tracef(fmt, ap); 213 va_end(ap); 214} 215 216/* Trace 'bool' return-values */ 217NCURSES_EXPORT(NCURSES_BOOL) 218_nc_retrace_bool(NCURSES_BOOL code) 219{ 220 T((T_RETURN("%s"), code ? "TRUE" : "FALSE")); 221 return code; 222} 223 224/* Trace 'int' return-values */ 225NCURSES_EXPORT(int) 226_nc_retrace_int(int code) 227{ 228 T((T_RETURN("%d"), code)); 229 return code; 230} 231 232/* Trace 'unsigned' return-values */ 233NCURSES_EXPORT(unsigned) 234_nc_retrace_unsigned(unsigned code) 235{ 236 T((T_RETURN("%#x"), code)); 237 return code; 238} 239 240/* Trace 'char*' return-values */ 241NCURSES_EXPORT(char *) 242_nc_retrace_ptr(char *code) 243{ 244 T((T_RETURN("%s"), _nc_visbuf(code))); 245 return code; 246} 247 248/* Trace 'const char*' return-values */ 249NCURSES_EXPORT(const char *) 250_nc_retrace_cptr(const char *code) 251{ 252 T((T_RETURN("%s"), _nc_visbuf(code))); 253 return code; 254} 255 256/* Trace 'NCURSES_CONST void*' return-values */ 257NCURSES_EXPORT(NCURSES_CONST void *) 258_nc_retrace_cvoid_ptr(NCURSES_CONST void *code) 259{ 260 T((T_RETURN("%p"), code)); 261 return code; 262} 263 264/* Trace 'void*' return-values */ 265NCURSES_EXPORT(void *) 266_nc_retrace_void_ptr(void *code) 267{ 268 T((T_RETURN("%p"), code)); 269 return code; 270} 271 272/* Trace 'SCREEN *' return-values */ 273NCURSES_EXPORT(SCREEN *) 274_nc_retrace_sp(SCREEN *code) 275{ 276 T((T_RETURN("%p"), code)); 277 return code; 278} 279 280/* Trace 'WINDOW *' return-values */ 281NCURSES_EXPORT(WINDOW *) 282_nc_retrace_win(WINDOW *code) 283{ 284 T((T_RETURN("%p"), code)); 285 return code; 286} 287 288#if USE_REENTRANT 289/* 290 * Check if the given trace-mask is enabled. 291 * 292 * This function may be called from within one of the functions that fills 293 * in parameters for _tracef(), but in that case we do not want to lock the 294 * mutex, since it is already locked. 295 */ 296NCURSES_EXPORT(int) 297_nc_use_tracef(unsigned mask) 298{ 299 bool result = FALSE; 300 301 _nc_lock_global(tst_tracef); 302 if (!_nc_globals.nested_tracef++) { 303 if ((result = (_nc_tracing & (mask))) != 0 304 && _nc_try_global(tracef) == 0) { 305 /* we will call _nc_locked_tracef(), no nesting so far */ 306 } else { 307 /* we will not call _nc_locked_tracef() */ 308 _nc_globals.nested_tracef = 0; 309 } 310 } else { 311 /* we may call _nc_locked_tracef(), but with nested_tracef > 0 */ 312 result = (_nc_tracing & (mask)); 313 } 314 _nc_unlock_global(tst_tracef); 315 return result; 316} 317 318/* 319 * We call this if _nc_use_tracef() returns true, which means we must unlock 320 * the tracef mutex. 321 */ 322NCURSES_EXPORT(void) 323_nc_locked_tracef(const char *fmt,...) 324{ 325 va_list ap; 326 327 va_start(ap, fmt); 328 _nc_va_tracef(fmt, ap); 329 va_end(ap); 330 331 if (--(_nc_globals.nested_tracef) == 0) 332 _nc_unlock_global(tracef); 333} 334#endif /* USE_REENTRANT */ 335 336#endif /* TRACE */ 337