Merge lp:~sergei.glushchenko/percona-xtrabackup/bug740254-memleak-stats into lp:percona-xtrabackup/2.0

Proposed by Sergei Glushchenko
Status: Work in progress
Proposed branch: lp:~sergei.glushchenko/percona-xtrabackup/bug740254-memleak-stats
Merge into: lp:percona-xtrabackup/2.0
Diff against target: 296 lines (+145/-103)
2 files modified
src/xtrabackup.c (+103/-103)
test/t/bug740254.sh (+42/-0)
To merge this branch: bzr merge lp:~sergei.glushchenko/percona-xtrabackup/bug740254-memleak-stats
Reviewer Review Type Date Requested Status
Laurynas Biveinis (community) Needs Fixing
Review via email: mp+96293@code.launchpad.net

Description of the change

Bug740254
Memory leak in xtrabackup_stats_level when calling rec_get_offsets inside
loop. It happens when table has more then REC_OFFS_NORMAL_SIZE columns.
Fix include testcase which do xtrabackup --stats on backup which contains
table with 1000 columns.

To post a comment you must log in.
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

For the reference, here is diff -b of the relevant part:

@@ -4829,9 +4576,14 @@
 #endif
        }

-loop:
        mem_heap_empty(heap);
- offsets = NULL;
+
+ ulint offsets_array[REC_OFFS_NORMAL_SIZE];
+ ulint* local_offsets = offsets_array;
+ *offsets_array = (sizeof offsets_array) / sizeof *offsets_array;
+
+ for (;;) {
+
 #if (MYSQL_VERSION_ID < 50100)
        mtr_x_lock(&(index->tree->lock), &mtr);
 #else /* MYSQL_VERSION_ID < 51000 */
@@ -4848,16 +4600,10 @@
        sum_data += page_get_data_size(page);
        n_recs += page_get_n_recs(page);

-
        if (level == 0) {
                page_cur_t cur;
                ulint n_fields;
                ulint i;
- mem_heap_t* local_heap = NULL;
- ulint offsets_[REC_OFFS_NORMAL_SIZE];
- ulint* local_offsets = offsets_;
-
- *offsets_ = (sizeof offsets_) / sizeof *offsets_;

 #ifndef INNODB_VERSION_SHORT
                page_cur_set_before_first(page, &cur);
@@ -4872,7 +4618,7 @@
                        }

                        local_offsets = rec_get_offsets(cur.rec, index, local_offsets,
- ULINT_UNDEFINED, &local_heap);
+ ULINT_UNDEFINED, &heap);
                        n_fields = rec_offs_n_fields(local_offsets);

                        for (i = 0; i < n_fields; i++) {
@@ -4955,7 +4701,8 @@

        mtr_commit(&mtr);
- if (right_page_no != FIL_NULL) {
+ if (right_page_no == FIL_NULL)
+ break;
                mtr_start(&mtr);
 #ifndef INNODB_VERSION_SHORT
                page = btr_page_get(space, right_page_no, RW_X_LATCH, &mtr);
@@ -4969,8 +4716,8 @@
 #endif
                page = buf_block_get_frame(block);
 #endif
- goto loop;
        }
+
        mem_heap_free(heap);

 #ifndef INNODB_VERSION_SHORT

Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

   Since you are touching the code anyway, in diff lines 127,141
   s/record/records.

   Please move all local var decls to the block start (in this case
   function start).

   The extra indentation makes a lot of lines to go over the line
   length limit. I am not sure if and how we'd want to fix this
   (especially for 2.1), please get a second opinion. IMVHO, we should
   leave it as-is for 2.0 and extract new function in 2.1.

review: Needs Fixing

Unmerged revisions

392. By Sergei Glushchenko

Bug740254
Memory leak in xtrabackup_stats_level when calling rec_get_offsets inside
loop. It happens when table has more then REC_OFFS_NORMAL_SIZE columns.
Fix include testcase which do xtrabackup --stats on backup which contains
table with 1000 columns.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/xtrabackup.c'
2--- src/xtrabackup.c 2012-02-20 04:38:44 +0000
3+++ src/xtrabackup.c 2012-03-07 05:03:18 +0000
4@@ -4579,133 +4579,133 @@
5 #endif
6 }
7
8-loop:
9 mem_heap_empty(heap);
10- offsets = NULL;
11+
12+ ulint offsets_array[REC_OFFS_NORMAL_SIZE];
13+ ulint* local_offsets = offsets_array;
14+ *offsets_array = (sizeof offsets_array) / sizeof *offsets_array;
15+
16+ for (;;) {
17+
18 #if (MYSQL_VERSION_ID < 50100)
19- mtr_x_lock(&(index->tree->lock), &mtr);
20+ mtr_x_lock(&(index->tree->lock), &mtr);
21 #else /* MYSQL_VERSION_ID < 51000 */
22- mtr_x_lock(&(index->lock), &mtr);
23+ mtr_x_lock(&(index->lock), &mtr);
24 #endif
25
26- right_page_no = btr_page_get_next(page, &mtr);
27-
28-
29- /*=================================*/
30- //fprintf(stdout, "%lu ", (ulint) buf_frame_get_page_no(page));
31-
32- n_pages++;
33- sum_data += page_get_data_size(page);
34- n_recs += page_get_n_recs(page);
35-
36-
37- if (level == 0) {
38- page_cur_t cur;
39- ulint n_fields;
40- ulint i;
41- mem_heap_t* local_heap = NULL;
42- ulint offsets_[REC_OFFS_NORMAL_SIZE];
43- ulint* local_offsets = offsets_;
44-
45- *offsets_ = (sizeof offsets_) / sizeof *offsets_;
46+ right_page_no = btr_page_get_next(page, &mtr);
47+
48+
49+ /*=================================*/
50+ //fprintf(stdout, "%lu ", (ulint) buf_frame_get_page_no(page));
51+
52+ n_pages++;
53+ sum_data += page_get_data_size(page);
54+ n_recs += page_get_n_recs(page);
55+
56+ if (level == 0) {
57+ page_cur_t cur;
58+ ulint n_fields;
59+ ulint i;
60
61 #ifndef INNODB_VERSION_SHORT
62- page_cur_set_before_first(page, &cur);
63+ page_cur_set_before_first(page, &cur);
64 #else
65- page_cur_set_before_first(block, &cur);
66+ page_cur_set_before_first(block, &cur);
67 #endif
68- page_cur_move_to_next(&cur);
69-
70- for (;;) {
71- if (page_cur_is_after_last(&cur)) {
72- break;
73- }
74-
75- local_offsets = rec_get_offsets(cur.rec, index, local_offsets,
76- ULINT_UNDEFINED, &local_heap);
77- n_fields = rec_offs_n_fields(local_offsets);
78-
79- for (i = 0; i < n_fields; i++) {
80- if (rec_offs_nth_extern(local_offsets, i)) {
81- page_t* local_page;
82- ulint space_id;
83- ulint page_no;
84- ulint offset;
85- byte* blob_header;
86- ulint part_len;
87- mtr_t local_mtr;
88- ulint local_len;
89- byte* data;
90+ page_cur_move_to_next(&cur);
91+
92+ for (;;) {
93+ if (page_cur_is_after_last(&cur)) {
94+ break;
95+ }
96+
97+ local_offsets = rec_get_offsets(cur.rec, index, local_offsets,
98+ ULINT_UNDEFINED, &heap);
99+ n_fields = rec_offs_n_fields(local_offsets);
100+
101+ for (i = 0; i < n_fields; i++) {
102+ if (rec_offs_nth_extern(local_offsets, i)) {
103+ page_t* local_page;
104+ ulint space_id;
105+ ulint page_no;
106+ ulint offset;
107+ byte* blob_header;
108+ ulint part_len;
109+ mtr_t local_mtr;
110+ ulint local_len;
111+ byte* data;
112 #ifdef INNODB_VERSION_SHORT
113- buf_block_t* local_block;
114+ buf_block_t* local_block;
115 #endif
116
117- data = rec_get_nth_field(cur.rec, local_offsets, i, &local_len);
118-
119- ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE);
120- local_len -= BTR_EXTERN_FIELD_REF_SIZE;
121-
122- space_id = mach_read_from_4(data + local_len + BTR_EXTERN_SPACE_ID);
123- page_no = mach_read_from_4(data + local_len + BTR_EXTERN_PAGE_NO);
124- offset = mach_read_from_4(data + local_len + BTR_EXTERN_OFFSET);
125-
126- if (offset != FIL_PAGE_DATA)
127- msg("\nWarning: several record may share same external page.\n");
128-
129- for (;;) {
130- mtr_start(&local_mtr);
131+ data = rec_get_nth_field(cur.rec, local_offsets, i, &local_len);
132+
133+ ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE);
134+ local_len -= BTR_EXTERN_FIELD_REF_SIZE;
135+
136+ space_id = mach_read_from_4(data + local_len + BTR_EXTERN_SPACE_ID);
137+ page_no = mach_read_from_4(data + local_len + BTR_EXTERN_PAGE_NO);
138+ offset = mach_read_from_4(data + local_len + BTR_EXTERN_OFFSET);
139+
140+ if (offset != FIL_PAGE_DATA)
141+ msg("\nWarning: several record may share same external page.\n");
142+
143+ for (;;) {
144+ mtr_start(&local_mtr);
145
146 #ifndef INNODB_VERSION_SHORT
147- local_page = buf_page_get(space_id, page_no, RW_S_LATCH, &local_mtr);
148+ local_page = buf_page_get(space_id, page_no, RW_S_LATCH, &local_mtr);
149 #ifdef UNIV_SYNC_DEBUG
150- buf_page_dbg_add_level(local_page, SYNC_NO_ORDER_CHECK);
151+ buf_page_dbg_add_level(local_page, SYNC_NO_ORDER_CHECK);
152 #endif
153 #else
154 #if (MYSQL_VERSION_ID < 50517)
155- local_block = btr_block_get(space_id, zip_size, page_no, RW_S_LATCH, &local_mtr);
156+ local_block = btr_block_get(space_id, zip_size, page_no, RW_S_LATCH, &local_mtr);
157 #else
158- local_block = btr_block_get(space_id, zip_size, page_no, RW_S_LATCH, index, &local_mtr);
159-#endif
160- local_page = buf_block_get_frame(local_block);
161-#endif
162- blob_header = local_page + offset;
163+ local_block = btr_block_get(space_id, zip_size, page_no, RW_S_LATCH, index, &local_mtr);
164+#endif
165+ local_page = buf_block_get_frame(local_block);
166+#endif
167+ blob_header = local_page + offset;
168 #define BTR_BLOB_HDR_PART_LEN 0
169 #define BTR_BLOB_HDR_NEXT_PAGE_NO 4
170- //part_len = btr_blob_get_part_len(blob_header);
171- part_len = mach_read_from_4(blob_header + BTR_BLOB_HDR_PART_LEN);
172-
173- //page_no = btr_blob_get_next_page_no(blob_header);
174- page_no = mach_read_from_4(blob_header + BTR_BLOB_HDR_NEXT_PAGE_NO);
175-
176- offset = FIL_PAGE_DATA;
177-
178-
179-
180-
181- /*=================================*/
182- //fprintf(stdout, "[%lu] ", (ulint) buf_frame_get_page_no(page));
183-
184- n_pages_extern++;
185- sum_data_extern += part_len;
186-
187-
188- mtr_commit(&local_mtr);
189-
190- if (page_no == FIL_NULL)
191- break;
192+ //part_len = btr_blob_get_part_len(blob_header);
193+ part_len = mach_read_from_4(blob_header + BTR_BLOB_HDR_PART_LEN);
194+
195+ //page_no = btr_blob_get_next_page_no(blob_header);
196+ page_no = mach_read_from_4(blob_header + BTR_BLOB_HDR_NEXT_PAGE_NO);
197+
198+ offset = FIL_PAGE_DATA;
199+
200+
201+
202+
203+ /*=================================*/
204+ //fprintf(stdout, "[%lu] ", (ulint) buf_frame_get_page_no(page));
205+
206+ n_pages_extern++;
207+ sum_data_extern += part_len;
208+
209+
210+ mtr_commit(&local_mtr);
211+
212+ if (page_no == FIL_NULL)
213+ break;
214+ }
215 }
216 }
217+
218+ page_cur_move_to_next(&cur);
219 }
220-
221- page_cur_move_to_next(&cur);
222 }
223- }
224-
225-
226-
227-
228- mtr_commit(&mtr);
229- if (right_page_no != FIL_NULL) {
230+
231+
232+
233+
234+ mtr_commit(&mtr);
235+ if (right_page_no == FIL_NULL)
236+ break;
237 mtr_start(&mtr);
238 #ifndef INNODB_VERSION_SHORT
239 page = btr_page_get(space, right_page_no, RW_X_LATCH, &mtr);
240@@ -4719,8 +4719,8 @@
241 #endif
242 page = buf_block_get_frame(block);
243 #endif
244- goto loop;
245 }
246+
247 mem_heap_free(heap);
248
249 #ifndef INNODB_VERSION_SHORT
250
251=== added file 'test/t/bug740254.sh'
252--- test/t/bug740254.sh 1970-01-01 00:00:00 +0000
253+++ test/t/bug740254.sh 2012-03-07 05:03:18 +0000
254@@ -0,0 +1,42 @@
255+##################################################################
256+# Bug #740254: Memory leak in xtrabackup --stats #
257+##################################################################
258+
259+. inc/common.sh
260+
261+init
262+run_mysqld
263+
264+# create table with 1000 columns
265+
266+CREATE_TABLE="CREATE TABLE t1 ("
267+for i in {1..999}; do
268+ CREATE_TABLE="${CREATE_TABLE}f${i} int,"
269+done
270+CREATE_TABLE="${CREATE_TABLE}f1000 int)"
271+
272+echo $CREATE_TABLE | run_cmd $MYSQL $MYSQL_ARGS test
273+
274+INS="INSERT INTO t1 values ("
275+for i in {1..999}; do
276+ INS="${INS}${i},"
277+done
278+INS="${INS}1000)"
279+
280+INS="$INS;$INS;$INS;$INS;"
281+
282+for i in {1..5} ; do
283+ echo $INS | run_cmd $MYSQL $MYSQL_ARGS test
284+done
285+
286+run_cmd $MYSQL $MYSQL_ARGS test -e "select * from t1"
287+
288+innobackupex --no-timestamp $topdir/backup
289+
290+stop_mysqld
291+
292+vlog "Applying log"
293+innobackupex --apply-log $topdir/backup
294+
295+# memmory leak might be here
296+xtrabackup --stats --datadir=$topdir/backup

Subscribers

People subscribed via source and target branches