> ## Documentation Index
> Fetch the complete documentation index at: https://dragonwingdocs.qualcomm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 评估内存使用情况

Linux 使用虚拟内存系统。因此,用户程序访问的地址并不直接对应硬件使用的物理地址。虚拟内存引入了一层间接机制,允许程序分配超出物理可用内存的额外内存。

内存管理的实现涵盖以下方面:

* 管理内存中的物理页
* 用于以大块分配内存的伙伴(buddy)系统
* 用于分配较小内存块的 slab、slub 和 slob 分配器
* 用于分配非连续内存块的 vmalloc 机制
* 进程的地址空间

## **/proc 文件系统**

`/proc` 文件系统提供以下文件:

* [/proc/meminfo](https://dragonwingdocs.qualcomm.com/System/Debug-Subsystem/assess-memory-usage#/proc/meminfo)
* [/proc/vmstat](https://dragonwingdocs.qualcomm.com/System/Debug-Subsystem/assess-memory-usage#/proc/vmstat)
* [/proc/iomem](https://dragonwingdocs.qualcomm.com/System/Debug-Subsystem/assess-memory-usage#/proc/iomem)
* [/proc/vmallocinfo](https://dragonwingdocs.qualcomm.com/System/Debug-Subsystem/assess-memory-usage#/proc/vmallocinfo)

### **/proc/meminfo**

此文件提供有关内存分布和使用情况的信息。

* 要查看此文件的内容,请运行以下命令:
  > ```text theme={null}
  > cat /proc/meminfo
  > ```
  >
  > **示例输出:**
  >
  > ```text theme={null}
  > MemTotal:        3813532 kB
  > MemFree:          624836 kB
  > MemAvailable:    2098008 kB
  > Buffers:           40416 kB
  > Cached:          1484320 kB
  > SwapCached:            0 kB
  > Active:          1334816 kB
  > .
  > .
  > .
  > .
  > NFS_Unstable:          0 kB
  > Bounce:                0 kB
  > WritebackTmp:          0 kB
  > CommitLimit:     2431048 kB
  > Committed_AS:   99995284 kB
  > VmallocTotal:  258867136 kB
  > VmallocUsed:           0 kB
  > VmallocChunk:          0 kB
  > CmaTotal:         163840 kB
  > CmaFree:            1368 kB
  > ```

有关此文件中可用参数的更多信息,请参阅 [/proc 文件系统](https://docs.kernel.org/filesystems/proc.html)。

### **/proc/vmstat**

此文件显示来自内核的详细虚拟内存统计信息。大多数统计信息仅在您于 `init/Kconfig` 文件中启用 `CONFIG_VM_EVENT_COUNTERS` 选项后才可用。

* 要查看此文件的内容,请运行以下命令:
  > ```text theme={null}
  > cat /proc/vmstat
  > ```
  >
  > **示例输出:**
  >
  > ```text theme={null}
  > nr_free_pages 156290
  > nr_alloc_batch 132
  > nr_inactive_anon 108
  > nr_active_anon 165006
  > nr_inactive_file 212275
  > nr_active_file 168709
  > nr_unevictable 64
  > nr_mlock 64
  > nr_anon_pages 164982
  > nr_mapped 90366
  > nr_file_pages 381184:
  > :
  > unevictable_pgs_mlocked 0
  > unevictable_pgs_munlocked 0
  > unevictable_pgs_cleared 0
  > unevictable_pgs_stranded 0
  > unevictable_pgs_mlockfreed 0
  > ```

有关此文件中可用参数的更多信息,请参阅 [Linux 手册页](https://man7.org/linux/man-pages/man8/vmstat.8.html)。

### **/proc/iomem**

此文件显示系统中各种设备驱动程序的内存映射。

* 要查看此文件的内容,请运行以下命令:
  > ```text theme={null}
  > cat /proc/iomem
  > ```
  >
  > **示例输出:**
  >
  > ```text theme={null}
  > 007781b8-007791b7 : vmpm
  > 010aa000-010abfff : tsens_physical
  > 010ac000-010ac003 : pshold-base
  > 010ad000-010aefff : tsens_physical
  > 01680000-0168ffff : /soc/arm,smmu-anoc1@1680000
  > 016c0000-016fffff : /soc/arm,smmu-anoc2@16c0000
  > 01d0101c-01d0101f : sp2soc_irq_status
  > 01d01024-01d01027 : sp2soc_irq_clr
  > 01d01028-01d0102b : sp2soc_irq_mask
  > .
  > .
  > .
  > 0caa0000-0caa3fff : jpeg_hw
  > 0caa4000-0caa47ff : fd_core
  > 0caa5000-0caa53ff : fd_misc
  > 0cd00000-0cd3ffff : /soc/arm,smmu-mmss@cd00000
  > 17817000-17817fff : msm-watchdog
  > 17900000-1790dfff : msm-gladiator-erp
  > 80000000-857fffff : System RAM
  > 80080000-817fffff : Kernel code
  > 82330000-82945fff : Kernel data
  > 88f00000-8aafffff : System RAM
  > 95300000-17e3bffff : System RAM
  > ```

### **/proc/vmallocinfo**

此文件显示通过 `vmalloc` 或 `ioremap` 进行的虚拟地址分配的详细信息。

* 要查看此文件的内容,请运行以下命令:
  > ```text theme={null}
  > cat /proc/vmallocinfo
  > ```
  >
  > **示例输出:**
  >
  > ```text theme={null}
  > 0xbf000000-0xbf002000     8192            module_alloc_update_bounds+0xc/0x5c     pages=1 vmalloc
  > 0xbf004000-0xbf008000     16384          module_alloc_update_bounds+0xc/0x5c     pages=3 vmalloc
  > 0xee800000-0xef800000    16777216   iotable_init+0x0/0xb0   phys=36800000   ioremap
  > 0xf0000000-0xf0002000     8192            of_iomap+0x30/0x38   ioremap
  > 0xf0002000-0xf0004000     8192            of_iomap+0x30/0x38   ioremap
  > 0xf0004000-0xf000c000     32768          gen_pool_add_virt+0x48/0xb8   pages= 7    vmalloc
  > 0xf000c000-0xf000e000     8192            msm_pm_setup_saved_state+0xcc/0x1bc   ioremap
  > ………………………
  > 0xf0174000-0xf0176000    8192            msm_cpu_status_probe+0xd8/0x20c   ioremap
  > 0xf0f24000-0xf0f28000      16384         _kgsl_sharedmem_page_alloc+0xa0/0x41c   pages=3   vmalloc
  > 0xf0f39000-0xf0f3e000      20480         _kgsl_sharedmem_page_alloc+0xa0/0x41c   pages=4   vmalloc
  > 0xf0f61000-0xf0f66000      20480         _kgsl_sharedmem_page_alloc+0xa0/0x41c   pages=4   vmalloc
  > ……….
  > 0xfa400000-0xfa600000    2097152     iotable_init+0x0/0xb0 phys=fa00000    ioremap
  > 0xfa71e000-0xfa71f000     4096            iotable_init+0x0/0xb0 phys=f991e000     ioremap
  > 0xfefd8000-0xff000000     163840        pcpu_get_vm_areas+0x0/0x56c  vmalloc
  > ```

## **memblock 接口**

`debugfs` 文件系统上的 `memblock` 接口提供系统中可用和保留内存区域的详细信息。该接口提供以下文件:

### **/sys/kernel/debug/memblock/memory**

此文件提供 Linux 内核可见的所有可用内存区域(HLOS 和非 HLOS)的信息。要确定内核可访问的总内存,请计算每个区域起始地址与结束地址之间的差值,并将这些值求和以得到已占用的 RAM 总量。剩余的内存(即设备 RAM 大小与已占用 RAM 之间的差值)为非 HLOS 内存,或被其他子系统占用的内存。

* 要查看此文件的内容,请运行以下命令:

  ```text theme={null}
  cat /sys/kernel/debug/memblock/memory
  ```

  **示例输出:**

  ```

  0: 0x0000000080000000..0x00000000857fffff

  1: 0x0000000088f00000..0x000000008aafffff

  2: 0x0000000095300000..0x000000017e3bffff
  ```

### **/sys/kernel/debug/memblock/reserved**

此文件提供系统中所有保留内存区域的信息。

* 要查看此文件的内容,请运行以下命令:
  ```text theme={null}
  cat /sys/kernel/debug/memblock/reserved
  ```
  **示例输出:**
  ```text theme={null}
  0: 0x0000000080080000..0x0000000082944fff
  1: 0x0000000083200000..0x0000000083259bb4
  2: 0x0000000083400000..0x00000000839506c9
  3: 0x00000000f5800000..0x00000000ffbfffff
  4: 0x00000000ffff7000..0x00000000ffffefff
  5: 0x00000000ffffff40..0x00000000ffffff77
  6: 0x00000000ffffff80..0x00000000ffffffb7
  7: 0x00000000ffffffc0..0x00000000fffffff7
  8: 0x0000000179258000..0x000000017d9fffff
  9: 0x000000017da17000..0x000000017da1ffff
  10: 0x000000017da20e00..0x000000017da26fff
  11: 0x000000017da27300..0x000000017da2735f
  12: 0x000000017da27380..0x000000017da273df
  13: 0x000000017da27400..0x000000017da2755f
  14: 0x000000017da27580..0x000000017da27587
  15: 0x000000017da275c0..0x000000017da275c7
  16: 0x000000017da29600..0x000000017da29924
  17: 0x000000017da29940..0x000000017da29c64
  18: 0x000000017da29c80..0x000000017da29fa4
  19: 0x000000017da29fac..0x000000017da2a3f8
  20: 0x000000017da2a3fc..0x000000017da2a42e
  21: 0x000000017da2a430..0x000000017da2a45e
  22: 0x000000017da2a460..0x000000017e3bffff
  ```

## **调试内存泄漏问题**

要调试内核内存泄漏问题,请启用以下配置选项:

* `CONFIG_DEBUG_KMEMLEAK=y`
* `CONFIG_DEBUG_KMEMLEAK_MEM_POOL_SIZE=4000`
* `CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y`

默认情况下,一个内核线程每 10 分钟扫描一次内存,并打印发现的新的未被引用对象的数量。例如,

```text theme={null}
unreferenced object 0xec26f000 (size 4096):

comm "Binder_2", pid 4592, jiffies 8848 (age 336.710s)

hex dump (first 32 bytes):

ec 4d f8 c0 02 00 00 00 00 00 00 00 00 00 00 00 .M..............

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

backtrace:

[<c0126f70>] kmem_cache_alloc_trace+0x17c/0x238

[<c03059c4>] ddl_client_transact+0xd0/0x158

[<c0314a3c>] ddl_open+0x4c/0x194

[<c0302288>] vcd_init_client_context+0x14/0x9c

[<c02ffd10>] vcd_open_in_ready+0x3c/0x94

[<c02fd31c>] vcd_open+0x214/0x274

[<c031b244>] vid_dec_open_client+0x1d0/0x288

[<c031b3ac>] vid_dec_open+0x30/0x7c

[<c012ff70>] chrdev_open+0x10c/0x134

[<c012aa7c>] __dentry_open.isra.12+0x190/0x29c

[<c0138a78>] do_last.isra.29+0x690/0x6c0

[<c0138c70>] path_openat+0xb8/0x35c

[<c0138ff4>] do_filp_open+0x2c/0x78

[<c012b7a0>] do_sys_open+0xd8/0x170

[<c000df20>] ret_fast_syscall+0x0/0x30

[<ffffffff>] 0xffffffff
```

通过在内核命令行上传递 `KMEMLEAK=off`,可以在启动时禁用 `KMEMLEAK` 选项。

有关 `kmemleak.txt` 文件的更多信息,请参阅[内核文档](https://www.kernel.org/doc/Documentation)。

以下额外的内核配置选项可用于跟踪每个内存页的分配者:

* `CONFIG_PAGE_OWNER`
* `CONFIG_PAGE_OWNER_ENABLE_DEFAULT`
* `CONFIG_PAGE_EXTENSION`

启用这些选项有助于识别多次分配的情况,这可能表明存在内存泄漏问题。

## **识别内存损坏问题**

启用以下内核配置选项以识别内存损坏问题:

* `CONFIG_PAGE_POISONING`
* `CONFIG_SLUB_DEBUG_ON`
* `CONFIG_DEBUG_LIST`
* `CONFIG_SLUB_DEBUG`

**示例日志:**

```text theme={null}
BUG <slab cache affected>: <What went wrong>
-----------------------------------------------
INFO: <corruption start>-<corruption_end> <more info>
INFO: Slab <address> <slab information>
INFO: Object <address> <object information>
INFO: Allocated in <kernel function> age=<jiffies since alloc> cpu=<allocated by cpu> pid=<pid of the process>
INFO: Freed in <kernel function> age=<jiffies since free> cpu=<freed by cpu> pid=<pid of the process>
```

有关 slub 调试的更多信息,请参阅[内核文档](https://www.kernel.org/doc/Documentation)中提供的 `Documentation/vm/slub.txt` 文件。

### **内存不足**

当系统无法分配页面时,内核日志会显示类似以下的消息:

```text theme={null}
<4>[12146.861355] Thread-430: page allocation failure: order:0, mode:0x10d2

<CALL STACK>

<4>[12146.951687] Mem-info:

<4>[12146.953909] Normal per-cpu:

<4>[12146.956686] CPU 0: hi: 186, btch: 31 usd: 61

<4>[12146.961489] CPU 1: hi: 186, btch: 31 usd: 0

<4>[12146.966235] HighMem per-cpu:

<4>[12146.969122] CPU 0: hi: 186, btch: 31 usd: 54

<4>[12146.973877] CPU 1: hi: 186, btch: 31 usd: 0

…….

<4>[12147.010770] Normal free:53192kB min:3508kB low:4384kB high:5260kB ……..

<4>[12147.050805] lowmem_reserve[]: 0 9022 9022

<4>[12147.054610] HighMem free:153360kB min:512kB low:1824kB high:3140kB ………

<4>[12147.095453] lowmem_reserve[]: 0 0 0

<4>[12147.098617] Normal: 118*4kB 232*8kB 161*16kB 110*32kB 34*64kB 9*128kB 10*256kB 6*512kB 7*1024kB 6*2048kB 4*4096kB = 53224kB

<4>[12147.115455] HighMem: 2774*4kB 11769*8kB 3005*16kB 1*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 153360kB

………………
```

这些消息表明系统无法分配所请求的页面。日志消息的第一行提供了有关内存不足问题的若干详细信息。例如,在以下日志中:

```text theme={null}
<4>[1214.855361] Thread-4: page allocation failure: order:2, mode:0x10d2
```

* **order**:表示页面的大小。在此示例中,2 <sup>2</sup> x PAGE\_SIZE(4 K)= 16 K
  * Linux 使用以 2 的幂分配页面的伙伴分配器。
    * 伙伴分配器的最大大小为 order 10 = 2 <sup>10</sup> x 4 kB = 4 MB。
    * 对于 > 4 MB 的分配,请使用替代分配方法,例如连续内存分配器(CMA)。
  * 对于高阶(higher-order)分配失败,请检查内存是否可以在虚拟上连续,而不必在物理上连续。
* **mode**:表示所请求页面的类型
  * `mode` 提供有关获取空闲页(GFP)标志的信息。在此示例中,`mode` 为 `0x10d2`。
  * `mode` 是对分配中所有 GFP 标志执行 OR 运算的结果。
* 系统中可用的页面:页面分配失败消息会打印系统中可用页面大小的详细信息。
  ```text theme={null}
  Normal: 118*4kB 232*8kB 161*16kB 110*32kB 34*64kB 9*128kB 10*256kB 6*512kB 7*1024kB 6*2048kB 4*4096kB = 53224kB
  HighMem: 2774*4kB 11769*8kB 3005*16kB 1*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 153360kB
  ```

### **IOMMU 页面错误**

输入输出内存管理单元(IOMMU),也称为系统内存管理单元(SMMU),代表没有自己 MMU 的子系统执行内存管理功能。

IOMMU 硬件模块允许物理上非连续的页面支持虚拟上连续的内存。IOMMU 中的内存转换逻辑与 CPU MMU 中的逻辑相同。

IOMMU 页面错误是最常见的 IOMMU 问题。当所请求的页面已在页表中映射但在内存中未找到时,会发生该错误。错误处理程序接收 IOMMU 的上下文库(context bank)实例,并转储该上下文的寄存器。

以下日志表示 IOMMU 页面错误。

```text theme={null}
[ 47.228992] msm_iommu_v1: Unexpected IOMMU page fault!

[ 47.233115] msm_iommu_v1: name = mdp_iommu

[ 47.237238] msm_iommu_v1: context = mdp_0 (0)

[ 47.241507] msm_iommu_v1: Interesting registers:

[ 47.246149] msm_iommu_v1: FAR = 0000000000000000

[ 47.250970] msm_iommu_v1: PAR = 0000000000000000

[ 47.255834] msm_iommu_v1: FSR = 00000002 [TF ]

[ 47.260540] msm_iommu_v1: FSYNR0 = 000005a1 FSYNR1 = 00030005

[ 47.266528] msm_iommu_v1: TTBR0 = 0000000071a28000

[ 47.271370] msm_iommu_v1: TTBR1 = 0000000000000000

[ 47.276248] msm_iommu_v1: SCTLR = 00001043 ACTLR = 70000000

[ 47.282221] msm_iommu_v1: CBAR = 00000000 CBFRSYNRA = 00000000

[ 47.288521] msm_iommu_v1: PRRR = ff0a81a8 NMRR = 40e040e0

[ 47.294461] msm_iommu_v1: NOTE: Value actually unknown for CBAR

[ 47.300394] msm_iommu_v1: NOTE: Value actually unknown for CBFRSYNRA

[ 47.306717] msm_iommu_v1: Page table in DDR shows PA = 0
```

下表描述了日志消息中捕获的字段。

**表:IOMMU 页面错误日志中的信息**

| **项目** |         **描述**         |
| :----: | :--------------------: |
| `name` |      导致错误的硬件模块的名称。     |
|  `FAR` | 故障地址寄存器(FAR)指示发生错误的地址。 |
|  `FSR` |   故障状态寄存器(FSR)指示以下内容:  |

> * 转换故障(TF)
> * 访问权限故障(APF)
> * 停滞状态(SS)

FSR 是 IOMMU 调试中最重要的寄存器之一。该寄存器具有读/写清除(read/write-clear)访问权限。对该寄存器的读操作读取寄存器中的值,而写操作会清除写入数据中为 1 的位所对应的位,并使为 0 的位所对应的位保持不变。此过程可防止在写入寄存器以清除旧故障时意外清除新故障。该寄存器中一些有用的位如下:

**表:故障状态寄存器中的位**

|        **位**       |       **描述**      |
| :----------------: | :---------------: |
|    `[Bit 1]: TF`   |   转换故障(无效的页表条目)   |
|   `[Bit 2]: AFF`   |        访问故障       |
|   `[Bit 3]: APF`   |    权限故障(写入只读区域)   |
|  `[Bit 4]: TLBMF`  |     TLB 未命中故障     |
| `[Bit 5]: HTWDEEF` |   硬件页表遍历解码错误外部故障  |
| `[Bit 6]: HTWSEEF` |   硬件页表遍历从属错误外部故障  |
|   `[Bit 7]: MHF`   |     TLB 中多次命中     |
|   `[Bit 16]: SL`   | 二级故障(故障发生在页表的第二级) |
|   `[Bit 30]: SS`   |        停滞状态       |
|  `[Bit 31]: MULTI` |        多个故障       |

`TF`、`APF` 和 `SL` 标志表示正常操作,而 `TLBMF`、`HTWDEEF`、`HTWSEEF` 和 `MHF` 标志表示存在问题。

#### **IOMMU 页表**

IOMMU 页表转储在内核日志中提供来自 FAR 的故障地址和寄存器转储。该故障地址代表虚拟地址,您可以从页表中获取对应的物理地址。从页表转储中,您可以识别所请求的地址是已映射还是未映射。每个 IOMMU 域都有一个页表,转储包括每个域的页表。目前有六个域。

以下是 `Domain: 2` 页表的示例转储。

```text theme={null}
Domain: 2 [L2 cache redirect for page tables is OFF]

0x00000000--0x0001ffff [0x00020000] [UNMAPPED]

0x00020000--0x01807fff [0x017e8000] A:0x82a8e000--0x84275fff [0x017e8000] [R/W][4K]

0x01808000--0x01939fff [0x00132000] A:0xf0c24000--0xf0d55fff [0x00132000] [R/W][4K]

0x0193a000--0x0199ffff [0x00066000] A:0xf13fa000--0xf145ffff [0x00066000] [R/W][4K]

0x019a0000--0x01e85fff [0x004e6000] A:0xf966e000--0xf9b53fff [0x004e6000] [R/W][4K]

0x01e86000--0x01ffffff [0x0017a000] [UNMAPPED]

0x02000000--0x02feffff [0x00ff0000] A:0xf5a22000--0xf6a11fff [0x00ff0000] [R/W][4K]
```

在此示例中,第一列代表虚拟地址,第二列代表对应的连续物理地址区域中的字节数,第三列代表物理地址。每个区域的权限也一并列出。

### **内存映射**

有关内存映射的更多信息,请参阅最新的[发行说明](https://docs.qualcomm.com/bundle/resource/topics/80-80021-300/introduction.html)。

### **栈损坏**

错误的编码逻辑访问栈中的内存位置,导致这些内存位置的值发生变化,从而造成栈损坏。栈损坏可能以以下方式发生:

* 由于错误的代码逻辑,程序耗尽所有栈内存并写入超出栈边界的内存,导致栈溢出。
* 访问越界的数组。
* 未定义或已释放的指针指向栈地址。
* 调用者函数的返回地址被损坏。

要识别栈损坏问题,请启用以下内核配置选项:

* `CONFIG_STACKPROTECTOR`
* `CONFIG_STACKPROTECTOR_STRONG`

内核地址消毒器(KASAN)实用工具也有助于识别某些栈损坏问题。
