/* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file ============================================= Client-side protocol tracing infrastructure ============================================= If a plugin of type MYSQL_CLIENT_TRACE_PLUGIN is loaded into libmysql and its instance is pointed by the global trace_plugin pointer, this plugin is notified of various protocol trace events. See include/mysql/plugin_trace.h for documentation of trace plugin methods and the list of possible trace events. These trace events are generated with MYSQL_TRACE() macro put in relevant places in libmysql code. The macro calls mysql_trace_trace() function defined here. This function calls trace plugin's trace_event() method, if it is defined. For each traced connection, the state is kept in st_mysql_trace_info structure (see mysql_trace.h). To correctly interpret each trace event, trace plugin is informed of the current protocol stage (see include/mysql/plugin_trace.h for list of stages). The current protocol stage is part of the connection tracing state. It is updated with MYSQL_TRACE_STAGE() hooks within libmysql code. */ #include #include "mysql.h" #include "mysql_trace.h" /* Definition of the global trace_plugin pointer - see plugin_trace.h for declaration and description. */ struct st_mysql_client_plugin_TRACE *trace_plugin= NULL; /* Macros for manipulating trace_info structure. */ #define GET_DATA(TI) (TI)->trace_plugin_data #define SET_DATA(TI,D) GET_DATA(TI) = (D) #define GET_STAGE(TI) (TI)->stage #define TEST_STAGE(TI,X) (GET_STAGE(TI) == PROTOCOL_STAGE_ ## X) #define SET_STAGE(TI,X) GET_STAGE(TI) = PROTOCOL_STAGE_ ## X /** Initialize tracing in a given connection. This function is called from MYSQL_TRACE_STAGE() when the initial CONNECTING stage is reported. It allocates and initializes trace_info structure which is then attached to the connection handle. */ void mysql_trace_start(struct st_mysql *m) { struct st_mysql_trace_info *trace_info; trace_info= my_malloc(PSI_NOT_INSTRUMENTED, sizeof(struct st_mysql_trace_info), MYF(MY_ZEROFILL)); if (!trace_info) { /* Note: in this case trace_data of the connection will remain NULL and thus tracing will be disabled. */ return; } /* This function should be called only when a trace plugin is loaded and thus trace_plugin pointer is not NULL. This is handled in MYSQL_TRACE_STAGE() macro (mysql_trace.h). */ DBUG_ASSERT(trace_plugin); trace_info->plugin= trace_plugin; trace_info->stage= PROTOCOL_STAGE_CONNECTING; /* Call plugin's tracing_start() method, if defined. */ if (trace_info->plugin->tracing_start) { trace_info->trace_plugin_data= trace_info->plugin->tracing_start( trace_info->plugin, m, PROTOCOL_STAGE_CONNECTING); } else { trace_info->trace_plugin_data= NULL; } /* Store trace_info in the connection handle. */ TRACE_DATA(m)= trace_info; } /** Report a protocol trace event to trace plugin. Calls plugin's trace_event() method, if it is defined, passing to it the event, the current protocol stage and event arguments (if any). Terminates tracing of the connection when appropriate. @param m MYSQL connection handle @param ev trace event to be reported @param args trace event arguments */ void mysql_trace_trace(struct st_mysql *m, enum trace_event ev, struct st_trace_event_args args) { struct st_mysql_trace_info *trace_info= TRACE_DATA(m); struct st_mysql_client_plugin_TRACE *plugin= trace_info ? trace_info->plugin : NULL; int quit_tracing= 0; /* If trace_info is NULL then this connection is not traced and this function should not be called - this is handled inside MYSQL_TRACE() macro. */ DBUG_ASSERT(trace_info); /* Call plugin's trace_event() method if defined */ if (plugin->trace_event) { /* Temporarily disable tracing while executing plugin's method by setting trace data pointer to NULL. Also, set reconnect flag to 0 in case plugin executes any queries. */ my_bool saved_reconnect_flag= m->reconnect; TRACE_DATA(m)= NULL; m->reconnect= 0; quit_tracing= plugin->trace_event(plugin, GET_DATA(trace_info), m, GET_STAGE(trace_info), ev, args); m->reconnect= saved_reconnect_flag; TRACE_DATA(m)= trace_info; } /* Stop tracing if requested or end of connection. */ if (quit_tracing || TEST_STAGE(trace_info, DISCONNECTED) || TRACE_EVENT_DISCONNECTED == ev) { /* Note: this disables further tracing */ TRACE_DATA(m)= NULL; if (plugin->tracing_stop) plugin->tracing_stop(plugin, m, GET_DATA(trace_info)); my_free(trace_info); } } #ifndef DBUG_OFF /* These functions are declared in plugin_trace.h. Consult documentation of *_LIST() macros (plugin_trace.h) to see how switch() bodies are constructed with the *_get_name() macros. */ #define protocol_stage_get_name(X) case PROTOCOL_STAGE_ ## X: return #X; const char* protocol_stage_name(enum protocol_stage stage) { switch(stage) { PROTOCOL_STAGE_LIST(get_name) default: return ""; } } #define trace_event_get_name(X) case TRACE_EVENT_ ## X: return #X; const char* trace_event_name(enum trace_event ev) { switch(ev) { TRACE_EVENT_LIST(get_name) default: return ""; } } #endif