返回全部文章
RESEARCH NOTES / 04系统研究 · 崩溃分析

minidump 调研

系统梳理 kdump、virsh dump 与 makedumpfile 的工作原理,并深入 ELF、位图过滤和内存页判定流程。

本文提及的几个技术

kdump

kdump 是提供崩溃转储机制的服务。该服务可以保存系统内存内容进行分析。

kdump 使用 kexec 系统调用在不重启的情况下引导至第二个内核(捕获内核),然后捕获崩溃内核的内存( coredump 或 vmcore )的内容,并将其保存到文件中。这个第二个内核位于系统内存保留的一部分。

kdump 内核崩溃转储指南

virsh dump

virsh dump是 libvirt的一个coredump 工具。virsh 会向 libvirt 发送 API 请求,libvirt 调用QEMU执行dump-guest-memory 命令开始实际的内存拷贝工作。

libvirt 通过调用 makedumpfile工具智能分析 vmcore 文件,实现过滤和压缩 vmcore 文件。

virsh dump指令

使用 virsh dump创建 guest 虚拟机内核的转储文件

文件大小控制策略

内存过滤

makedumpfile使用 5 个 bit 位来设置过滤 5 种内存类型:

选项描述
1(0x00001)零页
2(0x00010)缓存页
4(0x00100)缓存私有页
8(0x01000)用户页
16(0x10000)可用页

文件压缩

virtio dump 提供了多种压缩格式:

您可以使用 --format=格式 选项保存只内存转储。可用的格式如下:

ELF - 默认未压缩格式

kdump-zlib - kdump 压缩格式 zlib 压缩

kdump-lzo - kdump 压缩格式使用 LZO 压缩

kdump-snappy - kdump 压缩格式使用 Snappy 压缩

使用压缩后只能使用 crash工具分析(默认格式),要使用 GDB 分析需指定格式为 ELF。

makedumpfile原理

1. 读取 coredump 和 vmlinux 数据

makedumpfile 首先解析coredump文件头,了解内存布局。

makedumpfile 解析 vmlinux 的内核符号地址(例如内核代码段地址/全局变量地址),数据结构定义(struct page/pg_data_t/zone/mem_map)以及内核配置(PAGE_SIZE/MAX_PHYSMEM_BITS)

2. 建立内存位图

初始化位图:开始时,位图被初始化,标记所有内存页为“待移除”。

标记基础保留页,其中包括 kernel 的代码段和静态数据段/栈段/dmesg buffer/其他kernel 数据结构

3. 遍历和过滤内存页

根据用户指定的转储级别 (-d level) 来决定过滤策略。转储级别是一个 32 位的整数,其中每一位都代表一种类型的页面是否要被排除。

这里分为几个部分:

  • 识别 Zero Pages (空闲页):这是最基础的过滤。makedumpfile 会逐页读取 vmcore 源,如果一个页面的所有字节都是零,就在位图中将该页标记为“移除”。这是 -d 1 的功能。

  • 遍历页帧数组 (mem_map):为了进行更高级的过滤,makedumpfile 必须模拟内核的 page 管理机制。它会定位到内核的 mem_map 数组(在较新的内核中,这是通过 pg_data_t 结构体链表来管理的)。这个数组包含了描述每一个物理页帧(Physical Page Frame)状态的 struct page 结构体。

  • 分析 struct page:通过检查每个 struct page 结构体的标志位 (flags) 和引用计数 (_refcount),makedumpfile 可以判断出这个页的类型:

    • 缓存页 (Cache Pages):如果页面被标记为在 radix tree 或 xarray 中用于文件缓存,并且没有被进程映射(page->mapping != NULL 且 page->_refcount 符合特定条件),则可以被标记为“移除”。这是 -d 2 的功能。
    • 私有缓存页 (Cache Private):同上,但包含了一些私有的、可能非共享的缓存页。这是 -d 4 的功能。
    • 用户态数据页 (User Data Pages):如果页面被识别为属于某个用户进程的私有数据(例如,通过反向页表映射 rmap 信息判断),则可以被标记为“移除”。这是 -d 8 的功能。
    • 空闲页 (Free Pages):通过分析内核的伙伴系统(Buddy Allocator)数据结构,可以直接识别出哪些页面是空闲的,并将它们标记为“移除”。这是 -d 16 的功能。

4. 压缩文件

此时 makedumpfile知道最终输出文件应该包含哪些页,接下来就是生成最终的 vmcore 文件。

  • 读取-过滤-压缩-写入:makedumpfile 会再次从头读取原始的 vmcore 源文件。

    • 对于每一个页,它会查询之前建立的内存位图。
    • 如果位图显示该页需要“保留”,它就将该页的数据读入缓冲区。
    • 如果位图显示该页需要“移除”,它就直接跳过,不读取该页。
    • 收集到一定数量的“保留”页(形成一个数据块,Block)后,调用指定的压缩库(如 zlib, lzo, snappy)对这个数据块进行压缩。
    • 将压缩后的数据块写入到新的输出文件中。
  • 生成新文件头:输出的文件也是有特定格式的。makedumpfile 会生成一个新的文件头(可以是 kdump 的压缩格式头,也可以是 ELF 格式头),这个头中包含了如何解压和重构内存布局的元数据。分析工具(如 crash)读取这个文件时,会首先解析头部,然后按需解压数据块来还原出分析所需的内存内容。

makedumpfile原理

页面标志位判断

c
#define PG_arch_1       (9)     // 架构相关页面
#define PG_reserved     (10)    // 保留页面
#define PG_private      (11)    // 私有页面
#define PG_private_2    (12)    // 私有页面2
#define PG_writeback    (13)    // 回写页面
#define PG_head         (14)    // 复合页头部
#define PG_swapcache    (15)    // 交换缓存页面
#define PG_mappedtodisk (16)    // 映射到磁盘的页面
#define PG_reclaim      (17)    // 回收页面
#define PG_swapbacked   (18)    // 交换支持页面
#define PG_unevictable  (19)    // 不可驱逐页面
#define PG_mlocked      (20)    // 内存锁定页面
#define PG_uncached     (21)    // 未缓存页面
#define PG_hwpoison     (22)    // 硬件中毒页面

转储级别控制定义

c
#define DL_EXCLUDE_ZERO         (0x001)   // 排除零页面
#define DL_EXCLUDE_CACHE        (0x002)   // 排除缓存页面
#define DL_EXCLUDE_CACHE_PRI    (0x004)   // 排除私有缓存页面
#define DL_EXCLUDE_USER_DATA    (0x008)   // 排除用户数据页面
#define DL_EXCLUDE_FREE         (0x010)   // 排除空闲页面

检查函数

c
// 检查是否为缓存页面		int is_cache_page(unsigned long flags)
// 检查是否为空闲页面 		int is_free_page(unsigned long flags)
// 检查是否为硬件中毒页面     int is_hwpoison_page(unsigned long flags)
// 检查是否为零页面			 int is_zero_page(char *buf, long page_size)

is_dumpable() - 核心判断函数

  • 检查页面是否在可转储的物理内存范围内
  • 根据转储级别过滤不同类型的页面
  • 检查页面标志位决定是否转储

典型的页面判断流程:

  1. 物理地址检查

    1. 检查页面是否在有效的物理内存范围内
    2. 排除MMIO区域和其他非RAM区域
  2. 页面标志位分析

    1. 读取页面的flags字段
    2. 根据PG_*标志位判断页面类型和状态
  3. 转储级别过滤

    1. 根据用户指定的转储级别(-d参数)
    2. 决定是否排除特定类型的页面
  4. 特殊页面处理

    1. 零页面检测和处理
    2. 硬件中毒页面的排除
    3. 内核关键数据结构的保留

内存范围定义

c
// 内存范围描述
struct memory_range {
    unsigned long long start;
    unsigned long long end;
};

// 页面描述符相关信息
struct page_desc {
    unsigned long flags;
    unsigned int _count;
    unsigned int _mapcount;
    // ... 其他字段
};

具体流程

两级位图:

  • 第一级位图(1st bitmap): 初步标记可能需要转储的页面

  • 第二级位图(2nd bitmap): 经过详细分析后,最终确定需要转储的页面

  • check_param_for_creating_dumpfile(argc, argv) # 验证命令行参数

  • check_dump_file(info->name_dumpfile) #路径检查

  • create_dumpfile()

    • open_files_for_creating_dumpfile()

      • open_vmcoreinfo("r") | open_kernel_file # 要么读取vmcoreinfo,要么读取 vmlinux

      • open_dump_memory()

        • check_kdump_compressed(info->name_memory) # pass
        • get_kdump_compressed_header_info(info->name_memory) # pass
        • check_and_get_sadump_header_info(info->name_memory) # pass
    • get_elf_info(info->fd_memory, info->name_memory) # 读取 ELF 数据

      • check_elf_format(fd, filename, &phnum, &num_pt_loads)

      • get_phdr_memory(i, &phdr)

        • get_elf64_phdr(fd_memory, name_memory, index, phdr)
      • dump_Elf_load(&phdr, j)

    • initial()

      • read_vmcoreinfo()

      • or

      • get_symbol_info()

      • get_structure_info()

      • get_srcfile_info()

      • page_size

      • get_phys_base() # phys_base

      • get_max_mapnr()

        • get_max_paddr()
        • paddr_to_pfn(roundup(max_paddr, PAGESIZE()))
      • get_machdep_info() # only for xen

      • calibrate_machdep_info() # MAX_PHYSMEM_BITS, SECTION_SIZE_BITS

      • check_release() # system_utsname kernel版本检查

      • get_versiondep_info()

        • max_physmem_bits

        • get_page_offset_x86_64() # page_offset_base
        • info->vmemmap_start = VMEMMAP_START_2_6_31
        • info->vmemmap_end = VMEMMAP_END_2_6_31
      • get_numnodes() # 获取numa节点数

      • get_mem_map()

      • setup_page_is_buddy() # 设置处理的内存页为 buddy

      • init_compound_offset() # compound_order_offset

    • open_dump_bitmap() # 创建dump文件

      • info->fd_bitmap = fd # 创建位图文件
    • find_vmemmap()

      • find_vmemmap_x86_64()

        • init_level4_pgt or init_top_pgt 顶级页表

        • sme_mask 如果系统支持 AMD SME(安全内存加密),则从页表项掩码中排除加密位

        • info->vmemmap_start = info->mem_map_data[0].mem_map

        • pagestructsize = size_table.page; // struct page 结构体的大小

        • hugepagesize = PTRS_PER_PMD * info->page_size; // 大页(huge page)的大小

        • vaddr_base = info->vmemmap_start; // 内核存放stract page的虚拟内存区域的起始地址

        • max_paddr = get_max_paddr(); // 获取系统中的最大物理地址,用于确定需要多少个页结构体

        • high_pfn = max_paddr >> 12; // 计算页帧号 PFN,即需要多少个页结构体。页结构体信息将映射到VMEMMAP_START(info->vmemmap_start)

        • pgd_index = pgd_index(vaddr_base); // 计算 vmemmap addr 在页全局目录PGD中的索引

        • pgd_addr = vaddr_to_paddr(init_level4_pgt); // 将顶级页表的虚拟地址转换为物理地址,即获取 PGD 的物理地址

          • vtop4_x86_64(vaddr)

            • pagetable = init_level4_pgt - __START_KERNEL_map + info->phys_base # init_level4_pgt, phys_base
            • __vtop4_x86_64(vaddr, pagetable) // 使用 4 级分页将虚拟地址转换为物理地址
        • pgd_addr += pgd_index * sizeof(unsigned long); // 计算指向 vmemmap_start 的 PGD 表型的物理地址,每个表项占用一个unsigned long(64 位系统 8 字节)地址空间

        • page_structs_per_pud = (PTRS_PER_PUD * PTRS_PER_PMD * info->page_size) / pagestructsize; // 计算每个页上级目录 PUD 能容纳多少页结构体

        • num_puds = (high_pfn + page_structs_per_pud - 1) / page_structs_per_pud; // 计算需要的 PUD 数量,向上取整

        • pvaddr = VMEMMAP_START; // 初始化当前处理的虚拟地址

        • structsperhpage = hugepagesize / pagestructsize; // 计算每个大页能容纳的页结构体数量

        • 循环获取连续内存页

    • create_dump_bitmap()

      • info->flag_cyclic == TRUE

        • prepare_bitmap2_buffer()
      • info->flag_cyclic == FALSE

        • prepare_bitmap_buffer()

          • prepare_bitmap1_buffer()
          • prepare_bitmap2_buffer()
        • create_1st_bitmap()

        • create_2nd_bitmap()

          • exclude_nodata_pages(cycle)

          • exclude_unnecessary_pages(cycle) // DL_EXCLUDE_CACHE | DL_EXCLUDE_CACHE_PRI | DL_EXCLUDE_USER_DATA | NUMBER(PG_hwpoison) != NOT_FOUND_NUMBER

            • __exclude_unnecessary_pages
          • exclude_free_page(cycle) // DL_EXCLUDE_FREE

          • exclude_zero_pages_cyclic(cycle)

        • info->num_dumpable = get_num_dumpable_cyclic()

          • get_num_dumpable_cyclic_single()
    • writeout_dumpfile()

      • open_dump_file()

      • prepare_cache_data(&cd_header)

      • prepare_cache_data(&cd_page)

      • info->flag_elf_dumpfile == TRUE

        • write_elf_header(&cd_header) // 写 ELF 文件头

          • num_loads_dumpfile = get_loads_dumpfile_cyclic()
        • write_elf_pages_cyclic(&cd_header, &cd_page) // 写 ELF 页面数据

        • write_elf_eraseinfo(&cd_header) // 写擦除信息

    • print_report()

    • clear_filter_info()

    • close_files_for_creating_dumpfile()

ELF 文件格式

CODE
+------------------+  偏移 0
| ELF Header       |  (Elf64_Ehdr 或 Elf32_Ehdr)
+------------------+  
| Program Headers  |  (包含 PT_NOTE + PT_LOAD 段)
+------------------+  
| Section Header   |  (仅在扩展编号时存在)
| (可选)           |
+------------------+  offset_note_dumpfile
| PT_NOTE Segment  |  (包含原始NOTE + 擦除信息)
+------------------+  info->offset_load_dumpfile
| PT_LOAD Segments |  (实际的内存页面数据)
+------------------+

Elf_Phdr文件格式:

c
typedef struct {
    uint32_t    p_type;      /* Segment type */
    uint32_t    p_flags;     /* Segment flags */
    uint64_t    p_offset;    /* Segment file offset */
    uint64_t    p_vaddr;     /* Segment virtual address */
    uint64_t    p_paddr;     /* Segment physical address */
    uint64_t    p_filesz;    /* Segment size in file */
    uint64_t    p_memsz;     /* Segment size in memory */
    uint64_t    p_align;     /* Segment alignment */
} Elf64_Phdr;

pt_load_segment 格式:

c
struct pt_load_segment {
	off_t			file_offset;		// 该段在文件中的偏移位置
	off_t			file_size;			// 该段在文件中的大小
	unsigned long long	phys_start;		// 物理内存起始地址
	unsigned long long	phys_end;		// 物理内存结束地址
	unsigned long long	virt_start;		// 虚拟内存起始地址
	unsigned long long	virt_end;		// 虚拟内存结束地址
};
// Elf64_Phdr 转换为 pt_load_segment
pls->phys_start  = prog->p_paddr;
pls->phys_end    = pls->phys_start + prog->p_memsz;
pls->virt_start  = prog->p_vaddr;
pls->virt_end    = pls->virt_start + prog->p_memsz;
pls->file_offset = prog->p_offset;
pls->file_size   = prog->p_filesz;

page flags

c
/*
 * Page flags
 */
long	PG_lru;
long	PG_private;
long	PG_head;
long	PG_head_mask;
long	PG_swapcache;
long	PG_swapbacked;
long	PG_buddy;
long	PG_slab;
long    PG_hwpoison;
long    PG_hugetlb;

Tips

  1. 要使用 vmlinux 获取过滤内存的 coredump 信息,需要为 vmlinux 开启 CONFIG_DEBUG_INFO 和 CONFIG_KEXEC 编译配置,会导致 vmlinux 文件大小从 50M 增加到 200 M。这里可以提前使用 vmliux 生成一份 vmcoreinfo,大小为 2K
CODE
makedumpfile -g vmcoreinfo -x vmlinux

配置要求

CODE
CONFIG_DEBUG_INFO=y

CONFIG_DEBUG_INFO_REDUCED
CONFIG_DEBUG_INFO_COMPRESSED
CONFIG_DEBUG_INFO_SPLIT

CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_DEBUG_INFO_DWARF4 is not set
CONFIG_DEBUG_INFO_DWARF5 is not set

CONFIG_PANIC_ON_OOPS=y
CONFIG_PANIC_ON_OOPS_VALUE=1

# 符号信息
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y

# 
CONFIG_WATCHDOG=y
CONFIG_CLOCKSOURCE_WATCHDOG=y
CONFIG_WATCHDOG_CORE=y
CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y
CONFIG_WATCHDOG_OPEN_TIMEOUT=0

测试

minidump 速度和压缩大小测试:

规格过滤等级minidump 文件大小耗时
2C0.5G1101M10.212s
2513M9.797s
4511M9.749s
8513M9.710s
16111M9.450s
3183M9s
规格过滤等级minidump 文件大小耗时
2C0.5G1103M6.699s
2513M6.417s
4511M6.351s
8513M6.321s
16116M6.023s
3191M6.136s

panic测试

CODE
echo c | tee /proc/sysrq-trigger

soft lockup 测试

CODE
insmod soft_lockup.ko

提示

  1. 没有 vmlinux 时, 可以使用 vmcoreinfo
shell
cat /proc/kallsyms # 备用查看kernel信息
  1. 启用过滤配置时,必须读取与崩溃内核匹配的 vmlinux。

  2. info 各项的含义

c
struct DumpInfo {
	int32_t		kernel_version;      /* version of first kernel*/
	struct timeval	timestamp;
	struct utsname	system_utsname;

	/*
	 * General info:
	 */
	int		dump_level;          /* current dump level */
	int		max_dump_level;      /* maximum dump level */
	int		num_dump_level;      /* number of dump level */
	int		array_dump_level[NUM_ARRAY_DUMP_LEVEL];
	off_t		size_limit;          /* dump file size limit */
	int		flag_compress;       /* flag of compression */
	int		flag_lzo_support;    /* flag of LZO compression support */
	int		flag_elf_dumpfile;   /* flag of creating ELF dumpfile */
	int		flag_generate_vmcoreinfo;/* flag of generating vmcoreinfo file */
	int		flag_read_vmcoreinfo;    /* flag of reading vmcoreinfo file */
	int		flag_show_usage;     /* flag of showing usage */
	int		flag_show_version;   /* flag of showing version */
	int		flag_check_params;   /* only check parameters */
	int		flag_flatten;        /* flag of outputting flattened
						format to a standard out */
	int		flag_rearrange;      /* flag of creating dumpfile from
						flattened format */
	int		flag_split;	     /* splitting vmcore */
	int		flag_cyclic;	     /* multi-cycle processing is necessary */
	int		flag_usemmap;	     /* /proc/vmcore supports mmap(2) */
	int		flag_reassemble;     /* reassemble multiple dumpfiles into one */
	int		flag_refiltering;    /* refilter from kdump-compressed file */
	int		flag_force;	     /* overwrite existing stuff */
	int		flag_exclude_xen_dom;/* exclude Domain-U from xen-kdump */
	int             flag_dmesg;          /* dump the dmesg log out of the vmcore file */
	int             flag_partial_dmesg;  /* dmesg dump only from the last cleared index*/
	int             flag_mem_usage;  /*show the page number of memory in different use*/
	int		flag_use_printk_log; /* did we read printk_log symbol name? */
	int		flag_use_printk_ringbuffer; /* using lockless printk ringbuffer? */
	int		flag_nospace;	     /* the flag of "No space on device" error */
	int		flag_vmemmap;        /* kernel supports vmemmap address space */
	int		flag_excludevm;      /* -e - excluding unused vmemmap pages */
	int		flag_use_count;      /* _refcount is named _count in struct page */
	int		flag_dry_run;        /* do not create a vmcore file */
	unsigned long	vaddr_for_vtop;      /* virtual address for debugging */
	long		page_size;           /* size of page */
	long		page_shift;
	mdf_pfn_t	max_mapnr;   /* number of page descriptor */
	unsigned long   page_offset;
	unsigned long   section_size_bits;
	unsigned long   max_physmem_bits;
	unsigned long   sections_per_root;
	unsigned long	phys_base;
	unsigned long   kernel_start;
	unsigned long   vmalloc_start;
	unsigned long   vmalloc_end;
	unsigned long	vmemmap_start;
	unsigned long	vmemmap_end;
	int		vmemmap_psize;
	int		vmemmap_cnt;
	struct ppc64_vmemmap	*vmemmap_list;
	unsigned long	kaslr_offset;

	/*
	 * for compound page
	 */
	unsigned int	compound_order_offset;
	unsigned int	compound_dtor_offset;

	/*
	 * Filter config file containing filter commands to filter out kernel
	 * data from vmcore.
	 */
	char		*name_filterconfig;
	FILE		*file_filterconfig;

	/*
	 * Filter config file containing eppic language filtering rules
	 * to filter out kernel data from vmcore
	 */
	char		*name_eppic_config;

	/*
	 * diskdimp info:
	 */
	int		block_order;
	off_t		offset_bitmap1;
	unsigned long	len_bitmap;          /* size of bitmap(1st and 2nd) */
	struct dump_bitmap 		*bitmap1;
	struct dump_bitmap 		*bitmap2;
	struct disk_dump_header		*dump_header;
	struct kdump_sub_header		sub_header;

	/*
	 * ELF header info:
	 */
	unsigned int		num_load_dumpfile;
	size_t			offset_load_dumpfile;

	/*
	 * mem_map info:
	 */
	unsigned int		num_mem_map;
	struct mem_map_data	*mem_map_data;

	int			fd_vmlinux;
	char			*name_vmlinux;

	int			fd_xen_syms;
	char			*name_xen_syms;

	/*
	 * Dump memory image info:
	 */
	int			fd_memory;						// elf fd
	char			*name_memory;				// elf file
	struct disk_dump_header	*dh_memory;
	struct kdump_sub_header	*kh_memory;
	struct dump_bitmap 		*bitmap_memory;
	unsigned long			*valid_pages;

	/*
	 * Dump file info:
	 */
	int			fd_dumpfile;
	char			*name_dumpfile;
	int			num_dumpfile;
	struct splitting_info	*splitting_info;
	struct parallel_info	*parallel_info;

	/*
	 * bitmap info:
	 */
	int			fd_bitmap;
	char			*name_bitmap;

	/*
	 * vmcoreinfo file info:
	 */
	FILE			*file_vmcoreinfo;
	char			*name_vmcoreinfo;	     /* vmcoreinfo file */
	char			release[STRLEN_OSRELEASE];
	int			read_text_vmcoreinfo;

	/*
	 * ELF NOTE section in dump memory image info:
	 */
	off_t			offset_note_dumpfile;

	/*
	 * erased information in dump memory image info:
	 */
	unsigned long           size_elf_eraseinfo;

	/*
	 * for splitting
	 */
	mdf_pfn_t split_start_pfn;
	mdf_pfn_t split_end_pfn;

	/*
	 * for cyclic processing
	 */
	char	           *working_dir;	     /* working directory for bitmap */
	mdf_pfn_t          num_dumpable;
	unsigned long      bufsize_cyclic;
	unsigned long      pfn_cyclic;

	/*
	 * for mmap
	 */
	char	*mmap_buf;
	off_t	mmap_start_offset;
	off_t	mmap_end_offset;
	off_t   mmap_region_size;

	/*
	 * sadump info:
	 */
	int flag_sadump_diskset;
	enum sadump_format_type flag_sadump;         /* sadump format type */
	/*
	 * for filtering free pages managed by buddy system:
	 */
	int (*page_is_buddy)(unsigned long flags, unsigned int _mapcount,
			     unsigned long private, unsigned int _count);
	/*
	 * for cyclic_splitting mode, setup splitblock_size
	 */
	long long splitblock_size;
	/*
	 * for parallel process
	 */
	int num_threads;
	int num_buffers;
	pthread_t **threads;
	struct thread_args *kdump_thread_args;
	struct page_data *page_data_buf;
	struct page_flag **page_flag_buf;
	sem_t page_flag_buf_sem;
	pthread_rwlock_t usemmap_rwlock;
	mdf_pfn_t current_pfn;
	pthread_mutex_t current_pfn_mutex;
	pthread_mutex_t page_data_mutex;
	pthread_mutex_t filter_mutex;
};
  1. vmcoreinfo 所需的信息
CODE
OSRELEASE=6.12.8+
PAGESIZE=4096
SYMBOL(mem_section)=ffffffff82f32658
SYMBOL(init_uts_ns)=ffffffff8299e260
SYMBOL(_stext)=ffffffff81000000
SYMBOL(level4_kernel_pgt)=ffffffff82232000
SYMBOL(init_top_pgt)=ffffffff8222e000
SYMBOL(vmlist)=ffffffff82b4f9a8
SYMBOL(node_states)=ffffffff829b89e0
SYMBOL(node_data)=ffffffff82f39be0
SYMBOL(prb)=ffffffff82254510
SYMBOL(clear_seq)=ffffffff82ecc020
SYMBOL(log_buf)=ffffffff82954588
SYMBOL(log_buf_len)=ffffffff82954580
SYMBOL(max_pfn)=ffffffff82ed5628
SYMBOL(high_memory)=ffffffff82ed1c80
SIZE(page)=64
SIZE(mem_section)=16
SIZE(pglist_data)=172608
SIZE(zone)=1536
SIZE(free_area)=88
SIZE(list_head)=16
SIZE(nodemask_t)=128
SIZE(printk_ringbuffer)=88
SIZE(prb_desc)=24
SIZE(printk_info)=88
SIZE(latched_seq)=24
OFFSET(page.flags)=0
OFFSET(page._refcount)=52
OFFSET(page.mapping)=24
OFFSET(page.lru)=8
OFFSET(page._mapcount)=48
OFFSET(page.private)=40
OFFSET(page.compound_head)=8
OFFSET(mem_section.section_mem_map)=0
OFFSET(pglist_data.node_zones)=0
OFFSET(pglist_data.nr_zones)=171552
OFFSET(pglist_data.node_start_pfn)=171560
OFFSET(pglist_data.node_spanned_pages)=171576
OFFSET(zone.free_area)=256
OFFSET(zone.vm_stat)=1344
OFFSET(zone.spanned_pages)=160
OFFSET(free_area.free_list)=0
OFFSET(list_head.next)=0
OFFSET(list_head.prev)=8
OFFSET(vm_struct.addr)=8
OFFSET(vmap_area.va_start)=0
OFFSET(vmap_area.list)=40
OFFSET(printk_ringbuffer.desc_ring)=0
OFFSET(printk_ringbuffer.text_data_ring)=48
OFFSET(prb_desc_ring.count_bits)=0
OFFSET(prb_desc_ring.descs)=8
OFFSET(prb_desc_ring.infos)=16
OFFSET(prb_desc_ring.head_id)=24
OFFSET(prb_desc_ring.tail_id)=32
OFFSET(prb_desc.state_var)=0
OFFSET(prb_desc.text_blk_lpos)=8
OFFSET(prb_data_blk_lpos.begin)=0
OFFSET(prb_data_blk_lpos.next)=8
OFFSET(prb_data_ring.size_bits)=0
OFFSET(prb_data_ring.data)=8
OFFSET(printk_info.ts_nsec)=8
OFFSET(printk_info.text_len)=16
OFFSET(printk_info.caller_id)=20
OFFSET(atomic_long_t.counter)=0
OFFSET(latched_seq.val)=8
OFFSET(uts_namespace.name)=0
LENGTH(node_data)=9223372036854775806
LENGTH(zone.free_area)=11
LENGTH(free_area.free_list)=5
NUMBER(NR_FREE_PAGES)=0
NUMBER(N_ONLINE)=1
NUMBER(PG_lru)=5
NUMBER(PG_private)=14
NUMBER(PG_head_mask)=64
NUMBER(PG_swapcache)=10
NUMBER(PG_swapbacked)=17
SRCFILE(pud_t)=./arch/x86/include/asm/pgtable_types.h