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