Skip to content

Commit

Permalink
MDEV-35394 Innochecksum misinterprets freed pages
Browse files Browse the repository at this point in the history
- Innochecksum misinterprets the freed pages as active one.
This leads the user to think there are too many valid
pages exist.

- To avoid this confusion, innochecksum introduced one
more option --skip-freed-pages and -r to avoid the freed
pages while dumping or printing the summary of the tablespace.

- Innochecksum can safely assume the page is freed if
the respective extent doesn't belong to a segment and marked as
freed in XDES_BITMAP in extent descriptor page.

- Innochecksum shouldn't assume that zero-filled page as extent
descriptor page.
  • Loading branch information
Thirunarayanan committed Nov 26, 2024
1 parent 2255be0 commit 0be600a
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 20 deletions.
45 changes: 26 additions & 19 deletions extra/innochecksum.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ static my_bool do_leaf;
static my_bool per_page_details;
static ulint n_merge;
static ulint physical_page_size; /* Page size in bytes on disk. */
static ulint extent_size;
ulong srv_page_size;
ulong srv_page_size_shift;
/* Current page number (0 based). */
Expand All @@ -99,7 +100,7 @@ char* log_filename = NULL;
FILE* log_file = NULL;
/* Enabled for log write option. */
static bool is_log_enabled = false;

static bool skip_freed_pages;
static byte field_ref_zero_buf[UNIV_PAGE_SIZE_MAX];
const byte *field_ref_zero = field_ref_zero_buf;

Expand Down Expand Up @@ -267,6 +268,7 @@ static void init_page_size(const byte* buf)
srv_page_size_shift = UNIV_ZIP_SIZE_SHIFT_MIN - 1 + ssize;
srv_page_size = 512U << ssize;
physical_page_size = srv_page_size;
extent_size = FSP_EXTENT_SIZE;
return;
}

Expand All @@ -278,6 +280,7 @@ static void init_page_size(const byte* buf)

srv_page_size = fil_space_t::logical_size(flags);
physical_page_size = fil_space_t::physical_size(flags);
extent_size = FSP_EXTENT_SIZE;
}

#ifdef _WIN32
Expand Down Expand Up @@ -555,8 +558,8 @@ bool
is_page_doublewritebuffer(
const byte* page)
{
if ((cur_page_num >= FSP_EXTENT_SIZE)
&& (cur_page_num < FSP_EXTENT_SIZE * 3)) {
if ((cur_page_num >= extent_size)
&& (cur_page_num < extent_size * 3)) {
/* page is doublewrite buffer. */
return (true);
}
Expand Down Expand Up @@ -757,8 +760,8 @@ static inline bool is_page_free(const byte *xdes, ulint physical_page_size,
{
const byte *des=
xdes + XDES_ARR_OFFSET +
XDES_SIZE * ((page_no & (physical_page_size - 1)) / FSP_EXTENT_SIZE);
return xdes_is_free(des, page_no % FSP_EXTENT_SIZE);
XDES_SIZE * ((page_no & (physical_page_size - 1)) / extent_size);
return xdes_is_free(des, page_no % extent_size);
}

/*
Expand Down Expand Up @@ -786,6 +789,16 @@ parse_page(

/* Check whether page is doublewrite buffer. */
str = skip_page ? "Double_write_buffer" : "-";
page_no = mach_read_from_4(page + FIL_PAGE_OFFSET);
if (skip_freed_pages) {
const byte *des= xdes + XDES_ARR_OFFSET +
XDES_SIZE * ((page_no & (physical_page_size - 1))
/ extent_size);
if (mach_read_from_4(des) != XDES_FSEG &&
xdes_is_free(des, page_no % extent_size)) {
return;
}
}

switch (fil_page_get_type(page)) {

Expand Down Expand Up @@ -1211,6 +1224,9 @@ static struct my_option innochecksum_options[] = {
&do_leaf, &do_leaf, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"merge", 'm', "leaf page count if merge given number of consecutive pages",
&n_merge, &n_merge, 0, GET_ULONG, REQUIRED_ARG, 0, 0, (longlong)10L, 0, 1, 0},
{"skip-freed-pages", 'r', "skip freed pages for the tablespace",
&skip_freed_pages, &skip_freed_pages, 0, GET_BOOL, NO_ARG,
0, 0, 0, 0, 0, 0},

{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
Expand All @@ -1234,7 +1250,7 @@ static void usage(void)
print_version();
puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
printf("InnoDB offline file checksum utility.\n");
printf("Usage: %s [-c] [-s <start page>] [-e <end page>] "
printf("Usage: %s [-c] [-r] [-s <start page>] [-e <end page>] "
"[-p <page>] [-i] [-v] [-a <allow mismatches>] [-n] "
"[-S] [-D <page type dump>] "
"[-l <log>] [-l] [-m <merge pages>] <filename or [-]>\n", my_progname);
Expand All @@ -1247,8 +1263,8 @@ static void usage(void)
extern "C" my_bool
innochecksum_get_one_option(
const struct my_option *opt,
const char *argument MY_ATTRIBUTE((unused)),
const char *)
const char *IF_DBUG(argument,),
const char *)
{
switch (opt->id) {
#ifndef DBUG_OFF
Expand All @@ -1273,15 +1289,6 @@ innochecksum_get_one_option(
my_end(0);
exit(EXIT_SUCCESS);
break;
case 'n':
no_check = true;
break;
case 'a':
case 'S':
break;
case 'w':
do_write = true;
break;
case 'D':
page_type_dump = true;
break;
Expand Down Expand Up @@ -1329,7 +1336,7 @@ get_options(
static bool check_encryption(const char* filename, const byte* page)
{
ulint offset = FSP_HEADER_OFFSET + XDES_ARR_OFFSET + XDES_SIZE *
physical_page_size / FSP_EXTENT_SIZE;
physical_page_size / extent_size;

if (memcmp(page + offset, CRYPT_MAGIC, MAGIC_SZ) != 0) {
return false;
Expand Down Expand Up @@ -1861,7 +1868,7 @@ int main(
printf("page " UINT32PF " ", cur_page_num);
}

if (page_get_page_no(buf) % physical_page_size == 0) {
if (cur_page_num % physical_page_size == 0) {
memcpy(xdes, buf, physical_page_size);
}

Expand Down
10 changes: 10 additions & 0 deletions mysql-test/suite/innodb/r/innochecksum_undo_page.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
SET GLOBAL INNODB_FILE_PER_TABLE= 0;
CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB;
INSERT INTO t1 VALUES(1);
DROP TABLE t1;
SET GLOBAL innodb_fast_shutdown=0;
# Run the innochecksum to display undo log pages
FOUND 1 /Undo page state: 0 active, [1-9]+ cached, [0-9]+ to_purge, [0-9]+ prepared, [0-9]+ other/ in result.log
# Run the innochecksum with --skip-freed-pages
FOUND 1 /Undo page state: 0 active, 0 cached, 0 to_purge, 0 prepared, 0 other/ in result.log
# restart
1 change: 1 addition & 0 deletions mysql-test/suite/innodb/t/innochecksum_undo_page.opt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--innodb_undo_tablespaces=0
24 changes: 24 additions & 0 deletions mysql-test/suite/innodb/t/innochecksum_undo_page.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--source include/have_innodb.inc
--source include/not_embedded.inc
let MYSQLD_DATADIR= `SELECT @@datadir`;

SET GLOBAL INNODB_FILE_PER_TABLE= 0;
CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB;
INSERT INTO t1 VALUES(1);
DROP TABLE t1;
SET GLOBAL innodb_fast_shutdown=0;
--source include/shutdown_mysqld.inc

--echo # Run the innochecksum to display undo log pages
let $resultlog=$MYSQLTEST_VARDIR/tmp/result.log;
let SEARCH_FILE = $MYSQLTEST_VARDIR/tmp/result.log;
exec $INNOCHECKSUM -S $MYSQLD_DATADIR/ibdata1 > $resultlog;
let SEARCH_PATTERN= Undo page state: 0 active, [1-9]+ cached, [0-9]+ to_purge, [0-9]+ prepared, [0-9]+ other;
--source include/search_pattern_in_file.inc

--echo # Run the innochecksum with --skip-freed-pages
exec $INNOCHECKSUM -S -r $MYSQLD_DATADIR/ibdata1 > $resultlog;
let SEARCH_PATTERN= Undo page state: 0 active, 0 cached, 0 to_purge, 0 prepared, 0 other;
--source include/search_pattern_in_file.inc
--remove_file $resultlog
--source include/start_mysqld.inc
6 changes: 5 additions & 1 deletion mysql-test/suite/innodb_zip/r/innochecksum_2.result
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,15 @@ per-page-details FALSE
log (No default value)
leaf FALSE
merge 0
skip-freed-pages FALSE
[1]:# check the both short and long options for "help"
[2]:# Run the innochecksum when file isn't provided.
# It will print the innochecksum usage similar to --help option.
innochecksum Ver #.#.#
Copyright (c) YEAR, YEAR , Oracle, MariaDB Corporation Ab and others.

InnoDB offline file checksum utility.
Usage: innochecksum [-c] [-s <start page>] [-e <end page>] [-p <page>] [-i] [-v] [-a <allow mismatches>] [-n] [-S] [-D <page type dump>] [-l <log>] [-l] [-m <merge pages>] <filename or [-]>
Usage: innochecksum [-c] [-r] [-s <start page>] [-e <end page>] [-p <page>] [-i] [-v] [-a <allow mismatches>] [-n] [-S] [-D <page type dump>] [-l <log>] [-l] [-m <merge pages>] <filename or [-]>
See https://mariadb.com/kb/en/library/innochecksum/ for usage hints.
-?, --help Displays this help and exits.
-I, --info Synonym for --help.
Expand All @@ -66,6 +67,8 @@ See https://mariadb.com/kb/en/library/innochecksum/ for usage hints.
-f, --leaf Examine leaf index pages
-m, --merge=# leaf page count if merge given number of consecutive
pages
-r, --skip-freed-pages
skip freed pages for the tablespace

Variables (--variable-name=value)
and boolean options {FALSE|TRUE} Value (after reading options)
Expand All @@ -84,6 +87,7 @@ per-page-details FALSE
log (No default value)
leaf FALSE
merge 0
skip-freed-pages FALSE
[3]:# check the both short and long options for "count" and exit
Number of pages:#
Number of pages:#
Expand Down
1 change: 1 addition & 0 deletions mysql-test/suite/innodb_zip/r/innochecksum_3.result
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ per-page-details FALSE
log (No default value)
leaf FALSE
merge 0
skip-freed-pages FALSE
[5]: Page type dump for with shortform for tab1.ibd


Expand Down

0 comments on commit 0be600a

Please sign in to comment.