本文提及的几个技术
kdump
kdump 是提供崩溃转储机制的服务。该服务可以保存系统内存内容进行分析。
kdump 使用 kexec 系统调用在不重启的情况下引导至第二个内核(捕获内核),然后捕获崩溃内核的内存( coredump 或 vmcore )的内容,并将其保存到文件中。这个第二个内核位于系统内存保留的一部分。
virsh dump
virsh dump是 libvirt的一个coredump 工具。virsh 会向 libvirt 发送 API 请求,libvirt 调用QEMU执行dump-guest-memory 命令开始实际的内存拷贝工作。
libvirt 通过调用 makedumpfile工具智能分析 vmcore 文件,实现过滤和压缩 vmcore 文件。
使用 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的功能。
- 缓存页 (Cache Pages):如果页面被标记为在
4. 压缩文件
此时 makedumpfile知道最终输出文件应该包含哪些页,接下来就是生成最终的 vmcore 文件。
-
读取-过滤-压缩-写入:
makedumpfile会再次从头读取原始的vmcore源文件。- 对于每一个页,它会查询之前建立的内存位图。
- 如果位图显示该页需要“保留”,它就将该页的数据读入缓冲区。
- 如果位图显示该页需要“移除”,它就直接跳过,不读取该页。
- 收集到一定数量的“保留”页(形成一个数据块,Block)后,调用指定的压缩库(如 zlib, lzo, snappy)对这个数据块进行压缩。
- 将压缩后的数据块写入到新的输出文件中。
-
生成新文件头:输出的文件也是有特定格式的。
makedumpfile会生成一个新的文件头(可以是kdump的压缩格式头,也可以是ELF格式头),这个头中包含了如何解压和重构内存布局的元数据。分析工具(如crash)读取这个文件时,会首先解析头部,然后按需解压数据块来还原出分析所需的内存内容。
makedumpfile原理
页面标志位判断
#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) // 硬件中毒页面
转储级别控制定义
#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) // 排除空闲页面
检查函数
// 检查是否为缓存页面 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() - 核心判断函数
- 检查页面是否在可转储的物理内存范围内
- 根据转储级别过滤不同类型的页面
- 检查页面标志位决定是否转储
典型的页面判断流程:
-
物理地址检查
- 检查页面是否在有效的物理内存范围内
- 排除MMIO区域和其他非RAM区域
-
页面标志位分析
- 读取页面的flags字段
- 根据PG_*标志位判断页面类型和状态
-
转储级别过滤
- 根据用户指定的转储级别(-d参数)
- 决定是否排除特定类型的页面
-
特殊页面处理
- 零页面检测和处理
- 硬件中毒页面的排除
- 内核关键数据结构的保留
内存范围定义
// 内存范围描述
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 文件格式
+------------------+ 偏移 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文件格式:
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 格式:
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
/*
* 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
- 要使用 vmlinux 获取过滤内存的 coredump 信息,需要为 vmlinux 开启
CONFIG_DEBUG_INFO和CONFIG_KEXEC编译配置,会导致 vmlinux 文件大小从 50M 增加到 200 M。这里可以提前使用 vmliux 生成一份 vmcoreinfo,大小为 2K
makedumpfile -g vmcoreinfo -x vmlinux
配置要求
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.5G | 1 | 101M | 10.212s |
| 2 | 513M | 9.797s | |
| 4 | 511M | 9.749s | |
| 8 | 513M | 9.710s | |
| 16 | 111M | 9.450s | |
| 31 | 83M | 9s |
| 规格 | 过滤等级 | minidump 文件大小 | 耗时 |
|---|---|---|---|
| 2C0.5G | 1 | 103M | 6.699s |
| 2 | 513M | 6.417s | |
| 4 | 511M | 6.351s | |
| 8 | 513M | 6.321s | |
| 16 | 116M | 6.023s | |
| 31 | 91M | 6.136s |
panic测试
echo c | tee /proc/sysrq-trigger
soft lockup 测试
insmod soft_lockup.ko
提示
- 没有 vmlinux 时, 可以使用 vmcoreinfo
cat /proc/kallsyms # 备用查看kernel信息
-
启用过滤配置时,必须读取与崩溃内核匹配的
vmlinux。 -
info 各项的含义
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;
};
- vmcoreinfo 所需的信息
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