150276Speter/**************************************************************************** 2178866Srafan * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. * 350276Speter * * 450276Speter * Permission is hereby granted, free of charge, to any person obtaining a * 550276Speter * copy of this software and associated documentation files (the * 650276Speter * "Software"), to deal in the Software without restriction, including * 750276Speter * without limitation the rights to use, copy, modify, merge, publish, * 850276Speter * distribute, distribute with modifications, sublicense, and/or sell * 950276Speter * copies of the Software, and to permit persons to whom the Software is * 1050276Speter * furnished to do so, subject to the following conditions: * 1150276Speter * * 1250276Speter * The above copyright notice and this permission notice shall be included * 1350276Speter * in all copies or substantial portions of the Software. * 1450276Speter * * 1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2250276Speter * * 2350276Speter * Except as contained in this notice, the name(s) of the above copyright * 2450276Speter * holders shall not be used in advertising or otherwise to promote the * 2550276Speter * sale, use or other dealings in this Software without prior written * 2650276Speter * authorization. * 2750276Speter ****************************************************************************/ 2850276Speter 2950276Speter/**************************************************************************** 3050276Speter * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 3150276Speter * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32166124Srafan * and: Thomas E. Dickey 1996-on * 3350276Speter ****************************************************************************/ 3450276Speter 3550276Speter/* 3650276Speter * lib_trace.c - Tracing/Debugging routines 37166124Srafan * 38166124Srafan * The _tracef() function is originally from pcurses (by Pavel Curtis) in 1982. 39166124Srafan * pcurses allowed one to enable/disable tracing using traceon() and traceoff() 40166124Srafan * functions. ncurses provides a trace() function which allows one to 41166124Srafan * selectively enable or disable several tracing features. 4250276Speter */ 4350276Speter 4450276Speter#include <curses.priv.h> 4550276Speter#include <tic.h> 4650276Speter 4750276Speter#include <ctype.h> 4850276Speter 49184989SrafanMODULE_ID("$Id: lib_trace.c,v 1.71 2008/08/23 18:04:29 tom Exp $") 5062449Speter 51166124SrafanNCURSES_EXPORT_VAR(unsigned) _nc_tracing = 0; /* always define this */ 5250276Speter 5350276Speter#ifdef TRACE 54174993Srafan 55174993Srafan#if USE_REENTRANT 56174993SrafanNCURSES_EXPORT(const char *) 57174993SrafanNCURSES_PUBLIC_VAR(_nc_tputs_trace) (void) 58174993Srafan{ 59174993Srafan return SP ? SP->_tputs_trace : _nc_prescreen._tputs_trace; 60174993Srafan} 61174993SrafanNCURSES_EXPORT(long) 62174993SrafanNCURSES_PUBLIC_VAR(_nc_outchars) (void) 63174993Srafan{ 64174993Srafan return SP ? SP->_outchars : _nc_prescreen._outchars; 65174993Srafan} 66174993SrafanNCURSES_EXPORT(void) 67174993Srafan_nc_set_tputs_trace(const char *s) 68174993Srafan{ 69174993Srafan if (SP) 70174993Srafan SP->_tputs_trace = s; 71174993Srafan else 72174993Srafan _nc_prescreen._tputs_trace = s; 73174993Srafan} 74174993SrafanNCURSES_EXPORT(void) 75174993Srafan_nc_count_outchars(long increment) 76174993Srafan{ 77174993Srafan if (SP) 78174993Srafan SP->_outchars += increment; 79174993Srafan else 80174993Srafan _nc_prescreen._outchars += increment; 81174993Srafan} 82174993Srafan#else 83166124SrafanNCURSES_EXPORT_VAR(const char *) _nc_tputs_trace = ""; 84166124SrafanNCURSES_EXPORT_VAR(long) _nc_outchars = 0; 85174993Srafan#endif 8650276Speter 87174993Srafan#define TraceFP _nc_globals.trace_fp 88174993Srafan#define TracePath _nc_globals.trace_fname 89174993Srafan#define TraceLevel _nc_globals.trace_level 9050276Speter 9176726SpeterNCURSES_EXPORT(void) 92166124Srafantrace(const unsigned int tracelevel) 9350276Speter{ 94174993Srafan if ((TraceFP == 0) && tracelevel) { 95174993Srafan const char *mode = _nc_globals.init_trace ? "ab" : "wb"; 9650276Speter 97174993Srafan if (TracePath[0] == '\0') { 98184989Srafan int size = sizeof(TracePath) - 12; 99184989Srafan if (getcwd(TracePath, size) == 0) { 100166124Srafan perror("curses: Can't get working directory"); 101166124Srafan exit(EXIT_FAILURE); 102166124Srafan } 103184989Srafan TracePath[size] = '\0'; 104184989Srafan assert(strlen(TracePath) <= size); 105174993Srafan strcat(TracePath, "/trace"); 106174993Srafan if (_nc_is_dir_path(TracePath)) { 107174993Srafan strcat(TracePath, ".log"); 108174993Srafan } 109166124Srafan } 110166124Srafan 111174993Srafan _nc_globals.init_trace = TRUE; 11297049Speter _nc_tracing = tracelevel; 113174993Srafan if (_nc_access(TracePath, W_OK) < 0 114174993Srafan || (TraceFP = fopen(TracePath, mode)) == 0) { 115166124Srafan perror("curses: Can't open 'trace' file"); 11662449Speter exit(EXIT_FAILURE); 11750276Speter } 11862449Speter /* Try to set line-buffered mode, or (failing that) unbuffered, 11962449Speter * so that the trace-output gets flushed automatically at the 12062449Speter * end of each line. This is useful in case the program dies. 12162449Speter */ 12262449Speter#if HAVE_SETVBUF /* ANSI */ 123174993Srafan (void) setvbuf(TraceFP, (char *) 0, _IOLBF, 0); 12462449Speter#elif HAVE_SETBUF /* POSIX */ 125174993Srafan (void) setbuffer(TraceFP, (char *) 0); 12650276Speter#endif 127166124Srafan _tracef("TRACING NCURSES version %s.%d (tracelevel=%#x)", 128166124Srafan NCURSES_VERSION, 129166124Srafan NCURSES_VERSION_PATCH, 130166124Srafan tracelevel); 131166124Srafan } else if (tracelevel == 0) { 132174993Srafan if (TraceFP != 0) { 133174993Srafan fclose(TraceFP); 134174993Srafan TraceFP = 0; 135166124Srafan } 136166124Srafan _nc_tracing = tracelevel; 13797049Speter } else if (_nc_tracing != tracelevel) { 13897049Speter _nc_tracing = tracelevel; 13997049Speter _tracef("tracelevel=%#x", tracelevel); 14062449Speter } 14150276Speter} 14250276Speter 143174993Srafanstatic void 144174993Srafan_nc_va_tracef(const char *fmt, va_list ap) 14550276Speter{ 14662449Speter static const char Called[] = T_CALLED(""); 14762449Speter static const char Return[] = T_RETURN(""); 148174993Srafan 14962449Speter bool before = FALSE; 15062449Speter bool after = FALSE; 151166124Srafan unsigned doit = _nc_tracing; 15262449Speter int save_err = errno; 15350276Speter 15462449Speter if (strlen(fmt) >= sizeof(Called) - 1) { 15562449Speter if (!strncmp(fmt, Called, sizeof(Called) - 1)) { 15662449Speter before = TRUE; 157174993Srafan TraceLevel++; 15862449Speter } else if (!strncmp(fmt, Return, sizeof(Return) - 1)) { 15962449Speter after = TRUE; 16050276Speter } 16162449Speter if (before || after) { 162174993Srafan if ((TraceLevel <= 1) 16362449Speter || (doit & TRACE_ICALLS) != 0) 16462449Speter doit &= (TRACE_CALLS | TRACE_CCALLS); 16562449Speter else 16662449Speter doit = 0; 16762449Speter } 16862449Speter } 16950276Speter 17062449Speter if (doit != 0) { 171174993Srafan if (TraceFP == 0) 172174993Srafan TraceFP = stderr; 173178866Srafan#ifdef USE_PTHREADS 174178866Srafan /* 175178866Srafan * TRACE_ICALLS is "really" needed to show normal use with threaded 176178866Srafan * applications, since anything can be running during a napms(), 177178866Srafan * making it appear in the hierarchical trace as it other functions 178178866Srafan * are being called. 179178866Srafan * 180178866Srafan * Rather than add the complication of a per-thread stack, just 181178866Srafan * show the thread-id in each line of the trace. 182178866Srafan */ 183184989Srafan# if USE_WEAK_SYMBOLS 184184989Srafan if ((pthread_self)) 185184989Srafan# endif 186184989Srafan fprintf(TraceFP, "%#lx:", (long) (void *) pthread_self()); 187178866Srafan#endif 18862449Speter if (before || after) { 18962449Speter int n; 190174993Srafan for (n = 1; n < TraceLevel; n++) 191174993Srafan fputs("+ ", TraceFP); 19250276Speter } 193174993Srafan vfprintf(TraceFP, fmt, ap); 194174993Srafan fputc('\n', TraceFP); 195174993Srafan fflush(TraceFP); 19662449Speter } 19750276Speter 198174993Srafan if (after && TraceLevel) 199174993Srafan TraceLevel--; 200174993Srafan 20162449Speter errno = save_err; 20250276Speter} 20350276Speter 204174993SrafanNCURSES_EXPORT(void) 205174993Srafan_tracef(const char *fmt,...) 206174993Srafan{ 207174993Srafan va_list ap; 208174993Srafan 209174993Srafan va_start(ap, fmt); 210174993Srafan _nc_va_tracef(fmt, ap); 211174993Srafan va_end(ap); 212174993Srafan} 213174993Srafan 214166124Srafan/* Trace 'bool' return-values */ 215166124SrafanNCURSES_EXPORT(NCURSES_BOOL) 216166124Srafan_nc_retrace_bool(NCURSES_BOOL code) 217166124Srafan{ 218166124Srafan T((T_RETURN("%s"), code ? "TRUE" : "FALSE")); 219166124Srafan return code; 220166124Srafan} 221166124Srafan 22250276Speter/* Trace 'int' return-values */ 22376726SpeterNCURSES_EXPORT(int) 22462449Speter_nc_retrace_int(int code) 22550276Speter{ 22662449Speter T((T_RETURN("%d"), code)); 22762449Speter return code; 22850276Speter} 22950276Speter 230166124Srafan/* Trace 'unsigned' return-values */ 231166124SrafanNCURSES_EXPORT(unsigned) 232166124Srafan_nc_retrace_unsigned(unsigned code) 233166124Srafan{ 234166124Srafan T((T_RETURN("%#x"), code)); 235166124Srafan return code; 236166124Srafan} 237166124Srafan 23850276Speter/* Trace 'char*' return-values */ 23976726SpeterNCURSES_EXPORT(char *) 24062449Speter_nc_retrace_ptr(char *code) 24150276Speter{ 24262449Speter T((T_RETURN("%s"), _nc_visbuf(code))); 24362449Speter return code; 24450276Speter} 24550276Speter 246166124Srafan/* Trace 'const char*' return-values */ 247166124SrafanNCURSES_EXPORT(const char *) 248166124Srafan_nc_retrace_cptr(const char *code) 249166124Srafan{ 250166124Srafan T((T_RETURN("%s"), _nc_visbuf(code))); 251166124Srafan return code; 252166124Srafan} 253166124Srafan 254166124Srafan/* Trace 'NCURSES_CONST void*' return-values */ 255166124SrafanNCURSES_EXPORT(NCURSES_CONST void *) 256166124Srafan_nc_retrace_cvoid_ptr(NCURSES_CONST void *code) 257166124Srafan{ 258166124Srafan T((T_RETURN("%p"), code)); 259166124Srafan return code; 260166124Srafan} 261166124Srafan 262166124Srafan/* Trace 'void*' return-values */ 263166124SrafanNCURSES_EXPORT(void *) 264166124Srafan_nc_retrace_void_ptr(void *code) 265166124Srafan{ 266166124Srafan T((T_RETURN("%p"), code)); 267166124Srafan return code; 268166124Srafan} 269166124Srafan 27097049Speter/* Trace 'SCREEN *' return-values */ 27197049SpeterNCURSES_EXPORT(SCREEN *) 272166124Srafan_nc_retrace_sp(SCREEN *code) 27397049Speter{ 27497049Speter T((T_RETURN("%p"), code)); 27597049Speter return code; 27697049Speter} 27797049Speter 27850276Speter/* Trace 'WINDOW *' return-values */ 27976726SpeterNCURSES_EXPORT(WINDOW *) 28062449Speter_nc_retrace_win(WINDOW *code) 28150276Speter{ 28262449Speter T((T_RETURN("%p"), code)); 28362449Speter return code; 28450276Speter} 285174993Srafan 286174993Srafan#if USE_REENTRANT 287174993Srafan/* 288174993Srafan * Check if the given trace-mask is enabled. 289174993Srafan * 290174993Srafan * This function may be called from within one of the functions that fills 291174993Srafan * in parameters for _tracef(), but in that case we do not want to lock the 292174993Srafan * mutex, since it is already locked. 293174993Srafan */ 294174993SrafanNCURSES_EXPORT(int) 295174993Srafan_nc_use_tracef(unsigned mask) 296174993Srafan{ 297174993Srafan bool result = FALSE; 298174993Srafan 299174993Srafan _nc_lock_global(tst_tracef); 300174993Srafan if (!_nc_globals.nested_tracef++) { 301184989Srafan if ((result = (_nc_tracing & (mask))) != 0 302184989Srafan && _nc_try_global(tracef) == 0) { 303174993Srafan /* we will call _nc_locked_tracef(), no nesting so far */ 304174993Srafan } else { 305174993Srafan /* we will not call _nc_locked_tracef() */ 306174993Srafan _nc_globals.nested_tracef = 0; 307174993Srafan } 308174993Srafan } else { 309174993Srafan /* we may call _nc_locked_tracef(), but with nested_tracef > 0 */ 310174993Srafan result = (_nc_tracing & (mask)); 311174993Srafan } 312174993Srafan _nc_unlock_global(tst_tracef); 313174993Srafan return result; 314174993Srafan} 315174993Srafan 316174993Srafan/* 317174993Srafan * We call this if _nc_use_tracef() returns true, which means we must unlock 318174993Srafan * the tracef mutex. 319174993Srafan */ 320174993SrafanNCURSES_EXPORT(void) 321174993Srafan_nc_locked_tracef(const char *fmt,...) 322174993Srafan{ 323174993Srafan va_list ap; 324174993Srafan 325174993Srafan va_start(ap, fmt); 326174993Srafan _nc_va_tracef(fmt, ap); 327174993Srafan va_end(ap); 328174993Srafan 329174993Srafan if (--(_nc_globals.nested_tracef) == 0) 330174993Srafan _nc_unlock_global(tracef); 331174993Srafan} 332174993Srafan#endif /* USE_REENTRANT */ 333174993Srafan 33450276Speter#endif /* TRACE */ 335