写点什么

GreatSQL 的 sp 中添加新的 sp_instr 引入的 bug 解析

作者:GreatSQL
  • 2024-05-08
    福建
  • 本文字数:2812 字

    阅读完需:约 9 分钟

GreatSQL的sp中添加新的sp_instr引入的bug解析

GreatSQL 的 sp 中添加新的 sp_instr 引入的 bug 解析

一、问题发现

在一次开发中用到的 sp 需要添加新的 sp_instr 以满足需求,但是添加了数个 sp_instr 以后发现执行新的 sp 会发生 core。


注:本次使用的 GreatSQL 8.0.32-25


1、sp_head.cc 的 init_sp_psi_keys()代码里面添加 10 个新的 sp_instr:


void init_sp_psi_keys() {  mysql_statement_register(category, &sp_instr_stmt1::psi_info, 1);  mysql_statement_register(category, &sp_instr_stmt2::psi_info, 1);  mysql_statement_register(category, &sp_instr_stmt3::psi_info, 1);  ......  mysql_statement_register(category, &sp_instr_stmt10::psi_info, 1);}
复制代码


2、sp_instr.cc 里面添加新的 sp_instr_stmt 相关实现代码,其中 sql_yacc.yy 和 sql_lex.cc 需要相应添加新的语法。


3、sp_rcontext.h 处在·class sp_rcontext 里面添加几个新的成员变量。下面代码只是示例,不具有实际使用价值。


  Field *m_return_value_fld_tmp{m_return_value_fld};  Field *m_return_value_fld_tmp1{m_return_value_fld};  Field *m_return_value_fld_tmp2{m_return_value_fld};
复制代码


4、创建新的 sp,里面包含新的 sp_instr_stmt 的内容,然后 call 该 sp,结果发现代码逻辑处因为一个 list 里面 member 的值被清空了,然后导致 crash。下面是相关的堆栈。因为涉及代码机密,只截图开源部分相关堆栈。


#0  0x0000555558f3f3d9 in base_list_iterator::next_fast (this=0x7fffe01e9de0)    at /sql/sql_list.h:371#1  0x0000555558fc59b7 in List_iterator_fast<Create_field>::operator++ (this=0x7fffe01e9de0)    at /sql/sql_list.h:605#2  0x0000555559753ea2 in create_tmp_table_from_fields (thd=0x7fff20001050, field_list=...,     is_virtual=false, select_options=0, alias=0x0)    at /sql/sql_tmp_table.cc:2131#3  0x0000555559084a09 in Item_xx::val_str (this=0x7fff20b673c8)    at /sql/item_func.cc:10796#4  0x0000555558fa408b in Item::save_in_field_inner (this=0x7fff20b673c8, field=0x7fff20b9b1a8,     no_conversions=false) at /sql/item.cc:8202#5  0x0000555558fa3c43 in Item::save_in_field (this=0x7fff20b673c8, field=0x7fff20b9b1a8,     no_conversions=false) at /sql/item.cc:8144#6  0x0000555559400322 in sp_eval_expr (thd=0x7fff20001050, result_field=0x7fff20b9b1a8,     expr_item_ptr=0x7fff20b67620) at /sql/sp.cc:3613#7  0x000055555943b1d1 in sp_rcontext::set_variable (this=0x7fff20b85d80, thd=0x7fff20001050,     field=0x7fff20b9b1a8, value=0x7fff20b67620)    at /sql/sp_rcontext.cc:1023#8  0x0000555558fc3a8e in sp_rcontext::set_variable (this=0x7fff20b85d80, thd=0x7fff20001050,     var_idx=1, value=0x7fff20b67620)    at /sql/sp_rcontext.h:176打印crash处的信息,发现list里面的值被清空了。(gdb) p tmp$1 = (list_node *) 0x0
复制代码

二、问题调查过程

1、仔细检查代码发现代码逻辑没有问题,list 的值确实都有成功赋值,但是运行时候却发现 list 被清空,显然这是别的地方内存泄漏或者内存溢出导致 list 的元素空间被占用了或者被清空了。把 sp 的代码换成别的,有时候会 crash 有时候不会 crash,触发机制也不明朗,不知道具体哪句代码导致的内存泄漏。


2、于是回头继续看刚开始添加代码的地方,猜想是不是跟我添加了 10 个 sp_instr_stmt 有关,因为相关的数组或者内存没有添加扩容,很有可能因为这个导致内存溢出。


3、定位出疑似问题地方,就可以着手开始调查相关代码了。查看相关添加 sp_instr 的代码。


添加sp_instr实现代码如下:mysql_statement_register(category, &sp_instr_stmt1::psi_info, 1);
于是继续往下面调查mysql_statement_register实现的代码,看到这里果然用到了statement_class_max:PFS_statement_key register_statement_class(const char *name, uint name_length, PSI_statement_info *info) { /* See comments in register_mutex_class */ uint32 index; PFS_statement_class *entry;
REGISTER_CLASS_BODY_PART(index, statement_class_array, statement_class_max, name, name_length)
接着查看statement_class_max的赋值的地方:int init_statement_class(uint statement_class_sizing) { int result = 0; statement_class_dirty_count = statement_class_allocated_count = 0; statement_class_max = statement_class_sizing;
通过搜索代码查到statement_class_sizing相关的参数配置的地方,看到这里有一个SP_PSI_STATEMENT_INFO_COUNT宏定义,这个值跟sp_instr的数量有关。static Sys_var_ulong Sys_pfs_max_statement_classes( "performance_schema_max_statement_classes", "Maximum number of statement instruments.", READ_ONLY GLOBAL_VAR(pfs_param.m_statement_class_sizing), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256), DEFAULT((ulong)SQLCOM_END + (ulong)COM_END + 5 + SP_PSI_STATEMENT_INFO_COUNT + CLONE_PSI_STATEMENT_COUNT), BLOCK_SIZE(1), PFS_TRAILING_PROPERTIES);
继续全文搜索,发现在sp_head.h定义了,这里的值为16,数了一下现存的sp_instr个数刚好为16个,至此问题原因发现,因为我加了10个sp_instr,而这个宏定义的值没有跟着增加,导致内存溢出。#define SP_PSI_STATEMENT_INFO_COUNT 16
复制代码

三、问题解决方案

通过以上代码解析后,就可以修改相关问题代码,只要作如下修改即可。重新编译完,问题解决。


sp_head.h修改SP_PSI_STATEMENT_INFO_COUNT宏定义:#define SP_PSI_STATEMENT_INFO_COUNT 26
因为增加了Sys_pfs_max_statement_classes的default值,因为相关配置范围也要跟着增加,因此把range相应加大。static Sys_var_ulong Sys_pfs_max_statement_classes( "performance_schema_max_statement_classes", "Maximum number of statement instruments.", READ_ONLY GLOBAL_VAR(pfs_param.m_statement_class_sizing), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256 * 2), DEFAULT((ulong)SQLCOM_END + (ulong)COM_END + 5 + SP_PSI_STATEMENT_INFO_COUNT + CLONE_PSI_STATEMENT_COUNT), BLOCK_SIZE(1), PFS_TRAILING_PROPERTIES);
复制代码

四、问题总结

在 GreatSQL 的 sp 添加新的 sp_instr 需要相应增加对应的参数值以防止内存溢出,如果其他的功能也要做类似的修改,也要先仔细调查一下有没有涉及相关的参数配置或者宏定义,不然就会遇到各种莫名其妙的问题,调查起来也很花时间。


这次发现的问题属于新添加功能带入的 bug,在实际开发应用中类似的问题也要注意,一不小心就会踩坑。


上述问题在 MySQL/Percona 中同样存在。


发布于: 刚刚阅读数: 5
用户头像

GreatSQL

关注

GreatSQL社区 2023-01-31 加入

GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。 社区:https://greatsql.cn/ Gitee: https://gitee.com/GreatSQL/GreatSQL

评论

发布
暂无评论
GreatSQL的sp中添加新的sp_instr引入的bug解析_源码分析_GreatSQL_InfoQ写作社区