命令字:!ndx.pglist

用法描述

用于查看 Linux 内核中物理内存页相关的 pglist_data 和 zone 结构体信息,可展示节点(node)、内存域(zone)的内存分布、管理参数等内容
支持的命令行参数:
-v:显示详细信息

开发过程

1. 找到关键全局变量

关键全局变量 作用
contig_page_data 内核中表示物理内存页管理的关键全局变量,是 pglist_data 类型的结构体实例,用于管理系统的物理内存页,包含内存节点、内存域等信息,是访问物理内存页管理数据的核心入口。

2. 相关数据结构

3. 读取变量和相关偏移,输出信息

#include "ndx.h"
#include <windows.h>
#include <minwindef.h>
#include <WDBGEXTS.H>
#include <cstdio>
#include <vector>
#include <string>
#include <string.h>
#include <cctype>
#include <cstdlib>
#include <cerrno>
#include <fstream>
#include <iostream>

#include "pglist.h"


// 构造函数
PglistAnalyzer::PglistAnalyzer() {
    memset(&m_offsets, 0, sizeof(m_offsets));
    memset(&m_zone_offsets, 0, sizeof(m_zone_offsets));
    m_page_data_addr = 0;
    m_initialized = FALSE;
    m_zone_size = 0;
}

// 析构函数
PglistAnalyzer::~PglistAnalyzer() {

}

// 初始化 - 获取contig_page_data地址和字段偏移
BOOL PglistAnalyzer::Initialize() {
    // 获取contig_page_data全局变量地址
    if (!GetExpressionEx("lk!contig_page_data", &m_page_data_addr, NULL)) {
        dprintf("ERROR: Failed to get address of contig_page_data\n");
        return FALSE;
    }

    // 获取字段偏移
    if (!GetFieldOffsets()) {
        dprintf("ERROR: Failed to get pglist_data field offsets\n");
        return FALSE;
    }

    // 获取zone结构体字段偏移
    if (!GetZoneOffsets()) {
        dprintf("ERROR: Failed to get zone field offsets\n");
        return FALSE;
    }

    m_initialized = TRUE;
    return TRUE;
}

// 获取pglist_data结构体字段偏移
BOOL PglistAnalyzer::GetFieldOffsets() {
    // 初始化结构体
    memset(&m_offsets, 0, sizeof(PGLIST_DATA_OFFSETS));
    m_offsets.valid = TRUE;

    // 获取各字段偏移
    if (GetFieldOffset("pglist_data", "node_zones", &m_offsets.node_zones) != 0) {
        dprintf("WARNING: Failed to get offset of node_zones\n");
        m_offsets.valid = FALSE;
    }

    if (GetFieldOffset("pglist_data", "nr_zones", &m_offsets.nr_zones) != 0) {
        dprintf("WARNING: Failed to get offset of nr_zones\n");
        m_offsets.valid = FALSE;
    }

    if (GetFieldOffset("pglist_data", "node_id", &m_offsets.node_id) != 0) {
        dprintf("WARNING: Failed to get offset of node_id\n");
        m_offsets.valid = FALSE;
    }

    if (GetFieldOffset("pglist_data", "node_start_pfn", &m_offsets.node_start_pfn) != 0) {
        dprintf("WARNING: Failed to get offset of node_start_pfn\n");
        m_offsets.valid = FALSE;
    }

    if (GetFieldOffset("pglist_data", "node_present_pages", &m_offsets.node_present_pages) != 0) {
        dprintf("WARNING: Failed to get offset of node_present_pages\n");
        m_offsets.valid = FALSE;
    }

    if (GetFieldOffset("pglist_data", "node_spanned_pages", &m_offsets.node_spanned_pages) != 0) {
        dprintf("WARNING: Failed to get offset of node_spanned_pages\n");
        m_offsets.valid = FALSE;
    }

    if (GetFieldOffset("pglist_data", "kswapd", &m_offsets.kswapd) != 0) {
        dprintf("WARNING: Failed to get offset of kswapd\n");
        m_offsets.valid = FALSE;
    }

    if (GetFieldOffset("pglist_data", "kswapd_order", &m_offsets.kswapd_order) != 0) {
        dprintf("WARNING: Failed to get offset of kswapd_order\n");
        m_offsets.valid = FALSE;
    }

    if (GetFieldOffset("pglist_data", "kswapd_highest_zoneidx", &m_offsets.kswapd_highest_zoneidx) != 0) {
        dprintf("WARNING: Failed to get offset of kswapd_highest_zoneidx\n");
        m_offsets.valid = FALSE;
    }

    if (GetFieldOffset("pglist_data", "kswapd_failures", &m_offsets.kswapd_failures) != 0) {
        dprintf("WARNING: Failed to get offset of kswapd_failures\n");
        m_offsets.valid = FALSE;
    }

    if (GetFieldOffset("pglist_data", "totalreserve_pages", &m_offsets.totalreserve_pages) != 0) {
        dprintf("WARNING: Failed to get offset of totalreserve_pages\n");
        m_offsets.valid = FALSE;
    }

    if (GetFieldOffset("pglist_data", "flags", &m_offsets.flags) != 0) {
        dprintf("WARNING: Failed to get offset of flags\n");
        m_offsets.valid = FALSE;
    }

    return m_offsets.valid;
}

// 获取zone结构体字段偏移
BOOL PglistAnalyzer::GetZoneOffsets() {
    // 初始化结构体
    memset(&m_zone_offsets, 0, sizeof(ZONE_OFFSETS));
    m_zone_offsets.valid = TRUE;

    // 获取zone结构体大小
    m_zone_size = GetTypeSize("zone");
    if (m_zone_size == 0) {
        dprintf("ERROR: Failed to get size of zone structure\n");
        m_zone_offsets.valid = FALSE;
        return FALSE;
    }

    // 获取free_area结构体大小
    m_free_area_size = GetTypeSize("free_area");
    if (m_free_area_size == 0) {
        dprintf("ERROR: Failed to get size of free_area structure\n");
        m_zone_offsets.valid = FALSE;
        return FALSE;
    }

    // 获取watermark数组偏移
    if (GetFieldOffset("zone", "_watermark", &m_zone_offsets.watermark[0]) != 0) {
        dprintf("WARNING: Failed to get offset of watermark\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取managed_pages字段偏移
    if (GetFieldOffset("zone", "managed_pages", &m_zone_offsets.managed_pages) != 0) {
        dprintf("WARNING: Failed to get offset of managed_pages\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取spanned_pages字段偏移
    if (GetFieldOffset("zone", "spanned_pages", &m_zone_offsets.spanned_pages) != 0) {
        dprintf("WARNING: Failed to get offset of spanned_pages\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取present_pages字段偏移
    if (GetFieldOffset("zone", "present_pages", &m_zone_offsets.present_pages) != 0) {
        dprintf("WARNING: Failed to get offset of present_pages\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取flags字段偏移
    if (GetFieldOffset("zone", "flags", &m_zone_offsets.flags) != 0) {
        dprintf("WARNING: Failed to get offset of zone flags\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取zone_start_pfn字段偏移
    if (GetFieldOffset("zone", "zone_start_pfn", &m_zone_offsets.zone_start_pfn) != 0) {
        dprintf("WARNING: Failed to get offset of zone_start_pfn\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取watermark_boost字段偏移
    if (GetFieldOffset("zone", "watermark_boost", &m_zone_offsets.watermark_boost) != 0) {
        dprintf("WARNING: Failed to get offset of watermark_boost\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取nr_reserved_highatomic字段偏移
    if (GetFieldOffset("zone", "nr_reserved_highatomic", &m_zone_offsets.nr_reserved_highatomic) != 0) {
        dprintf("WARNING: Failed to get offset of nr_reserved_highatomic\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取lowmem_reserve数组偏移
    if (GetFieldOffset("zone", "lowmem_reserve", &m_zone_offsets.lowmem_reserve[0]) != 0) {
        dprintf("WARNING: Failed to get offset of lowmem_reserve\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取cma_pages字段偏移
    if (GetFieldOffset("zone", "cma_pages", &m_zone_offsets.cma_pages) != 0) {
        dprintf("WARNING: Failed to get offset of cma_pages\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取pageset_high字段偏移
    if (GetFieldOffset("zone", "pageset_high", &m_zone_offsets.pageset_high) != 0) {
        dprintf("WARNING: Failed to get offset of pageset_high\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取pageset_batch字段偏移
    if (GetFieldOffset("zone", "pageset_batch", &m_zone_offsets.pageset_batch) != 0) {
        dprintf("WARNING: Failed to get offset of pageset_batch\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取name字段偏移
    if (GetFieldOffset("zone", "name", &m_zone_offsets.name) != 0) {
        dprintf("WARNING: Failed to get offset of zone name\n");
        m_zone_offsets.valid = FALSE;
    }

    // 获取free_area字段偏移
    if (GetFieldOffset("zone", "free_area", &m_zone_offsets.free_area) != 0) {
        dprintf("WARNING: Failed to get offset of free_area\n");
        m_zone_offsets.valid = FALSE;
    }

    return m_zone_offsets.valid;
}

// 读取并打印pglist_data结构体字段值
void PglistAnalyzer::ReadAndPrintFields(BOOL detailed_mode) {
    if (!m_offsets.valid) {
        dprintf("ERROR: Invalid offsets structure\n");
        return;
    }


    // === 基本信息 ===
     // 读取node_id
    ULONG64 node_id_addr = m_page_data_addr + m_offsets.node_id;
    int node_id = 0;
    if (!ExtReadMemory(node_id_addr, &node_id, sizeof(node_id), NULL)) {
        dprintf("  node_id         : ERROR - Failed to read\n");
    }
    // 读取node_start_pfn
    ULONG64 node_start_pfn_addr = m_page_data_addr + m_offsets.node_start_pfn;
    unsigned long node_start_pfn = 0;
    if (!ExtReadMemory(node_start_pfn_addr, &node_start_pfn, sizeof(node_start_pfn), NULL)) {
        dprintf("  node_start_pfn  : ERROR - Failed to read\n");
    }

    // 读取node_present_pages
    ULONG64 node_present_pages_addr = m_page_data_addr + m_offsets.node_present_pages;
    unsigned long node_present_pages = 0;
    if (!ExtReadMemory(node_present_pages_addr, &node_present_pages, sizeof(node_present_pages), NULL)) {
        dprintf("  node_present_pages: ERROR - Failed to read\n");
    }

    // 读取node_spanned_pages
    ULONG64 node_spanned_pages_addr = m_page_data_addr + m_offsets.node_spanned_pages;
    unsigned long node_spanned_pages = 0;
    if (!ExtReadMemory(node_spanned_pages_addr, &node_spanned_pages, sizeof(node_spanned_pages), NULL)) {
        dprintf("  node_spanned_pages: ERROR - Failed to read\n");
    }

    // 打印节点信息
    dprintf("Node[%d] start_pfn:%08lx  present_pages:%8lu  spanned_pages:%8lu\n",
        node_id, node_start_pfn, node_present_pages, node_spanned_pages);

    // 读取nr_zones(总是需要读取,用于zone数组遍历)
    ULONG64 nr_zones_addr = m_page_data_addr + m_offsets.nr_zones;
    int nr_zones = 0;
    if (!ExtReadMemory(nr_zones_addr, &nr_zones, sizeof(nr_zones), NULL)) {
        dprintf("ERROR: Failed to read nr_zones\n");
        return;
    }

    // 详细模式下显示额外信息
    if (detailed_mode) {
        dprintf("  nr_zones        : %d\n", nr_zones);

        // 读取并打印totalreserve_pages
        ULONG64 totalreserve_pages_addr = m_page_data_addr + m_offsets.totalreserve_pages;
        unsigned long totalreserve_pages = 0;
        if (ExtReadMemory(totalreserve_pages_addr, &totalreserve_pages, sizeof(totalreserve_pages), NULL)) {
            dprintf("  totalreserve_pages: %lu\n", totalreserve_pages);
        }
        else {
            dprintf("  totalreserve_pages: ERROR - Failed to read\n");
        }

        // === kswapd相关字段和flags ===

        // 读取并打印kswapd_order
        ULONG64 kswapd_order_addr = m_page_data_addr + m_offsets.kswapd_order;
        int kswapd_order = 0;
        if (ExtReadMemory(kswapd_order_addr, &kswapd_order, sizeof(kswapd_order), NULL)) {
            dprintf("  kswapd_order      : %d\n", kswapd_order);
        }
        else {
            dprintf("  kswapd_order      : ERROR - Failed to read\n");
        }

        // 读取并打印kswapd_highest_zoneidx
        ULONG64 kswapd_highest_zoneidx_addr = m_page_data_addr + m_offsets.kswapd_highest_zoneidx;
        int kswapd_highest_zoneidx = 0;
        if (ExtReadMemory(kswapd_highest_zoneidx_addr, &kswapd_highest_zoneidx, sizeof(kswapd_highest_zoneidx), NULL)) {
            dprintf("  kswapd_highest_zoneidx: %d\n", kswapd_highest_zoneidx);
        }
        else {
            dprintf("  kswapd_highest_zoneidx: ERROR - Failed to read\n");
        }

        // 读取并打印kswapd_failures
        ULONG64 kswapd_failures_addr = m_page_data_addr + m_offsets.kswapd_failures;
        int kswapd_failures = 0;
        if (ExtReadMemory(kswapd_failures_addr, &kswapd_failures, sizeof(kswapd_failures), NULL)) {
            dprintf("  kswapd_failures   : %d\n", kswapd_failures);
        }
        else {
            dprintf("  kswapd_failures   : ERROR - Failed to read\n");
        }

        // 读取并打印flags
        ULONG64 flags_addr = m_page_data_addr + m_offsets.flags;
        unsigned long flags = 0;
        if (ExtReadMemory(flags_addr, &flags, sizeof(flags), NULL)) {
            dprintf("  flags             : 0x%lx\n", flags);
        }
        else {
            dprintf("  flags             : ERROR - Failed to read\n");
        }
    }

    // 打印zone数组信息
    ReadAndPrintZones(nr_zones, detailed_mode);

}

// 获取zone类型名称
const char* PglistAnalyzer::GetZoneTypeName(ULONG64 zone_addr) {
    static char zone_name_buffer[64] = { 0 }; // 静态缓冲区存储zone名称

    if (!m_zone_offsets.valid) {
        return "ZONE_UNKNOWN";
    }

    // 读取name字段(这是一个指向字符串的指针)
    ULONG64 name_ptr_addr = zone_addr + m_zone_offsets.name;
    ULONG64 name_ptr = 0;

    if (!ExtReadMemory(name_ptr_addr, &name_ptr, sizeof(name_ptr), NULL)) {
        return "ZONE_READ_ERROR";
    }

    if (name_ptr == 0) {
        return "ZONE_NULL_NAME";
    }

    // 读取实际的字符串内容
    memset(zone_name_buffer, 0, sizeof(zone_name_buffer));
    if (!ExtReadMemory(name_ptr, zone_name_buffer, sizeof(zone_name_buffer) - 1, NULL)) {
        return "ZONE_STRING_ERROR";
    }

    // 确保字符串以null结尾
    zone_name_buffer[sizeof(zone_name_buffer) - 1] = '\0';

    return zone_name_buffer;
}

// 读取并打印zone数组信息
void PglistAnalyzer::ReadAndPrintZones(int nr_zones, BOOL detailed_mode) {
    if (!m_zone_offsets.valid) {
        dprintf("ERROR: Invalid zone offsets structure\n");
        return;
    }

    // 计算zone数组的起始地址
    ULONG64 zones_array_addr = m_page_data_addr + m_offsets.node_zones;

    for (int i = 0; i < nr_zones; i++) {
        // 计算当前zone的地址
        ULONG64 current_zone_addr = zones_array_addr + (i * m_zone_size);

        // 读取zone_start_pfn
        ULONG64 zone_start_pfn_addr = current_zone_addr + m_zone_offsets.zone_start_pfn;
        ULONG64 zone_start_pfn = 0;
        ExtReadMemory(zone_start_pfn_addr, &zone_start_pfn, 8, NULL);

        // 读取present_pages
        ULONG64 present_pages_addr = current_zone_addr + m_zone_offsets.present_pages;
        ULONG64 present_pages = 0;
        ExtReadMemory(present_pages_addr, &present_pages, 8, NULL);

        // 读取spanned_pages
        ULONG64 spanned_pages_addr = current_zone_addr + m_zone_offsets.spanned_pages;
        ULONG64 spanned_pages = 0;
        ExtReadMemory(spanned_pages_addr, &spanned_pages, 8, NULL);

        dprintf("  Zone[%-6s] start_pfn:%08I64x   present_pages:%8I64u   spanned_pages:%8I64u\n",
            GetZoneTypeName(current_zone_addr), zone_start_pfn, present_pages, spanned_pages);

        // 详细模式下显示Zone详细信息
        if (detailed_mode) {

            // === 基本信息 ===

        // 读取flags
            ULONG64 zone_flags_addr = current_zone_addr + m_zone_offsets.flags;
            ULONG64 zone_flags = 0;
            if (ExtReadMemory(zone_flags_addr, &zone_flags, 8, NULL)) {
                dprintf("    flags          : 0x%I64x\n", zone_flags);
            }
            else {
                dprintf("    flags          : ERROR - Failed to read\n");
            }

            // 读取managed_pages
            ULONG64 managed_pages_addr = current_zone_addr + m_zone_offsets.managed_pages;
            ULONG64 managed_pages = 0;
            if (ExtReadMemory(managed_pages_addr, &managed_pages, 8, NULL)) {
                dprintf("    managed_pages  : %I64u\n", managed_pages);
            }
            else {
                dprintf("    managed_pages  : ERROR - Failed to read\n");
            }



            // 读取cma_pages
            ULONG64 cma_pages_addr = current_zone_addr + m_zone_offsets.cma_pages;
            ULONG64 cma_pages = 0;
            if (ExtReadMemory(cma_pages_addr, &cma_pages, 8, NULL)) {
                dprintf("    cma_pages      : %I64u\n", cma_pages);
            }
            else {
                dprintf("    cma_pages      : ERROR - Failed to read\n");
            }

            // === 水位线和保留信息 ===
            // 注意:Linux内核中unsigned long是8字节,而Windows环境中是4字节
            ULONG64 watermarks[3] = { 0 };
            BOOL watermark_success = TRUE;
            for (int j = 0; j < 3; j++) {
                ULONG64 watermark_addr = current_zone_addr + m_zone_offsets.watermark[0] + (j * 8); // 使用8字节偏移
                if (!ExtReadMemory(watermark_addr, &watermarks[j], 8, NULL)) { // 读取8字节
                    watermark_success = FALSE;
                    break;
                }
            }

            if (watermark_success) {
                dprintf("    watermark[MIN/LOW/HIGH]: %I64u/%I64u/%I64u\n",
                    watermarks[0], watermarks[1], watermarks[2]);
            }
            else {
                dprintf("    watermark[MIN/LOW/HIGH]: ERROR - Failed to read\n");
            }

            // 读取watermark_boost
            ULONG64 watermark_boost_addr = current_zone_addr + m_zone_offsets.watermark_boost;
            ULONG64 watermark_boost = 0;
            if (ExtReadMemory(watermark_boost_addr, &watermark_boost, 8, NULL)) {
                dprintf("    watermark_boost: %I64u\n", watermark_boost);
            }
            else {
                dprintf("    watermark_boost: ERROR - Failed to read\n");
            }

            // 读取nr_reserved_highatomic
            ULONG64 nr_reserved_highatomic_addr = current_zone_addr + m_zone_offsets.nr_reserved_highatomic;
            ULONG64 nr_reserved_highatomic = 0;
            if (ExtReadMemory(nr_reserved_highatomic_addr, &nr_reserved_highatomic, 8, NULL)) {
                dprintf("    nr_reserved_highatomic: %I64u\n", nr_reserved_highatomic);
            }
            else {
                dprintf("    nr_reserved_highatomic: ERROR - Failed to read\n");
            }

            // 读取lowmem_reserve数组
            ULONG64 lowmem_reserve[4] = { 0 };
            BOOL lowmem_reserve_success = TRUE;
            for (int j = 0; j < 4; j++) {
                ULONG64 lowmem_reserve_addr = current_zone_addr + m_zone_offsets.lowmem_reserve[0] + (j * 8);
                if (!ExtReadMemory(lowmem_reserve_addr, &lowmem_reserve[j], 8, NULL)) {
                    lowmem_reserve_success = FALSE;
                    break;
                }
            }

            if (lowmem_reserve_success) {
                dprintf("    lowmem_reserve : [%I64u, %I64u, %I64u, %I64u]\n",
                    lowmem_reserve[0], lowmem_reserve[1], lowmem_reserve[2], lowmem_reserve[3]);
            }
            else {
                dprintf("    lowmem_reserve : ERROR - Failed to read\n");
            }

            // === 配置参数 ===
            // 读取pageset_high
            ULONG64 pageset_high_addr = current_zone_addr + m_zone_offsets.pageset_high;
            ULONG pageset_high = 0;
            if (ExtReadMemory(pageset_high_addr, &pageset_high, 4, NULL)) {
                dprintf("    pageset_high   : %u\n", pageset_high);
            }
            else {
                dprintf("    pageset_high   : ERROR - Failed to read\n");
            }

            // 读取pageset_batch
            ULONG64 pageset_batch_addr = current_zone_addr + m_zone_offsets.pageset_batch;
            ULONG pageset_batch = 0;
            if (ExtReadMemory(pageset_batch_addr, &pageset_batch, 4, NULL)) {
                dprintf("    pageset_batch  : %u\n", pageset_batch);
            }
            else {
                dprintf("    pageset_batch  : ERROR - Failed to read\n");
            }

            // === free_area数组信息 ===
            ULONG64 free_area_base_addr = current_zone_addr + m_zone_offsets.free_area;
            dprintf("    free_area (free page frames count):\n");

            for (int order = 0; order < 11; order++) { // MAX_ORDER通常为11
                // 计算当前order的free_area结构体地址
                ULONG64 current_free_area_addr = free_area_base_addr + (order * m_free_area_size);

                // 获取nr_free字段偏移(通常在free_list数组之后)
                ULONG nr_free_offset = 0;
                if (GetFieldOffset("free_area", "nr_free", &nr_free_offset) == 0) {
                    ULONG64 nr_free_addr = current_free_area_addr + nr_free_offset;
                    unsigned long nr_free = 0;

                    if (ExtReadMemory(nr_free_addr, &nr_free, sizeof(nr_free), NULL)) {
                        dprintf("      order[%2d]     : %lu free pages\n", order, nr_free);
                    }
                    else {
                        dprintf("      order[%2d]     : ERROR - Failed to read\n", order);
                    }
                }
                else {
                    dprintf("      order[%2d]     : ERROR - Failed to get nr_free offset\n", order);
                }
            }

        }
    }
}


DECLARE_API(pglist) {

    // 解析命令行参数
    BOOL detailed_mode = FALSE;

    // 检查是否有-v参数或帮助命令
    if (args && strlen(args) > 0) {
        // 检查帮助命令
        if (strcmp(args, "-h") == 0 || strcmp(args, "--help") == 0 || strcmp(args, "?") == 0) {
            dprintf("Linux Kernel Page List Commands:\n");
            dprintf("  !ndx.pglist                          - Display basic page list information\n");
            dprintf("  !ndx.pglist -v                       - Display detailed page list information\n");
            dprintf("  !ndx.pglist -h, --help, ?            - Show this help information\n");
            return;
        }
        // 简单的参数解析,查找-v
        if (strstr(args, "-v") != NULL) {
            detailed_mode = TRUE;
        }
    }

    // 创建PglistAnalyzer对象
    PglistAnalyzer analyzer;

    // 初始化分析器
    if (!analyzer.Initialize()) {
        dprintf("ERROR: Failed to initialize PglistAnalyzer\n");
        return;
    }

    analyzer.ReadAndPrintFields(detailed_mode);
}

4. 实例测试

!ndx.pglist

!ndx.pglist -v

作者:郭建程  创建时间:2025-09-22 13:48
最后编辑:郭建程  更新时间:2025-09-22 21:32