1 /**************************************************************************** 2 * Copyright (c) 1998-2012,2013 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 * and: Juergen Pfeifer * 34 ****************************************************************************/ 35 36 /* 37 * lib_trace.c - Tracing/Debugging routines 38 * 39 * The _tracef() function is originally from pcurses (by Pavel Curtis) in 1982. 40 * pcurses allowed one to enable/disable tracing using traceon() and traceoff() 41 * functions. ncurses provides a trace() function which allows one to 42 * selectively enable or disable several tracing features. 43 */ 44 45 #include <curses.priv.h> 46 #include <tic.h> 47 48 #include <ctype.h> 49 50 MODULE_ID("$Id: lib_trace.c,v 1.82 2013/07/06 19:42:09 tom Exp $") 51 52 NCURSES_EXPORT_VAR(unsigned) _nc_tracing = 0; /* always define this */ 53 54 #ifdef TRACE 55 56 #if USE_REENTRANT 57 NCURSES_EXPORT(const char *) 58 NCURSES_PUBLIC_VAR(_nc_tputs_trace) (void) 59 { 60 return CURRENT_SCREEN ? CURRENT_SCREEN->_tputs_trace : _nc_prescreen._tputs_trace; 61 } 62 NCURSES_EXPORT(long) 63 NCURSES_PUBLIC_VAR(_nc_outchars) (void) 64 { 65 return CURRENT_SCREEN ? CURRENT_SCREEN->_outchars : _nc_prescreen._outchars; 66 } 67 NCURSES_EXPORT(void) 68 _nc_set_tputs_trace(const char *s) 69 { 70 if (CURRENT_SCREEN) 71 CURRENT_SCREEN->_tputs_trace = s; 72 else 73 _nc_prescreen._tputs_trace = s; 74 } 75 NCURSES_EXPORT(void) 76 _nc_count_outchars(long increment) 77 { 78 if (CURRENT_SCREEN) 79 CURRENT_SCREEN->_outchars += increment; 80 else 81 _nc_prescreen._outchars += increment; 82 } 83 #else 84 NCURSES_EXPORT_VAR(const char *) _nc_tputs_trace = ""; 85 NCURSES_EXPORT_VAR(long) _nc_outchars = 0; 86 #endif 87 88 #define TraceFP _nc_globals.trace_fp 89 #define TracePath _nc_globals.trace_fname 90 #define TraceLevel _nc_globals.trace_level 91 92 NCURSES_EXPORT(void) 93 trace(const unsigned int tracelevel) 94 { 95 if ((TraceFP == 0) && tracelevel) { 96 const char *mode = _nc_globals.init_trace ? "ab" : "wb"; 97 98 if (TracePath[0] == '\0') { 99 size_t size = sizeof(TracePath) - 12; 100 if (getcwd(TracePath, size) == 0) { 101 perror("curses: Can't get working directory"); 102 exit(EXIT_FAILURE); 103 } 104 TracePath[size] = '\0'; 105 assert(strlen(TracePath) <= size); 106 _nc_STRCAT(TracePath, "/trace", sizeof(TracePath)); 107 if (_nc_is_dir_path(TracePath)) { 108 _nc_STRCAT(TracePath, ".log", sizeof(TracePath)); 109 } 110 } 111 112 _nc_globals.init_trace = TRUE; 113 _nc_tracing = tracelevel; 114 if (_nc_access(TracePath, W_OK) < 0 115 || (TraceFP = fopen(TracePath, mode)) == 0) { 116 perror("curses: Can't open 'trace' file"); 117 exit(EXIT_FAILURE); 118 } 119 /* Try to set line-buffered mode, or (failing that) unbuffered, 120 * so that the trace-output gets flushed automatically at the 121 * end of each line. This is useful in case the program dies. 122 */ 123 #if HAVE_SETVBUF /* ANSI */ 124 (void) setvbuf(TraceFP, (char *) 0, _IOLBF, (size_t) 0); 125 #elif HAVE_SETBUF /* POSIX */ 126 (void) setbuffer(TraceFP, (char *) 0); 127 #endif 128 _tracef("TRACING NCURSES version %s.%d (tracelevel=%#x)", 129 NCURSES_VERSION, 130 NCURSES_VERSION_PATCH, 131 tracelevel); 132 } else if (tracelevel == 0) { 133 if (TraceFP != 0) { 134 fclose(TraceFP); 135 TraceFP = 0; 136 } 137 _nc_tracing = tracelevel; 138 } else if (_nc_tracing != tracelevel) { 139 _nc_tracing = tracelevel; 140 _tracef("tracelevel=%#x", tracelevel); 141 } 142 } 143 144 static void 145 _nc_va_tracef(const char *fmt, va_list ap) 146 { 147 static const char Called[] = T_CALLED(""); 148 static const char Return[] = T_RETURN(""); 149 150 bool before = FALSE; 151 bool after = FALSE; 152 unsigned doit = _nc_tracing; 153 int save_err = errno; 154 155 if (strlen(fmt) >= sizeof(Called) - 1) { 156 if (!strncmp(fmt, Called, sizeof(Called) - 1)) { 157 before = TRUE; 158 TraceLevel++; 159 } else if (!strncmp(fmt, Return, sizeof(Return) - 1)) { 160 after = TRUE; 161 } 162 if (before || after) { 163 if ((TraceLevel <= 1) 164 || (doit & TRACE_ICALLS) != 0) 165 doit &= (TRACE_CALLS | TRACE_CCALLS); 166 else 167 doit = 0; 168 } 169 } 170 171 if (doit != 0) { 172 if (TraceFP == 0) 173 TraceFP = stderr; 174 #ifdef USE_PTHREADS 175 /* 176 * TRACE_ICALLS is "really" needed to show normal use with threaded 177 * applications, since anything can be running during a napms(), 178 * making it appear in the hierarchical trace as it other functions 179 * are being called. 180 * 181 * Rather than add the complication of a per-thread stack, just 182 * show the thread-id in each line of the trace. 183 */ 184 # if USE_WEAK_SYMBOLS 185 if ((pthread_self)) 186 # endif 187 #ifdef __MINGW32__ 188 fprintf(TraceFP, "%#lx:", (long) (intptr_t) pthread_self().p); 189 #else 190 fprintf(TraceFP, "%#lx:", (long) (intptr_t) pthread_self()); 191 #endif 192 #endif 193 if (before || after) { 194 int n; 195 for (n = 1; n < TraceLevel; n++) 196 fputs("+ ", TraceFP); 197 } 198 vfprintf(TraceFP, fmt, ap); 199 fputc('\n', TraceFP); 200 fflush(TraceFP); 201 } 202 203 if (after && TraceLevel) 204 TraceLevel--; 205 206 errno = save_err; 207 } 208 209 NCURSES_EXPORT(void) 210 _tracef(const char *fmt,...) 211 { 212 va_list ap; 213 214 va_start(ap, fmt); 215 _nc_va_tracef(fmt, ap); 216 va_end(ap); 217 } 218 219 /* Trace 'bool' return-values */ 220 NCURSES_EXPORT(NCURSES_BOOL) 221 _nc_retrace_bool(int code) 222 { 223 T((T_RETURN("%s"), code ? "TRUE" : "FALSE")); 224 return code; 225 } 226 227 /* Trace 'char' return-values */ 228 NCURSES_EXPORT(char) 229 _nc_retrace_char(int code) 230 { 231 T((T_RETURN("%c"), code)); 232 return (char) code; 233 } 234 235 /* Trace 'int' return-values */ 236 NCURSES_EXPORT(int) 237 _nc_retrace_int(int code) 238 { 239 T((T_RETURN("%d"), code)); 240 return code; 241 } 242 243 /* Trace 'unsigned' return-values */ 244 NCURSES_EXPORT(unsigned) 245 _nc_retrace_unsigned(unsigned code) 246 { 247 T((T_RETURN("%#x"), code)); 248 return code; 249 } 250 251 /* Trace 'char*' return-values */ 252 NCURSES_EXPORT(char *) 253 _nc_retrace_ptr(char *code) 254 { 255 T((T_RETURN("%s"), _nc_visbuf(code))); 256 return code; 257 } 258 259 /* Trace 'const char*' return-values */ 260 NCURSES_EXPORT(const char *) 261 _nc_retrace_cptr(const char *code) 262 { 263 T((T_RETURN("%s"), _nc_visbuf(code))); 264 return code; 265 } 266 267 /* Trace 'NCURSES_CONST void*' return-values */ 268 NCURSES_EXPORT(NCURSES_CONST void *) 269 _nc_retrace_cvoid_ptr(NCURSES_CONST void *code) 270 { 271 T((T_RETURN("%p"), code)); 272 return code; 273 } 274 275 /* Trace 'void*' return-values */ 276 NCURSES_EXPORT(void *) 277 _nc_retrace_void_ptr(void *code) 278 { 279 T((T_RETURN("%p"), code)); 280 return code; 281 } 282 283 /* Trace 'SCREEN *' return-values */ 284 NCURSES_EXPORT(SCREEN *) 285 _nc_retrace_sp(SCREEN *code) 286 { 287 T((T_RETURN("%p"), (void *) code)); 288 return code; 289 } 290 291 /* Trace 'WINDOW *' return-values */ 292 NCURSES_EXPORT(WINDOW *) 293 _nc_retrace_win(WINDOW *code) 294 { 295 T((T_RETURN("%p"), (void *) code)); 296 return code; 297 } 298 299 #if USE_REENTRANT 300 /* 301 * Check if the given trace-mask is enabled. 302 * 303 * This function may be called from within one of the functions that fills 304 * in parameters for _tracef(), but in that case we do not want to lock the 305 * mutex, since it is already locked. 306 */ 307 NCURSES_EXPORT(int) 308 _nc_use_tracef(unsigned mask) 309 { 310 bool result = FALSE; 311 312 _nc_lock_global(tst_tracef); 313 if (!_nc_globals.nested_tracef++) { 314 if ((result = (_nc_tracing & (mask))) != 0 315 && _nc_try_global(tracef) == 0) { 316 /* we will call _nc_locked_tracef(), no nesting so far */ 317 } else { 318 /* we will not call _nc_locked_tracef() */ 319 _nc_globals.nested_tracef = 0; 320 } 321 } else { 322 /* we may call _nc_locked_tracef(), but with nested_tracef > 0 */ 323 result = (_nc_tracing & (mask)); 324 } 325 _nc_unlock_global(tst_tracef); 326 return result; 327 } 328 329 /* 330 * We call this if _nc_use_tracef() returns true, which means we must unlock 331 * the tracef mutex. 332 */ 333 NCURSES_EXPORT(void) 334 _nc_locked_tracef(const char *fmt,...) 335 { 336 va_list ap; 337 338 va_start(ap, fmt); 339 _nc_va_tracef(fmt, ap); 340 va_end(ap); 341 342 if (--(_nc_globals.nested_tracef) == 0) { 343 _nc_unlock_global(tracef); 344 } 345 } 346 #endif /* USE_REENTRANT */ 347 348 #endif /* TRACE */ 349