0001 :Original: Documentation/mm/page_owner.rst
0002
0003 :翻译:
0004
0005 司延腾 Yanteng Si <siyanteng@loongson.cn>
0006
0007 :校译:
0008
0009
0010 ================================
0011 page owner: 跟踪谁分配的每个页面
0012 ================================
0013
0014 概述
0015 ====
0016
0017 page owner是用来追踪谁分配的每一个页面。它可以用来调试内存泄漏或找到内存占用者。
0018 当分配发生时,有关分配的信息,如调用堆栈和页面的顺序被存储到每个页面的特定存储中。
0019 当我们需要了解所有页面的状态时,我们可以获得并分析这些信息。
0020
0021 尽管我们已经有了追踪页面分配/释放的tracepoint,但用它来分析谁分配的每个页面是
0022 相当复杂的。我们需要扩大跟踪缓冲区,以防止在用户空间程序启动前出现重叠。而且,启
0023 动的程序会不断地将跟踪缓冲区转出,供以后分析,这将会改变系统的行为,会产生更多的
0024 可能性,而不是仅仅保留在内存中,所以不利于调试。
0025
0026 页面所有者也可以用于各种目的。例如,可以通过每个页面的gfp标志信息获得精确的碎片
0027 统计。如果启用了page owner,它就已经实现并激活了。我们非常欢迎其他用途。
0028
0029 page owner在默认情况下是禁用的。所以,如果你想使用它,你需要在你的启动cmdline
0030 中加入"page_owner=on"。如果内核是用page owner构建的,并且由于没有启用启动
0031 选项而在运行时禁用page owner,那么运行时的开销是很小的。如果在运行时禁用,它不
0032 需要内存来存储所有者信息,所以没有运行时内存开销。而且,页面所有者在页面分配器的
0033 热路径中只插入了两个不可能的分支,如果不启用,那么分配就会像没有页面所有者的内核
0034 一样进行。这两个不可能的分支应该不会影响到分配的性能,特别是在静态键跳转标签修补
0035 功能可用的情况下。以下是由于这个功能而导致的内核代码大小的变化。
0036
0037 - 没有page owner::
0038
0039 text data bss dec hex filename
0040 48392 2333 644 51369 c8a9 mm/page_alloc.o
0041
0042 - 有page owner::
0043
0044 text data bss dec hex filename
0045 48800 2445 644 51889 cab1 mm/page_alloc.o
0046 6662 108 29 6799 1a8f mm/page_owner.o
0047 1025 8 8 1041 411 mm/page_ext.o
0048
0049 虽然总共增加了8KB的代码,但page_alloc.o增加了520字节,其中不到一半是在hotpath
0050 中。构建带有page owner的内核,并在需要时打开它,将是调试内核内存问题的最佳选择。
0051
0052 有一个问题是由实现细节引起的。页所有者将信息存储到struct page扩展的内存中。这
0053 个内存的初始化时间比稀疏内存系统中的页面分配器启动的时间要晚一些,所以,在初始化
0054 之前,许多页面可以被分配,但它们没有所有者信息。为了解决这个问题,这些早期分配的
0055 页面在初始化阶段被调查并标记为分配。虽然这并不意味着它们有正确的所有者信息,但至
0056 少,我们可以更准确地判断该页是否被分配。在2GB内存的x86-64虚拟机上,有13343
0057 个早期分配的页面被捕捉和标记,尽管它们大部分是由结构页扩展功能分配的。总之,在这
0058 之后,没有任何页面处于未追踪状态。
0059
0060 使用方法
0061 ========
0062
0063 1) 构建用户空间的帮助::
0064
0065 cd tools/vm
0066 make page_owner_sort
0067
0068 2) 启用page owner: 添加 "page_owner=on" 到 boot cmdline.
0069
0070 3) 做你想调试的工作。
0071
0072 4) 分析来自页面所有者的信息::
0073
0074 cat /sys/kernel/debug/page_owner > page_owner_full.txt
0075 ./page_owner_sort page_owner_full.txt sorted_page_owner.txt
0076
0077 ``page_owner_full.txt`` 的一般输出情况如下(输出信息无翻译价值)::
0078
0079 Page allocated via order XXX, ...
0080 PFN XXX ...
0081 // Detailed stack
0082
0083 Page allocated via order XXX, ...
0084 PFN XXX ...
0085 // Detailed stack
0086
0087 ``page_owner_sort`` 工具忽略了 ``PFN`` 行,将剩余的行放在buf中,使用regexp提
0088 取页序值,计算buf的次数和页数,最后根据参数进行排序。
0089
0090 在 ``sorted_page_owner.txt`` 中可以看到关于谁分配了每个页面的结果。一般输出::
0091
0092 XXX times, XXX pages:
0093 Page allocated via order XXX, ...
0094 // Detailed stack
0095
0096 默认情况下, ``page_owner_sort`` 是根据buf的时间来排序的。如果你想
0097 按buf的页数排序,请使用-m参数。详细的参数是:
0098
0099 基本函数::
0100
0101 排序:
0102 -a 按内存分配时间排序
0103 -m 按总内存排序
0104 -p 按pid排序。
0105 -P 按tgid排序。
0106 -n 按任务命令名称排序。
0107 -r 按内存释放时间排序。
0108 -s 按堆栈跟踪排序。
0109 -t 按时间排序(默认)。
0110 --sort <order> 指定排序顺序。排序的语法是[+|-]key[,[+|-]key[,...]]。从
0111 **标准格式指定器**那一节选择一个键。"+"是可选的,因为默认的方向是数字或
0112 词法的增加。允许混合使用缩写和完整格式的键。
0113
0114 例子:
0115 ./page_owner_sort <input> <output> --sort=n,+pid,-tgid
0116 ./page_owner_sort <input> <output> --sort=at
0117
0118 其它函数::
0119
0120 剔除:
0121 --cull <rules>
0122 指定剔除规则。剔除的语法是key[,key[,...]]。从**标准格式指定器**
0123 部分选择一个多字母键。
0124 <rules>是一个以逗号分隔的列表形式的单一参数,它提供了一种指定单个剔除规则的
0125 方法。 识别的关键字在下面的**标准格式指定器**部分有描述。<规则>可以通过键的
0126 序列k1,k2,...来指定,在下面的标准排序键部分有描述。允许混合使用简写和完整形
0127 式的键。
0128
0129 Examples:
0130 ./page_owner_sort <input> <output> --cull=stacktrace
0131 ./page_owner_sort <input> <output> --cull=st,pid,name
0132 ./page_owner_sort <input> <output> --cull=n,f
0133
0134 过滤:
0135 -f 过滤掉内存已被释放的块的信息。
0136
0137 选择:
0138 --pid <pidlist> 按pid选择。这将选择进程ID号出现在<pidlist>中的块。
0139 --tgid <tgidlist> 按tgid选择。这将选择其线程组ID号出现在<tgidlist>
0140 中的块。
0141 --name <cmdlist> 按任务命令名称选择。这将选择其任务命令名称出现在
0142 <cmdlist>中的区块。
0143
0144 <pidlist>, <tgidlist>, <cmdlist>是以逗号分隔的列表形式的单个参数,
0145 它提供了一种指定单个选择规则的方法。
0146
0147
0148 例子:
0149 ./page_owner_sort <input> <output> --pid=1
0150 ./page_owner_sort <input> <output> --tgid=1,2,3
0151 ./page_owner_sort <input> <output> --name name1,name2
0152
0153 标准格式指定器
0154 ==============
0155 ::
0156
0157 --sort的选项:
0158
0159 短键 长键 描述
0160 p pid 进程ID
0161 tg tgid 线程组ID
0162 n name 任务命令名称
0163 st stacktrace 页面分配的堆栈跟踪
0164 T txt 块的全文
0165 ft free_ts 页面释放时的时间戳
0166 at alloc_ts 页面被分配时的时间戳
0167 ator allocator 页面的内存分配器
0168
0169 --curl的选项:
0170
0171 短键 长键 描述
0172 p pid 进程ID
0173 tg tgid 线程组ID
0174 n name 任务命令名称
0175 f free 该页是否已经释放
0176 st stacktrace 页面分配的堆栈跟踪
0177 ator allocator 页面的内存分配器