aboutsummaryrefslogtreecommitdiff
path: root/externals/mysql/extlib/dbug
diff options
context:
space:
mode:
authorXanadu <none@none>2010-07-20 02:49:28 +0200
committerXanadu <none@none>2010-07-20 02:49:28 +0200
commit79622802f397258ee0f34327ba3ae6977ca3e7ff (patch)
tree1868946c234ab9ee256a6b7766a15713eae94235 /externals/mysql/extlib/dbug
parent7dd2dc91816ab8b3bc3b99a1b1c99c7ea314d5a8 (diff)
parentf906976837502fa5aa81b982b901d1509f5aa0c4 (diff)
Merge. Revision history for source files should be all back now.
--HG-- branch : trunk rename : sql/CMakeLists.txt => sql/tools/CMakeLists.txt rename : src/server/game/Pools/PoolHandler.cpp => src/server/game/Pools/PoolMgr.cpp rename : src/server/game/Pools/PoolHandler.h => src/server/game/Pools/PoolMgr.h rename : src/server/game/PrecompiledHeaders/NixCorePCH.cpp => src/server/game/PrecompiledHeaders/gamePCH.cpp rename : src/server/game/PrecompiledHeaders/NixCorePCH.h => src/server/game/PrecompiledHeaders/gamePCH.h
Diffstat (limited to 'externals/mysql/extlib/dbug')
-rw-r--r--externals/mysql/extlib/dbug/dbug.c2583
-rw-r--r--externals/mysql/extlib/dbug/dbug_analyze.c726
-rw-r--r--externals/mysql/extlib/dbug/dbug_long.h160
-rw-r--r--externals/mysql/extlib/dbug/example1.c10
-rw-r--r--externals/mysql/extlib/dbug/example2.c15
-rw-r--r--externals/mysql/extlib/dbug/example3.c14
-rw-r--r--externals/mysql/extlib/dbug/factorial.c27
-rw-r--r--externals/mysql/extlib/dbug/main.c24
-rw-r--r--externals/mysql/extlib/dbug/my_main.c42
-rw-r--r--externals/mysql/extlib/dbug/sanity.c13
-rw-r--r--externals/mysql/extlib/dbug/tests.c87
11 files changed, 3701 insertions, 0 deletions
diff --git a/externals/mysql/extlib/dbug/dbug.c b/externals/mysql/extlib/dbug/dbug.c
new file mode 100644
index 00000000000..68932adf1e1
--- /dev/null
+++ b/externals/mysql/extlib/dbug/dbug.c
@@ -0,0 +1,2583 @@
+/******************************************************************************
+ * *
+ * 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
+ *
+ * Binayak Banerjee (profiling enhancements)
+ * seismo!bpa!sjuvax!bbanerje
+ *
+ * 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
+*/
+
+#undef SAFE_MUTEX
+#include <my_global.h>
+#include <m_string.h>
+#include <errno.h>
+
+#ifdef HAVE_FNMATCH_H
+#include <fnmatch.h>
+#else
+#define fnmatch(A,B,C) strcmp(A,B)
+#endif
+
+#if defined(MSDOS) || defined(__WIN__)
+#include <process.h>
+#endif
+
+#ifndef DBUG_OFF
+
+
+/*
+ * 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 (1 << 1) /* Debug enabled */
+#define FILE_ON (1 << 2) /* File name print enabled */
+#define LINE_ON (1 << 3) /* Line number print enabled */
+#define DEPTH_ON (1 << 4) /* Function nest level print enabled */
+#define PROCESS_ON (1 << 5) /* Process name print enabled */
+#define NUMBER_ON (1 << 6) /* Number each line of output */
+#define PROFILE_ON (1 << 7) /* Print out profiling code */
+#define PID_ON (1 << 8) /* Identify each line with process id */
+#define TIMESTAMP_ON (1 << 9) /* timestamp every line of output */
+#define SANITY_CHECK_ON (1 << 10) /* Check safemalloc on DBUG_ENTER */
+#define FLUSH_ON_WRITE (1 << 11) /* Flush on every write */
+#define OPEN_APPEND (1 << 12) /* Open for append */
+#define TRACE_ON ((uint)1 << 31) /* Trace enabled. MUST be the highest bit!*/
+
+#define TRACING (cs->stack->flags & TRACE_ON)
+#define DEBUGGING (cs->stack->flags & DEBUG_ON)
+#define PROFILING (cs->stack->flags & PROFILE_ON)
+
+/*
+ * Typedefs to make things more obvious.
+ */
+
+#define BOOLEAN my_bool
+
+/*
+ * Make it easy to change storage classes if necessary.
+ */
+
+#define IMPORT extern /* Names defined externally */
+#define EXPORT /* Allocated here, available globally */
+#define AUTO auto /* Names to be allocated on stack */
+#define REGISTER register /* Names to be placed in registers */
+
+/*
+ * The default file for profiling. Could also add another flag
+ * (G?) which allowed the user to specify this.
+ *
+ * If the automatic variables get allocated on the stack in
+ * reverse order from their declarations, then define AUTOS_REVERSE to 1.
+ * This is used by the code that keeps track of stack usage. For
+ * forward allocation, the difference in the dbug frame pointers
+ * represents stack used by the callee function. For reverse allocation,
+ * the difference represents stack used by the caller function.
+ *
+ */
+
+#define PROF_FILE "dbugmon.out"
+#define PROF_EFMT "E\t%ld\t%s\n"
+#define PROF_SFMT "S\t%lx\t%lx\t%s\n"
+#define PROF_XFMT "X\t%ld\t%s\n"
+
+#ifdef M_I386 /* predefined by xenix 386 compiler */
+#define AUTOS_REVERSE 1
+#else
+#define AUTOS_REVERSE 0
+#endif
+
+/*
+ * Externally supplied functions.
+ */
+
+#ifndef HAVE_PERROR
+static void perror(); /* Fake system/library error print routine */
+#endif
+
+#ifdef SAFEMALLOC
+IMPORT int _sanity(const char *file,uint line); /* safemalloc sanity checker */
+#else
+#define _sanity(X,Y) (1)
+#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 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 */
+ FILE *out_file; /* Current output stream */
+ FILE *prof_file; /* Current profiling stream */
+ char name[FN_REFLEN]; /* Name of output file */
+ struct link *functions; /* List of functions */
+ struct link *p_functions; /* List of profiled 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 */
+ const char *jmpfunc; /* Remember current function for setjmp */
+ const char *jmpfile; /* Remember current file for setjmp */
+ int lineno; /* Current debugger output line number */
+ uint level; /* Current function nesting level */
+ int jmplevel; /* Remember nesting level at setjmp() */
+
+/*
+ * 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);
+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, FILE *fp);
+ /* Push current debug settings */
+static void PushState(CODE_STATE *cs);
+ /* Free memory associated with debug state. */
+static void FreeState (CODE_STATE *cs, struct settings *state, int free_state);
+ /* Test for tracing enabled */
+static int DoTrace(CODE_STATE *cs);
+/*
+ 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) && !defined(MSDOS)
+static BOOLEAN Writable(const char *pathname);
+ /* Change file owner and group */
+static void ChangeOwner(CODE_STATE *cs, char *pathname);
+ /* Allocate memory for runtime support */
+#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 DbugFprintf(FILE *stream, const char* format, va_list args);
+
+#ifndef THREAD
+ /* Open profile output stream */
+static FILE *OpenProfile(CODE_STATE *cs, const char *name);
+ /* Profile if asked for it */
+static BOOLEAN DoProfile(CODE_STATE *);
+ /* Return current user time (ms) */
+static unsigned long Clock(void);
+#endif
+
+/*
+ * Miscellaneous printf format strings.
+ */
+
+#define ERR_MISSING_RETURN "%s: 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"
+#define ERR_CHOWN "%s: can't change owner/group of \"%s\": "
+
+/*
+ * Macros and defines for testing file accessibility under UNIX and MSDOS.
+ */
+
+#undef EXISTS
+#if !defined(HAVE_ACCESS) || defined(MSDOS)
+#define EXISTS(pathname) (FALSE) /* Assume no existance */
+#define Writable(name) (TRUE)
+#else
+#define EXISTS(pathname) (access(pathname, F_OK) == 0)
+#define WRITABLE(pathname) (access(pathname, W_OK) == 0)
+#endif
+#ifndef MSDOS
+#define ChangeOwner(cs,name)
+#endif
+
+
+/*
+** Macros to allow dbugging with threads
+*/
+
+#ifdef THREAD
+#include <my_pthread.h>
+static pthread_mutex_t THR_LOCK_dbug;
+
+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;
+ pthread_mutex_init(&THR_LOCK_dbug,MY_MUTEX_INIT_FAST);
+ bzero(&init_settings, sizeof(init_settings));
+ init_settings.out_file=stderr;
+ 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;
+}
+
+#else /* !THREAD */
+
+static CODE_STATE static_code_state=
+{
+ "dbug", "?func", "?file", NULL, &init_settings,
+ NullS, NullS, 0,0,0,0,0,NullS
+};
+
+static CODE_STATE *code_state(void)
+{
+ if (!init_done)
+ {
+ bzero(&init_settings, sizeof(init_settings));
+ init_settings.out_file=stderr;
+ init_settings.flags=OPEN_APPEND;
+ init_done=TRUE;
+ }
+ return &static_code_state;
+}
+
+#define pthread_mutex_lock(A) {}
+#define pthread_mutex_unlock(A) {}
+#endif
+
+/*
+ * 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
+ */
+
+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)))
+ {
+ FreeState(cs, stack, 0);
+ stack->flags= 0;
+ stack->delay= 0;
+ stack->maxdepth= 0;
+ stack->sub_level= 0;
+ stack->out_file= stderr;
+ stack->prof_file= NULL;
+ stack->functions= NULL;
+ stack->p_functions= NULL;
+ stack->keywords= NULL;
+ stack->processes= NULL;
+ }
+ 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->prof_file= stack->next->prof_file;
+ if (stack->next == &init_settings)
+ {
+ /* never share with the global parent - it can change under your feet */
+ stack->functions= ListCopy(init_settings.functions);
+ stack->p_functions= ListCopy(init_settings.p_functions);
+ stack->keywords= ListCopy(init_settings.keywords);
+ stack->processes= ListCopy(init_settings.processes);
+ }
+ else
+ {
+ stack->functions= stack->next->functions;
+ stack->p_functions= stack->next->p_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)
+ {
+ if (!is_shared(stack, keywords))
+ FreeList(stack->keywords);
+ stack->keywords=NULL;
+ stack->flags &= ~DEBUG_ON;
+ break;
+ }
+ if (rel && is_shared(stack, keywords))
+ stack->keywords= ListCopy(stack->keywords);
+ if (sign < 0)
+ {
+ if (DEBUGGING)
+ stack->keywords= ListDel(stack->keywords, control, end);
+ break;
+ }
+ stack->keywords= ListAdd(stack->keywords, control, end);
+ 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;
+#ifndef THREAD
+ case 'g':
+ if (OpenProfile(cs, PROF_FILE))
+ {
+ stack->flags |= PROFILE_ON;
+ stack->p_functions= ListAdd(stack->p_functions, control, end);
+ }
+ break;
+#endif
+ 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)
+ {
+ if (!is_shared(stack, out_file))
+ DBUGCloseFile(cs, stack->out_file);
+ stack->flags &= ~FLUSH_ON_WRITE;
+ stack->out_file= stderr;
+ 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)
+
+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;
+
+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 with DbugParse()
+ *
+ */
+
+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;
+ 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_()
+{
+ struct settings *discard;
+ uint old_fflags;
+ CODE_STATE *cs;
+
+ get_code_state_or_return;
+
+ discard= cs->stack;
+ if (discard != &init_settings)
+ {
+ old_fflags=fflags(cs);
+ cs->stack= discard->next;
+ FreeState(cs, discard, 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), len+1); \
+ 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;
+
+ op_list_to_buf('d', cs->stack->keywords, DEBUGGING);
+ 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_list_to_buf('g', cs->stack->p_functions, PROFILING);
+ 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 != stderr);
+ 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_)
+{
+ int save_errno;
+ 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;
+ }
+ save_errno= errno;
+
+ _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_;
+#ifndef THREAD
+ if (DoProfile(cs))
+ {
+ long stackused;
+ if (cs->framep->prev == NULL)
+ stackused= 0;
+ else
+ {
+ stackused= (char*)(cs->framep->prev) - (char*)(cs->framep);
+ stackused= stackused > 0 ? stackused : -stackused;
+ }
+ (void) fprintf(cs->stack->prof_file, PROF_EFMT , Clock(), cs->func);
+ (void) fprintf(cs->stack->prof_file, PROF_SFMT, (ulong) cs->framep, stackused,
+ AUTOS_REVERSE ? _stack_frame_->func : cs->func);
+ (void) fflush(cs->stack->prof_file);
+ }
+#endif
+ 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) && _sanity(_file_,_line_))
+ cs->stack->flags &= ~SANITY_CHECK_ON;
+ if (TRACING)
+ {
+ if (!cs->locked)
+ pthread_mutex_lock(&THR_LOCK_dbug);
+ DoPrefix(cs, _line_);
+ Indent(cs, cs->level);
+ (void) fprintf(cs->stack->out_file, ">%s\n", cs->func);
+ DbugFlush(cs); /* This does a unlock */
+ }
+ break;
+ case DISABLE_TRACE:
+ cs->framep->level&= ~TRACE_ON;
+ /* fall through */
+ case DONT_TRACE:
+ break;
+ }
+ errno=save_errno;
+}
+
+/*
+ * 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.
+ *
+ */
+
+/* helper macro */
+void _db_return_(uint _line_, struct _db_stack_frame_ *_stack_frame_)
+{
+ int save_errno=errno;
+ uint _slevel_= _stack_frame_->level & ~TRACE_ON;
+ CODE_STATE *cs;
+ get_code_state_or_return;
+
+ if (cs->level != _slevel_)
+ {
+ if (!cs->locked)
+ pthread_mutex_lock(&THR_LOCK_dbug);
+ (void) fprintf(cs->stack->out_file, ERR_MISSING_RETURN, cs->process,
+ cs->func);
+ DbugFlush(cs);
+ }
+ else
+ {
+#ifndef THREAD
+ if (DoProfile(cs))
+ (void) fprintf(cs->stack->prof_file, PROF_XFMT, Clock(), cs->func);
+#endif
+ if (DoTrace(cs) & DO_TRACE)
+ {
+ if ((cs->stack->flags & SANITY_CHECK_ON) &&
+ _sanity(_stack_frame_->file,_line_))
+ cs->stack->flags &= ~SANITY_CHECK_ON;
+ if (TRACING)
+ {
+ if (!cs->locked)
+ pthread_mutex_lock(&THR_LOCK_dbug);
+ DoPrefix(cs, _line_);
+ Indent(cs, cs->level);
+ (void) fprintf(cs->stack->out_file, "<%s\n", cs->func);
+ DbugFlush(cs);
+ }
+ }
+ }
+ /*
+ 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;
+ errno=save_errno;
+}
+
+
+/*
+ * FUNCTION
+ *
+ * _db_pargs_ log arguments for subsequent use by _db_doprnt_()
+ *
+ * SYNOPSIS
+ *
+ * VOID _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_().
+ *
+ */
+
+void _db_pargs_(uint _line_, const char *keyword)
+{
+ CODE_STATE *cs;
+ get_code_state_or_return;
+ cs->u_line= _line_;
+ cs->u_keyword= keyword;
+}
+
+
+/*
+ * 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;
+ get_code_state_or_return;
+
+ va_start(args,format);
+
+ if (_db_keyword_(cs, cs->u_keyword, 0))
+ {
+ int save_errno=errno;
+ if (!cs->locked)
+ pthread_mutex_lock(&THR_LOCK_dbug);
+ DoPrefix(cs, cs->u_line);
+ if (TRACING)
+ Indent(cs, cs->level + 1);
+ else
+ (void) fprintf(cs->stack->out_file, "%s: ", cs->func);
+ (void) fprintf(cs->stack->out_file, "%s: ", cs->u_keyword);
+ DbugFprintf(cs->stack->out_file, format, args);
+ DbugFlush(cs);
+ errno=save_errno;
+ }
+ va_end(args);
+}
+
+/*
+ * fprintf clone with consistent, platform independent output for
+ * problematic formats like %p, %zd and %lld.
+ */
+static void DbugFprintf(FILE *stream, const char* format, va_list args)
+{
+ char cvtbuf[1024];
+ size_t len;
+ len = 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 corrputed memory or arrays.
+ */
+
+void _db_dump_(uint _line_, const char *keyword,
+ const unsigned char *memory, size_t length)
+{
+ int pos;
+ char dbuff[90];
+ CODE_STATE *cs;
+ get_code_state_or_return;
+
+ if (_db_keyword_(cs, keyword, 0))
+ {
+ if (!cs->locked)
+ pthread_mutex_lock(&THR_LOCK_dbug);
+ DoPrefix(cs, _line_);
+ if (TRACING)
+ {
+ Indent(cs, cs->level + 1);
+ pos= min(max(cs->level-cs->stack->sub_level,0)*INDENT,80);
+ }
+ else
+ {
+ fprintf(cs->stack->out_file, "%s: ", cs->func);
+ }
+ sprintf(dbuff,"%s: Memory: 0x%lx Bytes: (%ld)\n",
+ keyword, (ulong) memory, (long) length);
+ (void) fputs(dbuff,cs->stack->out_file);
+
+ pos=0;
+ while (length-- > 0)
+ {
+ uint tmp= *((unsigned char*) memory++);
+ if ((pos+=3) >= 80)
+ {
+ fputc('\n',cs->stack->out_file);
+ pos=3;
+ }
+ fputc(_dig_vec_upper[((tmp >> 4) & 15)], cs->stack->out_file);
+ fputc(_dig_vec_upper[tmp & 15], cs->stack->out_file);
+ fputc(' ',cs->stack->out_file);
+ }
+ (void) fputc('\n',cs->stack->out_file);
+ 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=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 result;
+
+ for (result=MATCHED; linkp != NULL; linkp= linkp->next_link)
+ {
+ if (!fnmatch(linkp->str, cp, 0))
+ return linkp->flags;
+ 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, struct settings *state, int free_state)
+{
+ if (!is_shared(state, keywords))
+ FreeList(state->keywords);
+ if (!is_shared(state, functions))
+ FreeList(state->functions);
+ if (!is_shared(state, processes))
+ FreeList(state->processes);
+ if (!is_shared(state, p_functions))
+ FreeList(state->p_functions);
+
+ if (!is_shared(state, out_file))
+ DBUGCloseFile(cs, state->out_file);
+ else
+ (void) fflush(state->out_file);
+
+ if (!is_shared(state, prof_file))
+ DBUGCloseFile(cs, state->prof_file);
+ else
+ (void) fflush(state->prof_file);
+
+ if (free_state)
+ free((void*) state);
+}
+
+
+/*
+ * 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_()
+{
+ struct settings *discard;
+ static struct settings tmp;
+ CODE_STATE *cs;
+ /*
+ Set _dbug_on_ to be able to do full reset even when DEBUGGER_OFF was
+ called after dbug was initialized
+ */
+ _dbug_on_= 1;
+ get_code_state_or_return;
+
+ while ((discard= cs->stack))
+ {
+ if (discard == &init_settings)
+ break;
+ cs->stack= discard->next;
+ FreeState(cs, discard, 1);
+ }
+ tmp= init_settings;
+
+ /* Use mutex lock to make it less likely anyone access out_file */
+ pthread_mutex_lock(&THR_LOCK_dbug);
+ init_settings.flags= OPEN_APPEND;
+ init_settings.out_file= stderr;
+ init_settings.prof_file= stderr;
+ init_settings.maxdepth= 0;
+ init_settings.delay= 0;
+ init_settings.sub_level= 0;
+ init_settings.functions= 0;
+ init_settings.p_functions= 0;
+ init_settings.keywords= 0;
+ init_settings.processes= 0;
+ pthread_mutex_unlock(&THR_LOCK_dbug);
+ FreeState(cs, &tmp, 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)
+{
+ if ((cs->stack->maxdepth == 0 || cs->level <= cs->stack->maxdepth) &&
+ InList(cs->stack->processes, cs->process) & (MATCHED|INCLUDE))
+ switch(InList(cs->stack->functions, cs->func)) {
+ case INCLUDE|SUBDIR: return ENABLE_TRACE;
+ case INCLUDE: return DO_TRACE;
+ case MATCHED|SUBDIR:
+ case NOT_MATCHED|SUBDIR:
+ case MATCHED: return framep_trace_flag(cs, cs->framep) ?
+ DO_TRACE : DONT_TRACE;
+ case EXCLUDE:
+ case NOT_MATCHED: return DONT_TRACE;
+ case EXCLUDE|SUBDIR: return DISABLE_TRACE;
+ }
+ return DONT_TRACE;
+}
+
+
+/*
+ * FUNCTION
+ *
+ * DoProfile check to see if profiling is current enabled
+ *
+ * SYNOPSIS
+ *
+ * static BOOLEAN DoProfile()
+ *
+ * DESCRIPTION
+ *
+ * Checks to see if profiling is enabled based on whether the
+ * user has specified profiling, the maximum trace depth has
+ * not yet been reached, the current function is selected,
+ * and the current process is selected. Returns TRUE if
+ * profiling is enabled, FALSE otherwise.
+ *
+ */
+
+#ifndef THREAD
+static BOOLEAN DoProfile(CODE_STATE *cs)
+{
+ return PROFILING &&
+ cs->level <= cs->stack->maxdepth &&
+ InList(cs->stack->p_functions, cs->func) & (INCLUDE|MATCHED) &&
+ InList(cs->stack->processes, cs->process) & (INCLUDE|MATCHED);
+}
+#endif
+
+FILE *_db_fp_(void)
+{
+ CODE_STATE *cs;
+ get_code_state_or_return NULL;
+ return cs->stack->out_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)
+{
+ get_code_state_if_not_set_or_return FALSE;
+ strict=strict ? INCLUDE : INCLUDE|MATCHED;
+
+ return DEBUGGING && DoTrace(cs) & DO_TRACE &&
+ InList(cs->stack->keywords, keyword) & strict;
+}
+
+/*
+ * 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)
+{
+ REGISTER int count;
+
+ indent= max(indent-1-cs->stack->sub_level,0)*INDENT;
+ for (count= 0; count < indent ; count++)
+ {
+ if ((count % INDENT) == 0)
+ fputc('|',cs->stack->out_file);
+ else
+ fputc(' ',cs->stack->out_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)
+{
+ REGISTER 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)
+ {
+#ifdef THREAD
+ (void) fprintf(cs->stack->out_file, "%-7s: ", my_thread_name());
+#else
+ (void) fprintf(cs->stack->out_file, "%5d: ", (int) getpid());
+#endif
+ }
+ if (cs->stack->flags & NUMBER_ON)
+ (void) fprintf(cs->stack->out_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,
+ /* "%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,
+ /* "%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, "%s: ", cs->process);
+ if (cs->stack->flags & FILE_ON)
+ (void) fprintf(cs->stack->out_file, "%14s: ", BaseName(cs->file));
+ if (cs->stack->flags & LINE_ON)
+ (void) fprintf(cs->stack->out_file, "%5d: ", _line_);
+ if (cs->stack->flags & DEPTH_ON)
+ (void) fprintf(cs->stack->out_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)
+{
+ REGISTER FILE *fp;
+ REGISTER BOOLEAN newfile;
+
+ 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)
+ {
+ cs->stack->out_file= stdout;
+ 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
+ {
+ newfile= !EXISTS(name);
+ if (!(fp= fopen(name, append ? "a+" : "w")))
+ {
+ (void) fprintf(stderr, ERR_OPEN, cs->process, name);
+ perror("");
+ fflush(stderr);
+ }
+ else
+ {
+ cs->stack->out_file= fp;
+ if (newfile)
+ {
+ ChangeOwner(cs, name);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * FUNCTION
+ *
+ * OpenProfile open new output stream for profiler output
+ *
+ * SYNOPSIS
+ *
+ * static FILE *OpenProfile(name)
+ * char *name;
+ *
+ * DESCRIPTION
+ *
+ * Given name of a new file, opens the file
+ * and sets the profiler output stream to the new file.
+ *
+ * It is currently unclear whether the prefered behavior is
+ * to truncate any existing file, or simply append to it.
+ * The latter behavior would be desirable for collecting
+ * accumulated runtime history over a number of separate
+ * runs. It might take some changes to the analyzer program
+ * though, and the notes that Binayak sent with the profiling
+ * diffs indicated that append was the normal mode, but this
+ * does not appear to agree with the actual code. I haven't
+ * investigated at this time [fnf; 24-Jul-87].
+ */
+
+#ifndef THREAD
+static FILE *OpenProfile(CODE_STATE *cs, const char *name)
+{
+ REGISTER FILE *fp;
+ REGISTER BOOLEAN newfile;
+
+ fp=0;
+ if (!Writable(name))
+ {
+ (void) fprintf(cs->stack->out_file, ERR_OPEN, cs->process, name);
+ perror("");
+ (void) Delay(cs->stack->delay);
+ }
+ else
+ {
+ newfile= !EXISTS(name);
+ if (!(fp= fopen(name, "w")))
+ {
+ (void) fprintf(cs->stack->out_file, ERR_OPEN, cs->process, name);
+ perror("");
+ }
+ else
+ {
+ cs->stack->prof_file= fp;
+ if (newfile)
+ {
+ ChangeOwner(cs, name);
+ }
+ }
+ }
+ return fp;
+}
+#endif
+
+/*
+ * 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, FILE *fp)
+{
+ if (fp && fp != stderr && fp != stdout && fclose(fp) == EOF)
+ {
+ pthread_mutex_lock(&THR_LOCK_dbug);
+ (void) fprintf(cs->stack->out_file, ERR_CLOSE, cs->process);
+ perror("");
+ DbugFlush(cs);
+ }
+}
+
+
+/*
+ * 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);
+ exit(1);
+}
+
+
+/*
+ * 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)
+{
+ register 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)
+{
+ register 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)
+{
+ REGISTER BOOLEAN granted;
+ REGISTER 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
+
+
+/*
+ * FUNCTION
+ *
+ * ChangeOwner change owner to real user for suid programs
+ *
+ * SYNOPSIS
+ *
+ * static VOID ChangeOwner(pathname)
+ *
+ * DESCRIPTION
+ *
+ * For unix systems, change the owner of the newly created debug
+ * file to the real owner. This is strictly for the benefit of
+ * programs that are running with the set-user-id bit set.
+ *
+ * Note that at this point, the fact that pathname represents
+ * a newly created file has already been established. If the
+ * program that the debugger is linked to is not running with
+ * the suid bit set, then this operation is redundant (but
+ * harmless).
+ *
+ */
+
+#ifndef ChangeOwner
+static void ChangeOwner(CODE_STATE *cs, char *pathname)
+{
+ if (chown(pathname, getuid(), getgid()) == -1)
+ {
+ (void) fprintf(stderr, ERR_CHOWN, cs->process, pathname);
+ perror("");
+ (void) fflush(stderr);
+ }
+}
+#endif
+
+
+/*
+ * FUNCTION
+ *
+ * _db_setjmp_ save debugger environment
+ *
+ * SYNOPSIS
+ *
+ * VOID _db_setjmp_()
+ *
+ * DESCRIPTION
+ *
+ * Invoked as part of the user's DBUG_SETJMP macro to save
+ * the debugger environment in parallel with saving the user's
+ * environment.
+ *
+ */
+
+#ifdef HAVE_LONGJMP
+
+EXPORT void _db_setjmp_()
+{
+ CODE_STATE *cs;
+ get_code_state_or_return;
+
+ cs->jmplevel= cs->level;
+ cs->jmpfunc= cs->func;
+ cs->jmpfile= cs->file;
+}
+
+/*
+ * FUNCTION
+ *
+ * _db_longjmp_ restore previously saved debugger environment
+ *
+ * SYNOPSIS
+ *
+ * VOID _db_longjmp_()
+ *
+ * DESCRIPTION
+ *
+ * Invoked as part of the user's DBUG_LONGJMP macro to restore
+ * the debugger environment in parallel with restoring the user's
+ * previously saved environment.
+ *
+ */
+
+EXPORT void _db_longjmp_()
+{
+ CODE_STATE *cs;
+ get_code_state_or_return;
+
+ cs->level= cs->jmplevel;
+ if (cs->jmpfunc)
+ cs->func= cs->jmpfunc;
+ if (cs->jmpfile)
+ cs->file= cs->jmpfile;
+}
+#endif
+
+/*
+ * FUNCTION
+ *
+ * perror perror simulation for systems that don't have it
+ *
+ * SYNOPSIS
+ *
+ * static VOID perror(s)
+ * char *s;
+ *
+ * DESCRIPTION
+ *
+ * Perror produces a message on the standard error stream which
+ * provides more information about the library or system error
+ * just encountered. The argument string s is printed, followed
+ * by a ':', a blank, and then a message and a newline.
+ *
+ * An undocumented feature of the unix perror is that if the string
+ * 's' is a null string (NOT a NULL pointer!), then the ':' and
+ * blank are not printed.
+ *
+ * This version just complains about an "unknown system error".
+ *
+ */
+
+#ifndef HAVE_PERROR
+static void perror(s)
+char *s;
+{
+ if (s && *s != '\0')
+ (void) fprintf(stderr, "%s: ", s);
+ (void) fprintf(stderr, "<unknown system error>\n");
+}
+#endif /* HAVE_PERROR */
+
+
+ /* flush dbug-stream, free mutex lock & wait delay */
+ /* This is because some systems (MSDOS!!) dosn'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);
+ if (cs->stack->delay)
+ (void) Delay(cs->stack->delay);
+ }
+ if (!cs->locked)
+ pthread_mutex_unlock(&THR_LOCK_dbug);
+} /* DbugFlush */
+
+
+/* For debugging */
+
+void _db_flush_()
+{
+ CODE_STATE *cs;
+ get_code_state_or_return;
+ (void) fflush(cs->stack->out_file);
+}
+
+
+void _db_lock_file_()
+{
+ CODE_STATE *cs;
+ get_code_state_or_return;
+ pthread_mutex_lock(&THR_LOCK_dbug);
+ cs->locked=1;
+}
+
+void _db_unlock_file_()
+{
+ CODE_STATE *cs;
+ get_code_state_or_return;
+ cs->locked=0;
+ pthread_mutex_unlock(&THR_LOCK_dbug);
+}
+
+/*
+ * Here we need the definitions of the clock routine. Add your
+ * own for whatever system that you have.
+ */
+
+#ifndef THREAD
+#if defined(HAVE_GETRUSAGE)
+
+#include <sys/param.h>
+#include <sys/resource.h>
+
+/* extern int getrusage(int, struct rusage *); */
+
+/*
+ * Returns the user time in milliseconds used by this process so
+ * far.
+ */
+
+static unsigned long Clock()
+{
+ struct rusage ru;
+
+ (void) getrusage(RUSAGE_SELF, &ru);
+ return ru.ru_utime.tv_sec*1000 + ru.ru_utime.tv_usec/1000;
+}
+
+#elif defined(MSDOS) || defined(__WIN__)
+
+static ulong Clock()
+{
+ return clock()*(1000/CLOCKS_PER_SEC);
+}
+#elif defined(amiga)
+
+struct DateStamp { /* Yes, this is a hack, but doing it right */
+ long ds_Days; /* is incredibly ugly without splitting this */
+ long ds_Minute; /* off into a separate file */
+ long ds_Tick;
+};
+
+static int first_clock= TRUE;
+static struct DateStamp begin;
+static struct DateStamp elapsed;
+
+static unsigned long Clock()
+{
+ register struct DateStamp *now;
+ register unsigned long millisec= 0;
+ extern VOID *AllocMem();
+
+ now= (struct DateStamp *) AllocMem((long) sizeof(struct DateStamp), 0L);
+ if (now != NULL)
+ {
+ if (first_clock == TRUE)
+ {
+ first_clock= FALSE;
+ (void) DateStamp(now);
+ begin= *now;
+ }
+ (void) DateStamp(now);
+ millisec= 24 * 3600 * (1000 / HZ) * (now->ds_Days - begin.ds_Days);
+ millisec += 60 * (1000 / HZ) * (now->ds_Minute - begin.ds_Minute);
+ millisec += (1000 / HZ) * (now->ds_Tick - begin.ds_Tick);
+ (void) FreeMem(now, (long) sizeof(struct DateStamp));
+ }
+ return millisec;
+}
+#else
+static unsigned long Clock()
+{
+ return 0;
+}
+#endif /* RUSAGE */
+#endif /* THREADS */
+
+#ifdef NO_VARARGS
+
+/*
+ * Fake vfprintf for systems that don't support it. If this
+ * doesn't work, you are probably SOL...
+ */
+
+static int vfprintf(stream, format, ap)
+FILE *stream;
+char *format;
+va_list ap;
+{
+ int rtnval;
+ ARGS_DCL;
+
+ ARG0= va_arg(ap, ARGS_TYPE);
+ ARG1= va_arg(ap, ARGS_TYPE);
+ ARG2= va_arg(ap, ARGS_TYPE);
+ ARG3= va_arg(ap, ARGS_TYPE);
+ ARG4= va_arg(ap, ARGS_TYPE);
+ ARG5= va_arg(ap, ARGS_TYPE);
+ ARG6= va_arg(ap, ARGS_TYPE);
+ ARG7= va_arg(ap, ARGS_TYPE);
+ ARG8= va_arg(ap, ARGS_TYPE);
+ ARG9= va_arg(ap, ARGS_TYPE);
+ rtnval= fprintf(stream, format, ARGS_LIST);
+ return rtnval;
+}
+
+#endif /* NO_VARARGS */
+
+#else
+
+/*
+ * Dummy function, workaround for MySQL bug#14420 related
+ * build failure on a platform where linking with an empty
+ * archive fails.
+ *
+ * This block can be removed as soon as a fix for bug#14420
+ * is implemented.
+ */
+int i_am_a_dummy_function() {
+ return 0;
+}
+
+#endif
diff --git a/externals/mysql/extlib/dbug/dbug_analyze.c b/externals/mysql/extlib/dbug/dbug_analyze.c
new file mode 100644
index 00000000000..3263b2ccc59
--- /dev/null
+++ b/externals/mysql/extlib/dbug/dbug_analyze.c
@@ -0,0 +1,726 @@
+/*
+ * Analyze the profile file (cmon.out) written out by the dbug
+ * routines with profiling enabled.
+ *
+ * Copyright June 1987, Binayak Banerjee
+ * All rights reserved.
+ *
+ * This program may be freely distributed under the same terms and
+ * conditions as Fred Fish's Dbug package.
+ *
+ * Compile with -- cc -O -s -o %s analyze.c
+ *
+ * Analyze will read an trace file created by the dbug package
+ * (when run with traceing enabled). It will then produce a
+ * summary on standard output listing the name of each traced
+ * function, the number of times it was called, the percentage
+ * of total calls, the time spent executing the function, the
+ * proportion of the total time and the 'importance'. The last
+ * is a metric which is obtained by multiplying the proportions
+ * of calls and the proportions of time for each function. The
+ * greater the importance, the more likely it is that a speedup
+ * could be obtained by reducing the time taken by that function.
+ *
+ * Note that the timing values that you obtain are only rough
+ * measures. The overhead of the dbug package is included
+ * within. However, there is no need to link in special profiled
+ * libraries and the like.
+ *
+ * CHANGES:
+ *
+ * 2-Mar-89: fnf
+ * Changes to support tracking of stack usage. This required
+ * reordering the fields in the profile log file to make
+ * parsing of different record types easier. Corresponding
+ * changes made in dbug runtime library. Also used this
+ * opportunity to reformat the code more to my liking (my
+ * apologies to Binayak Banerjee for "uglifying" his code).
+ *
+ * 24-Jul-87: fnf
+ * Because I tend to use functions names like
+ * "ExternalFunctionDoingSomething", I've rearranged the
+ * printout to put the function name last in each line, so
+ * long names don't screw up the formatting unless they are
+ * *very* long and wrap around the screen width...
+ *
+ * 24-Jul-87: fnf
+ * Modified to put out table very similar to Unix profiler
+ * by default, but also puts out original verbose table
+ * if invoked with -v flag.
+ */
+
+#include <my_global.h>
+#include <m_string.h>
+#include <my_pthread.h>
+
+static char *my_name;
+static int verbose;
+
+/*
+ * Structure of the stack.
+ */
+
+#define PRO_FILE "dbugmon.out" /* Default output file name */
+#define STACKSIZ 100 /* Maximum function nesting */
+#define MAXPROCS 10000 /* Maximum number of function calls */
+
+# ifdef BSD
+# include <sysexits.h>
+# else
+# define EX_SOFTWARE 1
+# define EX_DATAERR 1
+# define EX_USAGE 1
+# define EX_OSERR 1
+# define EX_IOERR 1
+#ifndef EX_OK
+# define EX_OK 0
+#endif
+# endif
+
+#define __MERF_OO_ "%s: Malloc Failed in %s: %d\n"
+
+#define MALLOC(Ptr,Num,Typ) do /* Malloc w/error checking & exit */ \
+ if (!(Ptr = (Typ *)malloc((Num)*(sizeof(Typ))))) \
+ {fprintf(stderr,__MERF_OO_,my_name,__FILE__,__LINE__);\
+ exit(EX_OSERR);} while(0)
+
+#define Malloc(Ptr,Num,Typ) do /* Weaker version of above */\
+ if (!(Ptr = (Typ *)malloc((Num)*(sizeof(Typ))))) \
+ fprintf(stderr,__MERF_OO_,my_name,__FILE__,__LINE__);\
+ while(0)
+
+#define FILEOPEN(Fp,Fn,Mod) do /* File open with error exit */ \
+ if (!(Fp = fopen(Fn,Mod)))\
+ {fprintf(stderr,"%s: Couldn't open %s\n",my_name,Fn);\
+ exit(EX_IOERR);} while(0)
+
+#define Fileopen(Fp,Fn,Mod) do /* Weaker version of above */ \
+ if(!(Fp = fopen(Fn,Mod))) \
+ fprintf(stderr,"%s: Couldn't open %s\n",my_name,Fn);\
+ while(0)
+
+
+struct stack_t {
+ unsigned int pos; /* which function? */
+ unsigned long time; /* Time that this was entered */
+ unsigned long children; /* Time spent in called funcs */
+};
+
+static struct stack_t fn_stack[STACKSIZ+1];
+
+static unsigned int stacktop = 0; /* Lowest stack position is a dummy */
+
+static unsigned long tot_time = 0;
+static unsigned long tot_calls = 0;
+static unsigned long highstack = 0;
+static unsigned long lowstack = (ulong) ~0;
+
+/*
+ * top() returns a pointer to the top item on the stack.
+ * (was a function, now a macro)
+ */
+
+#define top() &fn_stack[stacktop]
+
+/*
+ * Push - Push the given record on the stack.
+ */
+
+void push (name_pos, time_entered)
+register unsigned int name_pos;
+register unsigned long time_entered;
+{
+ register struct stack_t *t;
+
+ DBUG_ENTER("push");
+ if (++stacktop > STACKSIZ) {
+ fprintf (DBUG_FILE,"%s: stack overflow (%s:%d)\n",
+ my_name, __FILE__, __LINE__);
+ exit (EX_SOFTWARE);
+ }
+ DBUG_PRINT ("push", ("%d %ld",name_pos,time_entered));
+ t = &fn_stack[stacktop];
+ t -> pos = name_pos;
+ t -> time = time_entered;
+ t -> children = 0;
+ DBUG_VOID_RETURN;
+}
+
+/*
+ * Pop - pop the top item off the stack, assigning the field values
+ * to the arguments. Returns 0 on stack underflow, or on popping first
+ * item off stack.
+ */
+
+unsigned int pop (name_pos, time_entered, child_time)
+register unsigned int *name_pos;
+register unsigned long *time_entered;
+register unsigned long *child_time;
+{
+ register struct stack_t *temp;
+ register unsigned int rtnval;
+
+ DBUG_ENTER ("pop");
+
+ if (stacktop < 1) {
+ rtnval = 0;
+ } else {
+ temp = &fn_stack[stacktop];
+ *name_pos = temp->pos;
+ *time_entered = temp->time;
+ *child_time = temp->children;
+ DBUG_PRINT ("pop", ("%d %lu %lu",*name_pos,*time_entered,*child_time));
+ rtnval = stacktop--;
+ }
+ DBUG_RETURN (rtnval);
+}
+
+/*
+ * We keep the function info in another array (serves as a simple
+ * symbol table)
+ */
+
+struct module_t {
+ char *name;
+ unsigned long m_time;
+ unsigned long m_calls;
+ unsigned long m_stkuse;
+};
+
+static struct module_t modules[MAXPROCS];
+
+/*
+ * We keep a binary search tree in order to look up function names
+ * quickly (and sort them at the end.
+ */
+
+struct bnode {
+ unsigned int lchild; /* Index of left subtree */
+ unsigned int rchild; /* Index of right subtree */
+ unsigned int pos; /* Index of module_name entry */
+};
+
+static struct bnode s_table[MAXPROCS];
+
+static unsigned int n_items = 0; /* No. of items in the array so far */
+
+/*
+ * Need a function to allocate space for a string and squirrel it away.
+ */
+
+char *strsave (s)
+char *s;
+{
+ register char *retval;
+ register unsigned int len;
+
+ DBUG_ENTER ("strsave");
+ DBUG_PRINT ("strsave", ("%s",s));
+ if (!s || (len = strlen (s)) == 0) {
+ DBUG_RETURN (0);
+ }
+ MALLOC (retval, ++len, char);
+ strcpy (retval, s);
+ DBUG_RETURN (retval);
+}
+
+/*
+ * add() - adds m_name to the table (if not already there), and returns
+ * the index of its location in the table. Checks s_table (which is a
+ * binary search tree) to see whether or not it should be added.
+ */
+
+unsigned int add (m_name)
+char *m_name;
+{
+ register unsigned int ind = 0;
+ register int cmp;
+
+ DBUG_ENTER ("add");
+ if (n_items == 0) { /* First item to be added */
+ s_table[0].pos = ind;
+ s_table[0].lchild = s_table[0].rchild = MAXPROCS;
+ addit:
+ modules[n_items].name = strsave (m_name);
+ modules[n_items].m_time = 0;
+ modules[n_items].m_calls = 0;
+ modules[n_items].m_stkuse = 0;
+ DBUG_RETURN (n_items++);
+ }
+ while ((cmp = strcmp (m_name,modules[ind].name))) {
+ if (cmp < 0) { /* In left subtree */
+ if (s_table[ind].lchild == MAXPROCS) {
+ /* Add as left child */
+ if (n_items >= MAXPROCS) {
+ fprintf (DBUG_FILE,
+ "%s: Too many functions being profiled\n",
+ my_name);
+ exit (EX_SOFTWARE);
+ }
+ s_table[n_items].pos = s_table[ind].lchild = n_items;
+ s_table[n_items].lchild = s_table[n_items].rchild = MAXPROCS;
+#ifdef notdef
+ modules[n_items].name = strsave (m_name);
+ modules[n_items].m_time = modules[n_items].m_calls = 0;
+ DBUG_RETURN (n_items++);
+#else
+ goto addit;
+#endif
+
+ }
+ ind = s_table[ind].lchild; /* else traverse l-tree */
+ } else {
+ if (s_table[ind].rchild == MAXPROCS) {
+ /* Add as right child */
+ if (n_items >= MAXPROCS) {
+ fprintf (DBUG_FILE,
+ "%s: Too many functions being profiled\n",
+ my_name);
+ exit (EX_SOFTWARE);
+ }
+ s_table[n_items].pos = s_table[ind].rchild = n_items;
+ s_table[n_items].lchild = s_table[n_items].rchild = MAXPROCS;
+#ifdef notdef
+ modules[n_items].name = strsave (m_name);
+ modules[n_items].m_time = modules[n_items].m_calls = 0;
+ DBUG_RETURN (n_items++);
+#else
+ goto addit;
+#endif
+
+ }
+ ind = s_table[ind].rchild; /* else traverse r-tree */
+ }
+ }
+ DBUG_RETURN (ind);
+}
+
+/*
+ * process() - process the input file, filling in the modules table.
+ */
+
+void process (inf)
+FILE *inf;
+{
+ char buf[BUFSIZ];
+ char fn_name[64]; /* Max length of fn_name */
+ unsigned long fn_time;
+ unsigned long fn_sbot;
+ unsigned long fn_ssz;
+ unsigned long lastuse;
+ unsigned int pos;
+ unsigned long local_time;
+ unsigned int oldpos;
+ unsigned long oldtime;
+ unsigned long oldchild;
+ struct stack_t *t;
+
+ DBUG_ENTER ("process");
+ while (fgets (buf,BUFSIZ,inf) != NULL) {
+ switch (buf[0]) {
+ case 'E':
+ sscanf (buf+2, "%ld %64s", &fn_time, fn_name);
+ DBUG_PRINT ("erec", ("%ld %s", fn_time, fn_name));
+ pos = add (fn_name);
+ push (pos, fn_time);
+ break;
+ case 'X':
+ sscanf (buf+2, "%ld %64s", &fn_time, fn_name);
+ DBUG_PRINT ("xrec", ("%ld %s", fn_time, fn_name));
+ pos = add (fn_name);
+ /*
+ * An exited function implies that all stacked
+ * functions are also exited, until the matching
+ * function is found on the stack.
+ */
+ while (pop (&oldpos, &oldtime, &oldchild)) {
+ DBUG_PRINT ("popped", ("%lu %lu", oldtime, oldchild));
+ local_time = fn_time - oldtime;
+ t = top ();
+ t -> children += local_time;
+ DBUG_PRINT ("update", ("%s", modules[t -> pos].name));
+ DBUG_PRINT ("update", ("%lu", t -> children));
+ local_time -= oldchild;
+ modules[oldpos].m_time += local_time;
+ modules[oldpos].m_calls++;
+ tot_time += local_time;
+ tot_calls++;
+ if (pos == oldpos) {
+ goto next_line; /* Should be a break2 */
+ }
+ }
+ /*
+ * Assume that item seen started at time 0.
+ * (True for function main). But initialize
+ * it so that it works the next time too.
+ */
+ t = top ();
+ local_time = fn_time - t -> time - t -> children;
+ t -> time = fn_time; t -> children = 0;
+ modules[pos].m_time += local_time;
+ modules[pos].m_calls++;
+ tot_time += local_time;
+ tot_calls++;
+ break;
+ case 'S':
+ sscanf (buf+2, "%lx %lx %64s", &fn_sbot, &fn_ssz, fn_name);
+ DBUG_PRINT ("srec", ("%lx %lx %s", fn_sbot, fn_ssz, fn_name));
+ pos = add (fn_name);
+ lastuse = modules[pos].m_stkuse;
+#if 0
+ /*
+ * Needs further thought. Stack use is determined by
+ * difference in stack between two functions with DBUG_ENTER
+ * macros. If A calls B calls C, where A and C have the
+ * macros, and B doesn't, then B's stack use will be lumped
+ * in with either A's or C's. If somewhere else A calls
+ * C directly, the stack use will seem to change. Just
+ * take the biggest for now...
+ */
+ if (lastuse > 0 && lastuse != fn_ssz) {
+ fprintf (stderr,
+ "warning - %s stack use changed (%lx to %lx)\n",
+ fn_name, lastuse, fn_ssz);
+ }
+#endif
+ if (fn_ssz > lastuse) {
+ modules[pos].m_stkuse = fn_ssz;
+ }
+ if (fn_sbot > highstack) {
+ highstack = fn_sbot;
+ } else if (fn_sbot < lowstack) {
+ lowstack = fn_sbot;
+ }
+ break;
+ default:
+ fprintf (stderr, "unknown record type '%c'\n", buf[0]);
+ break;
+ }
+ next_line:;
+ }
+
+ /*
+ * Now, we've hit eof. If we still have stuff stacked, then we
+ * assume that the user called exit, so give everything the exited
+ * time of fn_time.
+ */
+ while (pop (&oldpos,&oldtime,&oldchild)) {
+ local_time = fn_time - oldtime;
+ t = top ();
+ t -> children += local_time;
+ local_time -= oldchild;
+ modules[oldpos].m_time += local_time;
+ modules[oldpos].m_calls++;
+ tot_time += local_time;
+ tot_calls++;
+ }
+ DBUG_VOID_RETURN;
+}
+
+/*
+ * out_header () -- print out the header of the report.
+ */
+
+void out_header (outf)
+FILE *outf;
+{
+ DBUG_ENTER ("out_header");
+ if (verbose) {
+ fprintf (outf, "Profile of Execution\n");
+ fprintf (outf, "Execution times are in milliseconds\n\n");
+ fprintf (outf, " Calls\t\t\t Time\n");
+ fprintf (outf, " -----\t\t\t ----\n");
+ fprintf (outf, "Times\tPercentage\tTime Spent\tPercentage\n");
+ fprintf (outf, "Called\tof total\tin Function\tof total Importance\tFunction\n");
+ fprintf (outf, "======\t==========\t===========\t========== ==========\t========\t\n");
+ } else {
+ fprintf (outf, "%ld bytes of stack used, from %lx down to %lx\n\n",
+ highstack - lowstack, highstack, lowstack);
+ fprintf (outf,
+ " %%time sec #call ms/call %%calls weight stack name\n");
+ }
+ DBUG_VOID_RETURN;
+}
+
+/*
+ * out_trailer () - writes out the summary line of the report.
+ */
+
+void out_trailer (outf,sum_calls,sum_time)
+FILE *outf;
+unsigned long int sum_calls, sum_time;
+{
+ DBUG_ENTER ("out_trailer");
+ if (verbose)
+ {
+ fprintf(outf, "======\t==========\t===========\t==========\t========\n");
+ fprintf(outf, "%6ld\t%10.2f\t%11ld\t%10.2f\t\t%-15s\n",
+ sum_calls, 100.0, sum_time, 100.0, "Totals");
+ }
+ DBUG_VOID_RETURN;
+}
+
+/*
+ * out_item () - prints out the output line for a single entry,
+ * and sets the calls and time fields appropriately.
+ */
+
+void out_item (outf, m,called,timed)
+FILE *outf;
+register struct module_t *m;
+unsigned long int *called, *timed;
+{
+ char *name = m -> name;
+ register unsigned int calls = m -> m_calls;
+ register unsigned long local_time = m -> m_time;
+ register unsigned long stkuse = m -> m_stkuse;
+ unsigned int import;
+ double per_time = 0.0;
+ double per_calls = 0.0;
+ double ms_per_call, local_ftime;
+
+ DBUG_ENTER ("out_item");
+
+ if (tot_time > 0) {
+ per_time = (double) (local_time * 100) / (double) tot_time;
+ }
+ if (tot_calls > 0) {
+ per_calls = (double) (calls * 100) / (double) tot_calls;
+ }
+ import = (unsigned int) (per_time * per_calls);
+
+ if (verbose) {
+ fprintf (outf, "%6d\t%10.2f\t%11ld\t%10.2f %10d\t%-15s\n",
+ calls, per_calls, local_time, per_time, import, name);
+ } else {
+ ms_per_call = local_time;
+ ms_per_call /= calls;
+ local_ftime = local_time;
+ local_ftime /= 1000;
+ fprintf(outf, "%8.2f%8.3f%8u%8.3f%8.2f%8u%8lu %-s\n",
+ per_time, local_ftime, calls, ms_per_call, per_calls, import,
+ stkuse, name);
+ }
+ *called = calls;
+ *timed = local_time;
+ DBUG_VOID_RETURN;
+}
+
+/*
+ * out_body (outf, root,s_calls,s_time) -- Performs an inorder traversal
+ * on the binary search tree (root). Calls out_item to actually print
+ * the item out.
+ */
+
+void out_body (outf, root,s_calls,s_time)
+FILE *outf;
+register unsigned int root;
+register unsigned long int *s_calls, *s_time;
+{
+ unsigned long int calls, local_time;
+
+ DBUG_ENTER ("out_body");
+ DBUG_PRINT ("out_body", ("%lu,%lu",*s_calls,*s_time));
+ if (root == MAXPROCS) {
+ DBUG_PRINT ("out_body", ("%lu,%lu",*s_calls,*s_time));
+ } else {
+ while (root != MAXPROCS) {
+ out_body (outf, s_table[root].lchild,s_calls,s_time);
+ out_item (outf, &modules[s_table[root].pos],&calls,&local_time);
+ DBUG_PRINT ("out_body", ("-- %lu -- %lu --", calls, local_time));
+ *s_calls += calls;
+ *s_time += local_time;
+ root = s_table[root].rchild;
+ }
+ DBUG_PRINT ("out_body", ("%lu,%lu", *s_calls, *s_time));
+ }
+ DBUG_VOID_RETURN;
+}
+
+/*
+ * output () - print out a nice sorted output report on outf.
+ */
+
+void output (outf)
+FILE *outf;
+{
+ unsigned long int sum_calls = 0;
+ unsigned long int sum_time = 0;
+
+ DBUG_ENTER ("output");
+ if (n_items == 0) {
+ fprintf (outf, "%s: No functions to trace\n", my_name);
+ exit (EX_DATAERR);
+ }
+ out_header (outf);
+ out_body (outf, 0,&sum_calls,&sum_time);
+ out_trailer (outf, sum_calls,sum_time);
+ DBUG_VOID_RETURN;
+}
+
+
+#define usage() fprintf (DBUG_FILE,"Usage: %s [-v] [prof-file]\n",my_name)
+
+#ifdef MSDOS
+extern int getopt(int argc, char **argv, char *opts);
+#endif
+extern int optind;
+extern char *optarg;
+
+int main (int argc, char **argv)
+{
+ register int c;
+ int badflg = 0;
+ FILE *infile;
+ FILE *outfile = {stdout};
+
+#ifdef THREAD
+#if defined(HAVE_PTHREAD_INIT)
+ pthread_init(); /* Must be called before DBUG_ENTER */
+#endif
+ my_thread_global_init();
+#endif /* THREAD */
+ {
+ DBUG_ENTER ("main");
+ DBUG_PROCESS (argv[0]);
+ my_name = argv[0];
+ while ((c = getopt (argc,argv,"#:v")) != EOF) {
+ switch (c) {
+ case '#': /* Debugging Macro enable */
+ DBUG_PUSH (optarg);
+ break;
+ case 'v': /* Verbose mode */
+ verbose++;
+ break;
+ default:
+ badflg++;
+ break;
+ }
+ }
+ if (badflg) {
+ usage ();
+ DBUG_RETURN (EX_USAGE);
+ }
+ if (optind < argc) {
+ FILEOPEN (infile, argv[optind], "r");
+ } else {
+ FILEOPEN (infile, PRO_FILE, "r");
+ }
+ process (infile);
+ output (outfile);
+ DBUG_RETURN (EX_OK);
+}
+}
+
+#ifdef MSDOS
+
+/*
+ * From std-unix@ut-sally.UUCP (Moderator, John Quarterman) Sun Nov 3 14:34:15 1985
+ * Relay-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site gatech.CSNET
+ * Posting-Version: version B 2.10.2 9/18/84; site ut-sally.UUCP
+ * Path: gatech!akgua!mhuxv!mhuxt!mhuxr!ulysses!allegra!mit-eddie!genrad!panda!talcott!harvard!seismo!ut-sally!std-unix
+ * From: std-unix@ut-sally.UUCP (Moderator, John Quarterman)
+ * Newsgroups: mod.std.unix
+ * Subject: public domain AT&T getopt source
+ * Message-ID: <3352@ut-sally.UUCP>
+ * Date: 3 Nov 85 19:34:15 GMT
+ * Date-Received: 4 Nov 85 12:25:09 GMT
+ * Organization: IEEE/P1003 Portable Operating System Environment Committee
+ * Lines: 91
+ * Approved: jsq@ut-sally.UUCP
+ *
+ * Here's something you've all been waiting for: the AT&T public domain
+ * source for getopt(3). It is the code which was given out at the 1985
+ * UNIFORUM conference in Dallas. I obtained it by electronic mail
+ * directly from AT&T. The people there assure me that it is indeed
+ * in the public domain.
+ *
+ * There is no manual page. That is because the one they gave out at
+ * UNIFORUM was slightly different from the current System V Release 2
+ * manual page. The difference apparently involved a note about the
+ * famous rules 5 and 6, recommending using white space between an option
+ * and its first argument, and not grouping options that have arguments.
+ * Getopt itself is currently lenient about both of these things White
+ * space is allowed, but not mandatory, and the last option in a group can
+ * have an argument. That particular version of the man page evidently
+ * has no official existence, and my source at AT&T did not send a copy.
+ * The current SVR2 man page reflects the actual behavor of this getopt.
+ * However, I am not about to post a copy of anything licensed by AT&T.
+ *
+ * I will submit this source to Berkeley as a bug fix.
+ *
+ * I, personally, make no claims or guarantees of any kind about the
+ * following source. I did compile it to get some confidence that
+ * it arrived whole, but beyond that you're on your own.
+ *
+ */
+
+/*LINTLIBRARY*/
+
+int opterr = 1;
+int optind = 1;
+int optopt;
+char *optarg;
+
+static void _ERR(s,c,argv)
+char *s;
+int c;
+char *argv[];
+{
+ char errbuf[3];
+
+ if (opterr) {
+ errbuf[0] = c;
+ errbuf[1] = '\n';
+ (void) fprintf(stderr, "%s", argv[0]);
+ (void) fprintf(stderr, "%s", s);
+ (void) fprintf(stderr, "%s", errbuf);
+ }
+}
+
+int getopt(argc, argv, opts)
+int argc;
+char **argv, *opts;
+{
+ static int sp = 1;
+ register int c;
+ register char *cp;
+
+ if(sp == 1)
+ if(optind >= argc ||
+ argv[optind][0] != '-' || argv[optind][1] == '\0')
+ return(EOF);
+ else if(strcmp(argv[optind], "--") == 0) {
+ optind++;
+ return(EOF);
+ }
+ optopt = c = argv[optind][sp];
+ if(c == ':' || (cp=strchr(opts, c)) == NULL) {
+ _ERR(": illegal option -- ", c, argv);
+ if(argv[optind][++sp] == '\0') {
+ optind++;
+ sp = 1;
+ }
+ return('?');
+ }
+ if(*++cp == ':') {
+ if(argv[optind][sp+1] != '\0')
+ optarg = &argv[optind++][sp+1];
+ else if(++optind >= argc) {
+ _ERR(": option requires an argument -- ", c, argv);
+ sp = 1;
+ return('?');
+ } else
+ optarg = argv[optind++];
+ sp = 1;
+ } else {
+ if(argv[optind][++sp] == '\0') {
+ sp = 1;
+ optind++;
+ }
+ optarg = NULL;
+ }
+ return(c);
+}
+
+#endif /* !unix && !xenix */
diff --git a/externals/mysql/extlib/dbug/dbug_long.h b/externals/mysql/extlib/dbug/dbug_long.h
new file mode 100644
index 00000000000..829df181ef1
--- /dev/null
+++ b/externals/mysql/extlib/dbug/dbug_long.h
@@ -0,0 +1,160 @@
+#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_2(keyword,format) /* Obsolete */
+# define DBUG_3(keyword,format,a1) /* Obsolete */
+# define DBUG_4(keyword,format,a1,a2) /* Obsolete */
+# define DBUG_5(keyword,format,a1,a2,a3) /* Obsolete */
+# define DBUG_PUSH(a1)
+# define DBUG_POP()
+# define DBUG_PROCESS(a1)
+# define DBUG_FILE (stderr)
+# define DBUG_SETJMP setjmp
+# define DBUG_LONGJMP longjmp
+# 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_2(keyword,format) \
+ DBUG_PRINT(keyword,(format)) /* Obsolete */
+# define DBUG_3(keyword,format,a1) \
+ DBUG_PRINT(keyword,(format,a1)) /* Obsolete */
+# define DBUG_4(keyword,format,a1,a2) \
+ DBUG_PRINT(keyword,(format,a1,a2)) /* Obsolete */
+# define DBUG_5(keyword,format,a1,a2,a3) \
+ DBUG_PRINT(keyword,(format,a1,a2,a3)) /* Obsolete */
+# 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_SETJMP(a1) (_db_setjmp_ (), setjmp (a1))
+# define DBUG_LONGJMP(a1,a2) (_db_longjmp_ (), longjmp (a1, a2))
+# define DBUG_DUMP(keyword,a1,a2) _db_dump_(__LINE__,keyword,a1,a2)
+# endif
diff --git a/externals/mysql/extlib/dbug/example1.c b/externals/mysql/extlib/dbug/example1.c
new file mode 100644
index 00000000000..7b3c3fcd63d
--- /dev/null
+++ b/externals/mysql/extlib/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/externals/mysql/extlib/dbug/example2.c b/externals/mysql/extlib/dbug/example2.c
new file mode 100644
index 00000000000..75fc1321b13
--- /dev/null
+++ b/externals/mysql/extlib/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/externals/mysql/extlib/dbug/example3.c b/externals/mysql/extlib/dbug/example3.c
new file mode 100644
index 00000000000..c035cdfffa0
--- /dev/null
+++ b/externals/mysql/extlib/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/externals/mysql/extlib/dbug/factorial.c b/externals/mysql/extlib/dbug/factorial.c
new file mode 100644
index 00000000000..7b190ea8d8e
--- /dev/null
+++ b/externals/mysql/extlib/dbug/factorial.c
@@ -0,0 +1,27 @@
+#ifdef DBUG_OFF /* We are testing dbug */
+
+int factorial(register int value) {
+ if(value > 1) {
+ value *= factorial(value-1);
+ }
+ return value;
+}
+
+#else
+
+#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);
+}
+
+#endif
+
diff --git a/externals/mysql/extlib/dbug/main.c b/externals/mysql/extlib/dbug/main.c
new file mode 100644
index 00000000000..00e80c8ba31
--- /dev/null
+++ b/externals/mysql/extlib/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/externals/mysql/extlib/dbug/my_main.c b/externals/mysql/extlib/dbug/my_main.c
new file mode 100644
index 00000000000..31c15aa67aa
--- /dev/null
+++ b/externals/mysql/extlib/dbug/my_main.c
@@ -0,0 +1,42 @@
+/*
+ this is modified version of the original example main.c
+ fixed so that it could compile and run in MySQL source tree
+*/
+
+#ifdef DBUG_OFF /* We are testing dbug */
+#undef DBUG_OFF
+#endif
+
+#include <my_global.h> /* This includes dbug.h */
+#include <my_pthread.h>
+
+int main (argc, argv)
+int argc;
+char *argv[];
+{
+ register int result, ix;
+ extern int factorial(int);
+#if defined(HAVE_PTHREAD_INIT) && defined(THREAD)
+ pthread_init(); /* Must be called before DBUG_ENTER */
+#endif
+#ifdef THREAD
+ my_thread_global_init();
+#endif
+ {
+ 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/externals/mysql/extlib/dbug/sanity.c b/externals/mysql/extlib/dbug/sanity.c
new file mode 100644
index 00000000000..df43fc14ba9
--- /dev/null
+++ b/externals/mysql/extlib/dbug/sanity.c
@@ -0,0 +1,13 @@
+/* Declarate _sanity() if not declared in main program */
+
+#include <my_global.h>
+
+extern int _sanity(const char *file,uint line);
+
+#if defined(SAFEMALLOC) && !defined(MASTER) /* Avoid errors in MySQL */
+int _sanity(const char * file __attribute__((unused)),
+ uint line __attribute__((unused)))
+{
+ return 0;
+}
+#endif
diff --git a/externals/mysql/extlib/dbug/tests.c b/externals/mysql/extlib/dbug/tests.c
new file mode 100644
index 00000000000..d76266d34a3
--- /dev/null
+++ b/externals/mysql/extlib/dbug/tests.c
@@ -0,0 +1,87 @@
+/*
+ A program to test DBUG features. Used by tests-t.pl
+*/
+
+char *push1=0;
+
+#include <my_global.h> /* This includes dbug.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;
+ 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, char *argv[])
+{
+ int i;
+#ifdef DBUG_OFF
+ return 1;
+#endif
+ if (argc == 1)
+ return 0;
+
+#if defined(HAVE_PTHREAD_INIT) && defined(THREAD)
+ pthread_init(); /* Must be called before DBUG_ENTER */
+#endif
+#ifdef THREAD
+ my_thread_global_init();
+#endif
+ 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");
+ DBUG_PROCESS ("dbug-tests");
+ 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_RETURN (0);
+ }
+}