/* ---------------------------------------------------------------------------------------
 * *
 * Copyright (c) 2020 Huawei Technologies Co.,Ltd.
 *
 * openGauss is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 * http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * ---------------------------------------------------------------------------------------
 *
 * pageparse.cpp
 *
 * IDENTIFICATION
 * src/gausskernel/storage/page/pageparse.cpp
 *
 * ---------------------------------------------------------------------------------------
 */

#include <stdio.h>
#include "access/htup.h"
#include "access/itup.h"
#include "access/nbtree.h"
#include "access/xlogdefs.h"
#include "commands/tablespace.h"
#include "catalog/catalog.h"
#include "knl/knl_variable.h"
#include "miscadmin.h"
#include "nodes/pg_list.h"
#include "pgstat.h"
#include "postgres.h"
#include "postgres_ext.h"
#include "storage/buf/bufmgr.h"
#include "storage/buf/bufpage.h"
#include "storage/buf/buf_internals.h"
#include "storage/checksum.h"
#include "storage/smgr/relfilenode.h"
#include "storage/smgr/segment.h"
#include "storage/smgr/smgr.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/palloc.h"
#include "utils/relmapper.h"
#include "pageparse.h"
#include "storage/file/fio_device_com.h"

#define INVALID_FD (-1)

typedef enum {
    BTREE_INDEX = 0,
    UBTREE_INDEX,
    INDEX_BOTT
} INDEX_TYPE;

typedef struct UndoHeader {
    UndoRecordHeader whdr;
    UndoRecordBlock wblk;
    UndoRecordTransaction wtxn;
    UndoRecordPayload wpay;
    UndoRecordOldTd wtd;
    UndoRecordPartition wpart;
    UndoRecordTablespace wtspc;
    StringInfoData rawdata;
} UndoHeader;

void CheckUser(const char *fName)
{
    if (!superuser() && !isOperatoradmin(GetUserId()))
        ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
            (errmsg("Must be system admin or operator admin in operation mode can use %s.", fName))));
}

static void ParseUHeapPageHeader(void *page, BlockNumber blkno, BlockNumber endBlk, char *output);

static void ParseUHeapPageTDInfo(void *page, char *output);

static void ParseUHeapPageItem(Item item, UHeapTuple tuple, char *output);

static void ParseUHeapPageItemUndo(void *page, ItemPointer ctid, char *output);

static void ParseUHeapPageData(void *page, BlockNumber blkno, char *output, bool dumpUndo = false);

static void ParseUHeapPageSpecialInfo(void *page, char *output);

static void ParseUHeapPage(void *page, BlockNumber blkno, BlockNumber endBlk, char *output, bool dumpUndo = false);

static void ParseIndexPageHeader(void *page, int type, BlockNumber blkno, BlockNumber endBlk, char *output);

static void ParseIndexPageItem(Item item, int type, uint32 len, char *output);

static void ParseIndexPageSpecialInfo(void *page, int type, char *output);

static void ParseIndexPageData(void *page, int type, char *output);

static void ParseIndexPage(void *page, int type, BlockNumber blkno, BlockNumber endBlk, char *output);

static void formatBitmap(const unsigned char *start, int len, char bit1, char bit0, char *strOutput)
{
    errno_t rc = EOK;
    for (int i = 0; i < len; ++i) {
        unsigned char ch = start[i];
        unsigned char bitmask = 1;
        /* print 8 bits within a loop */
        do {
            rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "%c",
                ((ch & bitmask) ? bit1 : bit0));
            securec_check_ss(rc, "\0", "\0");
            bitmask <<= 1;
        } while (bitmask != 0);
        rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, " ");
        securec_check_ss(rc, "\0", "\0");
    }
}

/* init RelFileNode and outputFilename */
void PrepForRead(char *path, int64 blocknum, char *relation_type, char *outputFilename, RelFileNode *relnode,
    bool parse_page)
{
    size_t len = strlen(path);
    if (len > strlen(COMPRESS_STR) &&
        strcmp(path + len - strlen(COMPRESS_STR), COMPRESS_STR) == 0) {
        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
            (errmsg("compressed table file is not allowed here."))));
    }
    char *pathFirstpart = (char *)palloc(MAXFNAMELEN * sizeof(char));
    errno_t rc = memset_s(pathFirstpart, MAXFNAMELEN, 0, MAXFNAMELEN);
    securec_check(rc, "\0", "\0");

    relnode->opt = 0;
    if ((strcmp(relation_type, "segment") == 0)) {
        relnode->bucketNode = SegmentBktId;
        char *bucketNodestr = strstr(path, "_b");
        if (NULL != bucketNodestr) {
            bucketNodestr += TWO; /* delete first two chars: _b */
            relnode->bucketNode = (int2)pg_strtouint64(bucketNodestr, NULL, TENBASE);
            rc = strncpy_s(pathFirstpart, strlen(path) - strlen(bucketNodestr) - 1, path,
                strlen(path) - strlen(bucketNodestr) - TWO);
            securec_check(rc, "\0", "\0");
        }
    } else {
        relnode->bucketNode = InvalidBktId;
    }
    RelFileNodeForkNum relfilenode;
    if (strlen(pathFirstpart) == 0) {
        relfilenode = relpath_to_filenode(path, true);
    } else {
        relfilenode = relpath_to_filenode(pathFirstpart, true);
    }
    if (relfilenode.rnode.node.spcNode == 0)
        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
            (errmsg("The tablespace oid is 0. Please check the first parameter path. "
                "If you are not sure about the table path, please check pg_relation_filepath."))));
    RelFileNodeRelCopy(*relnode, relfilenode.rnode.node);
    relnode->opt = 0;
    char *pagesuffix = "page";
    char *xlogsuffix = "xlog";
    rc = snprintf_s(outputFilename + (int)strlen(outputFilename), MAXFILENAME, MAXFILENAME - 1, "%s/%u_%u_%u_%d.%s",
        t_thrd.proc_cxt.DataDir, relnode->spcNode, relnode->dbNode, relnode->relNode, blocknum,
        (parse_page ? pagesuffix : xlogsuffix));
    securec_check_ss(rc, "\0", "\0");
    pfree_ext(pathFirstpart);
}

static void ParseHeapHeader(const PageHeader page, char *strOutput, BlockNumber blockNum, BlockNumber block_endpoint)
{
    errno_t rc = EOK;
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
        "Page information of block %u/%u", blockNum, block_endpoint);
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\tpd_lsn: %X/%X",
        (uint32)(PageGetLSN(page) >> XIDTHIRTYTWO), (uint32)PageGetLSN(page));
    securec_check_ss(rc, "\0", "\0");
    bool checksum_matched = false;
    if (CheckPageZeroCases(page)) {
        uint16 checksum = pg_checksum_page((char *)page, (BlockNumber)blockNum);
        checksum_matched = (checksum == page->pd_checksum);
    }

    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
        "\n\tpd_checksum: 0x%X, verify %s", page->pd_checksum, checksum_matched ? "success" : "fail");
    securec_check_ss(rc, "\0", "\0");

    rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\tpd_flags: ");
    securec_check(rc, "\0", "\0");
    if (PageHasFreeLinePointers(page)) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_HAS_FREE_LINES ");
        securec_check(rc, "\0", "\0");
    }
    if (PageIsFull(page)) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_PAGE_FULL ");
        securec_check(rc, "\0", "\0");
    }
    if (PageIsAllVisible(page)) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_ALL_VISIBLE ");
        securec_check(rc, "\0", "\0");
    }
    if (PageIsCompressed(page)) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_COMPRESSED_PAGE ");
        securec_check(rc, "\0", "\0");
    }
    if (PageIsLogical(page)) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_LOGICAL_PAGE ");
        securec_check(rc, "\0", "\0");
    }
    if (PageIsEncrypt(page)) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_ENCRYPT_PAGE ");
        securec_check(rc, "\0", "\0");
    }
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\tpd_lower: %u, %s",
        page->pd_lower, PageIsEmpty(page) ? "empty" : "non-empty");
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\tpd_upper: %u, %s",
        page->pd_upper, PageIsNew(page) ? "new" : "old");
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\tpd_special: %u, size %u",
        page->pd_special, PageGetSpecialSize(page));
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
        "\n\tPage size & version: %u, %u", (uint16)PageGetPageSize(page), (uint16)PageGetPageLayoutVersion(page));
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
        "\n\tpd_xid_base: %lu, pd_multi_base: %lu", ((HeapPageHeader)(page))->pd_xid_base,
        ((HeapPageHeader)(page))->pd_multi_base);
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\tpd_prune_xid: %lu",
        ((HeapPageHeader)(page))->pd_prune_xid + ((HeapPageHeader)(page))->pd_xid_base);
    securec_check_ss(rc, "\0", "\0");
}

static void PrintInfomask(HeapTupleHeader tup, char *strOutput)
{
    errno_t rc = EOK;
    if (tup->t_infomask & HEAP_HASNULL) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HASNULL ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_HASVARWIDTH) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HASVARWIDTH ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_HASEXTERNAL) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HASEXTERNAL ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_HASOID) {
        rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "HEAP_HASOID(%d) ",
            HeapTupleHeaderGetOid(tup));
        securec_check_ss(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_COMPRESSED) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_COMPRESSED ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_COMBOCID) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_COMBOCID ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_XMAX_EXCL_LOCK) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMAX_EXCL_LOCK ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_XMAX_SHARED_LOCK) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMAX_SHARED_LOCK ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_XMIN_COMMITTED) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMIN_COMMITTED ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_XMIN_INVALID) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMIN_INVALID ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_XMAX_COMMITTED) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMAX_COMMITTED ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_XMAX_INVALID) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMAX_INVALID ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_XMAX_IS_MULTI) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMAX_IS_MULTI ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask & HEAP_UPDATED) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_UPDATED ");
        securec_check(rc, "\0", "\0");
    }
    if ((tup->t_infomask & HEAP_HAS_8BYTE_UID)) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HAS_8BYTE_UID ");
        securec_check(rc, "\0", "\0");
    } else {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HAS_NO_UID ");
        securec_check(rc, "\0", "\0");
    }
    rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\t\t\tt_infomask2: ");
    securec_check(rc, "\0", "\0");
    if (tup->t_infomask2 & HEAP_HOT_UPDATED) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HOT_UPDATED ");
        securec_check(rc, "\0", "\0");
    }
    if (tup->t_infomask2 & HEAP_ONLY_TUPLE) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_ONLY_TUPLE ");
        securec_check(rc, "\0", "\0");
    }
}

static void ParseTupleHeader(const PageHeader page, uint lineno, char *strOutput)
{
    errno_t rc = EOK;
    ItemId lp = PageGetItemId(page, lineno);
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
        "\n\n\t\tTuple #%u is normal: length %u, offset %u", lineno, ItemIdGetLength(lp), ItemIdGetOffset(lp));
    securec_check_ss(rc, "\0", "\0");

    HeapTupleData dummyTuple;
    HeapTupleHeader tup = (HeapTupleHeader)(PageGetItem(page, lp));
    dummyTuple.t_data = tup;
    dummyTuple.t_xid_base = ((HeapPageHeader)(page))->pd_xid_base;
    dummyTuple.t_multi_base = ((HeapPageHeader)(page))->pd_multi_base;
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
        "\n\t\t\tt_xmin/t_xmax/t_cid: %lu/%lu/%u", HeapTupleGetRawXmin(&dummyTuple), HeapTupleGetRawXmax(&dummyTuple),
        HeapTupleHeaderGetRawCommandId(tup));
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
        "\n\t\t\tctid:(block %u/%u, offset %u)", tup->t_ctid.ip_blkid.bi_hi, tup->t_ctid.ip_blkid.bi_lo,
        tup->t_ctid.ip_posid);
    securec_check_ss(rc, "\0", "\0");

    rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\t\t\tt_infomask: ");
    securec_check(rc, "\0", "\0");

    PrintInfomask(tup, strOutput);

    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "Attrs Num: %d",
        HeapTupleHeaderGetNatts(tup, NULL));
    securec_check_ss(rc, "\0", "\0");

    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\t\t\tt_hoff: %u",
        tup->t_hoff);
    securec_check_ss(rc, "\0", "\0");
    rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\t\t\tt_bits: ");
    securec_check(rc, "\0", "\0");

    formatBitmap((const unsigned char *)tup->t_bits, BITMAPLEN(HeapTupleHeaderGetNatts(tup, NULL)), 'V', 'N',
        strOutput);
}

static void ParseHeapPage(const PageHeader page, BlockNumber blockNum, char *strOutput, BlockNumber block_endpoint)
{
    errno_t rc = EOK;
    if (PageIsNew(page)) {
        rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
                        "Page information of block %u/%u : new page\n", blockNum, block_endpoint);
        securec_check_ss(rc, "\0", "\0");
        ParseHeapHeader(page, strOutput, blockNum, block_endpoint);
        rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n");
        securec_check_ss(rc, "\0", "\0");
        return;
    }
    if (page->pd_lower < GetPageHeaderSize(page) || page->pd_lower > page->pd_upper ||
        page->pd_upper > page->pd_special || page->pd_special > BLCKSZ ||
        page->pd_special != MAXALIGN(page->pd_special)) {
        rc = snprintf_s(strOutput + (int)strlen(strOutput),
            MAXOUTPUTLEN,
            MAXOUTPUTLEN - 1,
            "The page data is corrupted, corrupted page pointers: lower = %u, upper = %u, special = %u\n",
            page->pd_lower,
            page->pd_upper,
            page->pd_special);
        securec_check_ss(rc, "\0", "\0");
        return;
    }

    ParseHeapHeader(page, strOutput, blockNum, block_endpoint);
    /* parse tuple header */
    rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\n\tHeap tuple information on this page");
    securec_check(rc, "\0", "\0");

    uint nline = PageGetMaxOffsetNumber((Page)page);
    uint nunused = 0, nnormal = 0, ndead = 0;
    for (uint i = (OffsetNumber)1; i <= nline; i++) {
        ItemId lp = PageGetItemId(page, i);
        if (ItemIdIsNormal(lp)) {
            nnormal++;
            ParseTupleHeader(page, i, strOutput);
        } else if (ItemIdIsDead(lp)) {
            ndead++;
            rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
                "\n\n\t\tTuple #%u is dead: length %u, offset %u", i, ItemIdGetLength(lp), ItemIdGetOffset(lp));
            securec_check_ss(rc, "\0", "\0");
        } else {
            nunused++;
            rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
                "\n\n\t\tTuple #%u is nunused: ", i);
            securec_check_ss(rc, "\0", "\0");
        }
    }
    rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
        "\n\tSummary (%u total):  %u normal, %u unused, %u dead\n\nNormal Heap Page, special space is %u\n\n", nline,
        nnormal, nunused, ndead, (uint)PageGetSpecialSize(page));
    securec_check_ss(rc, "\0", "\0");
}

static void ParseOnePage(const PageHeader page, BlockNumber blockNum, char *strOutput, char *relation_type,
    BlockNumber block_endpoint, bool dumpUndo)
{
    errno_t rc = EOK;
    if (strcmp(relation_type, "heap") == 0) {
        if ((PG_HEAP_PAGE_LAYOUT_VERSION != (uint16)PageGetPageLayoutVersion(page) &&
                   (uint16)PageGetPageLayoutVersion(page) != 0) || PageGetSpecialSize(page) != 0) {
            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                (errmsg("The target page is not heap, the given page version is: %u",
                (uint16)PageGetPageLayoutVersion(page)))));
        }
        ParseHeapPage(page, blockNum, strOutput, block_endpoint);
    } else if (strcmp(relation_type, "uheap") == 0) {
        if (PG_UHEAP_PAGE_LAYOUT_VERSION != (uint16)PageGetPageLayoutVersion(page) || PageGetSpecialSize(page) != 0) {
            ereport(LOG,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                    errmodule(MOD_USTORE),
                    errmsg("The target page(%u) is not uheap, the given page version is: %u",
                    blockNum, (uint16)PageGetPageLayoutVersion(page)),
                    errdetail("N/A"),
                    errcause("Page version is not uheap, or page is new."),
                    erraction("Check input parameters or perform checkpoint")));
            rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN,
            "Page (%u) is not uheap ,or page is new.\n", blockNum);
            securec_check_ss(rc, "\0", "\0");
            return;
        }
        ParseUHeapPage((void *)page, blockNum, block_endpoint, strOutput, dumpUndo);
    } else if (strcmp(relation_type, "btree") == 0) {
        if ((PG_COMM_PAGE_LAYOUT_VERSION != (uint16)PageGetPageLayoutVersion(page) &&
            (uint16)PageGetPageLayoutVersion(page) != 0) || PageGetSpecialSize(page) == 0) {
            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                (errmsg("The target page is not btree, the given page version is: %u",
                (uint16)PageGetPageLayoutVersion(page)))));
        }
        ParseIndexPage((void *)page, BTREE_INDEX, blockNum, block_endpoint, strOutput);
    } else if (strcmp(relation_type, "ubtree") == 0) {
        if ((PG_COMM_PAGE_LAYOUT_VERSION != (uint16)PageGetPageLayoutVersion(page) &&
            (uint16)PageGetPageLayoutVersion(page) != 0) || PageGetSpecialSize(page) == 0) {
            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                (errmsg("The target page is not ubtree, the given page version is: %u",
                (uint16)PageGetPageLayoutVersion(page)))));
        }
        ParseIndexPage((void *)page, UBTREE_INDEX, blockNum, block_endpoint, strOutput);
    } else if (strcmp(relation_type, "segment") == 0) {
        rc = strcat_s(strOutput, MAXOUTPUTLEN, "Parse segment table.");
        securec_check(rc, "\0", "\0");
        ParseHeapPage(page, blockNum, strOutput, block_endpoint);
    } else {
        ereport(ERROR,
            (errcode(ERRCODE_UNDEFINED_PARAMETER), (errmsg("Only support heap, uheap, btree, ubtree, segment."))));
    }
}

static void check_rdStatus(SMGR_READ_STATUS rdStatus, BlockNumber blockNum, const PageHeader page, char *strOutput1)
{
    errno_t rc = EOK;
    if (rdStatus == SMGR_RD_CRC_ERROR) {
        uint16 checksum = pg_checksum_page((char *)page, blockNum);
        rc = snprintf_s(strOutput1, SHORTOUTPUTLEN, SHORTOUTPUTLEN - 1,
                        "\nFor page %u, page verification failed, calculated checksum 0x%X but expected 0x%X.\n",
                        blockNum, checksum, page->pd_checksum);
        securec_check_ss(rc, "\0", "\0");
    } else if (rdStatus == SMGR_RD_NO_BLOCK) {
        rc = snprintf_s(strOutput1, SHORTOUTPUTLEN, SHORTOUTPUTLEN - 1, "\tThe page %u does not exist.\n", blockNum);
        securec_check_ss(rc, "\0", "\0");
    }
}

static bool readFromMemory(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, char *relation_type,
    char *strOutput, FILE *outputfile, char *outputFilename, bool dumpUndo)
{
    errno_t rc = EOK;
    SegPageLocation loc;
    loc.blocknum = 0;
    /* for segment, update to physical blocknum */
    if (strcmp(relation_type, "segment") == 0) {
        loc = seg_get_physical_location(smgr->smgr_rnode.node, forkNum, blockNum);
    }
    BufferTag new_tag;
    INIT_BUFFERTAG(new_tag, smgr->smgr_rnode.node, forkNum, blockNum);
    uint32 new_hash = BufTableHashCode(&new_tag);
    LWLock *partition_lock = BufMappingPartitionLock(new_hash);
    ResourceOwnerEnlargeBuffers(t_thrd.utils_cxt.CurrentResourceOwner);
    /* LW lock to avoid concurrent read/write */
    (void)LWLockAcquire(partition_lock, LW_SHARED);
    int buf_id = BufTableLookup(&new_tag, new_hash);

    do {
        if (buf_id >= 0) { /* read from memory */
            BufferDesc *bufDesc = GetBufferDescriptor(buf_id);
            bool valid = (strcmp(relation_type, "segment") == 0) ? SegPinBuffer(bufDesc) : PinBuffer(bufDesc, NULL);
            if (!valid)
                break; /* pin failed, read disk */
            LWLockRelease(partition_lock);
            (void)LWLockAcquire(BufferDescriptorGetContentLock(bufDesc), LW_SHARED); /* acquire content_lock */
            Buffer buf = BufferDescriptorGetBuffer(bufDesc);
            const PageHeader page = (const PageHeader)BufferGetPage(buf);
            rc = strcat_s(strOutput, MAXOUTPUTLEN, "The target page is from memory. ");
            securec_check(rc, "\0", "\0");
            if (strcmp(relation_type, "segment") == 0) {
                ParseOnePage(page, loc.blocknum, strOutput, relation_type, loc.blocknum, false); /* physical blocknum */
                LWLockRelease(BufferDescriptorGetContentLock(bufDesc));                   /* release content_lock */
                SegUnpinBuffer(bufDesc);
            } else {
                ParseOnePage(page, blockNum, strOutput, relation_type, blockNum, dumpUndo);
                LWLockRelease(BufferDescriptorGetContentLock(bufDesc)); /* release content_lock */
                UnpinBuffer(bufDesc, true);
            }
            CheckWriteFile(outputfile, outputFilename, strOutput);
            pfree_ext(strOutput);
            return true;
        }
    } while (0);
    LWLockRelease(partition_lock);
    return false;
}

static void CheckSegment(RelFileNode *relnode, ForkNumber forkNum, FILE *outputfile, char *outputFilename)
{
    SegSpace *spc = spc_open(relnode->spcNode, relnode->dbNode, false);
    if (spc == NULL || !spc_datafile_exist(spc, 1, forkNum)) {
        CheckCloseFile(outputfile, outputFilename, false);
        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("Page doesn't exist."))));
    }
    BlockNumber size = spc_size(spc, 1, forkNum);
    if (relnode->relNode >= size) {
        CheckCloseFile(outputfile, outputFilename, false);
        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
            (errmsg("The target page %u doesn't exist, the current max is %u.", relnode->relNode, size - 1))));
    }
    RelFileNode *relnodeHead = (RelFileNode *) palloc(sizeof(RelFileNode));
    relnodeHead->spcNode = relnode->spcNode;
    relnodeHead->dbNode = relnode->dbNode;
    relnodeHead->relNode = 1;
    relnodeHead->opt = relnode->opt;
    relnodeHead->bucketNode = relnode->bucketNode;
    relnodeHead->opt = relnode->opt;
    Buffer buffer_temp = ReadBufferFast(spc, *relnodeHead, forkNum, relnode->relNode, RBM_NORMAL);
    if (!BufferIsValid(buffer_temp)) {
        CheckCloseFile(outputfile, outputFilename, false);
        ereport(ERROR, (errcode_for_file_access(), errmsg("Segment Head is invalid %u/%u/%u %d %u",
        relnodeHead->spcNode, relnodeHead->dbNode, relnodeHead->relNode, forkNum, relnode->relNode)));
    }
    pfree_ext(relnodeHead);
    SegmentHead *head = (SegmentHead *)PageGetContents(BufferGetPage(buffer_temp));
    SegReleaseBuffer(buffer_temp);
    if (!(IsNormalSegmentHead(head) && relnode->bucketNode == SegmentBktId) &&
        !(IsBucketMainHead(head) && IsBucketFileNode(*relnode))) {
        CheckCloseFile(outputfile, outputFilename, false);
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                (errmsg("Page header does not match with corresponding segment type."))));
    }
}

void ValidateParameterPath(RelFileNode rnode, char *str)
{
    char *path = relpathbackend(rnode, InvalidBackendId, MAIN_FORKNUM);
    if ((strcmp(path, str) != 0))
        ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("\"%s\" is invalid input", str)));
}

char *ParsePage(char *path, int64 blocknum, char *relation_type, bool read_memory, bool dumpUndo)
{
    errno_t rc = EOK;

    /* check parameters */
    if (blocknum > MaxBlockNumber || blocknum < -1)
        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
            (errmsg("Blocknum should be between -1 and %u.", MaxBlockNumber))));
    /* initialize */
    char *strOutput = (char *)palloc(MAXOUTPUTLEN * sizeof(char));
    rc = memset_s(strOutput, MAXOUTPUTLEN, 0, MAXOUTPUTLEN);
    securec_check(rc, "\0", "\0");

    RelFileNode *relnode = (RelFileNode *)palloc(sizeof(RelFileNode));
    char *outputFilename = (char *)palloc(MAXFILENAME * sizeof(char));

    rc = memset_s(outputFilename, MAXFILENAME, 0, MAXFILENAME);
    securec_check(rc, "\0", "\0");

    PrepForRead(path, blocknum, relation_type, outputFilename, relnode, true);
    ValidateParameterPath(*relnode, path);

    FILE *outputfile = fopen(outputFilename, "w");
    CheckOpenFile(outputfile, outputFilename);

    ForkNumber forkNum = MAIN_FORKNUM;
    SMgrRelation smgr = smgropen(*relnode, InvalidBackendId, GetColumnNum(forkNum));
    if (strcmp(relation_type, "segment") == 0)
        CheckSegment(relnode, forkNum, outputfile, outputFilename);
    BlockNumber maxBlockNum = smgrnblocks(smgr, forkNum);
    if ((blocknum != -1 && blocknum > maxBlockNum) || (maxBlockNum == 0)) {
        CheckCloseFile(outputfile, outputFilename, false);
        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg(
            "The max blocknum of current file is %u. Target blocknum exceeds current file size.", maxBlockNum))));
    }

    pfree_ext(relnode);
    BlockNumber blockNum = 0;
    BlockNumber block_endpoint = 0;
    /* only parse one block, check if it is in memory */
    if (read_memory) {
        blockNum = (BlockNumber)blocknum;
        if (readFromMemory(smgr, forkNum, blockNum, relation_type, strOutput, outputfile, outputFilename, dumpUndo)) {
            /* found in memory */
            CheckCloseFile(outputfile, outputFilename, true);
            smgrclose(smgr);
            return outputFilename;
        }
    }
    /* read from disk */
    rc = strcat_s(strOutput, MAXOUTPUTLEN, "The target page is from disk. ");
    securec_check(rc, "\0", "\0");
    CheckWriteFile(outputfile, outputFilename, strOutput);
    pfree_ext(strOutput);
    if (blocknum >= 0) { /* only parse one block */
        blockNum = blocknum;
        block_endpoint = blocknum;
    } else { /* blocknum == -1, parse all blocks */
        block_endpoint = maxBlockNum - 1;
    }
    /* if not declare a single block, then loop all blocks */
    while (blockNum <= block_endpoint) {
        CHECK_FOR_INTERRUPTS(); /* Allow cancel/die interrupts */
        char *strOutput = (char *)palloc(MAXOUTPUTLEN * sizeof(char));
        rc = memset_s(strOutput, MAXOUTPUTLEN, 0, MAXOUTPUTLEN);
        securec_check(rc, "\0", "\0");
        char *buffer = (char *)palloc0(BLCKSZ);
        SMGR_READ_STATUS rdStatus = smgrread(smgr, forkNum, blockNum, buffer);
        const PageHeader page = (const PageHeader)buffer;
        char *strOutput1 = (char *)palloc(MAXOUTPUTLEN * sizeof(char));
        rc = memset_s(strOutput1, SHORTOUTPUTLEN, 0, SHORTOUTPUTLEN);
        securec_check(rc, "\0", "\0");
        check_rdStatus(rdStatus, blockNum, page, strOutput1);
        CheckWriteFile(outputfile, outputFilename, strOutput1);
        pfree_ext(strOutput1);
        if (strcmp(relation_type, "segment") == 0) {
            SegPageLocation loc = seg_get_physical_location(smgr->smgr_rnode.node, forkNum, blockNum);
            SegPageLocation endloc = seg_get_physical_location(smgr->smgr_rnode.node, forkNum, block_endpoint);
            ParseOnePage(page, loc.blocknum, strOutput, relation_type, endloc.blocknum, false); /* physical blocknum */
        } else
            ParseOnePage(page, blockNum, strOutput, relation_type, block_endpoint, dumpUndo);

        CheckWriteFile(outputfile, outputFilename, strOutput);
        pfree_ext(strOutput);
        pfree_ext(buffer);
        blockNum++;
    }
    CheckCloseFile(outputfile, outputFilename, true);
    smgrclose(smgr);
    return outputFilename;
}

static void ParseUHeapPageHeader(void *page, BlockNumber blkno, BlockNumber endBlk, char *output)
{
    errno_t rc = EOK;
    bool chksumResult = false;
    UHeapPageHeader pageHeader = NULL;

    if (page == NULL || output == NULL) {
        return;
    }

    pageHeader = (UHeapPageHeader)page;
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "Page information of block %u/%u\n",
        blkno, endBlk);
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_lsn: %X/%X\n",
        (uint32)(PageGetLSN(pageHeader) >> XIDTHIRTYTWO), (uint32)PageGetLSN(pageHeader));
    securec_check_ss(rc, "\0", "\0");

    if (CheckPageZeroCases((PageHeader)pageHeader)) {
        uint16 checksum = pg_checksum_page((char *)pageHeader, (BlockNumber)blkno);
        chksumResult = (checksum == pageHeader->pd_checksum);
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_checksum: 0x%X, verify %s\n",
        pageHeader->pd_checksum, chksumResult ? "success" : "fail");
    securec_check_ss(rc, "\0", "\0");

    if (UPageHasFreeLinePointers(pageHeader)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PAGE_HAS_FREE_LINES.");
        securec_check_ss(rc, "\0", "\0");
    }

    if (UPageIsFull(pageHeader)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PAGE_FULL.");
        securec_check_ss(rc, "\0", "\0");
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
        "\tpd_lower: %u, %s\n\tpd_upper: %u, %s\n\tpd_special: %u, size %u\n", pageHeader->pd_lower,
        PageIsEmpty(pageHeader) ? "empty" : "non-empty", pageHeader->pd_upper,
        PageIsNew(pageHeader) ? "new page" : "old page", pageHeader->pd_special, PageGetSpecialSize(pageHeader));
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tPage size & version: %u, %u\n",
        (uint16)PageGetPageSize(pageHeader), (uint16)PageGetPageLayoutVersion(pageHeader));
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpotential_freespace: %u\n",
        pageHeader->potential_freespace);
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\ttd_count: %u\n", pageHeader->td_count);
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_prune_xid: %lu\n",
        pageHeader->pd_prune_xid);
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
        "\tpd_xid_base: %lu, pd_multi_base: %lu\n", pageHeader->pd_xid_base, pageHeader->pd_multi_base);
    securec_check_ss(rc, "\0", "\0");
    return;
}

static void ParseUHeapPageTDInfo(void *page, char *output)
{
    TD *tdInfo = NULL;
    uint16 tdCount = 0;
    UHeapPageTDData *tdPtr = NULL;
    UHeapPageHeader pageHeader = NULL;
    errno_t rc = EOK;

    if (page == NULL || output == NULL) {
        return;
    }

    pageHeader = (UHeapPageHeader)page;
    tdPtr = (UHeapPageTDData *)PageGetTDPointer(page);
    tdCount = pageHeader->td_count;
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
        "\n\n\tUHeap Page TD information, nTDSlots = %u\n", tdCount);
    securec_check_ss(rc, "\0", "\0");

    for (uint16 i = 0; i < tdCount; i++) {
        tdInfo = &(tdPtr->td_info[i]);
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
            "\n\t\t TD Slot #%d, xid:%lu, urp:%lu\n", i + 1, tdInfo->xactid, tdInfo->undo_record_ptr);
        securec_check_ss(rc, "\0", "\0");
    }
    return;
}

static void ParseUHeapPageItem(Item item, UHeapTuple tuple, char *output)
{
    if (item == NULL || tuple == NULL || output == NULL) {
        return;
    }

    UHeapDiskTuple diskTuple = (UHeapDiskTuple)item;
    errno_t rc = EOK;
    tuple->disk_tuple = diskTuple;
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\t\t\txid:%lu, td:%d, locker_td %d\n",
        UHeapTupleGetRawXid(tuple), diskTuple->td_id, diskTuple->reserved);
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\t\t\tFlag:%u", diskTuple->flag);
    securec_check_ss(rc, "\0", "\0");

    if (diskTuple->flag & UHEAP_HAS_NULL) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_HASNULL");
        securec_check_ss(rc, "\0", "\0");
    }
    if (diskTuple->flag & UHEAP_DELETED) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_DELETED\n");
        securec_check_ss(rc, "\0", "\0");
    }
    if (diskTuple->flag & UHEAP_INPLACE_UPDATED) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_INPLACE_UPDATED ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (diskTuple->flag & UHEAP_UPDATED) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_UPDATED ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (diskTuple->flag & UHEAP_XID_KEYSHR_LOCK) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_XID_KEYSHR_LOCK ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (diskTuple->flag & UHEAP_XID_NOKEY_EXCL_LOCK) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_XID_NOKEY_EXCL_LOCK ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (diskTuple->flag & UHEAP_XID_EXCL_LOCK) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_XID_EXCL_LOCK ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (diskTuple->flag & UHEAP_MULTI_LOCKERS) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_MULTI_LOCKERS ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (diskTuple->flag & UHEAP_INVALID_XACT_SLOT) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_INVALID_XACT_SLOT ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (diskTuple->flag & SINGLE_LOCKER_XID_IS_LOCK) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tSINGLE_LOCKER_XID_IS_LOCK ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (diskTuple->flag & SINGLE_LOCKER_XID_IS_SUBXACT) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
            "\n\t\t\tSINGLE_LOCKER_XID_IS_SUBXACT ");
        securec_check_ss(rc, "\0", "\0");
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tFlag2:");
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tNumber of columns:: %d\n",
        UHeapTupleHeaderGetNatts(diskTuple));
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\t\t\tHoff: %u\n", diskTuple->t_hoff);
    securec_check_ss(rc, "\0", "\0");
    return;
}

static int OpenUndoSeg(int zoneId, BlockNumber blkNo)
{
    char fileName[MAXUNDOFILENAMELEN] = {0};
    errno_t rc = EOK;
    int segno = blkNo / UNDOSEG_SIZE;
    DECLARE_NODE_COUNT();
    GET_UPERSISTENCE_BY_ZONEID(zoneId, nodeCount);
    const char *persistence = (upersistence == UNDO_PERMANENT) ? "permanent" : ((upersistence == UNDO_TEMP) ? "temp" :
        "unlogged");
    rc = snprintf_s(fileName, sizeof(fileName), sizeof(fileName), "undo/%s/%05X.%07zX", persistence, zoneId, segno);
    securec_check_ss(rc, "\0", "\0");
    int fd = open(fileName, O_RDONLY | PG_BINARY, S_IRUSR | S_IWUSR);
    if (fd < 0) {
        return INVALID_FD;
    }

    return fd;
}

bool DumpUndoRecordsBytes(char *destptr, int destlen, char **readeptr, char *endptr, int *myBytesRead, int *alreadyRead)
{
    if (*myBytesRead >= destlen) {
        *myBytesRead -= destlen;
        return true;
    }
    int remaining = destlen - *myBytesRead;
    int maxReadOnCurrPage = endptr - *readeptr;
    int canRead = Min(remaining, maxReadOnCurrPage);
    if (canRead == 0) {
        return false;
    }
    errno_t rc = memcpy_s(destptr + *myBytesRead, remaining, *readeptr, canRead);
    securec_check(rc, "\0", "\0");
    *readeptr += canRead;
    *alreadyRead += canRead;
    *myBytesRead = 0;
    return (canRead == remaining);
}

bool ReadUndoRecordByUrp(UndoHeader *urec, char *buffer, int startingByte, int *alreadyRead)
{
    char *readptr = buffer + startingByte;
    char *endptr = buffer + BLCKSZ;
    int myBytesRead = *alreadyRead;

    if (!DumpUndoRecordsBytes((char*)&(urec->whdr), SIZE_OF_UNDO_RECORD_HEADER,
        &readptr, endptr, &myBytesRead, alreadyRead)) {
        return false;
    }
    if (!DumpUndoRecordsBytes((char*)&(urec->wblk), SIZE_OF_UNDO_RECORD_BLOCK,
        &readptr, endptr, &myBytesRead, alreadyRead)) {
        return false;
    }
    if ((urec->whdr.uinfo & UNDO_UREC_INFO_TRANSAC) != 0) {
        if (!DumpUndoRecordsBytes((char *)&urec->wtxn, SIZE_OF_UNDO_RECORD_TRANSACTION,
            &readptr, endptr, &myBytesRead, alreadyRead)) {
            return false;
        }
    }
    if ((urec->whdr.uinfo & UNDO_UREC_INFO_OLDTD) != 0) {
        if (!DumpUndoRecordsBytes((char *)&urec->wtd, SIZE_OF_UNDO_RECORD_OLDTD,
            &readptr, endptr, &myBytesRead, alreadyRead)) {
            return false;
        }
    }
    if ((urec->whdr.uinfo & UNDO_UREC_INFO_HAS_PARTOID) != 0) {
        if (!DumpUndoRecordsBytes((char *)&urec->wpart, SIZE_OF_UNDO_RECORD_PARTITION,
            &readptr, endptr, &myBytesRead, alreadyRead)) {
            return false;
        }
    }
    if ((urec->whdr.uinfo & UNDO_UREC_INFO_HAS_TABLESPACEOID) != 0) {
        if (!DumpUndoRecordsBytes((char *)&urec->wtspc, SIZE_OF_UNDO_RECORD_TABLESPACE,
            &readptr, endptr, &myBytesRead, alreadyRead)) {
            return false;
        }
    }
    if ((urec->whdr.uinfo & UNDO_UREC_INFO_PAYLOAD) != 0) {
        if (!DumpUndoRecordsBytes((char *)&urec->wpay, SIZE_OF_UNDO_RECORD_PAYLOAD,
            &readptr, endptr, &myBytesRead, alreadyRead)) {
            return false;
        }

        urec->rawdata.len = urec->wpay.payloadlen;
        if (urec->rawdata.len > 0) {
            if (urec->rawdata.data == NULL) {
                urec->rawdata.data = (char *)malloc(urec->rawdata.len);
                if (NULL == urec->rawdata.data) {
                    ereport(WARNING,
                        (errcode(ERRCODE_OUT_OF_MEMORY),
                            errmodule(MOD_USTORE),
                            errmsg("Alloc memory failed."),
                            errdetail("N/A"),
                            errcause("Out of memory"),
                            erraction("N/A")));
                    return false;
                }
            }
            if (!DumpUndoRecordsBytes((char *)urec->rawdata.data, urec->rawdata.len,
                &readptr, endptr, &myBytesRead, alreadyRead)) {
                return false;
            }
        }
    }
    return true;
}

static bool DumpUndoRecod(UndoRecord *urec)
{
    if (urec == NULL) {
        return false;
    }

    bool found = false;
    char undoBuf[BLCKSZ] = {'\0'};
    off_t seekpos;
    int zoneId = UNDO_PTR_GET_ZONE_ID(urec->Urp());
    BlockNumber blockNo = UNDO_PTR_GET_BLOCK_NUM(urec->Urp());
    uint32 startBytes = UNDO_PTR_GET_PAGE_OFFSET(urec->Urp());
    int alreadyRead = 0;
    uint32 ret = 0;
    int fd = INVALID_FD;
    errno_t rc = EOK;
    UndoHeader *urecInfo = NULL;

    urecInfo = (UndoHeader *)palloc0(sizeof(UndoHeader));

    do {
        fd = OpenUndoSeg(zoneId, blockNo);
        if (fd == INVALID_FD) {
            pfree(urecInfo);
            return false;
        }
        seekpos = (off_t)BLCKSZ * (blockNo % ((BlockNumber)UNDOSEG_SIZE));
        lseek(fd, seekpos, SEEK_SET);
        rc = memset_s(undoBuf, BLCKSZ, 0, BLCKSZ);
        securec_check(rc, "\0", "\0");
        ret = read(fd, (char *)undoBuf, BLCKSZ);
        if (ret != BLCKSZ) {
            ereport(WARNING,
                (errcode(ERRCODE_OPERATE_FAILED),
                    errmodule(MOD_USTORE),
                    errmsg("Read undo with urp(%lu) failed, errno(%d).", urec->Urp(), errno),
                    errdetail("N/A"),
                    errcause("Read size is not a block size."),
                    erraction("N/A")));
            free(urecInfo->rawdata.data);
            pfree(urecInfo);
            close(fd);
            return false;
        }

        if (ReadUndoRecordByUrp(urecInfo, undoBuf, startBytes, &alreadyRead)) {
            found = true;
            /* Set undorecord. */
            urec->SetXid(urecInfo->whdr.xid);
            urec->SetCid(urecInfo->whdr.cid);
            urec->SetReloid(urecInfo->whdr.reloid);
            urec->SetRelfilenode(urecInfo->whdr.relfilenode);
            urec->SetUtype(urecInfo->whdr.utype);
            urec->SetUinfo(urecInfo->whdr.uinfo);

            urec->SetBlkprev(urecInfo->wblk.blkprev);
            urec->SetBlkno(urecInfo->wblk.blkno);
            urec->SetOffset(urecInfo->wblk.offset);
            urec->SetPrevurp(urecInfo->wtxn.prevurp);
            urec->SetOldXactId(urecInfo->wtd.oldxactid);
            urec->SetPartitionoid(urecInfo->wpart.partitionoid);
            urec->SetTablespace(urecInfo->wtspc.tablespace);
            urec->SetPayLoadLen(urecInfo->wpay.payloadlen);
            if (urecInfo->wpay.payloadlen) {
                initStringInfo(urec->Rawdata());
                appendBinaryStringInfo(urec->Rawdata(), urecInfo->rawdata.data, urecInfo->wpay.payloadlen);
            }
            break;
        }

        startBytes = UNDO_LOG_BLOCK_HEADER_SIZE;
        blockNo++;
        close(fd);
        CHECK_FOR_INTERRUPTS();
    } while (true);

    if (urecInfo->rawdata.data != NULL) {
        free(urecInfo->rawdata.data);
    }
    pfree(urecInfo);
    close(fd);
    return found;
}

static void DumpUndoRecordByUrpFromPage(void *page, ItemPointer ctid, UndoRecPtr urp, int tdSlot,
    UHeapDiskTuple diskTuple, char *output)
{
    errno_t rc = EOK;
    int prevTdSlot = tdSlot;
    int currentTdSlot = tdSlot;
    UndoRecPtr currUrp = urp;
    /*xid is the transaction ID we need for tuple's version when we traverse the TD chain,
    for one version of tuple in undo_record, it's correct xid is stored in urec->OldXactId();
    urec->Xid() represents the transaction ID that placed the undo_record into the undo segment. 
    */
    TransactionId xid = InvalidTransactionId;
fetch_undo:
    VerifyMemoryContext();
    UndoRecord *urec = New(CurrentMemoryContext)UndoRecord();
    urec->SetUrp(currUrp);
    bool fetch = DumpUndoRecod(urec);
    if (fetch) {
       Assert(urec != NULL);
       if (urec->Utype() == UNDO_INSERT || urec->Utype() == UNDO_MULTI_INSERT) {
            diskTuple->flag &= ~UHEAP_VIS_STATUS_MASK;
            diskTuple->xid = (ShortTransactionId)FrozenTransactionId;
        } else if (urec->Utype() == UNDO_INPLACE_UPDATE) {
            Assert(urec->Rawdata() != NULL);
            Assert(urec->Rawdata()->len >= (int)SizeOfUHeapDiskTupleData);
            rc = memcpy_s((char *)diskTuple + OffsetTdId, SizeOfUHeapDiskTupleHeaderExceptXid,
                urec->Rawdata()->data + sizeof(uint8), SizeOfUHeapDiskTupleHeaderExceptXid);
            securec_check(rc, "", "");
            diskTuple->xid = (ShortTransactionId)FrozenTransactionId;
        } else {
            Assert(urec->Rawdata() != NULL);
            Assert(urec->Rawdata()->len >= (int)SizeOfUHeapDiskTupleHeaderExceptXid);
            rc = memcpy_s(((char *)diskTuple + OffsetTdId), SizeOfUHeapDiskTupleHeaderExceptXid,
                urec->Rawdata()->data, SizeOfUHeapDiskTupleHeaderExceptXid);
            securec_check(rc, "", "");
            diskTuple->xid = (ShortTransactionId)FrozenTransactionId;
        }

        currentTdSlot = diskTuple->td_id;

        /* Find matched undo version, and output. */
        if (ItemPointerGetBlockNumber(ctid) == urec->Blkno() && urec->Offset() == ctid->ip_posid
        && (!TransactionIdIsValid(xid) || TransactionIdEquals(xid, urec->Xid()))) {
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                "\n\t\tUndoRecPtr(%lu): whdr = xid(%lu), cid(%u), reloid(%u), relfilenode(%u), utype(%u), uinfo(%u).\n",
                urec->Urp(), urec->Xid(), urec->Cid(), urec->Reloid(), urec->Relfilenode(), urec->Utype(), urec->Uinfo());
            securec_check_ss(rc, "\0", "\0");

            if ((urec->Uinfo() & UNDO_UREC_INFO_PAYLOAD) != 0) {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tflag_payload, size = %lu.\n", SIZE_OF_UNDO_RECORD_PAYLOAD);
                securec_check_ss(rc, "\0", "\0");
            }
            if ((urec->Uinfo() & UNDO_UREC_INFO_TRANSAC) != 0) {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tflag_transac, size = %lu.\n", SIZE_OF_UNDO_RECORD_TRANSACTION);
                securec_check_ss(rc, "\0", "\0");
            }
            if ((urec->Uinfo() & UNDO_UREC_INFO_BLOCK) != 0) {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tflag_block, size = %lu.\n", SIZE_OF_UNDO_RECORD_BLOCK);
                securec_check_ss(rc, "\0", "\0");
            }
            if ((urec->Uinfo() & UNDO_UREC_INFO_OLDTD) != 0) {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tflag_oldtd, size = %lu.\n", SIZE_OF_UNDO_RECORD_OLDTD);
                securec_check_ss(rc, "\0", "\0");
            }
            if ((urec->Uinfo() & UNDO_UREC_INFO_CONTAINS_SUBXACT) != 0) {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tflag_subxact.\n");
                securec_check_ss(rc, "\0", "\0");
            }
            if ((urec->Uinfo() & UNDO_UREC_INFO_HAS_PARTOID) != 0) {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tflag_partoid, size = %lu.\n", SIZE_OF_UNDO_RECORD_PARTITION);
                securec_check_ss(rc, "\0", "\0");
            }
            if ((urec->Uinfo() & UNDO_UREC_INFO_HAS_TABLESPACEOID) != 0) {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tflag_tablespaceoid, size = %lu.\n", SIZE_OF_UNDO_RECORD_TABLESPACE);
                securec_check_ss(rc, "\0", "\0");
            }

            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                "\n\t\twblk = blk_prev(%lu), blockno(%u), offset(%u).\n", urec->Blkprev(), urec->Blkno(), urec->Offset());
            securec_check_ss(rc, "\0", "\0");
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                "\n\t\twtxn = prevurp(%lu).\n", urec->Prevurp2());
            securec_check_ss(rc, "\0", "\0");
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                "\n\t\twpay = payloadlen(%u).\n", urec->PayLoadLen());
            securec_check_ss(rc, "\0", "\0");
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                "\n\t\twtd = oldcaxtid(%lu).\n", urec->OldXactId());
            securec_check_ss(rc, "\0", "\0");
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                "\n\t\twpart_ = partitionoid(%u).\n", urec->Partitionoid());
            securec_check_ss(rc, "\0", "\0");
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                "\n\t\twtspc_ = tablespace(%u).\n", urec->Tablespace());
            securec_check_ss(rc, "\0", "\0");
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                "\n\t\tUndo diskTuple: td_id %u, reserved %u, flag %u, flag2 %u, t_hoff %u.\n",
                diskTuple->td_id, diskTuple->reserved, diskTuple->flag, diskTuple->flag2, diskTuple->t_hoff);
            securec_check_ss(rc, "\0", "\0");
        }else{
            if (IS_VALID_UNDO_REC_PTR(urec->Blkprev())) {
                currUrp = urec->Blkprev();
                DELETE_EX(urec);
                CHECK_FOR_INTERRUPTS();
                goto fetch_undo;
            }else{
                goto fetch_end;
            }
        }
    }else{
        goto fetch_end;
    }

    xid = urec->OldXactId();

    if (prevTdSlot == currentTdSlot) {
        if (IS_VALID_UNDO_REC_PTR(urec->Blkprev())) {
            currUrp = urec->Blkprev();
            DELETE_EX(urec);
            CHECK_FOR_INTERRUPTS();
            goto fetch_undo;
        }
    } else {
        if (currentTdSlot == UHEAPTUP_SLOT_FROZEN) {
           goto fetch_end;
        } else {
            prevTdSlot = currentTdSlot;
            UHeapPageTDData *tdPtr = (UHeapPageTDData *)PageGetTDPointer(page);
            UHeapPageHeaderData *phdr = (UHeapPageHeaderData *)page;
            if (currentTdSlot < 1 || currentTdSlot > phdr->td_count) {
                ereport(WARNING,
                    (errcode(ERRCODE_OPERATE_FAILED),
                        errmodule(MOD_USTORE),
                        errmsg("CurrentTd(%d) is out of td(%d) in page header", currentTdSlot, phdr->td_count),
                        errdetail("N/A"),
                        errcause("Td slot is corrupted"),
                        erraction("N/A")));
                goto fetch_end;
            } else {
                TD *thistrans = &tdPtr->td_info[currentTdSlot - 1];
                currUrp = thistrans->undo_record_ptr;
                DELETE_EX(urec);
                CHECK_FOR_INTERRUPTS();
                goto fetch_undo;
            }
        }
    }

fetch_end:
    DELETE_EX(urec);
    return;
}

static void ParseUHeapPageItemUndo(void *page, ItemPointer ctid, char *output)
{
    if (page == NULL || ctid == NULL || output == NULL) {
        return;
    }

    int tdSlot = 0;
    errno_t rc = EOK;
    UHeapPageHeader pHeader = (UHeapPageHeader) page;
    UHeapPageTDData *tdPtr = (UHeapPageTDData *)PageGetTDPointer(page);
    RowPtr *rp = UPageGetRowPtr(pHeader, ctid->ip_posid);

    if (RowPtrIsNormal(rp)) {
        UHeapDiskTuple udiskTuple = NULL;
        UHeapDiskTupleData inputDiskTuple;
        TD* tdInfo = NULL;
        udiskTuple = (UHeapDiskTuple) UPageGetRowData(pHeader, rp);
        Assert(udiskTuple != NULL);
        tdSlot = udiskTuple->td_id;
        if (tdSlot == UHEAPTUP_SLOT_FROZEN) {
            return;
        }
        tdInfo = &(tdPtr->td_info[tdSlot - 1]);
        rc = memset_s((char *) &inputDiskTuple, SizeOfUHeapDiskTupleData, 0, SizeOfUHeapDiskTupleData);
        securec_check_c(rc, "\0", "\0");
        rc = memcpy_s(&inputDiskTuple, SizeOfUHeapDiskTupleData, udiskTuple, SizeOfUHeapDiskTupleData);
        securec_check_c(rc, "\0", "\0");
        DumpUndoRecordByUrpFromPage(page, ctid, tdInfo->undo_record_ptr, tdSlot, &inputDiskTuple, output);
    } else if (RowPtrIsDeleted(rp)) {
        TD* tdInfo = NULL;
        UHeapDiskTupleData inputDiskTuple;
        tdSlot = RowPtrGetTDSlot(rp);
        if (tdSlot == UHEAPTUP_SLOT_FROZEN) {
            return;
        }
        tdInfo = &(tdPtr->td_info[tdSlot - 1]);
        rc = memset_s((char *) &inputDiskTuple, SizeOfUHeapDiskTupleData, 0, SizeOfUHeapDiskTupleData);
        securec_check_c(rc, "\0", "\0");
        DumpUndoRecordByUrpFromPage(page, ctid, tdInfo->undo_record_ptr, tdSlot, &inputDiskTuple, output);
    }
}

static void ParseUHeapPageData(void *page, BlockNumber blkno, char *output, bool dumpUndo)
{
    errno_t rc;
    uint32 rowPtrCnt = 0;
    uint32 storeCnt = 0;
    uint32 unusedCnt = 0;
    uint32 normalCnt = 0;
    uint32 deadCnt = 0;
    uint32 redirectCnt = 0;
    Item item;
    RowPtr *rowptr;
    UHeapTupleData utuple;
    UHeapPageHeader pageHeader = NULL;

    if (page == NULL || output == NULL) {
        return;
    }

    pageHeader = (UHeapPageHeader)page;
    if (pageHeader->pd_lower <= SizeOfUHeapPageHeaderData) {
        rowPtrCnt = 0;
    } else {
        rowPtrCnt =
            (pageHeader->pd_lower - (SizeOfUHeapPageHeaderData + SizeOfUHeapTDData(pageHeader))) / sizeof(RowPtr);
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
        "\n\tUHeap tuple information on this page\n");
    securec_check_ss(rc, "\0", "\0");

    for (OffsetNumber i = FirstOffsetNumber; i <= rowPtrCnt; i++) {
        rowptr = UPageGetRowPtr(pageHeader, i);
        if (RowPtrIsUsed(rowptr)) {
            if (RowPtrHasStorage(rowptr)) {
                storeCnt++;
            }

            if (RowPtrIsNormal(rowptr)) {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tTuple #%u is normal: length %u, offset %u\n", i, RowPtrGetLen(rowptr),
                    RowPtrGetOffset(rowptr));
                securec_check_ss(rc, "\0", "\0");
                normalCnt++;
                item = UPageGetRowData(pageHeader, rowptr);
                UHeapTupleCopyBaseFromPage(&utuple, pageHeader);
                ParseUHeapPageItem(item, &utuple, output);
                if (dumpUndo) {
                    ItemPointerData ctid;
                    ItemPointerSet(&ctid, blkno, i);
                    ParseUHeapPageItemUndo(page, &ctid, output);
                }
            } else if (RowPtrIsDead(rowptr)) {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tTuple #%u is dead: length %u, offset %u", i, RowPtrGetLen(rowptr), RowPtrGetOffset(rowptr));
                securec_check_ss(rc, "\0", "\0");
                deadCnt++;
            } else {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tTuple #%u is redirected: length %u, offset %u", i, RowPtrGetLen(rowptr),
                    RowPtrGetOffset(rowptr));
                securec_check_ss(rc, "\0", "\0");
                redirectCnt++;
            }
        } else {
            unusedCnt++;
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\tTuple #%u is unused\n", i);
            securec_check_ss(rc, "\0", "\0");
        }
    }
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
        "\tSummary (%u total): %u unused, %u normal, %u dead, %u redirect\n", rowPtrCnt, unusedCnt, normalCnt, deadCnt,
        redirectCnt);
    securec_check_ss(rc, "\0", "\0");
}

static void ParseUHeapPageSpecialInfo(void *page, char *output)
{
    errno_t rc = 0;

    if (page == NULL || output == NULL) {
        return;
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
        "\n\tSpecial area information on this page\n");
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\tNot used currently.\n");
    securec_check_ss(rc, "\0", "\0");
}

static void ParseUHeapPage(void *page, BlockNumber blkno, BlockNumber endBlk, char *output, bool dumpUndo)
{
    uint16 pageHeaderSize = 0;
    UHeapPageHeader pageHeader = NULL;
    errno_t rc;

    if (page == NULL || output == NULL) {
        return;
    }

    /* Parse header of uheap page. */
    pageHeader = (UHeapPageHeader)page;

    /* Check invalidation of uheap page head. */
    pageHeaderSize = GetPageHeaderSize(pageHeader);
    if (pageHeader->pd_lower < pageHeaderSize || pageHeader->pd_lower > pageHeader->pd_upper ||
        pageHeader->pd_upper > pageHeader->pd_special || pageHeader->pd_special > BLCKSZ ||
        pageHeader->pd_special != MAXALIGN(pageHeader->pd_special)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
            "The page data is corrupted, corrupted page pointers: lower = %u, upper = %u, special = %u\n",
            pageHeader->pd_lower, pageHeader->pd_upper, pageHeader->pd_special);
        securec_check_ss(rc, "\0", "\0");
        return;
    }

    ParseUHeapPageHeader((void *)pageHeader, blkno, endBlk, output);
    if (PageIsNew(pageHeader)) {
        return;
    }

    /* Parse td slot info of uheap page. */
    ParseUHeapPageTDInfo(pageHeader, output);
    /* Parse items of uheap page. */
    ParseUHeapPageData(pageHeader, blkno, output, dumpUndo);
    /* Parse special area of uheap page. */
    ParseUHeapPageSpecialInfo(pageHeader, output);
}

static void ParseIndexPageHeader(void *page, int type, BlockNumber blkno, BlockNumber endBlk, char *output)
{
    errno_t rc = 0;
    bool chksumResult = false;
    PageHeader pageHeader = NULL;

    if (page == NULL || output == NULL) {
        return;
    }

    pageHeader = (PageHeader)page;
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
        "%s index page information of block %u/%u\n", (type == BTREE_INDEX) ? "Btree" : "UBtree", blkno, endBlk);
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_lsn: %X/%X\n",
        (uint32)(PageGetLSN((Page)pageHeader) >> XIDTHIRTYTWO), (uint32)PageGetLSN((Page)pageHeader));
    securec_check_ss(rc, "\0", "\0");

    if (CheckPageZeroCases(pageHeader)) {
        uint16 checksum = pg_checksum_page((char *)pageHeader, (BlockNumber)blkno);
        chksumResult = (checksum == pageHeader->pd_checksum);
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_checksum: 0x%X, verify %s\n",
        pageHeader->pd_checksum, chksumResult ? "success" : "fail");
    securec_check_ss(rc, "\0", "\0");

    if (PageHasFreeLinePointers((Page)pageHeader)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_HAS_FREE_LINES.");
        securec_check_ss(rc, "\0", "\0");
    }
    if (PageIsFull(pageHeader)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_PAGE_FULL.");
        securec_check_ss(rc, "\0", "\0");
    }
    if (PageIsAllVisible(pageHeader)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_ALL_VISIBLE.");
        securec_check_ss(rc, "\0", "\0");
    }
    if (PageIsCompressed(pageHeader)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_COMPRESSED_PAGE.");
        securec_check_ss(rc, "\0", "\0");
    }
    if (PageIsLogical(pageHeader)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_LOGICAL_PAGE.");
        securec_check_ss(rc, "\0", "\0");
    }
    if (PageIsEncrypt(pageHeader)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_ENCRYPT_PAGE.");
        securec_check_ss(rc, "\0", "\0");
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
        "\tpd_lower: %u, %s\n\tpd_upper: %u, %s\n\tpd_special: %u, size %u\n", pageHeader->pd_lower,
        PageIsEmpty((Page)pageHeader) ? "empty" : "non-empty", pageHeader->pd_upper,
        PageIsNew((Page)pageHeader) ? "new page" : "old page", pageHeader->pd_special,
        PageGetSpecialSize((Page)pageHeader));
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tPage size & version: %u, %u\n",
        (uint16)PageGetPageSize(page), (uint16)PageGetPageLayoutVersion(page));
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_prune_xid: %lu\n",
        pageHeader->pd_prune_xid + ((HeapPageHeader)(pageHeader))->pd_xid_base);
    securec_check_ss(rc, "\0", "\0");
    rc =
        snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_xid_base: %lu, pd_multi_base: %lu\n",
        ((HeapPageHeader)(pageHeader))->pd_xid_base, ((HeapPageHeader)(pageHeader))->pd_multi_base);
    securec_check_ss(rc, "\0", "\0");

    return;
}

static void ParseIndexPageItem(Item item, int type, uint32 len, char *output)
{
    if (item == NULL || output == NULL) {
        return;
    }

    errno_t rc = 0;
    IndexTuple itup = (IndexTuple)item;
    bool hasnull = (itup->t_info & INDEX_NULL_MASK);
    unsigned int tuplen = (itup->t_info & INDEX_SIZE_MASK);
    if (tuplen != len) {
        if (type == UBTREE_INDEX) {
            UstoreIndexXid uxid = (UstoreIndexXid)UstoreIndexTupleGetXid(itup);
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                "\t\t\txmin:%d xmax:%d Heap Tid: block %u/%u, offset %u\n", uxid->xmin, uxid->xmax,
                itup->t_tid.ip_blkid.bi_hi, itup->t_tid.ip_blkid.bi_lo, itup->t_tid.ip_posid);
            securec_check_ss(rc, "\0", "\0");
        }
    } else {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
            "\t\t\tHeap Tid: block %u/%u, offset %u\n", itup->t_tid.ip_blkid.bi_hi, itup->t_tid.ip_blkid.bi_lo,
            itup->t_tid.ip_posid);
        securec_check_ss(rc, "\0", "\0");
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\t\t\tLength: %u", tuplen);
    securec_check_ss(rc, "\0", "\0");

    if (itup->t_info & INDEX_VAR_MASK) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, ", has var-width attrs");
        securec_check_ss(rc, "\0", "\0");
    }

    if (hasnull) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, ", has nulls ");
        securec_check_ss(rc, "\0", "\0");
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n");
    securec_check_ss(rc, "\0", "\0");
    return;
}

static void ParseIndexPageSpecialInfo(void *page, int type, char *output)
{
    errno_t rc = 0;
    BTPageOpaqueInternal opaque = NULL;
    UBTPageOpaqueInternal uopaque = NULL;
    PageHeader pageHeader = NULL;

    if (page == NULL || output == NULL || type >= INDEX_BOTT) {
        return;
    }

    pageHeader = (PageHeader)page;
    opaque = (BTPageOpaqueInternal)PageGetSpecialPointer((Page)pageHeader);
    if (PageGetSpecialSize((Page)pageHeader) > MAXALIGN(sizeof(BTPageOpaqueData))) {
        uopaque = (UBTPageOpaqueInternal)opaque;
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n%s index special information:\n",
        (type == BTREE_INDEX) ? "BTree" : "UBTree");
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tbtree left sibling: %u\n",
        opaque->btpo_prev);
    securec_check_ss(rc, "\0", "\0");
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tbtree right sibling: %u\n",
        opaque->btpo_next);
    securec_check_ss(rc, "\0", "\0");

    if (!P_ISDELETED(opaque)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tbtree tree level: %u\n",
            opaque->btpo.level);
        securec_check_ss(rc, "\0", "\0");
    } else {
        if (uopaque) {
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tnext txid (deleted): %lu\n",
                ((UBTPageOpaque)uopaque)->xact);
            securec_check_ss(rc, "\0", "\0");
        } else {
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tnext txid (deleted): %lu\n",
                ((BTPageOpaque)opaque)->xact);
            securec_check_ss(rc, "\0", "\0");
        }
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tbtree flag: ");
    securec_check_ss(rc, "\0", "\0");

    if (P_ISLEAF(opaque)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_LEAF ");
        securec_check_ss(rc, "\0", "\0");
    } else {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_INTERNAL ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (P_ISROOT(opaque)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_ROOT ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (P_ISDELETED(opaque)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_DELETED ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (P_ISHALFDEAD(opaque)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_HALF_DEAD ");
        securec_check_ss(rc, "\0", "\0");
    }
    if (P_HAS_GARBAGE(opaque)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_HAS_GARBAGE ");
        securec_check_ss(rc, "\0", "\0");
    }
    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n");
    securec_check_ss(rc, "\0", "\0");

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tbtree cycle ID: %u\n ",
        opaque->btpo_cycleid);
    securec_check_ss(rc, "\0", "\0");

    if (uopaque) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tubtree active tuples: %d\n",
            uopaque->activeTupleCount);
        securec_check_ss(rc, "\0", "\0");
    }

    return;
}

static void ParseIndexPageData(void *page, int type, char *output)
{
    errno_t rc;
    uint32 rowPtrCnt = 0;
    uint32 storeCnt = 0;
    uint32 notusedCnt = 0;
    uint32 normalCnt = 0;
    uint32 deadCnt = 0;
    uint32 redirectCnt = 0;
    Item item;
    ItemId lp;
    PageHeader pageHeader = NULL;

    if (page == NULL || output == NULL) {
        return;
    }

    pageHeader = (PageHeader)page;
    rowPtrCnt = PageGetMaxOffsetNumber((Page)pageHeader);

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
        "\n\tIndex tuple information on this page\n");
    securec_check_ss(rc, "\0", "\0");

    for (uint32 i = FirstOffsetNumber; i <= rowPtrCnt; i++) {
        lp = PageGetItemId((Page)pageHeader, i);
        if (ItemIdIsUsed(lp)) {
            if (ItemIdHasStorage(lp)) {
                storeCnt++;
            }
            if (ItemIdIsNormal(lp) || (IndexItemIdIsFrozen(lp))) {
                rc = ItemIdIsNormal(lp) ? (snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tTuple #%u is normal: length %u, offset %u\n", i, ItemIdGetLength(lp), ItemIdGetOffset(lp))) :
                    (snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tTuple #%u is frozen: length %u, offset %u\n", i, ItemIdGetLength(lp), ItemIdGetOffset(lp)));
                securec_check_ss(rc, "\0", "\0");
                normalCnt++;
                item = PageGetItem((Page)pageHeader, lp);
                ParseIndexPageItem(item, type, ItemIdGetLength(lp), output);
            } else if (ItemIdIsDead(lp)) {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tTuple #%u is dead: length %u, offset %u", i, ItemIdGetLength(lp), ItemIdGetOffset(lp));
                securec_check_ss(rc, "\0", "\0");
                deadCnt++;
            } else {
                rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
                    "\n\t\tTuple #%u is redirected: length %u, offset %u", i, ItemIdGetLength(lp), ItemIdGetOffset(lp));
                securec_check_ss(rc, "\0", "\0");
                redirectCnt++;
            }
        } else {
            notusedCnt++;
            rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\tTuple #%u is unused\n", i);
            securec_check_ss(rc, "\0", "\0");
        }
    }

    rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
        "\tSummary (%u total): %u unused, %u normal, %u dead, %u redirect\n", rowPtrCnt, notusedCnt, normalCnt, deadCnt,
        redirectCnt);
    securec_check_ss(rc, "\0", "\0");
    return;
}

static void ParseIndexPage(void *page, int type, BlockNumber blkno, BlockNumber endBlk, char *output)
{
    PageHeader pageHeader = NULL;
    uint16 headersize = 0;
    errno_t rc = 0;

    if (page == NULL || output == NULL) {
        return;
    }

    pageHeader = (PageHeader)page;
    headersize = GetPageHeaderSize(pageHeader);
    if (pageHeader->pd_lower < headersize || pageHeader->pd_lower > pageHeader->pd_upper ||
        pageHeader->pd_upper > pageHeader->pd_special || pageHeader->pd_special > BLCKSZ ||
        pageHeader->pd_special != MAXALIGN(pageHeader->pd_special)) {
        rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
            "The page data is corrupted, corrupted page pointers: lower = %u, upper = %u, special = %u\n",
            pageHeader->pd_lower, pageHeader->pd_upper, pageHeader->pd_special);
        securec_check_ss(rc, "\0", "\0");
        return;
    }

    ParseIndexPageHeader((void *)page, type, blkno, endBlk, output);
    if (PageIsNew(pageHeader)) {
        return;
    }

    ParseIndexPageData((void *)page, type, output);
    ParseIndexPageSpecialInfo((void *)page, type, output);
    return;
}

Datum gs_parse_page_bypath(PG_FUNCTION_ARGS)
{
    /* check user's right */
    const char fName[MAXFNAMELEN] = "gs_parse_page_bypath";
    CheckUser(fName);

    /* read in parameters */
    char *path = text_to_cstring(PG_GETARG_TEXT_P(0));
    int64 blocknum = PG_GETARG_INT64(1);
    char *relation_type = text_to_cstring(PG_GETARG_TEXT_P(2));
    bool read_memory = PG_GETARG_BOOL(3);

    /* In order to avoid querying the shared buffer and applying LW locks, blocking the business. */
    /* In the case of finding all pages, force to check disk  */
    if (blocknum == -1) {
        read_memory = false;
    }
    char *outputFilename = ParsePage(path, blocknum, relation_type, read_memory);
    PG_RETURN_TEXT_P(cstring_to_text(outputFilename));
}

bool CheckDumpPageSpecifiedDirectory(char* specifiedDir)
{
    errno_t rc = EOK;
    char *dumpDir = (char *)palloc0(MAXFILENAME * sizeof(char));

    rc = snprintf_s(dumpDir, MAXFILENAME, MAXFILENAME -1, "%s/%s", u_sess->attr.attr_common.Log_directory, "dump");
    securec_check_ss(rc, "\0", "\0");

    if (mkdir(dumpDir, S_IRWXU) < 0 && errno != EEXIST) {
        ereport(WARNING, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", dumpDir)));
        pfree(dumpDir);
        return false;
    }

    if (strcmp(specifiedDir, "") != 0) {
        rc = strcat_s(dumpDir, strlen(dumpDir) + strlen("/") + 1, "/");
        securec_check_ss(rc, "\0", "\0");
        rc = strcat_s(dumpDir, strlen(dumpDir) + strlen(specifiedDir) + 1, specifiedDir);
        securec_check_ss(rc, "\0", "\0");

        if (mkdir(dumpDir, S_IRWXU) < 0 && errno != EEXIST) {
            ereport(WARNING, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", dumpDir)));
            pfree(dumpDir);
            return false;
        }
    }

    pfree(dumpDir);
    return true;
}

/*
 * Function:dump_page_to_specified_directory
 * Description: dump one page to specified directory which named by page erroe type.
 * Support dump page: heap, segment_heap
 * Support parse page: heap
 * Support dump page input fomat: buffer, page, (rel, blkno)
 * pblk is NULL: On condition of segment head page, pblk is null.
 */
void DumpPageToSpecifiedDirectory(Buffer buffer, char* pageType, bool dump, Relation rel, BlockNumber blkno,
                                  const XLogPhyBlock *pblk, char* specifiedDir)
{
    errno_t rc = EOK;
    BufferDesc *buf_desc = nullptr;
    RelFileNode rnode = {InvalidOid, InvalidOid, InvalidOid, InvalidBktId, 0};
    Page dump_page = nullptr;
    BlockNumber blocknum = InvalidBlockNumber;
    BlockNumber seg_block = InvalidBlockNumber;
    struct stat statbuf;

    if (BufferIsInvalid(buffer)) {
        ereport(WARNING, (errmsg("[SS] dump_one_page: buffer %d is invalid, so dump page do not execute.", buffer)));
        return;
    }

    buf_desc = GetBufferDescriptor(buffer - 1);
    rnode.spcNode = buf_desc->tag.rnode.spcNode;
    rnode.dbNode = buf_desc->tag.rnode.dbNode;
    rnode.relNode = buf_desc->tag.rnode.relNode;
    blocknum = buf_desc->tag.blockNum;
    dump_page = BufferGetPage(buffer);

    char *outputFilename = (char *)palloc0(MAXFILENAME * sizeof(char));
    if (!CheckDumpPageSpecifiedDirectory(specifiedDir)) {
        pfree(outputFilename);
        return;
    }

    if (strcmp(pageType, "segment") == 0) {
        uint8 seg_fileno;
        if (pblk != NULL) {
            seg_fileno = pblk->relNode;
            seg_block = pblk->block;
        } else {
            SegPageLocation loc = seg_get_physical_location(rnode, MAIN_FORKNUM, blocknum, false);
            seg_fileno = EXTENT_SIZE_TO_TYPE(loc.extent_size);
            seg_block = loc.blocknum;
            ereport(LOG, (errmodule(MOD_REMOTE), errmsg("[SS] DumpPageToSpecifiedDirectory: get physical location"
                "page info:%u/%u/%u forknum %d blknum:%u, (pblk %u/%d)",
                rnode.spcNode, rnode.dbNode, rnode.relNode, buf_desc->tag.forkNum,
                blocknum, pblk->relNode, pblk->block)));
        }
        if (strcmp(specifiedDir, "") != 0) {
            rc = snprintf_s(outputFilename, MAXFILENAME, MAXFILENAME -1,
                            "%s/%s/%s/%u_%u_%u_%u_%u_%u.%s", u_sess->attr.attr_common.Log_directory, "dump",
                            specifiedDir, rnode.spcNode, rnode.dbNode, rnode.relNode, blocknum, seg_fileno,
                            seg_block, pageType);
            securec_check_ss(rc, "\0", "\0");
        } else {
            rc = snprintf_s(outputFilename, MAXFILENAME, MAXFILENAME -1,
                            "%s/%s/%u_%u_%u_%u_%u_%u.%s", u_sess->attr.attr_common.Log_directory, "dump",
                            rnode.spcNode, rnode.dbNode, rnode.relNode, blocknum, seg_fileno, seg_block, pageType);
            securec_check_ss(rc, "\0", "\0");
        }
    } else {
        if (strcmp(specifiedDir, "") != 0) {
            rc = snprintf_s(outputFilename, MAXFILENAME, MAXFILENAME -1,
                            "%s/%s/%s/%u_%u_%u_%u.%s", u_sess->attr.attr_common.Log_directory, "dump", specifiedDir,
                            rnode.spcNode, rnode.dbNode, rnode.relNode, blocknum, pageType);
            securec_check_ss(rc, "\0", "\0");
        } else {
            rc = snprintf_s(outputFilename, MAXFILENAME, MAXFILENAME -1,
                            "%s/%s/%u_%u_%u_%u.%s", u_sess->attr.attr_common.Log_directory, "dump",
                            rnode.spcNode, rnode.dbNode, rnode.relNode, blocknum, pageType);
            securec_check_ss(rc, "\0", "\0");
        }
    }

    if (lstat(outputFilename, &statbuf) == 0) {
        return;
    }

    FILE *outputfile = fopen(outputFilename, "w+");
    if (outputfile == nullptr) {
        ereport(WARNING, (errcode_for_file_access(), (errmsg("[SS] dump_page_to_specified_directory:"
            "could not open %s: %s", outputFilename, TRANSLATE_ERRNO))));
        pfree(outputFilename);
        return;
    }

    uint result = fwrite(dump_page, sizeof(char), BLCKSZ, outputfile);
    if (result != BLCKSZ) {
        ereport(WARNING,
                (errcode_for_file_access(),
                errmsg("[SS] dump_page_to_specified_directory:could not write file %s: %s",
                outputFilename, TRANSLATE_ERRNO)));
        fclose(outputfile);
        pfree(outputFilename);
        return;
    }

    fclose(outputfile);

    BufferTag buf_tag;
    INIT_BUFFERTAG(buf_tag, rnode, 0, blocknum);
    if (dump) {
        if (strcmp(pageType, "segment") == 0) {
            DumpOnePage(dump_page, buf_tag, pageType, specifiedDir, seg_block, rnode);
        } else {
            DumpOnePage(dump_page, buf_tag, pageType, specifiedDir, blocknum, rnode);
        }
    }
    pfree(outputFilename);
}

void DumpOnePage(Page buffer, const BufferTag& buf_tag, char* pageType, char* specifiedDir,
                 BlockNumber blkno, const RelFileNode &rnode)
{
    errno_t rc = EOK;
    PageParseText *page_parse_ctx = nullptr;
    page_parse_ctx = (PageParseText *)palloc0(sizeof(PageParseText) + MAXOUTPUTLEN * sizeof(char));
    rc = memset_s(page_parse_ctx, sizeof(PageParseText) + MAXOUTPUTLEN * sizeof(char),
                  0, sizeof(PageParseText) + MAXOUTPUTLEN * sizeof(char));
    securec_check_ss(rc, "\0", "\0");

    char *strOutput = page_parse_ctx->format_output;
    const PageHeader dump_page = (const PageHeader)buffer;

    ParseOnePage(dump_page, blkno, strOutput, pageType, 0, false);

    char *dumpFilename = (char *)palloc0(MAXFILENAME * sizeof(char));

    if (strcmp(specifiedDir, "") != 0) {
        rc = snprintf_s(dumpFilename, MAXFILENAME, MAXFILENAME -1,
                        "%s/%s/%s/%u_%u_%u_%u.dump", u_sess->attr.attr_common.Log_directory, "dump", specifiedDir,
                        buf_tag.rnode.spcNode, buf_tag.rnode.dbNode, buf_tag.rnode.relNode, buf_tag.blockNum);
        securec_check_ss(rc, "\0", "\0");
    } else {
        rc = snprintf_s(dumpFilename, MAXFILENAME, MAXFILENAME -1,
                        "%s/%s/%u_%u_%u_%u.dump", u_sess->attr.attr_common.Log_directory, "dump",
                        buf_tag.rnode.spcNode, buf_tag.rnode.dbNode, buf_tag.rnode.relNode, buf_tag.blockNum);
        securec_check_ss(rc, "\0", "\0");
    }

    FILE *outputfile = fopen(dumpFilename, "w+");
    if (outputfile == nullptr) {
        ereport(WARNING, (errcode_for_file_access(), (errmsg("[SS] dump_one_page: cannot open %s", dumpFilename))));
        pfree(dumpFilename);
        pfree(page_parse_ctx);
        return;
    }

    ereport(LOG, (errmsg("[SS] dump page to specified directory start.")));
    uint result = fwrite(strOutput, 1, strlen(strOutput), outputfile);
    if (result != strlen(strOutput)) {
        ereport(WARNING, (errcode(ERRCODE_FILE_WRITE_FAILED),
                (errmsg("[SS] dump_one_page: cannot write %s", dumpFilename))));
        fclose(outputfile);
        pfree(dumpFilename);
        pfree(page_parse_ctx);
        return;
    }
    ereport(LOG, (errmsg("[SS] dump page to specified directory end.")));

    fclose(outputfile);
    pfree(dumpFilename);
    pfree(page_parse_ctx);
    return;
}