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