• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Memory Debugging
2
3
4The purpose of memory debugging is to locate problems related to dynamic memory. The kernel provides a variety of memory debugging methods. Dynamic memory pool statistics helps you learn the memory pool waterline and fragmentation rate. Memory leak check helps you accurately locate the code where memory leak occurs and analyze the memory usage of each module. Memory corruption check helps you locate memory corruptions.
5
6
7## Memory Information Statistics
8
9
10### Basic Concepts
11
12Memory information includes the memory pool size, memory usage, remaining memory size, maximum free memory, memory waterline, number of memory nodes, and fragmentation rate.
13
14- Memory waterline indicates the maximum memory used in a memory pool. The waterline value is updated upon each memory allocation and release. The memory pool size can be optimized based on this value.
15
16- Fragmentation rate indicates the fragmentation degree of the memory pool. If the fragmentation rate is high, there are a large number of free memory blocks in the memory pool but each block is small. You can use the following formula to calculate the fragmentation rate:<br>Fragmentation rate = 100 – 100 x Maximum free memory block size/Remaining memory size
17
18- You can use [APIs for memory management](kernel-mini-basic-memory.md) to scan node information in the memory pool and collect statistics.
19
20
21### Function Configuration
22
23**LOSCFG_MEM_WATERLINE** specifies the setting of the memory information statistics function. This function is enabled by default. To disable the function, set this macro to **0** in **target_config.h**. If you want to obtain the memory waterline, you must enable this macro.
24
25
26### Development Guidelines
27
28
29#### How to Develop
30
31Key structure:
32
33
34```
35typedef struct {
36    UINT32 totalUsedSize;       // Memory usage of the memory pool.
37    UINT32 totalFreeSize;       // Remaining size of the memory pool.
38    UINT32 maxFreeNodeSize;     // Maximum size of the free memory block in the memory pool.
39    UINT32 usedNodeNum;         // Number of non-free memory blocks in the memory pool.
40    UINT32 freeNodeNum;         // Number of free memory blocks in the memory pool.
41#if (LOSCFG_MEM_WATERLINE == 1) // The function is enabled by default. To disable it, set this macro to 0 in target_config.h.
42    UINT32 usageWaterLine;      // Waterline of the memory pool.
43#endif
44} LOS_MEM_POOL_STATUS;
45```
46
47To obtain the memory waterline, call **LOS_MemInfoGet**. The first parameter in the API is the start address of the memory pool, and the second parameter is the handle of the **LOS_MEM_POOL_STATUS** type. The **usageWaterLine** field indicates the waterline.
48
49To calculate the memory fragmentation rate, call **LOS_MemInfoGet** to obtain the remaining memory size and the maximum free memory block size in the memory pool, and then calculate the fragmentation rate of the dynamic memory pool as follows:<br>Fragmentation rate = 100 – 100 x Maximum free memory block size/Remaining memory size
50
51
52#### Development Example
53
54This example implements the following:
55
561. Create a monitoring task to obtain information about the memory pool.
57
582. Calls **LOS_MemInfoGet** to obtain the basic information about the memory pool.
59
603. Calculate the memory usage and fragmentation rate.
61
62
63#### Sample Code
64
65The sample code is as follows:
66
67The sample code can be compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. The **MemTest** function is called in **TestTaskEntry**.
68
69```
70#include <stdio.h>
71#include <string.h>
72#include "los_task.h"
73#include "los_memory.h"
74#include "los_config.h"
75
76#define TEST_TASK_PRIO  5
77void MemInfoTaskFunc(void)
78{
79    LOS_MEM_POOL_STATUS poolStatus = {0};
80
81    /* pool is the memory address of the information to be collected. OS_SYS_MEM_ADDR is used as an example. */
82    void *pool = OS_SYS_MEM_ADDR;
83    LOS_MemInfoGet(pool, &poolStatus);
84    /* Calculate the fragmentation rate of the memory pool. */
85    float fragment = 100 - poolStatus.maxFreeNodeSize * 100.0 / poolStatus.totalFreeSize;
86    /* Calculate the memory usage of the memory pool. */
87    float usage = LOS_MemTotalUsedGet(pool) * 100.0 / LOS_MemPoolSizeGet(pool);
88    printf("usage = %f, fragment = %f, maxFreeSize = %d, totalFreeSize = %d, waterLine = %d\n", usage, fragment,
89    		poolStatus.maxFreeNodeSize, poolStatus.totalFreeSize, poolStatus.usageWaterLine);
90}
91
92int MemTest(void)
93{
94    unsigned int ret;
95    unsigned int taskID;
96    TSK_INIT_PARAM_S taskStatus = {0};
97    taskStatus.pfnTaskEntry = (TSK_ENTRY_FUNC)MemInfoTaskFunc;
98    taskStatus.uwStackSize  = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
99    taskStatus.pcName       = "memInfo";
100    taskStatus.usTaskPrio   = TEST_TASK_PRIO;
101    ret = LOS_TaskCreate(&taskID, &taskStatus);
102    if (ret != LOS_OK) {
103        printf("task create failed\n");
104        return -1;
105    }
106    return 0;
107}
108```
109
110
111#### Verification
112
113The result is as follows:
114
115
116```
117usage = 0.458344, fragment = 0.000000, maxFreeSize = 16474928, totalFreeSize = 16474928, waterLine = 76816
118
119The preceding data may vary depending on the running environment.
120```
121## Memory Leak Check
122
123
124### Basic Concepts
125
126As an optional function of the kernel, memory leak check is used to locate dynamic memory leak problems. After this function is enabled, the dynamic memory automatically records the link registers (LRs) used when memory is allocated. If a memory leak occurs, the recorded information helps locate the memory allocated for further analysis.
127
128
129### Function Configuration
130
131**LOSCFG_MEM_LEAKCHECK** specifies the setting of the memory leak check. This function is disabled by default. To enable the function, set this macro to **1** in **target_config.h**.
132
133**LOSCFG_MEM_RECORD_LR_CNT** specifies the number of LRs recorded. The default value is **3**. Each LR consumes the memory of **sizeof(void \*)** bytes.
134
135**LOSCFG_MEM_OMIT_LR_CNT** specifies the number of ignored LRs. The default value is **4**, which indicates that LRs are recorded from the time when **LOS_MemAlloc** is called. You can change the value based on actual requirements. This macro is configured because:
136
137- **LOS_MemAlloc** is also called internally.
138- **LOS_MemAlloc** may be encapsulated externally.
139- The number of LRs configured by **LOSCFG_MEM_RECORD_LR_CNT** is limited.
140
141Correctly setting this macro can ignore invalid LRs and reduce memory consumption.
142
143
144### Development Guidelines
145
146
147#### How to Develop
148
149Memory leak check provides a method to check for memory leak in key code logic. If this function is enabled, LR information is recorded each time when memory is allocated. When **LOS_MemUsedNodeShow** is called before and after the code snippet is checked, information about all nodes that have been used in the specified memory pool is printed. You can compare the node information. The newly added node information indicates the node where the memory leak may occur. You can locate the code based on the LR and further check whether a memory leak occurs.
150
151The node information output by calling **LOS_MemUsedNodeShow** is in the following format: <br>Each line contains information about a node. The first column indicates the node address, based on which you can obtain complete node information using a tool such as a GNU Debugger (GDB). The second column indicates the node size, which is equal to the node header size plus the data field size. Columns 3 to 5 list the LR addresses. You can determine the specific memory location of the node based on the LR addresses and the assembly file.
152
153
154```
155node        size   LR[0]      LR[1]       LR[2]
1560x10017320: 0x528 0x9b004eba  0x9b004f60  0x9b005002
1570x10017848: 0xe0  0x9b02c24e  0x9b02c246  0x9b008ef0
1580x10017928: 0x50  0x9b008ed0  0x9b068902  0x9b0687c4
1590x10017978: 0x24  0x9b008ed0  0x9b068924  0x9b0687c4
1600x1001799c: 0x30  0x9b02c24e  0x9b02c246  0x9b008ef0
1610x100179cc: 0x5c  0x9b02c24e  0x9b02c246  0x9b008ef0
162```
163
164> **CAUTION**
165>
166> Enabling memory leak check affects memory application performance. LR addresses will be recorded for each memory node, increasing memory overhead.
167
168
169#### Development Example
170
171This example implements the following:
172
1731. Call **LOS_MemUsedNodeShow** to print information about all nodes.
174
1752. Simulate a memory leak by requesting memory without releasing it.
176
1773. Call **LOS_MemUsedNodeShow** to print information about all nodes.
178
1794. Compare the logs to obtain information about the node where a memory leak occurred.
180
1815. Locate the code based on the LR address.
182
183
184#### Sample Code
185
186The sample code is as follows:
187
188The sample code can be compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. The **MemLeakTest** function is called in **TestTaskEntry**.
189
190When QEMU is running, ensure that the value of **LOSCFG_MEM_FREE_BY_TASKID** in **target_config.h** is **0**.
191
192After the memory check function is enabled, other tasks running on certain platforms may frequently print memory-related information such as "psp, start = xxxxx, end = xxxxxxx". Ignore the information or delete the print information called by **OsStackAddrGet**.
193
194
195```
196#include <stdio.h>
197#include <string.h>
198#include "los_memory.h"
199#include "los_config.h"
200
201void MemLeakTest(void)
202{
203    LOS_MemUsedNodeShow(LOSCFG_SYS_HEAP_ADDR);
204    void *ptr1 = LOS_MemAlloc(LOSCFG_SYS_HEAP_ADDR, 8);
205    void *ptr2 = LOS_MemAlloc(LOSCFG_SYS_HEAP_ADDR, 8);
206    LOS_MemUsedNodeShow(LOSCFG_SYS_HEAP_ADDR);
207}
208```
209
210
211#### Verification
212
213The log is as follows:
214
215
216```
217node         size   LR[0]       LR[1]       LR[2]
2180x20001b04:  0x24   0x08001a10  0x080035ce  0x080028fc
2190x20002058:  0x40   0x08002fe8  0x08003626  0x080028fc
2200x200022ac:  0x40   0x08000e0c  0x08000e56  0x0800359e
2210x20002594:  0x120  0x08000e0c  0x08000e56  0x08000c8a
2220x20002aac:  0x56   0x08000e0c  0x08000e56  0x08004220
223
224node         size   LR[0]       LR[1]       LR[2]
2250x20001b04:  0x24   0x08001a10  0x080035ce  0x080028fc
2260x20002058:  0x40   0x08002fe8  0x08003626  0x080028fc
2270x200022ac:  0x40   0x08000e0c  0x08000e56  0x0800359e
2280x20002594:  0x120  0x08000e0c  0x08000e56  0x08000c8a
2290x20002aac:  0x56   0x08000e0c  0x08000e56  0x08004220
2300x20003ac4:  0x1d   0x08001458  0x080014e0  0x080041e6
2310x20003ae0:  0x1d   0x080041ee  0x08000cc2  0x00000000
232
233The preceding data may vary depending on the running environment.
234```
235
236The difference between the two logs is as follows. The following memory nodes are suspected to have blocks with a memory leak.
237
238
239```
2400x20003ac4:  0x1d   0x08001458  0x080014e0  0x080041e6
2410x20003ae0:  0x1d   0x080041ee  0x08000cc2  0x00000000
242
243The preceding data may vary depending on the running environment.
244```
245
246The following is part of the assembly file:
247
248
249```
250                MemLeakTest:
251  0x80041d4: 0xb510         PUSH     {R4, LR}
252  0x80041d6: 0x4ca8         LDR.N    R4, [PC, #0x2a0]       ; g_memStart
253  0x80041d8: 0x0020         MOVS     R0, R4
254  0x80041da: 0xf7fd 0xf93e  BL       LOS_MemUsedNodeShow    ; 0x800145a
255  0x80041de: 0x2108         MOVS     R1, #8
256  0x80041e0: 0x0020         MOVS     R0, R4
257  0x80041e2: 0xf7fd 0xfbd9  BL       LOS_MemAlloc           ; 0x8001998
258  0x80041e6: 0x2108         MOVS     R1, #8
259  0x80041e8: 0x0020         MOVS     R0, R4
260  0x80041ea: 0xf7fd 0xfbd5  BL       LOS_MemAlloc           ; 0x8001998
261  0x80041ee: 0x0020         MOVS     R0, R4
262  0x80041f0: 0xf7fd 0xf933  BL       LOS_MemUsedNodeShow    ; 0x800145a
263  0x80041f4: 0xbd10         POP      {R4, PC}
264  0x80041f6: 0x0000         MOVS     R0, R0
265
266  The preceding data may vary depending on the running environment.
267```
268
269The memory node addressed by **0x080041ee** is not released after being requested in **MemLeakTest**.
270
271## Memory Corruption Check
272
273
274### Basic Concepts
275
276As an optional function of the kernel, memory corruption check is used to check the integrity of a dynamic memory pool. This mechanism can detect memory corruption errors in the memory pool in a timely manner and provide alerts. It helps reduce problem locating costs and increase troubleshooting efficiency.
277
278
279### Function Configuration
280
281**LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK** specifies the setting of the memory corruption check. This function is disabled by default. To enable the function, set this macro to **1** in **target_config.h**.
282
2831. If this macro is enabled, the memory pool integrity will be checked in real time upon each memory allocation.
284
2852. If this macro is not enabled, you can call **LOS_MemIntegrityCheck** to check the memory pool integrity when required. Using **LOS_MemIntegrityCheck** does not affect the system performance. However, the check accuracy decreases because the node header does not contain the magic number (which is available only when **LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK** is enabled).
286
287This check only detects the corrupted memory node and provides information about the previous node (because memory is contiguous, a node is most likely corrupted by the previous node). To further determine the location where the previous node is requested, you need to enable the memory leak check and use LRs to locate the fault.
288
289> **CAUTION**
290>
291> If memory corruption check is enabled, a magic number is added to the node header, which increases the size of the node header.  The real-time integrity check has a great impact on the performance. In performance-sensitive scenarios, you are advised to disable this function and use **LOS_MemIntegrityCheck** to check the memory pool integrity.
292
293
294### Development Guidelines
295
296
297#### How to Develop
298
299Check for memory corruption by calling **LOS_MemIntegrityCheck**. If no memory corruption occurs, **0** is returned and no log is output. If memory corruption occurs, the related log is output. For details, see the output of the following example.
300
301
302#### Development Example
303
304This example implements the following:
305
3061. Request two physically adjacent memory blocks.
307
3082. Use **memset** to construct an out-of-bounds access and overwrites the first four bytes of the next node.
309
3103. Call **LOS_MemIntegrityCheck** to check whether memory corruption occurs.
311
312
313#### Sample Code
314
315The sample code is as follows:
316
317The sample code can be compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. The **MemIntegrityTest** function is called in **TestTaskEntry**.
318
319When QEMU is running, ensure that the value of **LOSCFG_MEM_FREE_BY_TASKID** in **target_config.h** is **0**.
320
321Because the exception is triggered intentionally, you need to restart QEMU when the execution is complete. For example, open a new terminal and run **killall qemu-system-arm**.
322
323
324```
325#include <stdio.h>
326#include <string.h>
327#include "los_memory.h"
328#include "los_config.h"
329
330void MemIntegrityTest(void)
331{
332    /* Request two physically adjacent memory blocks. */
333    void *ptr1 = LOS_MemAlloc(LOSCFG_SYS_HEAP_ADDR, 8);
334    void *ptr2 = LOS_MemAlloc(LOSCFG_SYS_HEAP_ADDR, 8);
335    /* Construct an out-of-bounds access to cause memory corruption. The memory block of the first node is 8 bytes. Clearing 12 bytes overwrites the header of the second memory node. */
336    memset(ptr1, 0, 8 + 4);
337    LOS_MemIntegrityCheck(LOSCFG_SYS_HEAP_ADDR);
338}
339```
340
341
342#### Verification
343
344The log is as follows:
345
346
347```
348
349/* Error information indicating the field corrupted. In this example, the first four bytes of the next node are cleared, that is, the magic number field is corrupted. */
350[ERR][IT_TST_INI][OsMemMagicCheckPrint], 1664, memory check error!
351memory used but magic num wrong, magic num = 0x0
352
353 /* Key information about the corrupted node and its previous node, including the address of the previous node, magic number of the node, and sizeAndFlag of the node. In this example, the magic number of the corrupted node is cleared. */
354 broken node head: 0x2103d7e8  0x0  0x80000020, prev node head: 0x2103c7cc  0xabcddcba  0x80000020
355
356 /*The node LR information can be output only after the memory leak check is enabled. */
357 broken node head LR info:
358 LR[0]:0x2101906c
359 LR[1]:0x0
360 LR[2]:0x0
361
362 /* Based on the LR information, you can determine where the previous node in requsted in the assembly file and check the use of the node. */
363 pre node head LR info:
364 LR[0]:0x2101906c
365 LR[1]:0x0
366 LR[2]:0x0
367
368 /* Addresses of the corrupted node and its previous node. */
369[ERR][IT_TST_INI]Memory integrity check error, cur node: 0x2103d784, pre node: 0x0
370
371 The preceding data may vary depending on the running environment.
372```
373