1 /**************************************************************************** 2 * Copyright 2018-2019,2020 Thomas E. Dickey * 3 * Copyright 1998-2016,2017 Free Software Foundation, Inc. * 4 * * 5 * Permission is hereby granted, free of charge, to any person obtaining a * 6 * copy of this software and associated documentation files (the * 7 * "Software"), to deal in the Software without restriction, including * 8 * without limitation the rights to use, copy, modify, merge, publish, * 9 * distribute, distribute with modifications, sublicense, and/or sell * 10 * copies of the Software, and to permit persons to whom the Software is * 11 * furnished to do so, subject to the following conditions: * 12 * * 13 * The above copyright notice and this permission notice shall be included * 14 * in all copies or substantial portions of the Software. * 15 * * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 23 * * 24 * Except as contained in this notice, the name(s) of the above copyright * 25 * holders shall not be used in advertising or otherwise to promote the * 26 * sale, use or other dealings in this Software without prior written * 27 * authorization. * 28 ****************************************************************************/ 29 30 /**************************************************************************** 31 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 32 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 33 * and: Thomas E. Dickey 1996-on * 34 * and: Juergen Pfeifer * 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.95 2020/02/02 23:34:34 tom 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 CURRENT_SCREEN ? CURRENT_SCREEN->_tputs_trace : _nc_prescreen._tputs_trace; 62 } 63 NCURSES_EXPORT(long) 64 NCURSES_PUBLIC_VAR(_nc_outchars) (void) 65 { 66 return CURRENT_SCREEN ? CURRENT_SCREEN->_outchars : _nc_prescreen._outchars; 67 } 68 NCURSES_EXPORT(void) 69 _nc_set_tputs_trace(const char *s) 70 { 71 if (CURRENT_SCREEN) 72 CURRENT_SCREEN->_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 (CURRENT_SCREEN) 80 CURRENT_SCREEN->_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 MyFP _nc_globals.trace_fp 90 #define MyFD _nc_globals.trace_fd 91 #define MyInit _nc_globals.trace_opened 92 #define MyPath _nc_globals.trace_fname 93 #define MyLevel _nc_globals.trace_level 94 #define MyNested _nc_globals.nested_tracef 95 #endif /* TRACE */ 96 97 NCURSES_EXPORT(unsigned) 98 curses_trace(unsigned tracelevel) 99 { 100 unsigned result; 101 #if defined(TRACE) 102 result = _nc_tracing; 103 if ((MyFP == 0) && tracelevel) { 104 MyInit = TRUE; 105 if (MyFD >= 0) { 106 MyFP = fdopen(MyFD, BIN_W); 107 } else { 108 if (MyPath[0] == '\0') { 109 size_t size = sizeof(MyPath) - 12; 110 if (getcwd(MyPath, size) == 0) { 111 perror("curses: Can't get working directory"); 112 exit(EXIT_FAILURE); 113 } 114 MyPath[size] = '\0'; 115 assert(strlen(MyPath) <= size); 116 _nc_STRCAT(MyPath, "/trace", sizeof(MyPath)); 117 if (_nc_is_dir_path(MyPath)) { 118 _nc_STRCAT(MyPath, ".log", sizeof(MyPath)); 119 } 120 } 121 if (_nc_access(MyPath, W_OK) < 0 122 || (MyFD = open(MyPath, O_CREAT | O_EXCL | O_RDWR, 0600)) < 0 123 || (MyFP = fdopen(MyFD, BIN_W)) == 0) { 124 ; /* EMPTY */ 125 } 126 } 127 _nc_tracing = tracelevel; 128 /* Try to set line-buffered mode, or (failing that) unbuffered, 129 * so that the trace-output gets flushed automatically at the 130 * end of each line. This is useful in case the program dies. 131 */ 132 if (MyFP != 0) { 133 #if HAVE_SETVBUF /* ANSI */ 134 (void) setvbuf(MyFP, (char *) 0, _IOLBF, (size_t) 0); 135 #elif HAVE_SETBUF /* POSIX */ 136 (void) setbuffer(MyFP, (char *) 0); 137 #endif 138 } 139 _tracef("TRACING NCURSES version %s.%d (tracelevel=%#x)", 140 NCURSES_VERSION, 141 NCURSES_VERSION_PATCH, 142 tracelevel); 143 } else if (tracelevel == 0) { 144 if (MyFP != 0) { 145 MyFD = dup(MyFD); /* allow reopen of same file */ 146 fclose(MyFP); 147 MyFP = 0; 148 } 149 _nc_tracing = tracelevel; 150 } else if (_nc_tracing != tracelevel) { 151 _nc_tracing = tracelevel; 152 _tracef("tracelevel=%#x", tracelevel); 153 } 154 #else 155 (void) tracelevel; 156 result = 0; 157 #endif 158 return result; 159 } 160 161 #if defined(TRACE) 162 NCURSES_EXPORT(void) 163 trace(const unsigned int tracelevel) 164 { 165 curses_trace(tracelevel); 166 } 167 168 static void 169 _nc_va_tracef(const char *fmt, va_list ap) 170 { 171 static const char Called[] = T_CALLED(""); 172 static const char Return[] = T_RETURN(""); 173 174 bool before = FALSE; 175 bool after = FALSE; 176 unsigned doit = _nc_tracing; 177 int save_err = errno; 178 FILE *fp = MyFP; 179 180 #ifdef TRACE 181 /* verbose-trace in the command-line utilities relies on this */ 182 if (fp == 0 && !MyInit && _nc_tracing >= DEBUG_LEVEL(1)) 183 fp = stderr; 184 #endif 185 186 if (strlen(fmt) >= sizeof(Called) - 1) { 187 if (!strncmp(fmt, Called, sizeof(Called) - 1)) { 188 before = TRUE; 189 MyLevel++; 190 } else if (!strncmp(fmt, Return, sizeof(Return) - 1)) { 191 after = TRUE; 192 } 193 if (before || after) { 194 if ((MyLevel <= 1) 195 || (doit & TRACE_ICALLS) != 0) 196 doit &= (TRACE_CALLS | TRACE_CCALLS); 197 else 198 doit = 0; 199 } 200 } 201 202 if (doit != 0 && fp != 0) { 203 #ifdef USE_PTHREADS 204 /* 205 * TRACE_ICALLS is "really" needed to show normal use with threaded 206 * applications, since anything can be running during a napms(), 207 * making it appear in the hierarchical trace as it other functions 208 * are being called. 209 * 210 * Rather than add the complication of a per-thread stack, just 211 * show the thread-id in each line of the trace. 212 */ 213 # if USE_WEAK_SYMBOLS 214 if ((pthread_self)) 215 # endif 216 #ifdef _WIN32 217 fprintf(fp, "%#lx:", (long) (intptr_t) pthread_self().p); 218 #else 219 fprintf(fp, "%#lx:", (long) (intptr_t) pthread_self()); 220 #endif 221 #endif 222 if (before || after) { 223 int n; 224 for (n = 1; n < MyLevel; n++) 225 fputs("+ ", fp); 226 } 227 vfprintf(fp, fmt, ap); 228 fputc('\n', fp); 229 fflush(fp); 230 } 231 232 if (after && MyLevel) 233 MyLevel--; 234 235 errno = save_err; 236 } 237 238 NCURSES_EXPORT(void) 239 _tracef(const char *fmt, ...) 240 { 241 va_list ap; 242 243 va_start(ap, fmt); 244 _nc_va_tracef(fmt, ap); 245 va_end(ap); 246 } 247 248 /* Trace 'bool' return-values */ 249 NCURSES_EXPORT(NCURSES_BOOL) 250 _nc_retrace_bool(int code) 251 { 252 T((T_RETURN("%s"), code ? "TRUE" : "FALSE")); 253 return code; 254 } 255 256 /* Trace 'char' return-values */ 257 NCURSES_EXPORT(char) 258 _nc_retrace_char(int code) 259 { 260 T((T_RETURN("%c"), code)); 261 return (char) code; 262 } 263 264 /* Trace 'int' return-values */ 265 NCURSES_EXPORT(int) 266 _nc_retrace_int(int code) 267 { 268 T((T_RETURN("%d"), code)); 269 return code; 270 } 271 272 /* Trace 'unsigned' return-values */ 273 NCURSES_EXPORT(unsigned) 274 _nc_retrace_unsigned(unsigned code) 275 { 276 T((T_RETURN("%#x"), code)); 277 return code; 278 } 279 280 /* Trace 'char*' return-values */ 281 NCURSES_EXPORT(char *) 282 _nc_retrace_ptr(char *code) 283 { 284 T((T_RETURN("%s"), _nc_visbuf(code))); 285 return code; 286 } 287 288 /* Trace 'const char*' return-values */ 289 NCURSES_EXPORT(const char *) 290 _nc_retrace_cptr(const char *code) 291 { 292 T((T_RETURN("%s"), _nc_visbuf(code))); 293 return code; 294 } 295 296 /* Trace 'NCURSES_CONST void*' return-values */ 297 NCURSES_EXPORT(NCURSES_CONST void *) 298 _nc_retrace_cvoid_ptr(NCURSES_CONST void *code) 299 { 300 T((T_RETURN("%p"), code)); 301 return code; 302 } 303 304 /* Trace 'void*' return-values */ 305 NCURSES_EXPORT(void *) 306 _nc_retrace_void_ptr(void *code) 307 { 308 T((T_RETURN("%p"), code)); 309 return code; 310 } 311 312 /* Trace 'SCREEN *' return-values */ 313 NCURSES_EXPORT(SCREEN *) 314 _nc_retrace_sp(SCREEN *code) 315 { 316 T((T_RETURN("%p"), (void *) code)); 317 return code; 318 } 319 320 /* Trace 'WINDOW *' return-values */ 321 NCURSES_EXPORT(WINDOW *) 322 _nc_retrace_win(WINDOW *code) 323 { 324 T((T_RETURN("%p"), (void *) code)); 325 return code; 326 } 327 328 NCURSES_EXPORT(char *) 329 _nc_fmt_funcptr(char *target, const char *source, size_t size) 330 { 331 size_t n; 332 char *dst = target; 333 bool leading = TRUE; 334 335 union { 336 int value; 337 char bytes[sizeof(int)]; 338 } byteorder; 339 340 byteorder.value = 0x1234; 341 342 *dst++ = '0'; 343 *dst++ = 'x'; 344 345 for (n = 0; n < size; ++n) { 346 unsigned ch = ((byteorder.bytes[0] == 0x34) 347 ? UChar(source[size - n - 1]) 348 : UChar(source[n])); 349 if (ch != 0 || (n + 1) >= size) 350 leading = FALSE; 351 if (!leading) { 352 _nc_SPRINTF(dst, _nc_SLIMIT(TR_FUNC_LEN - (dst - target)) 353 "%02x", ch & 0xff); 354 dst += 2; 355 } 356 } 357 *dst = '\0'; 358 return target; 359 } 360 361 #if USE_REENTRANT 362 /* 363 * Check if the given trace-mask is enabled. 364 * 365 * This function may be called from within one of the functions that fills 366 * in parameters for _tracef(), but in that case we do not want to lock the 367 * mutex, since it is already locked. 368 */ 369 NCURSES_EXPORT(int) 370 _nc_use_tracef(unsigned mask) 371 { 372 bool result = FALSE; 373 374 _nc_lock_global(tst_tracef); 375 if (!MyNested++) { 376 if ((result = (_nc_tracing & (mask))) != 0 377 && _nc_try_global(tracef) == 0) { 378 /* we will call _nc_locked_tracef(), no nesting so far */ 379 } else { 380 /* we will not call _nc_locked_tracef() */ 381 MyNested = 0; 382 } 383 } else { 384 /* we may call _nc_locked_tracef(), but with nested_tracef > 0 */ 385 result = (_nc_tracing & (mask)); 386 } 387 _nc_unlock_global(tst_tracef); 388 return result; 389 } 390 391 /* 392 * We call this if _nc_use_tracef() returns true, which means we must unlock 393 * the tracef mutex. 394 */ 395 NCURSES_EXPORT(void) 396 _nc_locked_tracef(const char *fmt, ...) 397 { 398 va_list ap; 399 400 va_start(ap, fmt); 401 _nc_va_tracef(fmt, ap); 402 va_end(ap); 403 404 if (--(MyNested) == 0) { 405 _nc_unlock_global(tracef); 406 } 407 } 408 #endif /* USE_REENTRANT */ 409 410 #endif /* TRACE */ 411