List:Commits« Previous MessageNext Message »
From:Chad MILLER Date:February 20 2007 2:56am
Subject:bk commit into 5.0 tree (cmiller:1.2359) BUG#24795
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of cmiller. When cmiller does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-02-19 21:56:48-05:00, cmiller@stripped +38 -0
  Bug#24795: SHOW PROFILE
  
  Profiling is only partially functional on some architectures.  Where 
  there is no getrusage() system call, presently Null values are 
  returned where it would be required.  Notably, Windows needs some love 
  applied to make it as useful.
  
    Syntax this adds:
    
    SHOW PROFILES
    
    SHOW PROFILE [types] [FOR QUERY n] [OFFSET n] [LIMIT n]
     where "n" is an integer
     and "types" is zero or many (comma-separated) of
        "CPU"
        "MEMORY" (not presently supported)
        "BLOCK IO"
        "CONTEXT SWITCHES"
        "PAGE FAULTS"
        "IPC"
        "SWAPS"
        "SOURCE"
        "ALL"
  
  It also adds a session variable (boolean) "profiling", set to "no"
  by default, and (integer) profiling_history_size, set to 15 by 
  default.
  
  This patch abstracts setting THDs' "proc_info" behind a macro that 
  can be used as a hook into the profiling code when profiling 
  support is compiled in.  All future code in this line should use
  that mechanism for setting thd->proc_info.
  
  ---
  
  Tests are now set to omit the statistics.
  
  ---
  
  Adds an Information_schema table, "profiling" for access to 
  "show profile" data.

  BitKeeper/etc/collapsed@stripped, 2007-02-06 06:34:29-05:00, cmiller@stripped +2 -0

  configure.in@stripped, 2007-02-19 21:56:43-05:00, cmiller@stripped +14 -0
    Add a configure-time option to enable/disable query profiling.  The
    default is enabled.

  libmysqld/Makefile.am@stripped, 2007-02-19 21:56:43-05:00, cmiller@stripped +1 -0
    Add profiling source file

  mysql-test/r/profiling.result@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +78 -0
    New BitKeeper file ``mysql-test/r/profiling.result''

  mysql-test/r/profiling.result@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +0 -0

  mysql-test/t/profiling-master.opt@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +1 -0
    New BitKeeper file ``mysql-test/t/profiling-master.opt''

  mysql-test/t/profiling-master.opt@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +0 -0

  mysql-test/t/profiling.test@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +114 -0
    New BitKeeper file ``mysql-test/t/profiling.test''

  mysql-test/t/profiling.test@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +0 -0

  sql/Makefile.am@stripped, 2007-02-19 21:56:43-05:00, cmiller@stripped +2 -0
    Add profiling source and header file.

  sql/ha_archive.cc@stripped, 2007-02-19 21:56:43-05:00, cmiller@stripped +2 -2
    Abstract setting thread-info into a function or macro.

  sql/ha_myisam.cc@stripped, 2007-02-19 21:56:43-05:00, cmiller@stripped +12 -12
    Abstract setting thread-info into a function or macro.

  sql/item_func.cc@stripped, 2007-02-19 21:56:43-05:00, cmiller@stripped +4 -4
    Abstract setting thread-info into a function or macro.

  sql/lex.h@stripped, 2007-02-19 21:56:43-05:00, cmiller@stripped +13 -0
    Add new lexer symbols.

  sql/lock.cc@stripped, 2007-02-19 21:56:43-05:00, cmiller@stripped +4 -4
    Abstract setting thread-info into a function or macro.

  sql/log_event.cc@stripped, 2007-02-19 21:56:43-05:00, cmiller@stripped +4 -4
    Abstract setting thread-info into a function or macro.

  sql/mysql_priv.h@stripped, 2007-02-19 21:56:43-05:00, cmiller@stripped +44 -33
    Use 64-bit constants for the 64-bit bit field.
    
    Add a new option bit for whether profiling is active or not.

  sql/mysqld.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +8 -1
    Add semicolon to DBUG statement.
    
    Add a new system variable and set it.

  sql/repl_failsafe.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +5 -5
    Abstract setting thread-info into a function or macro.

  sql/set_var.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +14 -0
    Make a new system global variable and session variable, to determine
    behavior of profiling.	

  sql/set_var.h@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +2 -2
    The THD::options bit field is ulonglong, not ulong.

  sql/slave.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +19 -19
    Abstract setting thread-info into a function or macro.

  sql/sp_head.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +2 -2
    Abstract setting thread-info into a function or macro.

  sql/sql_base.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +12 -12
    Abstract setting thread-info into a function or macro.

  sql/sql_class.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +3 -0
    Insert a pointer to the current thread in the profiling code.

  sql/sql_class.h@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +8 -0
    Create a new system variable, profiling_history_size, and add a 
    member to THD to hold profiling information about this thread's 
    execution.

  sql/sql_delete.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +6 -6
    Abstract setting thread-info into a function or macro.

  sql/sql_insert.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +12 -12
    Abstract setting thread-info into a function or macro.

  sql/sql_lex.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +3 -1
    Initialize profiling options to empty.

  sql/sql_lex.h@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +9 -1
    Add info to the lexer object so that we can hold data that comes from
    parsing statements.
    
    Reuse memory addresses of uints that can't occur in the same state-
    ment.
    
    This is dangerous because it involves knowledge of what symbols are 
    never used together, which is information stored obliquely in another
    file.

  sql/sql_parse.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +46 -11
    Add hooks to the parser to jump to profiling code.
    
    If profiling is not present, then return an error message upon being
    used.

  sql/sql_profile.cc@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +775 -0
    New BitKeeper file ``sql/sql_profile.cc''

  sql/sql_profile.cc@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +0 -0

  sql/sql_profile.h@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +231 -0
    New BitKeeper file ``sql/sql_profile.h''

  sql/sql_profile.h@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +0 -0

  sql/sql_repl.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +9 -9
    Abstract setting thread-info into a function or macro.

  sql/sql_select.cc@stripped, 2007-02-19 21:56:44-05:00, cmiller@stripped +19 -19
    Abstract setting thread-info into a function or macro.

  sql/sql_show.cc@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +5 -3
    Abstract setting thread-info into a function or macro.
    
    Also, remove "static" qualification on schema_table_store_record()
    so that external functions may use it.

  sql/sql_table.cc@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +10 -10
    Abstract setting thread-info into a function or macro.

  sql/sql_update.cc@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +7 -7
    Abstract setting thread-info into a function or macro.

  sql/sql_view.cc@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +1 -1
    Abstract setting thread-info into a function or macro.

  sql/sql_yacc.yy@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +88 -0
    Add new lexer symbols and insert new grammatical rules necessary to 
    retreive profiling information.

  sql/table.h@stripped, 2007-02-19 21:56:45-05:00, cmiller@stripped +1 -0
    Add enum item for query profiling.

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	cmiller
# Host:	zippy.cornsilk.net
# Root:	/home/cmiller/work/mysql/mysql-5.0-community-3--bug24795

--- 1.413/configure.in	2007-02-19 21:56:54 -05:00
+++ 1.414/configure.in	2007-02-19 21:56:54 -05:00
@@ -667,6 +667,20 @@ else
   AC_MSG_RESULT([no])
 fi
 
+# Add query profiler
+AC_ARG_ENABLE(profiling,
+    [  --disable-profiling     Build a version without query profiling code ],
+    [ ENABLED_PROFILING=$enableval ],
+    [ ENABLED_PROFILING=yes ])
+
+if test "$ENABLED_PROFILING" = "yes"
+then
+  AC_DEFINE([ENABLED_PROFILING], [1],
+            [If SHOW PROFILE should be enabled])
+  AC_MSG_RESULT([yes])
+else
+  AC_MSG_RESULT([no])
+fi
 
 # Use this to set the place used for unix socket used to local communication.
 AC_ARG_WITH(unix-socket-path,

--- 1.130/sql/Makefile.am	2007-02-19 21:56:54 -05:00
+++ 1.131/sql/Makefile.am	2007-02-19 21:56:54 -05:00
@@ -51,6 +51,7 @@ noinst_HEADERS =	item.h item_func.h item
 			procedure.h sql_class.h sql_lex.h sql_list.h \
 			sql_manager.h sql_map.h sql_string.h unireg.h \
 			sql_error.h field.h handler.h mysqld_suffix.h \
+ 			sql_profile.h \
 			ha_myisammrg.h\
 			ha_heap.h ha_myisam.h ha_berkeley.h ha_innodb.h \
 			ha_ndbcluster.h opt_range.h protocol.h \
@@ -79,6 +80,7 @@ mysqld_SOURCES =	sql_lex.cc sql_handler.
 			set_var.cc sql_parse.cc sql_yacc.yy \
 			sql_base.cc table.cc sql_select.cc sql_insert.cc \
                         sql_prepare.cc sql_error.cc sql_locale.cc \
+			sql_profile.cc \
 			sql_update.cc sql_delete.cc uniques.cc sql_do.cc \
 			procedure.cc item_uniq.cc sql_test.cc \
 			log.cc log_event.cc init.cc derror.cc sql_acl.cc \

--- 1.172/sql/ha_myisam.cc	2007-02-19 21:56:54 -05:00
+++ 1.173/sql/ha_myisam.cc	2007-02-19 21:56:54 -05:00
@@ -339,7 +339,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_
   MYISAM_SHARE* share = file->s;
   const char *old_proc_info=thd->proc_info;
 
-  thd->proc_info="Checking table";
+  thd_proc_info(thd, "Checking table");
   myisamchk_init(&param);
   param.thd = thd;
   param.op_name =   "check";
@@ -413,7 +413,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_
     file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
   }
 
-  thd->proc_info=old_proc_info;
+  thd_proc_info(thd, old_proc_info);
   return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
 }
 
@@ -679,22 +679,22 @@ int ha_myisam::repair(THD *thd, MI_CHECK
         char buf[40];
         /* TODO: respect myisam_repair_threads variable */
         my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map));
-        thd->proc_info=buf;
+        thd_proc_info(thd, buf);
         error = mi_repair_parallel(&param, file, fixed_name,
             param.testflag & T_QUICK);
-        thd->proc_info="Repair done"; // to reset proc_info, as
+        thd_proc_info(thd, "Repair done"); // to reset proc_info, as
                                       // it was pointing to local buffer
       }
       else
       {
-        thd->proc_info="Repair by sorting";
+        thd_proc_info(thd, "Repair by sorting");
         error = mi_repair_by_sort(&param, file, fixed_name,
             param.testflag & T_QUICK);
       }
     }
     else
     {
-      thd->proc_info="Repair with keycache";
+      thd_proc_info(thd, "Repair with keycache");
       param.testflag &= ~T_REP_BY_SORT;
       error=  mi_repair(&param, file, fixed_name,
 			param.testflag & T_QUICK);
@@ -708,7 +708,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK
 	(share->state.changed & STATE_NOT_SORTED_PAGES))
     {
       optimize_done=1;
-      thd->proc_info="Sorting index";
+      thd_proc_info(thd, "Sorting index");
       error=mi_sort_index(&param,file,fixed_name);
     }
     if (!statistics_done && (local_testflag & T_STATISTICS))
@@ -716,14 +716,14 @@ int ha_myisam::repair(THD *thd, MI_CHECK
       if (share->state.changed & STATE_NOT_ANALYZED)
       {
 	optimize_done=1;
-	thd->proc_info="Analyzing";
+	thd_proc_info(thd, "Analyzing");
 	error = chk_key(&param, file);
       }
       else
 	local_testflag&= ~T_STATISTICS;		// Don't update statistics
     }
   }
-  thd->proc_info="Saving state";
+  thd_proc_info(thd, "Saving state");
   if (!error)
   {
     if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file))
@@ -761,7 +761,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK
     file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
     update_state_info(&param, file, 0);
   }
-  thd->proc_info=old_proc_info;
+  thd_proc_info(thd, old_proc_info);
   if (!thd->locked_tables)
     mi_lock_database(file,F_UNLCK);
   DBUG_RETURN(error ? HA_ADMIN_FAILED :
@@ -986,7 +986,7 @@ int ha_myisam::enable_indexes(uint mode)
     THD *thd=current_thd;
     MI_CHECK param;
     const char *save_proc_info=thd->proc_info;
-    thd->proc_info="Creating index";
+    thd_proc_info(thd, "Creating index");
     myisamchk_init(&param);
     param.op_name= "recreating_index";
     param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
@@ -1011,7 +1011,7 @@ int ha_myisam::enable_indexes(uint mode)
         thd->clear_error();
     }
     info(HA_STATUS_CONST);
-    thd->proc_info=save_proc_info;
+    thd_proc_info(thd, save_proc_info);
   }
   else
   {

--- 1.318/sql/item_func.cc	2007-02-19 21:56:54 -05:00
+++ 1.319/sql/item_func.cc	2007-02-19 21:56:54 -05:00
@@ -3204,7 +3204,7 @@ void debug_sync_point(const char* lock_n
     Structure is now initialized.  Try to get the lock.
     Set up control struct to allow others to abort locks
   */
-  thd->proc_info="User lock";
+  thd_proc_info(thd, "User lock");
   thd->mysys_var->current_mutex= &LOCK_user_locks;
   thd->mysys_var->current_cond=  &ull->cond;
 
@@ -3229,7 +3229,7 @@ void debug_sync_point(const char* lock_n
   }
   pthread_mutex_unlock(&LOCK_user_locks);
   pthread_mutex_lock(&thd->mysys_var->mutex);
-  thd->proc_info=0;
+  thd_proc_info(thd, 0);
   thd->mysys_var->current_mutex= 0;
   thd->mysys_var->current_cond=  0;
   pthread_mutex_unlock(&thd->mysys_var->mutex);
@@ -3310,7 +3310,7 @@ longlong Item_func_get_lock::val_int()
     Structure is now initialized.  Try to get the lock.
     Set up control struct to allow others to abort locks.
   */
-  thd->proc_info="User lock";
+  thd_proc_info(thd, "User lock");
   thd->mysys_var->current_mutex= &LOCK_user_locks;
   thd->mysys_var->current_cond=  &ull->cond;
 
@@ -3348,7 +3348,7 @@ longlong Item_func_get_lock::val_int()
   pthread_mutex_unlock(&LOCK_user_locks);
 
   pthread_mutex_lock(&thd->mysys_var->mutex);
-  thd->proc_info=0;
+  thd_proc_info(thd, 0);
   thd->mysys_var->current_mutex= 0;
   thd->mysys_var->current_cond=  0;
   pthread_mutex_unlock(&thd->mysys_var->mutex);

--- 1.145/sql/lex.h	2007-02-19 21:56:54 -05:00
+++ 1.146/sql/lex.h	2007-02-19 21:56:54 -05:00
@@ -87,6 +87,7 @@ static SYMBOL symbols[] = {
   { "BINLOG",		SYM(BINLOG_SYM)},
   { "BIT",		SYM(BIT_SYM)},
   { "BLOB",		SYM(BLOB_SYM)},
+  { "BLOCK",  SYM(BLOCK_SYM)},
   { "BOOL",		SYM(BOOL_SYM)},
   { "BOOLEAN",		SYM(BOOLEAN_SYM)},
   { "BOTH",		SYM(BOTH)},
@@ -125,8 +126,10 @@ static SYMBOL symbols[] = {
   { "CONSISTENT",	SYM(CONSISTENT_SYM)},
   { "CONSTRAINT",	SYM(CONSTRAINT)},
   { "CONTAINS",         SYM(CONTAINS_SYM)},
+  { "CONTEXT",    SYM(CONTEXT_SYM)},
   { "CONTINUE",         SYM(CONTINUE_SYM)},
   { "CONVERT",		SYM(CONVERT_SYM)},
+  { "CPU",        SYM(CPU_SYM)},
   { "CREATE",		SYM(CREATE)},
   { "CROSS",		SYM(CROSS)},
   { "CUBE",		SYM(CUBE_SYM)},
@@ -192,6 +195,7 @@ static SYMBOL symbols[] = {
   { "EXTENDED",		SYM(EXTENDED_SYM)},
   { "FALSE",		SYM(FALSE_SYM)},
   { "FAST",		SYM(FAST_SYM)},
+  { "FAULTS",  SYM(FAULTS_SYM)},
   { "FETCH",            SYM(FETCH_SYM)},
   { "FIELDS",		SYM(COLUMNS)},
   { "FILE",		SYM(FILE_SYM)},
@@ -251,7 +255,9 @@ static SYMBOL symbols[] = {
   { "INTEGER",		SYM(INT_SYM)},
   { "INTERVAL",		SYM(INTERVAL_SYM)},
   { "INTO",		SYM(INTO)},
+  { "IO",     SYM(IO_SYM)},
   { "IO_THREAD",        SYM(RELAY_THREAD)},
+  { "IPC",    SYM(IPC_SYM)},
   { "IS",		SYM(IS)},
   { "ISOLATION",	SYM(ISOLATION)},
   { "ISSUER",		SYM(ISSUER_SYM)},
@@ -309,6 +315,7 @@ static SYMBOL symbols[] = {
   { "MEDIUMBLOB",	SYM(MEDIUMBLOB)},
   { "MEDIUMINT",	SYM(MEDIUMINT)},
   { "MEDIUMTEXT",	SYM(MEDIUMTEXT)},
+  { "MEMORY",     SYM(MEMORY_SYM)},
   { "MERGE",		SYM(MERGE_SYM)},
   { "MICROSECOND",	SYM(MICROSECOND_SYM)},
   { "MIDDLEINT",	SYM(MEDIUMINT)},	/* For powerbuilder */
@@ -356,6 +363,7 @@ static SYMBOL symbols[] = {
   { "OUT",              SYM(OUT_SYM)},
   { "OUTER",		SYM(OUTER)},
   { "OUTFILE",		SYM(OUTFILE)},
+  { "PAGE",		SYM(PAGE_SYM)},
   { "PACK_KEYS",	SYM(PACK_KEYS_SYM)},
   { "PARTIAL",		SYM(PARTIAL)},
   { "PASSWORD",		SYM(PASSWORD)},
@@ -370,6 +378,8 @@ static SYMBOL symbols[] = {
   { "PROCEDURE",	SYM(PROCEDURE)},
   { "PROCESS"	,	SYM(PROCESS)},
   { "PROCESSLIST",	SYM(PROCESSLIST_SYM)},
+  { "PROFILE",    SYM(PROFILE_SYM)},
+  { "PROFILES",   SYM(PROFILES_SYM)},
   { "PURGE",		SYM(PURGE)},
   { "QUARTER",          SYM(QUARTER_SYM)},
   { "QUERY",		SYM(QUERY_SYM)},
@@ -437,6 +447,7 @@ static SYMBOL symbols[] = {
   { "SOME",             SYM(ANY_SYM)},
   { "SONAME",		SYM(UDF_SONAME_SYM)},
   { "SOUNDS",		SYM(SOUNDS_SYM)},
+  { "SOURCE",   SYM(SOURCE_SYM)},
   { "SPATIAL",		SYM(SPATIAL_SYM)},
   { "SPECIFIC",         SYM(SPECIFIC_SYM)},
   { "SQL",              SYM(SQL_SYM)},
@@ -471,6 +482,8 @@ static SYMBOL symbols[] = {
   { "SUBJECT",		SYM(SUBJECT_SYM)},
   { "SUPER",		SYM(SUPER_SYM)},
   { "SUSPEND",          SYM(SUSPEND_SYM)},
+  { "SWAPS",      SYM(SWAPS_SYM)},
+  { "SWITCHES",   SYM(SWITCHES_SYM)},
   { "TABLE",		SYM(TABLE_SYM)},
   { "TABLES",		SYM(TABLES)},
   { "TABLESPACE",	SYM(TABLESPACE)},

--- 1.92/sql/lock.cc	2007-02-19 21:56:54 -05:00
+++ 1.93/sql/lock.cc	2007-02-19 21:56:54 -05:00
@@ -150,7 +150,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, 
       }
     }
 
-    thd->proc_info="System lock";
+    thd_proc_info(thd, "System lock");
     if (lock_external(thd, tables, count))
     {
       /* Clear the lock type of all lock data to avoid reusage. */
@@ -159,7 +159,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, 
       sql_lock=0;
       break;
     }
-    thd->proc_info="Table lock";
+    thd_proc_info(thd, "Table lock");
     thd->locked=1;
     /* Copy the lock data array. thr_multi_lock() reorders its contens. */
     memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
@@ -193,7 +193,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, 
       thd->locked=0;
       break;
     }
-    thd->proc_info=0;
+    thd_proc_info(thd, 0);
 
     /* some table was altered or deleted. reopen tables marked deleted */
     mysql_unlock_tables(thd,sql_lock);
@@ -208,7 +208,7 @@ retry:
     if (wait_for_tables(thd))
       break;					// Couldn't open tables
   }
-  thd->proc_info=0;
+  thd_proc_info(thd, 0);
   if (thd->killed)
   {
     thd->send_kill_message();

--- 1.217/sql/log_event.cc	2007-02-19 21:56:54 -05:00
+++ 1.218/sql/log_event.cc	2007-02-19 21:56:54 -05:00
@@ -4265,7 +4265,7 @@ int Create_file_log_event::exec_event(st
   bzero((char*)&file, sizeof(file));
   fname_buf= strmov(proc_info, "Making temp file ");
   ext= slave_load_file_stem(fname_buf, file_id, server_id, ".info");
-  thd->proc_info= proc_info;
+  thd_proc_info(thd, proc_info);
   my_delete(fname_buf, MYF(0)); // old copy may exist already
   if ((fd= my_create(fname_buf, CREATE_MODE,
 		     O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
@@ -4319,7 +4319,7 @@ err:
     end_io_cache(&file);
   if (fd >= 0)
     my_close(fd, MYF(0));
-  thd->proc_info= 0;
+  thd_proc_info(thd, 0);
   return error ? 1 : Log_event::exec_event(rli);
 }
 #endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
@@ -4439,7 +4439,7 @@ int Append_block_log_event::exec_event(s
 
   fname= strmov(proc_info, "Making temp file ");
   slave_load_file_stem(fname, file_id, server_id, ".data");
-  thd->proc_info= proc_info;
+  thd_proc_info(thd, proc_info);
   if (get_create_or_append())
   {
     my_delete(fname, MYF(0)); // old copy may exist already
@@ -4473,7 +4473,7 @@ int Append_block_log_event::exec_event(s
 err:
   if (fd >= 0)
     my_close(fd, MYF(0));
-  thd->proc_info= 0;
+  thd_proc_info(thd, 0);
   DBUG_RETURN(error ? error : Log_event::exec_event(rli));
 }
 #endif

--- 1.426/sql/mysql_priv.h	2007-02-19 21:56:54 -05:00
+++ 1.427/sql/mysql_priv.h	2007-02-19 21:56:54 -05:00
@@ -20,6 +20,9 @@
   except the part which must be in the server and in the client.
 */
 
+#ifndef MYSQL_PRIV_H_INCLUDED
+#define MYSQL_PRIV_H_INCLUDED
+
 #ifndef MYSQL_CLIENT
 
 #include <my_global.h>
@@ -173,6 +176,8 @@ MY_LOCALE *my_locale_by_name(const char 
 #define BDB_LOG_ALLOC_BLOCK_SIZE	1024
 #define WARN_ALLOC_BLOCK_SIZE		2048
 #define WARN_ALLOC_PREALLOC_SIZE	1024
+#define PROFILE_ALLOC_BLOCK_SIZE  2048
+#define PROFILE_ALLOC_PREALLOC_SIZE 1024
 
 /*
   The following parameters is to decide when to use an extra cache to
@@ -296,55 +301,57 @@ MY_LOCALE *my_locale_by_name(const char 
    TODO: separate three contexts above, move them to separate bitfields.
 */
 
-#define SELECT_DISTINCT         (1L << 0)       // SELECT, user
-#define SELECT_STRAIGHT_JOIN    (1L << 1)       // SELECT, user
-#define SELECT_DESCRIBE         (1L << 2)       // SELECT, user
-#define SELECT_SMALL_RESULT     (1L << 3)       // SELECT, user
-#define SELECT_BIG_RESULT       (1L << 4)       // SELECT, user
-#define OPTION_FOUND_ROWS       (1L << 5)       // SELECT, user
-#define OPTION_TO_QUERY_CACHE   (1L << 6)       // SELECT, user
-#define SELECT_NO_JOIN_CACHE    (1L << 7)       // intern
-#define OPTION_BIG_TABLES       (1L << 8)       // THD, user
-#define OPTION_BIG_SELECTS      (1L << 9)       // THD, user
-#define OPTION_LOG_OFF          (1L << 10)      // THD, user
-#define OPTION_UPDATE_LOG       (1L << 11)      // THD, user, unused
-#define TMP_TABLE_ALL_COLUMNS   (1L << 12)      // SELECT, intern
-#define OPTION_WARNINGS         (1L << 13)      // THD, user
-#define OPTION_AUTO_IS_NULL     (1L << 14)      // THD, user, binlog
-#define OPTION_FOUND_COMMENT    (1L << 15)      // SELECT, intern, parser
-#define OPTION_SAFE_UPDATES     (1L << 16)      // THD, user
-#define OPTION_BUFFER_RESULT    (1L << 17)      // SELECT, user
-#define OPTION_BIN_LOG          (1L << 18)      // THD, user
-#define OPTION_NOT_AUTOCOMMIT   (1L << 19)      // THD, user
-#define OPTION_BEGIN            (1L << 20)      // THD, intern
-#define OPTION_TABLE_LOCK       (1L << 21)      // THD, intern
-#define OPTION_QUICK            (1L << 22)      // SELECT (for DELETE)
-#define OPTION_QUOTE_SHOW_CREATE (1L << 23)     // THD, user
+#define SELECT_DISTINCT         (ULL(1) << 0)     // SELECT, user
+#define SELECT_STRAIGHT_JOIN    (ULL(1) << 1)     // SELECT, user
+#define SELECT_DESCRIBE         (ULL(1) << 2)     // SELECT, user
+#define SELECT_SMALL_RESULT     (ULL(1) << 3)     // SELECT, user
+#define SELECT_BIG_RESULT       (ULL(1) << 4)     // SELECT, user
+#define OPTION_FOUND_ROWS       (ULL(1) << 5)     // SELECT, user
+#define OPTION_TO_QUERY_CACHE   (ULL(1) << 6)     // SELECT, user
+#define SELECT_NO_JOIN_CACHE    (ULL(1) << 7)     // intern
+#define OPTION_BIG_TABLES       (ULL(1) << 8)     // THD, user
+#define OPTION_BIG_SELECTS      (ULL(1) << 9)     // THD, user
+#define OPTION_LOG_OFF          (ULL(1) << 10)    // THD, user
+#define OPTION_UPDATE_LOG       (ULL(1) << 11)    // THD, user, unused
+#define TMP_TABLE_ALL_COLUMNS   (ULL(1) << 12)    // SELECT, intern
+#define OPTION_WARNINGS         (ULL(1) << 13)    // THD, user
+#define OPTION_AUTO_IS_NULL     (ULL(1) << 14)    // THD, user, binlog
+#define OPTION_FOUND_COMMENT    (ULL(1) << 15)    // SELECT, intern, parser
+#define OPTION_SAFE_UPDATES     (ULL(1) << 16)    // THD, user
+#define OPTION_BUFFER_RESULT    (ULL(1) << 17)    // SELECT, user
+#define OPTION_BIN_LOG          (ULL(1) << 18)    // THD, user
+#define OPTION_NOT_AUTOCOMMIT   (ULL(1) << 19)    // THD, user
+#define OPTION_BEGIN            (ULL(1) << 20)    // THD, intern
+#define OPTION_TABLE_LOCK       (ULL(1) << 21)    // THD, intern
+#define OPTION_QUICK            (ULL(1) << 22)    // SELECT (for DELETE)
+#define OPTION_QUOTE_SHOW_CREATE (ULL(1) << 23)   // THD, user
 
 /* Thr following is used to detect a conflict with DISTINCT
    in the user query has requested */
-#define SELECT_ALL              (1L << 24)      // SELECT, user, parser
+#define SELECT_ALL              (ULL(1) << 24)    // SELECT, user, parser
 
 /* Set if we are updating a non-transaction safe table */
-#define OPTION_STATUS_NO_TRANS_UPDATE   (1L << 25) // THD, intern
+#define OPTION_STATUS_NO_TRANS_UPDATE   (ULL(1) << 25) // THD, intern
 
 /* The following can be set when importing tables in a 'wrong order'
    to suppress foreign key checks */
-#define OPTION_NO_FOREIGN_KEY_CHECKS    (1L << 26) // THD, user, binlog
+#define OPTION_NO_FOREIGN_KEY_CHECKS    (ULL(1) << 26) // THD, user, binlog
 /* The following speeds up inserts to InnoDB tables by suppressing unique
    key checks in some cases */
-#define OPTION_RELAXED_UNIQUE_CHECKS    (1L << 27) // THD, user, binlog
-#define SELECT_NO_UNLOCK                (1L << 28) // SELECT, intern
-#define OPTION_SCHEMA_TABLE             (1L << 29) // SELECT, intern
+#define OPTION_RELAXED_UNIQUE_CHECKS    (ULL(1) << 27) // THD, user, binlog
+#define SELECT_NO_UNLOCK                (ULL(1) << 28) // SELECT, intern
+#define OPTION_SCHEMA_TABLE             (ULL(1) << 29) // SELECT, intern
 /* Flag set if setup_tables already done */
-#define OPTION_SETUP_TABLES_DONE        (1L << 30) // intern
+#define OPTION_SETUP_TABLES_DONE        (ULL(1) << 30) // intern
 /* If not set then the thread will ignore all warnings with level notes. */
-#define OPTION_SQL_NOTES                (1UL << 31) // THD, user
+#define OPTION_SQL_NOTES                (ULL(1) << 31) // THD, user
 /* 
   Force the used temporary table to be a MyISAM table (because we will use
   fulltext functions when reading from it.
 */
-#define TMP_TABLE_FORCE_MYISAM          (LL(1) << 32)
+#define TMP_TABLE_FORCE_MYISAM          (ULL(1) << 32)
+#define OPTION_PROFILING                (ULL(1) << 33)
+
 
 /*
   Maximum length of time zone name that we support
@@ -541,6 +548,8 @@ typedef my_bool (*qc_engine_callback)(TH
 #include "field.h"				/* Field definitions */
 #include "protocol.h"
 #include "sql_udf.h"
+#include "sql_profile.h"
+
 class user_var_entry;
 class Security_context;
 enum enum_var_type
@@ -1681,3 +1690,5 @@ inline void kill_delayed_threads(void) {
 #endif
 
 #endif /* MYSQL_CLIENT */
+
+#endif

--- 1.584/sql/mysqld.cc	2007-02-19 21:56:54 -05:00
+++ 1.585/sql/mysqld.cc	2007-02-19 21:56:54 -05:00
@@ -1653,7 +1653,7 @@ void end_thread(THD *thd, bool put_in_ca
       ! abort_loop && !kill_cached_threads)
   {
     /* Don't kill the thread, just put it in cache for reuse */
-    DBUG_PRINT("info", ("Adding thread to cache"))
+    DBUG_PRINT("info", ("Adding thread to cache"));
     cached_thread_count++;
     while (!abort_loop && ! wake_thread && ! kill_cached_threads)
       (void) pthread_cond_wait(&COND_thread_cache, &LOCK_thread_count);
@@ -4692,6 +4692,7 @@ enum options_mysqld
   OPT_TABLE_LOCK_WAIT_TIMEOUT,
   OPT_PORT_OPEN_TIMEOUT,
   OPT_MERGE,
+  OPT_PROFILING,
   OPT_INNODB_ROLLBACK_ON_TIMEOUT
 };
 
@@ -5249,6 +5250,12 @@ Disable with --skip-ndbcluster (will sav
    "Maximum time in seconds to wait for the port to become free. "
    "(Default: no wait)", (gptr*) &mysqld_port_timeout,
    (gptr*) &mysqld_port_timeout, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#ifdef ENABLED_PROFILING
+  {"profiling_history_size", OPT_PROFILING, "Limit of query profiling memory",
+   (gptr*) &global_system_variables.profiling_history_size,
+   (gptr*) &max_system_variables.profiling_history_size,
+   0, GET_UINT, REQUIRED_ARG, 15, 50, 100, 0, 0, 0},
+#endif
   {"relay-log", OPT_RELAY_LOG,
    "The location and name to use for relay logs.",
    (gptr*) &opt_relay_logname, (gptr*) &opt_relay_logname, 0,

--- 1.287/sql/slave.cc	2007-02-19 21:56:54 -05:00
+++ 1.288/sql/slave.cc	2007-02-19 21:56:54 -05:00
@@ -1558,7 +1558,7 @@ static int create_table_from_dump(THD* t
   /* Create the table. We do not want to log the "create table" statement */
   save_options = thd->options;
   thd->options &= ~(ulong) (OPTION_BIN_LOG);
-  thd->proc_info = "Creating table from master dump";
+  thd_proc_info(thd, "Creating table from master dump");
   // save old db in case we are creating in a different database
   save_db = thd->db;
   save_db_length= thd->db_length;
@@ -1572,7 +1572,7 @@ static int create_table_from_dump(THD* t
   if (thd->query_error)
     goto err;			// mysql_parse took care of the error send
 
-  thd->proc_info = "Opening master dump table";
+  thd_proc_info(thd, "Opening master dump table");
   tables.lock_type = TL_WRITE;
   if (!open_ltable(thd, &tables, TL_WRITE))
   {
@@ -1581,7 +1581,7 @@ static int create_table_from_dump(THD* t
   }
   
   file = tables.table->file;
-  thd->proc_info = "Reading master dump table data";
+  thd_proc_info(thd, "Reading master dump table data");
   /* Copy the data file */
   if (file->net_read_dump(net))
   {
@@ -1593,7 +1593,7 @@ static int create_table_from_dump(THD* t
 
   check_opt.init();
   check_opt.flags|= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK;
-  thd->proc_info = "Rebuilding the index on master dump table";
+  thd_proc_info(thd, "Rebuilding the index on master dump table");
   /*
     We do not want repair() to spam us with messages
     just send them to the error log, and report the failure in case of
@@ -2924,9 +2924,9 @@ static int init_slave_thread(THD* thd, S
 #endif
 
   if (thd_type == SLAVE_THD_SQL)
-    thd->proc_info= "Waiting for the next event in relay log";
+    thd_proc_info(thd, "Waiting for the next event in relay log");
   else
-    thd->proc_info= "Waiting for master update";
+    thd_proc_info(thd, "Waiting for master update");
   thd->version=refresh_version;
   thd->set_time();
   DBUG_RETURN(0);
@@ -3486,7 +3486,7 @@ slave_begin:
     goto err;
   }
 
-  thd->proc_info = "Connecting to master";
+  thd_proc_info(thd, "Connecting to master");
   // we can get killed during safe_connect
   if (!safe_connect(thd, mysql, mi))
   {
@@ -3513,7 +3513,7 @@ connected:
   // TODO: the assignment below should be under mutex (5.0)
   mi->slave_running= MYSQL_SLAVE_RUN_CONNECT;
   thd->slave_net = &mysql->net;
-  thd->proc_info = "Checking master version";
+  thd_proc_info(thd, "Checking master version");
   if (get_master_version_and_clock(mysql, mi))
     goto err;
 
@@ -3524,7 +3524,7 @@ connected:
       If fails, this is not fatal - we just print the error message and go
       on with life.
     */
-    thd->proc_info = "Registering slave on master";
+    thd_proc_info(thd, "Registering slave on master");
     if (register_slave_on_master(mysql) ||  update_slave_list(mysql, mi))
       goto err;
   }
@@ -3533,7 +3533,7 @@ connected:
   while (!io_slave_killed(thd,mi))
   {
     bool suppress_warnings= 0;
-    thd->proc_info = "Requesting binlog dump";
+    thd_proc_info(thd, "Requesting binlog dump");
     if (request_dump(mysql, mi, &suppress_warnings))
     {
       sql_print_error("Failed on request_dump()");
@@ -3545,7 +3545,7 @@ dump");
       }
 
       mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
-      thd->proc_info= "Waiting to reconnect after a failed binlog dump request";
+      thd_proc_info(thd, "Waiting to reconnect after a failed binlog dump request");
 #ifdef SIGNAL_WITH_VIO_CLOSE
       thd->clear_active_vio();
 #endif
@@ -3569,7 +3569,7 @@ dump");
 	goto err;
       }
 
-      thd->proc_info = "Reconnecting after a failed binlog dump request";
+      thd_proc_info(thd, "Reconnecting after a failed binlog dump request");
       if (!suppress_warnings)
 	sql_print_error("Slave I/O thread: failed dump request, \
 reconnecting to try again, log '%s' at postion %s", IO_RPL_LOG_NAME,
@@ -3594,7 +3594,7 @@ after reconnect");
          important thing is to not confuse users by saying "reading" whereas
          we're in fact receiving nothing.
       */
-      thd->proc_info = "Waiting for master to send event";
+      thd_proc_info(thd, "Waiting for master to send event");
       ulong event_len = read_event(mysql, mi, &suppress_warnings);
       if (io_slave_killed(thd,mi))
       {
@@ -3622,7 +3622,7 @@ max_allowed_packet",
 	  goto err;
 	}
         mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
-	thd->proc_info = "Waiting to reconnect after a failed master event read";
+	thd_proc_info(thd, "Waiting to reconnect after a failed master event read");
 #ifdef SIGNAL_WITH_VIO_CLOSE
         thd->clear_active_vio();
 #endif
@@ -3641,7 +3641,7 @@ max_allowed_packet",
 reconnect after a failed read");
 	  goto err;
 	}
-	thd->proc_info = "Reconnecting after a failed master event read";
+	thd_proc_info(thd, "Reconnecting after a failed master event read");
 	if (!suppress_warnings)
 	  sql_print_information("Slave I/O thread: Failed reading log event, \
 reconnecting to retry, log '%s' position %s", IO_RPL_LOG_NAME,
@@ -3658,7 +3658,7 @@ reconnect done to recover from failed re
       } // if (event_len == packet_error)
 
       retry_count=0;			// ok event, reset retry counter
-      thd->proc_info = "Queueing master event to the relay log";
+      thd_proc_info(thd, "Queueing master event to the relay log");
       if (queue_event(mi,(const char*)mysql->net.read_pos + 1,
 		      event_len))
       {
@@ -3740,7 +3740,7 @@ err:
     mi->mysql=0;
   }
   write_ignored_events_info_to_relay_log(thd, mi);
-  thd->proc_info = "Waiting for slave mutex on exit";
+  thd_proc_info(thd, "Waiting for slave mutex on exit");
   pthread_mutex_lock(&mi->run_lock);
 
   /* Forget the relay log's format */
@@ -3912,7 +3912,7 @@ Slave SQL thread aborted. Can't execute 
 
   while (!sql_slave_killed(thd,rli))
   {
-    thd->proc_info = "Reading event from the relay log";
+    thd_proc_info(thd, "Reading event from the relay log");
     DBUG_ASSERT(rli->sql_thd == thd);
     THD_CHECK_SENTRY(thd);
     if (exec_relay_log_event(thd,rli))
@@ -3944,7 +3944,7 @@ the slave SQL thread with \"SLAVE START\
   thd->query= 0; 
   thd->query_length= 0;
   VOID(pthread_mutex_unlock(&LOCK_thread_count));
-  thd->proc_info = "Waiting for slave mutex on exit";
+  thd_proc_info(thd, "Waiting for slave mutex on exit");
   pthread_mutex_lock(&rli->run_lock);
   /* We need data_lock, at least to wake up any waiting master_pos_wait() */
   pthread_mutex_lock(&rli->data_lock);

--- 1.360/sql/sql_base.cc	2007-02-19 21:56:54 -05:00
+++ 1.361/sql/sql_base.cc	2007-02-19 21:56:54 -05:00
@@ -308,7 +308,7 @@ bool close_cached_tables(THD *thd, bool 
     */
     thd->mysys_var->current_mutex= &LOCK_open;
     thd->mysys_var->current_cond= &COND_refresh;
-    thd->proc_info="Flushing tables";
+    thd_proc_info(thd, "Flushing tables");
 
     close_old_data_files(thd,thd->open_tables,1,1);
     mysql_ha_flush(thd, tables, MYSQL_HA_REOPEN_ON_USAGE | MYSQL_HA_FLUSH_ALL,
@@ -350,7 +350,7 @@ bool close_cached_tables(THD *thd, bool 
     pthread_mutex_lock(&thd->mysys_var->mutex);
     thd->mysys_var->current_mutex= 0;
     thd->mysys_var->current_cond= 0;
-    thd->proc_info=0;
+    thd_proc_info(thd, 0);
     pthread_mutex_unlock(&thd->mysys_var->mutex);
   }
   DBUG_RETURN(result);
@@ -1072,7 +1072,7 @@ void wait_for_refresh(THD *thd)
   thd->mysys_var->current_mutex= &LOCK_open;
   thd->mysys_var->current_cond= &COND_refresh;
   proc_info=thd->proc_info;
-  thd->proc_info="Waiting for table";
+  thd_proc_info(thd, "Waiting for table");
   if (!thd->killed)
     (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
 
@@ -1080,7 +1080,7 @@ void wait_for_refresh(THD *thd)
   pthread_mutex_lock(&thd->mysys_var->mutex);
   thd->mysys_var->current_mutex= 0;
   thd->mysys_var->current_cond= 0;
-  thd->proc_info= proc_info;
+  thd_proc_info(thd, proc_info);
   pthread_mutex_unlock(&thd->mysys_var->mutex);
   DBUG_VOID_RETURN;
 }
@@ -1785,7 +1785,7 @@ bool wait_for_tables(THD *thd)
   bool result;
   DBUG_ENTER("wait_for_tables");
 
-  thd->proc_info="Waiting for tables";
+  thd_proc_info(thd, "Waiting for tables");
   pthread_mutex_lock(&LOCK_open);
   while (!thd->killed)
   {
@@ -1801,12 +1801,12 @@ bool wait_for_tables(THD *thd)
   else
   {
     /* Now we can open all tables without any interference */
-    thd->proc_info="Reopen tables";
+    thd_proc_info(thd, "Reopen tables");
     thd->version= refresh_version;
     result=reopen_tables(thd,0,0);
   }
   pthread_mutex_unlock(&LOCK_open);
-  thd->proc_info=0;
+  thd_proc_info(thd, 0);
   DBUG_RETURN(result);
 }
 
@@ -2105,7 +2105,7 @@ int open_tables(THD *thd, TABLE_LIST **s
  restart:
   *counter= 0;
   query_tables_last_own= 0;
-  thd->proc_info="Opening tables";
+  thd_proc_info(thd, "Opening tables");
 
   /*
     If we are not already executing prelocked statement and don't have
@@ -2285,7 +2285,7 @@ process_view_routines:
   }
 
  err:
-  thd->proc_info=0;
+  thd_proc_info(thd, 0);
   free_root(&new_frm_mem, MYF(0));              // Free pre-alloced block
 
   if (query_tables_last_own)
@@ -2359,7 +2359,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST 
   bool refresh;
   DBUG_ENTER("open_ltable");
 
-  thd->proc_info="Opening table";
+  thd_proc_info(thd, "Opening table");
   thd->current_tablenr= 0;
   /* open_ltable can be used only for BASIC TABLEs */
   table_list->required_type= FRMTYPE_TABLE;
@@ -2393,7 +2393,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST 
 	  table= 0;
     }
   }
-  thd->proc_info=0;
+  thd_proc_info(thd, 0);
   DBUG_RETURN(table);
 }
 
@@ -5394,7 +5394,7 @@ int init_ftfuncs(THD *thd, SELECT_LEX *s
     List_iterator<Item_func_match> li(*(select_lex->ftfunc_list));
     Item_func_match *ifm;
     DBUG_PRINT("info",("Performing FULLTEXT search"));
-    thd->proc_info="FULLTEXT initialization";
+    thd_proc_info(thd, "FULLTEXT initialization");
 
     while ((ifm=li++))
       ifm->init_search(no_order);

--- 1.258/sql/sql_class.cc	2007-02-19 21:56:54 -05:00
+++ 1.259/sql/sql_class.cc	2007-02-19 21:56:54 -05:00
@@ -247,6 +247,9 @@ THD::THD()
   init();
   /* Initialize sub structures */
   init_sql_alloc(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
+#ifdef ENABLED_PROFILING
+  profiling.set_thd(this);
+#endif
   user_connect=(USER_CONN *)0;
   hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
 	    (hash_get_key) get_var_key,

--- 1.317/sql/sql_class.h	2007-02-19 21:56:54 -05:00
+++ 1.318/sql/sql_class.h	2007-02-19 21:56:54 -05:00
@@ -521,6 +521,7 @@ struct system_variables
   ulong optimizer_prune_level;
   ulong optimizer_search_depth;
   ulong preload_buff_size;
+  ulong profiling_history_size;
   ulong query_cache_type;
   ulong read_buff_size;
   ulong read_rnd_buff_size;
@@ -1154,6 +1155,9 @@ public:
     Points to info-string that we show in SHOW PROCESSLIST
     You are supposed to update thd->proc_info only if you have coded
     a time-consuming piece that MySQL can get stuck in for a long time.
+
+    Set it using the  thd_proc_info(THD *thread, const char *message)
+    macro/function.
   */
   const char *proc_info;
 
@@ -1308,6 +1312,10 @@ public:
   List	     <MYSQL_ERROR> warn_list;
   uint	     warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
   uint	     total_warn_count;
+#ifdef ENABLED_PROFILING
+  PROFILING  profiling;
+#endif
+
   /*
     Id of current query. Statement can be reused to execute several queries
     query_id is global in context of the whole MySQL server.

--- 1.188/sql/sql_delete.cc	2007-02-19 21:56:54 -05:00
+++ 1.189/sql/sql_delete.cc	2007-02-19 21:56:54 -05:00
@@ -54,7 +54,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *
     table->file->print_error(error, MYF(0));
     DBUG_RETURN(error);
   }
-  thd->proc_info="init";
+  thd_proc_info(thd, "init");
   table->map=1;
 
   if (mysql_prepare_delete(thd, table_list, &conds))
@@ -205,7 +205,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *
 
   deleted=0L;
   init_ftfuncs(thd, select_lex, 1);
-  thd->proc_info="updating";
+  thd_proc_info(thd, "updating");
 
   if (table->triggers)
     table->triggers->mark_fields_used(thd, TRG_EVENT_DELETE);
@@ -261,7 +261,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *
   }
   if (thd->killed && !error)
     error= 1;					// Aborted
-  thd->proc_info="end";
+  thd_proc_info(thd, "end");
   end_read_record(&info);
   free_io_cache(table);				// Will not do any harm
   if (options & OPTION_QUICK)
@@ -485,7 +485,7 @@ multi_delete::prepare(List<Item> &values
   DBUG_ENTER("multi_delete::prepare");
   unit= u;
   do_delete= 1;
-  thd->proc_info="deleting from main table";
+  thd_proc_info(thd, "deleting from main table");
   DBUG_RETURN(0);
 }
 
@@ -749,7 +749,7 @@ int multi_delete::do_deletes()
 
 bool multi_delete::send_eof()
 {
-  thd->proc_info="deleting from reference tables";
+  thd_proc_info(thd, "deleting from reference tables");
 
   /* Does deletes for the last n - 1 tables, returns 0 if ok */
   int local_error= do_deletes();		// returns 0 if success
@@ -758,7 +758,7 @@ bool multi_delete::send_eof()
   local_error= local_error || error;
 
   /* reset used flags */
-  thd->proc_info="end";
+  thd_proc_info(thd, "end");
 
   /*
     We must invalidate the query cache before binlog writing and

--- 1.211/sql/sql_insert.cc	2007-02-19 21:56:55 -05:00
+++ 1.212/sql/sql_insert.cc	2007-02-19 21:56:55 -05:00
@@ -397,7 +397,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
   if (res || thd->is_fatal_error)
     DBUG_RETURN(TRUE);
 
-  thd->proc_info="init";
+  thd_proc_info(thd, "init");
   thd->used_tables=0;
   values= its++;
 
@@ -470,7 +470,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
 
   error=0;
   id=0;
-  thd->proc_info="update";
+  thd_proc_info(thd, "update");
   if (duplic != DUP_ERROR || ignore)
     table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
   if (duplic == DUP_REPLACE)
@@ -676,7 +676,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
       thd->lock=0;
     }
   }
-  thd->proc_info="end";
+  thd_proc_info(thd, "end");
   table->next_number_field=0;
   thd->count_cuted_fields= CHECK_FIELD_IGNORE;
   thd->next_insert_id=0;			// Reset this if wrongly used
@@ -1407,7 +1407,7 @@ I_List<delayed_insert> delayed_threads;
 
 delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
 {
-  thd->proc_info="waiting for delay_list";
+  thd_proc_info(thd, "waiting for delay_list");
   pthread_mutex_lock(&LOCK_delayed_insert);	// Protect master list
   I_List_iterator<delayed_insert> it(delayed_threads);
   delayed_insert *tmp;
@@ -1444,7 +1444,7 @@ static TABLE *delayed_get_table(THD *thd
     */
     if (delayed_insert_threads >= thd->variables.max_insert_delayed_threads)
       DBUG_RETURN(0);
-    thd->proc_info="Creating delayed handler";
+    thd_proc_info(thd, "Creating delayed handler");
     pthread_mutex_lock(&LOCK_delayed_create);
     /*
       The first search above was done without LOCK_delayed_create.
@@ -1486,13 +1486,13 @@ static TABLE *delayed_get_table(THD *thd
       }
 
       /* Wait until table is open */
-      thd->proc_info="waiting for handler open";
+      thd_proc_info(thd, "waiting for handler open");
       while (!tmp->thd.killed && !tmp->table && !thd->killed)
       {
 	pthread_cond_wait(&tmp->cond_client,&tmp->mutex);
       }
       pthread_mutex_unlock(&tmp->mutex);
-      thd->proc_info="got old table";
+      thd_proc_info(thd, "got old table");
       if (tmp->thd.killed)
       {
 	if (tmp->thd.is_fatal_error)
@@ -1552,13 +1552,13 @@ TABLE *delayed_insert::get_local_table(T
   tables_in_use++;
   if (!thd.lock)				// Table is not locked
   {
-    client_thd->proc_info="waiting for handler lock";
+    thd_proc_info(client_thd, "waiting for handler lock");
     pthread_cond_signal(&cond);			// Tell handler to lock table
     while (!dead && !thd.lock && ! client_thd->killed)
     {
       pthread_cond_wait(&cond_client,&mutex);
     }
-    client_thd->proc_info="got handler lock";
+    thd_proc_info(client_thd, "got handler lock");
     if (client_thd->killed)
       goto error;
     if (dead)
@@ -1576,7 +1576,7 @@ TABLE *delayed_insert::get_local_table(T
     bytes. Since the table copy is used for creating one record only,
     the other record buffers and alignment are unnecessary.
   */
-  client_thd->proc_info="allocating local table";
+  thd_proc_info(client_thd, "allocating local table");
   copy= (TABLE*) client_thd->alloc(sizeof(*copy)+
 				   (table->s->fields+1)*sizeof(Field**)+
 				   table->s->reclength);
@@ -1656,11 +1656,11 @@ static int write_delayed(THD *thd,TABLE 
   delayed_insert *di=thd->di;
   DBUG_ENTER("write_delayed");
 
-  thd->proc_info="waiting for handler insert";
+  thd_proc_info(thd, "waiting for handler insert");
   pthread_mutex_lock(&di->mutex);
   while (di->stacked_inserts >= delayed_queue_size && !thd->killed)
     pthread_cond_wait(&di->cond_client,&di->mutex);
-  thd->proc_info="storing row into queue";
+  thd_proc_info(thd, "storing row into queue");
 
   if (thd->killed || !(row= new delayed_row(duplic, ignore, log_on)))
     goto err;

--- 1.208/sql/sql_lex.cc	2007-02-19 21:56:55 -05:00
+++ 1.209/sql/sql_lex.cc	2007-02-19 21:56:55 -05:00
@@ -173,7 +173,9 @@ void lex_start(THD *thd, uchar *buf,uint
   lex->proc_list.first= 0;
   lex->escape_used= FALSE;
   lex->reset_query_tables_list(FALSE);
-
+#ifdef ENABLED_PROFILING
+  lex->profile_options= PROFILE_NONE;
+#endif
   lex->nest_level=0 ;
   lex->allow_sum_func= 0;
   lex->in_sum_func= NULL;

--- 1.235/sql/sql_lex.h	2007-02-19 21:56:55 -05:00
+++ 1.236/sql/sql_lex.h	2007-02-19 21:56:55 -05:00
@@ -94,8 +94,13 @@ enum enum_sql_command {
   SQLCOM_XA_START, SQLCOM_XA_END, SQLCOM_XA_PREPARE,
   SQLCOM_XA_COMMIT, SQLCOM_XA_ROLLBACK, SQLCOM_XA_RECOVER,
   SQLCOM_SHOW_PROC_CODE, SQLCOM_SHOW_FUNC_CODE,
-  /* This should be the last !!! */
+  SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
 
+  /*
+    When a command is added here, be sure it's also added in mysqld.cc
+    in "struct show_var_st status_vars[]= {" ...
+  */
+  /* This should be the last !!! */
   SQLCOM_END
 };
 
@@ -939,6 +944,9 @@ typedef struct st_lex : public Query_tab
   enum enum_var_type option_type;
   enum enum_view_create_mode create_view_mode;
   enum enum_drop_mode drop_mode;
+
+  uint profile_query_id;
+  uint profile_options;
   uint uint_geom_type;
   uint grant, grant_tot_col, which_columns;
   uint fk_delete_opt, fk_update_opt, fk_match_option;

--- 1.597/sql/sql_parse.cc	2007-02-19 21:56:55 -05:00
+++ 1.598/sql/sql_parse.cc	2007-02-19 21:56:55 -05:00
@@ -1058,7 +1058,7 @@ void execute_init_command(THD *thd, sys_
   Vio* save_vio;
   ulong save_client_capabilities;
 
-  thd->proc_info= "Execution of init_command";
+  thd_proc_info(thd, "Execution of init_command");
   /*
     We need to lock init_command_var because
     during execution of init_command_var query
@@ -1158,7 +1158,7 @@ pthread_handler_t handle_one_connection(
       net->compress=1;				// Use compression
 
     thd->version= refresh_version;
-    thd->proc_info= 0;
+    thd_proc_info(thd, 0);
     thd->command= COM_SLEEP;
     thd->set_time();
     thd->init_for_queries();
@@ -1175,7 +1175,7 @@ pthread_handler_t handle_one_connection(
                           sctx->host_or_ip, "init_connect command failed");
         sql_print_warning("%s", net->last_error);
       }
-      thd->proc_info=0;
+      thd_proc_info(thd, 0);
       thd->set_time();
       thd->init_for_queries();
     }
@@ -1258,7 +1258,7 @@ pthread_handler_t handle_bootstrap(void 
   if (thd->variables.max_join_size == HA_POS_ERROR)
     thd->options |= OPTION_BIG_SELECTS;
 
-  thd->proc_info=0;
+  thd_proc_info(thd, 0);
   thd->version=refresh_version;
   thd->security_ctx->priv_user=
     thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
@@ -2105,7 +2105,7 @@ bool dispatch_command(enum enum_server_c
   if (thd->lock || thd->open_tables || thd->derived_tables ||
       thd->prelocked_mode)
   {
-    thd->proc_info="closing tables";
+    thd_proc_info(thd, "closing tables");
     close_thread_tables(thd);			/* Free tables */
   }
   /*
@@ -2128,9 +2128,9 @@ bool dispatch_command(enum enum_server_c
 
   log_slow_statement(thd);
 
-  thd->proc_info="cleaning up";
+  thd_proc_info(thd, "cleaning up");
   VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
-  thd->proc_info=0;
+  thd_proc_info(thd, 0);
   thd->command=COM_SLEEP;
   thd->query=0;
   thd->query_length=0;
@@ -2163,7 +2163,7 @@ void log_slow_statement(THD *thd)
   */
   if (thd->enable_slow_log && !thd->user_time)
   {
-    thd->proc_info="logging slow query";
+    thd_proc_info(thd, "logging slow query");
 
     if ((ulong) (thd->start_time - thd->time_after_lock) >
 	thd->variables.long_query_time ||
@@ -2697,6 +2697,37 @@ mysql_execute_command(THD *thd)
 			      (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
     break;
   }
+  case SQLCOM_SHOW_PROFILES:
+  {
+#ifdef ENABLED_PROFILING
+    thd->profiling.store();
+    thd->profiling.discard();
+    res= thd->profiling.show_profiles();
+    if (res)
+      goto error;
+#else
+    my_error(ER_FEATURE_DISABLED, MYF(0), "query profiling", "enable-profiling");
+    goto error;
+#endif
+    break;
+  }
+  case SQLCOM_SHOW_PROFILE:
+  {
+#ifdef ENABLED_PROFILING
+    thd->profiling.store();
+    thd->profiling.discard(); // will get re-enabled by reset()
+    if (lex->profile_query_id != 0)
+      res= thd->profiling.show(lex->profile_options, lex->profile_query_id);
+    else
+      res= thd->profiling.show_last(lex->profile_options);
+    if (res)
+      goto error;
+#else
+    my_error(ER_FEATURE_DISABLED, MYF(0), "query profiling", "enable-profiling");
+    goto error;
+#endif
+    break;
+  }
   case SQLCOM_SHOW_NEW_MASTER:
   {
     if (check_global_access(thd, REPL_SLAVE_ACL))
@@ -3544,7 +3575,7 @@ end_with_restore_list:
     if (add_item_to_list(thd, new Item_null()))
       goto error;
 
-    thd->proc_info="init";
+    thd_proc_info(thd, "init");
     if ((res= open_and_lock_tables(thd, all_tables)))
       break;
 
@@ -4973,7 +5004,7 @@ create_sp_error:
     send_ok(thd);
     break;
   }
-  thd->proc_info="query end";
+  thd_proc_info(thd, "query end");
   /* Two binlog-related cleanups: */
 
   /*
@@ -5144,6 +5175,7 @@ check_access(THD *thd, ulong want_access
   else
     save_priv= &dummy;
 
+  thd_proc_info(thd, "checking permissions");
   if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
   {
     DBUG_PRINT("error",("No database"));
@@ -5590,6 +5622,9 @@ mysql_init_query(THD *thd, uchar *buf, u
   DBUG_ENTER("mysql_init_query");
   lex_start(thd, buf, length);
   mysql_reset_thd_for_next_command(thd);
+#ifdef ENABLED_PROFILING
+  thd->profiling.reset();
+#endif
   DBUG_VOID_RETURN;
 }
 
@@ -5854,7 +5889,7 @@ void mysql_parse(THD *thd, char *inBuf, 
       query_cache_abort(&thd->net);
       lex->unit.cleanup();
     }
-    thd->proc_info="freeing items";
+    thd_proc_info(thd, "freeing items");
     thd->end_statement();
     thd->cleanup_after_query();
     DBUG_ASSERT(thd->change_list.is_empty());

--- 1.480/sql/sql_select.cc	2007-02-19 21:56:55 -05:00
+++ 1.481/sql/sql_select.cc	2007-02-19 21:56:55 -05:00
@@ -744,7 +744,7 @@ JOIN::optimize()
   sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables);
 
   /* Calculate how to do the join */
-  thd->proc_info= "statistics";
+  thd_proc_info(thd, "statistics");
   if (make_join_statistics(this, select_lex->leaf_tables, conds, &keyuse) ||
       thd->is_fatal_error)
   {
@@ -754,7 +754,7 @@ JOIN::optimize()
 
   /* Remove distinct if only const tables */
   select_distinct= select_distinct && (const_tables != tables);
-  thd->proc_info= "preparing";
+  thd_proc_info(thd, "preparing");
   if (result->initialize_tables(this))
   {
     DBUG_PRINT("error",("Error: initialize_tables() failed"));
@@ -1160,7 +1160,7 @@ JOIN::optimize()
   if (need_tmp)
   {
     DBUG_PRINT("info",("Creating tmp table"));
-    thd->proc_info="Creating tmp table";
+    thd_proc_info(thd, "Creating tmp table");
 
     init_items_ref_array();
 
@@ -1209,7 +1209,7 @@ JOIN::optimize()
     if (group_list && simple_group)
     {
       DBUG_PRINT("info",("Sorting for group"));
-      thd->proc_info="Sorting for group";
+      thd_proc_info(thd, "Sorting for group");
       if (create_sort_index(thd, this, group_list,
 			    HA_POS_ERROR, HA_POS_ERROR) ||
 	  alloc_group_fields(this, group_list) ||
@@ -1226,7 +1226,7 @@ JOIN::optimize()
       if (!group_list && ! exec_tmp_table1->distinct && order && simple_order)
       {
 	DBUG_PRINT("info",("Sorting for order"));
-	thd->proc_info="Sorting for order";
+	thd_proc_info(thd, "Sorting for order");
 	if (create_sort_index(thd, this, order,
                               HA_POS_ERROR, HA_POS_ERROR))
 	  DBUG_RETURN(1);
@@ -1492,7 +1492,7 @@ JOIN::exec()
     curr_tmp_table= exec_tmp_table1;
 
     /* Copy data to the temporary table */
-    thd->proc_info= "Copying to tmp table";
+    thd_proc_info(thd, "Copying to tmp table");
     DBUG_PRINT("info", ("%s", thd->proc_info));
     if ((tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table, 0)))
     {
@@ -1615,7 +1615,7 @@ JOIN::exec()
       }
       if (curr_join->group_list)
       {
-	thd->proc_info= "Creating sort index";
+	thd_proc_info(thd, "Creating sort index");
 	if (curr_join->join_tab == join_tab && save_join_tab())
 	{
 	  DBUG_VOID_RETURN;
@@ -1629,7 +1629,7 @@ JOIN::exec()
         sortorder= curr_join->sortorder;
       }
       
-      thd->proc_info="Copying to group table";
+      thd_proc_info(thd, "Copying to group table");
       DBUG_PRINT("info", ("%s", thd->proc_info));
       tmp_error= -1;
       if (curr_join != this)
@@ -1685,7 +1685,7 @@ JOIN::exec()
     curr_join->join_free();			/* Free quick selects */
     if (curr_join->select_distinct && ! curr_join->group_list)
     {
-      thd->proc_info="Removing duplicates";
+      thd_proc_info(thd, "Removing duplicates");
       if (curr_join->tmp_having)
 	curr_join->tmp_having->update_used_tables();
       if (remove_duplicates(curr_join, curr_tmp_table,
@@ -1746,7 +1746,7 @@ JOIN::exec()
   if (curr_join->group_list || curr_join->order)
   {
     DBUG_PRINT("info",("Sorting for send_fields"));
-    thd->proc_info="Sorting result";
+    thd_proc_info(thd, "Sorting result");
     /* If we have already done the group, add HAVING to sorted table */
     if (curr_join->tmp_having && ! curr_join->group_list && 
 	! curr_join->sort_and_group)
@@ -1870,7 +1870,7 @@ JOIN::exec()
   }
   else
   {
-    thd->proc_info="Sending data";
+    thd_proc_info(thd, "Sending data");
     DBUG_PRINT("info", ("%s", thd->proc_info));
     result->send_fields((procedure ? curr_join->procedure_fields_list :
                          *curr_fields_list),
@@ -2018,7 +2018,7 @@ mysql_select(THD *thd, Item ***rref_poin
   {
     if (!(join= new JOIN(thd, fields, select_options, result)))
 	DBUG_RETURN(TRUE);
-    thd->proc_info="init";
+    thd_proc_info(thd, "init");
     thd->used_tables=0;                         // Updated by setup_fields
     if (err= join->prepare(rref_pointer_array, tables, wild_num,
                            conds, og_num, order, group, having, proc_param,
@@ -2063,7 +2063,7 @@ mysql_select(THD *thd, Item ***rref_poin
 err:
   if (free_join)
   {
-    thd->proc_info="end";
+    thd_proc_info(thd, "end");
     err|= select_lex->cleanup();
     DBUG_RETURN(err || thd->net.report_error);
   }
@@ -9744,7 +9744,7 @@ free_tmp_table(THD *thd, TABLE *entry)
   DBUG_PRINT("enter",("table: %s",entry->alias));
 
   save_proc_info=thd->proc_info;
-  thd->proc_info="removing tmp table";
+  thd_proc_info(thd, "removing tmp table");
 
   if (entry->file)
   {
@@ -9772,7 +9772,7 @@ free_tmp_table(THD *thd, TABLE *entry)
     bitmap_clear_bit(&temp_pool, entry->temp_pool_slot);
 
   free_root(&own_root, MYF(0)); /* the table is allocated in its own root */
-  thd->proc_info=save_proc_info;
+  thd_proc_info(thd, save_proc_info);
 
   DBUG_VOID_RETURN;
 }
@@ -9802,7 +9802,7 @@ bool create_myisam_from_heap(THD *thd, T
     DBUG_RETURN(1);				// End of memory
 
   save_proc_info=thd->proc_info;
-  thd->proc_info="converting HEAP to MyISAM";
+  thd_proc_info(thd, "converting HEAP to MyISAM");
 
   if (create_myisam_tmp_table(&new_table,param,
 			      thd->lex->select_lex.options | thd->options))
@@ -9855,8 +9855,8 @@ bool create_myisam_from_heap(THD *thd, T
   table->s= &table->share_not_to_be_used;
   table->file->change_table_ptr(table);
   if (save_proc_info)
-    thd->proc_info= (!strcmp(save_proc_info,"Copying to tmp table") ?
-                     "Copying to tmp table on disk" : save_proc_info);
+    thd_proc_info(thd, (!strcmp(save_proc_info,"Copying to tmp table") ?
+                  "Copying to tmp table on disk" : save_proc_info));
   DBUG_RETURN(0);
 
  err:
@@ -9868,7 +9868,7 @@ bool create_myisam_from_heap(THD *thd, T
   new_table.file->delete_table(new_table.s->table_name);
   delete new_table.file;
  err2:
-  thd->proc_info=save_proc_info;
+  thd_proc_info(thd, save_proc_info);
   DBUG_RETURN(1);
 }
 

--- 1.334/sql/sql_show.cc	2007-02-19 21:56:55 -05:00
+++ 1.335/sql/sql_show.cc	2007-02-19 21:56:55 -05:00
@@ -44,7 +44,7 @@ static void
 append_algorithm(TABLE_LIST *table, String *buff);
 static int
 view_store_create_info(THD *thd, TABLE_LIST *table, String *buff);
-static bool schema_table_store_record(THD *thd, TABLE *table);
+bool schema_table_store_record(THD *thd, TABLE *table);
 
 
 /***************************************************************************
@@ -1349,7 +1349,7 @@ void mysqld_list_processes(THD *thd,cons
 
 #if !defined(DONT_USE_THR_ALARM) && ! defined(SCO)
         if (pthread_kill(tmp->real_id,0))
-          tmp->proc_info="*** DEAD ***";        // This shouldn't happen
+          thd_proc_info(tmp, "*** DEAD ***");        // This shouldn't happen
 #endif
 #ifdef EXTRA_DEBUG
         thd_info->start_time= tmp->time_after_lock;
@@ -1843,7 +1843,7 @@ typedef struct st_index_field_values
     1	                  error
 */
 
-static bool schema_table_store_record(THD *thd, TABLE *table)
+bool schema_table_store_record(THD *thd, TABLE *table)
 {
   int error;
   if ((error= table->file->write_row(table->record[0])))
@@ -4301,6 +4301,8 @@ ST_SCHEMA_TABLE schema_tables[]=
     get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0},
   {"OPEN_TABLES", open_tables_fields_info, create_schema_table,
    fill_open_tables, make_old_format, 0, -1, -1, 1},
+  {"PROFILING", query_profile_statistics_info, create_schema_table,
+    fill_query_profile_statistics_info, NULL, NULL, -1, -1, false},
   {"ROUTINES", proc_fields_info, create_schema_table, 
     fill_schema_proc, make_proc_old_format, 0, -1, -1, 0},
   {"SCHEMATA", schema_fields_info, create_schema_table,

--- 1.329/sql/sql_table.cc	2007-02-19 21:56:55 -05:00
+++ 1.330/sql/sql_table.cc	2007-02-19 21:56:55 -05:00
@@ -1729,7 +1729,7 @@ bool mysql_create_table(THD *thd,const c
     }
   }
 
-  thd->proc_info="creating table";
+  thd_proc_info(thd, "creating table");
   create_info->table_existed= 0;		// Mark that table is created
 
   if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
@@ -1760,7 +1760,7 @@ bool mysql_create_table(THD *thd,const c
 
 end:
   VOID(pthread_mutex_unlock(&LOCK_open));
-  thd->proc_info="After create";
+  thd_proc_info(thd, "After create");
   DBUG_RETURN(error);
 
 warn:
@@ -2879,7 +2879,7 @@ mysql_discard_or_import_tablespace(THD *
     ALTER TABLE
   */
 
-  thd->proc_info="discard_or_import_tablespace";
+  thd_proc_info(thd, "discard_or_import_tablespace");
 
   discard= test(tablespace_op == DISCARD_TABLESPACE);
 
@@ -2896,7 +2896,7 @@ mysql_discard_or_import_tablespace(THD *
 
   error=table->file->discard_or_import_tablespace(discard);
 
-  thd->proc_info="end";
+  thd_proc_info(thd, "end");
 
   if (error)
     goto err;
@@ -3007,7 +3007,7 @@ bool mysql_alter_table(THD *thd,char *ne
   frm_type_enum frm_type;
   DBUG_ENTER("mysql_alter_table");
 
-  thd->proc_info="init";
+  thd_proc_info(thd, "init");
   table_name=table_list->table_name;
   alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
 
@@ -3142,7 +3142,7 @@ view_err:
     DBUG_RETURN(TRUE);
   }
   
-  thd->proc_info="setup";
+  thd_proc_info(thd, "setup");
   if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
       !table->s->tmp_table) // no need to touch frm
   {
@@ -3173,7 +3173,7 @@ view_err:
 
     if (!error && (new_name != table_name || new_db != db))
     {
-      thd->proc_info="rename";
+      thd_proc_info(thd, "rename");
       /* Then do a 'simple' rename of the table */
       if (!access(new_name_buff,F_OK))
       {
@@ -3627,7 +3627,7 @@ view_err:
   /* We don't want update TIMESTAMP fields during ALTER TABLE. */
   thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
   thd->cuted_fields=0L;
-  thd->proc_info="copy to tmp table";
+  thd_proc_info(thd, "copy to tmp table");
   next_insert_id=thd->next_insert_id;		// Remember for logging
   copied=deleted=0;
   if (new_table && !new_table->s->is_view)
@@ -3711,7 +3711,7 @@ view_err:
     from the cache, free all locks, close the old table and remove it.
   */
 
-  thd->proc_info="rename result table";
+  thd_proc_info(thd, "rename result table");
   my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
 	      current_pid, thd->thread_id);
   if (lower_case_table_names)
@@ -3822,7 +3822,7 @@ view_err:
     broadcast_refresh();
     goto err;
   }
-  thd->proc_info="end";
+  thd_proc_info(thd, "end");
   if (mysql_bin_log.is_open())
   {
     thd->clear_error();

--- 1.207/sql/sql_update.cc	2007-02-19 21:56:55 -05:00
+++ 1.208/sql/sql_update.cc	2007-02-19 21:56:55 -05:00
@@ -166,7 +166,7 @@ int mysql_update(THD *thd,
        mysql_handle_derived(thd->lex, &mysql_derived_filling)))
     DBUG_RETURN(1);
 
-  thd->proc_info="init";
+  thd_proc_info(thd, "init");
   table= table_list->table;
   table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
 
@@ -358,7 +358,7 @@ int mysql_update(THD *thd,
       else
         init_read_record_idx(&info, thd, table, 1, used_index);
 
-      thd->proc_info="Searching rows for update";
+      thd_proc_info(thd, "Searching rows for update");
       uint tmp_limit= limit;
 
       while (!(error=info.read_record(&info)) && !thd->killed)
@@ -423,7 +423,7 @@ int mysql_update(THD *thd,
   updated= found= 0;
   thd->count_cuted_fields= CHECK_FIELD_WARN;		/* calc cuted fields */
   thd->cuted_fields=0L;
-  thd->proc_info="Updating";
+  thd_proc_info(thd, "Updating");
   query_id=thd->query_id;
 
   transactional_table= table->file->has_transactions();
@@ -511,7 +511,7 @@ int mysql_update(THD *thd,
   end_read_record(&info);
   free_io_cache(table);				// If ORDER BY
   delete select;
-  thd->proc_info="end";
+  thd_proc_info(thd, "end");
   VOID(table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY));
 
   /*
@@ -959,7 +959,7 @@ int multi_update::prepare(List<Item> &no
 
   thd->count_cuted_fields= CHECK_FIELD_WARN;
   thd->cuted_fields=0L;
-  thd->proc_info="updating main table";
+  thd_proc_info(thd, "updating main table");
 
   tables_to_update= get_table_map(fields);
 
@@ -1511,11 +1511,11 @@ err2:
 bool multi_update::send_eof()
 {
   char buff[STRING_BUFFER_USUAL_SIZE];
-  thd->proc_info="updating reference tables";
+  thd_proc_info(thd, "updating reference tables");
 
   /* Does updates for the last n - 1 tables, returns 0 if ok */
   int local_error = (table_count) ? do_updates(0) : 0;
-  thd->proc_info= "end";
+  thd_proc_info(thd, "end");
 
   /* We must invalidate the query cache before binlog writing and
   ha_autocommit_... */

--- 1.499/sql/sql_yacc.yy	2007-02-19 21:56:55 -05:00
+++ 1.500/sql/sql_yacc.yy	2007-02-19 21:56:55 -05:00
@@ -180,6 +180,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  BIT_SYM
 %token  BIT_XOR
 %token  BLOB_SYM
+%token  BLOCK_SYM
 %token  BOOLEAN_SYM
 %token  BOOL_SYM
 %token  BOTH
@@ -220,10 +221,12 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  CONSISTENT_SYM
 %token  CONSTRAINT
 %token  CONTAINS_SYM
+%token  CONTEXT_SYM
 %token  CONTINUE_SYM
 %token  CONVERT_SYM
 %token  CONVERT_TZ_SYM
 %token  COUNT_SYM
+%token  CPU_SYM
 %token  CREATE
 %token  CROSS
 %token  CUBE_SYM
@@ -297,6 +300,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  EXTRACT_SYM
 %token  FALSE_SYM
 %token  FAST_SYM
+%token  FAULTS_SYM
 %token  FETCH_SYM
 %token  FIELD_FUNC
 %token  FILE_SYM
@@ -366,6 +370,8 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  INT_SYM
 %token  INVOKER_SYM
 %token  IN_SYM
+%token  IO_SYM
+%token  IPC_SYM
 %token  IS
 %token  ISOLATION
 %token  ISSUER_SYM
@@ -434,6 +440,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  MEDIUMINT
 %token  MEDIUMTEXT
 %token  MEDIUM_SYM
+%token  MEMORY_SYM
 %token  MERGE_SYM
 %token  MICROSECOND_SYM
 %token  MIGRATE_SYM
@@ -492,6 +499,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  OUTFILE
 %token  OUT_SYM
 %token  PACK_KEYS_SYM
+%token  PAGE_SYM
 %token  PARTIAL
 %token  PASSWORD
 %token  PARAM_MARKER
@@ -509,6 +517,8 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  PROCEDURE
 %token  PROCESS
 %token  PROCESSLIST_SYM
+%token  PROFILE_SYM
+%token  PROFILES_SYM
 %token  PURGE
 %token  QUARTER_SYM
 %token  QUERY_SYM
@@ -579,6 +589,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  SMALLINT
 %token  SNAPSHOT_SYM
 %token  SOUNDS_SYM
+%token  SOURCE_SYM
 %token  SPATIAL_SYM
 %token  SPECIFIC_SYM
 %token  SQLEXCEPTION_SYM
@@ -609,6 +620,8 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  SUM_SYM
 %token  SUPER_SYM
 %token  SUSPEND_SYM
+%token  SWAPS_SYM
+%token  SWITCHES_SYM
 %token  SYSDATE
 %token  TABLES
 %token  TABLESPACE
@@ -6471,6 +6484,64 @@ opt_table_sym:
 	/* empty */
 	| TABLE_SYM;
 
+opt_profile_defs:
+  /* empty */
+  | profile_defs
+
+profile_defs:
+  profile_def
+  | profile_defs ',' profile_def
+
+profile_def:
+  CPU_SYM
+    {
+      Lex->profile_options|= PROFILE_CPU;
+    }
+  | MEMORY_SYM
+    {
+      Lex->profile_options|= PROFILE_MEMORY;
+    }
+  | BLOCK_SYM IO_SYM
+    {
+      Lex->profile_options|= PROFILE_BLOCK_IO;
+    }
+  | CONTEXT_SYM SWITCHES_SYM
+    {
+      Lex->profile_options|= PROFILE_CONTEXT;
+    }
+  | PAGE_SYM FAULTS_SYM
+    {
+      Lex->profile_options|= PROFILE_PAGE_FAULTS;
+    }
+  | IPC_SYM
+    {
+      Lex->profile_options|= PROFILE_IPC;
+    }
+  | SWAPS_SYM
+    {
+      Lex->profile_options|= PROFILE_SWAPS;
+    }
+  | SOURCE_SYM
+    {
+      Lex->profile_options|= PROFILE_SOURCE;
+    }
+  | ALL
+    {
+      Lex->profile_options|= PROFILE_ALL;
+    }
+  ;
+
+opt_profile_args:
+  /* empty */
+    {
+      Lex->profile_query_id= 0;
+    }
+  | FOR_SYM QUERY_SYM NUM
+    {
+      Lex->profile_query_id= atoi($3.str);
+    }
+  ;
+
 /* Show things */
 
 show:	SHOW
@@ -6606,6 +6677,10 @@ show_param:
           { Lex->sql_command = SQLCOM_SHOW_WARNS;}
         | ERRORS opt_limit_clause_init
           { Lex->sql_command = SQLCOM_SHOW_ERRORS;}
+        | PROFILES_SYM
+          { Lex->sql_command = SQLCOM_SHOW_PROFILES; }
+        | PROFILE_SYM opt_profile_defs opt_profile_args opt_limit_clause_init
+          { Lex->sql_command = SQLCOM_SHOW_PROFILE; }
         | opt_var_type STATUS_SYM wild_and_where
           {
             LEX *lex= Lex;
@@ -7697,6 +7772,7 @@ keyword_sp:
 	| BERKELEY_DB_SYM	{}
 	| BINLOG_SYM		{}
 	| BIT_SYM		{}
+	| BLOCK_SYM             {}
 	| BOOL_SYM		{}
 	| BOOLEAN_SYM		{}
 	| BTREE_SYM		{}
@@ -7713,6 +7789,8 @@ keyword_sp:
 	| COMPRESSED_SYM	{}
 	| CONCURRENT		{}
 	| CONSISTENT_SYM	{}
+	| CONTEXT_SYM           {}
+	| CPU_SYM               {}
 	| CUBE_SYM		{}
 	| DATA_SYM		{}
 	| DATETIME		{}
@@ -7735,6 +7813,7 @@ keyword_sp:
         | EXPANSION_SYM         {}
 	| EXTENDED_SYM		{}
 	| FAST_SYM		{}
+	| FAULTS_SYM            {}
 	| FOUND_SYM		{}
 	| DISABLE_SYM		{}
 	| ENABLE_SYM		{}
@@ -7759,6 +7838,8 @@ keyword_sp:
 	| ISSUER_SYM		{}
 	| INNOBASE_SYM		{}
 	| INSERT_METHOD		{}
+	| IO_SYM                {}
+	| IPC_SYM               {}
 	| RELAY_THREAD		{}
 	| LAST_SYM		{}
 	| LEAVES                {}
@@ -7788,6 +7869,7 @@ keyword_sp:
 	| MAX_UPDATES_PER_HOUR	{}
 	| MAX_USER_CONNECTIONS_SYM {}
 	| MEDIUM_SYM		{}
+	| MEMORY_SYM            {}
 	| MERGE_SYM		{}
 	| MICROSECOND_SYM	{}
         | MIGRATE_SYM           {}
@@ -7814,6 +7896,7 @@ keyword_sp:
 	| ONE_SHOT_SYM		{}
         | ONE_SYM               {}
 	| PACK_KEYS_SYM		{}
+	| PAGE_SYM              {}
 	| PARTIAL		{}
 	| PASSWORD		{}
         | PHASE_SYM             {}
@@ -7823,6 +7906,8 @@ keyword_sp:
         | PRIVILEGES            {}
 	| PROCESS		{}
 	| PROCESSLIST_SYM	{}
+	| PROFILE_SYM           {}
+	| PROFILES_SYM          {}
 	| QUARTER_SYM		{}
 	| QUERY_SYM		{}
 	| QUICK			{}
@@ -7856,6 +7941,7 @@ keyword_sp:
 	| SHUTDOWN		{}
 	| SNAPSHOT_SYM		{}
 	| SOUNDS_SYM		{}
+	| SOURCE_SYM            {}
 	| SQL_CACHE_SYM		{}
 	| SQL_BUFFER_RESULT	{}
 	| SQL_NO_CACHE_SYM	{}
@@ -7867,6 +7953,8 @@ keyword_sp:
 	| SUBJECT_SYM		{}
 	| SUPER_SYM		{}
         | SUSPEND_SYM           {}
+        | SWAPS_SYM             {}
+	| SWITCHES_SYM          {}
         | TABLES                {}
 	| TABLESPACE		{}
 	| TEMPORARY		{}

--- 1.137/sql/table.h	2007-02-19 21:56:55 -05:00
+++ 1.138/sql/table.h	2007-02-19 21:56:55 -05:00
@@ -308,6 +308,7 @@ enum enum_schema_tables
   SCH_COLUMN_PRIVILEGES,
   SCH_KEY_COLUMN_USAGE,
   SCH_OPEN_TABLES,
+  SCH_PROFILING,
   SCH_PROCEDURES,
   SCH_SCHEMATA,
   SCH_SCHEMA_PRIVILEGES,

--- 1.101/sql/sql_view.cc	2007-02-19 21:56:55 -05:00
+++ 1.102/sql/sql_view.cc	2007-02-19 21:56:55 -05:00
@@ -588,7 +588,7 @@ bool mysql_create_view(THD *thd, TABLE_L
   DBUG_RETURN(0);
 
 err:
-  thd->proc_info= "end";
+  thd_proc_info(thd, "end");
   lex->link_first_table_back(view, link_to_local);
   unit->cleanup();
   DBUG_RETURN(res || thd->net.report_error);

--- 1.79/sql/ha_archive.cc	2007-02-19 21:56:55 -05:00
+++ 1.80/sql/ha_archive.cc	2007-02-19 21:56:55 -05:00
@@ -1205,7 +1205,7 @@ int ha_archive::check(THD* thd, HA_CHECK
   ha_rows count= share->rows_recorded;
   DBUG_ENTER("ha_archive::check");
 
-  thd->proc_info= "Checking table";
+  thd_proc_info(thd, "Checking table");
   /* Flush any waiting data */
   gzflush(share->archive_write, Z_SYNC_FLUSH);
 
@@ -1229,7 +1229,7 @@ int ha_archive::check(THD* thd, HA_CHECK
 
   my_free((char*)buf, MYF(0));
 
-  thd->proc_info= old_proc_info;
+  thd_proc_info(thd, old_proc_info);
 
   if ((rc && rc != HA_ERR_END_OF_FILE) || count)  
   {
--- New file ---
+++ mysql-test/r/profiling.result	07/02/19 21:56:45
show session variables like 'profil%';
Variable_name	Value
profiling	OFF
profiling_history_size	15
select @@profiling;
@@profiling
0
set global profiling = ON;
ERROR HY000: Variable 'profiling' is a SESSION variable and can't be used with SET GLOBAL
set global profiling_history_size=100;
show global variables like 'profil%';
Variable_name	Value
profiling	OFF
profiling_history_size	100
set session profiling = ON;
set session profiling_history_size=97;
show session variables like 'profil%';
Variable_name	Value
profiling	ON
profiling_history_size	97
select @@profiling;
@@profiling
1
create table t1 (
a int,
b int
);
insert into t1 values (1,1), (2,null), (3, 4);
insert into t1 values (5,1), (6,null), (7, 4);
insert into t1 values (1,1), (2,null), (3, 4);
insert into t1 values (5,1), (6,null), (7, 4);
select max(x) from (select sum(a) as x from t1 group by b) as teeone;
insert into t1 select * from t1;
select count(*) from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
select count(*) from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
select count(*) from t1;
select sum(a) from t1;
select sum(a) from t1 group by b;
select sum(a) + sum(b) from t1 group by b;
select max(x) from (select sum(a) as x from t1 group by b) as teeone;
show profiles;
show profile for query 15;
show profile cpu for query 15;
show profile cpu, block io for query 15;
show profile cpu for query 9 limit 2 offset 2;
show profile cpu for query 10 limit 0;
show profile cpu for query 65534;
show profile memory;
show profile block io;
show profile context switches;
show profile page faults;
show profile ipc;
show profile swaps limit 1 offset 2;
show profile source;
show profile all for query 0 limit 0;
show profile all for query 15;
show profile all for query 29;
select * from information_schema.profiling;
select query_id, state, duration from information_schema.profiling;
select query_id, sum(duration) from information_schema.profiling group by query_id;
select query_id, count(*) from information_schema.profiling group by query_id;
select sum(duration) from information_schema.profiling;
select query_id, count(*), sum(duration) from information_schema.profiling group by query_id;
select CPU_user, CPU_system, Context_voluntary, Context_involuntary, Block_ops_in, Block_ops_out, Messages_sent, Messages_received, Page_faults_major, Page_faults_minor, Swaps, Source_function, Source_file, Source_line from information_schema.profiling;
set session profiling = OFF;
insert into t1 values (5,1), (6,null), (7, 4);
insert into t1 values (5,1), (6,null), (7, 4);
select sum(a) from t1;
select sum(a) from t1 group by b;
show profiles;
drop table t1;
End of 5.0 tests

--- New file ---
+++ mysql-test/t/profiling-master.opt	07/02/19 21:56:45
--port-open-timeout=42

--- New file ---
+++ mysql-test/t/profiling.test	07/02/19 21:56:45
# default is OFF
show session variables like 'profil%';
select @@profiling;

# setting global variable is an error
--error ER_LOCAL_VARIABLE
set global profiling = ON;

# But size is okay
set global profiling_history_size=100;
show global variables like 'profil%';

# turn on for testing
set session profiling = ON;
set session profiling_history_size=97;

# verify it is active
show session variables like 'profil%';
select @@profiling;

#   Profiling is a descriptive look into the way the server operated
#   in retrospect.  Chad doesn't think it's wise to include the result
#   log, as this creates a proscriptive specification about how the 
#   server should work in the future -- or it forces everyone who 
#   changes the server significantly to record the test results again,
#   and that violates the spirit of our tests.
#
#   So, yes, comment out "--disable_result_log" on the next line, to
#   play around with this test, but don't commit it, okay?
--disable_result_log


create table t1 (
  a int,
  b int
);
insert into t1 values (1,1), (2,null), (3, 4);
insert into t1 values (5,1), (6,null), (7, 4);
insert into t1 values (1,1), (2,null), (3, 4);
insert into t1 values (5,1), (6,null), (7, 4);
select max(x) from (select sum(a) as x from t1 group by b) as teeone;
insert into t1 select * from t1;
select count(*) from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
select count(*) from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
select count(*) from t1;
select sum(a) from t1;
select sum(a) from t1 group by b;
select sum(a) + sum(b) from t1 group by b;
select max(x) from (select sum(a) as x from t1 group by b) as teeone;

###--replace_column 2 #
show profiles;

###--replace_column 2 # 3 # 4 #
show profile for query 15;
###--replace_column 2 # 3 # 4 #
show profile cpu for query 15;
###--replace_column 2 # 3 # 4 # 5 # 6 #
show profile cpu, block io for query 15;
###--replace_column 2 # 3 # 4 #
show profile cpu for query 9 limit 2 offset 2;
show profile cpu for query 10 limit 0;
--error 0,ER_WRONG_ARGUMENTS
show profile cpu for query 65534;
###--replace_column 2 #
show profile memory;
###--replace_column 2 # 3 # 4 #
show profile block io;
###--replace_column 2 # 3 # 4 #
show profile context switches;
###--replace_column 2 # 3 # 4 #
show profile page faults;
###--replace_column 2 # 3 # 4 #
show profile ipc;
###--replace_column 2 #
show profile swaps limit 1 offset 2;
###--replace_column 2 # 5 #
show profile source;
show profile all for query 0 limit 0;
###--replace_column 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 # 10 # 11 # 12 # 13 # 16 #
show profile all for query 15;
###--replace_column 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 # 10 # 11 # 12 # 13 # 16 #
show profile all for query 29;

select * from information_schema.profiling;
select query_id, state, duration from information_schema.profiling;
select query_id, sum(duration) from information_schema.profiling group by query_id;
select query_id, count(*) from information_schema.profiling group by query_id;
select sum(duration) from information_schema.profiling;

# Broken down into number of stages and duration of each query.
select query_id, count(*), sum(duration) from information_schema.profiling group by query_id;
select CPU_user, CPU_system, Context_voluntary, Context_involuntary, Block_ops_in, Block_ops_out, Messages_sent, Messages_received, Page_faults_major, Page_faults_minor, Swaps, Source_function, Source_file, Source_line from information_schema.profiling;

# Verify that turning it off does freeze it.
set session profiling = OFF;
insert into t1 values (5,1), (6,null), (7, 4);
insert into t1 values (5,1), (6,null), (7, 4);
select sum(a) from t1;
select sum(a) from t1 group by b;
###--replace_column 2 #
show profiles;

drop table t1;
--enable_result_log

##
--echo End of 5.0 tests

--- New file ---
+++ sql/sql_profile.cc	07/02/19 21:56:45
/* Copyright (C) 2005 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; either version 2 of the License, or
   (at your option) any later version.

   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 "mysql_priv.h"
#include "my_sys.h"

#define TIME_FLOAT_DIGITS 7

bool schema_table_store_record(THD *thd, TABLE *table);

/* Reserved for systems that can't record the function name in source. */
const char *_db_func_= "<unknown>";

/**
  Connects Information_Schema and Profiling.
*/
int fill_query_profile_statistics_info(THD *thd, struct st_table_list *tables, 
                                       Item *cond)
{
#ifdef ENABLED_PROFILING
  return(thd->profiling.fill_statistics_info(thd, tables, cond));
#else
  return(1);
#endif
}

ST_FIELD_INFO query_profile_statistics_info[]=
{
  /* name, length, type, value, maybe_null, old_name */
  {"Query_id", 20, MYSQL_TYPE_LONG, 0, false, NULL},
  {"Order", 20, MYSQL_TYPE_LONG, 0, false, NULL},
  {"State", 30, MYSQL_TYPE_STRING, 0, false, NULL},
  {"Duration", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, false, NULL},
  {"CPU_user", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, true, NULL},
  {"CPU_system", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, true, NULL},
  {"Context_voluntary", 20, MYSQL_TYPE_LONG, 0, true, NULL},
  {"Context_involuntary", 20, MYSQL_TYPE_LONG, 0, true, NULL},
  {"Block_ops_in", 20, MYSQL_TYPE_LONG, 0, true, NULL},
  {"Block_ops_out", 20, MYSQL_TYPE_LONG, 0, true, NULL},
  {"Messages_sent", 20, MYSQL_TYPE_LONG, 0, true, NULL},
  {"Messages_received", 20, MYSQL_TYPE_LONG, 0, true, NULL},
  {"Page_faults_major", 20, MYSQL_TYPE_LONG, 0, true, NULL},
  {"Page_faults_minor", 20, MYSQL_TYPE_LONG, 0, true, NULL},
  {"Swaps", 20, MYSQL_TYPE_LONG, 0, true, NULL},
  {"Source_function", 30, MYSQL_TYPE_STRING, 0, true, NULL},
  {"Source_file", 20, MYSQL_TYPE_STRING, 0, true, NULL},
  {"Source_line", 20, MYSQL_TYPE_LONG, 0, true, NULL},
  {NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL}
};

#ifdef ENABLED_PROFILING

#define RUSAGE_USEC(tv)  ((tv).tv_sec*1000*1000 + (tv).tv_usec)
#define RUSAGE_DIFF_USEC(tv1, tv2) (RUSAGE_USEC((tv1))-RUSAGE_USEC((tv2)))

PROFILE_ENTRY::PROFILE_ENTRY()
  :profile(NULL), status(NULL), function(NULL), file(NULL), line(0), 
  time_usecs(0.0), allocated_status_memory(NULL)
{
  collect();
}

PROFILE_ENTRY::PROFILE_ENTRY(QUERY_PROFILE *profile_arg, const char *status_arg)
  :profile(profile_arg)
{
  collect();
  set_status(status_arg, NULL, NULL, 0);
}

PROFILE_ENTRY::PROFILE_ENTRY(QUERY_PROFILE *profile_arg, const char *status_arg,
                             const char *function_arg,
                             const char *file_arg, unsigned int line_arg)
  :profile(profile_arg)
{
  collect();
  set_status(status_arg, function_arg, file_arg, line_arg);
}

PROFILE_ENTRY::~PROFILE_ENTRY()
{
  if (allocated_status_memory != NULL)
    my_free(status, MYF(0));
  status= function= file= NULL;
}
  
void PROFILE_ENTRY::set_status(const char *status_arg, const char *function_arg, const char *file_arg, unsigned int line_arg)
{
  size_t sizes[3];                              /* 3 == status+function+file */
  char *cursor;

  /*
    Compute all the space we'll need to allocate one block for everything
    we'll need, instead of N mallocs.
  */
  if (status_arg != NULL)
    sizes[0]= strlen(status_arg) + 1;
  else
    sizes[0]= 0;

  if (function_arg != NULL)
    sizes[1]= strlen(function_arg) + 1;
  else
    sizes[1]= 0;

  if (file_arg != NULL)
    sizes[2]= strlen(file_arg) + 1;
  else
    sizes[2]= 0;
    
  allocated_status_memory= (char *) my_malloc(sizes[0] + sizes[1] + sizes[2], MYF(0));
  DBUG_ASSERT(allocated_status_memory != NULL);

  cursor= allocated_status_memory;

  if (status_arg != NULL)
  {
    strcpy(cursor, status_arg);
    status= cursor;
    cursor+= sizes[0];
  }
  else
    status= NULL;

  if (function_arg != NULL)
  {
    strcpy(cursor, function_arg);
    function= cursor;
    cursor+= sizes[1];
  }
  else
    function= NULL;

  if (file_arg != NULL)
  {
    strcpy(cursor, file_arg);
    file= cursor;
    cursor+= sizes[2];
  }
  else
    file= NULL;

  line= line_arg;
}

void PROFILE_ENTRY::collect()
{
  time_usecs= (double) my_getsystime() / 10.0;  /* 1 sec was 1e7, now is 1e6 */
#ifdef HAVE_GETRUSAGE
  getrusage(RUSAGE_SELF, &rusage);
#endif
}

QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg, 
                             const char *query_source_arg)
  :profiling(profiling_arg), profiling_query_id(-1)
{
  profile_end= &profile_start;
  query_source= my_strdup(query_source_arg, MYF(0));
}

QUERY_PROFILE::~QUERY_PROFILE()
{
  PROFILE_ENTRY *entry;
  List_iterator<PROFILE_ENTRY> it(entries);
  while ((entry= it++) != NULL)
    delete entry;
  entries.empty();

  if (query_source != NULL)
    my_free(query_source, MYF(0));
}

void QUERY_PROFILE::status(const char *status_arg,
                     const char *function_arg= NULL,
                     const char *file_arg= NULL, unsigned int line_arg= 0)
{
  THD *thd= profiling->thd;
  PROFILE_ENTRY *prof;
  MEM_ROOT *saved_mem_root;
  DBUG_ENTER("QUERY_PROFILE::status");

  /* Blank status.  Just return, and thd->proc_info will be set blank later. */
  if (unlikely(status_arg == NULL))
    DBUG_VOID_RETURN;

  /* If thd->proc_info is currently set to status_arg, don't profile twice. */
  if (likely((thd->proc_info != NULL) && 
      ((thd->proc_info == status_arg) || 
       (strcmp(thd->proc_info, status_arg) == 0))))
    DBUG_VOID_RETURN;

  /* Is this the same query as our profile currently contains? */
  if (unlikely((thd->query_id != profiling_query_id) && !thd->spcont))
    reset();
    
  /*
    In order to keep the profile information between queries (i.e. from
    SELECT to the following SHOW PROFILE command) the following code is
    necessary to keep the profile from getting freed automatically when
    mysqld cleans up after the query.

    The "entries" list allocates is memory from the current thd's mem_root.
    We substitute our mem_root temporarily so that we intercept those
    allocations into our own mem_root.
    
    The thd->mem_root structure is freed after each query is completed,
    so temporarily override it.
  */
  saved_mem_root= thd->mem_root;
  thd->mem_root= &profiling->mem_root;
  //thd->mem_root= NULL;

  if (function_arg && file_arg) 
  {
    if ((profile_end= prof= new PROFILE_ENTRY(this, status_arg, function_arg, 
                                              file_arg, line_arg)))
      entries.push_back(prof);
  } 
  else 
  {
    if ((profile_end= prof= new PROFILE_ENTRY(this, status_arg)))
      entries.push_back(prof);
  }

  /* Restore mem_root */
  thd->mem_root= saved_mem_root;

  DBUG_VOID_RETURN;
}

void QUERY_PROFILE::reset()
{
  DBUG_ENTER("QUERY_PROFILE::reset");
  if (likely(profiling->thd->query_id != profiling_query_id))
  {
    profiling_query_id= profiling->thd->query_id;
    profile_start.collect();
    entries.empty();
  }
  DBUG_VOID_RETURN;
}

bool QUERY_PROFILE::show(uint options)
{  
  THD *thd= profiling->thd;
  List<Item> field_list;
  DBUG_ENTER("QUERY_PROFILE::show");

  field_list.push_back(new Item_empty_string("Status", MYSQL_ERRMSG_SIZE));
  field_list.push_back(new Item_return_int("Duration", TIME_FLOAT_DIGITS,
                                           MYSQL_TYPE_DOUBLE));

  if (options & PROFILE_CPU)
  {
    field_list.push_back(new Item_return_int("CPU_user", TIME_FLOAT_DIGITS,
                                             MYSQL_TYPE_DOUBLE));
    field_list.push_back(new Item_return_int("CPU_system", TIME_FLOAT_DIGITS, 
                                             MYSQL_TYPE_DOUBLE));
  }
  
  if (options & PROFILE_MEMORY)
  {
  }
  
  if (options & PROFILE_CONTEXT)
  {
    field_list.push_back(new Item_return_int("Context_voluntary", 10,
                                             MYSQL_TYPE_LONG));
    field_list.push_back(new Item_return_int("Context_involuntary", 10,
                                             MYSQL_TYPE_LONG));
  }

  if (options & PROFILE_BLOCK_IO)
  {
    field_list.push_back(new Item_return_int("Block_ops_in", 10,
                                             MYSQL_TYPE_LONG));
    field_list.push_back(new Item_return_int("Block_ops_out", 10,
                                             MYSQL_TYPE_LONG));
  }
  
  if (options & PROFILE_IPC)
  {
    field_list.push_back(new Item_return_int("Messages_sent", 10,
                                             MYSQL_TYPE_LONG));
    field_list.push_back(new Item_return_int("Messages_received", 10,
                                             MYSQL_TYPE_LONG));
  }
  
  if (options & PROFILE_PAGE_FAULTS)
  {
    field_list.push_back(new Item_return_int("Page_faults_major", 10,
                                             MYSQL_TYPE_LONG));
    field_list.push_back(new Item_return_int("Page_faults_minor", 10,
                                             MYSQL_TYPE_LONG));
  }
  
  if (options & PROFILE_SWAPS)
  {
    field_list.push_back(new Item_return_int("Swaps", 10, MYSQL_TYPE_LONG));
  }
  
  if (options & PROFILE_SOURCE)
  {
    field_list.push_back(new Item_empty_string("Source_function",
                                               MYSQL_ERRMSG_SIZE));  
    field_list.push_back(new Item_empty_string("Source_file",
                                               MYSQL_ERRMSG_SIZE));
    field_list.push_back(new Item_return_int("Source_line", 10,
                                             MYSQL_TYPE_LONG));
  }
  
  if (thd->protocol->send_fields(&field_list,
                                 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
    DBUG_RETURN(TRUE);

  Protocol *protocol= thd->protocol;
  SELECT_LEX *sel= &thd->lex->select_lex;
  SELECT_LEX_UNIT *unit= &thd->lex->unit;
  ha_rows idx= 0;
  unit->set_limit(sel);
  ulonglong last_time= profile_start.time_usecs;
#ifdef HAVE_GETRUSAGE
  struct rusage *last_rusage= &(profile_start.rusage);
#endif

  List_iterator<PROFILE_ENTRY> it(entries);
  PROFILE_ENTRY *entry;
  while ((entry= it++) != NULL)
  {
#ifdef HAVE_GETRUSAGE
    struct rusage *rusage= &(entry->rusage);
#endif
    String elapsed;

    if (++idx <= unit->offset_limit_cnt)
      continue;
    if (idx > unit->select_limit_cnt)
      break;

    protocol->prepare_for_resend();
    protocol->store(entry->status, strlen(entry->status), system_charset_info);
    protocol->store((double)(entry->time_usecs - last_time)/(1000.0*1000),
                    (uint32) TIME_FLOAT_DIGITS-1, &elapsed);
    DBUG_PRINT("chad", ("time  %lu   %20.10lf", entry->time_usecs, (double)(entry->time_usecs)/1000000.0));
    //protocol->store((double)(entry->time - last_time)/(1000*1000*10));

    if (options & PROFILE_CPU)
    {
#ifdef HAVE_GETRUSAGE
      String cpu_utime, cpu_stime;
      protocol->store((double)(RUSAGE_DIFF_USEC(rusage->ru_utime,
                      last_rusage->ru_utime))/(1000.0*1000),
                      (uint32) TIME_FLOAT_DIGITS-1, &cpu_utime);
      protocol->store((double)(RUSAGE_DIFF_USEC(rusage->ru_stime,
                      last_rusage->ru_stime))/(1000.0*1000),
                      (uint32) TIME_FLOAT_DIGITS-1, &cpu_stime);
#else
      protocol->store_null();
      protocol->store_null();
#endif
    }
    
    if (options & PROFILE_CONTEXT)
    {
#ifdef HAVE_GETRUSAGE
      protocol->store((uint32)(rusage->ru_nvcsw - last_rusage->ru_nvcsw));
      protocol->store((uint32)(rusage->ru_nivcsw - last_rusage->ru_nivcsw));
#else
      protocol->store_null();
      protocol->store_null();
#endif
    }

    if (options & PROFILE_BLOCK_IO)
    {
#ifdef HAVE_GETRUSAGE
      protocol->store((uint32)(rusage->ru_inblock - last_rusage->ru_inblock));
      protocol->store((uint32)(rusage->ru_oublock - last_rusage->ru_oublock));
#else
      protocol->store_null();
      protocol->store_null();
#endif
    }
    
    if (options & PROFILE_IPC)
    {
#ifdef HAVE_GETRUSAGE
      protocol->store((uint32)(rusage->ru_msgsnd - last_rusage->ru_msgsnd));
      protocol->store((uint32)(rusage->ru_msgrcv - last_rusage->ru_msgrcv));
#else
      protocol->store_null();
      protocol->store_null();
#endif
    }
    
    if (options & PROFILE_PAGE_FAULTS)
    {
#ifdef HAVE_GETRUSAGE
      protocol->store((uint32)(rusage->ru_majflt - last_rusage->ru_majflt));
      protocol->store((uint32)(rusage->ru_minflt - last_rusage->ru_minflt));
#else
      protocol->store_null();
      protocol->store_null();
#endif
    }

    if (options & PROFILE_SWAPS)
    {
#ifdef HAVE_GETRUSAGE
      protocol->store((uint32)(rusage->ru_nswap - last_rusage->ru_nswap));
#else
      protocol->store_null();
#endif
    }
    
    if (options & PROFILE_SOURCE)
    {
      if ((entry->function != NULL) && (entry->file != NULL))
      {
        protocol->store(entry->function, strlen(entry->function),
                        system_charset_info);        
        protocol->store(entry->file, strlen(entry->file), system_charset_info);
        protocol->store(entry->line);
      } else {
        protocol->store_null();
        protocol->store_null();
        protocol->store_null();
      }
    }

    if (protocol->write())
      DBUG_RETURN(TRUE);

    last_time= entry->time_usecs;
#ifdef HAVE_GETRUSAGE
    last_rusage= &(entry->rusage);
#endif

  }
  send_eof(thd);
  DBUG_RETURN(FALSE);
}

PROFILING::PROFILING()
  :keeping(1), current(NULL), last(NULL)
{
  init_sql_alloc(&mem_root,
                 PROFILE_ALLOC_BLOCK_SIZE,
                 PROFILE_ALLOC_PREALLOC_SIZE);
}

PROFILING::~PROFILING()
{
  QUERY_PROFILE *prof;

  List_iterator<QUERY_PROFILE> it(history);
  while ((prof= it++) != NULL)
  {
    delete prof;
  }
  history.empty();

  if (current != NULL)
    delete current;

  free_root(&mem_root, MYF(0));
}

void PROFILING::status_change(const char *status_arg,
                              const char *function_arg,
                              const char *file_arg, unsigned int line_arg)
{
  DBUG_ENTER("PROFILING::status_change");
  
  if (unlikely(current == NULL))
    reset();

  DBUG_ASSERT(current != NULL);

  if (unlikely((thd->options & OPTION_PROFILING) != 0))
    current->status(status_arg, function_arg, file_arg, line_arg);

  thd->proc_info= status_arg;
  DBUG_VOID_RETURN;
}

void PROFILING::store()
{
  MEM_ROOT *saved_mem_root;
  DBUG_ENTER("PROFILING::store");

  /* Already stored */
  if (unlikely((last != NULL) && 
               (current != NULL) && 
               (last->profiling_query_id == current->profiling_query_id)))
    DBUG_VOID_RETURN;

  if (history.elements > thd->variables.profiling_history_size)
  {
    QUERY_PROFILE *tmp= history.pop();
    delete tmp;
  }

  /* 
    Switch out memory roots so that we're sure that we keep what we need 
    
    The "history" list implementation allocates its memory in the current 
    thd's mem_root.  We substitute our mem_root temporarily so that we
    intercept those allocations into our own mem_root.
  */

  saved_mem_root= thd->mem_root;
  thd->mem_root= &mem_root;
  
  if (current != NULL)
  {
    if (keeping && (!current->entries.is_empty())) 
    {
      last= current; /* never contains something that is not in the history. */
      history.push_back(current);
      current= NULL;
    } 
    else
    {
      delete current;
      current= NULL;
    }
  }
  
  DBUG_ASSERT(current == NULL);
  current= new QUERY_PROFILE(this, thd->query);

  /* Restore memory root */
  thd->mem_root= saved_mem_root;

  DBUG_VOID_RETURN;
}

void PROFILING::reset()
{
  DBUG_ENTER("PROFILING::reset");

  store();
  DBUG_ASSERT(current != NULL);

  current->reset();
  keep();

  DBUG_VOID_RETURN;
}

bool PROFILING::show_profiles()
{
  DBUG_ENTER("PROFILING::show_profiles");
  QUERY_PROFILE *prof;
  List<Item> field_list;

  field_list.push_back(new Item_return_int("Query_ID", 10,
                                           MYSQL_TYPE_LONG));
  field_list.push_back(new Item_return_int("Duration", TIME_FLOAT_DIGITS-1,
                                           MYSQL_TYPE_DOUBLE));
  field_list.push_back(new Item_empty_string("Query", 40));
                                           
  if (thd->protocol->send_fields(&field_list,
                                 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
    DBUG_RETURN(TRUE);
    
  SELECT_LEX *sel= &thd->lex->select_lex;
  SELECT_LEX_UNIT *unit= &thd->lex->unit;
  ha_rows idx= 0;
  Protocol *protocol= thd->protocol;

  unit->set_limit(sel);
  
  List_iterator<QUERY_PROFILE> it(history);
  while ((prof= it++) != NULL)
  {
    String elapsed;

    PROFILE_ENTRY *ps= &prof->profile_start;
    PROFILE_ENTRY *pe=  prof->profile_end;

    if (++idx <= unit->offset_limit_cnt)
      continue;
    if (idx > unit->select_limit_cnt)
      break;

    protocol->prepare_for_resend();
    protocol->store((uint32)(prof->profiling_query_id));
    protocol->store((double)(pe->time_usecs - ps->time_usecs)/(1000.0*1000), 
                    (uint32) TIME_FLOAT_DIGITS-1, &elapsed);
    if (prof->query_source != NULL)
      protocol->store(prof->query_source, strlen(prof->query_source), 
                      system_charset_info);
    else
      protocol->store_null();
    
    if (protocol->write())
      DBUG_RETURN(TRUE);
  }
  send_eof(thd);
  DBUG_RETURN(FALSE);
}

bool PROFILING::show(uint options, uint profiling_query_id)
{
  DBUG_ENTER("PROFILING::show");
  QUERY_PROFILE *prof;

  List_iterator<QUERY_PROFILE> it(history);
  while ((prof= it++) != NULL)
  {
    if(prof->profiling_query_id == profiling_query_id)
      DBUG_RETURN(prof->show(options));
  }

  my_error(ER_WRONG_ARGUMENTS, MYF(0), "SHOW PROFILE");
  DBUG_RETURN(TRUE);
}

bool PROFILING::show_last(uint options)
{
  DBUG_ENTER("PROFILING::show_last");
  if (!history.is_empty()) {
    DBUG_RETURN(last->show(options));
  }
  DBUG_RETURN(TRUE);
}


/**
  Fill the information schema table, "query_profile", as defined in show.cc .
*/
int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item *cond)
{
  DBUG_ENTER("fill_statistics_info");
  TABLE *table= tables->table;
  ulonglong row_number= 0;

  /* 
    Don't add this query to the history.  Peeking at the history shouldn't affect the
    history.
  */
  discard();

  List_iterator<QUERY_PROFILE> query_it(history);
  QUERY_PROFILE *query;
  /* Go through each query in this thread's stored history... */
  while ((query= query_it++) != NULL)
  {
    PROFILE_ENTRY *ps= &(query->profile_start);
    double last_time= ps->time_usecs;
#ifdef HAVE_GETRUSAGE
    struct rusage *last_rusage= &(ps->rusage);
#endif

    /*
      Because we put all profiling info into a table that may be reordered, let
      us also include a numbering of each state per query.  The query_id and
      the "seq" together are unique.
    */
    ulonglong seq;

    List_iterator<PROFILE_ENTRY> step_it(query->entries);
    PROFILE_ENTRY *entry;
    /* ...and for each query, go through all its state-change steps. */
    for (seq= 0, entry= step_it++; 
         entry != NULL; 
#ifdef HAVE_GETRUSAGE
         last_rusage= &(entry->rusage),
#endif
         seq++, last_time= entry->time_usecs, entry= step_it++, row_number++)
    {
      /* Set default values for this row. */
      restore_record(table, s->default_values);

      /*
        The order of these fields is set by the  query_profile_statistics_info
        array.
      */
      table->field[0]->store((ulonglong) query->profiling_query_id);
      table->field[1]->store((ulonglong) seq); /* the step in the sequence */
      table->field[2]->store(entry->status, strlen(entry->status), 
                             system_charset_info);
      table->field[3]->store((double)(entry->time_usecs - last_time)/(1000*1000));

#ifdef HAVE_GETRUSAGE
      table->field[4]->store((double)RUSAGE_DIFF_USEC(entry->rusage.ru_utime,
                             last_rusage->ru_utime)/(1000.0*1000));
      table->field[4]->null_ptr= NULL;
      table->field[5]->store((double)RUSAGE_DIFF_USEC(entry->rusage.ru_stime,
                             last_rusage->ru_stime)/(1000.0*1000));

      table->field[5]->null_ptr= NULL;
#else
      /* TODO: Add CPU-usage info for non-BSD systems */
#endif
      
#ifdef HAVE_GETRUSAGE
      table->field[6]->store((uint32)(entry->rusage.ru_nvcsw - last_rusage->ru_nvcsw));
      table->field[6]->null_ptr= NULL;
      table->field[7]->store((uint32)(entry->rusage.ru_nivcsw - last_rusage->ru_nivcsw));
      table->field[7]->null_ptr= NULL;
#else
      /* TODO: Add context switch info for non-BSD systems */
#endif

#ifdef HAVE_GETRUSAGE
      table->field[8]->store((uint32)(entry->rusage.ru_inblock - last_rusage->ru_inblock));
      table->field[8]->null_ptr= NULL;
      table->field[9]->store((uint32)(entry->rusage.ru_oublock - last_rusage->ru_oublock));
      table->field[9]->null_ptr= NULL;
#else
      /* TODO: Add block IO info for non-BSD systems */
#endif
    
#ifdef HAVE_GETRUSAGE
      table->field[10]->store((uint32)(entry->rusage.ru_msgsnd - last_rusage->ru_msgsnd), true);
      table->field[10]->null_ptr= NULL;
      table->field[11]->store((uint32)(entry->rusage.ru_msgrcv - last_rusage->ru_msgrcv), true);
      table->field[11]->null_ptr= NULL;
#else
      /* TODO: Add message info for non-BSD systems */
#endif
    
#ifdef HAVE_GETRUSAGE
      table->field[12]->store((uint32)(entry->rusage.ru_majflt - last_rusage->ru_majflt), true);
      table->field[12]->null_ptr= NULL;
      table->field[13]->store((uint32)(entry->rusage.ru_minflt - last_rusage->ru_minflt), true);
      table->field[13]->null_ptr= NULL;
#else
      /* TODO: Add page fault info for non-BSD systems */
#endif

#ifdef HAVE_GETRUSAGE
      table->field[14]->store((uint32)(entry->rusage.ru_nswap - last_rusage->ru_nswap), true);
      table->field[14]->null_ptr= NULL;
#else
      /* TODO: Add swap info for non-BSD systems */
#endif
    
      if ((entry->function != NULL) && (entry->file != NULL))
      {
        table->field[15]->store(entry->function, strlen(entry->function),
                        system_charset_info);        
        table->field[15]->null_ptr= NULL;
        table->field[16]->store(entry->file, strlen(entry->file), system_charset_info);
        table->field[16]->null_ptr= NULL;
        table->field[17]->store(entry->line, true);
        table->field[17]->null_ptr= NULL;
      }

      if (schema_table_store_record(thd, table))
        DBUG_RETURN(1);

    }
  }

  DBUG_RETURN(0);
}
#endif /* ENABLED_PROFILING */

--- New file ---
+++ sql/sql_profile.h	07/02/19 21:56:45
/* Copyright (C) 2007 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; either version 2 of the License, or
   (at your option) any later version.

   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 */

#ifndef _SQL_PROFILE_H
#define _SQL_PROFILE_H

#if __STDC_VERSION__ < 199901L
#  if __GNUC__ >= 2
#    define __func__ __FUNCTION__
#  else
#    define __func__ _db_func_
extern const char *_db_func_;
#  endif
#elif defined(_MSC_VER)
#  if _MSC_VER < 1300
#     define __func__ _db_func_
extern const char *_db_func_;
#  else
#    define __func__ __FUNCTION__
#  endif
#elif defined(__BORLANDC__)
#  define __func__ __FUNC__
#else
#  define __func__ _db_func_
extern const char *_db_func_;
#endif

extern ST_FIELD_INFO query_profile_statistics_info[];
int fill_query_profile_statistics_info(THD *thd, struct st_table_list *tables, Item *cond);


#define PROFILE_NONE         0
#define PROFILE_CPU          (1<<0)
#define PROFILE_MEMORY       (1<<1)
#define PROFILE_BLOCK_IO     (1<<2)
#define PROFILE_CONTEXT      (1<<3)
#define PROFILE_PAGE_FAULTS  (1<<4)
#define PROFILE_IPC          (1<<5)
#define PROFILE_SWAPS        (1<<6)
#define PROFILE_SOURCE       (1<<16)
#define PROFILE_ALL          (~0)


#ifndef ENABLED_PROFILING

#  define thd_proc_info(thd, msg) do { (thd)->proc_info= (msg); } while (0)

#else

#  define thd_proc_info(thd, msg)                                             \
  do {                                                                        \
    if (unlikely(((thd)->options & OPTION_PROFILING) != 0))                   \
    {                                                                         \
      (thd)->profiling.status_change((msg), __func__, __FILE__, __LINE__);    \
    }                                                                         \
    else                                                                      \
    {                                                                         \
      (thd)->proc_info= (msg);                                                \
    }                                                                         \
  } while (0)

#include "mysql_priv.h"

#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif


class PROFILE_ENTRY;
class QUERY_PROFILE;
class PROFILING;


/**
  A single entry in a single profile.
*/
class PROFILE_ENTRY
{
private:
  friend class QUERY_PROFILE;
  friend class PROFILING;

  QUERY_PROFILE *profile;
  char *status;
#ifdef HAVE_GETRUSAGE
  struct rusage rusage;
#endif

  char *function;
  char *file;
  unsigned int line;

  double time_usecs;
  char *allocated_status_memory;

  void set_status(const char *status_arg, const char *function_arg, 
                  const char *file_arg, unsigned int line_arg);
  void clean_up();
  
  PROFILE_ENTRY();
  PROFILE_ENTRY(QUERY_PROFILE *profile_arg, const char *status_arg);
  PROFILE_ENTRY(QUERY_PROFILE *profile_arg, const char *status_arg,
                const char *function_arg,
                const char *file_arg, unsigned int line_arg);
  ~PROFILE_ENTRY();
  void collect();
};


/**
  The full profile for a single query, and includes multiple PROFILE_ENTRY
  objects.
*/
class QUERY_PROFILE
{
private:
  friend class PROFILING;

  PROFILING *profiling;

  query_id_t profiling_query_id;
  char *query_source;
  PROFILE_ENTRY profile_start;
  PROFILE_ENTRY *profile_end;
  List<PROFILE_ENTRY> entries;


  QUERY_PROFILE(PROFILING *profiling_arg, const char *query_source_ptr);
  ~QUERY_PROFILE();

  /* Add a profile status change to the current profile. */
  void status(const char *status_arg,
              const char *function_arg,
              const char *file_arg, unsigned int line_arg);

  /* Reset the contents of this profile entry. */
  void reset();

  /* Show this profile.  This is called by PROFILING. */
  bool show(uint options);
};


/**
  Profiling state for a single THD; contains multiple QUERY_PROFILE objects.
*/
class PROFILING
{
private:
  friend class PROFILE_ENTRY;
  friend class QUERY_PROFILE;

  MEM_ROOT mem_root;
  THD *thd;
  bool keeping;

  QUERY_PROFILE *current;
  QUERY_PROFILE *last;
  List<QUERY_PROFILE> history;
 
public:
  PROFILING();
  ~PROFILING();

  /** Reset the current profile and state of profiling for the next query. */
  void reset();

  /**
    Do we intend to keep the currently collected profile?
    
    We don't keep profiles for some commands, such as SHOW PROFILE, SHOW
    PROFILES, and some SQLCOM commands which aren't useful to profile.  The
    keep() and discard() functions can be called many times, only the final
    setting when the query finishes is used to decide whether to discard the
    profile.
    
    The default is to keep the profile for all queries.
  */
  inline void keep()    { keeping= 1; };

  /**
    Do we intend to keep the currently collected profile?
    @see keep()
  */
  inline void discard() { keeping= 0; };

  /** 
    Stash this profile in the profile history and remove the oldest
    profile if the history queue is full, as defined by the 
    profiling_history_size system variable.
  */
  void store();

  /**
    Called with every update of the status via thd_proc_info() , and is
    therefore the main hook into the profiling code.
  */
  void status_change(const char *status_arg,
                     const char *function_arg,
                     const char *file_arg, unsigned int line_arg);

  inline void set_thd(THD *thd_arg) { thd= thd_arg; };

  /* SHOW PROFILES */
  bool show_profiles();

  /* SHOW PROFILE FOR QUERY query_id */
  bool show(uint options, uint profiling_query_id);

  /* SHOW PROFILE */
  bool show_last(uint options);

  /* ... from INFORMATION_SCHEMA.PROFILING ... */
  int fill_statistics_info(THD *thd, struct st_table_list *tables, Item *cond);
};

#  endif /* HAVE_PROFILING */
#endif /* _SQL_PROFILE_H */


--- 1.18/BitKeeper/etc/collapsed	2007-02-19 21:56:55 -05:00
+++ 1.19/BitKeeper/etc/collapsed	2007-02-19 21:56:55 -05:00
@@ -29,3 +29,5 @@
 4554a95d7txO1DuO9G3nAizI3SkFAA
 4554b3722d71SbPiI2Gx-RhbZjmuIQ
 4558b3d73Cxjlb7Wv1oytdSTthxDfw
+45c390d6BbWrwyEi5T5VsWKYxl06Rg
+45c39d31g0iik6UE_oTK5N55ry-ycA

--- 1.175/sql/set_var.cc	2007-02-19 21:56:55 -05:00
+++ 1.176/sql/set_var.cc	2007-02-19 21:56:55 -05:00
@@ -545,6 +545,12 @@ static sys_var_thd_bit	sys_unique_checks
 					  set_option_bit,
 					  OPTION_RELAXED_UNIQUE_CHECKS,
 					  1);
+#ifdef ENABLED_PROFILING
+static sys_var_thd_bit  sys_profiling("profiling", NULL, set_option_bit,
+                                      ulonglong(OPTION_PROFILING));
+sys_var_thd_ulong	sys_profiling_history_size("profiling_history_size",
+					      &SV::profiling_history_size);
+#endif
 
 /* Local state variables */
 
@@ -696,6 +702,10 @@ sys_var *sys_variables[]=
   &sys_optimizer_prune_level,
   &sys_optimizer_search_depth,
   &sys_preload_buff_size,
+#ifdef ENABLED_PROFILING
+  &sys_profiling,
+  &sys_profiling_history_size,
+#endif
   &sys_pseudo_thread_id,
   &sys_query_alloc_block_size,
   &sys_query_cache_size,
@@ -1003,6 +1013,10 @@ struct show_var_st init_vars[]= {
   {"pid_file",                (char*) pidfile_name,                 SHOW_CHAR},
   {"port",                    (char*) &mysqld_port,                  SHOW_INT},
   {sys_preload_buff_size.name, (char*) &sys_preload_buff_size,      SHOW_SYS},
+#ifdef ENABLED_PROFILING
+  {sys_profiling.name,        (char*) &sys_profiling,               SHOW_SYS},
+  {sys_profiling_history_size.name, (char*) &sys_profiling_history_size, SHOW_SYS},
+#endif
   {"protocol_version",        (char*) &protocol_version,            SHOW_INT},
   {sys_query_alloc_block_size.name, (char*) &sys_query_alloc_block_size,
    SHOW_SYS},

--- 1.80/sql/set_var.h	2007-02-19 21:56:55 -05:00
+++ 1.81/sql/set_var.h	2007-02-19 21:56:55 -05:00
@@ -457,11 +457,11 @@ class sys_var_thd_bit :public sys_var_th
   sys_check_func check_func;
   sys_update_func update_func;
 public:
-  ulong bit_flag;
+  ulonglong bit_flag;
   bool reverse;
   sys_var_thd_bit(const char *name_arg, 
                   sys_check_func c_func, sys_update_func u_func,
-                  ulong bit, bool reverse_arg=0)
+                  ulonglong bit, bool reverse_arg=0)
     :sys_var_thd(name_arg), check_func(c_func), update_func(u_func),
     bit_flag(bit), reverse(reverse_arg)
   {}

--- 1.72/libmysqld/Makefile.am	2007-02-19 21:56:55 -05:00
+++ 1.73/libmysqld/Makefile.am	2007-02-19 21:56:55 -05:00
@@ -53,6 +53,7 @@ sqlsources = derror.cc field.cc field_co
 	protocol.cc net_serv.cc opt_range.cc \
 	opt_sum.cc procedure.cc records.cc sql_acl.cc \
 	sql_load.cc discover.cc sql_locale.cc \
+	sql_profile.cc \
 	sql_analyse.cc sql_base.cc sql_cache.cc sql_class.cc \
 	sql_crypt.cc sql_db.cc sql_delete.cc sql_error.cc sql_insert.cc \
 	sql_lex.cc sql_list.cc sql_manager.cc sql_map.cc sql_parse.cc \

--- 1.227/sql/sp_head.cc	2007-02-19 21:56:55 -05:00
+++ 1.228/sql/sp_head.cc	2007-02-19 21:56:55 -05:00
@@ -2371,9 +2371,9 @@ sp_lex_keeper::reset_lex_and_exec_core(T
 
   m_lex->unit.cleanup();
 
-  thd->proc_info="closing tables";
+  thd_proc_info(thd, "closing tables");
   close_thread_tables(thd);
-  thd->proc_info= 0;
+  thd_proc_info(thd, 0);
 
   if (m_lex->query_tables_own_last)
   {

--- 1.63/sql/repl_failsafe.cc	2007-02-19 21:56:55 -05:00
+++ 1.64/sql/repl_failsafe.cc	2007-02-19 21:56:55 -05:00
@@ -91,7 +91,7 @@ static int init_failsafe_rpl_thread(THD*
   if (thd->variables.max_join_size == HA_POS_ERROR)
     thd->options|= OPTION_BIG_SELECTS;
 
-  thd->proc_info="Thread initialized";
+  thd_proc_info(thd, "Thread initialized");
   thd->version=refresh_version;
   thd->set_time();
   DBUG_RETURN(0);
@@ -597,7 +597,7 @@ pthread_handler_t handle_failsafe_rpl(vo
   {
     bool break_req_chain = 0;
     pthread_cond_wait(&COND_rpl_status, &LOCK_rpl_status);
-    thd->proc_info="Processing request";
+    thd_proc_info(thd, "Processing request");
     while (!break_req_chain)
     {
       switch (rpl_status) {
@@ -941,7 +941,7 @@ bool load_master_data(THD* thd)
       goto err;
     }
   }
-  thd->proc_info="purging old relay logs";
+  thd_proc_info(thd, "purging old relay logs");
   if (purge_relay_logs(&active_mi->rli,thd,
 		       0 /* not only reset, but also reinit */,
 		       &errmsg))
@@ -968,7 +968,7 @@ bool load_master_data(THD* thd)
   flush_relay_log_info(&active_mi->rli);
   pthread_cond_broadcast(&active_mi->rli.data_cond);
   pthread_mutex_unlock(&active_mi->rli.data_lock);
-  thd->proc_info = "starting slave";
+  thd_proc_info(thd, "starting slave");
   if (restart_thread_mask)
   {
     error=start_slave_threads(0 /* mutex not needed */,
@@ -980,7 +980,7 @@ bool load_master_data(THD* thd)
 err:
   unlock_slave_threads(active_mi);
   pthread_mutex_unlock(&LOCK_active_mi);
-  thd->proc_info = 0;
+  thd_proc_info(thd, 0);
 
   mysql_close(&mysql); // safe to call since we always do mysql_init()
   if (!error)

--- 1.155/sql/sql_repl.cc	2007-02-19 21:56:55 -05:00
+++ 1.156/sql/sql_repl.cc	2007-02-19 21:56:55 -05:00
@@ -645,7 +645,7 @@ impossible position";
 
 	if (read_packet)
 	{
-	  thd->proc_info = "Sending binlog event to slave";
+	  thd_proc_info(thd, "Sending binlog event to slave");
 	  if (my_net_write(net, (char*)packet->ptr(), packet->length()) )
 	  {
 	    errmsg = "Failed on my_net_write()";
@@ -683,7 +683,7 @@ impossible position";
       bool loop_breaker = 0;
       /* need this to break out of the for loop from switch */
 
-      thd->proc_info = "Finished reading one binlog; switching to next binlog";
+      thd_proc_info(thd, "Finished reading one binlog; switching to next binlog");
       switch (mysql_bin_log.find_next_log(&linfo, 1)) {
       case LOG_INFO_EOF:
 	loop_breaker = (flags & BINLOG_DUMP_NON_BLOCK);
@@ -729,14 +729,14 @@ end:
   (void)my_close(file, MYF(MY_WME));
 
   send_eof(thd);
-  thd->proc_info = "Waiting to finalize termination";
+  thd_proc_info(thd, "Waiting to finalize termination");
   pthread_mutex_lock(&LOCK_thread_count);
   thd->current_linfo = 0;
   pthread_mutex_unlock(&LOCK_thread_count);
   DBUG_VOID_RETURN;
 
 err:
-  thd->proc_info = "Waiting to finalize termination";
+  thd_proc_info(thd, "Waiting to finalize termination");
   end_io_cache(&log);
   /*
     Exclude  iteration through thread list
@@ -888,7 +888,7 @@ int stop_slave(THD* thd, MASTER_INFO* mi
 
   if (check_access(thd, SUPER_ACL, any_db,0,0,0,0))
     return 1;
-  thd->proc_info = "Killing slave";
+  thd_proc_info(thd, "Killing slave");
   int thread_mask;
   lock_slave_threads(mi);
   // Get a mask of _running_ threads
@@ -915,7 +915,7 @@ int stop_slave(THD* thd, MASTER_INFO* mi
                  ER(ER_SLAVE_WAS_NOT_RUNNING));
   }
   unlock_slave_threads(mi);
-  thd->proc_info = 0;
+  thd_proc_info(thd, 0);
 
   if (slave_errno)
   {
@@ -1071,7 +1071,7 @@ bool change_master(THD* thd, MASTER_INFO
     DBUG_RETURN(TRUE);
   }
 
-  thd->proc_info = "Changing master";
+  thd_proc_info(thd, "Changing master");
   LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
   // TODO: see if needs re-write
   if (init_master_info(mi, master_info_file, relay_log_info_file, 0,
@@ -1196,7 +1196,7 @@ bool change_master(THD* thd, MASTER_INFO
   if (need_relay_log_purge)
   {
     relay_log_purge= 1;
-    thd->proc_info="Purging old relay logs";
+    thd_proc_info(thd, "Purging old relay logs");
     if (purge_relay_logs(&mi->rli, thd,
 			 0 /* not only reset, but also reinit */,
 			 &errmsg))
@@ -1259,7 +1259,7 @@ bool change_master(THD* thd, MASTER_INFO
   pthread_mutex_unlock(&mi->rli.data_lock);
 
   unlock_slave_threads(mi);
-  thd->proc_info = 0;
+  thd_proc_info(thd, 0);
   send_ok(thd);
   DBUG_RETURN(FALSE);
 }
Thread
bk commit into 5.0 tree (cmiller:1.2359) BUG#24795Chad MILLER20 Feb