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中同樣存在。
Enjoy GreatSQL 😃
關於 GreatSQL
GreatSQL是適用於金融級應用的國內自主開源資料庫,具備高效能、高可靠、高易用性、高安全等多個核心特性,可以作為MySQL或Percona Server的可選替換,用於線上生產環境,且完全免費併相容MySQL或Percona Server。
相關連結: GreatSQL社群 Gitee GitHub Bilibili
GreatSQL社群:
社群部落格有獎徵稿詳情:https://greatsql.cn/thread-100-1-1.html
技術交流群:
微信:掃碼新增
GreatSQL社群助手
微信好友,傳送驗證資訊加群
。