00001 /****************************************************************************** 00002 * * 00003 * N O T I C E * 00004 * * 00005 * Copyright Abandoned, 1987, Fred Fish * 00006 * * 00007 * * 00008 * This previously copyrighted work has been placed into the public * 00009 * domain by the author and may be freely used for any purpose, * 00010 * private or commercial. * 00011 * * 00012 * Because of the number of inquiries I was receiving about the use * 00013 * of this product in commercially developed works I have decided to * 00014 * simply make it public domain to further its unrestricted use. I * 00015 * specifically would be most happy to see this material become a * 00016 * part of the standard Unix distributions by AT&T and the Berkeley * 00017 * Computer Science Research Group, and a standard part of the GNU * 00018 * system from the Free Software Foundation. * 00019 * * 00020 * I would appreciate it, as a courtesy, if this notice is left in * 00021 * all copies and derivative works. Thank you. * 00022 * * 00023 * The author makes no warranty of any kind with respect to this * 00024 * product and explicitly disclaims any implied warranties of mer- * 00025 * chantability or fitness for any particular purpose. * 00026 * * 00027 ****************************************************************************** 00028 */ 00029 00030 /* 00031 * FILE 00032 * 00033 * dbug.c runtime support routines for dbug package 00034 * 00035 * SCCS 00036 * 00037 * @(#)dbug.c 1.25 7/25/89 00038 * 00039 * DESCRIPTION 00040 * 00041 * These are the runtime support routines for the dbug package. 00042 * The dbug package has two main components; the user include 00043 * file containing various macro definitions, and the runtime 00044 * support routines which are called from the macro expansions. 00045 * 00046 * Externally visible functions in the runtime support module 00047 * use the naming convention pattern "_db_xx...xx_", thus 00048 * they are unlikely to collide with user defined function names. 00049 * 00050 * AUTHOR(S) 00051 * 00052 * Fred Fish (base code) 00053 * Enhanced Software Technologies, Tempe, AZ 00054 * asuvax!mcdphx!estinc!fnf 00055 * 00056 * Binayak Banerjee (profiling enhancements) 00057 * seismo!bpa!sjuvax!bbanerje 00058 * 00059 * Michael Widenius: 00060 * DBUG_DUMP - To dump a block of memory. 00061 * PUSH_FLAG "O" - To be used insted of "o" if we 00062 * want flushing after each write 00063 * PUSH_FLAG "A" - as 'O', but we will append to the out file instead 00064 * of creating a new one. 00065 * Check of malloc on entry/exit (option "S") 00066 * 00067 * DBUG_EXECUTE_IF 00068 * incremental mode (-#+t:-d,info ...) 00069 * DBUG_SET, _db_explain_ 00070 * thread-local settings 00071 * 00072 */ 00073 00074 #include <my_global.h> 00075 00076 /* This file won't compile unless DBUG_OFF is undefined locally */ 00077 #ifdef DBUG_OFF 00078 #undef DBUG_OFF 00079 #endif 00080 00081 #include <m_string.h> 00082 #include <errno.h> 00083 #if defined(MSDOS) || defined(__WIN__) 00084 #include <process.h> 00085 #endif 00086 00087 /* 00088 * Manifest constants which may be "tuned" if desired. 00089 */ 00090 00091 #define PRINTBUF 1024 /* Print buffer size */ 00092 #define INDENT 2 /* Indentation per trace level */ 00093 #define MAXDEPTH 200 /* Maximum trace depth default */ 00094 00095 /* 00096 * The following flags are used to determine which 00097 * capabilities the user has enabled with the settings 00098 * push macro. 00099 */ 00100 00101 #define TRACE_ON 000001 /* Trace enabled */ 00102 #define DEBUG_ON 000002 /* Debug enabled */ 00103 #define FILE_ON 000004 /* File name print enabled */ 00104 #define LINE_ON 000010 /* Line number print enabled */ 00105 #define DEPTH_ON 000020 /* Function nest level print enabled */ 00106 #define PROCESS_ON 000040 /* Process name print enabled */ 00107 #define NUMBER_ON 000100 /* Number each line of output */ 00108 #define PROFILE_ON 000200 /* Print out profiling code */ 00109 #define PID_ON 000400 /* Identify each line with process id */ 00110 #define TIMESTAMP_ON 001000 /* timestamp every line of output */ 00111 #define SANITY_CHECK_ON 002000 /* Check safemalloc on DBUG_ENTER */ 00112 #define FLUSH_ON_WRITE 004000 /* Flush on every write */ 00113 #define OPEN_APPEND 010000 /* Open for append */ 00114 00115 #define TRACING (cs->stack->flags & TRACE_ON) 00116 #define DEBUGGING (cs->stack->flags & DEBUG_ON) 00117 #define PROFILING (cs->stack->flags & PROFILE_ON) 00118 00119 /* 00120 * Typedefs to make things more obvious. 00121 */ 00122 00123 #ifndef __WIN__ 00124 typedef int BOOLEAN; 00125 #else 00126 #define BOOLEAN BOOL 00127 #endif 00128 00129 /* 00130 * Make it easy to change storage classes if necessary. 00131 */ 00132 00133 #define IMPORT extern /* Names defined externally */ 00134 #define EXPORT /* Allocated here, available globally */ 00135 #define AUTO auto /* Names to be allocated on stack */ 00136 #define REGISTER register /* Names to be placed in registers */ 00137 00138 /* 00139 * The default file for profiling. Could also add another flag 00140 * (G?) which allowed the user to specify this. 00141 * 00142 * If the automatic variables get allocated on the stack in 00143 * reverse order from their declarations, then define AUTOS_REVERSE. 00144 * This is used by the code that keeps track of stack usage. For 00145 * forward allocation, the difference in the dbug frame pointers 00146 * represents stack used by the callee function. For reverse allocation, 00147 * the difference represents stack used by the caller function. 00148 * 00149 */ 00150 00151 #define PROF_FILE "dbugmon.out" 00152 #define PROF_EFMT "E\t%ld\t%s\n" 00153 #define PROF_SFMT "S\t%lx\t%lx\t%s\n" 00154 #define PROF_XFMT "X\t%ld\t%s\n" 00155 00156 #ifdef M_I386 /* predefined by xenix 386 compiler */ 00157 #define AUTOS_REVERSE 1 00158 #endif 00159 00160 /* 00161 * Externally supplied functions. 00162 */ 00163 00164 #ifndef HAVE_PERROR 00165 static void perror(); /* Fake system/library error print routine */ 00166 #endif 00167 00168 IMPORT int _sanity(const char *file,uint line); /* safemalloc sanity checker */ 00169 00170 /* 00171 * The user may specify a list of functions to trace or 00172 * debug. These lists are kept in a linear linked list, 00173 * a very simple implementation. 00174 */ 00175 00176 struct link { 00177 struct link *next_link; /* Pointer to the next link */ 00178 char str[1]; /* Pointer to link's contents */ 00179 }; 00180 00181 /* 00182 * Debugging settings can be pushed or popped off of a 00183 * stack which is implemented as a linked list. Note 00184 * that the head of the list is the current settings and the 00185 * stack is pushed by adding a new settings to the head of the 00186 * list or popped by removing the first link. 00187 * 00188 * Note: if out_file is NULL, the other fields are not initialized at all! 00189 */ 00190 00191 struct settings { 00192 int flags; /* Current settings flags */ 00193 int maxdepth; /* Current maximum trace depth */ 00194 uint delay; /* Delay after each output line */ 00195 int sub_level; /* Sub this from code_state->level */ 00196 FILE *out_file; /* Current output stream */ 00197 FILE *prof_file; /* Current profiling stream */ 00198 char name[FN_REFLEN]; /* Name of output file */ 00199 struct link *functions; /* List of functions */ 00200 struct link *p_functions; /* List of profiled functions */ 00201 struct link *keywords; /* List of debug keywords */ 00202 struct link *processes; /* List of process names */ 00203 struct settings *next; /* Next settings in the list */ 00204 }; 00205 00206 #define is_shared(S, V) ((S)->next && (S)->next->V == (S)->V) 00207 00208 /* 00209 * Local variables not seen by user. 00210 */ 00211 00212 00213 static BOOLEAN init_done= FALSE; /* Set to TRUE when initialization done */ 00214 static struct settings init_settings; 00215 static const char *db_process= 0;/* Pointer to process name; argv[0] */ 00216 00217 typedef struct _db_code_state_ { 00218 const char *process; /* Pointer to process name; usually argv[0] */ 00219 const char *func; /* Name of current user function */ 00220 const char *file; /* Name of current user file */ 00221 char **framep; /* Pointer to current frame */ 00222 struct settings *stack; /* debugging settings */ 00223 const char *jmpfunc; /* Remember current function for setjmp */ 00224 const char *jmpfile; /* Remember current file for setjmp */ 00225 int lineno; /* Current debugger output line number */ 00226 int level; /* Current function nesting level */ 00227 int jmplevel; /* Remember nesting level at setjmp() */ 00228 00229 /* 00230 * The following variables are used to hold the state information 00231 * between the call to _db_pargs_() and _db_doprnt_(), during 00232 * expansion of the DBUG_PRINT macro. This is the only macro 00233 * that currently uses these variables. 00234 * 00235 * These variables are currently used only by _db_pargs_() and 00236 * _db_doprnt_(). 00237 */ 00238 00239 uint u_line; /* User source code line number */ 00240 int locked; /* If locked with _db_lock_file_ */ 00241 const char *u_keyword; /* Keyword for current macro */ 00242 } CODE_STATE; 00243 00244 /* 00245 The test below is so we could call functions with DBUG_ENTER before 00246 my_thread_init(). 00247 */ 00248 #define get_code_state_or_return if (!cs && !((cs=code_state()))) return 00249 00250 /* Handling lists */ 00251 static struct link *ListAdd(struct link *, const char *, const char *); 00252 static struct link *ListDel(struct link *, const char *, const char *); 00253 static struct link *ListCopy(struct link *); 00254 static void FreeList(struct link *linkp); 00255 00256 /* OpenClose debug output stream */ 00257 static void DBUGOpenFile(CODE_STATE *,const char *, const char *, int); 00258 static void DBUGCloseFile(CODE_STATE *cs, FILE *fp); 00259 /* Push current debug settings */ 00260 static void PushState(CODE_STATE *cs); 00261 /* Free memory associated with debug state. */ 00262 static void FreeState (CODE_STATE *cs, struct settings *state); 00263 /* Test for tracing enabled */ 00264 static BOOLEAN DoTrace(CODE_STATE *cs); 00265 00266 /* Test to see if file is writable */ 00267 #if !(!defined(HAVE_ACCESS) || defined(MSDOS)) 00268 static BOOLEAN Writable(char *pathname); 00269 /* Change file owner and group */ 00270 static void ChangeOwner(CODE_STATE *cs, char *pathname); 00271 /* Allocate memory for runtime support */ 00272 #endif 00273 00274 static void DoPrefix(CODE_STATE *cs, uint line); 00275 00276 static char *DbugMalloc(size_t size); 00277 static const char *BaseName(const char *pathname); 00278 static void Indent(CODE_STATE *cs, int indent); 00279 static BOOLEAN InList(struct link *linkp,const char *cp); 00280 static void dbug_flush(CODE_STATE *); 00281 static void DbugExit(const char *why); 00282 static const char *DbugStrTok(const char *s); 00283 00284 #ifndef THREAD 00285 /* Open profile output stream */ 00286 static FILE *OpenProfile(CODE_STATE *cs, const char *name); 00287 /* Profile if asked for it */ 00288 static BOOLEAN DoProfile(CODE_STATE *); 00289 /* Return current user time (ms) */ 00290 static unsigned long Clock(void); 00291 #endif 00292 00293 /* 00294 * Miscellaneous printf format strings. 00295 */ 00296 00297 #define ERR_MISSING_RETURN "%s: missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n" 00298 #define ERR_OPEN "%s: can't open debug output stream \"%s\": " 00299 #define ERR_CLOSE "%s: can't close debug file: " 00300 #define ERR_ABORT "%s: debugger aborting because %s\n" 00301 #define ERR_CHOWN "%s: can't change owner/group of \"%s\": " 00302 00303 /* 00304 * Macros and defines for testing file accessibility under UNIX and MSDOS. 00305 */ 00306 00307 #undef EXISTS 00308 #if !defined(HAVE_ACCESS) || defined(MSDOS) 00309 #define EXISTS(pathname) (FALSE) /* Assume no existance */ 00310 #define Writable(name) (TRUE) 00311 #else 00312 #define EXISTS(pathname) (access(pathname, F_OK) == 0) 00313 #define WRITABLE(pathname) (access(pathname, W_OK) == 0) 00314 #endif 00315 #ifndef MSDOS 00316 #define ChangeOwner(cs,name) 00317 #endif 00318 00319 /* 00320 ** Macros to allow dbugging with threads 00321 */ 00322 00323 #ifdef THREAD 00324 #include <my_pthread.h> 00325 pthread_mutex_t THR_LOCK_dbug; 00326 00327 static CODE_STATE *code_state(void) 00328 { 00329 CODE_STATE *cs=0; 00330 struct st_my_thread_var *tmp; 00331 00332 if (!init_done) 00333 { 00334 pthread_mutex_init(&THR_LOCK_dbug,MY_MUTEX_INIT_FAST); 00335 bzero(&init_settings, sizeof(init_settings)); 00336 init_settings.out_file=stderr; 00337 init_settings.flags=OPEN_APPEND; 00338 init_done=TRUE; 00339 } 00340 00341 if ((tmp=my_thread_var)) 00342 { 00343 if (!(cs=(CODE_STATE *) tmp->dbug)) 00344 { 00345 cs=(CODE_STATE*) DbugMalloc(sizeof(*cs)); 00346 bzero((char*) cs,sizeof(*cs)); 00347 cs->process= db_process ? db_process : "dbug"; 00348 cs->func="?func"; 00349 cs->file="?file"; 00350 cs->stack=&init_settings; 00351 tmp->dbug=(gptr) cs; 00352 } 00353 } 00354 return cs; 00355 } 00356 00357 #else /* !THREAD */ 00358 00359 static CODE_STATE static_code_state= 00360 { 00361 "dbug", "?func", "?file", NULL, &init_settings, 00362 NullS, NullS, 0,0,0,0,0,NullS 00363 }; 00364 00365 static CODE_STATE *code_state(void) 00366 { 00367 if (!init_done) 00368 { 00369 bzero(&init_settings, sizeof(init_settings)); 00370 init_settings.out_file=stderr; 00371 init_settings.flags=OPEN_APPEND; 00372 init_done=TRUE; 00373 } 00374 return &static_code_state; 00375 } 00376 00377 #define pthread_mutex_lock(A) {} 00378 #define pthread_mutex_unlock(A) {} 00379 #endif 00380 00381 /* 00382 * Translate some calls among different systems. 00383 */ 00384 00385 #ifdef HAVE_SLEEP 00386 /* sleep() wants seconds */ 00387 #define Delay(A) sleep(((uint) A)/10) 00388 #else 00389 #define Delay(A) (0) 00390 #endif 00391 00392 /* 00393 * FUNCTION 00394 * 00395 * _db_process_ give the name to the current process/thread 00396 * 00397 * SYNOPSIS 00398 * 00399 * VOID _db_push_(name) 00400 * char *name; 00401 * 00402 */ 00403 00404 void _db_process_(const char *name) 00405 { 00406 CODE_STATE *cs=0; 00407 00408 if (!db_process) 00409 db_process= name; 00410 00411 get_code_state_or_return; 00412 cs->process= name; 00413 } 00414 00415 /* 00416 * FUNCTION 00417 * 00418 * _db_set_ set current debugger settings 00419 * 00420 * SYNOPSIS 00421 * 00422 * VOID _db_set_(control) 00423 * char *control; 00424 * 00425 * DESCRIPTION 00426 * 00427 * Given pointer to a debug control string in "control", 00428 * parses the control string, and sets 00429 * up a current debug settings. 00430 * 00431 * The debug control string is a sequence of colon separated fields 00432 * as follows: 00433 * 00434 * [+]<field_1>:<field_2>:...:<field_N> 00435 * 00436 * Each field consists of a mandatory flag character followed by 00437 * an optional "," and comma separated list of modifiers: 00438 * 00439 * [sign]flag[,modifier,modifier,...,modifier] 00440 * 00441 * See the manual for the list of supported signs, flags, and modifiers 00442 * 00443 * For convenience, any leading "-#" is stripped off. 00444 * 00445 */ 00446 00447 void _db_set_(CODE_STATE *cs, const char *control) 00448 { 00449 const char *end; 00450 int rel=0; 00451 00452 get_code_state_or_return; 00453 00454 if (control[0] == '-' && control[1] == '#') 00455 control+=2; 00456 00457 rel= control[0] == '+' || control[0] == '-'; 00458 if (!rel || (!cs->stack->out_file && !cs->stack->next)) 00459 { 00460 cs->stack->flags= 0; 00461 cs->stack->delay= 0; 00462 cs->stack->maxdepth= 0; 00463 cs->stack->sub_level= 0; 00464 cs->stack->out_file= stderr; 00465 cs->stack->prof_file= NULL; 00466 cs->stack->functions= NULL; 00467 cs->stack->p_functions= NULL; 00468 cs->stack->keywords= NULL; 00469 cs->stack->processes= NULL; 00470 } 00471 else if (!cs->stack->out_file) 00472 { 00473 cs->stack->flags= cs->stack->next->flags; 00474 cs->stack->delay= cs->stack->next->delay; 00475 cs->stack->maxdepth= cs->stack->next->maxdepth; 00476 cs->stack->sub_level= cs->stack->next->sub_level; 00477 strcpy(cs->stack->name, cs->stack->next->name); 00478 cs->stack->out_file= cs->stack->next->out_file; 00479 cs->stack->prof_file= cs->stack->next->prof_file; 00480 if (cs->stack->next == &init_settings) 00481 { 00482 /* never share with the global parent - it can change under your feet */ 00483 cs->stack->functions= ListCopy(init_settings.functions); 00484 cs->stack->p_functions= ListCopy(init_settings.p_functions); 00485 cs->stack->keywords= ListCopy(init_settings.keywords); 00486 cs->stack->processes= ListCopy(init_settings.processes); 00487 } 00488 else 00489 { 00490 cs->stack->functions= cs->stack->next->functions; 00491 cs->stack->p_functions= cs->stack->next->p_functions; 00492 cs->stack->keywords= cs->stack->next->keywords; 00493 cs->stack->processes= cs->stack->next->processes; 00494 } 00495 } 00496 00497 end= DbugStrTok(control); 00498 while (1) 00499 { 00500 int c, sign= (*control == '+') ? 1 : (*control == '-') ? -1 : 0; 00501 if (sign) control++; 00502 if (!rel) sign=0; 00503 c= *control++; 00504 if (*control == ',') control++; 00505 /* XXX when adding new cases here, don't forget _db_explain_ ! */ 00506 switch (c) { 00507 case 'd': 00508 if (sign < 0 && control == end) 00509 { 00510 if (!is_shared(cs->stack, keywords)) 00511 FreeList(cs->stack->keywords); 00512 cs->stack->keywords=NULL; 00513 cs->stack->flags &= ~DEBUG_ON; 00514 break; 00515 } 00516 if (rel && is_shared(cs->stack, keywords)) 00517 cs->stack->keywords= ListCopy(cs->stack->keywords); 00518 if (sign < 0) 00519 { 00520 if (DEBUGGING) 00521 cs->stack->keywords= ListDel(cs->stack->keywords, control, end); 00522 break; 00523 } 00524 cs->stack->keywords= ListAdd(cs->stack->keywords, control, end); 00525 cs->stack->flags |= DEBUG_ON; 00526 break; 00527 case 'D': 00528 cs->stack->delay= atoi(control); 00529 break; 00530 case 'f': 00531 if (sign < 0 && control == end) 00532 { 00533 if (!is_shared(cs->stack,functions)) 00534 FreeList(cs->stack->functions); 00535 cs->stack->functions=NULL; 00536 break; 00537 } 00538 if (rel && is_shared(cs->stack,functions)) 00539 cs->stack->functions= ListCopy(cs->stack->functions); 00540 if (sign < 0) 00541 cs->stack->functions= ListDel(cs->stack->functions, control, end); 00542 else 00543 cs->stack->functions= ListAdd(cs->stack->functions, control, end); 00544 break; 00545 case 'F': 00546 if (sign < 0) 00547 cs->stack->flags &= ~FILE_ON; 00548 else 00549 cs->stack->flags |= FILE_ON; 00550 break; 00551 case 'i': 00552 if (sign < 0) 00553 cs->stack->flags &= ~PID_ON; 00554 else 00555 cs->stack->flags |= PID_ON; 00556 break; 00557 #ifndef THREAD 00558 case 'g': 00559 if (OpenProfile(cs, PROF_FILE)) 00560 { 00561 cs->stack->flags |= PROFILE_ON; 00562 cs->stack->p_functions= ListAdd(cs->stack->p_functions, control, end); 00563 } 00564 break; 00565 #endif 00566 case 'L': 00567 if (sign < 0) 00568 cs->stack->flags &= ~LINE_ON; 00569 else 00570 cs->stack->flags |= LINE_ON; 00571 break; 00572 case 'n': 00573 if (sign < 0) 00574 cs->stack->flags &= ~DEPTH_ON; 00575 else 00576 cs->stack->flags |= DEPTH_ON; 00577 break; 00578 case 'N': 00579 if (sign < 0) 00580 cs->stack->flags &= ~NUMBER_ON; 00581 else 00582 cs->stack->flags |= NUMBER_ON; 00583 break; 00584 case 'A': 00585 case 'O': 00586 cs->stack->flags |= FLUSH_ON_WRITE; 00587 /* fall through */ 00588 case 'a': 00589 case 'o': 00590 if (sign < 0) 00591 { 00592 if (!is_shared(cs->stack, out_file)) 00593 DBUGCloseFile(cs, cs->stack->out_file); 00594 cs->stack->flags &= ~FLUSH_ON_WRITE; 00595 cs->stack->out_file= stderr; 00596 break; 00597 } 00598 if (c == 'a' || c == 'A') 00599 cs->stack->flags |= OPEN_APPEND; 00600 else 00601 cs->stack->flags &= ~OPEN_APPEND; 00602 if (control != end) 00603 DBUGOpenFile(cs, control, end, cs->stack->flags & OPEN_APPEND); 00604 else 00605 DBUGOpenFile(cs, "-",0,0); 00606 break; 00607 case 'p': 00608 if (sign < 0 && control == end) 00609 { 00610 if (!is_shared(cs->stack,processes)) 00611 FreeList(cs->stack->processes); 00612 cs->stack->processes=NULL; 00613 break; 00614 } 00615 if (rel && is_shared(cs->stack, processes)) 00616 cs->stack->processes= ListCopy(cs->stack->processes); 00617 if (sign < 0) 00618 cs->stack->processes= ListDel(cs->stack->processes, control, end); 00619 else 00620 cs->stack->processes= ListAdd(cs->stack->processes, control, end); 00621 break; 00622 case 'P': 00623 if (sign < 0) 00624 cs->stack->flags &= ~PROCESS_ON; 00625 else 00626 cs->stack->flags |= PROCESS_ON; 00627 break; 00628 case 'r': 00629 cs->stack->sub_level= cs->level; 00630 break; 00631 case 't': 00632 if (sign < 0) 00633 { 00634 if (control != end) 00635 cs->stack->maxdepth-= atoi(control); 00636 else 00637 cs->stack->maxdepth= 0; 00638 } 00639 else 00640 { 00641 if (control != end) 00642 cs->stack->maxdepth+= atoi(control); 00643 else 00644 cs->stack->maxdepth= MAXDEPTH; 00645 } 00646 if (cs->stack->maxdepth > 0) 00647 cs->stack->flags |= TRACE_ON; 00648 else 00649 cs->stack->flags &= ~TRACE_ON; 00650 break; 00651 case 'T': 00652 if (sign < 0) 00653 cs->stack->flags &= ~TIMESTAMP_ON; 00654 else 00655 cs->stack->flags |= TIMESTAMP_ON; 00656 break; 00657 case 'S': 00658 if (sign < 0) 00659 cs->stack->flags &= ~SANITY_CHECK_ON; 00660 else 00661 cs->stack->flags |= SANITY_CHECK_ON; 00662 break; 00663 } 00664 if (!*end) 00665 break; 00666 control=end+1; 00667 end= DbugStrTok(control); 00668 } 00669 } 00670 00671 00672 /* 00673 * FUNCTION 00674 * 00675 * _db_push_ push current debugger settings and set up new one 00676 * 00677 * SYNOPSIS 00678 * 00679 * VOID _db_push_(control) 00680 * char *control; 00681 * 00682 * DESCRIPTION 00683 * 00684 * Given pointer to a debug control string in "control", pushes 00685 * the current debug settings, parses the control string, and sets 00686 * up a new debug settings with _db_set_() 00687 * 00688 */ 00689 00690 void _db_push_(const char *control) 00691 { 00692 CODE_STATE *cs=0; 00693 get_code_state_or_return; 00694 PushState(cs); 00695 _db_set_(cs, control); 00696 } 00697 00698 /* 00699 * FUNCTION 00700 * 00701 * _db_set_init_ set initial debugger settings 00702 * 00703 * SYNOPSIS 00704 * 00705 * VOID _db_set_init_(control) 00706 * char *control; 00707 * 00708 * DESCRIPTION 00709 * see _db_set_ 00710 * 00711 */ 00712 00713 void _db_set_init_(const char *control) 00714 { 00715 CODE_STATE cs; 00716 bzero((char*) &cs,sizeof(cs)); 00717 cs.stack=&init_settings; 00718 _db_set_(&cs, control); 00719 } 00720 00721 /* 00722 * FUNCTION 00723 * 00724 * _db_pop_ pop the debug stack 00725 * 00726 * DESCRIPTION 00727 * 00728 * Pops the debug stack, returning the debug settings to its 00729 * condition prior to the most recent _db_push_ invocation. 00730 * Note that the pop will fail if it would remove the last 00731 * valid settings from the stack. This prevents user errors 00732 * in the push/pop sequence from screwing up the debugger. 00733 * Maybe there should be some kind of warning printed if the 00734 * user tries to pop too many states. 00735 * 00736 */ 00737 00738 void _db_pop_() 00739 { 00740 struct settings *discard; 00741 CODE_STATE *cs=0; 00742 00743 get_code_state_or_return; 00744 00745 discard= cs->stack; 00746 if (discard->next != NULL) 00747 { 00748 cs->stack= discard->next; 00749 FreeState(cs, discard); 00750 } 00751 } 00752 00753 /* 00754 * FUNCTION 00755 * 00756 * _db_explain_ generates 'control' string for the current settings 00757 * 00758 * RETURN 00759 * 0 - ok 00760 * 1 - buffer too short, output truncated 00761 * 00762 */ 00763 00764 /* helper macros */ 00765 #define char_to_buf(C) do { \ 00766 *buf++=(C); \ 00767 if (buf >= end) goto overflow; \ 00768 } while (0) 00769 #define str_to_buf(S) do { \ 00770 char_to_buf(','); \ 00771 buf=strnmov(buf, (S), len+1); \ 00772 if (buf >= end) goto overflow; \ 00773 } while (0) 00774 #define list_to_buf(l) do { \ 00775 struct link *listp=(l); \ 00776 while (listp) \ 00777 { \ 00778 str_to_buf(listp->str); \ 00779 listp=listp->next_link; \ 00780 } \ 00781 } while (0) 00782 #define int_to_buf(i) do { \ 00783 char b[50]; \ 00784 int10_to_str((i), b, 10); \ 00785 str_to_buf(b); \ 00786 } while (0) 00787 #define colon_to_buf do { \ 00788 if (buf != start) char_to_buf(':'); \ 00789 } while(0) 00790 #define op_int_to_buf(C, val, def) do { \ 00791 if ((val) != (def)) \ 00792 { \ 00793 colon_to_buf; \ 00794 char_to_buf((C)); \ 00795 int_to_buf(val); \ 00796 } \ 00797 } while (0) 00798 #define op_intf_to_buf(C, val, def, cond) do { \ 00799 if ((cond)) \ 00800 { \ 00801 colon_to_buf; \ 00802 char_to_buf((C)); \ 00803 if ((val) != (def)) int_to_buf(val); \ 00804 } \ 00805 } while (0) 00806 #define op_str_to_buf(C, val, cond) do { \ 00807 if ((cond)) \ 00808 { \ 00809 char *s=(val); \ 00810 colon_to_buf; \ 00811 char_to_buf((C)); \ 00812 if (*s) str_to_buf(s); \ 00813 } \ 00814 } while (0) 00815 #define op_list_to_buf(C, val, cond) do { \ 00816 if ((cond)) \ 00817 { \ 00818 colon_to_buf; \ 00819 char_to_buf((C)); \ 00820 list_to_buf(val); \ 00821 } \ 00822 } while (0) 00823 #define op_bool_to_buf(C, cond) do { \ 00824 if ((cond)) \ 00825 { \ 00826 colon_to_buf; \ 00827 char_to_buf((C)); \ 00828 } \ 00829 } while (0) 00830 00831 int _db_explain_ (CODE_STATE *cs, char *buf, int len) 00832 { 00833 char *start=buf, *end=buf+len-4; 00834 00835 get_code_state_or_return *buf=0; 00836 00837 op_list_to_buf('d', cs->stack->keywords, DEBUGGING); 00838 op_int_to_buf ('D', cs->stack->delay, 0); 00839 op_list_to_buf('f', cs->stack->functions, cs->stack->functions); 00840 op_bool_to_buf('F', cs->stack->flags & FILE_ON); 00841 op_bool_to_buf('i', cs->stack->flags & PID_ON); 00842 op_list_to_buf('g', cs->stack->p_functions, PROFILING); 00843 op_bool_to_buf('L', cs->stack->flags & LINE_ON); 00844 op_bool_to_buf('n', cs->stack->flags & DEPTH_ON); 00845 op_bool_to_buf('N', cs->stack->flags & NUMBER_ON); 00846 op_str_to_buf( 00847 ((cs->stack->flags & FLUSH_ON_WRITE ? 0 : 32) | 00848 (cs->stack->flags & OPEN_APPEND ? 'A' : 'O')), 00849 cs->stack->name, cs->stack->out_file != stderr); 00850 op_list_to_buf('p', cs->stack->processes, cs->stack->processes); 00851 op_bool_to_buf('P', cs->stack->flags & PROCESS_ON); 00852 op_bool_to_buf('r', cs->stack->sub_level != 0); 00853 op_intf_to_buf('t', cs->stack->maxdepth, MAXDEPTH, TRACING); 00854 op_bool_to_buf('T', cs->stack->flags & TIMESTAMP_ON); 00855 op_bool_to_buf('S', cs->stack->flags & SANITY_CHECK_ON); 00856 00857 *buf= '\0'; 00858 return 0; 00859 00860 overflow: 00861 *end++= '.'; 00862 *end++= '.'; 00863 *end++= '.'; 00864 *end= '\0'; 00865 return 1; 00866 } 00867 00868 #undef char_to_buf 00869 #undef str_to_buf 00870 #undef list_to_buf 00871 #undef int_to_buf 00872 #undef colon_to_buf 00873 #undef op_int_to_buf 00874 #undef op_intf_to_buf 00875 #undef op_str_to_buf 00876 #undef op_list_to_buf 00877 #undef op_bool_to_buf 00878 00879 /* 00880 * FUNCTION 00881 * 00882 * _db_explain_init_ explain initial debugger settings 00883 * 00884 * DESCRIPTION 00885 * see _db_explain_ 00886 */ 00887 00888 int _db_explain_init_(char *buf, int len) 00889 { 00890 CODE_STATE cs; 00891 bzero((char*) &cs,sizeof(cs)); 00892 cs.stack=&init_settings; 00893 return _db_explain_(&cs, buf, len); 00894 } 00895 00896 /* 00897 * FUNCTION 00898 * 00899 * _db_enter_ process entry point to user function 00900 * 00901 * SYNOPSIS 00902 * 00903 * VOID _db_enter_(_func_, _file_, _line_, 00904 * _sfunc_, _sfile_, _slevel_, _sframep_) 00905 * char *_func_; points to current function name 00906 * char *_file_; points to current file name 00907 * int _line_; called from source line number 00908 * char **_sfunc_; save previous _func_ 00909 * char **_sfile_; save previous _file_ 00910 * int *_slevel_; save previous nesting level 00911 * char ***_sframep_; save previous frame pointer 00912 * 00913 * DESCRIPTION 00914 * 00915 * Called at the beginning of each user function to tell 00916 * the debugger that a new function has been entered. 00917 * Note that the pointers to the previous user function 00918 * name and previous user file name are stored on the 00919 * caller's stack (this is why the ENTER macro must be 00920 * the first "executable" code in a function, since it 00921 * allocates these storage locations). The previous nesting 00922 * level is also stored on the callers stack for internal 00923 * self consistency checks. 00924 * 00925 * Also prints a trace line if tracing is enabled and 00926 * increments the current function nesting depth. 00927 * 00928 * Note that this mechanism allows the debugger to know 00929 * what the current user function is at all times, without 00930 * maintaining an internal stack for the function names. 00931 * 00932 */ 00933 00934 void _db_enter_(const char *_func_, const char *_file_, 00935 uint _line_, const char **_sfunc_, const char **_sfile_, 00936 uint *_slevel_, char ***_sframep_ __attribute__((unused))) 00937 { 00938 int save_errno=errno; 00939 CODE_STATE *cs=0; 00940 get_code_state_or_return; 00941 00942 *_sfunc_= cs->func; 00943 *_sfile_= cs->file; 00944 cs->func= _func_; 00945 cs->file= _file_; 00946 *_slevel_= ++cs->level; 00947 #ifndef THREAD 00948 *_sframep_= cs->framep; 00949 cs->framep= (char **) _sframep_; 00950 if (DoProfile(cs)) 00951 { 00952 long stackused; 00953 if (*cs->framep == NULL) 00954 stackused= 0; 00955 else 00956 { 00957 stackused= ((long)(*cs->framep)) - ((long)(cs->framep)); 00958 stackused= stackused > 0 ? stackused : -stackused; 00959 } 00960 (void) fprintf(cs->stack->prof_file, PROF_EFMT , Clock(), cs->func); 00961 #ifdef AUTOS_REVERSE 00962 (void) fprintf(cs->stack->prof_file, PROF_SFMT, cs->framep, stackused, *_sfunc_); 00963 #else 00964 (void) fprintf(cs->stack->prof_file, PROF_SFMT, (ulong) cs->framep, stackused, 00965 cs->func); 00966 #endif 00967 (void) fflush(cs->stack->prof_file); 00968 } 00969 #endif 00970 if (DoTrace(cs)) 00971 { 00972 if (!cs->locked) 00973 pthread_mutex_lock(&THR_LOCK_dbug); 00974 DoPrefix(cs, _line_); 00975 Indent(cs, cs->level); 00976 (void) fprintf(cs->stack->out_file, ">%s\n", cs->func); 00977 dbug_flush(cs); /* This does a unlock */ 00978 } 00979 #ifdef SAFEMALLOC 00980 if (cs->stack->flags & SANITY_CHECK_ON) 00981 if (_sanity(_file_,_line_)) /* Check of safemalloc */ 00982 cs->stack->flags &= ~SANITY_CHECK_ON; 00983 #endif 00984 errno=save_errno; 00985 } 00986 00987 /* 00988 * FUNCTION 00989 * 00990 * _db_return_ process exit from user function 00991 * 00992 * SYNOPSIS 00993 * 00994 * VOID _db_return_(_line_, _sfunc_, _sfile_, _slevel_) 00995 * int _line_; current source line number 00996 * char **_sfunc_; where previous _func_ is to be retrieved 00997 * char **_sfile_; where previous _file_ is to be retrieved 00998 * int *_slevel_; where previous level was stashed 00999 * 01000 * DESCRIPTION 01001 * 01002 * Called just before user function executes an explicit or implicit 01003 * return. Prints a trace line if trace is enabled, decrements 01004 * the current nesting level, and restores the current function and 01005 * file names from the defunct function's stack. 01006 * 01007 */ 01008 01009 /* helper macro */ 01010 void _db_return_(uint _line_, const char **_sfunc_, 01011 const char **_sfile_, uint *_slevel_) 01012 { 01013 int save_errno=errno; 01014 CODE_STATE *cs=0; 01015 get_code_state_or_return; 01016 01017 if (cs->level != (int) *_slevel_) 01018 { 01019 if (!cs->locked) 01020 pthread_mutex_lock(&THR_LOCK_dbug); 01021 (void) fprintf(cs->stack->out_file, ERR_MISSING_RETURN, cs->process, 01022 cs->func); 01023 dbug_flush(cs); 01024 } 01025 else 01026 { 01027 #ifdef SAFEMALLOC 01028 if (cs->stack->flags & SANITY_CHECK_ON) 01029 { 01030 if (_sanity(*_sfile_,_line_)) 01031 cs->stack->flags &= ~SANITY_CHECK_ON; 01032 } 01033 #endif 01034 #ifndef THREAD 01035 if (DoProfile(cs)) 01036 (void) fprintf(cs->stack->prof_file, PROF_XFMT, Clock(), cs->func); 01037 #endif 01038 if (DoTrace(cs)) 01039 { 01040 if (!cs->locked) 01041 pthread_mutex_lock(&THR_LOCK_dbug); 01042 DoPrefix(cs, _line_); 01043 Indent(cs, cs->level); 01044 (void) fprintf(cs->stack->out_file, "<%s\n", cs->func); 01045 dbug_flush(cs); 01046 } 01047 } 01048 cs->level= *_slevel_-1; 01049 cs->func= *_sfunc_; 01050 cs->file= *_sfile_; 01051 #ifndef THREAD 01052 if (cs->framep != NULL) 01053 cs->framep= (char **) *cs->framep; 01054 #endif 01055 errno=save_errno; 01056 } 01057 01058 01059 /* 01060 * FUNCTION 01061 * 01062 * _db_pargs_ log arguments for subsequent use by _db_doprnt_() 01063 * 01064 * SYNOPSIS 01065 * 01066 * VOID _db_pargs_(_line_, keyword) 01067 * int _line_; 01068 * char *keyword; 01069 * 01070 * DESCRIPTION 01071 * 01072 * The new universal printing macro DBUG_PRINT, which replaces 01073 * all forms of the DBUG_N macros, needs two calls to runtime 01074 * support routines. The first, this function, remembers arguments 01075 * that are used by the subsequent call to _db_doprnt_(). 01076 * 01077 */ 01078 01079 void _db_pargs_(uint _line_, const char *keyword) 01080 { 01081 CODE_STATE *cs=0; 01082 get_code_state_or_return; 01083 cs->u_line= _line_; 01084 cs->u_keyword= (char*) keyword; 01085 } 01086 01087 01088 /* 01089 * FUNCTION 01090 * 01091 * _db_doprnt_ handle print of debug lines 01092 * 01093 * SYNOPSIS 01094 * 01095 * VOID _db_doprnt_(format, va_alist) 01096 * char *format; 01097 * va_dcl; 01098 * 01099 * DESCRIPTION 01100 * 01101 * When invoked via one of the DBUG macros, tests the current keyword 01102 * set by calling _db_pargs_() to see if that macro has been selected 01103 * for processing via the debugger control string, and if so, handles 01104 * printing of the arguments via the format string. The line number 01105 * of the DBUG macro in the source is found in u_line. 01106 * 01107 * Note that the format string SHOULD NOT include a terminating 01108 * newline, this is supplied automatically. 01109 * 01110 */ 01111 01112 #include <stdarg.h> 01113 01114 void _db_doprnt_(const char *format,...) 01115 { 01116 va_list args; 01117 01118 CODE_STATE *cs=0; 01119 get_code_state_or_return; 01120 01121 va_start(args,format); 01122 01123 if (_db_keyword_(cs, cs->u_keyword)) 01124 { 01125 int save_errno=errno; 01126 if (!cs->locked) 01127 pthread_mutex_lock(&THR_LOCK_dbug); 01128 DoPrefix(cs, cs->u_line); 01129 if (TRACING) 01130 Indent(cs, cs->level + 1); 01131 else 01132 (void) fprintf(cs->stack->out_file, "%s: ", cs->func); 01133 (void) fprintf(cs->stack->out_file, "%s: ", cs->u_keyword); 01134 (void) vfprintf(cs->stack->out_file, format, args); 01135 (void) fputc('\n',cs->stack->out_file); 01136 dbug_flush(cs); 01137 errno=save_errno; 01138 } 01139 va_end(args); 01140 } 01141 01142 01143 /* 01144 * FUNCTION 01145 * 01146 * _db_dump_ dump a string in hex 01147 * 01148 * SYNOPSIS 01149 * 01150 * void _db_dump_(_line_,keyword,memory,length) 01151 * int _line_; current source line number 01152 * char *keyword; 01153 * char *memory; Memory to print 01154 * int length; Bytes to print 01155 * 01156 * DESCRIPTION 01157 * Dump N characters in a binary array. 01158 * Is used to examine corrputed memory or arrays. 01159 */ 01160 01161 void _db_dump_(uint _line_, const char *keyword, const char *memory, uint length) 01162 { 01163 int pos; 01164 char dbuff[90]; 01165 01166 CODE_STATE *cs=0; 01167 get_code_state_or_return; 01168 01169 if (_db_keyword_(cs, (char*) keyword)) 01170 { 01171 if (!cs->locked) 01172 pthread_mutex_lock(&THR_LOCK_dbug); 01173 DoPrefix(cs, _line_); 01174 if (TRACING) 01175 { 01176 Indent(cs, cs->level + 1); 01177 pos= min(max(cs->level-cs->stack->sub_level,0)*INDENT,80); 01178 } 01179 else 01180 { 01181 fprintf(cs->stack->out_file, "%s: ", cs->func); 01182 } 01183 sprintf(dbuff,"%s: Memory: 0x%lx Bytes: (%d)\n", 01184 keyword,(ulong) memory, length); 01185 (void) fputs(dbuff,cs->stack->out_file); 01186 01187 pos=0; 01188 while (length-- > 0) 01189 { 01190 uint tmp= *((unsigned char*) memory++); 01191 if ((pos+=3) >= 80) 01192 { 01193 fputc('\n',cs->stack->out_file); 01194 pos=3; 01195 } 01196 fputc(_dig_vec_upper[((tmp >> 4) & 15)], cs->stack->out_file); 01197 fputc(_dig_vec_upper[tmp & 15], cs->stack->out_file); 01198 fputc(' ',cs->stack->out_file); 01199 } 01200 (void) fputc('\n',cs->stack->out_file); 01201 dbug_flush(cs); 01202 } 01203 } 01204 01205 01206 /* 01207 * FUNCTION 01208 * 01209 * ListAdd add to the list modifiers from debug control string 01210 * 01211 * SYNOPSIS 01212 * 01213 * static struct link *ListAdd(listp, ctlp, end) 01214 * struct link *listp; 01215 * char *ctlp; 01216 * char *end; 01217 * 01218 * DESCRIPTION 01219 * 01220 * Given pointer to a comma separated list of strings in "cltp", 01221 * parses the list, and adds it to listp, returning a pointer 01222 * to the new list 01223 * 01224 * Note that since each link is added at the head of the list, 01225 * the final list will be in "reverse order", which is not 01226 * significant for our usage here. 01227 * 01228 */ 01229 01230 static struct link *ListAdd(struct link *head, 01231 const char *ctlp, const char *end) 01232 { 01233 const char *start; 01234 struct link *new_malloc; 01235 int len; 01236 01237 while (ctlp < end) 01238 { 01239 start= ctlp; 01240 while (ctlp < end && *ctlp != ',') 01241 ctlp++; 01242 len=ctlp-start; 01243 new_malloc= (struct link *) DbugMalloc(sizeof(struct link)+len); 01244 memcpy(new_malloc->str, start, len); 01245 new_malloc->str[len]=0; 01246 new_malloc->next_link= head; 01247 head= new_malloc; 01248 ctlp++; 01249 } 01250 return head; 01251 } 01252 01253 /* 01254 * FUNCTION 01255 * 01256 * ListDel remove from the list modifiers in debug control string 01257 * 01258 * SYNOPSIS 01259 * 01260 * static struct link *ListDel(listp, ctlp, end) 01261 * struct link *listp; 01262 * char *ctlp; 01263 * char *end; 01264 * 01265 * DESCRIPTION 01266 * 01267 * Given pointer to a comma separated list of strings in "cltp", 01268 * parses the list, and removes these strings from the listp, 01269 * returning a pointer to the new list. 01270 * 01271 */ 01272 01273 static struct link *ListDel(struct link *head, 01274 const char *ctlp, const char *end) 01275 { 01276 const char *start; 01277 struct link **cur; 01278 int len; 01279 01280 while (ctlp < end) 01281 { 01282 start= ctlp; 01283 while (ctlp < end && *ctlp != ',') 01284 ctlp++; 01285 len=ctlp-start; 01286 cur=&head; 01287 do 01288 { 01289 while (*cur && !strncmp((*cur)->str, start, len)) 01290 { 01291 struct link *delme=*cur; 01292 *cur=(*cur)->next_link; 01293 free((char*)delme); 01294 } 01295 } while (*cur && *(cur=&((*cur)->next_link))); 01296 } 01297 return head; 01298 } 01299 01300 /* 01301 * FUNCTION 01302 * 01303 * ListCopy make a copy of the list 01304 * 01305 * SYNOPSIS 01306 * 01307 * static struct link *ListCopy(orig) 01308 * struct link *orig; 01309 * 01310 * DESCRIPTION 01311 * 01312 * Given pointer to list, which contains a copy of every element from 01313 * the original list. 01314 * 01315 * the orig pointer can be NULL 01316 * 01317 * Note that since each link is added at the head of the list, 01318 * the final list will be in "reverse order", which is not 01319 * significant for our usage here. 01320 * 01321 */ 01322 01323 static struct link *ListCopy(struct link *orig) 01324 { 01325 struct link *new_malloc; 01326 struct link *head; 01327 int len; 01328 01329 head= NULL; 01330 while (orig != NULL) 01331 { 01332 len= strlen(orig->str); 01333 new_malloc= (struct link *) DbugMalloc(sizeof(struct link)+len); 01334 memcpy(new_malloc->str, orig->str, len); 01335 new_malloc->str[len]= 0; 01336 new_malloc->next_link= head; 01337 head= new_malloc; 01338 orig= orig->next_link; 01339 } 01340 return head; 01341 } 01342 01343 /* 01344 * FUNCTION 01345 * 01346 * InList test a given string for member of a given list 01347 * 01348 * SYNOPSIS 01349 * 01350 * static BOOLEAN InList(linkp, cp) 01351 * struct link *linkp; 01352 * char *cp; 01353 * 01354 * DESCRIPTION 01355 * 01356 * Tests the string pointed to by "cp" to determine if it is in 01357 * the list pointed to by "linkp". Linkp points to the first 01358 * link in the list. If linkp is NULL then the string is treated 01359 * as if it is in the list (I.E all strings are in the null list). 01360 * This may seem rather strange at first but leads to the desired 01361 * operation if no list is given. The net effect is that all 01362 * strings will be accepted when there is no list, and when there 01363 * is a list, only those strings in the list will be accepted. 01364 * 01365 */ 01366 01367 static BOOLEAN InList(struct link *linkp, const char *cp) 01368 { 01369 REGISTER struct link *scan; 01370 REGISTER BOOLEAN result; 01371 01372 if (linkp == NULL) 01373 result= TRUE; 01374 else 01375 { 01376 result= FALSE; 01377 for (scan= linkp; scan != NULL; scan= scan->next_link) 01378 { 01379 if (!strcmp(scan->str, cp)) 01380 { 01381 result= TRUE; 01382 break; 01383 } 01384 } 01385 } 01386 return result; 01387 } 01388 01389 01390 /* 01391 * FUNCTION 01392 * 01393 * PushState push current settings onto stack and set up new one 01394 * 01395 * SYNOPSIS 01396 * 01397 * static VOID PushState() 01398 * 01399 * DESCRIPTION 01400 * 01401 * Pushes the current settings on the settings stack, and creates 01402 * a new settings. The new settings is NOT initialized 01403 * 01404 * The settings stack is a linked list of settings, with the new 01405 * settings added at the head. This allows the stack to grow 01406 * to the limits of memory if necessary. 01407 * 01408 */ 01409 01410 static void PushState(CODE_STATE *cs) 01411 { 01412 struct settings *new_malloc; 01413 01414 new_malloc= (struct settings *) DbugMalloc(sizeof(struct settings)); 01415 new_malloc->next= cs->stack; 01416 new_malloc->out_file= NULL; 01417 cs->stack= new_malloc; 01418 } 01419 01420 /* 01421 * FUNCTION 01422 * 01423 * FreeState Free memory associated with a struct state. 01424 * 01425 * SYNOPSIS 01426 * 01427 * static void FreeState (state) 01428 * struct state *state; 01429 * 01430 * DESCRIPTION 01431 * 01432 * Deallocates the memory allocated for various information in a 01433 * state. 01434 * 01435 */ 01436 static void FreeState ( 01437 CODE_STATE *cs, 01438 struct settings *state) 01439 { 01440 if (!is_shared(state, keywords)) 01441 FreeList(state->keywords); 01442 if (!is_shared(state, functions)) 01443 FreeList(state->functions); 01444 if (!is_shared(state, processes)) 01445 FreeList(state->processes); 01446 if (!is_shared(state, p_functions)) 01447 FreeList(state->p_functions); 01448 if (!is_shared(state, out_file)) 01449 DBUGCloseFile(cs, state->out_file); 01450 if (state->prof_file) 01451 DBUGCloseFile(cs, state->prof_file); 01452 free((char *) state); 01453 } 01454 01455 01456 /* 01457 * FUNCTION 01458 * 01459 * _db_end_ End debugging, freeing state stack memory. 01460 * 01461 * SYNOPSIS 01462 * 01463 * static VOID _db_end_ () 01464 * 01465 * DESCRIPTION 01466 * 01467 * Ends debugging, de-allocating the memory allocated to the 01468 * state stack. 01469 * 01470 * To be called at the very end of the program. 01471 * 01472 */ 01473 void _db_end_ () 01474 { 01475 struct settings *discard; 01476 CODE_STATE *cs=0; 01477 01478 get_code_state_or_return; 01479 01480 while((discard= cs->stack) != NULL) { 01481 if(discard == &init_settings) 01482 break; 01483 cs->stack= discard->next; 01484 FreeState (cs, discard); 01485 } 01486 } 01487 01488 01489 /* 01490 * FUNCTION 01491 * 01492 * DoTrace check to see if tracing is current enabled 01493 * 01494 * SYNOPSIS 01495 * 01496 * static BOOLEAN DoTrace(stack) 01497 * 01498 * DESCRIPTION 01499 * 01500 * Checks to see if tracing is enabled based on whether the 01501 * user has specified tracing, the maximum trace depth has 01502 * not yet been reached, the current function is selected, 01503 * and the current process is selected. Returns TRUE if 01504 * tracing is enabled, FALSE otherwise. 01505 * 01506 */ 01507 01508 static BOOLEAN DoTrace(CODE_STATE *cs) 01509 { 01510 return (TRACING && cs->level <= cs->stack->maxdepth && 01511 InList(cs->stack->functions, cs->func) && 01512 InList(cs->stack->processes, cs->process)); 01513 } 01514 01515 01516 /* 01517 * FUNCTION 01518 * 01519 * DoProfile check to see if profiling is current enabled 01520 * 01521 * SYNOPSIS 01522 * 01523 * static BOOLEAN DoProfile() 01524 * 01525 * DESCRIPTION 01526 * 01527 * Checks to see if profiling is enabled based on whether the 01528 * user has specified profiling, the maximum trace depth has 01529 * not yet been reached, the current function is selected, 01530 * and the current process is selected. Returns TRUE if 01531 * profiling is enabled, FALSE otherwise. 01532 * 01533 */ 01534 01535 #ifndef THREAD 01536 static BOOLEAN DoProfile(CODE_STATE *cs) 01537 { 01538 return PROFILING && 01539 cs->level <= cs->stack->maxdepth && 01540 InList(cs->stack->p_functions, cs->func) && 01541 InList(cs->stack->processes, cs->process); 01542 } 01543 #endif 01544 01545 FILE *_db_fp_(void) 01546 { 01547 CODE_STATE *cs=0; 01548 get_code_state_or_return NULL; 01549 return cs->stack->out_file; 01550 } 01551 01552 01553 /* 01554 * FUNCTION 01555 * 01556 * _db_strict_keyword_ test keyword for member of keyword list 01557 * 01558 * SYNOPSIS 01559 * 01560 * BOOLEAN _db_strict_keyword_(keyword) 01561 * char *keyword; 01562 * 01563 * DESCRIPTION 01564 * 01565 * Similar to _db_keyword_, but keyword is NOT accepted if keyword list 01566 * is empty. Used in DBUG_EXECUTE_IF() - for actions that must not be 01567 * executed by default. 01568 * 01569 * Returns TRUE if keyword accepted, FALSE otherwise. 01570 * 01571 */ 01572 01573 BOOLEAN _db_strict_keyword_(const char *keyword) 01574 { 01575 CODE_STATE *cs=0; 01576 get_code_state_or_return FALSE; 01577 if (!DEBUGGING || cs->stack->keywords == NULL) 01578 return FALSE; 01579 return _db_keyword_(cs, keyword); 01580 } 01581 01582 /* 01583 * FUNCTION 01584 * 01585 * _db_keyword_ test keyword for member of keyword list 01586 * 01587 * SYNOPSIS 01588 * 01589 * BOOLEAN _db_keyword_(keyword) 01590 * char *keyword; 01591 * 01592 * DESCRIPTION 01593 * 01594 * Test a keyword to determine if it is in the currently active 01595 * keyword list. As with the function list, a keyword is accepted 01596 * if the list is null, otherwise it must match one of the list 01597 * members. When debugging is not on, no keywords are accepted. 01598 * After the maximum trace level is exceeded, no keywords are 01599 * accepted (this behavior subject to change). Additionally, 01600 * the current function and process must be accepted based on 01601 * their respective lists. 01602 * 01603 * Returns TRUE if keyword accepted, FALSE otherwise. 01604 * 01605 */ 01606 01607 BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword) 01608 { 01609 get_code_state_or_return FALSE; 01610 01611 return (DEBUGGING && 01612 (!TRACING || cs->level <= cs->stack->maxdepth) && 01613 InList(cs->stack->functions, cs->func) && 01614 InList(cs->stack->keywords, keyword) && 01615 InList(cs->stack->processes, cs->process)); 01616 } 01617 01618 /* 01619 * FUNCTION 01620 * 01621 * Indent indent a line to the given indentation level 01622 * 01623 * SYNOPSIS 01624 * 01625 * static VOID Indent(indent) 01626 * int indent; 01627 * 01628 * DESCRIPTION 01629 * 01630 * Indent a line to the given level. Note that this is 01631 * a simple minded but portable implementation. 01632 * There are better ways. 01633 * 01634 * Also, the indent must be scaled by the compile time option 01635 * of character positions per nesting level. 01636 * 01637 */ 01638 01639 static void Indent(CODE_STATE *cs, int indent) 01640 { 01641 REGISTER int count; 01642 01643 indent= max(indent-1-cs->stack->sub_level,0)*INDENT; 01644 for (count= 0; count < indent ; count++) 01645 { 01646 if ((count % INDENT) == 0) 01647 fputc('|',cs->stack->out_file); 01648 else 01649 fputc(' ',cs->stack->out_file); 01650 } 01651 } 01652 01653 01654 /* 01655 * FUNCTION 01656 * 01657 * FreeList free all memory associated with a linked list 01658 * 01659 * SYNOPSIS 01660 * 01661 * static VOID FreeList(linkp) 01662 * struct link *linkp; 01663 * 01664 * DESCRIPTION 01665 * 01666 * Given pointer to the head of a linked list, frees all 01667 * memory held by the list and the members of the list. 01668 * 01669 */ 01670 01671 static void FreeList(struct link *linkp) 01672 { 01673 REGISTER struct link *old; 01674 01675 while (linkp != NULL) 01676 { 01677 old= linkp; 01678 linkp= linkp->next_link; 01679 free((char *) old); 01680 } 01681 } 01682 01683 01684 /* 01685 * FUNCTION 01686 * 01687 * DoPrefix print debugger line prefix prior to indentation 01688 * 01689 * SYNOPSIS 01690 * 01691 * static VOID DoPrefix(_line_) 01692 * int _line_; 01693 * 01694 * DESCRIPTION 01695 * 01696 * Print prefix common to all debugger output lines, prior to 01697 * doing indentation if necessary. Print such information as 01698 * current process name, current source file name and line number, 01699 * and current function nesting depth. 01700 * 01701 */ 01702 01703 static void DoPrefix(CODE_STATE *cs, uint _line_) 01704 { 01705 cs->lineno++; 01706 if (cs->stack->flags & PID_ON) 01707 { 01708 #ifdef THREAD 01709 (void) fprintf(cs->stack->out_file, "%-7s: ", my_thread_name()); 01710 #else 01711 (void) fprintf(cs->stack->out_file, "%5d: ", (int) getpid()); 01712 #endif 01713 } 01714 if (cs->stack->flags & NUMBER_ON) 01715 (void) fprintf(cs->stack->out_file, "%5d: ", cs->lineno); 01716 if (cs->stack->flags & TIMESTAMP_ON) 01717 { 01718 #ifdef __WIN__ 01719 /* FIXME This doesn't give microseconds as in Unix case, and the resolution is 01720 in system ticks, 10 ms intervals. See my_getsystime.c for high res */ 01721 SYSTEMTIME loc_t; 01722 GetLocalTime(&loc_t); 01723 (void) fprintf (cs->stack->out_file, 01724 /* "%04d-%02d-%02d " */ 01725 "%02d:%02d:%02d.%06d ", 01726 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ 01727 loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds); 01728 #else 01729 struct timeval tv; 01730 struct tm *tm_p; 01731 if (gettimeofday(&tv, NULL) != -1) 01732 { 01733 if ((tm_p= localtime((const time_t *)&tv.tv_sec))) 01734 { 01735 (void) fprintf (cs->stack->out_file, 01736 /* "%04d-%02d-%02d " */ 01737 "%02d:%02d:%02d.%06d ", 01738 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ 01739 tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec, 01740 (int) (tv.tv_usec)); 01741 } 01742 } 01743 #endif 01744 } 01745 if (cs->stack->flags & PROCESS_ON) 01746 (void) fprintf(cs->stack->out_file, "%s: ", cs->process); 01747 if (cs->stack->flags & FILE_ON) 01748 (void) fprintf(cs->stack->out_file, "%14s: ", BaseName(cs->file)); 01749 if (cs->stack->flags & LINE_ON) 01750 (void) fprintf(cs->stack->out_file, "%5d: ", _line_); 01751 if (cs->stack->flags & DEPTH_ON) 01752 (void) fprintf(cs->stack->out_file, "%4d: ", cs->level); 01753 } 01754 01755 01756 /* 01757 * FUNCTION 01758 * 01759 * DBUGOpenFile open new output stream for debugger output 01760 * 01761 * SYNOPSIS 01762 * 01763 * static VOID DBUGOpenFile(name) 01764 * char *name; 01765 * 01766 * DESCRIPTION 01767 * 01768 * Given name of a new file (or "-" for stdout) opens the file 01769 * and sets the output stream to the new file. 01770 * 01771 */ 01772 01773 static void DBUGOpenFile(CODE_STATE *cs, 01774 const char *name,const char *end,int append) 01775 { 01776 REGISTER FILE *fp; 01777 REGISTER BOOLEAN newfile; 01778 01779 if (name != NULL) 01780 { 01781 if (end) 01782 { 01783 int len=end-name; 01784 memcpy(cs->stack->name, name, len); 01785 cs->stack->name[len]=0; 01786 } 01787 else 01788 strmov(cs->stack->name,name); 01789 name=cs->stack->name; 01790 if (strcmp(name, "-") == 0) 01791 { 01792 cs->stack->out_file= stdout; 01793 cs->stack->flags |= FLUSH_ON_WRITE; 01794 cs->stack->name[0]=0; 01795 } 01796 else 01797 { 01798 if (!Writable((char*)name)) 01799 { 01800 (void) fprintf(stderr, ERR_OPEN, cs->process, name); 01801 perror(""); 01802 fflush(stderr); 01803 } 01804 else 01805 { 01806 newfile= !EXISTS(name); 01807 if (!(fp= fopen(name, append ? "a+" : "w"))) 01808 { 01809 (void) fprintf(stderr, ERR_OPEN, cs->process, name); 01810 perror(""); 01811 fflush(stderr); 01812 } 01813 else 01814 { 01815 cs->stack->out_file= fp; 01816 if (newfile) 01817 { 01818 ChangeOwner(cs, name); 01819 } 01820 } 01821 } 01822 } 01823 } 01824 } 01825 01826 01827 /* 01828 * FUNCTION 01829 * 01830 * OpenProfile open new output stream for profiler output 01831 * 01832 * SYNOPSIS 01833 * 01834 * static FILE *OpenProfile(name) 01835 * char *name; 01836 * 01837 * DESCRIPTION 01838 * 01839 * Given name of a new file, opens the file 01840 * and sets the profiler output stream to the new file. 01841 * 01842 * It is currently unclear whether the prefered behavior is 01843 * to truncate any existing file, or simply append to it. 01844 * The latter behavior would be desirable for collecting 01845 * accumulated runtime history over a number of separate 01846 * runs. It might take some changes to the analyzer program 01847 * though, and the notes that Binayak sent with the profiling 01848 * diffs indicated that append was the normal mode, but this 01849 * does not appear to agree with the actual code. I haven't 01850 * investigated at this time [fnf; 24-Jul-87]. 01851 */ 01852 01853 #ifndef THREAD 01854 static FILE *OpenProfile(CODE_STATE *cs, const char *name) 01855 { 01856 REGISTER FILE *fp; 01857 REGISTER BOOLEAN newfile; 01858 01859 fp=0; 01860 if (!Writable(name)) 01861 { 01862 (void) fprintf(cs->stack->out_file, ERR_OPEN, cs->process, name); 01863 perror(""); 01864 dbug_flush(0); 01865 (void) Delay(cs->stack->delay); 01866 } 01867 else 01868 { 01869 newfile= !EXISTS(name); 01870 if (!(fp= fopen(name, "w"))) 01871 { 01872 (void) fprintf(cs->stack->out_file, ERR_OPEN, cs->process, name); 01873 perror(""); 01874 dbug_flush(0); 01875 } 01876 else 01877 { 01878 cs->stack->prof_file= fp; 01879 if (newfile) 01880 { 01881 ChangeOwner(cs, name); 01882 } 01883 } 01884 } 01885 return fp; 01886 } 01887 #endif 01888 01889 /* 01890 * FUNCTION 01891 * 01892 * DBUGCloseFile close the debug output stream 01893 * 01894 * SYNOPSIS 01895 * 01896 * static VOID DBUGCloseFile(fp) 01897 * FILE *fp; 01898 * 01899 * DESCRIPTION 01900 * 01901 * Closes the debug output stream unless it is standard output 01902 * or standard error. 01903 * 01904 */ 01905 01906 static void DBUGCloseFile(CODE_STATE *cs, FILE *fp) 01907 { 01908 if (fp != stderr && fp != stdout && fclose(fp) == EOF) 01909 { 01910 pthread_mutex_lock(&THR_LOCK_dbug); 01911 (void) fprintf(cs->stack->out_file, ERR_CLOSE, cs->process); 01912 perror(""); 01913 dbug_flush(0); 01914 } 01915 } 01916 01917 01918 /* 01919 * FUNCTION 01920 * 01921 * DbugExit print error message and exit 01922 * 01923 * SYNOPSIS 01924 * 01925 * static VOID DbugExit(why) 01926 * char *why; 01927 * 01928 * DESCRIPTION 01929 * 01930 * Prints error message using current process name, the reason for 01931 * aborting (typically out of memory), and exits with status 1. 01932 * This should probably be changed to use a status code 01933 * defined in the user's debugger include file. 01934 * 01935 */ 01936 01937 static void DbugExit(const char *why) 01938 { 01939 CODE_STATE *cs=code_state(); 01940 (void) fprintf(stderr, ERR_ABORT, cs ? cs->process : "(null)", why); 01941 (void) fflush(stderr); 01942 exit(1); 01943 } 01944 01945 01946 /* 01947 * FUNCTION 01948 * 01949 * DbugMalloc allocate memory for debugger runtime support 01950 * 01951 * SYNOPSIS 01952 * 01953 * static long *DbugMalloc(size) 01954 * int size; 01955 * 01956 * DESCRIPTION 01957 * 01958 * Allocate more memory for debugger runtime support functions. 01959 * Failure to to allocate the requested number of bytes is 01960 * immediately fatal to the current process. This may be 01961 * rather unfriendly behavior. It might be better to simply 01962 * print a warning message, freeze the current debugger cs, 01963 * and continue execution. 01964 * 01965 */ 01966 01967 static char *DbugMalloc(size_t size) 01968 { 01969 register char *new_malloc; 01970 01971 if (!(new_malloc= (char*) malloc((size_t) size))) 01972 DbugExit("out of memory"); 01973 return new_malloc; 01974 } 01975 01976 01977 /* 01978 * strtok lookalike - splits on ':', magically handles :\ and :/ 01979 */ 01980 01981 static const char *DbugStrTok(const char *s) 01982 { 01983 while (s[0] && (s[0] != ':' || (s[1] == '\\' || s[1] == '/'))) 01984 s++; 01985 return s; 01986 } 01987 01988 01989 /* 01990 * FUNCTION 01991 * 01992 * BaseName strip leading pathname components from name 01993 * 01994 * SYNOPSIS 01995 * 01996 * static char *BaseName(pathname) 01997 * char *pathname; 01998 * 01999 * DESCRIPTION 02000 * 02001 * Given pointer to a complete pathname, locates the base file 02002 * name at the end of the pathname and returns a pointer to 02003 * it. 02004 * 02005 */ 02006 02007 static const char *BaseName(const char *pathname) 02008 { 02009 register const char *base; 02010 02011 base= strrchr(pathname, FN_LIBCHAR); 02012 if (base++ == NullS) 02013 base= pathname; 02014 return base; 02015 } 02016 02017 02018 /* 02019 * FUNCTION 02020 * 02021 * Writable test to see if a pathname is writable/creatable 02022 * 02023 * SYNOPSIS 02024 * 02025 * static BOOLEAN Writable(pathname) 02026 * char *pathname; 02027 * 02028 * DESCRIPTION 02029 * 02030 * Because the debugger might be linked in with a program that 02031 * runs with the set-uid-bit (suid) set, we have to be careful 02032 * about opening a user named file for debug output. This consists 02033 * of checking the file for write access with the real user id, 02034 * or checking the directory where the file will be created. 02035 * 02036 * Returns TRUE if the user would normally be allowed write or 02037 * create access to the named file. Returns FALSE otherwise. 02038 * 02039 */ 02040 02041 02042 #ifndef Writable 02043 02044 static BOOLEAN Writable(char *pathname) 02045 { 02046 REGISTER BOOLEAN granted; 02047 REGISTER char *lastslash; 02048 02049 granted= FALSE; 02050 if (EXISTS(pathname)) 02051 { 02052 if (WRITABLE(pathname)) 02053 granted= TRUE; 02054 } 02055 else 02056 { 02057 lastslash= strrchr(pathname, '/'); 02058 if (lastslash != NULL) 02059 *lastslash= '\0'; 02060 else 02061 pathname= "."; 02062 if (WRITABLE(pathname)) 02063 granted= TRUE; 02064 if (lastslash != NULL) 02065 *lastslash= '/'; 02066 } 02067 return granted; 02068 } 02069 #endif 02070 02071 02072 /* 02073 * FUNCTION 02074 * 02075 * ChangeOwner change owner to real user for suid programs 02076 * 02077 * SYNOPSIS 02078 * 02079 * static VOID ChangeOwner(pathname) 02080 * 02081 * DESCRIPTION 02082 * 02083 * For unix systems, change the owner of the newly created debug 02084 * file to the real owner. This is strictly for the benefit of 02085 * programs that are running with the set-user-id bit set. 02086 * 02087 * Note that at this point, the fact that pathname represents 02088 * a newly created file has already been established. If the 02089 * program that the debugger is linked to is not running with 02090 * the suid bit set, then this operation is redundant (but 02091 * harmless). 02092 * 02093 */ 02094 02095 #ifndef ChangeOwner 02096 static void ChangeOwner(CODE_STATE *cs, char *pathname) 02097 { 02098 if (chown(pathname, getuid(), getgid()) == -1) 02099 { 02100 (void) fprintf(stderr, ERR_CHOWN, cs->process, pathname); 02101 perror(""); 02102 (void) fflush(stderr); 02103 } 02104 } 02105 #endif 02106 02107 02108 /* 02109 * FUNCTION 02110 * 02111 * _db_setjmp_ save debugger environment 02112 * 02113 * SYNOPSIS 02114 * 02115 * VOID _db_setjmp_() 02116 * 02117 * DESCRIPTION 02118 * 02119 * Invoked as part of the user's DBUG_SETJMP macro to save 02120 * the debugger environment in parallel with saving the user's 02121 * environment. 02122 * 02123 */ 02124 02125 #ifdef HAVE_LONGJMP 02126 02127 EXPORT void _db_setjmp_() 02128 { 02129 CODE_STATE *cs=0; 02130 get_code_state_or_return; 02131 02132 cs->jmplevel= cs->level; 02133 cs->jmpfunc= cs->func; 02134 cs->jmpfile= cs->file; 02135 } 02136 02137 /* 02138 * FUNCTION 02139 * 02140 * _db_longjmp_ restore previously saved debugger environment 02141 * 02142 * SYNOPSIS 02143 * 02144 * VOID _db_longjmp_() 02145 * 02146 * DESCRIPTION 02147 * 02148 * Invoked as part of the user's DBUG_LONGJMP macro to restore 02149 * the debugger environment in parallel with restoring the user's 02150 * previously saved environment. 02151 * 02152 */ 02153 02154 EXPORT void _db_longjmp_() 02155 { 02156 CODE_STATE *cs=0; 02157 get_code_state_or_return; 02158 02159 cs->level= cs->jmplevel; 02160 if (cs->jmpfunc) 02161 cs->func= cs->jmpfunc; 02162 if (cs->jmpfile) 02163 cs->file= cs->jmpfile; 02164 } 02165 #endif 02166 02167 /* 02168 * FUNCTION 02169 * 02170 * perror perror simulation for systems that don't have it 02171 * 02172 * SYNOPSIS 02173 * 02174 * static VOID perror(s) 02175 * char *s; 02176 * 02177 * DESCRIPTION 02178 * 02179 * Perror produces a message on the standard error stream which 02180 * provides more information about the library or system error 02181 * just encountered. The argument string s is printed, followed 02182 * by a ':', a blank, and then a message and a newline. 02183 * 02184 * An undocumented feature of the unix perror is that if the string 02185 * 's' is a null string (NOT a NULL pointer!), then the ':' and 02186 * blank are not printed. 02187 * 02188 * This version just complains about an "unknown system error". 02189 * 02190 */ 02191 02192 #ifndef HAVE_PERROR 02193 static void perror(s) 02194 char *s; 02195 { 02196 if (s && *s != '\0') 02197 (void) fprintf(stderr, "%s: ", s); 02198 (void) fprintf(stderr, "<unknown system error>\n"); 02199 } 02200 #endif /* HAVE_PERROR */ 02201 02202 02203 /* flush dbug-stream, free mutex lock & wait delay */ 02204 /* This is because some systems (MSDOS!!) dosn't flush fileheader */ 02205 /* and dbug-file isn't readable after a system crash !! */ 02206 02207 static void dbug_flush(CODE_STATE *cs) 02208 { 02209 #ifndef THREAD 02210 if (cs->stack->flags & FLUSH_ON_WRITE) 02211 #endif 02212 { 02213 #if defined(MSDOS) || defined(__WIN__) 02214 if (cs->stack->out_file != stdout && cs->stack->out_file != stderr) 02215 { 02216 if (!(freopen(cs->stack->name,"a",cs->stack->out_file))) 02217 { 02218 (void) fprintf(stderr, ERR_OPEN, cs->process, cs->stack->name); 02219 fflush(stderr); 02220 cs->stack->out_file= stderr; 02221 } 02222 } 02223 else 02224 #endif 02225 { 02226 (void) fflush(cs->stack->out_file); 02227 if (cs->stack->delay) 02228 (void) Delay(cs->stack->delay); 02229 } 02230 } 02231 if (!cs->locked) 02232 pthread_mutex_unlock(&THR_LOCK_dbug); 02233 } /* dbug_flush */ 02234 02235 02236 void _db_lock_file_() 02237 { 02238 CODE_STATE *cs=0; 02239 get_code_state_or_return; 02240 pthread_mutex_lock(&THR_LOCK_dbug); 02241 cs->locked=1; 02242 } 02243 02244 void _db_unlock_file_() 02245 { 02246 CODE_STATE *cs=0; 02247 get_code_state_or_return; 02248 cs->locked=0; 02249 pthread_mutex_unlock(&THR_LOCK_dbug); 02250 } 02251 02252 /* 02253 * Here we need the definitions of the clock routine. Add your 02254 * own for whatever system that you have. 02255 */ 02256 02257 #ifndef THREAD 02258 #if defined(HAVE_GETRUSAGE) 02259 02260 #include <sys/param.h> 02261 #include <sys/resource.h> 02262 02263 /* extern int getrusage(int, struct rusage *); */ 02264 02265 /* 02266 * Returns the user time in milliseconds used by this process so 02267 * far. 02268 */ 02269 02270 static unsigned long Clock() 02271 { 02272 struct rusage ru; 02273 02274 (void) getrusage(RUSAGE_SELF, &ru); 02275 return ru.ru_utime.tv_sec*1000 + ru.ru_utime.tv_usec/1000; 02276 } 02277 02278 #elif defined(MSDOS) || defined(__WIN__) 02279 02280 static ulong Clock() 02281 { 02282 return clock()*(1000/CLOCKS_PER_SEC); 02283 } 02284 #elif defined(amiga) 02285 02286 struct DateStamp { /* Yes, this is a hack, but doing it right */ 02287 long ds_Days; /* is incredibly ugly without splitting this */ 02288 long ds_Minute; /* off into a separate file */ 02289 long ds_Tick; 02290 }; 02291 02292 static int first_clock= TRUE; 02293 static struct DateStamp begin; 02294 static struct DateStamp elapsed; 02295 02296 static unsigned long Clock() 02297 { 02298 register struct DateStamp *now; 02299 register unsigned long millisec= 0; 02300 extern VOID *AllocMem(); 02301 02302 now= (struct DateStamp *) AllocMem((long) sizeof(struct DateStamp), 0L); 02303 if (now != NULL) 02304 { 02305 if (first_clock == TRUE) 02306 { 02307 first_clock= FALSE; 02308 (void) DateStamp(now); 02309 begin= *now; 02310 } 02311 (void) DateStamp(now); 02312 millisec= 24 * 3600 * (1000 / HZ) * (now->ds_Days - begin.ds_Days); 02313 millisec += 60 * (1000 / HZ) * (now->ds_Minute - begin.ds_Minute); 02314 millisec += (1000 / HZ) * (now->ds_Tick - begin.ds_Tick); 02315 (void) FreeMem(now, (long) sizeof(struct DateStamp)); 02316 } 02317 return millisec; 02318 } 02319 #else 02320 static unsigned long Clock() 02321 { 02322 return 0; 02323 } 02324 #endif /* RUSAGE */ 02325 #endif /* THREADS */ 02326 02327 #ifdef NO_VARARGS 02328 02329 /* 02330 * Fake vfprintf for systems that don't support it. If this 02331 * doesn't work, you are probably SOL... 02332 */ 02333 02334 static int vfprintf(stream, format, ap) 02335 FILE *stream; 02336 char *format; 02337 va_list ap; 02338 { 02339 int rtnval; 02340 ARGS_DCL; 02341 02342 ARG0= va_arg(ap, ARGS_TYPE); 02343 ARG1= va_arg(ap, ARGS_TYPE); 02344 ARG2= va_arg(ap, ARGS_TYPE); 02345 ARG3= va_arg(ap, ARGS_TYPE); 02346 ARG4= va_arg(ap, ARGS_TYPE); 02347 ARG5= va_arg(ap, ARGS_TYPE); 02348 ARG6= va_arg(ap, ARGS_TYPE); 02349 ARG7= va_arg(ap, ARGS_TYPE); 02350 ARG8= va_arg(ap, ARGS_TYPE); 02351 ARG9= va_arg(ap, ARGS_TYPE); 02352 rtnval= fprintf(stream, format, ARGS_LIST); 02353 return rtnval; 02354 } 02355 02356 #endif /* NO_VARARGS */
1.4.7

