1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 08:47:17 +01:00
SqMod/vendor/MDBC/plugins/trace/trace_example.c
2021-09-21 20:59:01 +03:00

459 lines
12 KiB
C

/************************************************************************************
Copyright (C) 2015 MariaDB Corporation AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/
#ifndef _WIN32
#define _GNU_SOURCE 1
#endif
#include <ma_global.h>
#include <mysql.h>
#include <mysql/client_plugin.h>
#include <string.h>
#include <memory.h>
#ifndef WIN32
#include <dlfcn.h>
#endif
#define READ 0
#define WRITE 1
/* function prototypes */
static int trace_init(char *errormsg,
size_t errormsg_size,
int unused __attribute__((unused)),
va_list unused1 __attribute__((unused)));
static int trace_deinit(void);
int (*register_callback)(my_bool register_callback,
void (*callback_function)(int mode, MYSQL *mysql, const uchar *buffer, size_t length));
void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
#ifndef HAVE_TRACE_EXAMPLE_PLUGIN_DYNAMIC
struct st_mysql_client_plugin trace_example_plugin=
#else
struct st_mysql_client_plugin _mysql_client_plugin_declaration_ =
#endif
{
MARIADB_CLIENT_TRACE_PLUGIN,
MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION,
"trace_example",
"Georg Richter",
"Trace example plugin",
{1,0,0},
"LGPL",
NULL,
&trace_init,
&trace_deinit,
NULL
};
static const char *commands[]= {
"COM_SLEEP",
"COM_QUIT",
"COM_INIT_DB",
"COM_QUERY",
"COM_FIELD_LIST",
"COM_CREATE_DB",
"COM_DROP_DB",
"COM_REFRESH",
"COM_SHUTDOWN",
"COM_STATISTICS",
"COM_PROCESS_INFO",
"COM_CONNECT",
"COM_PROCESS_KILL",
"COM_DEBUG",
"COM_PING",
"COM_TIME",
"COM_DELAYED_INSERT",
"COM_CHANGE_USER",
"COM_BINLOG_DUMP",
"COM_TABLE_DUMP",
"COM_CONNECT_OUT",
"COM_REGISTER_SLAVE",
"COM_STMT_PREPARE",
"COM_STMT_EXECUTE",
"COM_STMT_SEND_LONG_DATA",
"COM_STMT_CLOSE",
"COM_STMT_RESET",
"COM_SET_OPTION",
"COM_STMT_FETCH",
"COM_DAEMON",
"COM_END"
};
typedef struct {
unsigned long thread_id;
int last_command; /* COM_* values, -1 for handshake */
unsigned int max_packet_size;
unsigned int num_commands;
size_t total_size[2];
unsigned int client_flags;
char *username;
char *db;
char *command;
char *filename;
unsigned long refid; /* stmt_id, thread_id for kill */
uchar charset;
void *next;
int local_infile;
unsigned long pkt_length;
} TRACE_INFO;
#define TRACE_STATUS(a) ((!a) ? "ok" : "error")
TRACE_INFO *trace_info= NULL;
static TRACE_INFO *get_trace_info(unsigned long thread_id)
{
TRACE_INFO *info= trace_info;
/* search connection */
while (info)
{
if (info->thread_id == thread_id)
return info;
else
info= (TRACE_INFO *)info->next;
}
if (!(info= (TRACE_INFO *)calloc(sizeof(TRACE_INFO), 1)))
return NULL;
info->thread_id= thread_id;
info->next= trace_info;
trace_info= info;
return info;
}
static void delete_trace_info(unsigned long thread_id)
{
TRACE_INFO *last= NULL, *current;
current= trace_info;
while (current)
{
if (current->thread_id == thread_id)
{
printf("deleting thread %lu\n", thread_id);
if (last)
last->next= current->next;
else
trace_info= (TRACE_INFO *)current->next;
if (current->command)
free(current->command);
if (current->db)
free(current->db);
if (current->username)
free(current->username);
if (current->filename)
free(current->filename);
free(current);
}
last= current;
current= (TRACE_INFO *)current->next;
}
}
/* {{{ static int trace_init */
/*
Initialization routine
SYNOPSIS
trace_init
unused1
unused2
unused3
unused4
DESCRIPTION
Init function registers a callback handler for PVIO interface.
RETURN
0 success
*/
static int trace_init(char *errormsg,
size_t errormsg_size,
int unused1 __attribute__((unused)),
va_list unused2 __attribute__((unused)))
{
void *func;
#ifdef WIN32
if (!(func= GetProcAddress(GetModuleHandle(NULL), "ma_pvio_register_callback")))
#else
if (!(func= dlsym(RTLD_DEFAULT, "ma_pvio_register_callback")))
#endif
{
strncpy(errormsg, "Can't find ma_pvio_register_callback function", errormsg_size);
return 1;
}
register_callback= func;
register_callback(TRUE, trace_callback);
return 0;
}
/* }}} */
static int trace_deinit(void)
{
/* unregister plugin */
while(trace_info)
{
printf("Warning: Connection for thread %lu not properly closed\n", trace_info->thread_id);
trace_info= (TRACE_INFO *)trace_info->next;
}
register_callback(FALSE, trace_callback);
return 0;
}
static void trace_set_command(TRACE_INFO *info, char *buffer, size_t size)
{
if (info->command)
free(info->command);
info->command= calloc(1, size + 1);
memcpy(info->command, buffer, size);
}
void dump_buffer(uchar *buffer, size_t len)
{
uchar *p= buffer;
while (p < buffer + len)
{
printf("%02x ", *p);
p++;
}
printf("\n");
}
static void dump_simple(TRACE_INFO *info, my_bool is_error)
{
printf("%8lu: %s %s\n", info->thread_id, commands[info->last_command], TRACE_STATUS(is_error));
}
static void dump_reference(TRACE_INFO *info, my_bool is_error)
{
printf("%8lu: %s(%lu) %s\n", info->thread_id, commands[info->last_command], (long)info->refid, TRACE_STATUS(is_error));
}
static void dump_command(TRACE_INFO *info, my_bool is_error)
{
size_t i;
printf("%8lu: %s(", info->thread_id, commands[info->last_command]);
for (i= 0; info->command && i < strlen(info->command); i++)
if (info->command[i] == '\n')
printf("\\n");
else if (info->command[i] == '\r')
printf("\\r");
else if (info->command[i] == '\t')
printf("\\t");
else
printf("%c", info->command[i]);
printf(") %s\n", TRACE_STATUS(is_error));
}
void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length)
{
unsigned long thread_id= mysql->thread_id;
TRACE_INFO *info;
/* check if package is server greeting package,
* and set thread_id */
if (!thread_id && mode == READ)
{
char *p= (char *)buffer;
p+= 4; /* packet length */
if ((uchar)*p != 0xFF) /* protocol version 0xFF indicates error */
{
p+= strlen(p + 1) + 2;
thread_id= uint4korr(p);
}
info= get_trace_info(thread_id);
info->last_command= -1;
}
else
{
char *p= (char *)buffer;
info= get_trace_info(thread_id);
if (info->last_command == -1)
{
if (mode == WRITE)
{
/* client authentication reply packet:
*
* ofs description length
* ------------------------
* 0 length 3
* 3 packet_no 1
* 4 client capab. 4
* 8 max_packet_size 4
* 12 character set 1
* 13 reserved 23
* ------------------------
* 36 username (zero terminated)
* len (1 byte) + password or
*/
p+= 4;
info->client_flags= uint4korr(p);
p+= 4;
info->max_packet_size= uint4korr(p);
p+= 4;
info->charset= *p;
p+= 24;
info->username= strdup(p);
p+= strlen(p) + 1;
if (*p) /* we are not interested in authentication data */
p+= *p;
p++;
if (info->client_flags & CLIENT_CONNECT_WITH_DB)
info->db= strdup(p);
}
else
{
p++;
if ((uchar)*p == 0xFF)
printf("%8lu: CONNECT_ERROR(%d)\n", info->thread_id, uint4korr(p+1));
else
printf("%8lu: CONNECT_SUCCESS(host=%s,user=%s,db=%s)\n", info->thread_id,
mysql->host, info->username, info->db ? info->db : "'none'");
info->last_command= COM_SLEEP;
}
}
else {
char *p= (char *)buffer;
int len;
if (mode == WRITE)
{
if (info->pkt_length > 0)
{
info->pkt_length-= length;
return;
}
len= uint3korr(p);
info->pkt_length= len + 4 - length;
p+= 4;
info->last_command= *p;
p++;
switch (info->last_command) {
case COM_INIT_DB:
case COM_DROP_DB:
case COM_CREATE_DB:
case COM_DEBUG:
case COM_QUERY:
case COM_STMT_PREPARE:
trace_set_command(info, p, len - 1);
break;
case COM_PROCESS_KILL:
info->refid= uint4korr(p);
break;
case COM_QUIT:
printf("%8lu: COM_QUIT\n", info->thread_id);
delete_trace_info(info->thread_id);
break;
case COM_PING:
printf("%8lu: COM_PING\n", info->thread_id);
break;
case COM_STMT_EXECUTE:
case COM_STMT_RESET:
case COM_STMT_CLOSE:
info->refid= uint4korr(p);
break;
case COM_CHANGE_USER:
break;
default:
if (info->local_infile == 1)
{
printf("%8lu: SEND_LOCAL_INFILE(%s) ", info->thread_id, info->filename);
if (len)
printf("sent %d bytes\n", len);
else
printf("- error\n");
info->local_infile= 2;
}
else
printf("%8lu: UNKNOWN_COMMAND: %d\n", info->thread_id, info->last_command);
break;
}
}
else
{
my_bool is_error;
len= uint3korr(p);
p+= 4;
is_error= (len == -1);
switch(info->last_command) {
case COM_STMT_EXECUTE:
case COM_STMT_RESET:
case COM_STMT_CLOSE:
case COM_PROCESS_KILL:
dump_reference(info, is_error);
info->refid= 0;
info->last_command= 0;
break;
case COM_QUIT:
dump_simple(info, is_error);
break;
case COM_QUERY:
case COM_INIT_DB:
case COM_DROP_DB:
case COM_CREATE_DB:
case COM_DEBUG:
case COM_CHANGE_USER:
if (info->last_command == COM_QUERY && (uchar)*p == 251)
{
info->local_infile= 1;
p++;
info->filename= (char *)malloc(len);
strncpy(info->filename, (char *)p, len);
dump_command(info, is_error);
break;
}
dump_command(info, is_error);
if (info->local_infile != 1)
{
free(info->command);
info->command= NULL;
}
break;
case COM_STMT_PREPARE:
printf("%8lu: COM_STMT_PREPARE(%s) ", info->thread_id, info->command);
if (!*p)
{
unsigned long stmt_id= uint4korr(p+1);
printf("-> stmt_id(%lu)\n", stmt_id);
}
else
printf("error\n");
break;
}
}
}
}
info->total_size[mode]+= length;
}