1 /* Copyright (c) 2012, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    Without limiting anything contained in the foregoing, this file,
15    which is part of C Driver for MySQL (Connector/C), is also subject to the
16    Universal FOSS Exception, version 1.0, a copy of which can be found at
17    http://oss.oracle.com/licenses/universal-foss-exception.
18 
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22    GNU General Public License, version 2.0, for more details.
23 
24    You should have received a copy of the GNU General Public License
25    along with this program; if not, write to the Free Software
26    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
27 
28 /**
29   @file
30 
31   =============================================
32    Client-side protocol tracing infrastructure
33   =============================================
34 
35   If a plugin of type MYSQL_CLIENT_TRACE_PLUGIN is loaded into
36   libmysql and its instance is pointed by the global trace_plugin
37   pointer, this plugin is notified of various protocol trace events.
38   See include/mysql/plugin_trace.h for documentation of trace plugin
39   methods and the list of possible trace events.
40 
41   These trace events are generated with MYSQL_TRACE() macro put in
42   relevant places in libmysql code. The macro calls mysql_trace_trace()
43   function defined here. This function calls trace plugin's
44   trace_event() method, if it is defined.
45 
46   For each traced connection, the state is kept in st_mysql_trace_info
47   structure (see mysql_trace.h).
48 
49   To correctly interpret each trace event, trace plugin is informed
50   of the current protocol stage (see include/mysql/plugin_trace.h for
51   list of stages). The current protocol stage is part of the
52   connection tracing state. It is updated with MYSQL_TRACE_STAGE()
53   hooks within libmysql code.
54 */
55 
56 #include <my_global.h>
57 #include "mysql.h"
58 #include "mysql_trace.h"
59 
60 /*
61   Definition of the global trace_plugin pointer - see plugin_trace.h
62   for declaration and description.
63 */
64 struct st_mysql_client_plugin_TRACE *trace_plugin= NULL;
65 
66 /*
67   Macros for manipulating trace_info structure.
68 */
69 #define GET_DATA(TI)      (TI)->trace_plugin_data
70 #define SET_DATA(TI,D)    GET_DATA(TI) = (D)
71 #define GET_STAGE(TI)     (TI)->stage
72 #define TEST_STAGE(TI,X)  (GET_STAGE(TI) == PROTOCOL_STAGE_ ## X)
73 #define SET_STAGE(TI,X)   GET_STAGE(TI) = PROTOCOL_STAGE_ ## X
74 
75 
76 /**
77   Initialize tracing in a given connection.
78 
79   This function is called from MYSQL_TRACE_STAGE() when the initial
80   CONNECTING stage is reported. It allocates and initializes trace_info
81   structure which is then attached to the connection handle.
82 */
83 
mysql_trace_start(struct st_mysql * m)84 void mysql_trace_start(struct st_mysql *m)
85 {
86   struct st_mysql_trace_info *trace_info;
87 
88   trace_info= my_malloc(PSI_NOT_INSTRUMENTED,
89                         sizeof(struct st_mysql_trace_info),
90                         MYF(MY_ZEROFILL));
91   if (!trace_info)
92   {
93     /*
94       Note: in this case trace_data of the connection will
95       remain NULL and thus tracing will be disabled.
96     */
97     return;
98   }
99 
100   /*
101     This function should be called only when a trace plugin
102     is loaded and thus trace_plugin pointer is not NULL. This
103     is handled in MYSQL_TRACE_STAGE() macro (mysql_trace.h).
104   */
105   assert(trace_plugin);
106 
107   trace_info->plugin= trace_plugin;
108   trace_info->stage=  PROTOCOL_STAGE_CONNECTING;
109 
110   /*
111     Call plugin's tracing_start() method, if defined.
112   */
113 
114   if (trace_info->plugin->tracing_start)
115   {
116     trace_info->trace_plugin_data=
117       trace_info->plugin->tracing_start(
118         trace_info->plugin,
119         m, PROTOCOL_STAGE_CONNECTING);
120   }
121   else
122   {
123     trace_info->trace_plugin_data= NULL;
124   }
125 
126   /* Store trace_info in the connection handle. */
127 
128   TRACE_DATA(m)= trace_info;
129 }
130 
131 
132 /**
133   Report a protocol trace event to trace plugin.
134 
135   Calls plugin's trace_event() method, if it is defined, passing to
136   it the event, the current protocol stage and event arguments (if any).
137 
138   Terminates tracing of the connection when appropriate.
139 
140   @param m        MYSQL connection handle
141   @param ev       trace event to be reported
142   @param args     trace event arguments
143 */
144 
mysql_trace_trace(struct st_mysql * m,enum trace_event ev,struct st_trace_event_args args)145 void mysql_trace_trace(struct st_mysql  *m,
146                        enum trace_event ev,
147                        struct st_trace_event_args args)
148 {
149   struct st_mysql_trace_info *trace_info= TRACE_DATA(m);
150   struct st_mysql_client_plugin_TRACE *plugin= trace_info ? trace_info->plugin : NULL;
151   int    quit_tracing= 0;
152 
153   /*
154     If trace_info is NULL then this connection is not traced and this
155     function should not be called - this is handled inside MYSQL_TRACE()
156     macro.
157   */
158   assert(trace_info);
159 
160   /* Call plugin's trace_event() method if defined */
161 
162   if (plugin->trace_event)
163   {
164     /*
165       Temporarily disable tracing while executing plugin's method
166       by setting trace data pointer to NULL. Also, set reconnect
167       flag to 0 in case plugin executes any queries.
168     */
169     my_bool saved_reconnect_flag= m->reconnect;
170 
171     TRACE_DATA(m)= NULL;
172     m->reconnect=  0;
173     quit_tracing= plugin->trace_event(plugin, GET_DATA(trace_info), m,
174                                       GET_STAGE(trace_info), ev, args);
175     m->reconnect= saved_reconnect_flag;
176     TRACE_DATA(m)= trace_info;
177   }
178 
179   /* Stop tracing if requested or end of connection. */
180 
181   if (quit_tracing
182       || TEST_STAGE(trace_info, DISCONNECTED)
183       || TRACE_EVENT_DISCONNECTED == ev)
184   {
185     /* Note: this disables further tracing */
186     TRACE_DATA(m)= NULL;
187 
188     if (plugin->tracing_stop)
189       plugin->tracing_stop(plugin, m, GET_DATA(trace_info));
190 
191     my_free(trace_info);
192   }
193 }
194 
195 
196 #ifndef NDEBUG
197 /*
198   These functions are declared in plugin_trace.h.
199 
200   Consult documentation of *_LIST() macros (plugin_trace.h) to see how
201   switch() bodies are constructed with the *_get_name() macros.
202 */
203 
204 #define protocol_stage_get_name(X) case PROTOCOL_STAGE_ ## X: return #X;
205 
protocol_stage_name(enum protocol_stage stage)206 const char* protocol_stage_name(enum protocol_stage stage)
207 {
208   switch(stage)
209   {
210   PROTOCOL_STAGE_LIST(get_name)
211   default: return "<unknown stage>";
212   }
213 }
214 
215 
216 #define trace_event_get_name(X) case TRACE_EVENT_ ## X: return #X;
217 
trace_event_name(enum trace_event ev)218 const char* trace_event_name(enum trace_event ev)
219 {
220   switch(ev)
221   {
222   TRACE_EVENT_LIST(get_name)
223   default: return "<unknown event>";
224   }
225 }
226 
227 #endif
228