Skip to content

Commit eac8249

Browse files
author
Sven Sandberg
committed
BUG#21304682: VALGRIND ERRORS IN DEBUG PRINTOUTS FOR REPLICATION CODE
Two places in replication code were causing Valgrind errors: 1. Memory leak in Relay_log_info::wait_for_gtid_set, since it passed the return value from Gtid_set::to_string() directly to DBUG_PRINT, without storing it anywhere so that it can be freed. 2. In MYSQL_BIN_LOG::init_gtid_sets would pass a bad pointer to DBUG_PRINT in some cases. In problem #1, an underlying problem was that to_string returns newly allocated memory and this was easy to miss when reading the code that calls the function. It would be better to return the value through a parameter, since that forces the caller to store it in a variable, and then it is more obvious that the value must be freed. And in fact such a function existed already, so we fix the problem by removing the (redundant) no-args version of Gtid_set::to_string and using the one- or two-argument function instead. In problem #2, print an empty string if we detect that the pointer will be bad. These bugs were found when adding some debug printouts to read_gtids_from_binlog. These debug printouts never made it to the server code through any other bug report, but would be useful to have for future debugging, so including them in this patch. Additionally, removed the call to global_sid_lock->rdlock() used before Previous_gtids_log_event::get_str(). This is not needed since Previous_gtids_log_event doesn't use shared resources.
1 parent fc77c11 commit eac8249

File tree

9 files changed

+114
-65
lines changed

9 files changed

+114
-65
lines changed

sql/binlog.cc

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3895,6 +3895,21 @@ read_gtids_from_binlog(const char *filename, Gtid_set *all_gtids,
38953895
mysql_file_close(file, MYF(MY_WME));
38963896
end_io_cache(&log);
38973897

3898+
if (all_gtids)
3899+
all_gtids->dbug_print("all_gtids");
3900+
else
3901+
DBUG_PRINT("info", ("all_gtids==NULL"));
3902+
if (prev_gtids)
3903+
prev_gtids->dbug_print("prev_gtids");
3904+
else
3905+
DBUG_PRINT("info", ("prev_gtids==NULL"));
3906+
if (first_gtid == NULL)
3907+
DBUG_PRINT("info", ("first_gtid==NULL"));
3908+
else if (first_gtid->sidno == 0)
3909+
DBUG_PRINT("info", ("first_gtid.sidno==0"));
3910+
else
3911+
first_gtid->dbug_print(sid_map, "first_gtid");
3912+
38983913
DBUG_PRINT("info", ("returning %d", ret));
38993914
DBUG_RETURN(ret);
39003915
}
@@ -4019,8 +4034,9 @@ bool MYSQL_BIN_LOG::init_gtid_sets(Gtid_set *all_gtids, Gtid_set *lost_gtids,
40194034
bool is_server_starting)
40204035
{
40214036
DBUG_ENTER("MYSQL_BIN_LOG::init_gtid_sets");
4022-
DBUG_PRINT("info", ("lost_gtids=%p; so we are recovering a %s log",
4023-
lost_gtids, lost_gtids == NULL ? "relay" : "binary"));
4037+
DBUG_PRINT("info", ("lost_gtids=%p; so we are recovering a %s log; is_relay_log=%d",
4038+
lost_gtids, lost_gtids == NULL ? "relay" : "binary",
4039+
is_relay_log));
40244040

40254041
/*
40264042
If this is a relay log, we must have the IO thread Master_info trx_parser
@@ -4108,7 +4124,8 @@ bool MYSQL_BIN_LOG::init_gtid_sets(Gtid_set *all_gtids, Gtid_set *lost_gtids,
41084124
bool can_stop_reading= false;
41094125
reached_first_file= (rit == filename_list.rend());
41104126
DBUG_PRINT("info", ("filename='%s' reached_first_file=%d",
4111-
reached_first_file ? "" : rit->c_str(), reached_first_file));
4127+
reached_first_file ? "" : rit->c_str(),
4128+
reached_first_file));
41124129
while (!can_stop_reading && !reached_first_file)
41134130
{
41144131
const char *filename= rit->c_str();

sql/log_event.cc

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13118,9 +13118,7 @@ Previous_gtids_log_event::Previous_gtids_log_event(const Gtid_set *set)
1311813118
int Previous_gtids_log_event::pack_info(Protocol *protocol)
1311913119
{
1312013120
size_t length= 0;
13121-
global_sid_lock->rdlock();
1312213121
char *str= get_str(&length, &Gtid_set::default_string_format);
13123-
global_sid_lock->unlock();
1312413122
if (str == NULL)
1312513123
return 1;
1312613124
protocol->store(str, length, &my_charset_bin);
@@ -13134,10 +13132,7 @@ void Previous_gtids_log_event::print(FILE *file,
1313413132
PRINT_EVENT_INFO *print_event_info)
1313513133
{
1313613134
IO_CACHE *const head= &print_event_info->head_cache;
13137-
13138-
global_sid_lock->rdlock();
1313913135
char *str= get_str(NULL, &Gtid_set::commented_string_format);
13140-
global_sid_lock->unlock();
1314113136
if (str != NULL)
1314213137
{
1314313138
if (!print_event_info->short_form)
@@ -13167,7 +13162,7 @@ int Previous_gtids_log_event::add_to_set(Gtid_set *target) const
1316713162
char *Previous_gtids_log_event::get_str(
1316813163
size_t *length_p, const Gtid_set::String_format *string_format) const
1316913164
{
13170-
DBUG_ENTER("Previous_gtids_log_event::get_str(size_t *)");
13165+
DBUG_ENTER("Previous_gtids_log_event::get_str(size_t *, const Gtid_set::String_format *)");
1317113166
Sid_map sid_map(NULL);
1317213167
Gtid_set set(&sid_map, NULL);
1317313168
DBUG_PRINT("info", ("temp_buf=%p buf=%p", temp_buf, buf));
@@ -13180,7 +13175,7 @@ char *Previous_gtids_log_event::get_str(
1318013175
length + 1, MYF(MY_WME));
1318113176
if (str != NULL)
1318213177
{
13183-
set.to_string(str, string_format);
13178+
set.to_string(str, false/*need_lock*/, string_format);
1318413179
if (length_p != NULL)
1318513180
*length_p= length;
1318613181
}

sql/mysqld.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4703,6 +4703,9 @@ int mysqld_main(int argc, char **argv)
47034703

47044704
global_sid_lock->wrlock();
47054705

4706+
purged_gtids_from_binlog.dbug_print("purged_gtids_from_binlog");
4707+
gtids_in_binlog.dbug_print("gtids_in_binlog");
4708+
47064709
if (!gtids_in_binlog.is_empty() &&
47074710
!gtids_in_binlog.is_subset(executed_gtids))
47084711
{

sql/rpl_group_replication.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,9 @@ char* encoded_gtid_set_to_string(uchar *encoded_gtid_set,
333333
RETURN_STATUS_OK)
334334
return NULL;
335335

336-
return set.to_string();
336+
char *buf;
337+
set.to_string(&buf);
338+
return buf;
337339
}
338340
#endif
339341

sql/rpl_gtid.h

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,39 +1338,6 @@ class Gtid_set
13381338
Gtid_set, false otherwise.
13391339
*/
13401340
static bool is_valid(const char *text);
1341-
/**
1342-
Return a newly allocated string containing this Gtid_set, or NULL
1343-
on out of memory.
1344-
*/
1345-
char *to_string() const
1346-
{
1347-
char *str= (char *)my_malloc(key_memory_Gtid_set_to_string,
1348-
get_string_length() + 1, MYF(MY_WME));
1349-
if (str != NULL)
1350-
to_string(str);
1351-
return str;
1352-
}
1353-
#ifndef DBUG_OFF
1354-
/// Debug only: Print this Gtid_set to stdout.
1355-
void print() const
1356-
{
1357-
char *str= to_string();
1358-
printf("%s\n", str);
1359-
my_free(str);
1360-
}
1361-
#endif
1362-
/**
1363-
Print this Gtid_set to the trace file if debug is enabled; no-op
1364-
otherwise.
1365-
*/
1366-
void dbug_print(const char *text= "") const
1367-
{
1368-
#ifndef DBUG_OFF
1369-
char *str= to_string();
1370-
DBUG_PRINT("info", ("%s%s'%s'", text, *text ? ": " : "", str));
1371-
my_free(str);
1372-
#endif
1373-
}
13741341

13751342
/**
13761343
Class Gtid_set::String_format defines the separators used by
@@ -1417,20 +1384,58 @@ class Gtid_set
14171384
14181385
@param[out] buf Pointer to the buffer where the string should be
14191386
stored. This should have size at least get_string_length()+1.
1387+
@param need_lock If this Gtid_set has a sid_lock, then the write
1388+
lock must be held while generating the string. If this parameter
1389+
is true, then this function acquires and releases the lock;
1390+
otherwise it asserts that the caller holds the lock.
14201391
@param string_format String_format object that specifies
14211392
separators in the resulting text.
14221393
@return Length of the generated string.
14231394
*/
1424-
int to_string(char *buf, const String_format *string_format= NULL) const;
1395+
int to_string(char *buf, bool need_lock= false,
1396+
const String_format *string_format= NULL) const;
14251397

14261398
/**
14271399
Formats a Gtid_set as a string and saves in a newly allocated buffer.
14281400
@param[out] buf Pointer to pointer to string. The function will
14291401
set it to point to the newly allocated buffer, or NULL on out of memory.
1402+
@param need_lock If this Gtid_set has a sid_lock, then the write
1403+
lock must be held while generating the string. If this parameter
1404+
is true, then this function acquires and releases the lock;
1405+
otherwise it asserts that the caller holds the lock.
14301406
@param string_format Specifies how to format the string.
14311407
@retval Length of the generated string, or -1 on out of memory.
14321408
*/
1433-
int to_string(char **buf, const String_format *string_format= NULL) const;
1409+
int to_string(char **buf, bool need_lock= false,
1410+
const String_format *string_format= NULL) const;
1411+
#ifndef DBUG_OFF
1412+
/// Debug only: Print this Gtid_set to stdout.
1413+
void print(bool need_lock= false,
1414+
const Gtid_set::String_format *sf= NULL) const
1415+
{
1416+
char *str;
1417+
to_string(&str, need_lock, sf);
1418+
printf("%s\n", str ? str : "out of memory in Gtid_set::print");
1419+
my_free(str);
1420+
}
1421+
#endif
1422+
/**
1423+
Print this Gtid_set to the trace file if debug is enabled; no-op
1424+
otherwise.
1425+
*/
1426+
void dbug_print(const char *text= "", bool need_lock= false,
1427+
const Gtid_set::String_format *sf= NULL) const
1428+
{
1429+
#ifndef DBUG_OFF
1430+
char *str;
1431+
to_string(&str, need_lock, sf);
1432+
DBUG_PRINT("info", ("%s%s'%s'",
1433+
text,
1434+
*text ? ": " : "",
1435+
str ? str : "out of memory in Gtid_set::dbug_print"));
1436+
my_free(str);
1437+
#endif
1438+
}
14341439
/**
14351440
Gets all gtid intervals from this Gtid_set.
14361441

sql/rpl_gtid_set.cc

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -829,30 +829,48 @@ rpl_gno Gtid_set::get_last_gno(rpl_sidno sidno) const
829829
DBUG_RETURN(gno);
830830
}
831831

832-
int Gtid_set::to_string(char **buf_arg, const Gtid_set::String_format *sf_arg) const
832+
int Gtid_set::to_string(char **buf_arg, bool need_lock,
833+
const Gtid_set::String_format *sf_arg) const
833834
{
834835
DBUG_ENTER("Gtid_set::to_string");
836+
if (sid_lock != NULL)
837+
{
838+
if (need_lock)
839+
sid_lock->wrlock();
840+
else
841+
sid_lock->assert_some_wrlock();
842+
}
835843
int len= get_string_length(sf_arg);
836844
*buf_arg= (char *)my_malloc(key_memory_Gtid_set_to_string,
837845
len + 1, MYF(MY_WME));
838846
if (*buf_arg == NULL)
839847
DBUG_RETURN(-1);
840-
to_string(*buf_arg, sf_arg);
848+
to_string(*buf_arg, false/*need_lock*/, sf_arg);
849+
if (sid_lock != NULL && need_lock)
850+
sid_lock->unlock();
841851
DBUG_RETURN(len);
842852
}
843853

844-
int Gtid_set::to_string(char *buf, const Gtid_set::String_format *sf) const
854+
int Gtid_set::to_string(char *buf, bool need_lock,
855+
const Gtid_set::String_format *sf) const
845856
{
846857
DBUG_ENTER("Gtid_set::to_string");
847858
DBUG_ASSERT(sid_map != NULL);
848859
if (sid_lock != NULL)
849-
sid_lock->assert_some_wrlock();
860+
{
861+
if (need_lock)
862+
sid_lock->wrlock();
863+
else
864+
sid_lock->assert_some_wrlock();
865+
}
850866
if (sf == NULL)
851867
sf= &default_string_format;
852868
if (sf->empty_set_string != NULL && is_empty())
853869
{
854870
memcpy(buf, sf->empty_set_string, sf->empty_set_string_length);
855871
buf[sf->empty_set_string_length]= '\0';
872+
if (sid_lock != NULL && need_lock)
873+
sid_lock->unlock();
856874
DBUG_RETURN(sf->empty_set_string_length);
857875
}
858876
rpl_sidno map_max_sidno= sid_map->get_max_sidno();
@@ -907,6 +925,8 @@ int Gtid_set::to_string(char *buf, const Gtid_set::String_format *sf) const
907925
DBUG_PRINT("info", ("ret='%s' strlen(s)=%zu s-buf=%lu get_string_length=%d", buf,
908926
strlen(buf), (ulong) (s - buf), get_string_length(sf)));
909927
DBUG_ASSERT(s - buf == get_string_length(sf));
928+
if (sid_lock != NULL && need_lock)
929+
sid_lock->unlock();
910930
DBUG_RETURN((int)(s - buf));
911931
}
912932

sql/rpl_master.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ bool com_binlog_dump_gtid(THD *thd, char *packet, size_t packet_length)
386386
if (slave_gtid_executed.add_gtid_encoding(packet_position, data_size) !=
387387
RETURN_STATUS_OK)
388388
DBUG_RETURN(true);
389-
gtid_string= slave_gtid_executed.to_string();
389+
slave_gtid_executed.to_string(&gtid_string);
390390
DBUG_PRINT("info", ("Slave %d requested to read %s at position %llu gtid set "
391391
"'%s'.", thd->server_id, name, pos, gtid_string));
392392

sql/rpl_rli.cc

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -955,10 +955,14 @@ int Relay_log_info::wait_for_gtid_set(THD* thd, const Gtid_set* wait_gtid_set,
955955
const Gtid_set* executed_gtids= gtid_state->get_executed_gtids();
956956
const Owned_gtids* owned_gtids= gtid_state->get_owned_gtids();
957957

958+
char *wait_gtid_set_buf;
959+
wait_gtid_set->to_string(&wait_gtid_set_buf);
958960
DBUG_PRINT("info", ("Waiting for '%s'. is_subset: %d and "
959961
"!is_intersection_nonempty: %d",
960-
wait_gtid_set->to_string(), wait_gtid_set->is_subset(executed_gtids),
961-
!owned_gtids->is_intersection_nonempty(wait_gtid_set)));
962+
wait_gtid_set_buf,
963+
wait_gtid_set->is_subset(executed_gtids),
964+
!owned_gtids->is_intersection_nonempty(wait_gtid_set)));
965+
my_free(wait_gtid_set_buf);
962966
executed_gtids->dbug_print("gtid_executed:");
963967
owned_gtids->dbug_print("owned_gtids:");
964968

@@ -1446,7 +1450,8 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev)
14461450
const Gtid_set* executed_gtids= gtid_state->get_executed_gtids();
14471451
if (until_sql_gtids.is_intersection_nonempty(executed_gtids))
14481452
{
1449-
char *buffer= until_sql_gtids.to_string();
1453+
char *buffer;
1454+
until_sql_gtids.to_string(&buffer);
14501455
global_sid_lock->unlock();
14511456
sql_print_information("Slave SQL thread stopped because "
14521457
"UNTIL SQL_BEFORE_GTIDS %s is already "
@@ -1462,7 +1467,8 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev)
14621467
global_sid_lock->rdlock();
14631468
if (until_sql_gtids.contains_gtid(gev->get_sidno(false), gev->get_gno()))
14641469
{
1465-
char *buffer= until_sql_gtids.to_string();
1470+
char *buffer;
1471+
until_sql_gtids.to_string(&buffer);
14661472
global_sid_lock->unlock();
14671473
sql_print_information("Slave SQL thread stopped because it reached "
14681474
"UNTIL SQL_BEFORE_GTIDS %s", buffer);
@@ -1480,7 +1486,8 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev)
14801486
const Gtid_set* executed_gtids= gtid_state->get_executed_gtids();
14811487
if (until_sql_gtids.is_subset(executed_gtids))
14821488
{
1483-
char *buffer= until_sql_gtids.to_string();
1489+
char *buffer;
1490+
until_sql_gtids.to_string(&buffer);
14841491
global_sid_lock->unlock();
14851492
sql_print_information("Slave SQL thread stopped because it reached "
14861493
"UNTIL SQL_AFTER_GTIDS %s", buffer);

sql/sys_vars.cc

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5179,10 +5179,10 @@ bool Sys_var_gtid_purged::global_update(THD *thd, set_var *var)
51795179
bool error= false;
51805180

51815181
global_sid_lock->wrlock();
5182-
char *previous_gtid_executed= gtid_state->get_executed_gtids()->to_string();
5183-
char *previous_gtid_lost= gtid_state->get_lost_gtids()->to_string();
5184-
char *current_gtid_executed= NULL;
5185-
char *current_gtid_lost= NULL;
5182+
char *previous_gtid_executed= NULL, *previous_gtid_purged= NULL,
5183+
*current_gtid_executed= NULL, *current_gtid_purged= NULL;
5184+
gtid_state->get_executed_gtids()->to_string(&previous_gtid_executed);
5185+
gtid_state->get_lost_gtids()->to_string(&previous_gtid_purged);
51865186
enum_return_status ret;
51875187
Gtid_set gtid_set(global_sid_map, var->save_result.string_value.str,
51885188
&ret, global_sid_lock);
@@ -5199,21 +5199,21 @@ bool Sys_var_gtid_purged::global_update(THD *thd, set_var *var)
51995199
error= true;
52005200
goto end;
52015201
}
5202-
current_gtid_executed= gtid_state->get_executed_gtids()->to_string();
5203-
current_gtid_lost= gtid_state->get_lost_gtids()->to_string();
5202+
gtid_state->get_executed_gtids()->to_string(&current_gtid_executed);
5203+
gtid_state->get_lost_gtids()->to_string(&current_gtid_purged);
52045204
global_sid_lock->unlock();
52055205

52065206
// Log messages saying that GTID_PURGED and GTID_EXECUTED were changed.
52075207
sql_print_information(ER(ER_GTID_PURGED_WAS_CHANGED),
5208-
previous_gtid_lost, current_gtid_lost);
5208+
previous_gtid_purged, current_gtid_purged);
52095209
sql_print_information(ER(ER_GTID_EXECUTED_WAS_CHANGED),
52105210
previous_gtid_executed, current_gtid_executed);
52115211

52125212
end:
52135213
my_free(previous_gtid_executed);
5214-
my_free(previous_gtid_lost);
5214+
my_free(previous_gtid_purged);
52155215
my_free(current_gtid_executed);
5216-
my_free(current_gtid_lost);
5216+
my_free(current_gtid_purged);
52175217
DBUG_RETURN(error);
52185218
#else
52195219
DBUG_RETURN(true);

0 commit comments

Comments
 (0)