MySQL5.5-audit plugin的函数调用流程分析
2011-07-10 13:47
381 查看
继续研究audit plugin,首先gdb源代码
启动mysqld,加上--debug选项
进入gdb:
(gdb) attach 19079 (mysqld的进程号)
(gdb) b mysql_audit_notify(THD*, unsigned int, unsigned int, ...) (猜测该函数会调用audit的notify函数)
(gdb) c
Continuing.
启动mysql客户端
Breakpoint 1, mysql_audit_notify (thd=0x16fe09b0, event_class=1, event_subtype=0)
at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.cc:207
207 audit_handler_t *handlers= audit_handlers + event_class;
(gdb) bt
#0 mysql_audit_notify (thd=0x16fe09b0, event_class=1, event_subtype=0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.cc:207
#1 0x00000000006943d6 in thd_prepare_connection (thd=0x16fe09b0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:715
#2 0x0000000000694c61 in do_handle_one_connection (thd_arg=0x16fe09b0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:780
#3 0x0000000000694d3b in handle_one_connection (arg=0x16fe09b0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:706
#4 0x00000034f7e064a7 in start_thread () from /lib64/libpthread.so.0
#5 0x00000034f72d3c2d in clone () from /lib64/libc.so.6
(gdb)
可见,在准备创建客户端连接时也会调用audit,在gdb过程中,发现该函数被调用了很多次:
Breakpoint 1, mysql_audit_notify (thd=0x1706c790, event_class=1, event_subtype=0)
Breakpoint 1, mysql_audit_notify (thd=0x16fe09b0, event_class=0, event_subtype=3)
查了一下notify函数的记录,写入了如下操作:
select @@version_comment limit 1;
select @@version_comment limit 1;
mysql > use yinfeng
SELECT DATABASE();
SELECT DATABASE();
看起来,似乎每次操作之前都会调用两次mysql_audit_notify函数,即使执行一次错误的操作(比如向一个不存在的表插入数据)
————————————————————————————————————————————————
但当执行select、create table之类的命令时,则是先完成命令,然后才会跳到断点
#0 mysql_audit_notify (thd=0x1706c790, event_class=0, event_subtype=2) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.cc:207
#1 0x00000000005c7f84 in mysql_audit_general (thd=0x1706c790, event_subtype=2, error_code=0, msg=0x0)
at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.h:131
#2 0x00000000005c9d22 in dispatch_command (command=COM_QUERY, thd=0x1706c790, packet=0x1706f601 "", packet_length=27)
at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_parse.cc:1398
#3 0x00000000005ca0f8 in do_command (thd=0x1706c790) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_parse.cc:771
#4 0x0000000000694c7e in do_handle_one_connection (thd_arg=0x1706c790) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:787
#5 0x0000000000694d3b in handle_one_connection (arg=0x1706c790) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:706
#6 0x00000034f7e064a7 in start_thread () from /lib64/libpthread.so.0
#7 0x00000034f72d3c2d in clone () from /lib64/libc.so.6
看起来dispatch_command函数似乎是audit的入口,简要的归纳一下流程:
1.do_handle_one_connection()
2. do_command
3.dispatch_command(sql_parse.cc)
这里会调用两次mysql_audit_general函数,分别对应不同的事件类型
4.mysql_audit_general() (sql_audit.h)
5.mysql_audit_notify() (sql_audit.cc),该函数比较简短,会调用一个函数指针
这里,以event_class作为偏移量,来调用相应的函数指针:
audit_handlers作为全局变量,定义:
5.1 general_class_handler()
深入代码可以发现,在创建连接的时候调用了上述两个函数都被调用了,其中conneciton_class_handler()在函数thd_prepare_connection中被调用:
6.event_class_dispatch() (sql_audit.cc:466-491)
7. plugins_dispatch()
————————————————————————————————————————————————————————————
下一步:
尝试对该audit审计插件的执行流程进行修改,来满足一些有趣的需求
启动mysqld,加上--debug选项
进入gdb:
(gdb) attach 19079 (mysqld的进程号)
(gdb) b mysql_audit_notify(THD*, unsigned int, unsigned int, ...) (猜测该函数会调用audit的notify函数)
(gdb) c
Continuing.
启动mysql客户端
Breakpoint 1, mysql_audit_notify (thd=0x16fe09b0, event_class=1, event_subtype=0)
at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.cc:207
207 audit_handler_t *handlers= audit_handlers + event_class;
(gdb) bt
#0 mysql_audit_notify (thd=0x16fe09b0, event_class=1, event_subtype=0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.cc:207
#1 0x00000000006943d6 in thd_prepare_connection (thd=0x16fe09b0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:715
#2 0x0000000000694c61 in do_handle_one_connection (thd_arg=0x16fe09b0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:780
#3 0x0000000000694d3b in handle_one_connection (arg=0x16fe09b0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:706
#4 0x00000034f7e064a7 in start_thread () from /lib64/libpthread.so.0
#5 0x00000034f72d3c2d in clone () from /lib64/libc.so.6
(gdb)
可见,在准备创建客户端连接时也会调用audit,在gdb过程中,发现该函数被调用了很多次:
Breakpoint 1, mysql_audit_notify (thd=0x1706c790, event_class=1, event_subtype=0)
Breakpoint 1, mysql_audit_notify (thd=0x16fe09b0, event_class=0, event_subtype=3)
查了一下notify函数的记录,写入了如下操作:
select @@version_comment limit 1;
select @@version_comment limit 1;
mysql > use yinfeng
SELECT DATABASE();
SELECT DATABASE();
看起来,似乎每次操作之前都会调用两次mysql_audit_notify函数,即使执行一次错误的操作(比如向一个不存在的表插入数据)
————————————————————————————————————————————————
但当执行select、create table之类的命令时,则是先完成命令,然后才会跳到断点
#0 mysql_audit_notify (thd=0x1706c790, event_class=0, event_subtype=2) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.cc:207
#1 0x00000000005c7f84 in mysql_audit_general (thd=0x1706c790, event_subtype=2, error_code=0, msg=0x0)
at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.h:131
#2 0x00000000005c9d22 in dispatch_command (command=COM_QUERY, thd=0x1706c790, packet=0x1706f601 "", packet_length=27)
at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_parse.cc:1398
#3 0x00000000005ca0f8 in do_command (thd=0x1706c790) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_parse.cc:771
#4 0x0000000000694c7e in do_handle_one_connection (thd_arg=0x1706c790) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:787
#5 0x0000000000694d3b in handle_one_connection (arg=0x1706c790) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:706
#6 0x00000034f7e064a7 in start_thread () from /lib64/libpthread.so.0
#7 0x00000034f72d3c2d in clone () from /lib64/libc.so.6
看起来dispatch_command函数似乎是audit的入口,简要的归纳一下流程:
1.do_handle_one_connection()
2. do_command
3.dispatch_command(sql_parse.cc)
1397 if (!thd->is_error() && !thd->killed_errno()) 1398 mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0); 1399 1400 mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS, 1401 thd->stmt_da->is_error() ? thd->stmt_da->sql_errno() : 0, 1402 command_name[command].str);
这里会调用两次mysql_audit_general函数,分别对应不同的事件类型
4.mysql_audit_general() (sql_audit.h)
100 static inline 101 void mysql_audit_general(THD *thd, uint event_subtype, 102 int error_code, const char *msg) 103 { 104 #ifndef EMBEDDED_LIBRARY 105 if (mysql_global_audit_mask[0] & MYSQL_AUDIT_GENERAL_CLASSMASK) 106 { 107 time_t time= my_time(0); 108 uint msglen= msg ? strlen(msg) : 0; 109 const char *user; 110 uint userlen; 111 char user_buff[MAX_USER_HOST_SIZE]; 112 CSET_STRING query; 113 ha_rows rows; 114 115 if (thd) 116 { 117 query= thd->query_string; 118 user= user_buff; 119 userlen= make_user_name(thd, user_buff); 120 rows= thd->warning_info->current_row_for_warning(); 121 } 122 else 123 { 124 user= 0; 125 userlen= 0; 126 rows= 0; 127 } 128 129 mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, event_subtype, 130 error_code, time, user, userlen, msg, msglen, 131 query.str(), query.length(), query.charset(), rows); 132 } 133 #endif 134 }
5.mysql_audit_notify() (sql_audit.cc),该函数比较简短,会调用一个函数指针
audit_handler_t *handlers= audit_handlers + event_class;
这里,以event_class作为偏移量,来调用相应的函数指针:
audit_handlers作为全局变量,定义:
114 static audit_handler_t audit_handlers[] = 115 { 116 general_class_handler, connection_class_handler 117 };
5.1 general_class_handler()
event_class_dispatch(thd, MYSQL_AUDIT_GENERAL_CLASS, &event);5.2 connection_class_handler()
event_class_dispatch(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);可以看到上述两个函数都会调用event_class_dispatch,该函数的参数形式与自定义的插件函数参数完全一致,
深入代码可以发现,在创建连接的时候调用了上述两个函数都被调用了,其中conneciton_class_handler()在函数thd_prepare_connection中被调用:
710 bool thd_prepare_connection(THD *thd) 711 { 712 bool rc; 713 lex_start(thd); 714 rc= login_connection(thd); 715 MYSQL_AUDIT_NOTIFY_CONNECTION_CONNECT(thd);
…… ……
…
6.event_class_dispatch() (sql_audit.cc:466-491)
466 static void event_class_dispatch(THD *thd, unsigned int event_class, 467 const void *event) 468 { 469 struct st_mysql_event_generic event_generic; 470 event_generic.event_class= event_class; 471 event_generic.event= event; 472 /* 473 Check if we are doing a slow global dispatch. This event occurs when 474 thd == NULL as it is not associated with any particular thread. 475 */ 476 if (unlikely(!thd)) 477 { 478 plugin_foreach(thd, plugins_dispatch, MYSQL_AUDIT_PLUGIN, &event_generic); 479 } 480 else 481 { 482 plugin_ref *plugins, *plugins_last; 483 484 /* Use the cached set of audit plugins */ 485 plugins= (plugin_ref*) thd->audit_class_plugins.buffer; 486 plugins_last= plugins + thd->audit_class_plugins.elements; 487 488 for (; plugins < plugins_last; plugins++) 489 plugins_dispatch(thd, *plugins, &event_generic); 490 } 491 }
7. plugins_dispatch()
439 static my_bool plugins_dispatch(THD *thd, plugin_ref plugin, void *arg) 440 { 441 const struct st_mysql_event_generic *event_generic= 442 (const struct st_mysql_event_generic *) arg; 443 unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; 444 st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *); 445 446 set_audit_mask(event_class_mask, event_generic->event_class); 447 448 /* Check to see if the plugin is interested in this event */ 449 if (check_audit_mask(data->class_mask, event_class_mask)) 450 return 0; 451 452 /* Actually notify the plugin */ 453 data->event_notify(thd, event_generic->event_class, event_generic->event); 454 455 return 0; 456 }最终,我们到达了实际调用插件函数,使用函数指针来实现:
data->event_notify(thd, event_generic->event_class, event_generic->event);
————————————————————————————————————————————————————————————
下一步:
尝试对该audit审计插件的执行流程进行修改,来满足一些有趣的需求
相关文章推荐
- HEVC/H.265整个程序调用流程分析以及函数复杂度分析
- MySQL Audit Plugin now available in Percona Server 5.5 and 5.6
- MySQL源码分析(4):InnoDB主要数据结构及调用流程
- MySQL5.5的audit plugin
- Binder 机制详解—重要函数调用流程分析
- HEVC/H.265整个程序调用流程分析以及函数复杂度分析
- HEVC/H.265整个程序调用流程分析以及函数复杂度分析
- HEVC/H.265整个程序调用流程分析以及函数复杂度分析 .
- Mysql源代码分析系列(4): 主要调用流程(续)--转载
- 分析springmvc底层调用流程
- mysql5.5 主从复制 (触发器,函数,存储引擎,事件处理)说明
- CSP开发基础--加密通信过程中CryptAPI函数的调用流程
- <深入浅出>函数调用过程堆栈变化分析
- 函数调用堆栈变化分析
- Windows系统调用架构分析—也谈KiFastCallEntry函数地址的获取
- Stagefright AwesomePlayer 播放流程的函数调用
- Mysql工作流程分析
- C++类对象内存模型与成员函数调用分析(上)
- MySQL Bug导致异常宕机的分析流程
- VC环境下对函数调用的汇编分析【原创】