diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
commit | a175314c3e5827eb193872241446f2f8f5c9d33c (patch) | |
tree | cd3d60ca99ae00829c52a6ca79150a5b6e62528b /dbug | |
parent | Initial commit. (diff) | |
download | mariadb-10.5-upstream.tar.xz mariadb-10.5-upstream.zip |
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dbug')
-rw-r--r-- | dbug/CMakeLists.txt | 72 | ||||
-rw-r--r-- | dbug/dbug.c | 2289 | ||||
-rwxr-xr-x | dbug/dbug_add_tags.pl | 88 | ||||
-rw-r--r-- | dbug/dbug_long.h | 144 | ||||
-rw-r--r-- | dbug/example1.c | 10 | ||||
-rw-r--r-- | dbug/example2.c | 15 | ||||
-rw-r--r-- | dbug/example3.c | 14 | ||||
-rw-r--r-- | dbug/factorial.c | 13 | ||||
-rw-r--r-- | dbug/main.c | 24 | ||||
-rw-r--r-- | dbug/monty.doc | 9 | ||||
-rw-r--r-- | dbug/my_main.c | 37 | ||||
-rwxr-xr-x | dbug/remove_function_from_trace.pl | 25 | ||||
-rwxr-xr-x | dbug/tests-t.pl | 497 | ||||
-rw-r--r-- | dbug/tests.c | 100 | ||||
-rw-r--r-- | dbug/user.r | 1121 |
15 files changed, 4458 insertions, 0 deletions
diff --git a/dbug/CMakeLists.txt b/dbug/CMakeLists.txt new file mode 100644 index 00000000..d938788a --- /dev/null +++ b/dbug/CMakeLists.txt @@ -0,0 +1,72 @@ +# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA + +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/dbug + ${CMAKE_SOURCE_DIR}/include +) +SET(DBUG_SOURCES dbug.c) +ADD_CONVENIENCE_LIBRARY(dbug ${DBUG_SOURCES}) +TARGET_LINK_LIBRARIES(dbug mysys) +MAYBE_DISABLE_IPO(dbug) + +ADD_EXECUTABLE(tests tests.c) +TARGET_LINK_LIBRARIES(tests dbug) + +IF(NOT CMAKE_CROSSCOMPILING OR DEFINED CMAKE_CROSSCOMPILING_EMULATOR) + ADD_EXECUTABLE(factorial my_main.c factorial.c) + TARGET_LINK_LIBRARIES(factorial dbug) +ENDIF() + +IF(NOT WIN32 AND NOT CMAKE_GENERATOR MATCHES Xcode AND NOT RPM AND NOT DEB) + FIND_PROGRAM(GROFF groff) + FIND_PROGRAM(NROFF nroff) + MARK_AS_ADVANCED(GROFF) + MARK_AS_ADVANCED(NROFF) + SET(OUTPUT_INC output1.r output2.r output3.r output4.r output5.r) + SET(SOURCE_INC factorial.r main.r example1.r example2.r example3.r) + ADD_CUSTOM_COMMAND(OUTPUT ${OUTPUT_INC} + DEPENDS factorial + COMMAND factorial 1 2 3 4 5 > output1.r + COMMAND factorial -\#t:o 2 3 > output2.r + COMMAND factorial -\#d:t:o 3 > output3.r + COMMAND factorial -\#d,result:o 4 > output4.r + COMMAND factorial -\#d:f,factorial:F:L:o 3 > output5.r) + FOREACH(file ${SOURCE_INC}) + STRING(REGEX REPLACE "\\.r" ".c" srcfile ${file}) + ADD_CUSTOM_COMMAND(OUTPUT ${file} DEPENDS ${srcfile} + COMMAND sed -e 's!\\\\!\\\\\\\\!g' + <${CMAKE_CURRENT_SOURCE_DIR}/${srcfile} >${file}) + ENDFOREACH(file) + ADD_CUSTOM_COMMAND(OUTPUT dbug-t DEPENDS tests-t.pl + COMMAND cp -f ${CMAKE_CURRENT_SOURCE_DIR}/tests-t.pl dbug-t) + ADD_CUSTOM_TARGET(dbug-unit-tests ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/dbug-t) + MY_ADD_TEST(dbug) + + IF(GROFF) + ADD_CUSTOM_COMMAND(OUTPUT user.ps + DEPENDS user.r ${OUTPUT_INC} ${SOURCE_INC} + COMMAND ${GROFF} -mm ${CMAKE_CURRENT_SOURCE_DIR}/user.r > user.ps || touch user.ps) + ADD_CUSTOM_TARGET(user_ps ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/user.ps) + ENDIF(GROFF) + IF(NROFF) + ADD_CUSTOM_COMMAND(OUTPUT user.t + DEPENDS user.r ${OUTPUT_INC} ${SOURCE_INC} + COMMAND ${NROFF} -mm ${CMAKE_CURRENT_SOURCE_DIR}/user.r > user.t || touch user.t) + ADD_CUSTOM_TARGET(user_t ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/user.t) + ENDIF(NROFF) + +ENDIF() + diff --git a/dbug/dbug.c b/dbug/dbug.c new file mode 100644 index 00000000..011b932a --- /dev/null +++ b/dbug/dbug.c @@ -0,0 +1,2289 @@ +/****************************************************************************** + * * + * N O T I C E * + * * + * Copyright Abandoned, 1987, Fred Fish * + * * + * * + * This previously copyrighted work has been placed into the public * + * domain by the author and may be freely used for any purpose, * + * private or commercial. * + * * + * Because of the number of inquiries I was receiving about the use * + * of this product in commercially developed works I have decided to * + * simply make it public domain to further its unrestricted use. I * + * specifically would be most happy to see this material become a * + * part of the standard Unix distributions by AT&T and the Berkeley * + * Computer Science Research Group, and a standard part of the GNU * + * system from the Free Software Foundation. * + * * + * I would appreciate it, as a courtesy, if this notice is left in * + * all copies and derivative works. Thank you. * + * * + * The author makes no warranty of any kind with respect to this * + * product and explicitly disclaims any implied warranties of mer- * + * chantability or fitness for any particular purpose. * + * * + ****************************************************************************** + */ + +/* + * FILE + * + * dbug.c runtime support routines for dbug package + * + * SCCS + * + * @(#)dbug.c 1.25 7/25/89 + * + * DESCRIPTION + * + * These are the runtime support routines for the dbug package. + * The dbug package has two main components; the user include + * file containing various macro definitions, and the runtime + * support routines which are called from the macro expansions. + * + * Externally visible functions in the runtime support module + * use the naming convention pattern "_db_xx...xx_", thus + * they are unlikely to collide with user defined function names. + * + * AUTHOR(S) + * + * Fred Fish (base code) + * Enhanced Software Technologies, Tempe, AZ + * asuvax!mcdphx!estinc!fnf + * + * Michael Widenius: + * DBUG_DUMP - To dump a block of memory. + * PUSH_FLAG "O" - To be used insted of "o" if we + * want flushing after each write + * PUSH_FLAG "A" - as 'O', but we will append to the out file instead + * of creating a new one. + * Check of malloc on entry/exit (option "S") + * + * Sergei Golubchik: + * DBUG_EXECUTE_IF + * incremental mode (-#+t:-d,info ...) + * DBUG_SET, _db_explain_ + * thread-local settings + * negative lists (-#-d,info => everything but "info") + * + * function/ syntax + * (the logic is - think of a call stack as of a path. + * "function" means only this function, "function/" means the hierarchy. + * in the future, filters like function1/function2 could be supported. + * following this logic glob(7) wildcards are supported.) + * + */ + +/* + We can't have SAFE_MUTEX defined here as this will cause recursion + in pthread_mutex_lock +*/ + +#include <my_global.h> +#undef SAFE_MUTEX +#include <m_string.h> +#include <errno.h> + +#ifndef DBUG_OFF + +#ifdef HAVE_FNMATCH_H +#include <fnmatch.h> +#else +#define fnmatch(A,B,C) strcmp(A,B) +#endif + +#if defined(__WIN__) +#include <process.h> +#endif + +/* + * Manifest constants which may be "tuned" if desired. + */ + +#define PRINTBUF 1024 /* Print buffer size */ +#define INDENT 2 /* Indentation per trace level */ +#define MAXDEPTH 200 /* Maximum trace depth default */ + +/* + * The following flags are used to determine which + * capabilities the user has enabled with the settings + * push macro. + * + * TRACE_ON is also used in _db_stack_frame_->level + * (until we add flags to _db_stack_frame_, increasing it by 4 bytes) + */ + +#define DEBUG_ON (1U << 1) /* Debug enabled */ +#define FILE_ON (1U << 2) /* File name print enabled */ +#define LINE_ON (1U << 3) /* Line number print enabled */ +#define DEPTH_ON (1U << 4) /* Function nest level print enabled */ +#define PROCESS_ON (1U << 5) /* Process name print enabled */ +#define NUMBER_ON (1U << 6) /* Number each line of output */ +#define PID_ON (1U << 8) /* Identify each line with process id */ +#define TIMESTAMP_ON (1U << 9) /* timestamp every line of output */ +#define FLUSH_ON_WRITE (1U << 10) /* Flush on every write */ +#define OPEN_APPEND (1U << 11) /* Open for append */ +#define SANITY_CHECK_ON (1U << 12) /* Check memory on every DBUG_ENTER/RETURN */ +#define TRACE_ON (1U << 31) /* Trace enabled. MUST be the highest bit!*/ + +#define TRACING (cs->stack->flags & TRACE_ON) +#define DEBUGGING (cs->stack->flags & DEBUG_ON) + +/* + * Typedefs to make things more obvious. + */ + +#define BOOLEAN my_bool + +/* + * Externally supplied functions. + */ + +#ifndef HAVE_PERROR +static void perror(char *s) +{ + if (s && *s != '\0') + (void) fprintf(stderr, "%s: ", s); + (void) fprintf(stderr, "<unknown system error>\n"); +} +#endif + +/* + * The user may specify a list of functions to trace or + * debug. These lists are kept in a linear linked list, + * a very simple implementation. + */ + +struct link { + struct link *next_link; /* Pointer to the next link */ + char flags; + char str[1]; /* Pointer to link's contents */ +}; + +/* flags for struct link and return flags of InList */ +#define SUBDIR 1 /* this MUST be 1 */ +#define INCLUDE 2 +#define EXCLUDE 4 +/* this is not a struct link flag, but only a return flags of InList */ +#define MATCHED 65536 +#define NOT_MATCHED 0 + +/* + * Debugging settings can be shared between threads. + * But FILE* streams cannot normally be shared - what if + * one thread closes a stream, while another thread still uses it? + * As a workaround, we have shared FILE pointers with reference counters + */ +typedef struct { + FILE *file; + uint used; +} sFILE; + +sFILE shared_stdout = { 0, 1 << 30 }, *sstdout = &shared_stdout; +sFILE shared_stderr = { 0, 1 << 30 }, *sstderr = &shared_stderr; + +/* + * Debugging settings can be pushed or popped off of a + * stack which is implemented as a linked list. Note + * that the head of the list is the current settings and the + * stack is pushed by adding a new settings to the head of the + * list or popped by removing the first link. + * + * Note: if out_file is NULL, the other fields are not initialized at all! + */ + +struct settings { + uint flags; /* Current settings flags */ + uint maxdepth; /* Current maximum trace depth */ + uint delay; /* Delay after each output line */ + uint sub_level; /* Sub this from code_state->level */ + sFILE *out_file; /* Current output stream */ + char name[FN_REFLEN]; /* Name of output file */ + struct link *functions; /* List of functions */ + struct link *keywords; /* List of debug keywords */ + struct link *processes; /* List of process names */ + struct settings *next; /* Next settings in the list */ +}; + +#define is_shared(S, V) ((S)->next && (S)->next->V == (S)->V) + +/* + * Local variables not seen by user. + */ + + +static BOOLEAN init_done= FALSE; /* Set to TRUE when initialization done */ +static struct settings init_settings; +static const char *db_process= 0;/* Pointer to process name; argv[0] */ +my_bool _dbug_on_= TRUE; /* FALSE if no debugging at all */ + +typedef struct _db_code_state_ { + const char *process; /* Pointer to process name; usually argv[0] */ + const char *func; /* Name of current user function */ + const char *file; /* Name of current user file */ + struct _db_stack_frame_ *framep; /* Pointer to current frame */ + struct settings *stack; /* debugging settings */ + int lineno; /* Current debugger output line number */ + uint level; /* Current function nesting level */ + +/* + * The following variables are used to hold the state information + * between the call to _db_pargs_() and _db_doprnt_(), during + * expansion of the DBUG_PRINT macro. This is the only macro + * that currently uses these variables. + * + * These variables are currently used only by _db_pargs_() and + * _db_doprnt_(). + */ + + uint u_line; /* User source code line number */ + int locked; /* If locked with _db_lock_file_ */ + const char *u_keyword; /* Keyword for current macro */ +} CODE_STATE; + +/* + The test below is so we could call functions with DBUG_ENTER before + my_thread_init(). +*/ +#define get_code_state_if_not_set_or_return if (!cs && !((cs=code_state()))) return +#define get_code_state_or_return if (!((cs=code_state()))) return + + /* Handling lists */ +#define ListAdd(A,B,C) ListAddDel(A,B,C,INCLUDE) +#define ListDel(A,B,C) ListAddDel(A,B,C,EXCLUDE) +static struct link *ListAddDel(struct link *, const char *, const char *, int); +static struct link *ListCopy(struct link *); +static int InList(struct link *linkp,const char *cp,int exact_match); +static uint ListFlags(struct link *linkp); +static void FreeList(struct link *linkp); + + /* OpenClose debug output stream */ +static void DBUGOpenFile(CODE_STATE *,const char *, const char *, int); +static void DBUGCloseFile(CODE_STATE *cs, sFILE *new_value); + /* Push current debug settings */ +static void PushState(CODE_STATE *cs); + /* Free memory associated with debug state. */ +static void FreeState (CODE_STATE *cs, int free_state); + /* Test for tracing enabled */ +static int DoTrace(CODE_STATE *cs); +static int default_my_dbug_sanity(void); + +int (*dbug_sanity)(void)= default_my_dbug_sanity; + + +/* + return values of DoTrace. + Can also be used as bitmask: ret & DO_TRACE +*/ +#define DO_TRACE 1 +#define DONT_TRACE 2 +#define ENABLE_TRACE 3 +#define DISABLE_TRACE 4 + + /* Test to see if file is writable */ +#if defined(HAVE_ACCESS) +static BOOLEAN Writable(const char *pathname); +#endif + +static void DoPrefix(CODE_STATE *cs, uint line); + +static char *DbugMalloc(size_t size); +static const char *BaseName(const char *pathname); +static void Indent(CODE_STATE *cs, int indent); +static void DbugFlush(CODE_STATE *); +static void DbugExit(const char *why); +static const char *DbugStrTok(const char *s); +static void DbugVfprintf(FILE *stream, const char* format, va_list args); + +/* + * Miscellaneous printf format strings. + */ + +#define ERR_MISSING_RETURN "missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n" +#define ERR_OPEN "%s: can't open debug output stream \"%s\": " +#define ERR_CLOSE "%s: can't close debug file: " +#define ERR_ABORT "%s: debugger aborting because %s\n" + +/* + * Macros and defines for testing file accessibility under UNIX and MSDOS. + */ + +#undef EXISTS +#if !defined(HAVE_ACCESS) +#define EXISTS(pathname) (FALSE) /* Assume no existence */ +#define Writable(name) (TRUE) +#else +#define EXISTS(pathname) (access(pathname, F_OK) == 0) +#define WRITABLE(pathname) (access(pathname, W_OK) == 0) +#endif + +/* +** Macros to allow dbugging with threads +*/ + +#include <my_pthread.h> +/* +** Protects writing to all file descriptors, init_settings.keywords +** pointer and it's pointee - a linked list with keywords. +*/ +static pthread_mutex_t THR_LOCK_dbug; + +static void LockMutex(CODE_STATE *cs) +{ + if (!cs->locked) + pthread_mutex_lock(&THR_LOCK_dbug); + cs->locked++; +} +static void UnlockMutex(CODE_STATE *cs) +{ + --cs->locked; + assert(cs->locked >= 0); + if (cs->locked == 0) + pthread_mutex_unlock(&THR_LOCK_dbug); +} +static void LockIfInitSettings(CODE_STATE *cs) +{ + if (cs->stack == &init_settings) + LockMutex(cs); +} +static void UnlockIfInitSettings(CODE_STATE *cs) +{ + if (cs->stack == &init_settings) + UnlockMutex(cs); +} + +static CODE_STATE *code_state(void) +{ + CODE_STATE *cs, **cs_ptr; + + /* + _dbug_on_ is reset if we don't plan to use any debug commands at all and + we want to run on maximum speed + */ + if (!_dbug_on_) + return 0; + + if (!init_done) + { + init_done=TRUE; + sstdout->file= stdout; + sstderr->file= stderr; + pthread_mutex_init(&THR_LOCK_dbug, NULL); + bzero(&init_settings, sizeof(init_settings)); + init_settings.out_file= sstderr; + init_settings.flags=OPEN_APPEND; + } + + if (!(cs_ptr= (CODE_STATE**) my_thread_var_dbug())) + return 0; /* Thread not initialised */ + if (!(cs= *cs_ptr)) + { + cs=(CODE_STATE*) DbugMalloc(sizeof(*cs)); + bzero((uchar*) cs,sizeof(*cs)); + cs->process= db_process ? db_process : "dbug"; + cs->func= "?func"; + cs->file= "?file"; + cs->stack=&init_settings; + *cs_ptr= cs; + } + return cs; +} + +void +dbug_swap_code_state(void **code_state_store) +{ + CODE_STATE *cs, **cs_ptr; + + if (!(cs_ptr= (CODE_STATE**) my_thread_var_dbug())) + return; + cs= *cs_ptr; + *cs_ptr= *code_state_store; + *code_state_store= cs; +} + +void dbug_free_code_state(void **code_state_store) +{ + if (*code_state_store) + { + free(*code_state_store); + *code_state_store= NULL; + } +} + +/* + * Translate some calls among different systems. + */ + +#ifdef HAVE_SLEEP +/* sleep() wants seconds */ +#define Delay(A) sleep(((uint) A)/10) +#else +#define Delay(A) (0) +#endif + +/* + * FUNCTION + * + * _db_process_ give the name to the current process/thread + * + * SYNOPSIS + * + * VOID _process_(name) + * char *name; + * + */ + +void _db_process_(const char *name) +{ + CODE_STATE *cs; + + if (!db_process) + db_process= name; + + get_code_state_or_return; + cs->process= name; +} + +/* + * FUNCTION + * + * DbugParse parse control string and set current debugger settings + * + * DESCRIPTION + * + * Given pointer to a debug control string in "control", + * parses the control string, and sets + * up a current debug settings. + * + * The debug control string is a sequence of colon separated fields + * as follows: + * + * [+]<field_1>:<field_2>:...:<field_N> + * + * Each field consists of a mandatory flag character followed by + * an optional "," and comma separated list of modifiers: + * + * [sign]flag[,modifier,modifier,...,modifier] + * + * See the manual for the list of supported signs, flags, and modifiers + * + * For convenience, any leading "-#" is stripped off. + * + * RETURN + * 1 - a list of functions ("f" flag) was possibly changed + * 0 - a list of functions was not changed + */ + +static int DbugParse(CODE_STATE *cs, const char *control) +{ + const char *end; + int rel, f_used=0; + struct settings *stack; + + stack= cs->stack; + + if (control[0] == '-' && control[1] == '#') + control+=2; + + rel= control[0] == '+' || control[0] == '-'; + if ((!rel || (!stack->out_file && !stack->next))) + { + LockIfInitSettings(cs); + FreeState(cs, 0); + stack->flags= 0; + stack->delay= 0; + stack->maxdepth= 0; + stack->sub_level= 0; + stack->out_file= sstderr; + stack->functions= NULL; + stack->keywords= NULL; + stack->processes= NULL; + UnlockIfInitSettings(cs); + } + else if (!stack->out_file) + { + stack->flags= stack->next->flags; + stack->delay= stack->next->delay; + stack->maxdepth= stack->next->maxdepth; + stack->sub_level= stack->next->sub_level; + strcpy(stack->name, stack->next->name); + stack->out_file= stack->next->out_file; + stack->out_file->used++; + if (stack->next == &init_settings) + { + /* never share with the global parent - it can change under your feet */ + stack->functions= ListCopy(init_settings.functions); + LockMutex(cs); + stack->keywords= ListCopy(init_settings.keywords); + UnlockMutex(cs); + stack->processes= ListCopy(init_settings.processes); + } + else + { + stack->functions= stack->next->functions; + stack->keywords= stack->next->keywords; + stack->processes= stack->next->processes; + } + } + + end= DbugStrTok(control); + while (control < end) + { + int c, sign= (*control == '+') ? 1 : (*control == '-') ? -1 : 0; + if (sign) control++; + c= *control++; + if (*control == ',') + control++; + /* XXX when adding new cases here, don't forget _db_explain_ ! */ + switch (c) { + case 'd': + if (sign < 0 && control == end) + { + LockIfInitSettings(cs); + if (!is_shared(stack, keywords)) + FreeList(stack->keywords); + stack->keywords=NULL; + UnlockIfInitSettings(cs); + stack->flags &= ~DEBUG_ON; + break; + } + LockIfInitSettings(cs); + if (rel && is_shared(stack, keywords)) + stack->keywords= ListCopy(stack->keywords); + UnlockIfInitSettings(cs); + if (sign < 0) + { + if (DEBUGGING) + { + LockIfInitSettings(cs); + stack->keywords= ListDel(stack->keywords, control, end); + UnlockIfInitSettings(cs); + } + break; + } + LockIfInitSettings(cs); + stack->keywords= ListAdd(stack->keywords, control, end); + UnlockIfInitSettings(cs); + stack->flags |= DEBUG_ON; + break; + case 'D': + stack->delay= atoi(control); + break; + case 'f': + f_used= 1; + if (sign < 0 && control == end) + { + if (!is_shared(stack,functions)) + FreeList(stack->functions); + stack->functions=NULL; + break; + } + if (rel && is_shared(stack,functions)) + stack->functions= ListCopy(stack->functions); + if (sign < 0) + stack->functions= ListDel(stack->functions, control, end); + else + stack->functions= ListAdd(stack->functions, control, end); + break; + case 'F': + if (sign < 0) + stack->flags &= ~FILE_ON; + else + stack->flags |= FILE_ON; + break; + case 'i': + if (sign < 0) + stack->flags &= ~PID_ON; + else + stack->flags |= PID_ON; + break; + case 'L': + if (sign < 0) + stack->flags &= ~LINE_ON; + else + stack->flags |= LINE_ON; + break; + case 'n': + if (sign < 0) + stack->flags &= ~DEPTH_ON; + else + stack->flags |= DEPTH_ON; + break; + case 'N': + if (sign < 0) + stack->flags &= ~NUMBER_ON; + else + stack->flags |= NUMBER_ON; + break; + case 'A': + case 'O': + stack->flags |= FLUSH_ON_WRITE; + /* fall through */ + case 'a': + case 'o': + if (sign < 0) + { + DBUGCloseFile(cs, sstderr); + stack->flags &= ~FLUSH_ON_WRITE; + break; + } + if (c == 'a' || c == 'A') + stack->flags |= OPEN_APPEND; + else + stack->flags &= ~OPEN_APPEND; + if (control != end) + DBUGOpenFile(cs, control, end, stack->flags & OPEN_APPEND); + else + DBUGOpenFile(cs, "-",0,0); + break; + case 'p': + if (sign < 0 && control == end) + { + if (!is_shared(stack,processes)) + FreeList(stack->processes); + stack->processes=NULL; + break; + } + if (rel && is_shared(stack, processes)) + stack->processes= ListCopy(stack->processes); + if (sign < 0) + stack->processes= ListDel(stack->processes, control, end); + else + stack->processes= ListAdd(stack->processes, control, end); + break; + case 'P': + if (sign < 0) + stack->flags &= ~PROCESS_ON; + else + stack->flags |= PROCESS_ON; + break; + case 'r': + stack->sub_level= cs->level; + break; + case 't': + if (sign < 0) + { + if (control != end) + stack->maxdepth-= atoi(control); + else + stack->maxdepth= 0; + } + else + { + if (control != end) + stack->maxdepth+= atoi(control); + else + stack->maxdepth= MAXDEPTH; + } + if (stack->maxdepth > 0) + stack->flags |= TRACE_ON; + else + stack->flags &= ~TRACE_ON; + break; + case 'T': + if (sign < 0) + stack->flags &= ~TIMESTAMP_ON; + else + stack->flags |= TIMESTAMP_ON; + break; + case 'S': + if (sign < 0) + stack->flags &= ~SANITY_CHECK_ON; + else + stack->flags |= SANITY_CHECK_ON; + break; + } + if (!*end) + break; + control=end+1; + end= DbugStrTok(control); + } + return !rel || f_used; +} + +#define framep_trace_flag(cs, frp) (frp ? \ + frp->level & TRACE_ON : \ + (ListFlags(cs->stack->functions) & INCLUDE) ? \ + 0 : (uint)TRACE_ON) + +static void FixTraceFlags_helper(CODE_STATE *cs, const char *func, + struct _db_stack_frame_ *framep) +{ + if (framep->prev) + FixTraceFlags_helper(cs, framep->func, framep->prev); + + cs->func= func; + cs->level= framep->level & ~TRACE_ON; + framep->level= cs->level | framep_trace_flag(cs, framep->prev); + /* + we don't set cs->framep correctly, even though DoTrace uses it. + It's ok, because cs->framep may only affect DO_TRACE/DONT_TRACE return + values, but we ignore them here anyway + */ + switch(DoTrace(cs)) { + case ENABLE_TRACE: + framep->level|= TRACE_ON; + break; + case DISABLE_TRACE: + framep->level&= ~TRACE_ON; + break; + } +} + +#define fflags(cs) cs->stack->out_file ? ListFlags(cs->stack->functions) : TRACE_ON; + +static void FixTraceFlags(uint old_fflags, CODE_STATE *cs) +{ + const char *func; + uint new_fflags, traceon, level; + struct _db_stack_frame_ *framep; + + /* + first (a.k.a. safety) check: + if we haven't started tracing yet, no call stack at all - we're safe. + */ + framep=cs->framep; + if (framep == 0) + return; + + /* + Ok, the tracing has started, call stack isn't empty. + + second check: does the new list have a SUBDIR rule ? + */ + new_fflags=fflags(cs); + if (new_fflags & SUBDIR) + goto yuck; + + /* + Ok, new list doesn't use SUBDIR. + + third check: we do NOT need to re-scan if + neither old nor new lists used SUBDIR flag and if a default behavior + (whether an unlisted function is traced) hasn't changed. + Default behavior depends on whether there're INCLUDE elements in the list. + */ + if (!(old_fflags & SUBDIR) && !((new_fflags^old_fflags) & INCLUDE)) + return; + + /* + Ok, old list may've used SUBDIR, or defaults could've changed. + + fourth check: are we inside a currently active SUBDIR rule ? + go up the call stack, if TRACE_ON flag ever changes its value - we are. + */ + for (traceon=framep->level; framep; framep=framep->prev) + if ((traceon ^ framep->level) & TRACE_ON) + goto yuck; + + /* + Ok, TRACE_ON flag doesn't change in the call stack. + + fifth check: but is the top-most value equal to a default one ? + */ + if (((traceon & TRACE_ON) != 0) == ((new_fflags & INCLUDE) == 0)) + return; + +yuck: + /* + Yuck! function list was changed, and one of the currently active rules + was possibly affected. For example, a tracing could've been enabled or + disabled for a function somewhere up the call stack. + To react correctly, we must go up the call stack all the way to + the top and re-match rules to set TRACE_ON bit correctly. + + We must traverse the stack forwards, not backwards. + That's what a recursive helper is doing. + It'll destroy two CODE_STATE fields, save them now. + */ + func= cs->func; + level= cs->level; + FixTraceFlags_helper(cs, func, cs->framep); + /* now we only need to restore CODE_STATE fields, and we're done */ + cs->func= func; + cs->level= level; +} + +/* + * FUNCTION + * + * _db_set_ set current debugger settings + * + * SYNOPSIS + * + * VOID _db_set_(control) + * char *control; + * + * DESCRIPTION + * + * Given pointer to a debug control string in "control", + * parses the control string, and sets up a current debug + * settings. Pushes a new debug settings if the current is + * set to the initial debugger settings. + * + */ + +void _db_set_(const char *control) +{ + CODE_STATE *cs; + uint old_fflags; + get_code_state_or_return; + old_fflags=fflags(cs); + if (cs->stack == &init_settings) + PushState(cs); + if (DbugParse(cs, control)) + FixTraceFlags(old_fflags, cs); +} + +/* + * FUNCTION + * + * _db_push_ push current debugger settings and set up new one + * + * SYNOPSIS + * + * VOID _db_push_(control) + * char *control; + * + * DESCRIPTION + * + * Given pointer to a debug control string in "control", pushes + * the current debug settings, parses the control string, and sets + * up a new debug settings + * + */ + +void _db_push_(const char *control) +{ + CODE_STATE *cs; + uint old_fflags; + get_code_state_or_return; + old_fflags=fflags(cs); + PushState(cs); + if (DbugParse(cs, control)) + FixTraceFlags(old_fflags, cs); +} + + +/** + Returns TRUE if session-local settings have been set. +*/ + +int _db_is_pushed_() +{ + CODE_STATE *cs= NULL; + get_code_state_or_return FALSE; + return (cs->stack != &init_settings); +} + +/* + * FUNCTION + * + * _db_set_init_ set initial debugger settings + * + * SYNOPSIS + * + * VOID _db_set_init_(control) + * char *control; + * + * DESCRIPTION + * see _db_set_ + * + */ + +void _db_set_init_(const char *control) +{ + CODE_STATE tmp_cs; + bzero((uchar*) &tmp_cs, sizeof(tmp_cs)); + tmp_cs.stack= &init_settings; + tmp_cs.process= db_process ? db_process : "dbug"; + DbugParse(&tmp_cs, control); +} + +/* + * FUNCTION + * + * _db_pop_ pop the debug stack + * + * DESCRIPTION + * + * Pops the debug stack, returning the debug settings to its + * condition prior to the most recent _db_push_ invocation. + * Note that the pop will fail if it would remove the last + * valid settings from the stack. This prevents user errors + * in the push/pop sequence from screwing up the debugger. + * Maybe there should be some kind of warning printed if the + * user tries to pop too many states. + * + */ + +void _db_pop_() +{ + uint old_fflags; + CODE_STATE *cs; + + get_code_state_or_return; + + if (cs->stack != &init_settings) + { + old_fflags=fflags(cs); + FreeState(cs, 1); + FixTraceFlags(old_fflags, cs); + } +} + +/* + * FUNCTION + * + * _db_explain_ generates 'control' string for the current settings + * + * RETURN + * 0 - ok + * 1 - buffer too short, output truncated + * + */ + +/* helper macros */ +#define char_to_buf(C) do { \ + *buf++=(C); \ + if (buf >= end) goto overflow; \ + } while (0) +#define str_to_buf(S) do { \ + char_to_buf(','); \ + buf=strnmov(buf, (S), (uint) (end-buf)); \ + if (buf >= end) goto overflow; \ + } while (0) +#define list_to_buf(l, f) do { \ + struct link *listp=(l); \ + while (listp) \ + { \ + if (listp->flags & (f)) \ + { \ + str_to_buf(listp->str); \ + if (listp->flags & SUBDIR) \ + char_to_buf('/'); \ + } \ + listp=listp->next_link; \ + } \ + } while (0) +#define int_to_buf(i) do { \ + char b[50]; \ + int10_to_str((i), b, 10); \ + str_to_buf(b); \ + } while (0) +#define colon_to_buf do { \ + if (buf != start) char_to_buf(':'); \ + } while(0) +#define op_int_to_buf(C, val, def) do { \ + if ((val) != (def)) \ + { \ + colon_to_buf; \ + char_to_buf((C)); \ + int_to_buf(val); \ + } \ + } while (0) +#define op_intf_to_buf(C, val, def, cond) do { \ + if ((cond)) \ + { \ + colon_to_buf; \ + char_to_buf((C)); \ + if ((val) != (def)) int_to_buf(val); \ + } \ + } while (0) +#define op_str_to_buf(C, val, cond) do { \ + if ((cond)) \ + { \ + char *s=(val); \ + colon_to_buf; \ + char_to_buf((C)); \ + if (*s) str_to_buf(s); \ + } \ + } while (0) +#define op_list_to_buf(C, val, cond) do { \ + if ((cond)) \ + { \ + int f=ListFlags(val); \ + colon_to_buf; \ + char_to_buf((C)); \ + if (f & INCLUDE) \ + list_to_buf(val, INCLUDE); \ + if (f & EXCLUDE) \ + { \ + colon_to_buf; \ + char_to_buf('-'); \ + char_to_buf((C)); \ + list_to_buf(val, EXCLUDE); \ + } \ + } \ + } while (0) +#define op_bool_to_buf(C, cond) do { \ + if ((cond)) \ + { \ + colon_to_buf; \ + char_to_buf((C)); \ + } \ + } while (0) + +int _db_explain_ (CODE_STATE *cs, char *buf, size_t len) +{ + char *start=buf, *end=buf+len-4; + + get_code_state_if_not_set_or_return *buf=0; + + LockIfInitSettings(cs); + op_list_to_buf('d', cs->stack->keywords, DEBUGGING); + UnlockIfInitSettings(cs); + op_int_to_buf ('D', cs->stack->delay, 0); + op_list_to_buf('f', cs->stack->functions, cs->stack->functions); + op_bool_to_buf('F', cs->stack->flags & FILE_ON); + op_bool_to_buf('i', cs->stack->flags & PID_ON); + op_bool_to_buf('L', cs->stack->flags & LINE_ON); + op_bool_to_buf('n', cs->stack->flags & DEPTH_ON); + op_bool_to_buf('N', cs->stack->flags & NUMBER_ON); + op_str_to_buf( + ((cs->stack->flags & FLUSH_ON_WRITE ? 0 : 32) | + (cs->stack->flags & OPEN_APPEND ? 'A' : 'O')), + cs->stack->name, cs->stack->out_file != sstderr); + op_list_to_buf('p', cs->stack->processes, cs->stack->processes); + op_bool_to_buf('P', cs->stack->flags & PROCESS_ON); + op_bool_to_buf('r', cs->stack->sub_level != 0); + op_intf_to_buf('t', cs->stack->maxdepth, MAXDEPTH, TRACING); + op_bool_to_buf('T', cs->stack->flags & TIMESTAMP_ON); + op_bool_to_buf('S', cs->stack->flags & SANITY_CHECK_ON); + + *buf= '\0'; + return 0; + +overflow: + *end++= '.'; + *end++= '.'; + *end++= '.'; + *end= '\0'; + return 1; +} + +#undef char_to_buf +#undef str_to_buf +#undef list_to_buf +#undef int_to_buf +#undef colon_to_buf +#undef op_int_to_buf +#undef op_intf_to_buf +#undef op_str_to_buf +#undef op_list_to_buf +#undef op_bool_to_buf + +/* + * FUNCTION + * + * _db_explain_init_ explain initial debugger settings + * + * DESCRIPTION + * see _db_explain_ + */ + +int _db_explain_init_(char *buf, size_t len) +{ + CODE_STATE cs; + bzero((uchar*) &cs,sizeof(cs)); + cs.stack=&init_settings; + return _db_explain_(&cs, buf, len); +} + +/* + * FUNCTION + * + * _db_enter_ process entry point to user function + * + * SYNOPSIS + * + * VOID _db_enter_(_func_, _file_, _line_, _stack_frame_) + * char *_func_; points to current function name + * char *_file_; points to current file name + * int _line_; called from source line number + * struct _db_stack_frame_ allocated on the caller's stack + * + * DESCRIPTION + * + * Called at the beginning of each user function to tell + * the debugger that a new function has been entered. + * Note that the pointers to the previous user function + * name and previous user file name are stored on the + * caller's stack (this is why the ENTER macro must be + * the first "executable" code in a function, since it + * allocates these storage locations). The previous nesting + * level is also stored on the callers stack for internal + * self consistency checks. + * + * Also prints a trace line if tracing is enabled and + * increments the current function nesting depth. + * + * Note that this mechanism allows the debugger to know + * what the current user function is at all times, without + * maintaining an internal stack for the function names. + * + */ + +void _db_enter_(const char *_func_, const char *_file_, + uint _line_, struct _db_stack_frame_ *_stack_frame_) +{ + CODE_STATE *cs; + if (!((cs=code_state()))) + { + _stack_frame_->level= 0; /* Set to avoid valgrind warnings if dbug is enabled later */ + _stack_frame_->prev= 0; + return; + } + + _stack_frame_->line= -1; + _stack_frame_->func= cs->func; + _stack_frame_->file= cs->file; + cs->func= _func_; + cs->file= _file_; + _stack_frame_->prev= cs->framep; + _stack_frame_->level= ++cs->level | framep_trace_flag(cs, cs->framep); + cs->framep= _stack_frame_; + + switch (DoTrace(cs)) { + case ENABLE_TRACE: + cs->framep->level|= TRACE_ON; + if (!TRACING) break; + /* fall through */ + case DO_TRACE: + if ((cs->stack->flags & SANITY_CHECK_ON) && (*dbug_sanity)()) + cs->stack->flags &= ~SANITY_CHECK_ON; + if (TRACING) + { + int save_errno= errno; + LockMutex(cs); + DoPrefix(cs, _line_); + Indent(cs, cs->level); + (void) fprintf(cs->stack->out_file->file, ">%s\n", cs->func); + UnlockMutex(cs); + DbugFlush(cs); + errno=save_errno; + } + break; + case DISABLE_TRACE: + cs->framep->level&= ~TRACE_ON; + /* fall through */ + case DONT_TRACE: + break; + } +} + +/* + * FUNCTION + * + * _db_return_ process exit from user function + * + * SYNOPSIS + * + * VOID _db_return_(_line_, _stack_frame_) + * int _line_; current source line number + * struct _db_stack_frame_ allocated on the caller's stack + * + * DESCRIPTION + * + * Called just before user function executes an explicit or implicit + * return. Prints a trace line if trace is enabled, decrements + * the current nesting level, and restores the current function and + * file names from the defunct function's stack. + * + */ + +void _db_return_(struct _db_stack_frame_ *_stack_frame_) +{ + uint _slevel_= _stack_frame_->level & ~TRACE_ON; + CODE_STATE *cs; + get_code_state_or_return; + + if (_stack_frame_->line == 0) + return; + + if (_stack_frame_->line == -1 || cs->framep != _stack_frame_) + { + char buf[512]; + my_snprintf(buf, sizeof(buf), ERR_MISSING_RETURN, cs->func); + DbugExit(buf); + } + + if (DoTrace(cs) & DO_TRACE) + { + if ((cs->stack->flags & SANITY_CHECK_ON) && (*dbug_sanity)()) + cs->stack->flags &= ~SANITY_CHECK_ON; + if (TRACING) + { + int save_errno=errno; + LockMutex(cs); + DoPrefix(cs, _stack_frame_->line); + Indent(cs, cs->level); + (void) fprintf(cs->stack->out_file->file, "<%s\n", cs->func); + UnlockMutex(cs); + DbugFlush(cs); + errno=save_errno; + } + } + /* + Check to not set level < 0. This can happen if DBUG was disabled when + function was entered and enabled in function. + */ + cs->level= _slevel_ != 0 ? _slevel_ - 1 : 0; + cs->func= _stack_frame_->func; + cs->file= _stack_frame_->file; + if (cs->framep != NULL) + cs->framep= cs->framep->prev; +} + + +/* + * FUNCTION + * + * _db_pargs_ log arguments for subsequent use by _db_doprnt_() + * + * SYNOPSIS + * + * int _db_pargs_(_line_, keyword) + * int _line_; + * char *keyword; + * + * DESCRIPTION + * + * The new universal printing macro DBUG_PRINT, which replaces + * all forms of the DBUG_N macros, needs two calls to runtime + * support routines. The first, this function, remembers arguments + * that are used by the subsequent call to _db_doprnt_(). + * + */ + +int _db_pargs_(uint _line_, const char *keyword) +{ + CODE_STATE *cs; + get_code_state_or_return 0; + cs->u_line= _line_; + cs->u_keyword= keyword; + + return DEBUGGING && _db_keyword_(cs, cs->u_keyword, 0); +} + + +/* + * FUNCTION + * + * _db_doprnt_ handle print of debug lines + * + * SYNOPSIS + * + * VOID _db_doprnt_(format, va_alist) + * char *format; + * va_dcl; + * + * DESCRIPTION + * + * When invoked via one of the DBUG macros, tests the current keyword + * set by calling _db_pargs_() to see if that macro has been selected + * for processing via the debugger control string, and if so, handles + * printing of the arguments via the format string. The line number + * of the DBUG macro in the source is found in u_line. + * + * Note that the format string SHOULD NOT include a terminating + * newline, this is supplied automatically. + * + */ + +#include <stdarg.h> + +void _db_doprnt_(const char *format,...) +{ + va_list args; + CODE_STATE *cs; + int save_errno; + + get_code_state_or_return; + + va_start(args,format); + + save_errno=errno; + LockMutex(cs); + DoPrefix(cs, cs->u_line); + if (TRACING) + Indent(cs, cs->level + 1); + else + (void) fprintf(cs->stack->out_file->file, "%s: ", cs->func); + (void) fprintf(cs->stack->out_file->file, "%s: ", cs->u_keyword); + DbugVfprintf(cs->stack->out_file->file, format, args); + UnlockMutex(cs); + DbugFlush(cs); + errno=save_errno; + + va_end(args); +} + +/* + * This function is intended as a + * vfprintf clone with consistent, platform independent output for + * problematic formats like %p, %zd and %lld. + */ +static void DbugVfprintf(FILE *stream, const char* format, va_list args) +{ + char cvtbuf[1024]; + (void) my_vsnprintf(cvtbuf, sizeof(cvtbuf), format, args); + (void) fprintf(stream, "%s\n", cvtbuf); +} + + +/* + * FUNCTION + * + * _db_dump_ dump a string in hex + * + * SYNOPSIS + * + * void _db_dump_(_line_,keyword,memory,length) + * int _line_; current source line number + * char *keyword; + * char *memory; Memory to print + * int length; Bytes to print + * + * DESCRIPTION + * Dump N characters in a binary array. + * Is used to examine corrupted memory or arrays. + */ + +void _db_dump_(uint _line_, const char *keyword, + const unsigned char *memory, size_t length) +{ + int pos; + CODE_STATE *cs; + get_code_state_or_return; + + if (_db_keyword_(cs, keyword, 0)) + { + LockMutex(cs); + DoPrefix(cs, _line_); + if (TRACING) + { + Indent(cs, cs->level + 1); + pos= MY_MIN(MY_MAX(cs->level-cs->stack->sub_level,0)*INDENT,80); + } + else + { + fprintf(cs->stack->out_file->file, "%s: ", cs->func); + } + (void) fprintf(cs->stack->out_file->file, "%s: Memory: %p Bytes: (%ld)\n", + keyword, memory, (long) length); + + pos=0; + while (length-- > 0) + { + uint tmp= *((unsigned char*) memory++); + if ((pos+=3) >= 80) + { + fputc('\n',cs->stack->out_file->file); + pos=3; + } + fputc(_dig_vec_upper[((tmp >> 4) & 15)], cs->stack->out_file->file); + fputc(_dig_vec_upper[tmp & 15], cs->stack->out_file->file); + fputc(' ',cs->stack->out_file->file); + } + (void) fputc('\n',cs->stack->out_file->file); + UnlockMutex(cs); + DbugFlush(cs); + } +} + + +/* + * FUNCTION + * + * ListAddDel modify the list according to debug control string + * + * DESCRIPTION + * + * Given pointer to a comma separated list of strings in "cltp", + * parses the list, and modifies "listp", returning a pointer + * to the new list. + * + * The mode of operation is defined by "todo" parameter. + * + * If it is INCLUDE, elements (strings from "cltp") are added to the + * list, they will have INCLUDE flag set. If the list already contains + * the string in question, new element is not added, but a flag of + * the existing element is adjusted (INCLUDE bit is set, EXCLUDE bit + * is removed). + * + * If it is EXCLUDE, elements are added to the list with the EXCLUDE + * flag set. If the list already contains the string in question, + * it is removed, new element is not added. + */ + +static struct link *ListAddDel(struct link *head, const char *ctlp, + const char *end, int todo) +{ + const char *start; + struct link **cur; + size_t len; + int subdir; + + ctlp--; +next: + while (++ctlp < end) + { + start= ctlp; + subdir=0; + while (ctlp < end && *ctlp != ',') + ctlp++; + len= (int) (ctlp-start); + if (start[len-1] == '/') + { + len--; + subdir=SUBDIR; + } + if (len == 0) continue; + for (cur=&head; *cur; cur=&((*cur)->next_link)) + { + if (!strncmp((*cur)->str, start, len)) + { + if ((*cur)->flags & todo) /* same action ? */ + (*cur)->flags|= subdir; /* just merge the SUBDIR flag */ + else if (todo == EXCLUDE) + { + struct link *delme=*cur; + *cur=(*cur)->next_link; + free((void*) delme); + } + else + { + (*cur)->flags&=~(EXCLUDE & SUBDIR); + (*cur)->flags|=INCLUDE | subdir; + } + goto next; + } + } + *cur= (struct link *) DbugMalloc(sizeof(struct link)+len); + memcpy((*cur)->str, start, len); + (*cur)->str[len]=0; + (*cur)->flags=todo | subdir; + (*cur)->next_link=0; + } + return head; +} + +/* + * FUNCTION + * + * ListCopy make a copy of the list + * + * SYNOPSIS + * + * static struct link *ListCopy(orig) + * struct link *orig; + * + * DESCRIPTION + * + * Given pointer to list, which contains a copy of every element from + * the original list. + * + * the orig pointer can be NULL + * + * Note that since each link is added at the head of the list, + * the final list will be in "reverse order", which is not + * significant for our usage here. + * + */ + +static struct link *ListCopy(struct link *orig) +{ + struct link *new_malloc; + struct link *head; + size_t len; + + head= NULL; + while (orig != NULL) + { + len= strlen(orig->str); + new_malloc= (struct link *) DbugMalloc(sizeof(struct link)+len); + memcpy(new_malloc->str, orig->str, len); + new_malloc->str[len]= 0; + new_malloc->flags=orig->flags; + new_malloc->next_link= head; + head= new_malloc; + orig= orig->next_link; + } + return head; +} + +/* + * FUNCTION + * + * InList test a given string for member of a given list + * + * DESCRIPTION + * + * Tests the string pointed to by "cp" to determine if it is in + * the list pointed to by "linkp". Linkp points to the first + * link in the list. If linkp is NULL or contains only EXCLUDE + * elements then the string is treated as if it is in the list. + * This may seem rather strange at first but leads to the desired + * operation if no list is given. The net effect is that all + * strings will be accepted when there is no list, and when there + * is a list, only those strings in the list will be accepted. + * + * RETURN + * combination of SUBDIR, INCLUDE, EXCLUDE, MATCHED flags + * + */ + +static int InList(struct link *linkp, const char *cp, int exact_match) +{ + int result; + for (result=MATCHED; linkp != NULL; linkp= linkp->next_link) + { + if (!(exact_match ? strcmp(linkp->str,cp) : fnmatch(linkp->str, cp, 0))) + { + result= linkp->flags; + break; + } + if (!(linkp->flags & EXCLUDE)) + result=NOT_MATCHED; + if (linkp->flags & SUBDIR) + result|=SUBDIR; + } + return result; +} + +/* + * FUNCTION + * + * ListFlags returns aggregated list flags (ORed over all elements) + * + */ + +static uint ListFlags(struct link *linkp) +{ + uint f; + for (f=0; linkp != NULL; linkp= linkp->next_link) + f|= linkp->flags; + return f; +} + +/* + * FUNCTION + * + * PushState push current settings onto stack and set up new one + * + * SYNOPSIS + * + * static VOID PushState() + * + * DESCRIPTION + * + * Pushes the current settings on the settings stack, and creates + * a new settings. The new settings is NOT initialized + * + * The settings stack is a linked list of settings, with the new + * settings added at the head. This allows the stack to grow + * to the limits of memory if necessary. + * + */ + +static void PushState(CODE_STATE *cs) +{ + struct settings *new_malloc; + + new_malloc= (struct settings *) DbugMalloc(sizeof(struct settings)); + bzero(new_malloc, sizeof(*new_malloc)); + new_malloc->next= cs->stack; + cs->stack= new_malloc; +} + +/* + * FUNCTION + * + * FreeState Free memory associated with a struct state. + * + * SYNOPSIS + * + * static void FreeState (state) + * struct state *state; + * int free_state; + * + * DESCRIPTION + * + * Deallocates the memory allocated for various information in a + * state. If free_state is set, also free 'state' + * + */ +static void FreeState(CODE_STATE *cs, int free_state) +{ + struct settings *state= cs->stack; + LockIfInitSettings(cs); + if (!is_shared(state, keywords)) + { + FreeList(state->keywords); + state->keywords= NULL; + } + UnlockIfInitSettings(cs); + if (!is_shared(state, functions)) + FreeList(state->functions); + if (!is_shared(state, processes)) + FreeList(state->processes); + + DBUGCloseFile(cs, NULL); + + if (free_state) + { + struct settings *next= state->next; + free(state); + cs->stack= next; + } +} + + +/* + * FUNCTION + * + * _db_end_ End debugging, freeing state stack memory. + * + * SYNOPSIS + * + * static VOID _db_end_ () + * + * DESCRIPTION + * + * Ends debugging, de-allocating the memory allocated to the + * state stack. + * + * To be called at the very end of the program. + * + */ +void _db_end_() +{ + CODE_STATE *cs, dummy_cs; + /* + Set _dbug_on_ to be able to do full reset even when DEBUGGER_OFF was + called after dbug was initialized + */ + _dbug_on_= 1; + cs= code_state(); + + if (cs) + { + while (cs->stack && cs->stack != &init_settings) + FreeState(cs, 1); + } + else + { + cs= &dummy_cs; + bzero(cs, sizeof(*cs)); + } + + cs->stack= &init_settings; + FreeState(cs, 0); + pthread_mutex_destroy(&THR_LOCK_dbug); + init_done= 0; + _dbug_on_= 0; +} + + +/* + * FUNCTION + * + * DoTrace check to see if tracing is current enabled + * + * DESCRIPTION + * + * Checks to see if dbug in this function is enabled based on + * whether the maximum trace depth has been reached, the current + * function is selected, and the current process is selected. + * + */ + +static int DoTrace(CODE_STATE *cs) +{ + int res= DONT_TRACE; + if ((cs->stack->maxdepth == 0 || cs->level <= cs->stack->maxdepth) && + InList(cs->stack->processes, cs->process, 0) & (MATCHED|INCLUDE)) + { + switch(InList(cs->stack->functions, cs->func, 0)) { + case INCLUDE|SUBDIR: + res= ENABLE_TRACE; + break; + case INCLUDE: + res= DO_TRACE; + break; + case MATCHED|SUBDIR: + case NOT_MATCHED|SUBDIR: + case MATCHED: + res= (framep_trace_flag(cs, cs->framep) ? DO_TRACE : DONT_TRACE); + break; + case EXCLUDE: + case NOT_MATCHED: + res= DONT_TRACE; + break; + case EXCLUDE|SUBDIR: + res= DISABLE_TRACE; + break; + } + } + return res; +} + + +FILE *_db_fp_(void) +{ + CODE_STATE *cs; + get_code_state_or_return NULL; + return cs->stack->out_file->file; +} + +/* + * FUNCTION + * + * _db_keyword_ test keyword for member of keyword list + * + * DESCRIPTION + * + * Test a keyword to determine if it is in the currently active + * keyword list. If strict=0, a keyword is accepted + * if the list is null, otherwise it must match one of the list + * members. When debugging is not on, no keywords are accepted. + * After the maximum trace level is exceeded, no keywords are + * accepted (this behavior subject to change). Additionally, + * the current function and process must be accepted based on + * their respective lists. + * + * Returns TRUE if keyword accepted, FALSE otherwise. + * + */ + +BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict) +{ + int match= strict ? INCLUDE : INCLUDE|MATCHED; + int res; + get_code_state_if_not_set_or_return FALSE; + + if (!(DEBUGGING && (DoTrace(cs) & DO_TRACE))) + return 0; + LockIfInitSettings(cs); + res= (InList(cs->stack->keywords, keyword, strict) & match); + UnlockIfInitSettings(cs); + return res != 0; +} + +/* + * FUNCTION + * + * Indent indent a line to the given indentation level + * + * SYNOPSIS + * + * static VOID Indent(indent) + * int indent; + * + * DESCRIPTION + * + * Indent a line to the given level. Note that this is + * a simple minded but portable implementation. + * There are better ways. + * + * Also, the indent must be scaled by the compile time option + * of character positions per nesting level. + * + */ + +static void Indent(CODE_STATE *cs, int indent) +{ + int count; + + indent= MY_MAX(indent-1-cs->stack->sub_level,0)*INDENT; + for (count= 0; count < indent ; count++) + { + if ((count % INDENT) == 0) + fputc('|',cs->stack->out_file->file); + else + fputc(' ',cs->stack->out_file->file); + } +} + + +/* + * FUNCTION + * + * FreeList free all memory associated with a linked list + * + * SYNOPSIS + * + * static VOID FreeList(linkp) + * struct link *linkp; + * + * DESCRIPTION + * + * Given pointer to the head of a linked list, frees all + * memory held by the list and the members of the list. + * + */ + +static void FreeList(struct link *linkp) +{ + struct link *old; + + while (linkp != NULL) + { + old= linkp; + linkp= linkp->next_link; + free((void*) old); + } +} + + +/* + * FUNCTION + * + * DoPrefix print debugger line prefix prior to indentation + * + * SYNOPSIS + * + * static VOID DoPrefix(_line_) + * int _line_; + * + * DESCRIPTION + * + * Print prefix common to all debugger output lines, prior to + * doing indentation if necessary. Print such information as + * current process name, current source file name and line number, + * and current function nesting depth. + * + */ + +static void DoPrefix(CODE_STATE *cs, uint _line_) +{ + cs->lineno++; + if (cs->stack->flags & PID_ON) + { + (void) fprintf(cs->stack->out_file->file, "%-7s: ", my_thread_name()); + } + if (cs->stack->flags & NUMBER_ON) + (void) fprintf(cs->stack->out_file->file, "%5d: ", cs->lineno); + if (cs->stack->flags & TIMESTAMP_ON) + { +#ifdef __WIN__ + /* FIXME This doesn't give microseconds as in Unix case, and the resolution is + in system ticks, 10 ms intervals. See my_getsystime.c for high res */ + SYSTEMTIME loc_t; + GetLocalTime(&loc_t); + (void) fprintf (cs->stack->out_file->file, + /* "%04d-%02d-%02d " */ + "%02d:%02d:%02d.%06d ", + /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ + loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds); +#else + struct timeval tv; + struct tm *tm_p; + if (gettimeofday(&tv, NULL) != -1) + { + if ((tm_p= localtime((const time_t *)&tv.tv_sec))) + { + (void) fprintf (cs->stack->out_file->file, + /* "%04d-%02d-%02d " */ + "%02d:%02d:%02d.%06d ", + /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ + tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec, + (int) (tv.tv_usec)); + } + } +#endif + } + if (cs->stack->flags & PROCESS_ON) + (void) fprintf(cs->stack->out_file->file, "%s: ", cs->process); + if (cs->stack->flags & FILE_ON) + (void) fprintf(cs->stack->out_file->file, "%14s: ", BaseName(cs->file)); + if (cs->stack->flags & LINE_ON) + (void) fprintf(cs->stack->out_file->file, "%5d: ", _line_); + if (cs->stack->flags & DEPTH_ON) + (void) fprintf(cs->stack->out_file->file, "%4d: ", cs->level); +} + + +/* + * FUNCTION + * + * DBUGOpenFile open new output stream for debugger output + * + * SYNOPSIS + * + * static VOID DBUGOpenFile(name) + * char *name; + * + * DESCRIPTION + * + * Given name of a new file (or "-" for stdout) opens the file + * and sets the output stream to the new file. + * + */ + +static void DBUGOpenFile(CODE_STATE *cs, + const char *name,const char *end,int append) +{ + FILE *fp; + + if (name != NULL) + { + if (end) + { + size_t len=end-name; + memcpy(cs->stack->name, name, len); + cs->stack->name[len]=0; + } + else + strmov(cs->stack->name,name); + name=cs->stack->name; + if (strcmp(name, "-") == 0) + { + DBUGCloseFile(cs, sstdout); + cs->stack->flags |= FLUSH_ON_WRITE; + cs->stack->name[0]=0; + } + else + { + if (!Writable(name)) + { + (void) fprintf(stderr, ERR_OPEN, cs->process, name); + perror(""); + fflush(stderr); + } + else + { + if (!(fp= fopen(name, append ? "a+" : "w"))) + { + (void) fprintf(stderr, ERR_OPEN, cs->process, name); + perror(""); + fflush(stderr); + } + else + { + sFILE *sfp= (sFILE *)DbugMalloc(sizeof(sFILE)); + sfp->file= fp; + sfp->used= 1; + DBUGCloseFile(cs, sfp); + } + } + } + } +} + +/* + * FUNCTION + * + * DBUGCloseFile close the debug output stream + * + * SYNOPSIS + * + * static VOID DBUGCloseFile(fp) + * FILE *fp; + * + * DESCRIPTION + * + * Closes the debug output stream unless it is standard output + * or standard error. + * + */ + +static void DBUGCloseFile(CODE_STATE *cs, sFILE *new_value) +{ + sFILE *fp; + if (!cs || !cs->stack || !cs->stack->out_file) + return; + + fp= cs->stack->out_file; + if (--fp->used == 0) + { + if (fclose(fp->file) == EOF) + { + LockMutex(cs); + (void) fprintf(stderr, ERR_CLOSE, cs->process); + perror(""); + UnlockMutex(cs); + } + else + { + free(fp); + } + } + cs->stack->out_file= new_value; +} + + +/* + * FUNCTION + * + * DbugExit print error message and exit + * + * SYNOPSIS + * + * static VOID DbugExit(why) + * char *why; + * + * DESCRIPTION + * + * Prints error message using current process name, the reason for + * aborting (typically out of memory), and exits with status 1. + * This should probably be changed to use a status code + * defined in the user's debugger include file. + * + */ + +static void DbugExit(const char *why) +{ + CODE_STATE *cs=code_state(); + (void) fprintf(stderr, ERR_ABORT, cs ? cs->process : "(null)", why); + (void) fflush(stderr); + DBUG_ABORT(); +} + + +/* + * FUNCTION + * + * DbugMalloc allocate memory for debugger runtime support + * + * SYNOPSIS + * + * static long *DbugMalloc(size) + * int size; + * + * DESCRIPTION + * + * Allocate more memory for debugger runtime support functions. + * Failure to to allocate the requested number of bytes is + * immediately fatal to the current process. This may be + * rather unfriendly behavior. It might be better to simply + * print a warning message, freeze the current debugger cs, + * and continue execution. + * + */ + +static char *DbugMalloc(size_t size) +{ + char *new_malloc; + + if (!(new_malloc= (char*) malloc(size))) + DbugExit("out of memory"); + return new_malloc; +} + + +/* + * strtok lookalike - splits on ':', magically handles ::, :\ and :/ + */ + +static const char *DbugStrTok(const char *s) +{ + while (s[0] && (s[0] != ':' || + (s[1] == '\\' || s[1] == '/' || (s[1] == ':' && s++)))) + s++; + return s; +} + + +/* + * FUNCTION + * + * BaseName strip leading pathname components from name + * + * SYNOPSIS + * + * static char *BaseName(pathname) + * char *pathname; + * + * DESCRIPTION + * + * Given pointer to a complete pathname, locates the base file + * name at the end of the pathname and returns a pointer to + * it. + * + */ + +static const char *BaseName(const char *pathname) +{ + const char *base; + + base= strrchr(pathname, FN_LIBCHAR); + if (base++ == NullS) + base= pathname; + return base; +} + + +/* + * FUNCTION + * + * Writable test to see if a pathname is writable/creatable + * + * SYNOPSIS + * + * static BOOLEAN Writable(pathname) + * char *pathname; + * + * DESCRIPTION + * + * Because the debugger might be linked in with a program that + * runs with the set-uid-bit (suid) set, we have to be careful + * about opening a user named file for debug output. This consists + * of checking the file for write access with the real user id, + * or checking the directory where the file will be created. + * + * Returns TRUE if the user would normally be allowed write or + * create access to the named file. Returns FALSE otherwise. + * + */ + + +#ifndef Writable + +static BOOLEAN Writable(const char *pathname) +{ + BOOLEAN granted; + char *lastslash; + + granted= FALSE; + if (EXISTS(pathname)) + { + if (WRITABLE(pathname)) + granted= TRUE; + } + else + { + lastslash= strrchr(pathname, '/'); + if (lastslash != NULL) + *lastslash= '\0'; + else + pathname= "."; + if (WRITABLE(pathname)) + granted= TRUE; + if (lastslash != NULL) + *lastslash= '/'; + } + return granted; +} +#endif + +/* + flush dbug-stream, free mutex lock & wait delay + This is because some systems (MSDOS!!) doesn't flush fileheader + and dbug-file isn't readable after a system crash !! +*/ + +static void DbugFlush(CODE_STATE *cs) +{ + if (cs->stack->flags & FLUSH_ON_WRITE) + { + (void) fflush(cs->stack->out_file->file); + if (cs->stack->delay) + (void) Delay(cs->stack->delay); + } +} /* DbugFlush */ + + +/* For debugging */ + +void _db_flush_() +{ + CODE_STATE *cs; + get_code_state_or_return; + if (DEBUGGING) + { + (void) fflush(cs->stack->out_file->file); + } +} + + +#ifndef __WIN__ +void _db_suicide_() +{ + int retval; + sigset_t new_mask; + sigfillset(&new_mask); + + fprintf(stderr, "SIGKILL myself\n"); + fflush(stderr); + + retval= kill(getpid(), SIGKILL); + assert(retval == 0); + retval= sigsuspend(&new_mask); + fprintf(stderr, "sigsuspend returned %d errno %d \n", retval, errno); + assert(FALSE); /* With full signal mask, we should never return here. */ +} +#endif /* ! __WIN__ */ + + +void _db_lock_file_() +{ + CODE_STATE *cs; + get_code_state_or_return; + LockMutex(cs); +} + +void _db_unlock_file_() +{ + CODE_STATE *cs; + get_code_state_or_return; + UnlockMutex(cs); +} + +const char* _db_get_func_(void) +{ + CODE_STATE *cs; + get_code_state_or_return NULL; + return cs->func; +} + + +static int default_my_dbug_sanity(void) +{ + return 0; +} + +extern my_bool my_assert; +ATTRIBUTE_COLD +my_bool _db_my_assert(const char *file, int line, const char *msg) +{ + my_bool a = my_assert; + _db_flush_(); + if (!a) + fprintf(stderr, "%s:%d: assert: %s\n", file, line, msg); + return a; +} +#else + +/* + * Dummy function, workaround for build failure on a platform where linking + * with an empty archive fails. + */ +int i_am_a_dummy_function() { + return 0; +} + +#endif /* DBUG_OFF */ + +/* + This function is called by default on DBUG_ASSERT() when compiled with + DBUG_ASSERT_AS_PRINTF +*/ + +#ifdef DBUG_ASSERT_AS_PRINTF + +static void default_my_dbug_assert_failed(const char *assert_expr, + const char *file, + unsigned long line) +{ + fprintf(stderr, "Warning: assertion failed: %s at %s line %lu\n", + assert_expr, file, line); +} + +void (*my_dbug_assert_failed)(const char *assert_expr, const char* file, + unsigned long line)= default_my_dbug_assert_failed; + +#endif /* DBUG_ASSERT_AS_PRINTF */ diff --git a/dbug/dbug_add_tags.pl b/dbug/dbug_add_tags.pl new file mode 100755 index 00000000..f117bdcd --- /dev/null +++ b/dbug/dbug_add_tags.pl @@ -0,0 +1,88 @@ +#!/usr/bin/env perl + +# Copyright (c) 2002 MySQL AB, 2009 Sun Microsystems, Inc. +# Use is subject to license terms. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA + +die "No files specified\n" unless $ARGV[0]; + +$ctags="exctags -x -f - --c-types=f -u"; + +sub get_tag { + local $.; local $_=<TAGS>; + ($symbol, $line)= /^(.*\S)\s+function\s+(\d+)/; + $symbol=$1 if /[\s*]([^\s*]+)\s*\(/; + $line=1e50 unless $line; +} + +while($src=shift) +{ + warn "==> $src\n"; + + $dst=$src.$$; + open(TAGS, "$ctags $src|") || die "Cannot exec('$ctags $src'): $!"; + open(SRC, "<$src") || die "Cannot open $src: $!"; + open(DST, ">$dst") || die "Cannot create $dst: $!"; + select DST; + + &get_tag; + $in_func=0; + while(<SRC>) + { + my $orig=$_; + if ($in_func) + { + if (/\breturn\b/ && !/\/\*.*\breturn\b.*\*\// && !/;/ ) + { + $_.=<SRC> until /;/; + } + s/(?<=\s)return\s*;/DBUG_VOID_RETURN;/; + s/(?<=\s)return\s*(.+)\s*;/DBUG_RETURN(\1);/s; + $ret_line=$. if /DBUG_(VOID_)?RETURN/; #{{ + print "$tab DBUG_VOID_RETURN;\n" if /^$tab}/ && $ret_line < $.-1; + $in_func=0 if /^$tab}/; + warn "$src:".($.-1)."\t$orig" if /\breturn\b/; + } + print; + next if $. < $line; + die "Something wrong: \$.=$., \$line=$line, \$symbol=$symbol\n" if $. > $line; + &get_tag && next if /^\s*inline /; + print $_=<SRC> until /{/; $tab=$`; + &get_tag && next if /}/; # skip one-liners + $semicolon=1; + while(<SRC>) + { + $skip=!$semicolon; + $semicolon= /;\s*$/; + print && next if $skip || + (/^\s+\w+((::\w+)?|<\w+>)\s+\**\w+/ && !/^\s*return\b/); + last if /DBUG_ENTER/; + print "$tab DBUG_ENTER(\"$symbol\");\n"; + print "\n" unless $_ eq "\n"; + last; + } + $in_func=1; + &get_tag; + redo; + } + close SRC; + close DST; + close TAGS; + unlink("$src.orig"); + rename($src, "$src.orig") || die "Cannot rename $src to $src.orig: $!"; + rename($dst, $src) || die "Cannot rename $dst to $src: $!"; +} + +warn "All done!\n"; diff --git a/dbug/dbug_long.h b/dbug/dbug_long.h new file mode 100644 index 00000000..e77218b2 --- /dev/null +++ b/dbug/dbug_long.h @@ -0,0 +1,144 @@ +#error This file is not used in MySQL - see ../include/my_dbug.h instead +/****************************************************************************** + * * + * N O T I C E * + * * + * Copyright Abandoned, 1987, Fred Fish * + * * + * * + * This previously copyrighted work has been placed into the public * + * domain by the author and may be freely used for any purpose, * + * private or commercial. * + * * + * Because of the number of inquiries I was receiving about the use * + * of this product in commercially developed works I have decided to * + * simply make it public domain to further its unrestricted use. I * + * specifically would be most happy to see this material become a * + * part of the standard Unix distributions by AT&T and the Berkeley * + * Computer Science Research Group, and a standard part of the GNU * + * system from the Free Software Foundation. * + * * + * I would appreciate it, as a courtesy, if this notice is left in * + * all copies and derivative works. Thank you. * + * * + * The author makes no warranty of any kind with respect to this * + * product and explicitly disclaims any implied warranties of mer- * + * chantability or fitness for any particular purpose. * + * * + ****************************************************************************** + */ + +/* + * FILE + * + * dbug.h user include file for programs using the dbug package + * + * SYNOPSIS + * + * #include <local/dbug.h> + * + * SCCS ID + * + * @(#)dbug.h 1.13 7/17/89 + * + * DESCRIPTION + * + * Programs which use the dbug package must include this file. + * It contains the appropriate macros to call support routines + * in the dbug runtime library. + * + * To disable compilation of the macro expansions define the + * preprocessor symbol "DBUG_OFF". This will result in null + * macros expansions so that the resulting code will be smaller + * and faster. (The difference may be smaller than you think + * so this step is recommended only when absolutely necessary). + * In general, tradeoffs between space and efficiency are + * decided in favor of efficiency since space is seldom a + * problem on the new machines). + * + * All externally visible symbol names follow the pattern + * "_db_xxx..xx_" to minimize the possibility of a dbug package + * symbol colliding with a user defined symbol. + * + * The DBUG_<N> style macros are obsolete and should not be used + * in new code. Macros to map them to instances of DBUG_PRINT + * are provided for compatibility with older code. They may go + * away completely in subsequent releases. + * + * AUTHOR + * + * Fred Fish + * (Currently employed by Motorola Computer Division, Tempe, Az.) + * hao!noao!mcdsun!fnf + * (602) 438-3614 + * + */ + +/* + * Internally used dbug variables which must be global. + */ + +#ifndef DBUG_OFF + extern int _db_on_; /* TRUE if debug currently enabled */ + extern FILE *_db_fp_; /* Current debug output stream */ + extern char *_db_process_; /* Name of current process */ + extern int _db_keyword_ (); /* Accept/reject keyword */ + extern void _db_push_ (); /* Push state, set up new state */ + extern void _db_pop_ (); /* Pop previous debug state */ + extern void _db_enter_ (); /* New user function entered */ + extern void _db_return_ (); /* User function return */ + extern void _db_pargs_ (); /* Remember args for line */ + extern void _db_doprnt_ (); /* Print debug output */ + extern void _db_setjmp_ (); /* Save debugger environment */ + extern void _db_longjmp_ (); /* Restore debugger environment */ + extern void _db_dump_(); /* Dump memory */ +# endif + + +/* + * These macros provide a user interface into functions in the + * dbug runtime support library. They isolate users from changes + * in the MACROS and/or runtime support. + * + * The symbols "__LINE__" and "__FILE__" are expanded by the + * preprocessor to the current source file line number and file + * name respectively. + * + * WARNING --- Because the DBUG_ENTER macro allocates space on + * the user function's stack, it must precede any executable + * statements in the user function. + * + */ + +# ifdef DBUG_OFF +# define DBUG_ENTER(a1) +# define DBUG_RETURN(a1) return(a1) +# define DBUG_VOID_RETURN return +# define DBUG_EXECUTE(keyword,a1) +# define DBUG_PRINT(keyword,arglist) +# define DBUG_PUSH(a1) +# define DBUG_POP() +# define DBUG_PROCESS(a1) +# define DBUG_FILE (stderr) +# define DBUG_DUMP(keyword,a1) +# else +# define DBUG_ENTER(a) \ + auto char *_db_func_; auto char *_db_file_; auto int _db_level_; \ + auto char **_db_framep_; \ + _db_enter_ (a,__FILE__,__LINE__,&_db_func_,&_db_file_,&_db_level_, \ + &_db_framep_) +# define DBUG_LEAVE \ + (_db_return_ (__LINE__, &_db_func_, &_db_file_, &_db_level_)) +# define DBUG_RETURN(a1) return (DBUG_LEAVE, (a1)) +/* define DBUG_RETURN(a1) {DBUG_LEAVE; return(a1);} Alternate form */ +# define DBUG_VOID_RETURN {DBUG_LEAVE; return;} +# define DBUG_EXECUTE(keyword,a1) \ + {if (_db_on_) {if (_db_keyword_ (keyword)) { a1 }}} +# define DBUG_PRINT(keyword,arglist) \ + {if (_db_on_) {_db_pargs_(__LINE__,keyword); _db_doprnt_ arglist;}} +# define DBUG_PUSH(a1) _db_push_ (a1) +# define DBUG_POP() _db_pop_ () +# define DBUG_PROCESS(a1) (_db_process_ = a1) +# define DBUG_FILE (_db_fp_) +# define DBUG_DUMP(keyword,a1,a2) _db_dump_(__LINE__,keyword,a1,a2) +# endif diff --git a/dbug/example1.c b/dbug/example1.c new file mode 100644 index 00000000..7b3c3fcd --- /dev/null +++ b/dbug/example1.c @@ -0,0 +1,10 @@ +main (argc, argv) +int argc; +char *argv[]; +{ + printf ("argv[0] = %d\n", argv[0]); + /* + * Rest of program + */ + printf ("== done ==\n"); +} diff --git a/dbug/example2.c b/dbug/example2.c new file mode 100644 index 00000000..75fc1321 --- /dev/null +++ b/dbug/example2.c @@ -0,0 +1,15 @@ +int debug = 0; + +main (argc, argv) +int argc; +char *argv[]; +{ + /* printf ("argv = %x\n", argv) */ + if (debug) printf ("argv[0] = %d\n", argv[0]); + /* + * Rest of program + */ +#ifdef DEBUG + printf ("== done ==\n"); +#endif +} diff --git a/dbug/example3.c b/dbug/example3.c new file mode 100644 index 00000000..c035cdff --- /dev/null +++ b/dbug/example3.c @@ -0,0 +1,14 @@ +main (argc, argv) +int argc; +char *argv[]; +{ +# ifdef DEBUG + printf ("argv[0] = %d\n", argv[0]); +# endif + /* + * Rest of program + */ +# ifdef DEBUG + printf ("== done ==\n"); +# endif +} diff --git a/dbug/factorial.c b/dbug/factorial.c new file mode 100644 index 00000000..e1cb77f0 --- /dev/null +++ b/dbug/factorial.c @@ -0,0 +1,13 @@ +#include <my_global.h> + +int factorial ( +register int value) +{ + DBUG_ENTER ("factorial"); + DBUG_PRINT ("find", ("find %d factorial", value)); + if (value > 1) { + value *= factorial (value - 1); + } + DBUG_PRINT ("result", ("result is %d", value)); + DBUG_RETURN (value); +} diff --git a/dbug/main.c b/dbug/main.c new file mode 100644 index 00000000..00e80c8b --- /dev/null +++ b/dbug/main.c @@ -0,0 +1,24 @@ +#include <dbug.h> + +int main (argc, argv) +int argc; +char *argv[]; +{ + int result, ix; + extern int factorial(int); + DBUG_ENTER ("main"); + DBUG_PROCESS (argv[0]); + for (ix = 1; ix < argc && argv[ix][0] == '-'; ix++) { + switch (argv[ix][1]) { + case '#': + DBUG_PUSH (&(argv[ix][2])); + break; + } + } + for (; ix < argc; ix++) { + DBUG_PRINT ("args", ("argv[%d] = %s", ix, argv[ix])); + result = factorial (atoi(argv[ix])); + printf ("%d\n", result); + } + DBUG_RETURN (0); +} diff --git a/dbug/monty.doc b/dbug/monty.doc new file mode 100644 index 00000000..1af67b81 --- /dev/null +++ b/dbug/monty.doc @@ -0,0 +1,9 @@ + +All changes that I or other people at MySQL AB have done to all files +in the dbug library (Mainly in dbug.c, dbug_analyze.c, dbug_long.h, +dbug.h) are put in public domain, as the rest of the dbug.c library) + +To my knowledge, all code in dbug library is in public domain. + +Michael Widenius + diff --git a/dbug/my_main.c b/dbug/my_main.c new file mode 100644 index 00000000..2b3e92b5 --- /dev/null +++ b/dbug/my_main.c @@ -0,0 +1,37 @@ +/* + this is modified version of the original example main.c + fixed so that it could compile and run in MySQL source tree +*/ + +#include <my_global.h> /* This includes dbug.h */ +#include <my_sys.h> +#include <my_pthread.h> + +int main (argc, argv) +int argc; +char *argv[]; +{ + register int result, ix; + extern int factorial(int); + MY_INIT(argv[0]); + + { + DBUG_ENTER ("main"); + DBUG_PROCESS (argv[0]); + for (ix = 1; ix < argc && argv[ix][0] == '-'; ix++) { + switch (argv[ix][1]) { + case '#': + DBUG_PUSH (&(argv[ix][2])); + break; + } + } + for (; ix < argc; ix++) { + DBUG_PRINT ("args", ("argv[%d] = %s", ix, argv[ix])); + result = factorial (atoi(argv[ix])); + printf ("%d\n", result); + } + DBUG_LEAVE; + } + my_end(0); + exit(0); +} diff --git a/dbug/remove_function_from_trace.pl b/dbug/remove_function_from_trace.pl new file mode 100755 index 00000000..67d7fa54 --- /dev/null +++ b/dbug/remove_function_from_trace.pl @@ -0,0 +1,25 @@ +#!/usr/bin/env perl + +die <<EEE unless @ARGV; +Usage: $0 func1 [func2 [ ...] ] + +This filter (stdin->stdout) removes lines from dbug trace that were generated +by specified functions and all functions down the call stack. Produces the +same effect as if the original source had DBUG_PUSH(""); right after +DBUG_ENTER() and DBUG_POP(); right before DBUG_RETURN in every such a function. +EEE + +$re=join('|', @ARGV); + +while(<STDIN>) { + ($thd) = /^(T@\d+)/; + print unless $skip{$thd}; + next unless /^(?:.*: )*((?:\| )*)([<>])($re)\n/o; + if ($2 eq '>') { + $skip{$thd}=$1.$3 unless $skip{$thd}; + next; + } + next if $skip{$thd} ne $1.$3; + delete $skip{$thd}; + print; +} diff --git a/dbug/tests-t.pl b/dbug/tests-t.pl new file mode 100755 index 00000000..4803627e --- /dev/null +++ b/dbug/tests-t.pl @@ -0,0 +1,497 @@ +#!/usr/bin/env perl + +# +# A driver program to test DBUG features - runs tests (shell commands) +# from the end of file to invoke tests.c, which does the real dbug work. +# + +use Test::More; + +$exe=$0; +$exe =~ s@[^/]+$@tests@; + +die unless -x $exe; + +# load tests +@tests=(); +while (<DATA>) { + if (/^% \.\/tests /) { + push @tests, [ $' ] + } elsif (/^#/) { + next; + } else { + push @{$tests[$#tests]}, $_ + } +} + +plan skip_all => "because dbug is disabled" if system $exe; + +plan tests => scalar(@tests); + +for (@tests) { + $t=$exe . ' ' . shift @$_; + chomp($t); + open F, '-|', $t or die "open($t|): $!"; + local $"; + $out=join($", <F>); close(F); + # special cases are handled here: + $out =~ s/Memory: 0x[0-9A-Fa-f]+/Memory: 0x####/g if $t =~ /dump/; + # compare ("\n" at the beginning makes better output in case of errors) + is("\n$out","\n@$_", $t); +} + +__DATA__ +% ./tests -#d +func2: info: s=ok +=> execute +=> evaluate: ON +=> evaluate_if: OFF +main: explain: dbug explained: d +func2: info: s=ok +% ./tests d,ret3 +=> evaluate: OFF +=> evaluate_if: OFF +# +## Testing negative lists +# +% ./tests d:-d,ret3 +func2: info: s=ko +=> execute +=> evaluate: ON +=> evaluate_if: OFF +main: explain: dbug explained: d:-d,ret3 +func2: info: s=ko +% ./tests t:-d,ret3 +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +% ./tests t:d,info:-d,ret3 +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ko +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| | info: s=ko +| <func2 +<main +% ./tests t:d,info:-d,ret3:-f,func2 +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| | >func3 +| | <func3 +<main +% ./tests t:d,info:-d,ret3:-f,func2 d,evaluate +=> evaluate: ON +=> evaluate_if: OFF +% ./tests t:d,info:-d,ret3:-f,func2 d,evaluate_if +=> evaluate: OFF +=> evaluate_if: ON +% ./tests t:d:-d,ret3:-f,func2 d,evaluate_if +=> evaluate: OFF +=> evaluate_if: ON +% ./tests t:d:-d,ret3:-f,func2 +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +=> execute +=> evaluate: ON +=> evaluate_if: OFF +| explain: dbug explained: d:-d,ret3:f:-f,func2:t +| | >func3 +| | <func3 +<main +# +## Adding incremental settings to the brew +# +% ./tests t:d:-d,ret3:-f,func2 +d,evaluate_if +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +=> evaluate: OFF +=> evaluate_if: ON +| | >func3 +| | <func3 +<main +# +## DBUG_DUMP +# +% ./tests t:d:-d,ret3:f:-f,func2 +d,dump +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +| dump: Memory: 0x#### Bytes: (27) +64 2C 64 75 6D 70 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D 66 2C 66 75 6E 63 32 3A +74 +=> evaluate: OFF +=> evaluate_if: OFF +| | >func3 +| | <func3 +<main +% ./tests t:d:-d,ret3:f:-f,func2 +d,dump +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +| dump: Memory: 0x#### Bytes: (27) +64 2C 64 75 6D 70 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D 66 2C 66 75 6E 63 32 3A +74 +=> evaluate: OFF +=> evaluate_if: OFF +| | >func3 +| | <func3 +<main +% ./tests t:d:-d,ret3:f:-f,func2:+d,dump +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +| dump: Memory: 0x#### Bytes: (27) +64 2C 64 75 6D 70 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D 66 2C 66 75 6E 63 32 3A +74 +=> evaluate: OFF +=> evaluate_if: OFF +| | >func3 +| | <func3 +<main +% ./tests t:d:-d,ret3:f:-f,func2 +d,dump,explain +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +| dump: Memory: 0x#### Bytes: (35) +64 2C 64 75 6D 70 2C 65 78 70 6C 61 69 6E 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D +66 2C 66 75 6E 63 32 3A 74 +=> evaluate: OFF +=> evaluate_if: OFF +| explain: dbug explained: d,dump,explain:-d,ret3:f:-f,func2:t +| | >func3 +| | <func3 +<main +% ./tests t:d:-d,ret3:f:-f,func2 +d,dump,explain:P +dbug-tests: >main +dbug-tests: | >func1 +dbug-tests: | | | >func3 +dbug-tests: | | | <func3 +dbug-tests: | <func1 +dbug-tests: | dump: Memory: 0x#### Bytes: (37) +64 2C 64 75 6D 70 2C 65 78 70 6C 61 69 6E 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D +66 2C 66 75 6E 63 32 3A 50 3A 74 +=> evaluate: OFF +=> evaluate_if: OFF +dbug-tests: | explain: dbug explained: d,dump,explain:-d,ret3:f:-f,func2:P:t +dbug-tests: | | >func3 +dbug-tests: | | <func3 +dbug-tests: <main +% ./tests t:d:-d,ret3:f:-f,func2 +d,dump,explain:P:F +dbug-tests: tests.c: >main +dbug-tests: tests.c: | >func1 +dbug-tests: tests.c: | | | >func3 +dbug-tests: tests.c: | | | <func3 +dbug-tests: tests.c: | <func1 +dbug-tests: tests.c: | dump: Memory: 0x#### Bytes: (39) +64 2C 64 75 6D 70 2C 65 78 70 6C 61 69 6E 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D +66 2C 66 75 6E 63 32 3A 46 3A 50 3A 74 +=> evaluate: OFF +=> evaluate_if: OFF +dbug-tests: tests.c: | explain: dbug explained: d,dump,explain:-d,ret3:f:-f,func2:F:P:t +dbug-tests: tests.c: | | >func3 +dbug-tests: tests.c: | | <func3 +dbug-tests: tests.c: <main +# +## DBUG_EXPLAIN, DBUG_PUSH, DBUG_POP, DBUG_SET +# +% ./tests t:d:-d,ret3:f:-f,func2 +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +=> execute +=> evaluate: ON +=> evaluate_if: OFF +| explain: dbug explained: d:-d,ret3:f:-f,func2:t +| | >func3 +| | <func3 +<main +% ./tests t:d:-d,ret3 +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ko +| | <func2 +| <func1 +=> execute +=> evaluate: ON +=> evaluate_if: OFF +| explain: dbug explained: d:-d,ret3:t +| >func2 +| | >func3 +| | <func3 +| | info: s=ko +| <func2 +<main +% ./tests d,info:-d,ret3:d,push +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| | info: s=ko +| <func2 +<main +% ./tests d,info:-d,ret3:d,push,explain +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF +| explain: dbug explained: d,info,push,explain:-d,ret3:t +| >func2 +| | >func3 +| | <func3 +| | info: s=ko +| <func2 +<main +% ./tests d,info:-d,ret3:d,explain +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF +main: explain: dbug explained: d,info,explain:-d,ret3 +func2: info: s=ko +% ./tests d,info:-d,ret3:d,explain,pop +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF +% ./tests d,info:-d,ret3:d,explain t:d,pop +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +main: explain: dbug explained: d,info,explain:-d,ret3 +func2: info: s=ko +% ./tests d,info:-d,ret3:d,explain,pop +t +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ko +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +main: explain: dbug explained: d,info,explain,pop:-d,ret3 +func2: info: s=ko +% ./tests d,info:-d,ret3:d,explain,set +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF + tests.c: main: explain: dbug explained: d,info,explain,set:-d,ret3:F + tests.c: func2: info: s=ko +% ./tests d,info:-d,ret3:d,explain,set:t +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ko +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF + tests.c: | explain: dbug explained: d,info,explain,set:-d,ret3:F:t + tests.c: | >func2 + tests.c: | | >func3 + tests.c: | | <func3 + tests.c: | | info: s=ko + tests.c: | <func2 + tests.c: <main +% ./tests t d,info:-d,ret3:d,explain,set:t +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ko +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF + tests.c: | explain: dbug explained: d,info,explain,set:-d,ret3:F:t + tests.c: | >func2 + tests.c: | | >func3 + tests.c: | | <func3 + tests.c: | | info: s=ko + tests.c: | <func2 + tests.c: <main +% ./tests t d,info:-d,ret3:d,explain,set,pop +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +% ./tests t:f,func2 +| | >func2 +| | <func2 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| <func2 +# +## Testing SUBDIR rules +# +% ./tests t:-f,func2/:d +>main +| >func1 +| <func1 +=> execute +=> evaluate: ON +=> evaluate_if: OFF +| explain: dbug explained: d:f:-f,func2/:t +<main +% ./tests t:f,func1/:d +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ok +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +% ./tests t:f,main/:d,pop +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +% ./tests f,main/:d,push +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +# +## Testing FixTraceFlags() - when we need to traverse the call stack +# (these tests fail with FixTraceFlags() disabled) +# +# delete the INCLUDE rule up the stack +% ./tests t:f,func1/ --push1=t:f,func3/ +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | <func2 +=> push1 +=> evaluate: OFF +=> evaluate_if: OFF +| | >func3 +| | <func3 +# delete the EXCLUDE rule up the stack +% ./tests t:-f,func1/ --push1=t +>main +=> push1 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +# add the INCLUDE rule up the stack +% ./tests t:f,func3 --push1=t:f,main/ +| | | >func3 +| | | <func3 +=> push1 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +# add the EXCLUDE rule up the stack +% ./tests t --push1=t:-f,main/ +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | <func2 +=> push1 +=> evaluate: OFF +=> evaluate_if: OFF +# change the defaults +% ./tests t:f,func3 --push1=t +| | | >func3 +| | | <func3 +=> push1 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +# repeated keyword +% ./tests d:-d,info,info +=> execute +=> evaluate: ON +=> evaluate_if: OFF +main: explain: dbug explained: d:-d,info +% ./tests d:-d,info/,info +=> execute +=> evaluate: ON +=> evaluate_if: OFF +main: explain: dbug explained: d:-d,info/ diff --git a/dbug/tests.c b/dbug/tests.c new file mode 100644 index 00000000..3e77bf82 --- /dev/null +++ b/dbug/tests.c @@ -0,0 +1,100 @@ +/* + A program to test DBUG features. Used by tests-t.pl +*/ + +char *push1=0; + +#ifndef DBUG_TRACE +#define DBUG_TRACE +#endif + +#include <my_global.h> /* This includes dbug.h */ +#include <my_sys.h> +#include <my_pthread.h> +#include <string.h> + +const char *func3() +{ + DBUG_ENTER("func3"); + DBUG_RETURN(DBUG_EVALUATE("ret3", "ok", "ko")); +} + +void func2() +{ + const char *s __attribute__((unused)); + DBUG_ENTER("func2"); + s=func3(); + DBUG_PRINT("info", ("s=%s", s)); + DBUG_VOID_RETURN; +} + +int func1() +{ + DBUG_ENTER("func1"); + func2(); + if (push1) + { + DBUG_PUSH(push1); + fprintf(DBUG_FILE, "=> push1\n"); + } + DBUG_RETURN(10); +} + +int main (int argc __attribute__((unused)), + char *argv[] __attribute__((unused))) +{ +#ifdef DBUG_OFF + return 1; +#else + int i; + if (argc == 1) + return 0; + + MY_INIT("dbug-tests"); + + dup2(1, 2); + for (i = 1; i < argc; i++) + { + if (strncmp(argv[i], "--push1=", 8) == 0) + push1=argv[i]+8; + else + DBUG_PUSH (argv[i]); + } + { + DBUG_ENTER ("main"); + func1(); + DBUG_EXECUTE_IF("dump", + { + char s[1000]; + DBUG_EXPLAIN(s, sizeof(s)-1); + DBUG_DUMP("dump", (uchar*)s, strlen(s)); + }); + DBUG_EXECUTE_IF("push", DBUG_PUSH("+t"); ); + DBUG_EXECUTE("execute", fprintf(DBUG_FILE, "=> execute\n"); ); + DBUG_EXECUTE_IF("set", DBUG_SET("+F"); ); + fprintf(DBUG_FILE, "=> evaluate: %s\n", + DBUG_EVALUATE("evaluate", "ON", "OFF")); + fprintf(DBUG_FILE, "=> evaluate_if: %s\n", + DBUG_EVALUATE_IF("evaluate_if", "ON", "OFF")); + DBUG_EXECUTE_IF("pop", DBUG_POP(); ); + { + char s[1000] __attribute__((unused)); + DBUG_EXPLAIN(s, sizeof(s)-1); + DBUG_PRINT("explain", ("dbug explained: %s", s)); + } + func2(); + DBUG_LEAVE; + } + DBUG_SET(""); /* to not have my_end() in the traces */ + my_end(0); + return 0; +#endif /* DBUG_OFF */ +} + +#ifdef __SANITIZE_ADDRESS__ +/* Disable LeakSanitizer in this executable */ +const char* __asan_default_options() +{ + return "detect_leaks=0"; +} +#endif diff --git a/dbug/user.r b/dbug/user.r new file mode 100644 index 00000000..8d8a9ce6 --- /dev/null +++ b/dbug/user.r @@ -0,0 +1,1121 @@ +.\" @(#)user.r 1.13 10/29/86 +.\" +.\" 2004-10-29: documented features implemented since 10/29/86 +.\" formatting cleanup +.\" - Sergei Golubchik +.\" +.\" DBUG (Macro Debugger Package) nroff source +.\" +.\" nroff -mm user.r >user.t +.\" groff -mm user.r >user.ps +.\" +.\" =================================================== +.\" +.\" === Some sort of black magic, but I forget... +.tr ~ +.\" === Hyphenation control (1 = on) +.\".nr Hy 1 +.\" === Force all first level headings to start on new page +.nr Ej 1 +.\" === Set for breaks after headings for levels 1-3 +.nr Hb 3 +.\" === Set for space after headings for levels 1-3 +.nr Hs 3 +.\" === Set standard indent for one/half inch +.nr Si 10 +.\" === Set page header +.PH "/DBUG User Manual//\*(DT/" +.\" === Set page footer +.PF "// - % - //" +.\" === Set page offset +.\".po 0.60i +.\" === Set line length +.\".ll 6.5i +.TL +.warn 0 +D B U G +.P 0 +C Program Debugging Package +.P 0 +by +.AU "Fred Fish" +.AF "" +.SA 1 +.\" === All paragraphs indented. +.nr Pt 1 +.AS 1 +This document introduces +.I dbug , +a macro based C debugging +package which has proven to be a very flexible and useful tool +for debugging, testing, and porting C programs. + +.P +All of the features of the +.I dbug +package can be enabled or disabled dynamically at execution time. +This means that production programs will run normally when +debugging is not enabled, and eliminates the need to maintain two +separate versions of a program. + +.P +Many of the things easily accomplished with conventional debugging +tools, such as symbolic debuggers, are difficult or impossible with this +package, and vice versa. +Thus the +.I dbug +package should +.I not +be thought of as a replacement or substitute for +other debugging tools, but simply as a useful +.I addition +to the +program development and maintenance environment. + +.AE +.MT 4 +.SK +.B +INTRODUCTION +.R + +.P +Almost every program development environment worthy of the name +provides some sort of debugging facility. +Usually this takes the form of a program which is capable of +controlling execution of other programs and examining the internal +state of other executing programs. +These types of programs will be referred to as external debuggers +since the debugger is not part of the executing program. +Examples of this type of debugger include the +.B adb +and +.B sdb +debuggers provided with the +.B UNIX\*F +.FS +UNIX is a trademark of AT&T Bell Laboratories. +.FE +operating system. + +.P +One of the problems associated with developing programs in an environment +with good external debuggers is that developed programs tend to have +little or no internal instrumentation. +This is usually not a problem for the developer since he is, +or at least should be, intimately familiar with the internal organization, +data structures, and control flow of the program being debugged. +It is a serious problem for maintenance programmers, who +are unlikely to have such familiarity with the program being +maintained, modified, or ported to another environment. +It is also a problem, even for the developer, when the program is +moved to an environment with a primitive or unfamiliar debugger, +or even no debugger. + +.P +On the other hand, +.I dbug +is an example of an internal debugger. +Because it requires internal instrumentation of a program, +and its usage does not depend on any special capabilities of +the execution environment, it is always available and will +execute in any environment that the program itself will +execute in. +In addition, since it is a complete package with a specific +user interface, all programs which use it will be provided +with similar debugging capabilities. +This is in sharp contrast to other forms of internal instrumentation +where each developer has their own, usually less capable, form +of internal debugger. +In summary, +because +.I dbug +is an internal debugger it provides consistency across operating +environments, +and because it is available to all developers it provides +consistency across all programs in the same environment. + +.P +The +.I dbug +package imposes only a slight speed penalty on executing +programs, typically much less than 10 percent, and a modest size +penalty, typically 10 to 20 percent. +By defining a specific C preprocessor symbol both of these +can be reduced to zero with no changes required to the +source code. + +.P +The following list is a quick summary of the capabilities +of the +.I dbug +package. +Each capability can be individually enabled or disabled +at the time a program is invoked by specifying the appropriate +command line arguments. +.SP 1 +.ML o 1i +.LI +Execution trace showing function level control flow in a +semi-graphically manner using indentation to indicate nesting +depth. +.LI +Output the values of all, or any subset of, key internal variables. +.LI +Limit actions to a specific set of named functions. +.LI +Limit function trace to a specified nesting depth. +.LI +Label each output line with source file name and line number. +.LI +Label each output line with name of current process. +.LI +Push or pop internal debugging state to allow execution with +built in debugging defaults. +.LI +Redirect the debug output stream to standard output (stdout) +or a named file. +The default output stream is standard error (stderr). +The redirection mechanism is completely independent of +normal command line redirection to avoid output conflicts. +.LE + +.SK +.B +PRIMITIVE DEBUGGING TECHNIQUES +.R + +.P +Internal instrumentation is already a familiar concept +to most programmers, since it is usually the first debugging +technique learned. +Typically, "print\ statements" are inserted in the source +code at interesting points, the code is recompiled and executed, +and the resulting output is examined in an attempt to determine +where the problem is. + +The procedure is iterative, with each iteration yielding more +and more output, and hopefully the source of the problem is +discovered before the output becomes too large to deal with +or previously inserted statements need to be removed. +Figure 1 is an example of this type of primitive debugging +technique. +.DS I N +.SP 2 +\fC +.so example1.r +\fR +.SP 2 +.ll -5 +.ce +Figure 1 +.ce +Primitive Debugging Technique +.ll +5 +.SP 2 +.DE + +.P +Eventually, and usually after at least several iterations, the +problem will be found and corrected. +At this point, the newly inserted print statements must be +dealt with. +One obvious solution is to simply delete them all. +Beginners usually do this a few times until they have to +repeat the entire process every time a new bug pops up. +The second most obvious solution is to somehow disable +the output, either through the source code comment facility, +creation of a debug variable to be switched on or off, or by using the +C preprocessor. +Figure 2 is an example of all three techniques. +.DS I N +.SP 2 +\fC +.so example2.r +\fR +.SP 2 +.ll -5 +.ce +Figure 2 +.ce +Debug Disable Techniques +.ll +5 +.SP 2 +.DE + +.P +Each technique has its advantages and disadvantages with respect +to dynamic vs static activation, source code overhead, recompilation +requirements, ease of use, program readability, etc. +Overuse of the preprocessor solution quickly leads to problems with +source code readability and maintainability when multiple +.B #ifdef +symbols are to be defined or undefined based on specific types +of debug desired. +The source code can be made slightly more readable by suitable indentation +of the +.B #ifdef +arguments to match the indentation of the code, but +not all C preprocessors allow this. +The only requirement for the standard +.B UNIX +C preprocessor is for the '#' character to appear +in the first column, but even this seems +like an arbitrary and unreasonable restriction. +Figure 3 is an example of this usage. +.DS I N +.SP 2 +\fC +.so example3.r +\fR +.SP 2 +.ll -5 +.ce +Figure 3 +.ce +More Readable Preprocessor Usage +.ll +5 +.SP 2 +.DE + +.SK +.B +FUNCTION TRACE EXAMPLE +.R + +.P +We will start off learning about the capabilities of the +.I dbug +package by using a simple minded program which computes the +factorial of a number. +In order to better demonstrate the function trace mechanism, this +program is implemented recursively. +Figure 4 is the main function for this factorial program. +.DS I N +.SP 2 +\fC +.so main.r +\fR +.SP 2 +.ll -5 +.ce +Figure 4 +.ce +Factorial Program Mainline +.ll +5 +.SP 2 +.DE + +.P +The +.B main +function is responsible for processing any command line +option arguments and then computing and printing the factorial of +each non-option argument. +.P +First of all, notice that all of the debugger functions are implemented +via preprocessor macros. +This does not detract from the readability of the code and makes disabling +all debug compilation trivial (a single preprocessor symbol, +.B DBUG_OFF , +forces the macro expansions to be null). +.P +Also notice the inclusion of the header file +.B dbug.h +from the local header file directory. +(The version included here is the test version in the dbug source +distribution directory). +This file contains all the definitions for the debugger macros, which +all have the form +.B DBUG_XX...XX . + +.P +The +.B DBUG_ENTER +macro informs that debugger that we have entered the +function named +.B main . +It must be the very first "executable" line in a function, after +all declarations and before any other executable line. +The +.B DBUG_PROCESS +macro is generally used only once per program to +inform the debugger what name the program was invoked with. +The +.B DBUG_PUSH +macro modifies the current debugger state by +saving the previous state and setting a new state based on the +control string passed as its argument. +The +.B DBUG_PRINT +macro is used to print the values of each argument +for which a factorial is to be computed. +The +.B DBUG_RETURN +macro tells the debugger that the end of the current +function has been reached and returns a value to the calling +function. +All of these macros will be fully explained in subsequent sections. +.P +To use the debugger, the factorial program is invoked with a command +line of the form: +.DS CB N +\fCfactorial -#d:t 1 2 3 +.DE +The +.B main +function recognizes the "-#d:t" string as a debugger control +string, and passes the debugger arguments ("d:t") to the +.I dbug +runtime support routines via the +.B DBUG_PUSH +macro. +This particular string enables output from the +.B DBUG_PRINT +macro with the 'd' flag and enables function tracing with the 't' flag. +The factorial function is then called three times, with the arguments +"1", "2", and "3". +Note that the DBUG_PRINT takes exactly +.B two +arguments, with the second argument (a format string and list +of printable values) enclosed in parentheses. +.P +Debug control strings consist of a header, the "-#", followed +by a colon separated list of debugger arguments. +Each debugger argument is a single character flag followed +by an optional comma separated list of arguments specific +to the given flag. +Some examples are: +.DS CB N +\fC +-#d:t:o +-#d,in,out:f,main:F:L +.DE +Note that previously enabled debugger actions can be disabled by the +control string "-#". + +.P +The definition of the factorial function, symbolized as "N!", is +given by: +.DS CB N +N! = N * N-1 * ... 2 * 1 +.DE +Figure 5 is the factorial function which implements this algorithm +recursively. +Note that this is not necessarily the best way to do factorials +and error conditions are ignored completely. +.DS I N +.SP 2 +\fC +.so factorial.r +\fR +.SP 2 +.ll -5 +.ce +Figure 5 +.ce +Factorial Function +.ll +5 +.SP 2 +.DE + +.P +One advantage (some may not consider it so) to using the +.I dbug +package is that it strongly encourages fully structured coding +with only one entry and one exit point in each function. +Multiple exit points, such as early returns to escape a loop, +may be used, but each such point requires the use of an +appropriate +.B DBUG_RETURN +or +.B DBUG_VOID_RETURN +macro. + +.P +To build the factorial program on a +.B UNIX +system, compile and +link with the command: +.DS CB N +\fCcc -o factorial main.c factorial.c -ldbug +.DE +The "-ldbug" argument tells the loader to link in the +runtime support modules for the +.I dbug +package. +Executing the factorial program with a command of the form: +.DS CB N +\fCfactorial 1 2 3 4 5 +.DE +generates the output shown in figure 6. +.DS I N +.SP 2 +\fC +.so output1.r +\fR +.SP 2 +.ll -5 +.ce +Figure 6 +.ce +\fCfactorial 1 2 3 4 5 +.ll +5 +.SP 2 +.DE + +.P +Function level tracing is enabled by passing the debugger +the 't' flag in the debug control string. +Figure 7 is the output resulting from the command +"factorial\ -#t:o\ 2\ 3". +.DS I N +.SP 2 +\fC +.so output2.r +\fR +.SP 2 +.ll -5 +.ce +Figure 7 +.ce +\fCfactorial -#t:o 2 3 +.ll +5 +.SP 2 +.DE + +.P +Each entry to or return from a function is indicated by '>' for the +entry point and '<' for the exit point, connected by +vertical bars to allow matching points to be easily found +when separated by large distances. + +.P +This trace output indicates that there was an initial call +to factorial from main (to compute 2!), followed by +a single recursive call to factorial to compute 1!. +The main program then output the result for 2! and called the +factorial function again with the second argument, 3. +Factorial called itself recursively to compute 2! and 1!, then +returned control to main, which output the value for 3! and exited. + +.P +Note that there is no matching entry point "main>" for the +return point "<main" because at the time the +.B DBUG_ENTER +macro was reached in main, tracing was not enabled yet. +It was only after the macro +.B DBUG_PUSH +was executing that tracing became enabled. +This implies that the argument list should be processed as early as +possible since all code preceding the first call to +.B DBUG_PUSH +is +essentially invisible to +.I dbug +(this can be worked around by +inserting a temporary +.B DBUG_PUSH(argv[1]) +immediately after the +.B DBUG_ENTER("main") +macro. + +.P +One last note, +the trace output normally comes out on the standard error. +Since the factorial program prints its result on the standard +output, there is the possibility of the output on the terminal +being scrambled if the two streams are not synchronized. +Thus the debugger is told to write its output on the standard +output instead, via the 'o' flag character. +Note that no 'o' implies the default (standard error), a 'o' +with no arguments means standard output, and a 'o' +with an argument means used the named file. +i.e, "factorial\ -#t:o,logfile\ 3\ 2" would write the trace +output in "logfile". +Because of +.B UNIX +implementation details, programs usually run +faster when writing to stdout rather than stderr, though this +is not a prime consideration in this example. + +.SK +.B +USE OF DBUG_PRINT MACRO +.R + +.P +The mechanism used to produce "printf" style output is the +.B DBUG_PRINT +macro. + +.P +To allow selection of output from specific macros, the first argument +to every +.B DBUG_PRINT +macro is a +.I dbug +keyword. +When this keyword appears in the argument list of the 'd' flag in +a debug control string, as in "-#d,keyword1,keyword2,...:t", +output from the corresponding macro is enabled. +The default when there is no 'd' flag in the control string is to +enable output from all +.B DBUG_PRINT +macros. + +.P +Typically, a program will be run once, with no keywords specified, +to determine what keywords are significant for the current problem +(the keywords are printed in the macro output line). +Then the program will be run again, with the desired keywords, +to examine only specific areas of interest. + +.P +The second argument to a +.B DBUG_PRINT +macro is a standard printf style +format string and one or more arguments to print, all +enclosed in parentheses so that they collectively become a single macro +argument. +This is how variable numbers of printf arguments are supported. +Also note that no explicit newline is required at the end of the format string. +As a matter of style, two or three small +.B DBUG_PRINT +macros are preferable +to a single macro with a huge format string. +Figure 8 shows the output for default tracing and debug. +.DS I N +.SP 2 +\fC +.so output3.r +\fR +.SP 2 +.ll -5 +.ce +Figure 8 +.ce +\fCfactorial -#d:t:o 3 +.ll +5 +.SP 2 +.DE + +.P +The output from the +.B DBUG_PRINT +macro is indented to match the trace output +for the function in which the macro occurs. +When debugging is enabled, but not trace, the output starts at the left +margin, without indentation. + +.P +To demonstrate selection of specific macros for output, figure +9 shows the result when the factorial program is invoked with +the debug control string "-#d,result:o". +.DS I N +.SP 2 +\fC +.so output4.r +\fR +.SP 2 +.ll -5 +.ce +Figure 9 +.ce +\fCfactorial -#d,result:o 4 +.ll +5 +.SP 2 +.DE + +.P +It is sometimes desirable to restrict debugging and trace actions +to a specific function or list of functions. +This is accomplished with the 'f' flag character in the debug +control string. +Figure 10 is the output of the factorial program when run with the +control string "-#d:f,factorial:F:L:o". +The 'F' flag enables printing of the source file name and the 'L' +flag enables printing of the source file line number. +.DS I N +.SP 2 +\fC +.so output5.r +\fR +.SP 2 +.ll -5 +.ce +Figure 10 +.ce +\fCfactorial -#d:f,factorial:F:L:o 3 +.ll +5 +.SP 2 +.DE + +.P +The output in figure 10 shows that the "find" macro is in file +"factorial.c" at source line 8 and the "result" macro is in the same +file at source line 12. + +.SK +.B +SUMMARY OF MACROS +.R + +.P +This section summarizes the usage of all currently defined macros +in the +.I dbug +package. +The macros definitions are found in the user include file +.B dbug.h +from the standard include directory. + +.SP 2 +.BL 20 +.LI DBUG_ENTER\ +Used to tell the runtime support module the name of the function being +entered. The argument must be of type "pointer to character". The +DBUG_ENTER macro must precede all executable lines in the function +just entered, and must come after all local declarations. Each +DBUG_ENTER macro must have a matching DBUG_RETURN or DBUG_VOID_RETURN +macro at the function exit points. DBUG_ENTER macros used without a +matching DBUG_RETURN or DBUG_VOID_RETURN macro will cause warning +messages from the +.I dbug +package runtime support module. +.SP 1 +EX:\ \fCDBUG_ENTER\ ("main");\fR +.SP 1 +.LI DBUG_RETURN\ +Used at each exit point of a function containing a DBUG_ENTER macro at +the entry point. The argument is the value to return. Functions +which return no value (void) should use the DBUG_VOID_RETURN macro. +It is an error to have a DBUG_RETURN or DBUG_VOID_RETURN macro in a +function which has no matching DBUG_ENTER macro, and the compiler will +complain if the macros are actually used (expanded). +.SP 1 +EX:\ \fCDBUG_RETURN\ (value);\fR +.br +EX:\ \fCDBUG_VOID_RETURN;\fR +.SP 1 +.LI DBUG_PROCESS\ +Used to name the current process being executed. +A typical argument for this macro is "argv[0]", though +it will be perfectly happy with any other string. +Im multi-threaded environment threads may have different names. +.SP 1 +EX:\ \fCDBUG_PROCESS\ (argv[0]);\fR +.SP 1 +.LI DBUG_PUSH\ +Sets a new debugger state by pushing the current +.I dbug +state onto an internal stack and setting up the new state using the +debug control string passed as the macro argument. The most common +usage is to set the state specified by a debug control string +retrieved from the argument list. If the control string is +.I incremental, +the new state is a copy of the old state, modified by the control +string. +.SP 1 +EX:\ \fCDBUG_PUSH\ (\&(argv[i][2]));\fR +.br +EX:\ \fCDBUG_PUSH\ ("d:t");\fR +.br +EX:\ \fCDBUG_PUSH\ ("");\fR +.SP 1 +.LI DBUG_POP\ +Restores the previous debugger state by popping the state stack. +Attempting to pop more states than pushed will be ignored and no +warning will be given. The DBUG_POP macro has no arguments. +.SP 1 +EX:\ \fCDBUG_POP\ ();\fR +.SP 1 +.LI DBUG_SET\ +Modifies the current debugger state on top of the stack or pushes +a new state if the current is set to the initial settings, using +the debug control string passed as the macro argument. Unless +.I incremental +control string is used (see below), it's equivalent to a combination of +DBUG_POP and DBUG_PUSH. +.SP 1 +EX:\ \fCDBUG_SET\ ("d:t");\fR +.br +EX:\ \fCDBUG_SET\ ("+d,info");\fR +.br +EX:\ \fCDBUG_SET\ ("+t:-d");\fR +.SP 1 +.LI DBUG_FILE\ +The DBUG_FILE macro is used to do explicit I/O on the debug output +stream. It is used in the same manner as the symbols "stdout" and +"stderr" in the standard I/O package. +.SP 1 +EX:\ \fCfprintf\ (DBUG_FILE,\ "Doing\ my\ own\ I/O!\\n");\fR +.SP 1 +.LI DBUG_EXECUTE\ +The DBUG_EXECUTE macro is used to execute any arbitrary C code. The +first argument is the debug keyword, used to trigger execution of the +code specified as the second argument. This macro must be used +cautiously because, like the DBUG_PRINT macro, it is automatically +selected by default whenever the 'd' flag has no argument list (i.e., +a "-#d:t" control string). +.SP 1 +EX:\ \fCDBUG_EXECUTE\ ("status",\ print_status\ ());\fR +.SP 1 +.LI DBUG_EXECUTE_IF\ +Works like DBUG_EXECUTE macro, but the code is +.B not +executed "by default", if the keyword is not explicitly listed in +the 'd' flag. Used to conditionally execute "dangerous" actions, e.g +to crash the program testing how recovery works, or to introduce an +artificial delay checking for race conditions. +.SP 1 +EX:\ \fCDBUG_EXECUTE_IF\ ("crashme",\ DBUG_ABORT()\ ());\fR +.SP 1 +.LI DBUG_EVALUATE\ +The DBUG_EVALUATE macro is similar to DBUG_EXECUTE, but it can be used in +the expression context. The first argument is the debug keyword that is used to +choose whether the second (keyword is enabled) or the third (keyword is not +enabled) argument is evaluated. When +.I dbug +is compiled off, the third argument is evaluated. +.SP 1 +EX:\fC +.br + printf("Info-debug is %s", +.br + DBUG_EVALUATE\ ("info", "ON", "OFF"));\fR +.SP 1 +.LI DBUG_EVALUATE_IF\ +Works like DBUG_EVALUATE macro, but the second argument is +.B not +evaluated, if the keyword is not explicitly listed in +the 'd' flag. Like DBUG_EXECUTE_IF this could be used to conditionally execute +"dangerous" actions. +.SP 1 +EX:\fC +.br + if (prepare_transaction () || +.br + DBUG_EVALUATE ("crashme", (DBUG_ABORT(), 0), 0) || +.br + commit_transaction () )\fR +.SP 1 +.LI DBUG_PRINT\ +Used to do printing via the "fprintf" library function on the current +debug stream, DBUG_FILE. The first argument is a debug keyword, the +second is a format string and the corresponding argument list. Note +that the format string and argument list are all one macro argument +and +.B must +be enclosed in parentheses. +.SP 1 +EX:\ \fCDBUG_PRINT\ ("eof",\ ("end\ of\ file\ found"));\fR +.br +EX:\ \fCDBUG_PRINT\ ("type",\ ("type\ is\ %x", type));\fR +.br +EX:\ \fCDBUG_PRINT\ ("stp",\ ("%x\ ->\ %s", stp, stp\ ->\ name));\fR +.SP 1 +.LI DBUG_DUMP\ +Used to dump a memory block in hex via the "fprintf" library function +on the current debug stream, DBUG_FILE. The first argument is a debug +keyword, the second is a pointer to a memory to dump, the third is a +number of bytes to dump. +.SP 1 +EX: \fCDBUG_DBUG\ ("net",\ packet,\ len);\fR +.SP 1 +.LI DBUG_LOCK_FILE\ +Used in multi-threaded environment to lock DBUG_FILE stream. +It can be used, for example, in functions that need to write something to a +debug stream more than in one fprintf() call and want to ensure that no other +thread will write something in between. +.SP 1 +EX:\fC +.br + DBUG_LOCK_FILE; +.br + fprintf (DBUG_FILE, "a=["); +.br + for (int i=0; i < a_length; i++) +.br + fprintf (DBUG_FILE, "0x%03x ", a[i]); +.br + fprintf (DBUG_FILE, "]"); +.br + DBUG_UNLOCK_FILE;\fR +.SP 1 +.LI DBUG_UNLOCK_FILE\ +Unlocks DBUG_FILE stream, that was locked with a DBUG_LOCK_FILE. +.LI DBUG_ASSERT\ +This macro just does a regular assert(). The difference is that it will be +disabled by DBUG_OFF togeher with the +.I dbug +library. So there will be no need to disable asserts separately with NDEBUG. +.SP 1 +EX:\ \fCDBUG_ASSERT(\ a\ >\ 0\ );\fR +.SP 1 +.LI DBUG_ABORT\ +This macro could be used instead of abort(). It flushes DBUG_FILE stream +to ensure that no +.I dbug +output is lost and then calls abort(). +.SP 1 +.LI DBUG_EXPLAIN\ +Generates control string corresponding to the current debug state. +The macro takes two arguments - a buffer to store the result string +into and its length. The macro (which could be used as a function) +returns 1 if the control string didn't fit into the buffer and was +truncated and 0 otherwise. +.SP 1 +EX:\fC +.br + char buf[256]; +.br + DBUG_EXPLAIN( buf, sizeof(buf) );\fR +.SP 1 +.LI DBUG_SET_INITIAL\ +.LI DBUG_EXPLAIN_INITIAL\ +.br +These two macros are identical to DBUG_SET and DBUG_EXPLAIN, but they +operate on the debug state that any new thread starts from. +Modifying +.I initial +value does not affect threads that are already running. Obviously, +these macros are only useful in the multi-threaded environment. +.LE + +.SK +.B +DEBUG CONTROL STRING +.R + +.P +The debug control string is used to set the state of the debugger +via the +.B DBUG_PUSH +or +.B DBUG_SET +macros. Control string consists of colon separated flags. Colons +that are part of ':\\', ':/', or '::' are not considered flag +separators. A flag may take an argument or a list of arguments. +If a control string starts from a '+' sign it works +.I incrementally, +that is, it can modify existing state without overriding it. Every +flag may be preceded by a '+' or '-' to enable or disable a +corresponding option in the debugger state or to add or remove +arguments to the list. This section summarizes the currently available +debugger options and the flag characters which enable or disable them. +Argument lists enclosed in '[' and ']' are optional. +.SP 2 +.BL 22 +.LI a[,file] +Redirect the debugger output stream and append it to the specified +file. The default output stream is stderr. A null argument list +causes output to be redirected to stdout. +.SP 1 +EX: \fCa,C:\\tmp\\log\fR +.LI A[,file] +Like 'a[,file]' but ensure that data are written after each write +(this typically implies flush or close/reopen). It helps to get +a complete log file in case of crashes. This mode is implicit in +multi-threaded environment. +.LI d[,keywords] +Enable output from macros with specified keywords. +Every keyword can be a +.I glob(7) +pattern. +An empty list of keywords implies that all keywords are selected. +.LI D[,time] +Delay for specified time after each output line, to let output drain. +Time is given in tenths of a second (value of 10 is one second). +Default is zero. +.LI f[,functions] +Limit debugger actions to the specified list of functions. +Every function can be a +.I glob(7) +pattern. +An empty list of functions implies that all functions are selected. +Every function in the list may optionally be followed by a '/' - +this will implicitly select all the functions down the call stack. +.SP 1 +EX: \fCf,func1,func2/:-f,func3,func4/\fR +.SP 1 +This would enable debugger in functions 'func1()', 'func2()' and all +functions called from it (directly or indirectly). But not in +functions 'func3()' or 'func4()' and all functions called from +it. +.LI F +Mark each debugger output line with the name of the source file +containing the macro causing the output. +.LI i +Mark each debugger output line with the PID (or thread ID) of the +current process. +.LI L +Mark each debugger output line with the source file line number of +the macro causing the output. +.LI n +Mark each debugger output line with the current function nesting depth. +.LI N +Sequentially number each debugger output line starting at 1. +This is useful for reference purposes when debugger output is +interspersed with program output. +.LI o[,file] +Like 'a[,file]' but overwrite old file, do not append. +.LI O[,file] +Like 'A[,file]' but overwrite old file, do not append. +.LI p[,processes] +Limit debugger actions to the specified processes. +Every name can be a +.I glob(7) +pattern. +An empty list +implies all processes. This is useful for processes which run child +processes. Note that each debugger output line can be marked with the +name of the current process via the 'P' flag. The process name must +match the argument passed to the +.B DBUG_PROCESS +macro. +.LI P +Mark each debugger output line with the name of the current process. +Most useful when used with a process which runs child processes that +are also being debugged. Note that the parent process must arrange +for the debugger control string to be passed to the child processes. +.LI r +Used in conjunction with the +.B DBUG_PUSH +macro to reset the current +indentation level back to zero. +Most useful with +.B DBUG_PUSH +macros used to temporarily alter the +debugger state. +.LI S +When compiled with +.I safemalloc +this flag invokes "sanity" memory checks (for overwrites/underwrites) +on each +.B DBUG_ENTER +and +.B DBUG_RETURN. +.LI t[,N] +Enable function control flow tracing. +The maximum nesting depth is specified by N, and defaults to +200. +.LI T +Mark each debugger output line with the current timestamp. +The value is printed with microsecond resolution, as returned by +.I gettimeofday() +system call. The actual resolution is OS- and hardware-dependent. +.LE + +.SK +.B +MULTI-THREADED DEBUGGING +.R + +.P +When +.I dbug +is used in a multi-threaded environment there are few differences from a single-threaded +case to keep in mind. This section tries to summarize them. +.SP 2 +.BL 5 +.LI +Every thread has its own stack of debugger states. +.B DBUG_PUSH +and +.B DBUG_POP +affect only the thread that executed them. +.LI +At the bottom of the stack for all threads there is the common +.I initial +state. Changes to this state (for example, with +.B DBUG_SET_INITIAL +macro) affect all new threads and all running threads that didn't +.B DBUG_PUSH +yet. +.LI +Every thread can have its own name, that can be set with +.B DBUG_PROCESS +macro. Thus, "-#p,name1,name2" can be used to limit the output to specific threads. +.LI +When printing directly to +.B DBUG_FILE +it may be necessary to prevent other threads from writing something between two parts +of logically indivisible output. It is done with +.B DBUG_LOCK_FILE +and +.B DBUG_UNLOCK_FILE +macors. See the appropriate section for examples. +.LI +"-#o,file" and "-#O,file" are treated as "-#a,file" and "-#A,file" respectively. That is +all writes to a file are always followed by a flush. +.LI +"-#i" prints not a PID but a thread id in the form of "T@nnn" +.LE + +.SK +.B + +HINTS AND MISCELLANEOUS +.R + +.P +One of the most useful capabilities of the +.I dbug +package is to compare the executions of a given program in two +different environments. +This is typically done by executing the program in the environment +where it behaves properly and saving the debugger output in a +reference file. +The program is then run with identical inputs in the environment where +it misbehaves and the output is again captured in a reference file. +The two reference files can then be differentially compared to +determine exactly where execution of the two processes diverges. + +.P +A related usage is regression testing where the execution of a current +version is compared against executions of previous versions. +This is most useful when there are only minor changes. + +.P +It is not difficult to modify an existing compiler to implement +some of the functionality of the +.I dbug +package automatically, without source code changes to the +program being debugged. +In fact, such changes were implemented in a version of the +Portable C Compiler by the author in less than a day. +However, it is strongly encouraged that all newly +developed code continue to use the debugger macros +for the portability reasons noted earlier. +The modified compiler should be used only for testing existing +programs. + +.SK +.B +CAVEATS +.R + +.P +The +.I dbug +package works best with programs which have "line\ oriented" +output, such as text processors, general purpose utilities, etc. +It can be interfaced with screen oriented programs such as +visual editors by redefining the appropriate macros to call +special functions for displaying the debugger results. +Of course, this caveat is not applicable if the debugger output +is simply dumped into a file for post-execution examination. + +.P +Programs which use memory allocation functions other than +.B malloc +will usually have problems using the standard +.I dbug +package. +The most common problem is multiply allocated memory. +.SP 2 +.\" .DE nroff dident like this. davida 900108 +.CS + +.\" vim:filetype=nroff |