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 51 MODULE_ID("$Id: lib_trace.c,v 1.8 2010/01/12 23:22:06 nicm Exp $") 52 53 NCURSES_EXPORT_VAR(unsigned) _nc_tracing = 0; /* always define this */ 54 55 #ifdef TRACE 56 57 #if USE_REENTRANT 58 NCURSES_EXPORT(const char *) 59 NCURSES_PUBLIC_VAR(_nc_tputs_trace) (void) 60 { 61 return SP ? SP->_tputs_trace : _nc_prescreen._tputs_trace; 62 } 63 NCURSES_EXPORT(long) 64 NCURSES_PUBLIC_VAR(_nc_outchars) (void) 65 { 66 return SP ? SP->_outchars : _nc_prescreen._outchars; 67 } 68 NCURSES_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 } 76 NCURSES_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 85 NCURSES_EXPORT_VAR(const char *) _nc_tputs_trace = ""; 86 NCURSES_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 93 NCURSES_EXPORT(void) 94 trace(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 145 static 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 206 NCURSES_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 */ 217 NCURSES_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 */ 225 NCURSES_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 */ 233 NCURSES_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 */ 241 NCURSES_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 */ 249 NCURSES_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 */ 257 NCURSES_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 */ 265 NCURSES_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 */ 273 NCURSES_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 */ 281 NCURSES_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 */ 296 NCURSES_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 */ 322 NCURSES_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