aboutsummaryrefslogtreecommitdiff
path: root/mysql/libmysql/mysql_trace.c
blob: 3073479b2491d5927281afa91187f4c5ae83d283 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/* 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 <my_global.h>
#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 "<unknown stage>";
  }
}


#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 "<unknown event>";
  }
}

#endif