diff options
Diffstat (limited to 'dbug')
-rw-r--r-- | dbug/CMakeLists.txt | 45 | ||||
-rw-r--r-- | dbug/dbug.c | 627 | ||||
-rwxr-xr-x | dbug/dbug_add_tags.pl | 1 | ||||
-rw-r--r-- | dbug/dbug_analyze.c | 605 | ||||
-rw-r--r-- | dbug/dbug_long.h | 16 | ||||
-rwxr-xr-x | dbug/remove_function_from_trace.pl | 11 | ||||
-rwxr-xr-x | dbug/tests-t.pl | 5 | ||||
-rw-r--r-- | dbug/user.r | 124 |
8 files changed, 495 insertions, 939 deletions
diff --git a/dbug/CMakeLists.txt b/dbug/CMakeLists.txt index b0a6eba5d08..a4f30f75f97 100644 --- a/dbug/CMakeLists.txt +++ b/dbug/CMakeLists.txt @@ -20,3 +20,48 @@ INCLUDE_DIRECTORIES( SET(DBUG_SOURCES dbug.c) ADD_CONVENIENCE_LIBRARY(dbug ${DBUG_SOURCES}) TARGET_LINK_LIBRARIES(dbug mysys) + +ADD_EXECUTABLE(tests tests.c) +TARGET_LINK_LIBRARIES(tests dbug) + +ADD_EXECUTABLE(factorial my_main.c factorial.c) +TARGET_LINK_LIBRARIES(factorial dbug) + +IF(NOT WIN32) + FIND_PROGRAM(GROFF groff) + FIND_PROGRAM(NROFF nroff) + SET(OUTPUT_INC output1.r output2.r output3.r output4.r output5.r) + SET(SOURCE_INC factorial.r main.r example1.r example2.r example3.r) + ADD_CUSTOM_COMMAND(OUTPUT ${OUTPUT_INC} + DEPENDS factorial + COMMAND ./factorial 1 2 3 4 5 > output1.r + COMMAND ./factorial -\#t:o 2 3 > output2.r + COMMAND ./factorial -\#d:t:o 3 > output3.r + COMMAND ./factorial -\#d,result:o 4 > output4.r + COMMAND ./factorial -\#d:f,factorial:F:L:o 3 > output5.r) + FOREACH(file ${SOURCE_INC}) + STRING(REGEX REPLACE "\\.r" ".c" srcfile ${file}) + ADD_CUSTOM_COMMAND(OUTPUT ${file} DEPENDS ${srcfile} + COMMAND sed -e 's!\\\\!\\\\\\\\!g' + <${CMAKE_CURRENT_SOURCE_DIR}/${srcfile} >${file}) + ENDFOREACH(file) + ADD_CUSTOM_COMMAND(OUTPUT dbug-t DEPENDS tests-t.pl + COMMAND cp -f ${CMAKE_CURRENT_SOURCE_DIR}/tests-t.pl dbug-t) + ADD_CUSTOM_TARGET(dbug-unit-tests ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/dbug-t) + MY_ADD_TEST(dbug) + + IF(GROFF) + ADD_CUSTOM_COMMAND(OUTPUT user.ps + DEPENDS user.r ${OUTPUT_INC} ${SOURCE_INC} + COMMAND ${GROFF} -mm ${CMAKE_CURRENT_SOURCE_DIR}/user.r > user.ps || touch user.ps) + ADD_CUSTOM_TARGET(ps ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/user.ps) + ENDIF(GROFF) + IF(NROFF) + ADD_CUSTOM_COMMAND(OUTPUT user.t + DEPENDS user.r ${OUTPUT_INC} ${SOURCE_INC} + COMMAND ${NROFF} -mm ${CMAKE_CURRENT_SOURCE_DIR}/user.r > user.t || touch user.t) + ADD_CUSTOM_TARGET(t ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/user.t) + ENDIF(NROFF) + +ENDIF(NOT WIN32) + diff --git a/dbug/dbug.c b/dbug/dbug.c index 2dadf7bb2d5..8d2d5082b6a 100644 --- a/dbug/dbug.c +++ b/dbug/dbug.c @@ -53,9 +53,6 @@ * 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 @@ -84,8 +81,8 @@ in pthread_mutex_lock */ -#undef SAFE_MUTEX #include <my_global.h> +#undef SAFE_MUTEX #include <m_string.h> #include <errno.h> @@ -99,8 +96,8 @@ #include <process.h> #endif -#ifndef DBUG_OFF - +#include <my_valgrind.h> /* TRASH */ +#include <my_stacktrace.h> /* my_safe_print_str */ /* * Manifest constants which may be "tuned" if desired. @@ -125,16 +122,15 @@ #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 FLUSH_ON_WRITE (1 << 10) /* Flush on every write */ #define OPEN_APPEND (1 << 11) /* Open for append */ +#define SANITY_CHECK_ON (1 << 12) /* Check memory on every DBUG_ENTER/RETURN */ #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. @@ -143,44 +139,16 @@ #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 */ +static void perror(char *s) +{ + if (s && *s != '\0') + (void) fprintf(stderr, "%s: ", s); + (void) fprintf(stderr, "<unknown system error>\n"); +} #endif /* @@ -219,10 +187,8 @@ struct settings { 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 */ @@ -239,6 +205,8 @@ 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 */ +static const char *unknown_func= "?func"; +static const char *unknown_file= "?file"; typedef struct _db_code_state_ { const char *process; /* Pointer to process name; usually argv[0] */ @@ -279,7 +247,7 @@ typedef struct _db_code_state_ { #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 int InList(struct link *linkp,const char *cp, int exact_match); static uint ListFlags(struct link *linkp); static void FreeList(struct link *linkp); @@ -319,6 +287,8 @@ static void DbugExit(const char *why); static const char *DbugStrTok(const char *s); static void DbugVfprintf(FILE *stream, const char* format, va_list args); +static void DbugErr(CODE_STATE *, uint, const char* format, ...); + /* * Miscellaneous printf format strings. */ @@ -341,6 +311,8 @@ static void DbugVfprintf(FILE *stream, const char* format, va_list args); #define WRITABLE(pathname) (access(pathname, W_OK) == 0) #endif +static int sf_sanity(); +static void sf_terminate(); /* ** Macros to allow dbugging with threads @@ -349,6 +321,9 @@ static void DbugVfprintf(FILE *stream, const char* format, va_list args); #include <my_pthread.h> static pthread_mutex_t THR_LOCK_dbug; +/* this mutex protects all sf_* variables, and nothing else*/ +static pthread_mutex_t sf_mutex; + static CODE_STATE *code_state(void) { CODE_STATE *cs, **cs_ptr; @@ -364,6 +339,7 @@ static CODE_STATE *code_state(void) { init_done=TRUE; pthread_mutex_init(&THR_LOCK_dbug, NULL); + pthread_mutex_init(&sf_mutex, NULL); bzero(&init_settings, sizeof(init_settings)); init_settings.out_file=stderr; init_settings.flags=OPEN_APPEND; @@ -376,8 +352,8 @@ static CODE_STATE *code_state(void) 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->func= unknown_func; + cs->file= unknown_file; cs->stack=&init_settings; *cs_ptr= cs; } @@ -453,25 +429,29 @@ int DbugParse(CODE_STATE *cs, const char *control) const char *end; int rel, f_used=0; struct settings *stack; + int org_cs_locked; stack= cs->stack; + if (!(org_cs_locked= cs->locked)) + { + cs->locked= 1; + pthread_mutex_lock(&THR_LOCK_dbug); + } + if (control[0] == '-' && control[1] == '#') control+=2; rel= control[0] == '+' || control[0] == '-'; if ((!rel || (!stack->out_file && !stack->next))) { - /* Free memory associated with the state before resetting its members */ 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; } @@ -482,7 +462,6 @@ int DbugParse(CODE_STATE *cs, const char *control) stack->maxdepth= stack->next->maxdepth; stack->sub_level= stack->next->sub_level; strcpy(stack->name, stack->next->name); - stack->prof_file= stack->next->prof_file; if (stack->next == &init_settings) { /* @@ -493,7 +472,6 @@ int DbugParse(CODE_STATE *cs, const char *control) */ stack->out_file= stderr; 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); } @@ -501,7 +479,6 @@ int DbugParse(CODE_STATE *cs, const char *control) { stack->out_file= stack->next->out_file; stack->functions= stack->next->functions; - stack->p_functions= stack->next->p_functions; stack->keywords= stack->next->keywords; stack->processes= stack->next->processes; } @@ -513,7 +490,8 @@ int DbugParse(CODE_STATE *cs, const char *control) int c, sign= (*control == '+') ? 1 : (*control == '-') ? -1 : 0; if (sign) control++; c= *control++; - if (*control == ',') control++; + if (*control == ',') + control++; /* XXX when adding new cases here, don't forget _db_explain_ ! */ switch (c) { case 'd': @@ -531,7 +509,7 @@ int DbugParse(CODE_STATE *cs, const char *control) { if (DEBUGGING) stack->keywords= ListDel(stack->keywords, control, end); - break; + break; } stack->keywords= ListAdd(stack->keywords, control, end); stack->flags |= DEBUG_ON; @@ -658,15 +636,26 @@ int DbugParse(CODE_STATE *cs, const char *control) 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); } + if (!org_cs_locked) + { + pthread_mutex_unlock(&THR_LOCK_dbug); + cs->locked= 0; + } return !rel || f_used; } - + #define framep_trace_flag(cs, frp) (frp ? \ frp->level & TRACE_ON : \ (ListFlags(cs->stack->functions) & INCLUDE) ? \ @@ -815,7 +804,7 @@ void _db_set_(const char *control) * * 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() + * up a new debug settings * */ @@ -919,7 +908,7 @@ void _db_pop_() } while (0) #define str_to_buf(S) do { \ char_to_buf(','); \ - buf=strnmov(buf, (S), len+1); \ + buf=strnmov(buf, (S), (uint) (end-buf)); \ if (buf >= end) goto overflow; \ } while (0) #define list_to_buf(l, f) do { \ @@ -1004,7 +993,6 @@ int _db_explain_ (CODE_STATE *cs, char *buf, size_t len) 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); @@ -1017,6 +1005,7 @@ int _db_explain_ (CODE_STATE *cs, char *buf, size_t len) 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; @@ -1118,6 +1107,8 @@ void _db_enter_(const char *_func_, const char *_file_, if (!TRACING) break; /* fall through */ case DO_TRACE: + if ((cs->stack->flags & SANITY_CHECK_ON) && sf_sanity()) + cs->stack->flags &= ~SANITY_CHECK_ON; if (TRACING) { if (!cs->locked) @@ -1173,6 +1164,8 @@ void _db_return_(uint _line_, struct _db_stack_frame_ *_stack_frame_) if (DoTrace(cs) & DO_TRACE) { + if ((cs->stack->flags & SANITY_CHECK_ON) && sf_sanity()) + cs->stack->flags &= ~SANITY_CHECK_ON; if (TRACING) { if (!cs->locked) @@ -1259,11 +1252,11 @@ void _db_doprnt_(const char *format,...) va_start(args,format); + if (!cs->locked) + pthread_mutex_lock(&THR_LOCK_dbug); 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); @@ -1274,6 +1267,9 @@ void _db_doprnt_(const char *format,...) DbugFlush(cs); errno=save_errno; } + else if (!cs->locked) + pthread_mutex_unlock(&THR_LOCK_dbug); + va_end(args); } @@ -1315,10 +1311,10 @@ void _db_dump_(uint _line_, const char *keyword, CODE_STATE *cs; get_code_state_or_return; + if (!cs->locked) + pthread_mutex_lock(&THR_LOCK_dbug); if (_db_keyword_(cs, keyword, 0)) { - if (!cs->locked) - pthread_mutex_lock(&THR_LOCK_dbug); DoPrefix(cs, _line_); if (TRACING) { @@ -1348,6 +1344,8 @@ void _db_dump_(uint _line_, const char *keyword, (void) fputc('\n',cs->stack->out_file); DbugFlush(cs); } + else if (!cs->locked) + pthread_mutex_unlock(&THR_LOCK_dbug); } @@ -1391,7 +1389,7 @@ next: subdir=0; while (ctlp < end && *ctlp != ',') ctlp++; - len=ctlp-start; + len= (int) (ctlp-start); if (start[len-1] == '/') { len--; @@ -1492,13 +1490,13 @@ static struct link *ListCopy(struct link *orig) * */ -static int InList(struct link *linkp, const char *cp) +static int InList(struct link *linkp, const char *cp, int exact_match) { int result; for (result=MATCHED; linkp != NULL; linkp= linkp->next_link) { - if (!fnmatch(linkp->str, cp, 0)) + if (!(exact_match ? strcmp(linkp->str,cp) : fnmatch(linkp->str, cp, 0))) return linkp->flags; if (!(linkp->flags & EXCLUDE)) result=NOT_MATCHED; @@ -1548,7 +1546,7 @@ static void PushState(CODE_STATE *cs) struct settings *new_malloc; new_malloc= (struct settings *) DbugMalloc(sizeof(struct settings)); - bzero(new_malloc, sizeof(struct settings)); + bzero(new_malloc, sizeof(*new_malloc)); new_malloc->next= cs->stack; cs->stack= new_malloc; } @@ -1578,19 +1576,12 @@ static void FreeState(CODE_STATE *cs, struct settings *state, int free_state) 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); } @@ -1623,31 +1614,28 @@ void _db_end_() called after dbug was initialized */ _dbug_on_= 1; - get_code_state_or_return; + cs= code_state(); - while ((discard= cs->stack)) - { - if (discard == &init_settings) - break; - cs->stack= discard->next; - FreeState(cs, discard, 1); - } + if (cs) + 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); + sf_terminate(); } @@ -1667,8 +1655,8 @@ void _db_end_() 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)) { + InList(cs->stack->processes, cs->process, 0) & (MATCHED|INCLUDE)) + switch(InList(cs->stack->functions, cs->func, 0)) { case INCLUDE|SUBDIR: return ENABLE_TRACE; case INCLUDE: return DO_TRACE; case MATCHED|SUBDIR: @@ -1711,11 +1699,11 @@ FILE *_db_fp_(void) BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict) { + int match= strict ? INCLUDE : INCLUDE|MATCHED; 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; + return (DEBUGGING && DoTrace(cs) & DO_TRACE && + InList(cs->stack->keywords, keyword, strict) & match); } /* @@ -1741,7 +1729,7 @@ BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict) static void Indent(CODE_STATE *cs, int indent) { - REGISTER int count; + int count; indent= max(indent-1-cs->stack->sub_level,0)*INDENT; for (count= 0; count < indent ; count++) @@ -1773,7 +1761,7 @@ static void Indent(CODE_STATE *cs, int indent) static void FreeList(struct link *linkp) { - REGISTER struct link *old; + struct link *old; while (linkp != NULL) { @@ -1872,7 +1860,7 @@ static void DoPrefix(CODE_STATE *cs, uint _line_) static void DBUGOpenFile(CODE_STATE *cs, const char *name,const char *end,int append) { - REGISTER FILE *fp; + FILE *fp; if (name != NULL) { @@ -1935,9 +1923,10 @@ static void DBUGOpenFile(CODE_STATE *cs, static void DBUGCloseFile(CODE_STATE *cs, FILE *fp) { - if (fp != NULL && fp != stderr && fp != stdout && fclose(fp) == EOF) + if (cs && fp && fp != stderr && fp != stdout && fclose(fp) == EOF) { - pthread_mutex_lock(&THR_LOCK_dbug); + if (!cs->locked) + pthread_mutex_lock(&THR_LOCK_dbug); (void) fprintf(cs->stack->out_file, ERR_CLOSE, cs->process); perror(""); DbugFlush(cs); @@ -1996,7 +1985,7 @@ static void DbugExit(const char *why) static char *DbugMalloc(size_t size) { - register char *new_malloc; + char *new_malloc; if (!(new_malloc= (char*) malloc(size))) DbugExit("out of memory"); @@ -2037,7 +2026,7 @@ static const char *DbugStrTok(const char *s) static const char *BaseName(const char *pathname) { - register const char *base; + const char *base; base= strrchr(pathname, FN_LIBCHAR); if (base++ == NullS) @@ -2074,8 +2063,8 @@ static const char *BaseName(const char *pathname) static BOOLEAN Writable(const char *pathname) { - REGISTER BOOLEAN granted; - REGISTER char *lastslash; + BOOLEAN granted; + char *lastslash; granted= FALSE; if (EXISTS(pathname)) @@ -2099,105 +2088,11 @@ static BOOLEAN Writable(const char *pathname) } #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 !! */ + 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) { @@ -2216,7 +2111,7 @@ static void DbugFlush(CODE_STATE *cs) void _db_flush_() { - CODE_STATE *cs= NULL; + CODE_STATE *cs; get_code_state_or_return; (void) fflush(cs->stack->out_file); } @@ -2264,19 +2159,333 @@ const char* _db_get_func_(void) return cs->func; } +/* + prints the error message, followed by a stack trace + of the specified depth +*/ +static void DbugErr(CODE_STATE *cs, uint depth, const char* format, ...) +{ + va_list args; + va_start(args,format); + vfprintf(stderr, format, args); + va_end(args); + + if (cs || ((cs= code_state()))) + { + uint i= depth; + struct _db_stack_frame_ *frame= cs->framep; + while (i-- && frame) + { + fprintf(stderr, ", at %s", frame->func); + frame= frame->prev; + } + } -#else + fprintf(stderr, "\n"); +} + +/******************************************************************** + memory debugger + based on safemalloc, memory sub-system, written by Bjorn Benson +********************************************************************/ + +#ifndef SF_REMEMBER_FRAMES +#define SF_REMEMBER_FRAMES 16 +#endif + +/* + Structure that stores information of an allocated memory block + The data is at &struct_adr+sizeof(struct irem) + Note that sizeof(struct st_irem) % sizeof(double) == 0 +*/ +struct st_irem +{ + struct st_irem *next; /* Linked list of structures */ + struct st_irem *prev; /* Other link */ + size_t datasize; /* Size requested */ + const char *frame[SF_REMEMBER_FRAMES]; /* call stack */ + uint32 marker; /* Underrun marker value */ +}; + +/* + DBUG_MALLOC/DBUG_REALLOC/DBUG_FREE can be called even + before dbug is initialized. We cannot properly take into account + these calls, but we can at least wrap allocated memory + in st_irem's and check for overrun/underruns. + These special irem's - that are not linked into a global list - + are distinguished by a special value in the 'next' pointer. +*/ +#define NOT_LINKED ((struct st_irem *)1) + +size_t sf_malloc_mem_limit= (intptr)~0ULL; +static size_t sf_malloc_cur_memory= 0L; /* Current memory usage */ +static size_t sf_malloc_max_memory= 0L; /* Maximum memory usage */ + +static int sf_malloc_count= 0; /* Number of allocated chunks */ + +static void *sf_min_adress= (void*) (intptr)~0ULL, + *sf_max_adress= 0; + +static struct st_irem *sf_malloc_root = 0; + +#define MAGICSTART 0x14235296 /* A magic value for underrun key */ + +#define MAGICEND0 0x68 /* Magic values for overrun keys */ +#define MAGICEND1 0x34 /* " */ +#define MAGICEND2 0x7A /* " */ +#define MAGICEND3 0x15 /* " */ + +static int bad_ptr(const char *where, void *ptr); +static void free_memory(void *ptr); /* - * Dummy function, workaround for MySQL bug#14420 related - * build failure on a platform where linking with an empty - * archive fails. + * FUNCTION * - * This block can be removed as soon as a fix for bug#14420 - * is implemented. + * _db_malloc_ allocates memory + * + * SYNOPSIS + * + * void *_db_malloc_(size_t size) + * size_t size; Bytes to allocate */ -int i_am_a_dummy_function() { - return 0; + +void *_db_malloc_(size_t size) +{ + CODE_STATE *cs= code_state(); + struct st_irem *irem; + uchar *data; + struct _db_stack_frame_ *frame; + int i= 0; + + if (size + sf_malloc_cur_memory > sf_malloc_mem_limit) + irem= 0; + else + irem= (struct st_irem *) malloc (sizeof(struct st_irem) + size + 4); + + if (!irem) + return 0; + + compile_time_assert(sizeof(struct st_irem) % sizeof(double) == 0); + + /* Fill up the structure */ + data= (uchar*) (irem + 1); + irem->datasize= size; + irem->prev= 0; + irem->marker= MAGICSTART; + data[size + 0]= MAGICEND0; + data[size + 1]= MAGICEND1; + data[size + 2]= MAGICEND2; + data[size + 3]= MAGICEND3; + + if (cs && cs->framep) + { + for (frame= cs->framep; + i < SF_REMEMBER_FRAMES && frame->func != unknown_func; + i++, frame= frame->prev) + irem->frame[i]= frame->func; + } + + if (i < SF_REMEMBER_FRAMES) + irem->frame[i]= unknown_func; + if (i==0) + irem->frame[0]= (char*)1; + + if (init_done) + { + pthread_mutex_lock(&sf_mutex); + /* Add this structure to the linked list */ + if ((irem->next= sf_malloc_root)) + sf_malloc_root->prev= irem; + sf_malloc_root= irem; + + /* Keep the statistics */ + sf_malloc_count++; + sf_malloc_cur_memory+= size; + set_if_bigger(sf_malloc_max_memory, sf_malloc_cur_memory); + set_if_smaller(sf_min_adress, (void*)data); + set_if_bigger(sf_max_adress, (void*)data); + pthread_mutex_unlock(&sf_mutex); + } + else + { + set_if_bigger(sf_malloc_max_memory, sf_malloc_cur_memory); + set_if_smaller(sf_min_adress, (void*)data); + set_if_bigger(sf_max_adress, (void*)data); + irem->next= NOT_LINKED; + } + + TRASH_ALLOC(data, size); + return data; } -#endif +void *_db_realloc_(void *ptr, size_t size) +{ + char *data; + + if (!ptr) + return _db_malloc_(size); + + if (bad_ptr("Reallocating", ptr)) + return 0; + + if ((data= _db_malloc_(size))) + { + struct st_irem *irem= (struct st_irem *)ptr - 1; + set_if_smaller(size, irem->datasize); + memcpy(data, ptr, size); + free_memory(ptr); + } + return data; +} + +void _db_free_(void *ptr) +{ + if (!ptr || bad_ptr("Freeing", ptr)) + return; + + free_memory(ptr); + return; +} + +static void free_memory(void *ptr) +{ + struct st_irem *irem= (struct st_irem *)ptr - 1; + + if (irem->next != NOT_LINKED) + { + pthread_mutex_lock(&sf_mutex); + /* Remove this structure from the linked list */ + if (irem->prev) + irem->prev->next= irem->next; + else + sf_malloc_root= irem->next; + + if (irem->next) + irem->next->prev= irem->prev; + + /* Handle the statistics */ + sf_malloc_cur_memory-= irem->datasize; + sf_malloc_count--; + pthread_mutex_unlock(&sf_mutex); + } + + /* only trash the data and magic values, but keep the stack trace */ + TRASH_FREE((uchar*)(irem + 1) - 4, irem->datasize + 8); + free(irem); + return; +} + +#define SF_ADD_NL 1 +#define SF_USE_SAFE_PRINT 2 +static void print_allocated_at(struct st_irem *irem, int flags) +{ + int i; + const char *allocated= flags & SF_ADD_NL ? "Allocated" : ", allocated"; + + for (i=0; + i < SF_REMEMBER_FRAMES && irem->frame[i] != unknown_func; + i++) + { + fprintf(stderr, "%s at ", i ? "," : allocated); + if (flags & SF_USE_SAFE_PRINT) + my_safe_print_str(irem->frame[i], 80); + else + fputs(irem->frame[i], stderr); + } + if (i && (flags & SF_ADD_NL)) + fprintf(stderr, "\n"); +} + +static int bad_ptr(const char *where, void *ptr) +{ + struct st_irem *irem= (struct st_irem *)ptr - 1; + const uchar *magicend; + + if (((intptr) ptr) % sizeof(double)) + { + DbugErr(0, SF_REMEMBER_FRAMES, "Error: %s wrong aligned pointer", where); + return 1; + } + if (ptr < sf_min_adress || ptr > sf_max_adress) + { + DbugErr(0, SF_REMEMBER_FRAMES, "Error: %s pointer out of range", where); + return 1; + } + if (irem->marker != MAGICSTART) + { + DbugErr(0, SF_REMEMBER_FRAMES, + "Error: %s unallocated data or underrun buffer", where); + /* + we cannot use print_allocated_at here: + if the memory was not allocated, there's nothing to print, + if it was allocated and underrun, call stack may be corrupted + */ + return 1; + } + + magicend= (uchar*)ptr + irem->datasize; + if (magicend[0] != MAGICEND0 || + magicend[1] != MAGICEND1 || + magicend[2] != MAGICEND2 || + magicend[3] != MAGICEND3) + { + DbugErr(0, SF_REMEMBER_FRAMES, "Error: %s overrun buffer", where); + print_allocated_at(irem, SF_ADD_NL); + return 1; + } + + return 0; +} + +/* check all allocated memory list for consistency */ +static int sf_sanity() +{ + struct st_irem *irem; + int flag= 0; + int count= 0; + + pthread_mutex_lock(&sf_mutex); + count= sf_malloc_count; + for (irem= sf_malloc_root; irem && count > 0; count--, irem= irem->next) + flag+= bad_ptr("Safemalloc", irem + 1); + pthread_mutex_unlock(&sf_mutex); + if (count || irem) + { + DbugErr(0, SF_REMEMBER_FRAMES, "Error: Safemalloc link list destroyed"); + return 1; + } + return 0; +} + +/* + * FUNCTION + * + * sf_terminate Report on all the memory pieces that have not been free'd + * + * SYNOPSIS + * + * void sf_terminate() + */ + +static void sf_terminate() +{ + struct st_irem *irem; + + sf_sanity(); + + /* Report on all the memory that was allocated but not free'd */ + if ((irem= sf_malloc_root)) + { + while (irem) + { + fprintf(stderr, "Warning: %6lu bytes at %p are not freed", (ulong) irem->datasize, irem + 1); + print_allocated_at(irem, SF_USE_SAFE_PRINT); + fprintf(stderr, "\n"); + irem= irem->next; + } + fprintf(stderr, "Memory lost: %lu bytes in %d chunks\n", + (ulong) sf_malloc_cur_memory, sf_malloc_count); + } + + return; +} diff --git a/dbug/dbug_add_tags.pl b/dbug/dbug_add_tags.pl index 66096450f82..fe3637b4d12 100755 --- a/dbug/dbug_add_tags.pl +++ b/dbug/dbug_add_tags.pl @@ -86,4 +86,3 @@ while($src=shift) } warn "All done!\n"; - diff --git a/dbug/dbug_analyze.c b/dbug/dbug_analyze.c deleted file mode 100644 index cd395cd3b02..00000000000 --- a/dbug/dbug_analyze.c +++ /dev/null @@ -1,605 +0,0 @@ -/* - * 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) - -extern int optind; -extern char *optarg; - -int main (int argc, char **argv) -{ - register int c; - int badflg = 0; - FILE *infile; - FILE *outfile = {stdout}; - - my_thread_global_init(); - { - 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); - } -} diff --git a/dbug/dbug_long.h b/dbug/dbug_long.h index 829df181ef1..e77218b29b7 100644 --- a/dbug/dbug_long.h +++ b/dbug/dbug_long.h @@ -116,16 +116,10 @@ # 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) \ @@ -142,19 +136,9 @@ {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/dbug/remove_function_from_trace.pl b/dbug/remove_function_from_trace.pl index 1da9e25f9ba..380df168caf 100755 --- a/dbug/remove_function_from_trace.pl +++ b/dbug/remove_function_from_trace.pl @@ -1,6 +1,5 @@ #!/usr/bin/perl - die <<EEE unless @ARGV; Usage: $0 func1 [func2 [ ...] ] @@ -11,16 +10,16 @@ DBUG_ENTER() and DBUG_POP(); right before DBUG_RETURN in every such a function. EEE $re=join('|', @ARGV); -$skip=''; while(<STDIN>) { - print unless $skip; + ($thd) = /^(T@\d+)/; + print unless $skip{$thd}; next unless /^(?:.*: )*((?:\| )*)([<>])($re)\n/o; if ($2 eq '>') { - $skip=$1.$3 unless $skip; + $skip{$thd}=$1.$3 unless $skip{$thd}; next; } - next if $skip ne $1.$3; - $skip=''; + next if $skip{$thd} ne $1.$3; + delete $skip{$thd}; print; } diff --git a/dbug/tests-t.pl b/dbug/tests-t.pl index de9ed6f6ab9..9f517ba3efd 100755 --- a/dbug/tests-t.pl +++ b/dbug/tests-t.pl @@ -8,8 +8,9 @@ use Test::More; $exe=$0; +$exe =~ s@[^/]+$@tests@; -die unless $exe =~ s/(tests)-t(\.exe)?$/$1$2 /; +die unless -x $exe; # load tests @tests=(); @@ -28,7 +29,7 @@ plan skip_all => "because dbug is disabled" if system $exe; plan tests => scalar(@tests); for (@tests) { - $t=$exe . shift @$_; + $t=$exe . ' ' . shift @$_; chomp($t); open F, '-|', $t or die "open($t|): $!"; local $"; diff --git a/dbug/user.r b/dbug/user.r index 5628f5a4fa1..1ccd46bd21e 100644 --- a/dbug/user.r +++ b/dbug/user.r @@ -823,28 +823,6 @@ number of bytes to dump. .SP 1 EX: \fCDBUG_DBUG\ ("net",\ packet,\ len);\fR .SP 1 -.LI DBUG_SETJMP\ -Used in place of the setjmp() function to first save the current -debugger state and then execute the standard setjmp call. -This allows to the debugger to restore its state when the -DBUG_LONGJMP macro is used to invoke the standard longjmp() call. -Currently all instances of DBUG_SETJMP must occur within the -same function and at the same function nesting level. -.SP 1 -EX: \fCDBUG_SETJMP\ (env);\fR -.SP 1 -.LI DBUG_LONGJMP\ -Used in place of the longjmp() function to first restore the -previous debugger state at the time of the last DBUG_SETJMP -and then execute the standard longjmp() call. -Note that currently all DBUG_LONGJMP macros restore the state -at the time of the last DBUG_SETJMP. -It would be possible to maintain separate DBUG_SETJMP and DBUG_LONGJMP -pairs by having the debugger runtime support module use the first -argument to differentiate the pairs. -.SP 1 -EX: \fCDBUG_LONGJMP\ (env,val);\fR -.SP 1 .LI DBUG_LOCK_FILE\ Used in multi-threaded environment to lock DBUG_FILE stream. It can be used, for example, in functions that need to write something to a @@ -903,6 +881,20 @@ Modifying .I initial value does not affect threads that are already running. Obviously, these macros are only useful in the multi-threaded environment. +.SP 1 +.LI DBUG_MALLOC\ +.LI DBUG_REALLOC\ +.LI DBUG_FREE\ +When these macros are used instead of system malloc(), realloc(), and free(), +.I dbug +built-in memory debugger performs checks for memory overwrites, underwrites, +memory leaks, and accesses to uninitialized or freed memory. Memory leaks are +found as memory not deallocated at shutdown. Memory overwrites and underwrites +are detected when this memory is about to be freed (by +.B DBUG_FREE +macro), unless +.B S +flag is present in the debug control string (see below). .LE .SK @@ -971,15 +963,6 @@ containing the macro causing the output. .LI i Mark each debugger output line with the PID (or thread ID) of the current process. -.LI g,[functions] -Enable profiling for the specified list of functions. -Every function can be a -.I glob(7) -pattern. -An empty list of functions enables profiling for all functions. -See -.B PROFILING\ WITH\ DBUG -below. .LI L Mark each debugger output line with the source file line number of the macro causing the output. @@ -1019,6 +1002,16 @@ Most useful with .B DBUG_PUSH macros used to temporarily alter the debugger state. +.LI S +Check the memory allocated with +.B DBUG_MALLOC +and +.B DBUG_REALLOC +for overwrites/underwrites +on each +.B DBUG_ENTER +and +.B DBUG_RETURN. .LI t[,N] Enable function control flow tracing. The maximum nesting depth is specified by N, and defaults to @@ -1078,76 +1071,7 @@ all writes to a file are always followed by a flush. .SK .B -PROFILING WITH DBUG -.R - -.P -With -.I dbug -one can do profiling in a machine independent fashion, -without a need for profiled version of system libraries. -For this, -.I dbug -can write out a file -called -.B dbugmon.out -(by default). This is an ascii file containing lines of the form: -.DS CB N -\fC<function-name> E <time-entered> -<function-name> X <time-exited> -.DE -.P -A second program (\fBanalyze\fR) reads this file, and produces a report on -standard output. - -.P -Profiling is enabled through the -.B g -flag. It can take a list of -function names for which profiling is enabled. By default, it -profiles all functions. - -.P -The profile file is opened for appending. This -is in order that one can run a program several times, and get the -sum total of all the times, etc. - -.P -An example of the report generated follows: -.DS CB N -\fC - Profile of Execution - Execution times are in milliseconds - - Calls Time - ----- ---- - Times Percentage Time Spent Percentage -Function Called of total in Function of total Importance -======== ====== ========== =========== ========== ========== -factorial 5 83.33 30 100.00 8333 -main 1 16.67 0 0.00 0 -======== ====== ========== =========== ========== -Totals 6 100.00 30 100.00 -.DE -.P -As you can see, it's quite self-evident. The -.B Importance -column is a -metric obtained by multiplying the percentage of the calls and the percentage -of the time. Functions with higher 'importance' benefit the most from -being sped up. - -.P -As a limitation - setjmp/longjmp, or child processes, are ignored -for the time being. Also, profiling does not work -in a multi-threaded environment. - -.P -Profiling code is (c) Binayak Banerjee. - -.SK -.B HINTS AND MISCELLANEOUS .R |