diff options
author | Alexander Nozdrin <alik@sun.com> | 2009-11-10 10:31:33 +0300 |
---|---|---|
committer | Alexander Nozdrin <alik@sun.com> | 2009-11-10 10:31:33 +0300 |
commit | b712dce3d57335d3957afe94d1c3200880aebe99 (patch) | |
tree | b118b585c21710f0fef58c3d009fdd39a4578791 | |
parent | 49bb8cba8e964bec96c1ca9cdc486b3a468dd822 (diff) | |
parent | d476bbb0becb27d7d3cb1fc5cc0ac4d401622893 (diff) | |
download | mariadb-git-b712dce3d57335d3957afe94d1c3200880aebe99.tar.gz |
Auto-merge from mysql-next-mr.
65 files changed, 2248 insertions, 1109 deletions
diff --git a/.bzrignore b/.bzrignore index ac0347558b7..351417a4353 100644 --- a/.bzrignore +++ b/.bzrignore @@ -3068,3 +3068,5 @@ libmysqld/examples/mysqltest.cc libmysqld/sql_signal.cc libmysqld/rpl_handler.cc libmysqld/debug_sync.cc +libmysqld/rpl_handler.cc +dbug/tests diff --git a/CMakeLists.txt b/CMakeLists.txt index c6769eca72d..6e53ff30159 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -311,6 +311,7 @@ ADD_SUBDIRECTORY(extra) ADD_SUBDIRECTORY(client) ADD_SUBDIRECTORY(sql) ADD_SUBDIRECTORY(libmysql) +ADD_SUBDIRECTORY(libservices) ADD_SUBDIRECTORY(tests) IF(WITH_EMBEDDED_SERVER) ADD_SUBDIRECTORY(libmysqld) diff --git a/Makefile.am b/Makefile.am index 97cd363bc52..c208b5b6df1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,7 +23,7 @@ EXTRA_DIST = INSTALL-SOURCE INSTALL-WIN-SOURCE \ SUBDIRS = . include @docs_dirs@ @zlib_dir@ \ @readline_topdir@ sql-common scripts \ - @pstack_dir@ \ + @pstack_dir@ libservices \ @sql_union_dirs@ unittest \ @sql_server@ @man_dirs@ tests \ netware @libmysqld_dirs@ \ @@ -32,7 +32,7 @@ SUBDIRS = . include @docs_dirs@ @zlib_dir@ \ DIST_SUBDIRS = . include Docs zlib \ cmd-line-utils sql-common scripts \ - pstack \ + pstack libservices \ strings mysys dbug extra regex libmysql libmysql_r client unittest storage plugin \ vio sql man tests \ netware libmysqld \ diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 1acf4da8d53..f185c775e90 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -1161,7 +1161,6 @@ void free_used_memory() mysql_server_end(); /* Don't use DBUG after mysql_server_end() */ - DBUG_VIOLATION_HELPER_LEAVE; return; } diff --git a/configure.in b/configure.in index 11b12352081..9fe5c741a03 100644 --- a/configure.in +++ b/configure.in @@ -2921,7 +2921,7 @@ AC_CONFIG_FILES(Makefile extra/Makefile mysys/Makefile dnl man/Makefile BUILD/Makefile vio/Makefile dnl libmysql/Makefile libmysql_r/Makefile client/Makefile dnl sql/Makefile sql/share/Makefile dnl - sql/sql_builtin.cc sql-common/Makefile dnl + sql/sql_builtin.cc sql-common/Makefile libservices/Makefile dnl dbug/Makefile scripts/Makefile include/Makefile dnl tests/Makefile Docs/Makefile support-files/Makefile dnl support-files/MacOSX/Makefile support-files/RHEL4-SElinux/Makefile dnl diff --git a/dbug/.cvsignore b/dbug/.cvsignore deleted file mode 100644 index e9955884756..00000000000 --- a/dbug/.cvsignore +++ /dev/null @@ -1,3 +0,0 @@ -.deps -Makefile -Makefile.in diff --git a/dbug/Makefile.am b/dbug/Makefile.am index 872ebdb7902..4091858a1af 100644 --- a/dbug/Makefile.am +++ b/dbug/Makefile.am @@ -23,19 +23,20 @@ libdbug_a_SOURCES = dbug.c sanity.c EXTRA_DIST = CMakeLists.txt example1.c example2.c example3.c \ user.r monty.doc dbug_add_tags.pl \ my_main.c main.c factorial.c dbug_analyze.c \ - CMakeLists.txt + CMakeLists.txt tests.c tests-t.pl NROFF_INC = example1.r example2.r example3.r main.r \ factorial.r output1.r output2.r output3.r \ output4.r output5.r -CLEANFILES = $(NROFF_INC) user.t user.ps +CLEANFILES = $(NROFF_INC) user.t user.ps tests-t # Must be linked with libs that are not compiled yet -noinst_PROGRAMS = factorial dbug_analyze +noinst_PROGRAMS = factorial dbug_analyze tests factorial_SOURCES = my_main.c factorial.c +tests_SOURCES = tests.c dbug_analyze_SOURCES = dbug_analyze.c -all: user.t user.ps +all: user.t user.ps tests-t user.t: user.r $(NROFF_INC) -nroff -mm user.r > $@ @@ -61,5 +62,7 @@ output5.r: factorial @RM@ -f $@ @SED@ -e 's!\\!\\\\!g' $< > $@ -# Don't update the files from bitkeeper -%::SCCS/s.% +# a hack to have executable in builddir, not in srcdir +tests-t: tests-t.pl + cp -f $(srcdir)/tests-t.pl ./tests-t + diff --git a/dbug/dbug.c b/dbug/dbug.c index 1ea160852eb..0a0c56170fa 100644 --- a/dbug/dbug.c +++ b/dbug/dbug.c @@ -57,29 +57,48 @@ * 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") - * - * DBUG_EXECUTE_IF - * incremental mode (-#+t:-d,info ...) - * DBUG_SET, _db_explain_ - * thread-local settings + * 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 @@ -95,21 +114,24 @@ * 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 TRACE_ON 000001 /* Trace enabled */ -#define DEBUG_ON 000002 /* Debug enabled */ -#define FILE_ON 000004 /* File name print enabled */ -#define LINE_ON 000010 /* Line number print enabled */ -#define DEPTH_ON 000020 /* Function nest level print enabled */ -#define PROCESS_ON 000040 /* Process name print enabled */ -#define NUMBER_ON 000100 /* Number each line of output */ -#define PROFILE_ON 000200 /* Print out profiling code */ -#define PID_ON 000400 /* Identify each line with process id */ -#define TIMESTAMP_ON 001000 /* timestamp every line of output */ -#define SANITY_CHECK_ON 002000 /* Check safemalloc on DBUG_ENTER */ -#define FLUSH_ON_WRITE 004000 /* Flush on every write */ -#define OPEN_APPEND 010000 /* Open for append */ +#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) @@ -119,11 +141,7 @@ * Typedefs to make things more obvious. */ -#ifndef __WIN__ -typedef int BOOLEAN; -#else -#define BOOLEAN BOOL -#endif +#define BOOLEAN my_bool /* * Make it easy to change storage classes if necessary. @@ -139,7 +157,7 @@ typedef int BOOLEAN; * (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. + * 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, @@ -154,6 +172,8 @@ typedef int BOOLEAN; #ifdef M_I386 /* predefined by xenix 386 compiler */ #define AUTOS_REVERSE 1 +#else +#define AUTOS_REVERSE 0 #endif /* @@ -164,7 +184,11 @@ typedef int BOOLEAN; 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 @@ -174,9 +198,18 @@ IMPORT int _sanity(const char *file,uint line); /* safemalloc sanity checker */ struct link { struct link *next_link; /* Pointer to the next link */ - char str[1]; /* Pointer to link's contents */ + 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 @@ -188,18 +221,18 @@ struct link { */ struct settings { - int flags; /* Current settings flags */ - int maxdepth; /* Current maximum trace depth */ - uint delay; /* Delay after each output line */ - int 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 */ + 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) @@ -212,18 +245,19 @@ struct settings { 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 */ - char **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 */ - int level; /* Current function nesting level */ - int jmplevel; /* Remember nesting level at setjmp() */ + 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 @@ -244,12 +278,16 @@ typedef struct _db_code_state_ { The test below is so we could call functions with DBUG_ENTER before my_thread_init(). */ -#define get_code_state_or_return if (!cs && !((cs=code_state()))) return +#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 */ -static struct link *ListAdd(struct link *, const char *, const char *); -static struct link *ListDel(struct link *, const char *, const char *); +#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 */ @@ -260,10 +298,18 @@ 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 BOOLEAN DoTrace(CODE_STATE *cs); +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)) +#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); @@ -275,10 +321,10 @@ 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 BOOLEAN InList(struct link *linkp,const char *cp); -static void dbug_flush(CODE_STATE *); +static void DbugFlush(CODE_STATE *); static void DbugExit(const char *why); static const char *DbugStrTok(const char *s); +static void DbugVfprintf(FILE *stream, const char* format, va_list args); #ifndef THREAD /* Open profile output stream */ @@ -293,7 +339,7 @@ static unsigned long Clock(void); * Miscellaneous printf format strings. */ -#define ERR_MISSING_RETURN "%s: missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n" +#define ERR_MISSING_RETURN "missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n" #define ERR_OPEN "%s: can't open debug output stream \"%s\": " #define ERR_CLOSE "%s: can't close debug file: " #define ERR_ABORT "%s: debugger aborting because %s\n" @@ -322,34 +368,39 @@ static unsigned long Clock(void); #ifdef THREAD #include <my_pthread.h> -pthread_mutex_t THR_LOCK_dbug; +static pthread_mutex_t THR_LOCK_dbug; static CODE_STATE *code_state(void) { - CODE_STATE *cs=0; - struct st_my_thread_var *tmp; + 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; - init_done=TRUE; } - if ((tmp=my_thread_var)) + if (!(cs_ptr= (CODE_STATE**) my_thread_var_dbug())) + return 0; /* Thread not initialised */ + if (!(cs= *cs_ptr)) { - if (!(cs=(CODE_STATE *) tmp->dbug)) - { - 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; - tmp->dbug= (void*) cs; - } + 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; } @@ -403,20 +454,19 @@ static CODE_STATE *code_state(void) void _db_process_(const char *name) { - CODE_STATE *cs=0; + 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 setting + * DbugParse parse control string and set current debugger settings * * DESCRIPTION * @@ -438,15 +488,17 @@ void _db_process_(const char *name) * * For convenience, any leading "-#" is stripped off. * + * RETURN + * 1 - a list of functions ("f" flag) was possibly changed + * 0 - a list of functions was not changed */ -static void DbugParse(CODE_STATE *cs, const char *control) +int DbugParse(CODE_STATE *cs, const char *control) { const char *end; - int rel=0; + int rel, f_used=0; struct settings *stack; - get_code_state_or_return; stack= cs->stack; if (control[0] == '-' && control[1] == '#') @@ -455,6 +507,7 @@ static void DbugParse(CODE_STATE *cs, const char *control) 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; @@ -497,7 +550,6 @@ static void DbugParse(CODE_STATE *cs, const char *control) { int c, sign= (*control == '+') ? 1 : (*control == '-') ? -1 : 0; if (sign) control++; - if (!rel) sign=0; c= *control++; if (*control == ',') control++; /* XXX when adding new cases here, don't forget _db_explain_ ! */ @@ -526,6 +578,7 @@ static void DbugParse(CODE_STATE *cs, const char *control) stack->delay= atoi(control); break; case 'f': + f_used= 1; if (sign < 0 && control == end) { if (!is_shared(stack,functions)) @@ -664,8 +717,111 @@ static void DbugParse(CODE_STATE *cs, const char *control) 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 @@ -683,19 +839,21 @@ static void DbugParse(CODE_STATE *cs, const char *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_(CODE_STATE *cs, const char *control) +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); - - DbugParse(cs, control); + if (DbugParse(cs, control)) + FixTraceFlags(old_fflags, cs); } - /* * FUNCTION * @@ -716,10 +874,25 @@ void _db_set_(CODE_STATE *cs, const char *control) void _db_push_(const char *control) { - CODE_STATE *cs=0; + CODE_STATE *cs; + uint old_fflags; get_code_state_or_return; + old_fflags=fflags(cs); PushState(cs); - DbugParse(cs, control); + 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); } /* @@ -765,15 +938,18 @@ void _db_set_init_(const char *control) void _db_pop_() { struct settings *discard; - CODE_STATE *cs=0; + uint old_fflags; + CODE_STATE *cs; get_code_state_or_return; discard= cs->stack; - if (discard->next != NULL) + if (discard != &init_settings) { + old_fflags=fflags(cs); cs->stack= discard->next; FreeState(cs, discard, 1); + FixTraceFlags(old_fflags, cs); } } @@ -798,11 +974,16 @@ void _db_pop_() buf=strnmov(buf, (S), len+1); \ if (buf >= end) goto overflow; \ } while (0) -#define list_to_buf(l) do { \ +#define list_to_buf(l, f) do { \ struct link *listp=(l); \ while (listp) \ { \ - str_to_buf(listp->str); \ + if (listp->flags & (f)) \ + { \ + str_to_buf(listp->str); \ + if (listp->flags & SUBDIR) \ + char_to_buf('/'); \ + } \ listp=listp->next_link; \ } \ } while (0) @@ -842,9 +1023,18 @@ void _db_pop_() #define op_list_to_buf(C, val, cond) do { \ if ((cond)) \ { \ + int f=ListFlags(val); \ colon_to_buf; \ char_to_buf((C)); \ - list_to_buf(val); \ + 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 { \ @@ -859,7 +1049,7 @@ int _db_explain_ (CODE_STATE *cs, char *buf, size_t len) { char *start=buf, *end=buf+len-4; - get_code_state_or_return *buf=0; + 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); @@ -927,15 +1117,11 @@ int _db_explain_init_(char *buf, size_t len) * * SYNOPSIS * - * VOID _db_enter_(_func_, _file_, _line_, - * _sfunc_, _sfile_, _slevel_, _sframep_) + * 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 - * char **_sfunc_; save previous _func_ - * char **_sfile_; save previous _file_ - * int *_slevel_; save previous nesting level - * char ***_sframep_; save previous frame pointer + * struct _db_stack_frame_ allocated on the caller's stack * * DESCRIPTION * @@ -959,55 +1145,66 @@ int _db_explain_init_(char *buf, size_t len) */ void _db_enter_(const char *_func_, const char *_file_, - uint _line_, const char **_sfunc_, const char **_sfile_, - uint *_slevel_, char ***_sframep_ __attribute__((unused))) + uint _line_, struct _db_stack_frame_ *_stack_frame_) { - int save_errno=errno; - CODE_STATE *cs=0; - get_code_state_or_return; + 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; - *_sfunc_= cs->func; - *_sfile_= cs->file; + _stack_frame_->func= cs->func; + _stack_frame_->file= cs->file; cs->func= _func_; cs->file= _file_; - *_slevel_= ++cs->level; + _stack_frame_->prev= cs->framep; + _stack_frame_->level= ++cs->level | framep_trace_flag(cs, cs->framep); + cs->framep= _stack_frame_; #ifndef THREAD - *_sframep_= cs->framep; - cs->framep= (char **) _sframep_; if (DoProfile(cs)) { long stackused; - if (*cs->framep == NULL) + if (cs->framep->prev == NULL) stackused= 0; else { - stackused= ((long)(*cs->framep)) - ((long)(cs->framep)); + stackused= (char*)(cs->framep->prev) - (char*)(cs->framep); stackused= stackused > 0 ? stackused : -stackused; } (void) fprintf(cs->stack->prof_file, PROF_EFMT , Clock(), cs->func); -#ifdef AUTOS_REVERSE - (void) fprintf(cs->stack->prof_file, PROF_SFMT, cs->framep, stackused, *_sfunc_); -#else (void) fprintf(cs->stack->prof_file, PROF_SFMT, (ulong) cs->framep, stackused, - cs->func); -#endif + AUTOS_REVERSE ? _stack_frame_->func : cs->func); (void) fflush(cs->stack->prof_file); } #endif - if (DoTrace(cs)) - { - 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); - dbug_flush(cs); /* This does a unlock */ - } -#ifdef SAFEMALLOC - if (cs->stack->flags & SANITY_CHECK_ON) - if (_sanity(_file_,_line_)) /* Check of safemalloc */ + 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; -#endif + 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; } @@ -1018,11 +1215,9 @@ void _db_enter_(const char *_func_, const char *_file_, * * SYNOPSIS * - * VOID _db_return_(_line_, _sfunc_, _sfile_, _slevel_) + * VOID _db_return_(_line_, _stack_frame_) * int _line_; current source line number - * char **_sfunc_; where previous _func_ is to be retrieved - * char **_sfile_; where previous _file_ is to be retrieved - * int *_slevel_; where previous level was stashed + * struct _db_stack_frame_ allocated on the caller's stack * * DESCRIPTION * @@ -1033,52 +1228,47 @@ void _db_enter_(const char *_func_, const char *_file_, * */ -/* helper macro */ -void _db_return_(uint _line_, const char **_sfunc_, - const char **_sfile_, uint *_slevel_) +void _db_return_(uint _line_, struct _db_stack_frame_ *_stack_frame_) { int save_errno=errno; - CODE_STATE *cs=0; + uint _slevel_= _stack_frame_->level & ~TRACE_ON; + CODE_STATE *cs; get_code_state_or_return; - if (cs->level != (int) *_slevel_) + if (cs->framep != _stack_frame_) { - if (!cs->locked) - pthread_mutex_lock(&THR_LOCK_dbug); - (void) fprintf(cs->stack->out_file, ERR_MISSING_RETURN, cs->process, - cs->func); - dbug_flush(cs); + char buf[512]; + my_snprintf(buf, sizeof(buf), ERR_MISSING_RETURN, cs->func); + DbugExit(buf); } - else - { -#ifdef SAFEMALLOC - if (cs->stack->flags & SANITY_CHECK_ON) - { - if (_sanity(*_sfile_,_line_)) - cs->stack->flags &= ~SANITY_CHECK_ON; - } -#endif #ifndef THREAD - if (DoProfile(cs)) - (void) fprintf(cs->stack->prof_file, PROF_XFMT, Clock(), cs->func); + if (DoProfile(cs)) + (void) fprintf(cs->stack->prof_file, PROF_XFMT, Clock(), cs->func); #endif - if (DoTrace(cs)) + 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); - dbug_flush(cs); + DbugFlush(cs); } } - cs->level= *_slevel_-1; - cs->func= *_sfunc_; - cs->file= *_sfile_; -#ifndef THREAD + /* + 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= (char **) *cs->framep; -#endif + cs->framep= cs->framep->prev; errno=save_errno; } @@ -1105,7 +1295,7 @@ void _db_return_(uint _line_, const char **_sfunc_, void _db_pargs_(uint _line_, const char *keyword) { - CODE_STATE *cs=0; + CODE_STATE *cs; get_code_state_or_return; cs->u_line= _line_; cs->u_keyword= keyword; @@ -1141,13 +1331,12 @@ void _db_pargs_(uint _line_, const char *keyword) void _db_doprnt_(const char *format,...) { va_list args; - - CODE_STATE *cs=0; + CODE_STATE *cs; get_code_state_or_return; va_start(args,format); - if (_db_keyword_(cs, cs->u_keyword)) + if (_db_keyword_(cs, cs->u_keyword, 0)) { int save_errno=errno; if (!cs->locked) @@ -1158,14 +1347,25 @@ void _db_doprnt_(const char *format,...) else (void) fprintf(cs->stack->out_file, "%s: ", cs->func); (void) fprintf(cs->stack->out_file, "%s: ", cs->u_keyword); - (void) vfprintf(cs->stack->out_file, format, args); - (void) fputc('\n',cs->stack->out_file); - dbug_flush(cs); + DbugVfprintf(cs->stack->out_file, format, args); + DbugFlush(cs); errno=save_errno; } va_end(args); } +/* + * vfprintf clone with consistent, platform independent output for + * problematic formats like %p, %zd and %lld. + */ +static void DbugVfprintf(FILE *stream, const char* format, va_list args) +{ + char cvtbuf[1024]; + size_t len; + len = my_vsnprintf(cvtbuf, sizeof(cvtbuf), format, args); + (void) fprintf(stream, "%s\n", cvtbuf); +} + /* * FUNCTION @@ -1182,19 +1382,17 @@ void _db_doprnt_(const char *format,...) * * DESCRIPTION * Dump N characters in a binary array. - * Is used to examine corrputed memory or arrays. + * Is used to examine corrupted memory or arrays. */ void _db_dump_(uint _line_, const char *keyword, const unsigned char *memory, size_t length) { int pos; - char dbuff[90]; - - CODE_STATE *cs=0; + CODE_STATE *cs; get_code_state_or_return; - if (_db_keyword_(cs, keyword)) + if (_db_keyword_(cs, keyword, 0)) { if (!cs->locked) pthread_mutex_lock(&THR_LOCK_dbug); @@ -1208,9 +1406,8 @@ void _db_dump_(uint _line_, const char *keyword, { fprintf(cs->stack->out_file, "%s: ", cs->func); } - sprintf(dbuff,"%s: Memory: 0x%lx Bytes: (%ld)\n", + (void) fprintf(cs->stack->out_file, "%s: Memory: 0x%lx Bytes: (%ld)\n", keyword, (ulong) memory, (long) length); - (void) fputs(dbuff,cs->stack->out_file); pos=0; while (length-- > 0) @@ -1226,7 +1423,7 @@ void _db_dump_(uint _line_, const char *keyword, fputc(' ',cs->stack->out_file); } (void) fputc('\n',cs->stack->out_file); - dbug_flush(cs); + DbugFlush(cs); } } @@ -1234,93 +1431,75 @@ void _db_dump_(uint _line_, const char *keyword, /* * FUNCTION * - * ListAdd add to the list modifiers from debug control string - * - * SYNOPSIS - * - * static struct link *ListAdd(listp, ctlp, end) - * struct link *listp; - * char *ctlp; - * char *end; + * 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 adds it to listp, returning a pointer - * to the new list + * parses the list, and modifies "listp", returning a pointer + * to the new list. * - * 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. + * The mode of operation is defined by "todo" parameter. * - */ - -static struct link *ListAdd(struct link *head, - const char *ctlp, const char *end) -{ - const char *start; - struct link *new_malloc; - int len; - - while (ctlp < end) - { - start= ctlp; - while (ctlp < end && *ctlp != ',') - ctlp++; - len=ctlp-start; - new_malloc= (struct link *) DbugMalloc(sizeof(struct link)+len); - memcpy(new_malloc->str, start, len); - new_malloc->str[len]=0; - new_malloc->next_link= head; - head= new_malloc; - ctlp++; - } - return head; -} - -/* - * FUNCTION - * - * ListDel remove from the list modifiers in debug control string - * - * SYNOPSIS - * - * static struct link *ListDel(listp, ctlp, end) - * struct link *listp; - * char *ctlp; - * char *end; - * - * DESCRIPTION - * - * Given pointer to a comma separated list of strings in "cltp", - * parses the list, and removes these strings from the listp, - * returning a pointer to the new list. + * 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 *ListDel(struct link *head, - const char *ctlp, const char *end) +static struct link *ListAddDel(struct link *head, const char *ctlp, + const char *end, int todo) { const char *start; struct link **cur; - int len; + size_t len; + int subdir; - while (ctlp < end) + ctlp--; +next: + while (++ctlp < end) { start= ctlp; + subdir=0; while (ctlp < end && *ctlp != ',') ctlp++; len=ctlp-start; - cur=&head; - do + if (start[len-1] == '/') { - while (*cur && !strncmp((*cur)->str, start, len)) + len--; + subdir=SUBDIR; + } + if (len == 0) continue; + for (cur=&head; *cur; cur=&((*cur)->next_link)) + { + if (!strncmp((*cur)->str, start, len)) { - struct link *delme=*cur; - *cur=(*cur)->next_link; - free((void*) delme); + 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; } - } while (*cur && *(cur=&((*cur)->next_link))); + } + *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; } @@ -1352,7 +1531,7 @@ static struct link *ListCopy(struct link *orig) { struct link *new_malloc; struct link *head; - int len; + size_t len; head= NULL; while (orig != NULL) @@ -1361,6 +1540,7 @@ static struct link *ListCopy(struct link *orig) 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; @@ -1373,47 +1553,52 @@ static struct link *ListCopy(struct link *orig) * * InList test a given string for member of a given list * - * SYNOPSIS - * - * static BOOLEAN InList(linkp, cp) - * struct link *linkp; - * char *cp; - * * 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 then the string is treated - * as if it is in the list (I.E all strings are in the null list). + * 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 BOOLEAN InList(struct link *linkp, const char *cp) +static int InList(struct link *linkp, const char *cp) { - REGISTER struct link *scan; - REGISTER BOOLEAN result; + int result; - if (linkp == NULL) - result= TRUE; - else + for (result=MATCHED; linkp != NULL; linkp= linkp->next_link) { - result= FALSE; - for (scan= linkp; scan != NULL; scan= scan->next_link) - { - if (!strcmp(scan->str, cp)) - { - result= TRUE; - break; - } - } + 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 @@ -1440,8 +1625,8 @@ 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; - new_malloc->out_file= NULL; cs->stack= new_malloc; } @@ -1472,11 +1657,17 @@ static void FreeState(CODE_STATE *cs, struct settings *state, int free_state) FreeList(state->processes); if (!is_shared(state, p_functions)) FreeList(state->p_functions); + if (!is_shared(state, out_file)) DBUGCloseFile(cs, state->out_file); - (void) fflush(cs->stack->out_file); - if (state->prof_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); } @@ -1503,8 +1694,12 @@ void _db_end_() { struct settings *discard; static struct settings tmp; - CODE_STATE *cs=0; - + 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)) @@ -1538,25 +1733,30 @@ void _db_end_() * * DoTrace check to see if tracing is current enabled * - * SYNOPSIS - * - * static BOOLEAN DoTrace(stack) - * * DESCRIPTION * - * Checks to see if tracing is enabled based on whether the - * user has specified tracing, the maximum trace depth has - * not yet been reached, the current function is selected, - * and the current process is selected. Returns TRUE if - * tracing is enabled, FALSE otherwise. + * 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 BOOLEAN DoTrace(CODE_STATE *cs) +static int DoTrace(CODE_STATE *cs) { - return (TRACING && cs->level <= cs->stack->maxdepth && - InList(cs->stack->functions, cs->func) && - InList(cs->stack->processes, cs->process)); + 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; } @@ -1584,62 +1784,27 @@ static BOOLEAN DoProfile(CODE_STATE *cs) { return PROFILING && cs->level <= cs->stack->maxdepth && - InList(cs->stack->p_functions, cs->func) && - InList(cs->stack->processes, cs->process); + 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=0; + CODE_STATE *cs; get_code_state_or_return NULL; return cs->stack->out_file; } - -/* - * FUNCTION - * - * _db_strict_keyword_ test keyword for member of keyword list - * - * SYNOPSIS - * - * BOOLEAN _db_strict_keyword_(keyword) - * char *keyword; - * - * DESCRIPTION - * - * Similar to _db_keyword_, but keyword is NOT accepted if keyword list - * is empty. Used in DBUG_EXECUTE_IF() - for actions that must not be - * executed by default. - * - * Returns TRUE if keyword accepted, FALSE otherwise. - * - */ - -BOOLEAN _db_strict_keyword_(const char *keyword) -{ - CODE_STATE *cs=0; - get_code_state_or_return FALSE; - if (!DEBUGGING || cs->stack->keywords == NULL) - return FALSE; - return _db_keyword_(cs, keyword); -} - /* * FUNCTION * * _db_keyword_ test keyword for member of keyword list * - * SYNOPSIS - * - * BOOLEAN _db_keyword_(keyword) - * char *keyword; - * * DESCRIPTION * * Test a keyword to determine if it is in the currently active - * keyword list. As with the function list, a keyword is accepted + * 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 @@ -1651,36 +1816,13 @@ BOOLEAN _db_strict_keyword_(const char *keyword) * */ -BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword) -{ - get_code_state_or_return FALSE; - - return (DEBUGGING && - (!TRACING || cs->level <= cs->stack->maxdepth) && - InList(cs->stack->functions, cs->func) && - InList(cs->stack->keywords, keyword) && - InList(cs->stack->processes, cs->process)); -} - -/* - * FUNCTION - * - * _db_keywords_ test keyword formed by a set of strings for member - * of keyword list - * - * DESCRIPTION - * - * This function is similar to _db_keyword but receives a set of strings to - * be concatenated in order to make the keyword to be compared. - */ - -BOOLEAN _db_keywords_(const char *function, const char *type) +BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict) { - char dest[_DBUG_MAX_FUNC_NAME_ + 1]; - - strxnmov(dest, _DBUG_MAX_FUNC_NAME_, function, type, NULL); + get_code_state_if_not_set_or_return FALSE; + strict=strict ? INCLUDE : INCLUDE|MATCHED; - return _db_strict_keyword_(dest); + return DEBUGGING && DoTrace(cs) & DO_TRACE && + InList(cs->stack->keywords, keyword) & strict; } /* @@ -1848,7 +1990,7 @@ static void DBUGOpenFile(CODE_STATE *cs, { if (end) { - int len=end-name; + size_t len=end-name; memcpy(cs->stack->name, name, len); cs->stack->name[len]=0; } @@ -1971,12 +2113,12 @@ static FILE *OpenProfile(CODE_STATE *cs, const char *name) static void DBUGCloseFile(CODE_STATE *cs, FILE *fp) { - if (fp != stderr && fp != stdout && fclose(fp) == EOF) + 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(""); - dbug_flush(cs); + DbugFlush(cs); } } @@ -2005,7 +2147,7 @@ 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); + DBUG_ABORT(); } @@ -2193,7 +2335,7 @@ static void ChangeOwner(CODE_STATE *cs, char *pathname) EXPORT void _db_setjmp_() { - CODE_STATE *cs=0; + CODE_STATE *cs; get_code_state_or_return; cs->jmplevel= cs->level; @@ -2220,7 +2362,7 @@ EXPORT void _db_setjmp_() EXPORT void _db_longjmp_() { - CODE_STATE *cs=0; + CODE_STATE *cs; get_code_state_or_return; cs->level= cs->jmplevel; @@ -2271,11 +2413,9 @@ char *s; /* This is because some systems (MSDOS!!) dosn't flush fileheader */ /* and dbug-file isn't readable after a system crash !! */ -static void dbug_flush(CODE_STATE *cs) +static void DbugFlush(CODE_STATE *cs) { -#ifndef THREAD if (cs->stack->flags & FLUSH_ON_WRITE) -#endif { (void) fflush(cs->stack->out_file); if (cs->stack->delay) @@ -2283,12 +2423,22 @@ static void dbug_flush(CODE_STATE *cs) } if (!cs->locked) pthread_mutex_unlock(&THR_LOCK_dbug); -} /* dbug_flush */ +} /* 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=0; + CODE_STATE *cs; get_code_state_or_return; pthread_mutex_lock(&THR_LOCK_dbug); cs->locked=1; @@ -2296,7 +2446,7 @@ void _db_lock_file_() void _db_unlock_file_() { - CODE_STATE *cs=0; + CODE_STATE *cs; get_code_state_or_return; cs->locked=0; pthread_mutex_unlock(&THR_LOCK_dbug); @@ -2304,7 +2454,7 @@ void _db_unlock_file_() const char* _db_get_func_(void) { - CODE_STATE *cs= 0; + CODE_STATE *cs; get_code_state_or_return NULL; return cs->func; } diff --git a/dbug/dbug_add_tags.pl b/dbug/dbug_add_tags.pl index 141a2ed85f1..3e51a54c707 100755 --- a/dbug/dbug_add_tags.pl +++ b/dbug/dbug_add_tags.pl @@ -7,7 +7,7 @@ $ctags="exctags -x -f - --c-types=f -u"; sub get_tag { local $.; local $_=<TAGS>; ($symbol, $line)= /^(.*\S)\s+function\s+(\d+)/; - $symbol=$1 if /\s(\S+)\s*\(/; + $symbol=$1 if /[\s*]([^\s*]+)\s*\(/; $line=1e50 unless $line; } @@ -51,7 +51,7 @@ while($src=shift) $skip=!$semicolon; $semicolon= /;\s*$/; print && next if $skip || - (/^\s+\w+((::\w+)?|<\w+>)\s+\**\w+/ && !/^\s*return/); + (/^\s+\w+((::\w+)?|<\w+>)\s+\**\w+/ && !/^\s*return\b/); last if /DBUG_ENTER/; print "$tab DBUG_ENTER(\"$symbol\");\n"; print "\n" unless $_ eq "\n"; diff --git a/dbug/doinstall.sh b/dbug/doinstall.sh deleted file mode 100644 index 707f193c761..00000000000 --- a/dbug/doinstall.sh +++ /dev/null @@ -1,15 +0,0 @@ - -# Warning - first line left blank for sh/csh/ksh compatibility. Do not -# remove it. fnf@Unisoft - -# doinstall.sh --- figure out environment and do recursive make with -# appropriate pathnames. Works under SV or BSD. - -if [ -r /usr/include/search.h ] -then - # System V - $* LLIB=/usr/lib -else - # 4.2 BSD - $* LLIB=/usr/lib/lint -fi diff --git a/dbug/install.sh b/dbug/install.sh deleted file mode 100644 index 7226e01b1cf..00000000000 --- a/dbug/install.sh +++ /dev/null @@ -1,64 +0,0 @@ - -# WARNING -- first line intentionally left blank for sh/csh/ksh -# compatibility. Do not remove it! FNF, UniSoft Systems. -# -# Usage is: -# install <from> <to> -# -# The file <to> is replaced with the file <from>, after first -# moving <to> to a backup file. The backup file name is created -# by prepending the filename (after removing any leading pathname -# components) with "OLD". -# -# This script is currently not real robust in the face of signals -# or permission problems. It also does not do (by intention) all -# the things that the System V or BSD install scripts try to do -# - -if [ $# -ne 2 ] -then - echo "usage: $0 <from> <to>" - exit 1 -fi - -# Now extract the dirname and basename components. Unfortunately, BSD does -# not have dirname, so we do it the hard way. - -fd=`expr $1'/' : '\(/\)[^/]*/$' \| $1'/' : '\(.*[^/]\)//*[^/][^/]*//*$' \| .` -ff=`basename $1` -td=`expr $2'/' : '\(/\)[^/]*/$' \| $2'/' : '\(.*[^/]\)//*[^/][^/]*//*$' \| .` -tf=`basename $2` - -# Now test to make sure that they are not the same files. - -if [ $fd/$ff = $td/$tf ] -then - echo "install: input and output are same files" - exit 2 -fi - -# Save a copy of the "to" file as a backup. - -if test -f $td/$tf -then - if test -f $td/OLD$tf - then - rm -f $td/OLD$tf - fi - mv $td/$tf $td/OLD$tf - if [ $? != 0 ] - then - exit 3 - fi -fi - -# Now do the copy and return appropriate status - -cp $fd/$ff $td/$tf -if [ $? != 0 ] -then - exit 4 -else - exit 0 -fi - diff --git a/dbug/mklintlib.sh b/dbug/mklintlib.sh deleted file mode 100644 index 6963016f334..00000000000 --- a/dbug/mklintlib.sh +++ /dev/null @@ -1,30 +0,0 @@ - -# Warning - first line left blank for sh/csh/ksh compatibility. Do not -# remove it. fnf@Unisoft - -# mklintlib --- make a lint library, under either System V or 4.2 BSD -# -# usage: mklintlib <infile> <outfile> -# - -if test $# -ne 2 -then - echo "usage: mklintlib <infile> <outfile>" - exit 1 -fi - -if grep SIGTSTP /usr/include/signal.h >/dev/null -then # BSD - if test -r /usr/include/whoami.h # 4.1 - then - /lib/cpp -C -Dlint $1 >hlint - (/usr/lib/lint/lint1 <hlint >$2) 2>&1 | grep -v warning - else # 4.2 - lint -Cxxxx $1 - mv llib-lxxxx.ln $2 - fi -else # USG - cc -E -C -Dlint $1 | /usr/lib/lint1 -vx -Hhlint >$2 - rm -f hlint -fi -exit 0 # don't kill make diff --git a/dbug/qmake.cmd b/dbug/qmake.cmd deleted file mode 100644 index ebd4432f7fc..00000000000 --- a/dbug/qmake.cmd +++ /dev/null @@ -1,4 +0,0 @@ -CL -I\my\include -AL -Gsm2 -FPi -DDBUG_OFF *.c -rm \my\lib\dbug.lib -lib.exe \my\lib\dbug dbug.obj sanity.obj; -link /NOD /STACK:8000 main factoria,factoria,,DBUG+STRINGS+LLIBCEP+DOSCALLS; diff --git a/dbug/remove_function_from_trace.pl b/dbug/remove_function_from_trace.pl new file mode 100755 index 00000000000..1da9e25f9ba --- /dev/null +++ b/dbug/remove_function_from_trace.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl + + +die <<EEE unless @ARGV; +Usage: $0 func1 [func2 [ ...] ] + +This filter (stdin->stdout) removes lines from dbug trace that were generated +by specified functions and all functions down the call stack. Produces the +same effect as if the original source had DBUG_PUSH(""); right after +DBUG_ENTER() and DBUG_POP(); right before DBUG_RETURN in every such a function. +EEE + +$re=join('|', @ARGV); +$skip=''; + +while(<STDIN>) { + print unless $skip; + next unless /^(?:.*: )*((?:\| )*)([<>])($re)\n/o; + if ($2 eq '>') { + $skip=$1.$3 unless $skip; + next; + } + next if $skip ne $1.$3; + $skip=''; + print; +} diff --git a/dbug/tests-t.pl b/dbug/tests-t.pl new file mode 100755 index 00000000000..de9ed6f6ab9 --- /dev/null +++ b/dbug/tests-t.pl @@ -0,0 +1,496 @@ +#!/usr/bin/env perl + +# +# A driver program to test DBUG features - runs tests (shell commands) +# from the end of file to invoke tests.c, which does the real dbug work. +# + +use Test::More; + +$exe=$0; + +die unless $exe =~ s/(tests)-t(\.exe)?$/$1$2 /; + +# load tests +@tests=(); +while (<DATA>) { + if (/^% \.\/tests /) { + push @tests, [ $' ] + } elsif (/^#/) { + next; + } else { + push @{$tests[$#tests]}, $_ + } +} + +plan skip_all => "because dbug is disabled" if system $exe; + +plan tests => scalar(@tests); + +for (@tests) { + $t=$exe . shift @$_; + chomp($t); + open F, '-|', $t or die "open($t|): $!"; + local $"; + $out=join($", <F>); close(F); + # special cases are handled here: + $out =~ s/Memory: 0x[0-9A-Fa-f]+/Memory: 0x####/g if $t =~ /dump/; + # compare ("\n" at the beginning makes better output in case of errors) + is("\n$out","\n@$_", $t); +} + +__DATA__ +% ./tests -#d +func2: info: s=ok +=> execute +=> evaluate: ON +=> evaluate_if: OFF +main: explain: dbug explained: d +func2: info: s=ok +% ./tests d,ret3 +=> evaluate: OFF +=> evaluate_if: OFF +# +## Testing negative lists +# +% ./tests d:-d,ret3 +func2: info: s=ko +=> execute +=> evaluate: ON +=> evaluate_if: OFF +main: explain: dbug explained: d:-d,ret3 +func2: info: s=ko +% ./tests t:-d,ret3 +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +% ./tests t:d,info:-d,ret3 +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ko +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| | info: s=ko +| <func2 +<main +% ./tests t:d,info:-d,ret3:-f,func2 +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| | >func3 +| | <func3 +<main +% ./tests t:d,info:-d,ret3:-f,func2 d,evaluate +=> evaluate: ON +=> evaluate_if: OFF +% ./tests t:d,info:-d,ret3:-f,func2 d,evaluate_if +=> evaluate: OFF +=> evaluate_if: ON +% ./tests t:d:-d,ret3:-f,func2 d,evaluate_if +=> evaluate: OFF +=> evaluate_if: ON +% ./tests t:d:-d,ret3:-f,func2 +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +=> execute +=> evaluate: ON +=> evaluate_if: OFF +| explain: dbug explained: d:-d,ret3:f:-f,func2:t +| | >func3 +| | <func3 +<main +# +## Adding incremental settings to the brew +# +% ./tests t:d:-d,ret3:-f,func2 +d,evaluate_if +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +=> evaluate: OFF +=> evaluate_if: ON +| | >func3 +| | <func3 +<main +# +## DBUG_DUMP +# +% ./tests t:d:-d,ret3:f:-f,func2 +d,dump +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +| dump: Memory: 0x#### Bytes: (27) +64 2C 64 75 6D 70 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D 66 2C 66 75 6E 63 32 3A +74 +=> evaluate: OFF +=> evaluate_if: OFF +| | >func3 +| | <func3 +<main +% ./tests t:d:-d,ret3:f:-f,func2 +d,dump +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +| dump: Memory: 0x#### Bytes: (27) +64 2C 64 75 6D 70 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D 66 2C 66 75 6E 63 32 3A +74 +=> evaluate: OFF +=> evaluate_if: OFF +| | >func3 +| | <func3 +<main +% ./tests t:d:-d,ret3:f:-f,func2:+d,dump +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +| dump: Memory: 0x#### Bytes: (27) +64 2C 64 75 6D 70 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D 66 2C 66 75 6E 63 32 3A +74 +=> evaluate: OFF +=> evaluate_if: OFF +| | >func3 +| | <func3 +<main +% ./tests t:d:-d,ret3:f:-f,func2 +d,dump,explain +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +| dump: Memory: 0x#### Bytes: (35) +64 2C 64 75 6D 70 2C 65 78 70 6C 61 69 6E 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D +66 2C 66 75 6E 63 32 3A 74 +=> evaluate: OFF +=> evaluate_if: OFF +| explain: dbug explained: d,dump,explain:-d,ret3:f:-f,func2:t +| | >func3 +| | <func3 +<main +% ./tests t:d:-d,ret3:f:-f,func2 +d,dump,explain:P +dbug: >main +dbug-tests: | >func1 +dbug-tests: | | | >func3 +dbug-tests: | | | <func3 +dbug-tests: | <func1 +dbug-tests: | dump: Memory: 0x#### Bytes: (37) +64 2C 64 75 6D 70 2C 65 78 70 6C 61 69 6E 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D +66 2C 66 75 6E 63 32 3A 50 3A 74 +=> evaluate: OFF +=> evaluate_if: OFF +dbug-tests: | explain: dbug explained: d,dump,explain:-d,ret3:f:-f,func2:P:t +dbug-tests: | | >func3 +dbug-tests: | | <func3 +dbug-tests: <main +% ./tests t:d:-d,ret3:f:-f,func2 +d,dump,explain:P:F +dbug: tests.c: >main +dbug-tests: tests.c: | >func1 +dbug-tests: tests.c: | | | >func3 +dbug-tests: tests.c: | | | <func3 +dbug-tests: tests.c: | <func1 +dbug-tests: tests.c: | dump: Memory: 0x#### Bytes: (39) +64 2C 64 75 6D 70 2C 65 78 70 6C 61 69 6E 3A 2D 64 2C 72 65 74 33 3A 66 3A 2D +66 2C 66 75 6E 63 32 3A 46 3A 50 3A 74 +=> evaluate: OFF +=> evaluate_if: OFF +dbug-tests: tests.c: | explain: dbug explained: d,dump,explain:-d,ret3:f:-f,func2:F:P:t +dbug-tests: tests.c: | | >func3 +dbug-tests: tests.c: | | <func3 +dbug-tests: tests.c: <main +# +## DBUG_EXPLAIN, DBUG_PUSH, DBUG_POP, DBUG_SET +# +% ./tests t:d:-d,ret3:f:-f,func2 +>main +| >func1 +| | | >func3 +| | | <func3 +| <func1 +=> execute +=> evaluate: ON +=> evaluate_if: OFF +| explain: dbug explained: d:-d,ret3:f:-f,func2:t +| | >func3 +| | <func3 +<main +% ./tests t:d:-d,ret3 +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ko +| | <func2 +| <func1 +=> execute +=> evaluate: ON +=> evaluate_if: OFF +| explain: dbug explained: d:-d,ret3:t +| >func2 +| | >func3 +| | <func3 +| | info: s=ko +| <func2 +<main +% ./tests d,info:-d,ret3:d,push +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| | info: s=ko +| <func2 +<main +% ./tests d,info:-d,ret3:d,push,explain +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF +| explain: dbug explained: d,info,push,explain:-d,ret3:t +| >func2 +| | >func3 +| | <func3 +| | info: s=ko +| <func2 +<main +% ./tests d,info:-d,ret3:d,explain +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF +main: explain: dbug explained: d,info,explain:-d,ret3 +func2: info: s=ko +% ./tests d,info:-d,ret3:d,explain,pop +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF +% ./tests d,info:-d,ret3:d,explain t:d,pop +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +main: explain: dbug explained: d,info,explain:-d,ret3 +func2: info: s=ko +% ./tests d,info:-d,ret3:d,explain,pop +t +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ko +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +main: explain: dbug explained: d,info,explain,pop:-d,ret3 +func2: info: s=ko +% ./tests d,info:-d,ret3:d,explain,set +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF + tests.c: main: explain: dbug explained: d,info,explain,set:-d,ret3:F + tests.c: func2: info: s=ko +% ./tests d,info:-d,ret3:d,explain,set:t +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ko +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF + tests.c: | explain: dbug explained: d,info,explain,set:-d,ret3:F:t + tests.c: | >func2 + tests.c: | | >func3 + tests.c: | | <func3 + tests.c: | | info: s=ko + tests.c: | <func2 + tests.c: <main +% ./tests t d,info:-d,ret3:d,explain,set:t +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ko +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF + tests.c: | explain: dbug explained: d,info,explain,set:-d,ret3:F:t + tests.c: | >func2 + tests.c: | | >func3 + tests.c: | | <func3 + tests.c: | | info: s=ko + tests.c: | <func2 + tests.c: <main +% ./tests t d,info:-d,ret3:d,explain,set,pop +func2: info: s=ko +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +% ./tests t:f,func2 +| | >func2 +| | <func2 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| <func2 +# +## Testing SUBDIR rules +# +% ./tests t:-f,func2/:d +>main +| >func1 +| <func1 +=> execute +=> evaluate: ON +=> evaluate_if: OFF +| explain: dbug explained: d:f:-f,func2/:t +<main +% ./tests t:f,func1/:d +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | | info: s=ok +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +% ./tests t:f,main/:d,pop +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | <func2 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +% ./tests f,main/:d,push +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +# +## Testing FixTraceFlags() - when we need to traverse the call stack +# (these tests fail with FixTraceFlags() disabled) +# +# delete the INCLUDE rule up the stack +% ./tests t:f,func1/ --push1=t:f,func3/ +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | <func2 +=> push1 +=> evaluate: OFF +=> evaluate_if: OFF +| | >func3 +| | <func3 +# delete the EXCLUDE rule up the stack +% ./tests t:-f,func1/ --push1=t +>main +=> push1 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +# add the INCLUDE rule up the stack +% ./tests t:f,func3 --push1=t:f,main/ +| | | >func3 +| | | <func3 +=> push1 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +# add the EXCLUDE rule up the stack +% ./tests t --push1=t:-f,main/ +>main +| >func1 +| | >func2 +| | | >func3 +| | | <func3 +| | <func2 +=> push1 +=> evaluate: OFF +=> evaluate_if: OFF +# change the defaults +% ./tests t:f,func3 --push1=t +| | | >func3 +| | | <func3 +=> push1 +| <func1 +=> evaluate: OFF +=> evaluate_if: OFF +| >func2 +| | >func3 +| | <func3 +| <func2 +<main +# repeated keyword +% ./tests d:-d,info,info +=> execute +=> evaluate: ON +=> evaluate_if: OFF +main: explain: dbug explained: d:-d,info +% ./tests d:-d,info/,info +=> execute +=> evaluate: ON +=> evaluate_if: OFF +main: explain: dbug explained: d:-d,info/ diff --git a/dbug/tests.c b/dbug/tests.c new file mode 100644 index 00000000000..d76266d34a3 --- /dev/null +++ b/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); + } +} diff --git a/dbug/user.r b/dbug/user.r index ef67ef7a7cf..847ad80b30f 100644 --- a/dbug/user.r +++ b/dbug/user.r @@ -512,7 +512,7 @@ possible since all code preceding the first call to .B DBUG_PUSH is essentially invisible to -.B dbug +.I dbug (this can be worked around by inserting a temporary .B DBUG_PUSH(argv[1]) @@ -708,7 +708,7 @@ EX:\ \fCDBUG_PROCESS\ (argv[0]);\fR .SP 1 .LI DBUG_PUSH\ Sets a new debugger state by pushing the current -.B dbug +.I dbug state onto an internal stack and setting up the new state using the debug control string passed as the macro argument. The most common usage is to set the state specified by a debug control string @@ -769,14 +769,14 @@ the 'd' flag. Used to conditionally execute "dangerous" actions, e.g to crash the program testing how recovery works, or to introduce an artificial delay checking for race conditions. .SP 1 -EX:\ \fCDBUG_EXECUTE_IF\ ("crashme",\ abort\ ());\fR +EX:\ \fCDBUG_EXECUTE_IF\ ("crashme",\ DBUG_ABORT()\ ());\fR .SP 1 .LI DBUG_EVALUATE\ The DBUG_EVALUATE macro is similar to DBUG_EXECUTE, but it can be used in the expression context. The first argument is the debug keyword that is used to choose whether the second (keyword is enabled) or the third (keyword is not enabled) argument is evaluated. When -.B dbug +.I dbug is compiled off, the third argument is evaluated. .SP 1 EX:\fC @@ -796,7 +796,7 @@ EX:\fC .br if (prepare_transaction () || .br - DBUG_EVALUATE ("crashme", (abort (), 0), 0) || + DBUG_EVALUATE ("crashme", (DBUG_ABORT(), 0), 0) || .br commit_transaction () )\fR .SP 1 @@ -875,6 +875,12 @@ library. So there will be no need to disable asserts separately with NDEBUG. .SP 1 EX:\ \fCDBUG_ASSERT(\ a\ >\ 0\ );\fR .SP 1 +.LI DBUG_ABORT\ +This macro could be used instead of abort(). It flushes DBUG_FILE stream +to ensure that no +.I dbug +output is lost and then calls abort(). +.SP 1 .LI DBUG_EXPLAIN\ Generates control string corresponding to the current debug state. The macro takes two arguments - a buffer to store the result string @@ -910,17 +916,17 @@ via the .B DBUG_PUSH or .B DBUG_SET -macros. Control string consists of colon separate flags. Colons +macros. Control string consists of colon separated flags. Colons that are part of ':\\', ':/', or '::' are not considered flag separators. A flag may take an argument or a list of arguments. If a control string starts from a '+' sign it works .I incrementally, -that is, it can modify existing state without overriding it. In such a -string every flag may be preceded by a '+' or '-' to enable or disable -a corresponding option in the debugger state. This section summarizes -the currently available debugger options and the flag characters which -enable or disable them. Argument lists enclosed in '[' and ']' are -optional. +that is, it can modify existing state without overriding it. Every +flag may be preceded by a '+' or '-' to enable or disable a +corresponding option in the debugger state or to add or remove +arguments to the list. This section summarizes the currently available +debugger options and the flag characters which enable or disable them. +Argument lists enclosed in '[' and ']' are optional. .SP 2 .BL 22 .LI a[,file] @@ -936,6 +942,9 @@ a complete log file in case of crashes. This mode is implicit in multi-threaded environment. .LI d[,keywords] Enable output from macros with specified keywords. +Every keyword can be a +.I glob(7) +pattern. An empty list of keywords implies that all keywords are selected. .LI D[,time] Delay for specified time after each output line, to let output drain. @@ -943,7 +952,19 @@ Time is given in tenths of a second (value of 10 is one second). Default is zero. .LI f[,functions] Limit debugger actions to the specified list of functions. +Every function can be a +.I glob(7) +pattern. An empty list of functions implies that all functions are selected. +Every function in the list may optionally be followed by a '/' - +this will implicitly select all the functions down the call stack. +.SP 1 +EX: \fCf,func1,func2/:-f,func3,func4/\fR +.SP 1 +This would enable debugger in functions 'func1()', 'func2()' and all +functions called from it (directly or indirectly). But not in +functions 'func3()' or 'func4()' and all functions called from +it. .LI F Mark each debugger output line with the name of the source file containing the macro causing the output. @@ -952,6 +973,9 @@ 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 @@ -970,7 +994,11 @@ Like 'a[,file]' but overwrite old file, do not append. .LI O[,file] Like 'A[,file]' but overwrite old file, do not append. .LI p[,processes] -Limit debugger actions to the specified processes. An empty list +Limit debugger actions to the specified processes. +Every name can be a +.I glob(7) +pattern. +An empty list implies all processes. This is useful for processes which run child processes. Note that each debugger output line can be marked with the name of the current process via the 'P' flag. The process name must diff --git a/dbug/vargs.h b/dbug/vargs.h deleted file mode 100644 index 4609c8301bb..00000000000 --- a/dbug/vargs.h +++ /dev/null @@ -1,139 +0,0 @@ -/****************************************************************************** - * * - * 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 - * - * vargs.h include file for environments without varargs.h - * - * SCCS - * - * @(#)vargs.h 1.2 5/8/88 - * - * SYNOPSIS - * - * #include "vargs.h" - * - * DESCRIPTION - * - * This file implements a varargs macro set for use in those - * environments where there is no system supplied varargs. This - * generally works because systems which don't supply a varargs - * package are precisely those which don't strictly need a varargs - * package. Using this one then allows us to minimize source - * code changes. So in some sense, this is a "portable" varargs - * since it is only used for convenience, when it is not strictly - * needed. - * - */ - -/* - * These macros allow us to rebuild an argument list on the stack - * given only a va_list. We can use these to fake a function like - * vfprintf, which gets a fixed number of arguments, the last of - * which is a va_list, by rebuilding a stack and calling the variable - * argument form fprintf. Of course this only works when vfprintf - * is not available in the host environment, and thus is not available - * for fprintf to call (which would give us an infinite loop). - * - * Note that ARGS_TYPE is a long, which lets us get several bytes - * at a time while also preventing lots of "possible pointer alignment - * problem" messages from lint. The messages are valid, because this - * IS nonportable, but then we should only be using it in very - * nonrestrictive environments, and using the real varargs where it - * really counts. - * - */ - -#define ARG0 a0 -#define ARG1 a1 -#define ARG2 a2 -#define ARG3 a3 -#define ARG4 a4 -#define ARG5 a5 -#define ARG6 a6 -#define ARG7 a7 -#define ARG8 a8 -#define ARG9 a9 - -#define ARGS_TYPE long -#define ARGS_LIST ARG0,ARG1,ARG2,ARG3,ARG4,ARG5,ARG6,ARG7,ARG8,ARG9 -#define ARGS_DCL auto ARGS_TYPE ARGS_LIST - -/* - * A pointer of type "va_list" points to a section of memory - * containing an array of variable sized arguments of unknown - * number. This pointer is initialized by the va_start - * macro to point to the first byte of the first argument. - * We can then use it to walk through the argument list by - * incrementing it by the size of the argument being referenced. - */ - -typedef char *va_list; - -/* - * The first variable argument overlays va_alist, which is - * nothing more than a "handle" which allows us to get the - * address of the first argument on the stack. Note that - * by definition, the va_dcl macro includes the terminating - * semicolon, which makes use of va_dcl in the source code - * appear to be missing a semicolon. - */ - -#define va_dcl ARGS_TYPE va_alist; - -/* - * The va_start macro takes a variable of type "va_list" and - * initializes it. In our case, it initializes a local variable - * of type "pointer to char" to point to the first argument on - * the stack. - */ - -#define va_start(list) list = (char *) &va_alist - -/* - * The va_end macro is a null operation for our use. - */ - -#define va_end(list) - -/* - * The va_arg macro is the tricky one. This one takes - * a va_list as the first argument, and a type as the second - * argument, and returns a value of the appropriate type - * while advancing the va_list to the following argument. - * For our case, we first increment the va_list arg by the - * size of the type being recovered, cast the result to - * a pointer of the appropriate type, and then dereference - * that pointer as an array to get the previous arg (which - * is the one we wanted. - */ - -#define va_arg(list,type) ((type *) (list += sizeof (type)))[-1] diff --git a/include/Makefile.am b/include/Makefile.am index dd6f53f7ca2..0a7a35bd6fa 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -21,7 +21,8 @@ HEADERS_GEN_MAKE = my_config.h HEADERS_ABI = mysql.h mysql_com.h mysql_time.h \ my_list.h my_alloc.h typelib.h mysql/plugin.h pkginclude_HEADERS = $(HEADERS_ABI) my_dbug.h m_string.h my_sys.h \ - my_xml.h mysql_embed.h \ + my_xml.h mysql_embed.h mysql/services.h \ + mysql/service_my_snprintf.h mysql/service_thd_alloc.h \ my_pthread.h my_no_pthread.h \ decimal.h errmsg.h my_global.h my_net.h \ my_getopt.h sslopt-longopts.h my_dir.h \ @@ -36,7 +37,7 @@ noinst_HEADERS = config-win.h config-netware.h my_bit.h \ my_nosys.h my_alarm.h queues.h rijndael.h sha1.h \ my_aes.h my_tree.h my_trie.h hash.h thr_alarm.h \ thr_lock.h t_ctype.h violite.h my_md5.h base64.h \ - my_handler.h my_time.h \ + my_handler.h my_time.h service_versions.h \ my_vle.h my_user.h my_atomic.h atomic/nolock.h \ atomic/rwlock.h atomic/x86-gcc.h atomic/x86-msvc.h \ atomic/solaris.h \ diff --git a/include/m_string.h b/include/m_string.h index c24bfd7aa6c..3c4c6ea088a 100644 --- a/include/m_string.h +++ b/include/m_string.h @@ -250,16 +250,10 @@ extern size_t my_snprintf(char *to, size_t n, const char *fmt, ...) /* LEX_STRING -- a pair of a C-string and its length. + (it's part of the plugin API as a MYSQL_LEX_STRING) */ -#ifndef _my_plugin_h -/* This definition must match the one given in mysql/plugin.h */ -struct st_mysql_lex_string -{ - char *str; - size_t length; -}; -#endif +#include <mysql/plugin.h> typedef struct st_mysql_lex_string LEX_STRING; #define STRING_WITH_LEN(X) (X), ((size_t) (sizeof(X) - 1)) diff --git a/include/my_dbug.h b/include/my_dbug.h index 0451a868aa4..956a5504e86 100644 --- a/include/my_dbug.h +++ b/include/my_dbug.h @@ -16,103 +16,64 @@ #ifndef _dbug_h #define _dbug_h -#if defined(__cplusplus) && !defined(DBUG_OFF) -class Dbug_violation_helper -{ -public: - inline Dbug_violation_helper() : - _entered(TRUE) - { } - - inline ~Dbug_violation_helper() - { - assert(!_entered); - } - - inline void leave() - { - _entered= FALSE; - } - -private: - bool _entered; -}; -#endif /* C++ */ - -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif #if !defined(DBUG_OFF) && !defined(_lint) -struct _db_code_state_; -extern int _db_keyword_(struct _db_code_state_ *cs, const char *keyword); -extern int _db_keywords_(const char *, const char *); -extern int _db_strict_keyword_(const char *keyword); + +struct _db_stack_frame_ { + const char *func; /* function name of the previous stack frame */ + const char *file; /* filename of the function of previous frame */ + uint level; /* this nesting level, highest bit enables tracing */ + struct _db_stack_frame_ *prev; /* pointer to the previous frame */ +}; + +struct _db_code_state_; +extern my_bool _dbug_on_; +extern my_bool _db_keyword_(struct _db_code_state_ *, const char *, int); extern int _db_explain_(struct _db_code_state_ *cs, char *buf, size_t len); extern int _db_explain_init_(char *buf, size_t len); -extern void _db_setjmp_(void); -extern void _db_longjmp_(void); +extern int _db_is_pushed_(void); +extern void _db_setjmp_(void); +extern void _db_longjmp_(void); extern void _db_process_(const char *name); -extern void _db_push_(const char *control); -extern void _db_pop_(void); -extern void _db_set_(struct _db_code_state_ *cs, const char *control); +extern void _db_push_(const char *control); +extern void _db_pop_(void); +extern void _db_set_(const char *control); extern void _db_set_init_(const char *control); -extern void _db_enter_(const char *_func_,const char *_file_,uint _line_, - const char **_sfunc_,const char **_sfile_, - uint *_slevel_, char ***); -extern void _db_return_(uint _line_,const char **_sfunc_,const char **_sfile_, - uint *_slevel_); -extern void _db_pargs_(uint _line_,const char *keyword); -extern void _db_doprnt_ _VARARGS((const char *format,...)) +extern void _db_enter_(const char *_func_, const char *_file_, uint _line_, + struct _db_stack_frame_ *_stack_frame_); +extern void _db_return_(uint _line_, struct _db_stack_frame_ *_stack_frame_); +extern void _db_pargs_(uint _line_,const char *keyword); +extern void _db_doprnt_ _VARARGS((const char *format,...)) ATTRIBUTE_FORMAT(printf, 1, 2); -extern void _db_dump_(uint _line_,const char *keyword, +extern void _db_dump_(uint _line_,const char *keyword, const unsigned char *memory, size_t length); -extern void _db_end_(void); -extern void _db_lock_file_(void); -extern void _db_unlock_file_(void); -extern FILE *_db_fp_(void); +extern void _db_end_(void); +extern void _db_lock_file_(void); +extern void _db_unlock_file_(void); +extern FILE *_db_fp_(void); +extern void _db_flush_(); extern const char* _db_get_func_(void); -#ifdef __cplusplus - -#define DBUG_ENTER(a) \ - const char *_db_func_, *_db_file_; \ - uint _db_level_; \ - char **_db_framep_; \ - Dbug_violation_helper dbug_violation_helper; \ - _db_enter_ (a, __FILE__, __LINE__, &_db_func_, &_db_file_, \ - &_db_level_, &_db_framep_) -#define DBUG_VIOLATION_HELPER_LEAVE dbug_violation_helper.leave() - -#else /* C */ - -#define DBUG_ENTER(a) \ - const char *_db_func_, *_db_file_; \ - uint _db_level_; \ - char **_db_framep_; \ - _db_enter_ (a, __FILE__, __LINE__, &_db_func_, &_db_file_, \ - &_db_level_, &_db_framep_) -#define DBUG_VIOLATION_HELPER_LEAVE do { } while(0) - -#endif /* C++ */ - -#define DBUG_LEAVE \ - DBUG_VIOLATION_HELPER_LEAVE; \ - _db_return_ (__LINE__, &_db_func_, &_db_file_, &_db_level_) +#define DBUG_ENTER(a) struct _db_stack_frame_ _db_stack_frame_; \ + _db_enter_ (a,__FILE__,__LINE__,&_db_stack_frame_) +#define DBUG_LEAVE _db_return_ (__LINE__, &_db_stack_frame_) #define DBUG_RETURN(a1) do {DBUG_LEAVE; return(a1);} while(0) #define DBUG_VOID_RETURN do {DBUG_LEAVE; return;} while(0) #define DBUG_EXECUTE(keyword,a1) \ - do {if (_db_keyword_(0, (keyword))) { a1 }} while(0) + do {if (_db_keyword_(0, (keyword), 0)) { a1 }} while(0) #define DBUG_EXECUTE_IF(keyword,a1) \ - do {if (_db_strict_keyword_ (keyword)) { a1 } } while(0) + do {if (_db_keyword_(0, (keyword), 1)) { a1 }} while(0) #define DBUG_EVALUATE(keyword,a1,a2) \ - (_db_keyword_(0,(keyword)) ? (a1) : (a2)) + (_db_keyword_(0,(keyword), 0) ? (a1) : (a2)) #define DBUG_EVALUATE_IF(keyword,a1,a2) \ - (_db_strict_keyword_((keyword)) ? (a1) : (a2)) + (_db_keyword_(0,(keyword), 1) ? (a1) : (a2)) #define DBUG_PRINT(keyword,arglist) \ do {_db_pargs_(__LINE__,keyword); _db_doprnt_ arglist;} while(0) #define DBUG_PUSH(a1) _db_push_ (a1) #define DBUG_POP() _db_pop_ () -#define DBUG_SET(a1) _db_set_ (0, (a1)) +#define DBUG_SET(a1) _db_set_ (a1) #define DBUG_SET_INITIAL(a1) _db_set_init_ (a1) #define DBUG_PROCESS(a1) _db_process_(a1) #define DBUG_FILE _db_fp_() @@ -125,54 +86,66 @@ extern const char* _db_get_func_(void); #define DBUG_ASSERT(A) assert(A) #define DBUG_EXPLAIN(buf,len) _db_explain_(0, (buf),(len)) #define DBUG_EXPLAIN_INITIAL(buf,len) _db_explain_init_((buf),(len)) -#define IF_DBUG(A) A -#define _DBUG_MAX_FUNC_NAME_ 255 +#define DEBUGGER_OFF do { _dbug_on_= 0; } while(0) +#define DEBUGGER_ON do { _dbug_on_= 1; } while(0) +#ifndef __WIN__ +#define DBUG_ABORT() (_db_flush_(), abort()) +#else +/* + Avoid popup with abort/retry/ignore buttons. When BUG#31745 is fixed we can + call abort() instead of _exit(3) (now it would cause a "test signal" popup). +*/ +#include <crtdbg.h> +#define DBUG_ABORT() (_db_flush_(),\ + (void)_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE),\ + (void)_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR),\ + _exit(3)) +#endif #define DBUG_CHECK_CRASH(func, op) \ - do { \ - if (_db_keywords_((func), (op))) \ - { abort(); } \ - } while (0) + do { char _dbuf_[255]; strxnmov(_dbuf_, sizeof(_dbuf_)-1, (func), (op)); \ + DBUG_EXECUTE_IF(_dbuf_, DBUG_ABORT()); } while(0) #define DBUG_CRASH_ENTER(func) \ DBUG_ENTER(func); DBUG_CHECK_CRASH(func, "_crash_enter") #define DBUG_CRASH_RETURN(val) \ - do {DBUG_CHECK_CRASH(_db_get_func_(), "_crash_return"); \ - DBUG_RETURN(val);} while(0) + DBUG_CHECK_CRASH(_db_get_func_(), "_crash_return") #define DBUG_CRASH_VOID_RETURN \ - do {DBUG_CHECK_CRASH (_db_get_func_(), "_crash_return"); \ - DBUG_VOID_RETURN;} while(0) -#else /* No debugger */ + DBUG_CHECK_CRASH (_db_get_func_(), "_crash_return") + +#else /* No debugger */ #define DBUG_ENTER(a1) #define DBUG_LEAVE -#define DBUG_VIOLATION_HELPER_LEAVE -#define DBUG_RETURN(a1) do { return(a1); } while(0) -#define DBUG_VOID_RETURN do { return; } while(0) -#define DBUG_EXECUTE(keyword,a1) do { } while(0) -#define DBUG_EXECUTE_IF(keyword,a1) do { } while(0) +#define DBUG_RETURN(a1) do { return(a1); } while(0) +#define DBUG_VOID_RETURN do { return; } while(0) +#define DBUG_EXECUTE(keyword,a1) do { } while(0) +#define DBUG_EXECUTE_IF(keyword,a1) do { } while(0) #define DBUG_EVALUATE(keyword,a1,a2) (a2) #define DBUG_EVALUATE_IF(keyword,a1,a2) (a2) -#define DBUG_PRINT(keyword,arglist) do { } while(0) -#define DBUG_PUSH(a1) -#define DBUG_SET(a1) do { } while(0) -#define DBUG_SET_INITIAL(a1) do { } while(0) -#define DBUG_POP() -#define DBUG_PROCESS(a1) +#define DBUG_PRINT(keyword,arglist) do { } while(0) +#define DBUG_PUSH(a1) do { } while(0) +#define DBUG_SET(a1) do { } while(0) +#define DBUG_SET_INITIAL(a1) do { } while(0) +#define DBUG_POP() do { } while(0) +#define DBUG_PROCESS(a1) do { } while(0) #define DBUG_SETJMP(a1) setjmp(a1) #define DBUG_LONGJMP(a1) longjmp(a1) -#define DBUG_DUMP(keyword,a1,a2) do { } while(0) -#define DBUG_END() -#define DBUG_ASSERT(A) do { } while(0) -#define DBUG_LOCK_FILE +#define DBUG_DUMP(keyword,a1,a2) do { } while(0) +#define DBUG_END() do { } while(0) +#define DBUG_ASSERT(A) do { } while(0) +#define DBUG_LOCK_FILE do { } while(0) #define DBUG_FILE (stderr) -#define DBUG_UNLOCK_FILE +#define DBUG_UNLOCK_FILE do { } while(0) #define DBUG_EXPLAIN(buf,len) #define DBUG_EXPLAIN_INITIAL(buf,len) -#define IF_DBUG(A) +#define DEBUGGER_OFF do { } while(0) +#define DEBUGGER_ON do { } while(0) +#define DBUG_ABORT() abort() #define DBUG_CRASH_ENTER(func) -#define DBUG_CRASH_RETURN(val) do { return(val); } while(0) -#define DBUG_CRASH_VOID_RETURN do { return; } while(0) +#define DBUG_CRASH_RETURN(val) do { return(val); } while(0) +#define DBUG_CRASH_VOID_RETURN do { return; } while(0) + #endif -#ifdef __cplusplus +#ifdef __cplusplus } #endif #endif diff --git a/include/my_global.h b/include/my_global.h index ca1e1a9d845..f6d1592fc6f 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -649,8 +649,6 @@ C_MODE_END # endif #endif -#include <my_dbug.h> - #define MIN_ARRAY_SIZE 0 /* Zero or One. Gcc allows zero*/ #define ASCII_BITS_USED 8 /* Bit char used */ #define NEAR_F /* No near function handling */ @@ -1177,6 +1175,8 @@ typedef char bool; /* Ordinary boolean values 0 1 */ #define reg16 register #endif +#include <my_dbug.h> + /* Sometimes we want to make sure that the variable is not put into a register in debugging mode so we can see its value in the core diff --git a/include/my_pthread.h b/include/my_pthread.h index b4fe1203d2b..b6d9feae067 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -670,6 +670,7 @@ struct st_my_thread_var }; extern struct st_my_thread_var *_my_thread_var(void) __attribute__ ((const)); +extern void **my_thread_var_dbug(); extern uint my_thread_end_wait_time; #define my_thread_var (_my_thread_var()) #define my_errno my_thread_var->thr_errno diff --git a/include/my_sys.h b/include/my_sys.h index b4366a4cd3d..9121f0f249e 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -683,6 +683,8 @@ extern void my_error _VARARGS((int nr,myf MyFlags, ...)); extern void my_printf_error _VARARGS((uint my_err, const char *format, myf MyFlags, ...)) ATTRIBUTE_FORMAT(printf, 2, 4); +extern void my_printv_error(uint error, const char *format, myf MyFlags, + va_list ap); extern int my_error_register(const char** (*get_errmsgs) (), int first, int last); extern const char **my_error_unregister(int first, int last); diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h index 45d0234cb67..f158dc20999 100644 --- a/include/mysql/plugin.h +++ b/include/mysql/plugin.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2005 MySQL AB +/* Copyright (C) 2005 MySQL AB, 2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,12 +16,6 @@ #ifndef _my_plugin_h #define _my_plugin_h -/* size_t */ -#include <stdlib.h> - -typedef struct st_mysql MYSQL; - - /* On Windows, exports from DLL need to be declared */ @@ -39,15 +33,7 @@ class Item; #define MYSQL_THD void* #endif -#ifndef _m_string_h -/* This definition must match the one given in m_string.h */ -struct st_mysql_lex_string -{ - char *str; - unsigned int length; -}; -#endif /* _m_string_h */ -typedef struct st_mysql_lex_string MYSQL_LEX_STRING; +#include <mysql/services.h> #define MYSQL_XIDDATASIZE 128 /** @@ -70,7 +56,7 @@ typedef struct st_mysql_xid MYSQL_XID; Plugin API. Common for all plugin types. */ -#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0100 +#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0101 /* The allowable types of plugins @@ -126,7 +112,8 @@ enum enum_mysql_show_type { SHOW_UNDEF, SHOW_BOOL, SHOW_INT, SHOW_LONG, SHOW_LONGLONG, SHOW_CHAR, SHOW_CHAR_PTR, - SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE + SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE, + SHOW_always_last }; struct st_mysql_show_var { @@ -750,54 +737,6 @@ int thd_killed(const MYSQL_THD thd); */ unsigned long thd_get_thread_id(const MYSQL_THD thd); - -/** - Allocate memory in the connection's local memory pool - - @details - When properly used in place of @c my_malloc(), this can significantly - improve concurrency. Don't use this or related functions to allocate - large chunks of memory. Use for temporary storage only. The memory - will be freed automatically at the end of the statement; no explicit - code is required to prevent memory leaks. - - @see alloc_root() -*/ -void *thd_alloc(MYSQL_THD thd, unsigned int size); -/** - @see thd_alloc() -*/ -void *thd_calloc(MYSQL_THD thd, unsigned int size); -/** - @see thd_alloc() -*/ -char *thd_strdup(MYSQL_THD thd, const char *str); -/** - @see thd_alloc() -*/ -char *thd_strmake(MYSQL_THD thd, const char *str, unsigned int size); -/** - @see thd_alloc() -*/ -void *thd_memdup(MYSQL_THD thd, const void* str, unsigned int size); - -/** - Create a LEX_STRING in this connection's local memory pool - - @param thd user thread connection handle - @param lex_str pointer to LEX_STRING object to be initialized - @param str initializer to be copied into lex_str - @param size length of str, in bytes - @param allocate_lex_string flag: if TRUE, allocate new LEX_STRING object, - instead of using lex_str value - @return NULL on failure, or pointer to the LEX_STRING object - - @see thd_alloc() -*/ -MYSQL_LEX_STRING *thd_make_lex_string(MYSQL_THD thd, MYSQL_LEX_STRING *lex_str, - const char *str, unsigned int size, - int allocate_lex_string); - /** Get the XID for this connection's transaction @@ -818,64 +757,6 @@ void mysql_query_cache_invalidate4(MYSQL_THD thd, const char *key, unsigned int key_length, int using_trx); -/** - Get the value of user variable as an integer. - - This function will return the value of variable @a name as an - integer. If the original value of the variable is not an integer, - the value will be converted into an integer. - - @param name user variable name - @param value pointer to return the value - @param null_value if not NULL, the function will set it to true if - the value of variable is null, set to false if not - - @retval 0 Success - @retval 1 Variable not found -*/ -int get_user_var_int(const char *name, - long long int *value, int *null_value); - -/** - Get the value of user variable as a double precision float number. - - This function will return the value of variable @a name as real - number. If the original value of the variable is not a real number, - the value will be converted into a real number. - - @param name user variable name - @param value pointer to return the value - @param null_value if not NULL, the function will set it to true if - the value of variable is null, set to false if not - - @retval 0 Success - @retval 1 Variable not found -*/ -int get_user_var_real(const char *name, - double *value, int *null_value); - -/** - Get the value of user variable as a string. - - This function will return the value of variable @a name as - string. If the original value of the variable is not a string, - the value will be converted into a string. - - @param name user variable name - @param value pointer to the value buffer - @param len length of the value buffer - @param precision precision of the value if it is a float number - @param null_value if not NULL, the function will set it to true if - the value of variable is null, set to false if not - - @retval 0 Success - @retval 1 Variable not found -*/ -int get_user_var_str(const char *name, - char *value, unsigned long len, - unsigned int precision, int *null_value); - - #ifdef __cplusplus } #endif diff --git a/include/mysql/plugin.h.pp b/include/mysql/plugin.h.pp index d864140333b..b7dd7bc0032 100644 --- a/include/mysql/plugin.h.pp +++ b/include/mysql/plugin.h.pp @@ -1,11 +1,38 @@ +#include <mysql/services.h> +#include <mysql/service_my_snprintf.h> +#include <stdarg.h> +#include <stdlib.h> +extern struct my_snprintf_service_st { + size_t (*my_snprintf_type)(char*, size_t, const char*, ...); + size_t (*my_vsnprintf_type)(char *, size_t, const char*, va_list); +} *my_snprintf_service; +size_t my_snprintf(char* to, size_t n, const char* fmt, ...); +size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap); +#include <mysql/service_thd_alloc.h> #include <stdlib.h> -typedef struct st_mysql MYSQL; struct st_mysql_lex_string { char *str; - unsigned int length; + size_t length; }; typedef struct st_mysql_lex_string MYSQL_LEX_STRING; +extern struct thd_alloc_service_st { + void *(*thd_alloc_func)(void*, unsigned int); + void *(*thd_calloc_func)(void*, unsigned int); + char *(*thd_strdup_func)(void*, const char *); + char *(*thd_strmake_func)(void*, const char *, unsigned int); + void *(*thd_memdup_func)(void*, const void*, unsigned int); + MYSQL_LEX_STRING *(*thd_make_lex_string_func)(void*, MYSQL_LEX_STRING *, + const char *, unsigned int, int); +} *thd_alloc_service; +void *thd_alloc(void* thd, unsigned int size); +void *thd_calloc(void* thd, unsigned int size); +char *thd_strdup(void* thd, const char *str); +char *thd_strmake(void* thd, const char *str, unsigned int size); +void *thd_memdup(void* thd, const void* str, unsigned int size); +MYSQL_LEX_STRING *thd_make_lex_string(void* thd, MYSQL_LEX_STRING *lex_str, + const char *str, unsigned int size, + int allocate_lex_string); struct st_mysql_xid { long formatID; long gtrid_length; @@ -17,7 +44,8 @@ enum enum_mysql_show_type { SHOW_UNDEF, SHOW_BOOL, SHOW_INT, SHOW_LONG, SHOW_LONGLONG, SHOW_CHAR, SHOW_CHAR_PTR, - SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE + SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE, + SHOW_always_last }; struct st_mysql_show_var { const char *name; @@ -107,9 +135,9 @@ struct st_mysql_information_schema { int interface_version; }; -struct Mysql_replication { - int interface_version; -}; + struct Mysql_replication { + int interface_version; + }; struct st_mysql_value { int (*value_type)(struct st_mysql_value *); @@ -130,22 +158,7 @@ void thd_inc_row_count(void* thd); int mysql_tmpfile(const char *prefix); int thd_killed(const void* thd); unsigned long thd_get_thread_id(const void* thd); -void *thd_alloc(void* thd, unsigned int size); -void *thd_calloc(void* thd, unsigned int size); -char *thd_strdup(void* thd, const char *str); -char *thd_strmake(void* thd, const char *str, unsigned int size); -void *thd_memdup(void* thd, const void* str, unsigned int size); -MYSQL_LEX_STRING *thd_make_lex_string(void* thd, MYSQL_LEX_STRING *lex_str, - const char *str, unsigned int size, - int allocate_lex_string); void thd_get_xid(const void* thd, MYSQL_XID *xid); void mysql_query_cache_invalidate4(void* thd, const char *key, unsigned int key_length, int using_trx); -int get_user_var_int(const char *name, - long long int *value, int *null_value); -int get_user_var_real(const char *name, - double *value, int *null_value); -int get_user_var_str(const char *name, - char *value, unsigned long len, - unsigned int precision, int *null_value); diff --git a/include/mysql/service_my_snprintf.h b/include/mysql/service_my_snprintf.h new file mode 100644 index 00000000000..ad344864c34 --- /dev/null +++ b/include/mysql/service_my_snprintf.h @@ -0,0 +1,98 @@ +#ifndef MYSQL_SERVICE_MY_SNPRINTF_INCLUDED +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file + my_snprintf service + + Portable and limited vsnprintf() implementation. + + This is a portable, limited vsnprintf() implementation, with some + extra features. "Portable" means that it'll produce identical result + on all platforms (for example, on Windows and Linux system printf %e + formats the exponent differently, on different systems %p either + prints leading 0x or not, %s may accept null pointer or crash on + it). "Limited" means that it does not support all the C89 features. + But it supports few extensions, not in any standard. + + my_vsnprintf(to, n, fmt, ap) + + @param[out] to A buffer to store the result in + @param[in] n Store up to n-1 characters, followed by an end 0 + @param[in] fmt printf-like format string + @param[in] ap Arguments + + @return a number of bytes written to a buffer *excluding* terminating '\0' + + @post + The syntax of a format string is generally the same: + % <flag> <width> <precision> <length modifier> <format> + where everithing but the format is optional. + + Three one-character flags are recognized: + '0' has the standard zero-padding semantics; + '-' is parsed, but silently ignored; + '`' (backtick) is only supported for strings (%s) and means that the + string will be quoted according to MySQL identifier quoting rules. + + Both <width> and <precision> can be specified as numbers or '*'. + + <length modifier> can be 'l', 'll', or 'z'. + + Supported formats are 's' (null pointer is accepted, printed as + "(null)"), 'b' (extension, see below), 'c', 'd', 'u', 'x', + 'X', 'p' (works as 0x%x). + + Standard syntax for positional arguments $n is supported. + + Extensions: + + Flag '`' (backtick): see above. + + Format 'b': binary buffer, prints exactly <precision> bytes from the + argument, without stopping at '\0'. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdarg.h> +#include <stdlib.h> +extern struct my_snprintf_service_st { + size_t (*my_snprintf_type)(char*, size_t, const char*, ...); + size_t (*my_vsnprintf_type)(char *, size_t, const char*, va_list); +} *my_snprintf_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN + +#define my_vsnprintf my_snprintf_service->my_vsnprintf_type +#define my_snprintf my_snprintf_service->my_snprintf_type + +#else + +size_t my_snprintf(char* to, size_t n, const char* fmt, ...); +size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap); + +#endif + +#ifdef __cplusplus +} +#endif + +#define MYSQL_SERVICE_MY_SNPRINTF_INCLUDED +#endif + diff --git a/include/mysql/service_thd_alloc.h b/include/mysql/service_thd_alloc.h new file mode 100644 index 00000000000..86158ba1359 --- /dev/null +++ b/include/mysql/service_thd_alloc.h @@ -0,0 +1,128 @@ +#ifndef MYSQL_SERVICE_THD_ALLOC_INCLUDED +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file + This service provdes functions to allocate memory in a connection local + memory pool. The memory allocated there will be automatically freed at the + end of the statement, don't use it for allocations that should live longer + than that. For short living allocations this is more efficient than + using my_malloc and friends, and automatic "garbage collection" allows not + to think about memory leaks. + + The pool is best for small to medium objects, don't use it for large + allocations - they are better served with my_malloc. +*/ + +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct st_mysql_lex_string +{ + char *str; + size_t length; +}; +typedef struct st_mysql_lex_string MYSQL_LEX_STRING; + +extern struct thd_alloc_service_st { + void *(*thd_alloc_func)(MYSQL_THD, unsigned int); + void *(*thd_calloc_func)(MYSQL_THD, unsigned int); + char *(*thd_strdup_func)(MYSQL_THD, const char *); + char *(*thd_strmake_func)(MYSQL_THD, const char *, unsigned int); + void *(*thd_memdup_func)(MYSQL_THD, const void*, unsigned int); + MYSQL_LEX_STRING *(*thd_make_lex_string_func)(MYSQL_THD, MYSQL_LEX_STRING *, + const char *, unsigned int, int); +} *thd_alloc_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN + +#define thd_alloc(thd,size) (thd_alloc_service->thd_alloc_func((thd), (size))) + +#define thd_calloc(thd,size) (thd_alloc_service->thd_calloc_func((thd), (size))) + +#define thd_strdup(thd,str) (thd_alloc_service->thd_strdup_func((thd), (str))) + +#define thd_strmake(thd,str,size) \ + (thd_alloc_service->thd_strmake_func((thd), (str), (size))) + +#define thd_memdup(thd,str,size) \ + (thd_alloc_service->thd_memdup_func((thd), (str), (size))) + +#define thd_make_lex_string(thd, lex_str, str, size, allocate_lex_string) \ + (thd_alloc_service->thd_make_lex_string_func((thd), (lex_str), (str), \ + (size), (allocate_lex_string))) + +#else + +/** + Allocate memory in the connection's local memory pool + + @details + When properly used in place of @c my_malloc(), this can significantly + improve concurrency. Don't use this or related functions to allocate + large chunks of memory. Use for temporary storage only. The memory + will be freed automatically at the end of the statement; no explicit + code is required to prevent memory leaks. + + @see alloc_root() +*/ +void *thd_alloc(MYSQL_THD thd, unsigned int size); +/** + @see thd_alloc() +*/ +void *thd_calloc(MYSQL_THD thd, unsigned int size); +/** + @see thd_alloc() +*/ +char *thd_strdup(MYSQL_THD thd, const char *str); +/** + @see thd_alloc() +*/ +char *thd_strmake(MYSQL_THD thd, const char *str, unsigned int size); +/** + @see thd_alloc() +*/ +void *thd_memdup(MYSQL_THD thd, const void* str, unsigned int size); + +/** + Create a LEX_STRING in this connection's local memory pool + + @param thd user thread connection handle + @param lex_str pointer to LEX_STRING object to be initialized + @param str initializer to be copied into lex_str + @param size length of str, in bytes + @param allocate_lex_string flag: if TRUE, allocate new LEX_STRING object, + instead of using lex_str value + @return NULL on failure, or pointer to the LEX_STRING object + + @see thd_alloc() +*/ +MYSQL_LEX_STRING *thd_make_lex_string(MYSQL_THD thd, MYSQL_LEX_STRING *lex_str, + const char *str, unsigned int size, + int allocate_lex_string); + +#endif + +#ifdef __cplusplus +} +#endif + +#define MYSQL_SERVICE_THD_ALLOC_INCLUDED +#endif + diff --git a/include/mysql/services.h b/include/mysql/services.h new file mode 100644 index 00000000000..19003e66b96 --- /dev/null +++ b/include/mysql/services.h @@ -0,0 +1,30 @@ +#ifndef MYSQL_SERVICES_INCLUDED +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <mysql/service_my_snprintf.h> +#include <mysql/service_thd_alloc.h> + +#ifdef __cplusplus +} +#endif + +#define MYSQL_SERVICES_INCLUDED +#endif + diff --git a/include/service_versions.h b/include/service_versions.h new file mode 100644 index 00000000000..114957cdd86 --- /dev/null +++ b/include/service_versions.h @@ -0,0 +1,24 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef _WIN32 +#define SERVICE_VERSION __declspec(dllexport) void * +#else +#define SERVICE_VERSION void * +#endif + +#define VERSION_my_snprintf 0x0100 +#define VERSION_thd_alloc 0x0100 + diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index adcb65b8c5d..477830a6ebd 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -131,7 +131,7 @@ int STDCALL mysql_server_init(int argc __attribute__((unused)), mysql_port = MYSQL_PORT; #ifndef MSDOS { - struct servent *serv_ptr; + struct servent *serv_ptr __attribute__((unused)); char *env; /* diff --git a/libservices/CMakeLists.txt b/libservices/CMakeLists.txt new file mode 100644 index 00000000000..ddfa2495ade --- /dev/null +++ b/libservices/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2006 MySQL AB +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) + +SET(MYSQLSERVICES_SOURCES my_snprintf_service.c thd_alloc_service.c) + +ADD_LIBRARY(mysqlservices ${MYSQLSERVICES_SOURCES}) diff --git a/libservices/HOWTO b/libservices/HOWTO new file mode 100644 index 00000000000..b4960cb11dc --- /dev/null +++ b/libservices/HOWTO @@ -0,0 +1,100 @@ +How to create a new service +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A "service" is a set of C functions in a structure that a +service dynamic linker uses when a dynamic plugin is loaded. + +If you want to export C++ class you need to provide an +extern "C" function that will create a new instance of your class, +and put it in a service. + +Data structures are not part of the service structure, but they are part +of the API you create and usually need to be declared in the same +service_*.h file. + +To turn a set of functions (foo_func1, foo_func2) +into a service "foo" you need to + +1. create a new file include/mysql/service_foo.h + +2. the template is +================================================================== + #ifndef MYSQL_SERVICE_FOO_INCLUDED + /* standard GPL header */ + + /** + @file + *exhaustive* description of the interface you provide. + This file is the main user documentation of the new service + */ + #ifdef __cplusplus + extern "C" { + #endif + + extern struct foo_service_st { + int (*foo_func1_type)(...); /* fix the prototype as appropriate */ + void (*foo_func2_type)(...); /* fix the prototype as appropriate */ + } *foo_service; + + #ifdef MYSQL_DYNAMIC_PLUGIN + + #define foo_func1(...) foo_service->foo_func1_type(...) + #define foo_func2(...) foo_service->foo_func2_type(...) + + #else + + int foo_func1_type(...); /* fix the prototype as appropriate */ + void foo_func2_type(...); /* fix the prototype as appropriate */ + + #endif + + #ifdef __cplusplus + } + #endif + + #define MYSQL_SERVICE_FOO_INCLUDED + #endif +================================================================== + +the service_foo.h file should be self-contained, if it needs system headers - +include them in it, e.g. if you use size_t - #include <stdlib.h> + +it should also declare all the accompanying data structures, as necessary +(e.g. thd_alloc_service declares MYSQL_LEX_STRING). + +3. add the new file to include/Makefile.am (pkginclude_HEADERS) +4. add the new file to include/mysql/services.h +5. increase the minor plugin ABI version in include/mysql/plugin.h + (MYSQL_PLUGIN_INTERFACE_VERSION = MYSQL_PLUGIN_INTERFACE_VERSION+1) +6. add the version of your service to include/service_versions.h: +================================================================== + #define VERSION_foo 0x0100 +================================================================== + +7. create a new file libservices/foo_service.h using the following template: +================================================================== + /* GPL header */ + #include <service_versions.h> + SERVICE_VERSION *foo_service= (void*)VERSION_foo; +================================================================== + +8. add the new file to libservices/CMakeLists.txt (MYSQLSERVICES_SOURCES) +9. add the new file to libservices/Makefile.am (libmysqlservices_a_SOURCES) +10. and finally, register your service for dynamic linking in + sql/sql_plugin_services.h +10.1 fill in the service structure: +================================================================== + static struct foo_service_st foo_handler = { + foo_func1, + foo_func2 + } +================================================================== + +10.2 and add it to the list of services + +================================================================== + { "foo_service", VERSION_foo, &foo_handler } +================================================================== + +that's all. + diff --git a/libservices/Makefile.am b/libservices/Makefile.am new file mode 100644 index 00000000000..642081859c1 --- /dev/null +++ b/libservices/Makefile.am @@ -0,0 +1,19 @@ +# Copyright 2009 Sun Microsystems, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +AM_CPPFLAGS = -I$(top_srcdir)/include +pkglib_LIBRARIES = libmysqlservices.a +libmysqlservices_a_SOURCES = my_snprintf_service.c thd_alloc_service.c +EXTRA_DIST = CMakeLists.txt diff --git a/libservices/my_snprintf_service.c b/libservices/my_snprintf_service.c new file mode 100644 index 00000000000..40d778e4b8d --- /dev/null +++ b/libservices/my_snprintf_service.c @@ -0,0 +1,17 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <service_versions.h> +SERVICE_VERSION my_snprintf_service= (void*)VERSION_my_snprintf; diff --git a/libservices/thd_alloc_service.c b/libservices/thd_alloc_service.c new file mode 100644 index 00000000000..5d4d496774c --- /dev/null +++ b/libservices/thd_alloc_service.c @@ -0,0 +1,17 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <service_versions.h> +SERVICE_VERSION *thd_alloc_service= (void*)VERSION_thd_alloc; diff --git a/mysql-test/r/plugin.result b/mysql-test/r/plugin.result index 782d2a5a9a4..85fbd1353cc 100644 --- a/mysql-test/r/plugin.result +++ b/mysql-test/r/plugin.result @@ -12,6 +12,15 @@ CREATE TABLE t1(a int) ENGINE=EXAMPLE; SELECT * FROM t1; a DROP TABLE t1; +set global example_ulong_var=500; +set global example_enum_var= e1; +show status like 'example%'; +Variable_name Value +example_func_example enum_var is 0, ulong_var is 500, really +show variables like 'example%'; +Variable_name Value +example_enum_var e1 +example_ulong_var 500 UNINSTALL PLUGIN example; UNINSTALL PLUGIN EXAMPLE; ERROR 42000: PLUGIN EXAMPLE does not exist diff --git a/mysql-test/t/plugin.test b/mysql-test/t/plugin.test index 788a7b336ef..0bf86b47dd7 100644 --- a/mysql-test/t/plugin.test +++ b/mysql-test/t/plugin.test @@ -22,6 +22,12 @@ SELECT * FROM t1; DROP TABLE t1; +# a couple of tests for variables +set global example_ulong_var=500; +set global example_enum_var= e1; +show status like 'example%'; +show variables like 'example%'; + UNINSTALL PLUGIN example; --error 1305 UNINSTALL PLUGIN EXAMPLE; diff --git a/mysys/my_error.c b/mysys/my_error.c index ff9156faf82..e2523a39d0b 100644 --- a/mysys/my_error.c +++ b/mysys/my_error.c @@ -126,6 +126,29 @@ void my_printf_error(uint error, const char *format, myf MyFlags, ...) } /* + Error with va_list + + SYNOPSIS + my_printv_error() + error Errno + format Format string + MyFlags Flags + ... variable list +*/ + +void my_printv_error(uint error, const char *format, myf MyFlags, va_list ap) +{ + char ebuff[ERRMSGSIZE]; + DBUG_ENTER("my_printv_error"); + DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d format: %s", + error, MyFlags, errno, format)); + + (void) my_vsnprintf(ebuff, sizeof(ebuff), format, ap); + (*error_handler_hook)(error, ebuff, MyFlags); + DBUG_VOID_RETURN; +} + +/* Give message using error_handler_hook SYNOPSIS diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index f37ba111993..b2972faf0f8 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -387,6 +387,15 @@ const char *my_thread_name(void) } return tmp->name; } + +/* Return pointer to DBUG for holding current state */ + +extern void **my_thread_var_dbug() +{ + struct st_my_thread_var *tmp= + my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys); + return tmp && tmp->init ? &tmp->dbug : 0; +} #endif /* DBUG_OFF */ diff --git a/plugin/daemon_example/Makefile.am b/plugin/daemon_example/Makefile.am index c5414cd46c7..fce67285a5f 100644 --- a/plugin/daemon_example/Makefile.am +++ b/plugin/daemon_example/Makefile.am @@ -26,7 +26,8 @@ INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include \ EXTRA_LTLIBRARIES = libdaemon_example.la pkgplugin_LTLIBRARIES = @plugin_daemon_example_shared_target@ -libdaemon_example_la_LDFLAGS = -module -rpath $(pkgplugindir) +libdaemon_example_la_LDFLAGS = -module -rpath $(pkgplugindir) -L$(top_builddir)/libservices -lmysqlservices + libdaemon_example_la_CXXFLAGS= $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN libdaemon_example_la_CFLAGS = $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN libdaemon_example_la_SOURCES = daemon_example.cc diff --git a/sql-common/client.c b/sql-common/client.c index 537a992e770..5114b645818 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -2479,7 +2479,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, for (i= 0; status && hp->h_addr_list[i]; i++) { - IF_DBUG(char ipaddr[18];) + char ipaddr[18] __attribute__((unused)); memcpy(&sock_addr.sin_addr, hp->h_addr_list[i], min(sizeof(sock_addr.sin_addr), (size_t) hp->h_length)); DBUG_PRINT("info",("Trying %s...", diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 3e350144f74..15c2d950ff9 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -45,7 +45,7 @@ SET (SQL_SOURCE discover.cc ../libmysql/errmsg.c field.cc field_conv.cc filesort.cc gstream.cc ha_partition.cc - handler.cc hash_filo.cc hash_filo.h + handler.cc hash_filo.cc hash_filo.h sql_plugin_services.h hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc item_create.cc item_func.cc item_geofunc.cc item_row.cc item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc diff --git a/sql/Makefile.am b/sql/Makefile.am index 92fb86ac942..15ee0d588c4 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -82,7 +82,7 @@ mysqld_LDADD = libndb.la \ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ item_strfunc.h item_timefunc.h \ - item_xmlfunc.h \ + item_xmlfunc.h sql_plugin_services.h \ item_create.h item_subselect.h item_row.h \ mysql_priv.h item_geofunc.h sql_bitmap.h \ procedure.h sql_class.h sql_lex.h sql_list.h \ diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 2580d526b52..81870e6b7a3 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -1701,9 +1701,11 @@ uchar *sys_var_debug_sync::value_ptr(THD *thd, static void debug_sync_execute(THD *thd, st_debug_sync_action *action) { - IF_DBUG(const char *dsp_name= action->sync_point.c_ptr()); - IF_DBUG(const char *sig_emit= action->signal.c_ptr()); - IF_DBUG(const char *sig_wait= action->wait_for.c_ptr()); +#ifndef DBUG_OFF + const char *dsp_name= action->sync_point.c_ptr(); + const char *sig_emit= action->signal.c_ptr(); + const char *sig_wait= action->wait_for.c_ptr(); +#endif DBUG_ENTER("debug_sync_execute"); DBUG_ASSERT(thd); DBUG_ASSERT(action); diff --git a/sql/field.cc b/sql/field.cc index 19b0ee8ec7e..dc32624db10 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -6669,9 +6669,8 @@ int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_string::sort_string(uchar *to,uint length) { - IF_DBUG(uint tmp=) my_strnxfrm(field_charset, - to, length, - ptr, field_length); + uint tmp __attribute__((unused))= + my_strnxfrm(field_charset, to, length, ptr, field_length); DBUG_ASSERT(tmp == length); } diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 176eefb25c9..83cceb0da76 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -4316,7 +4316,7 @@ int ha_ndbcluster::end_bulk_insert() } else { - IF_DBUG(int res=) trans->restart(); + int res __attribute__((unused))= trans->restart(); DBUG_ASSERT(res == 0); } } @@ -5928,7 +5928,7 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) { DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u", share->key, share->use_count)); - IF_DBUG(int r=) rename_share(share, to); + int r __attribute__((unused))= rename_share(share, to); DBUG_ASSERT(r == 0); } #endif @@ -5952,7 +5952,7 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) #ifdef HAVE_NDB_BINLOG if (share) { - IF_DBUG(int ret=) rename_share(share, from); + int ret __attribute__((unused))= rename_share(share, from); DBUG_ASSERT(ret == 0); /* ndb_share reference temporary free */ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 1dc43f43865..e34a22cf9f4 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -3390,14 +3390,14 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, if (share->flags & NSF_BLOB_FLAG) { my_ptrdiff_t ptrdiff= 0; - IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[0], + int ret __attribute__((unused))= get_ndb_blobs_value(table, share->ndb_value[0], blobs_buffer[0], blobs_buffer_size[0], ptrdiff); DBUG_ASSERT(ret == 0); } ndb_unpack_record(table, share->ndb_value[0], &b, table->record[0]); - IF_DBUG(int ret=) trans.write_row(originating_server_id, + int ret __attribute__((unused))= trans.write_row(originating_server_id, injector::transaction::table(table, TRUE), &b, n_fields, table->record[0]); @@ -3429,7 +3429,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, if (share->flags & NSF_BLOB_FLAG) { my_ptrdiff_t ptrdiff= table->record[n] - table->record[0]; - IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[n], + int ret __attribute__((unused))= get_ndb_blobs_value(table, share->ndb_value[n], blobs_buffer[n], blobs_buffer_size[n], ptrdiff); @@ -3437,7 +3437,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, } ndb_unpack_record(table, share->ndb_value[n], &b, table->record[n]); DBUG_EXECUTE("info", print_records(table, table->record[n]);); - IF_DBUG(int ret =) trans.delete_row(originating_server_id, + int ret __attribute__((unused))= trans.delete_row(originating_server_id, injector::transaction::table(table, TRUE), &b, n_fields, table->record[n]); @@ -3452,7 +3452,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, if (share->flags & NSF_BLOB_FLAG) { my_ptrdiff_t ptrdiff= 0; - IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[0], + int ret __attribute__((unused))= get_ndb_blobs_value(table, share->ndb_value[0], blobs_buffer[0], blobs_buffer_size[0], ptrdiff); @@ -3480,7 +3480,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, if (share->flags & NSF_BLOB_FLAG) { my_ptrdiff_t ptrdiff= table->record[1] - table->record[0]; - IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[1], + int ret __attribute__((unused))= get_ndb_blobs_value(table, share->ndb_value[1], blobs_buffer[1], blobs_buffer_size[1], ptrdiff); @@ -3488,7 +3488,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, } ndb_unpack_record(table, share->ndb_value[1], &b, table->record[1]); DBUG_EXECUTE("info", print_records(table, table->record[1]);); - IF_DBUG(int ret =) trans.update_row(originating_server_id, + int ret __attribute__((unused))= trans.update_row(originating_server_id, injector::transaction::table(table, TRUE), &b, n_fields, @@ -3792,7 +3792,7 @@ restart: { C_STRING_WITH_LEN("mysqld startup") }, { C_STRING_WITH_LEN("cluster disconnect")} }; - IF_DBUG(int error=) + int error __attribute__((unused))= inj->record_incident(thd, INCIDENT_LOST_EVENTS, msg[incident_id]); DBUG_ASSERT(!error); break; @@ -4107,7 +4107,7 @@ restart: DBUG_PRINT("info", ("use_table: %.*s", (int) name.length, name.str)); injector::transaction::table tbl(table, TRUE); - IF_DBUG(int ret=) trans.use_table(::server_id, tbl); + int ret __attribute__((unused))= trans.use_table(::server_id, tbl); DBUG_ASSERT(ret == 0); } } @@ -4123,7 +4123,7 @@ restart: (int) name.length, name.str)); #endif injector::transaction::table tbl(table, TRUE); - IF_DBUG(int ret=) trans.use_table(::server_id, tbl); + int ret __attribute__((unused))= trans.use_table(::server_id, tbl); DBUG_ASSERT(ret == 0); /* @@ -4193,7 +4193,7 @@ restart: else { // set injector_ndb database/schema from table internal name - IF_DBUG(int ret=) + int ret __attribute__((unused))= i_ndb->setDatabaseAndSchemaName(pOp->getEvent()->getTable()); DBUG_ASSERT(ret == 0); ndb_binlog_thread_handle_non_data_event(thd, i_ndb, pOp, row); diff --git a/sql/log_event.cc b/sql/log_event.cc index 6a73bd69d02..3a54717a45f 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1200,14 +1200,14 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len, */ if (description_event->event_type_permutation) { - IF_DBUG({ - int new_event_type= - description_event->event_type_permutation[event_type]; - DBUG_PRINT("info", - ("converting event type %d to %d (%s)", - event_type, new_event_type, - get_type_str((Log_event_type)new_event_type))); - }); +#ifndef DBUG_OFF + int new_event_type= + description_event->event_type_permutation[event_type]; + DBUG_PRINT("info", + ("converting event type %d to %d (%s)", + event_type, new_event_type, + get_type_str((Log_event_type)new_event_type))); +#endif event_type= description_event->event_type_permutation[event_type]; } @@ -3609,10 +3609,12 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver) */ if (post_header_len) { +#ifndef DBUG_OFF // Allows us to sanity-check that all events initialized their // events (see the end of this 'if' block). - IF_DBUG(memset(post_header_len, 255, - number_of_event_types*sizeof(uint8));); + memset(post_header_len, 255, + number_of_event_types*sizeof(uint8)); +#endif /* Note: all event types must explicitly fill in their lengths here. */ post_header_len[START_EVENT_V3-1]= START_V3_HEADER_LEN; @@ -3666,11 +3668,9 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver) post_header_len[HEARTBEAT_LOG_EVENT-1]= 0; // Sanity-check that all post header lengths are initialized. - IF_DBUG({ - int i; - for (i=0; i<number_of_event_types; i++) - assert(post_header_len[i] != 255); - }); + int i; + for (i=0; i<number_of_event_types; i++) + DBUG_ASSERT(post_header_len[i] != 255); } break; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 5ec2ffc89b9..b539e004481 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -28,6 +28,16 @@ #ifndef MYSQL_CLIENT +/* + the following #define adds server-only members to enum_mysql_show_type, + that is defined in mysql/plugin.h + it has to be before mysql/plugin.h is included. +*/ +#define SHOW_always_last SHOW_KEY_CACHE_LONG, \ + SHOW_KEY_CACHE_LONGLONG, SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, \ + SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, \ + SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS + #include <my_global.h> #include <mysql_version.h> #include <mysql_embed.h> diff --git a/sql/replication.h b/sql/replication.h index 6b7be58a5b1..eea77ef9f8e 100644 --- a/sql/replication.h +++ b/sql/replication.h @@ -16,6 +16,8 @@ #ifndef REPLICATION_H #define REPLICATION_H +typedef struct st_mysql MYSQL; + #ifdef __cplusplus extern "C" { #endif @@ -483,6 +485,64 @@ const char* thd_enter_cond(MYSQL_THD thd, pthread_cond_t *cond, */ void thd_exit_cond(MYSQL_THD thd, const char *old_msg); +/** + Get the value of user variable as an integer. + + This function will return the value of variable @a name as an + integer. If the original value of the variable is not an integer, + the value will be converted into an integer. + + @param name user variable name + @param value pointer to return the value + @param null_value if not NULL, the function will set it to true if + the value of variable is null, set to false if not + + @retval 0 Success + @retval 1 Variable not found +*/ +int get_user_var_int(const char *name, + long long int *value, int *null_value); + +/** + Get the value of user variable as a double precision float number. + + This function will return the value of variable @a name as real + number. If the original value of the variable is not a real number, + the value will be converted into a real number. + + @param name user variable name + @param value pointer to return the value + @param null_value if not NULL, the function will set it to true if + the value of variable is null, set to false if not + + @retval 0 Success + @retval 1 Variable not found +*/ +int get_user_var_real(const char *name, + double *value, int *null_value); + +/** + Get the value of user variable as a string. + + This function will return the value of variable @a name as + string. If the original value of the variable is not a string, + the value will be converted into a string. + + @param name user variable name + @param value pointer to the value buffer + @param len length of the value buffer + @param precision precision of the value if it is a float number + @param null_value if not NULL, the function will set it to true if + the value of variable is null, set to false if not + + @retval 0 Success + @retval 1 Variable not found +*/ +int get_user_var_str(const char *name, + char *value, unsigned long len, + unsigned int precision, int *null_value); + + #ifdef __cplusplus } diff --git a/sql/slave.cc b/sql/slave.cc index 18f419c604a..8be17860c61 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -583,7 +583,7 @@ terminate_slave_thread(THD *thd, EINVAL: invalid signal number (can't happen) ESRCH: thread already killed (can happen, should be ignored) */ - IF_DBUG(int err= ) pthread_kill(thd->real_id, thr_client_alarm); + int err __attribute__((unused))= pthread_kill(thd->real_id, thr_client_alarm); DBUG_ASSERT(err != EINVAL); #endif thd->awake(THD::NOT_KILLED); @@ -1087,7 +1087,7 @@ static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info) if (io_slave_killed(thd, mi)) { if (info && global_system_variables.log_warnings) - sql_print_information(info); + sql_print_information("%s", info); return TRUE; } return FALSE; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index fb7c27f6267..60aed4ce53b 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -274,12 +274,7 @@ const char* thd_enter_cond(MYSQL_THD thd, pthread_cond_t *cond, if (!thd) thd= current_thd; - const char* old_msg = thd->proc_info; - safe_mutex_assert_owner(mutex); - thd->mysys_var->current_mutex = mutex; - thd->mysys_var->current_cond = cond; - thd->proc_info = msg; - return old_msg; + return thd->enter_cond(cond, mutex, msg); } extern "C" @@ -288,18 +283,7 @@ void thd_exit_cond(MYSQL_THD thd, const char *old_msg) if (!thd) thd= current_thd; - /* - Putting the mutex unlock in thd_exit_cond() ensures that - mysys_var->current_mutex is always unlocked _before_ mysys_var->mutex is - locked (if that would not be the case, you'll get a deadlock if someone - does a THD::awake() on you). - */ - pthread_mutex_unlock(thd->mysys_var->current_mutex); - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex = 0; - thd->mysys_var->current_cond = 0; - thd->proc_info = old_msg; - pthread_mutex_unlock(&thd->mysys_var->mutex); + thd->exit_cond(old_msg); return; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 255d7a4d63a..01ac3c12641 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -25,7 +25,6 @@ #include "log.h" #include "rpl_tblmap.h" -#include "replication.h" class Reprepare_observer; @@ -1885,11 +1884,28 @@ public: inline const char* enter_cond(pthread_cond_t *cond, pthread_mutex_t* mutex, const char* msg) { - return thd_enter_cond(this, cond, mutex, msg); + const char* old_msg = proc_info; + safe_mutex_assert_owner(mutex); + mysys_var->current_mutex = mutex; + mysys_var->current_cond = cond; + proc_info = msg; + return old_msg; } inline void exit_cond(const char* old_msg) { - thd_exit_cond(this, old_msg); + /* + Putting the mutex unlock in thd->exit_cond() ensures that + mysys_var->current_mutex is always unlocked _before_ mysys_var->mutex is + locked (if that would not be the case, you'll get a deadlock if someone + does a THD::awake() on you). + */ + pthread_mutex_unlock(mysys_var->current_mutex); + pthread_mutex_lock(&mysys_var->mutex); + mysys_var->current_mutex = 0; + mysys_var->current_cond = 0; + proc_info = old_msg; + pthread_mutex_unlock(&mysys_var->mutex); + return; } inline time_t query_start() { query_start_used=1; return start_time; } inline void set_time() diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index a4da72c0948..936c9ae8866 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2005 MySQL AB +/* Copyright (C) 2005 MySQL AB, 2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -99,7 +99,9 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= MYSQL_REPLICATION_INTERFACE_VERSION, }; -static bool initialized= 0; +/* support for Services */ + +#include "sql_plugin_services.h" /* A mutex LOCK_plugin must be acquired before accessing the @@ -113,6 +115,8 @@ static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM]; static bool reap_needed= false; static int plugin_array_version=0; +static bool initialized= 0; + /* write-lock on LOCK_system_variables_hash is required before modifying the following variables/structures @@ -225,6 +229,22 @@ extern bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd, extern bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists); #endif /* EMBEDDED_LIBRARY */ +static void report_error(int where_to, uint error, ...) +{ + va_list args; + if (where_to & REPORT_TO_USER) + { + va_start(args, error); + my_printv_error(error, ER(error), MYF(0), args); + va_end(args); + } + if (where_to & REPORT_TO_LOG) + { + va_start(args, error); + error_log_print(ERROR_LEVEL, ER_DEFAULT(error), args); + va_end(args); + } +} /**************************************************************************** Value type thunks, allows the C world to play in the C++ world @@ -345,7 +365,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) { #ifdef HAVE_DLOPEN char dlpath[FN_REFLEN]; - uint plugin_dir_len, dummy_errors, dlpathlen; + uint plugin_dir_len, dummy_errors, dlpathlen, i; struct st_plugin_dl *tmp, plugin_dl; void *sym; DBUG_ENTER("plugin_dl_add"); @@ -360,10 +380,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) system_charset_info, 1) || plugin_dir_len + dl->length + 1 >= FN_REFLEN) { - if (report & REPORT_TO_USER) - my_error(ER_UDF_NO_PATHS, MYF(0)); - if (report & REPORT_TO_LOG) - sql_print_error("%s", ER_DEFAULT(ER_UDF_NO_PATHS)); + report_error(report, ER_UDF_NO_PATHS); DBUG_RETURN(0); } /* If this dll is already loaded just increase ref_count. */ @@ -388,21 +405,14 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) if (*errmsg == ':') errmsg++; if (*errmsg == ' ') errmsg++; } - if (report & REPORT_TO_USER) - my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dlpath, errno, errmsg); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_CANT_OPEN_LIBRARY), dlpath, errno, errmsg); + report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, errno, errmsg); DBUG_RETURN(0); } /* Determine interface version */ if (!(sym= dlsym(plugin_dl.handle, plugin_interface_version_sym))) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), plugin_interface_version_sym); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_CANT_FIND_DL_ENTRY), - plugin_interface_version_sym); + report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_interface_version_sym); DBUG_RETURN(0); } plugin_dl.version= *(int *)sym; @@ -411,29 +421,42 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) (plugin_dl.version >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8)) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dlpath, 0, - "plugin interface version mismatch"); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_CANT_OPEN_LIBRARY), dlpath, 0, - "plugin interface version mismatch"); + report_error(report, ER_CANT_OPEN_LIBRARY, MYF(0), dlpath, 0, + "plugin interface version mismatch"); DBUG_RETURN(0); } + + /* link the services in */ + for (i= 0; i < array_elements(list_of_services); i++) + { + if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name))) + { + uint ver= (uint)(intptr)*(void**)sym; + if (ver > list_of_services[i].version || + (ver >> 8) < (list_of_services[i].version >> 8)) + { + char buf[MYSQL_ERRMSG_SIZE]; + my_snprintf(buf, sizeof(buf), + "service '%s' interface version mismatch", + list_of_services[i].name); + report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0, buf); + DBUG_RETURN(0); + } + *(void**)sym= list_of_services[i].service; + } + } + /* Find plugin declarations */ if (!(sym= dlsym(plugin_dl.handle, plugin_declarations_sym))) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), plugin_declarations_sym); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_CANT_FIND_DL_ENTRY), - plugin_declarations_sym); + report_error(report, ER_CANT_FIND_DL_ENTRY, MYF(0), + plugin_declarations_sym); DBUG_RETURN(0); } if (plugin_dl.version != MYSQL_PLUGIN_INTERFACE_VERSION) { - int i; uint sizeof_st_plugin; struct st_mysql_plugin *old, *cur; char *ptr= (char *)sym; @@ -443,11 +466,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) else { #ifdef ERROR_ON_NO_SIZEOF_PLUGIN_SYMBOL - free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), sizeof_st_plugin_sym); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_CANT_FIND_DL_ENTRY), sizeof_st_plugin_sym); + report_error(report, ER_CANT_FIND_DL_ENTRY, sizeof_st_plugin_sym); DBUG_RETURN(0); #else /* @@ -469,10 +488,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) if (!cur) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_OUTOFMEMORY, MYF(0), plugin_dl.dl.length); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_OUTOFMEMORY), plugin_dl.dl.length); + report_error(report, ER_OUTOFMEMORY, plugin_dl.dl.length); DBUG_RETURN(0); } /* @@ -494,10 +510,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) if (! (plugin_dl.dl.str= (char*) my_malloc(plugin_dl.dl.length, MYF(0)))) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_OUTOFMEMORY, MYF(0), plugin_dl.dl.length); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_OUTOFMEMORY), plugin_dl.dl.length); + report_error(report, ER_OUTOFMEMORY, plugin_dl.dl.length); DBUG_RETURN(0); } plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length, @@ -508,19 +521,13 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl))) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_OUTOFMEMORY, MYF(0), sizeof(struct st_plugin_dl)); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_OUTOFMEMORY), sizeof(struct st_plugin_dl)); + report_error(report, ER_OUTOFMEMORY, sizeof(struct st_plugin_dl)); DBUG_RETURN(0); } DBUG_RETURN(tmp); #else DBUG_ENTER("plugin_dl_add"); - if (report & REPORT_TO_USER) - my_error(ER_FEATURE_DISABLED, MYF(0), "plugin", "HAVE_DLOPEN"); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_FEATURE_DISABLED), "plugin", "HAVE_DLOPEN"); + report_error(report, ER_FEATURE_DISABLED, "plugin", "HAVE_DLOPEN"); DBUG_RETURN(0); #endif } @@ -637,7 +644,7 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc CALLER_INFO_PROTO) /* For debugging, we do an additional malloc which allows the memory manager and/or valgrind to track locked references and - double unlocks to aid resolving reference counting.problems. + double unlocks to aid resolving reference counting problems. */ if (!(plugin= (plugin_ref) my_malloc_ci(sizeof(pi), MYF(MY_WME)))) DBUG_RETURN(NULL); @@ -720,10 +727,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, DBUG_ENTER("plugin_add"); if (plugin_find_internal(name, MYSQL_ANY_PLUGIN)) { - if (report & REPORT_TO_USER) - my_error(ER_UDF_EXISTS, MYF(0), name->str); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_UDF_EXISTS), name->str); + report_error(report, ER_UDF_EXISTS, name->str); DBUG_RETURN(TRUE); } /* Clear the whole struct to catch future extensions. */ @@ -750,10 +754,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, strxnmov(buf, sizeof(buf) - 1, "API version for ", plugin_type_names[plugin->type].str, " plugin is too different", NullS); - if (report & REPORT_TO_USER) - my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dl->str, 0, buf); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_CANT_OPEN_LIBRARY), dl->str, 0, buf); + report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, 0, buf); goto err; } tmp.plugin= plugin; @@ -782,10 +783,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, DBUG_RETURN(FALSE); } } - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), name->str); - if (report & REPORT_TO_LOG) - sql_print_error(ER_DEFAULT(ER_CANT_FIND_DL_ENTRY), name->str); + report_error(report, ER_CANT_FIND_DL_ENTRY, name->str); err: plugin_dl_del(dl); DBUG_RETURN(TRUE); diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index c6ad846943c..23ce85c994b 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -35,16 +35,6 @@ class sys_var; #define INITIAL_LEX_PLUGIN_LIST_SIZE 16 -/* - the following #define adds server-only members to enum_mysql_show_type, - that is defined in plugin.h -*/ -#define SHOW_FUNC SHOW_FUNC, SHOW_KEY_CACHE_LONG, SHOW_KEY_CACHE_LONGLONG, \ - SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, SHOW_HAVE, \ - SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, SHOW_LONG_NOFLUSH, \ - SHOW_LONGLONG_STATUS -#include <mysql/plugin.h> -#undef SHOW_FUNC typedef enum enum_mysql_show_type SHOW_TYPE; typedef struct st_mysql_show_var SHOW_VAR; diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h new file mode 100644 index 00000000000..7491ddab79d --- /dev/null +++ b/sql/sql_plugin_services.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* support for Services */ +#include <service_versions.h> + +struct st_service_ref { + const char *name; + uint version; + void *service; +}; + +static struct my_snprintf_service_st my_snprintf_handler = { + my_snprintf, + my_vsnprintf +}; + +static struct thd_alloc_service_st thd_alloc_handler= { + thd_alloc, + thd_calloc, + thd_strdup, + thd_strmake, + thd_memdup, + thd_make_lex_string +}; + +static struct st_service_ref list_of_services[]= +{ + { "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler }, + { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler } +}; + diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6b67a216341..95270487ec8 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5105,8 +5105,8 @@ greedy_search(JOIN *join, the interleaving state to the one of the non-extended partial plan on exit. */ - IF_DBUG(bool is_interleave_error= ) - check_interleaving_with_nj (best_table); + bool is_interleave_error __attribute__((unused))= + check_interleaving_with_nj (best_table); /* This has been already checked by best_extension_by_limited_search */ DBUG_ASSERT(!is_interleave_error); @@ -15183,7 +15183,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, Item *pos; List_iterator_fast<Item> li(all_fields); Copy_field *copy= NULL; - IF_DBUG(Copy_field *copy_start); + Copy_field *copy_start __attribute__((unused)); res_selected_fields.empty(); res_all_fields.empty(); List_iterator_fast<Item> itr(res_all_fields); @@ -15196,7 +15196,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, goto err2; param->copy_funcs.empty(); - IF_DBUG(copy_start= copy); + copy_start= copy; for (i= 0; (pos= li++); i++) { Field *field; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 3ff60c88236..63dc4e68fdf 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5465,7 +5465,7 @@ binlog: } VOID(pthread_mutex_unlock(&LOCK_open)); - IF_DBUG(int result=) + int result __attribute__((unused))= store_create_info(thd, table, &query, create_info, FALSE /* show_database */); diff --git a/storage/example/Makefile.am b/storage/example/Makefile.am index ce269aee59b..1179a338ee2 100644 --- a/storage/example/Makefile.am +++ b/storage/example/Makefile.am @@ -34,7 +34,7 @@ noinst_HEADERS = ha_example.h EXTRA_LTLIBRARIES = ha_example.la pkgplugin_LTLIBRARIES = @plugin_example_shared_target@ -ha_example_la_LDFLAGS = -module -rpath $(pkgplugindir) +ha_example_la_LDFLAGS = -module -rpath $(pkgplugindir) -L$(top_builddir)/libservices -lmysqlservices ha_example_la_CXXFLAGS= $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN ha_example_la_CFLAGS = $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN ha_example_la_SOURCES = ha_example.cc diff --git a/storage/example/ha_example.cc b/storage/example/ha_example.cc index 852dd50cb3e..9bc666663c3 100644 --- a/storage/example/ha_example.cc +++ b/storage/example/ha_example.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2003 MySQL AB +/* Copyright (C) 2003 MySQL AB, 2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -921,6 +921,24 @@ static struct st_mysql_sys_var* example_system_variables[]= { NULL }; +// this is an example of SHOW_FUNC and of my_snprintf() service +static int show_func_example(MYSQL_THD thd, struct st_mysql_show_var *var, + char *buf) +{ + var->type= SHOW_CHAR; + var->value= buf; // it's of SHOW_VAR_FUNC_BUFF_SIZE bytes + my_snprintf(buf, SHOW_VAR_FUNC_BUFF_SIZE, + "enum_var is %u, ulong_var is %lu, %.6b", // %b is MySQL extension + srv_enum_var, srv_ulong_var, "really"); + return 0; +} + +static struct st_mysql_show_var func_status[]= +{ + {"example_func_example", (char *)show_func_example, SHOW_FUNC}, + {0,0,SHOW_UNDEF} +}; + mysql_declare_plugin(example) { MYSQL_STORAGE_ENGINE_PLUGIN, @@ -932,7 +950,7 @@ mysql_declare_plugin(example) example_init_func, /* Plugin Init */ example_done_func, /* Plugin Deinit */ 0x0001 /* 0.1 */, - NULL, /* status variables */ + func_status, /* status variables */ example_system_variables, /* system variables */ NULL /* config options */ } diff --git a/storage/mysql_storage_engine.cmake b/storage/mysql_storage_engine.cmake index af8c3a85cd1..b920f16452b 100644 --- a/storage/mysql_storage_engine.cmake +++ b/storage/mysql_storage_engine.cmake @@ -34,7 +34,7 @@ IF(NOT SOURCE_SUBLIBS) #The dll is linked to the mysqld executable SET(dyn_libname ha_${libname}) ADD_LIBRARY(${dyn_libname} SHARED ${${engine}_SOURCES}) - TARGET_LINK_LIBRARIES (${dyn_libname} mysqld) + TARGET_LINK_LIBRARIES (${dyn_libname} mysqlservices mysqld) IF(${engine}_LIBS) TARGET_LINK_LIBRARIES(${dyn_libname} ${${engine}_LIBS}) ENDIF(${engine}_LIBS) diff --git a/strings/my_vsnprintf.c b/strings/my_vsnprintf.c index 3362559972a..ad8e7c8c776 100644 --- a/strings/my_vsnprintf.c +++ b/strings/my_vsnprintf.c @@ -260,6 +260,14 @@ static char *process_int_arg(char *to, char *end, size_t length, { size_t diff= (length- res_length); bfill(to, diff, (print_type & PREZERO_ARG) ? '0' : ' '); + if (arg_type == 'p' && print_type & PREZERO_ARG) + { + if (diff > 1) + to[1]= 'x'; + else + store_start[0]= 'x'; + store_start[1]= '0'; + } to+= diff; } bmove(to, store_start, res_length); @@ -323,6 +331,7 @@ start: /* Get print width */ if (*fmt == '*') { + fmt++; fmt= get_width(fmt, &print_arr[idx].width); print_arr[idx].width--; DBUG_ASSERT(*fmt == '$' && print_arr[idx].width < MAX_ARGS); @@ -472,6 +481,8 @@ start: /** Produces output string according to a format string + See the detailed documentation around my_snprintf_service_st + @param cs string charset @param to buffer where processed string will be place @param n size of buffer @@ -621,54 +632,3 @@ size_t my_snprintf(char* to, size_t n, const char* fmt, ...) return result; } -#ifdef MAIN -#define OVERRUN_SENTRY 250 -static void my_printf(const char * fmt, ...) -{ - char buf[33]; - int n; - va_list ar; - va_start(ar, fmt); - buf[sizeof(buf)-1]=OVERRUN_SENTRY; - n = my_vsnprintf(buf, sizeof(buf)-1,fmt, ar); - printf(buf); - printf("n=%d, strlen=%d\n", n, strlen(buf)); - if ((uchar) buf[sizeof(buf)-1] != OVERRUN_SENTRY) - { - fprintf(stderr, "Buffer overrun\n"); - abort(); - } - va_end(ar); -} - - -int main() -{ - - my_printf("Hello\n"); - my_printf("Hello int, %d\n", 1); - my_printf("Hello string '%s'\n", "I am a string"); - my_printf("Hello hack hack hack hack hack hack hack %d\n", 1); - my_printf("Hello %d hack %d\n", 1, 4); - my_printf("Hello %d hack hack hack hack hack %d\n", 1, 4); - my_printf("Hello '%s' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\n", "hack"); - my_printf("Hello hhhhhhhhhhhhhh %d sssssssssssssss\n", 1); - my_printf("Hello %u\n", 1); - my_printf("Hex: %lx '%6lx'\n", 32, 65); - my_printf("conn %ld to: '%-.64s' user: '%-.32s' host:\ - `%-.64s' (%-.64s)", 1, 0,0,0,0); - - my_printf("Hello string %`s\n", "I am a string"); - my_printf("Hello %05s\n", "TEST"); - my_printf("My %1$`-.1s test\n", "QQQQ"); - my_printf("My %1$s test done %2$s\n", "DDDD", "AAAA"); - my_printf("My %1$s test %2$s, %1$-.3s\n", "DDDD", "CCCC"); - my_printf("My %1$`-.4b test\n", "QQQQ"); - my_printf("My %1$c test\n", 'X'); - my_printf("My `%010d` test1 %4x test2 %4X\n", 10, 10, 10); - my_printf("My `%1$010d` test1 %2$4x test2 %2$4x\n", 10, 10); - my_printf("My %1$*02$d test\n", 10, 5); - my_printf("My %1$`s test %2$s, %1$`-.3s\n", "DDDD", "CCCC"); - return 0; -} -#endif diff --git a/unittest/mysys/Makefile.am b/unittest/mysys/Makefile.am index f0ffc7a6720..56c65d71396 100644 --- a/unittest/mysys/Makefile.am +++ b/unittest/mysys/Makefile.am @@ -21,7 +21,7 @@ LDADD = $(top_builddir)/unittest/mytap/libmytap.a \ $(top_builddir)/dbug/libdbug.a \ $(top_builddir)/strings/libmystrings.a -noinst_PROGRAMS = bitmap-t base64-t +noinst_PROGRAMS = bitmap-t base64-t my_vsnprintf-t if NEED_THREAD # my_atomic-t is used to check thread functions, so it is safe to diff --git a/unittest/mysys/my_vsnprintf-t.c b/unittest/mysys/my_vsnprintf-t.c new file mode 100644 index 00000000000..f3a6b5700da --- /dev/null +++ b/unittest/mysys/my_vsnprintf-t.c @@ -0,0 +1,155 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <m_string.h> +#include <tap.h> + +char buf[1024]; /* let's hope that's enough */ + +void test1(const char *res, const char *fmt, ...) +{ + va_list args; + size_t len; + va_start(args,fmt); + len= my_vsnprintf(buf, sizeof(buf)-1, fmt, args); + va_end(args); + ok(strlen(res) == len && strcmp(buf, res) == 0, "\"%s\"", buf); +} + +int main(void) +{ + plan(47); + + test1("Constant string", + "Constant string"); + + test1("Format specifier s works", + "Format specifier s %s", "works"); + test1("Format specifier b works (mysql extension)", + "Format specifier b %.5b (mysql extension)", "works!!!"); + test1("Format specifier c !", + "Format specifier c %c", '!'); + test1("Format specifier d 1", + "Format specifier d %d", 1); + test1("Format specifier u 2", + "Format specifier u %u", 2); + test1("Format specifier x a", + "Format specifier x %x", 10); + test1("Format specifier X B", + "Format specifier X %X", 11); + test1("Format specifier p 0x5", + "Format specifier p %p", 5); + + test1("Flag '-' is ignored < 1>", + "Flag '-' is ignored <%-4d>", 1); + test1("Flag '0' works <0006>", + "Flag '0' works <%04d>", 6); + + test1("Width is ignored for strings <x> <y>", + "Width is ignored for strings <%04s> <%5s>", "x", "y"); + + test1("Precision works for strings <abcde>", + "Precision works for strings <%.5s>", "abcdef!"); + + test1("Flag '`' (backtick) works: `abcd` `op``q` (mysql extension)", + "Flag '`' (backtick) works: %`s %`.4s (mysql extension)", + "abcd", "op`qrst"); + + test1("Length modifiers work: 1 * -1 * 2 * 3", + "Length modifiers work: %d * %ld * %lld * %zd", 1, -1L, 2LL, (size_t)3); + + test1("(null) pointer is fine", + "%s pointer is fine", NULL); + + test1("Positional arguments work: on the dark side they are", + "Positional arguments work: %3$s %1$s %2$s", + "they", "are", "on the dark side"); + + test1("Asterisk '*' as a width works: < 4>", + "Asterisk '*' as a width works: <%*d>", 5, 4); + + test1("Asterisk '*' as a precision works: <qwerty>", + "Asterisk '*' as a precision works: <%.*s>", 6, "qwertyuiop"); + + test1("Positional arguments for a width: < 4>", + "Positional arguments for a width: <%1$*2$d>", 4, 5); + + test1("Positional arguments for a precision: <qwerty>", + "Positional arguments for a precision: <%1$.*2$s>", "qwertyuiop", 6); + + test1("Positional arguments and a width: <0000ab>", + "Positional arguments and a width: <%1$06x>", 0xab); + + test1("Padding and %p <0x12> <0x034> <0x0000ab> < 0xcd>", + "Padding and %%p <%04p> <%05p> <%08p> <%8p>", 0x12, 0x34, 0xab, 0xcd); + +#if MYSQL_VERSION_ID > 60000 +#error %f/%g tests go here +#endif + + test1("Hello", + "Hello"); + test1("Hello int, 1", + "Hello int, %d", 1); + test1("Hello int, -1", + "Hello int, %d", -1); + test1("Hello string 'I am a string'", + "Hello string '%s'", "I am a string"); + test1("Hello hack hack hack hack hack hack hack 1", + "Hello hack hack hack hack hack hack hack %d", 1); + test1("Hello 1 hack 4", + "Hello %d hack %d", 1, 4); + test1("Hello 1 hack hack hack hack hack 4", + "Hello %d hack hack hack hack hack %d", 1, 4); + test1("Hello 'hack' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", + "Hello '%s' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", "hack"); + test1("Hello hhhhhhhhhhhhhh 1 sssssssssssssss", + "Hello hhhhhhhhhhhhhh %d sssssssssssssss", 1); + test1("Hello 1", + "Hello %u", 1); + test1("Hello 4294967295", + "Hello %u", -1); + test1("Hex: 20 ' 41'", + "Hex: %lx '%6lx'", 32, 65); + test1("conn 1 to: '(null)' user: '(null)' host: '(null)' ((null))", + "conn %ld to: '%-.64s' user: '%-.32s' host: '%-.64s' (%-.64s)", + 1L, NULL, NULL, NULL, NULL); + test1("Hello string `I am a string`", + "Hello string %`s", "I am a string"); + test1("Hello TEST", + "Hello %05s", "TEST"); + test1("My `Q` test", + "My %1$`-.1s test", "QQQQ"); + test1("My AAAA test done DDDD", + "My %2$s test done %1$s", "DDDD", "AAAA"); + test1("My DDDD test CCCC, DDD", + "My %1$s test %2$s, %1$-.3s", "DDDD", "CCCC"); + test1("My QQQQ test", + "My %1$`-.4b test", "QQQQ"); + test1("My X test", + "My %1$c test", 'X'); + test1("My <0000000010> test1 < a> test2 < A>", + "My <%010d> test1 <%4x> test2 <%4X>", 10, 10, 10); + test1("My <0000000010> test1 < a> test2 < a>", + "My <%1$010d> test1 <%2$4x> test2 <%2$4x>", 10, 10); + test1("My 00010 test", + "My %1$*02$d test", 10, 5); + test1("My `DDDD` test CCCC, `DDD`", + "My %1$`s test %2$s, %1$`-.3s", "DDDD", "CCCC"); + + return exit_status(); +} + |