Skip to content

Commit 023fdc4

Browse files
committed
add leakless ostest story
1 parent f30d53d commit 023fdc4

File tree

3 files changed

+157
-1
lines changed

3 files changed

+157
-1
lines changed

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
Some notes on NuttX
22

3-
- How [clean](leakless-ostest.md) is NuttX?
3+
How [clean](leakless-ostest) is NuttX?
44

docs/leakless-ostest.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Leakless ostest
2+
3+
This document findings on the kernel memory growth issue when running `ostest` app.
4+
5+
## The Problem
6+
7+
The NSH command `free` shows memory information like below:
8+
9+
```
10+
ABC
11+
nsh> free
12+
total used free maxused maxfree nused nfree
13+
Umem: 33378172 7036 33371136 7020 33371120 23 2
14+
```
15+
16+
Where we can notice the used memory in kernel is `7036` bytes after a boot. After using some built-in commands or the simple `hello` app, it doesn't change.
17+
18+
However, after using the `getprime 4` app, the used memory grows:
19+
20+
```
21+
nsh> free
22+
total used free maxused maxfree nused nfree
23+
Umem: 33377308 7052 33370256 18324 33363472 23 4
24+
```
25+
26+
It grows further after running the `ostest` app:
27+
28+
```
29+
nsh> free
30+
total used free maxused maxfree nused nfree
31+
Umem: 33377308 9140 33368168 46012 33363616 42 7
32+
```
33+
34+
Are these implying that we are leaking memory?
35+
36+
## Findings
37+
38+
After some investigations, we have findings below:
39+
40+
- There is undelivered message leakage in timed mqueue that is fixed in [patch 12402](https://github.com/apache/nuttx/pull/12402). This is a true leak that is triggered by the `ostest` app.
41+
42+
- There is a global list of free sigaction objects in NuttX which are allocated dynamically and never freed after use. Thus when `ostest` app runs for the first time, used memory grows due to their allocation, but they don't grow if `ostest` app runs again. [Patch 12406](https://github.com/apache/nuttx/pull/12406) adds pre-allocated sigaction list and reclaims dynamically allocated ones timely thus we won't see the initial growth any more.
43+
44+
- There is a pid hash table which is a dynamic array of TCB pointers. The array has a very small initial length, thus when multi-threading apps like `getprime` or `ostest` are used, it grows quickly to accomdate more thread ids. The array never shrinks currently. The frequent initial growth feels like leakage. so [patch 12427](https://github.com/apache/nuttx/pull/12427) contains a solution to avoid frequent growth.
45+
46+
- There are a few folders created by `ostest` when doing mqueue tests, they are not cleaned timely thus their inodes are still using kernel memory.
47+
48+
## Result
49+
50+
After applying above patches, with proper configuration and clean up commands, we can see stable system memory usage after running `ostest` like below:
51+
52+
```
53+
nsh> free
54+
total used free maxused maxfree nused nfree
55+
Umem: 33374748 7084 33367664 7068 33367632 21 2
56+
nsh> ostest >/dev/null >>/dev/null
57+
stdio_test: write fd=2
58+
stdio_test: Standard I/O Check: fprintf to stderr
59+
setvbuf_test: Using NO buffering
60+
setvbuf_test: Using default FULL buffering
61+
setvbuf_test: Using FULL buffering, buffer size 64
62+
setvbuf_test: Using FULL buffering, pre-allocated buffer
63+
setvbuf_test: Using LINE buffering, buffer size 64
64+
setvbuf_test: Using FULL buffering, pre-allocated buffer
65+
nsh> echo $?
66+
0
67+
nsh> rm -r /var
68+
nsh> free
69+
total used free maxused maxfree nused nfree
70+
Umem: 33374748 7084 33367664 46108 33367632 21 2
71+
```
72+
73+
So with a few improvements, our NuttX build can finish `ostest` cleanly.
74+
75+
Though real world app situations may vary, but for simple apps like `ostest`, being clean makes people more confident about NuttX.

leakless-ostest.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
2+
3+
## Problem
4+
5+
The NSH command `free` shows memory information like below:
6+
7+
```
8+
nsh> free
9+
total used free maxused maxfree nused nfree
10+
Umem: 33378172 7036 33371136 7020 33371120 23 2
11+
```
12+
13+
Where we can notice the `used` memory in kernel heap is 7036 bytes after a clean boot.
14+
15+
How this number changes during the use of NuttX? After using some built-in commands or the simple `hello` app, it doesn't change.
16+
17+
However, after using the `getprime 4` app, the used memory grows:
18+
19+
```
20+
nsh> free
21+
total used free maxused maxfree nused nfree
22+
Umem: 33377308 7052 33370256 18324 33363472 23 4
23+
```
24+
25+
It grows further after running the `ostest` app once:
26+
27+
```
28+
nsh> free
29+
total used free maxused maxfree nused nfree
30+
Umem: 33377308 9140 33368168 46012 33363616 42 7
31+
```
32+
33+
Are these implying that the kernel is leaking memory?
34+
35+
## Findings
36+
37+
After some investigations, we have a few findings:
38+
39+
- There is a pid hash table which is actually dynamic array of TCB pointers. The table has a very small initial length, thus when multi-threading apps like `getprime` or `ostest` are used, the table grows to accomdate more thread ids. The table doesn't shrink currently. This isn't a real leakage but it is a little misleading, and frequent growth may lead to more memory fragements. So patch 12427 adds a `PIDHASH_INITIAL_LENGTH` so that to avoid unnecessary growth.
40+
41+
- There is undelivered message leakage in timed mqueue source, as resolved in patch 12402. This is a true leak. This is triggered by `ostest` app usage.
42+
43+
- There is a global list of free sigaction objects which are allocated dynamically and never freed after use. Thus when `ostest` app runs for the first time, used memory grows due to their allocation, but they don't grow if `ostest` app runs again. Patch 12406 adds pre-allocated sigaction list and reclaims dynamically allocated ones timely thus we won't see used memory growth due to this list.
44+
45+
- There is a few folders created by `ostest` when using mqueue functions, they are not cleaned timely thus their inodes are using kernel memory.
46+
47+
## Result
48+
49+
After applying above patches and with proper clean up commands, we can see stable used memory before and after using the `ostest` app:
50+
51+
```
52+
nsh> free
53+
total used free maxused maxfree nused nfree
54+
Umem: 33374748 7084 33367664 7068 33367632 21 2
55+
nsh> ostest >/dev/null >>/dev/null
56+
stdio_test: write fd=2
57+
stdio_test: Standard I/O Check: fprintf to stderr
58+
setvbuf_test: Using NO buffering
59+
setvbuf_test: Using default FULL buffering
60+
setvbuf_test: Using FULL buffering, buffer size 64
61+
setvbuf_test: Using FULL buffering, pre-allocated buffer
62+
setvbuf_test: Using LINE buffering, buffer size 64
63+
setvbuf_test: Using FULL buffering, pre-allocated buffer
64+
inode_alloc: 0x8002e6a8 var/mqueue/mqueue
65+
inode_alloc: 0x8002e6d0 mqueue/mqueue
66+
inode_alloc: 0x80030ff8 mqueue
67+
nxmq_alloc_msgq: 0x80031028
68+
inode_alloc: 0x80030ff8 timedmq
69+
nxmq_alloc_msgq: 0x80031028
70+
nxmq_alloc_msg: 0x80031090
71+
nxmq_alloc_msg: 0x800310c0
72+
nsh> echo $?
73+
0
74+
nsh> rm -r /var
75+
nsh> free
76+
total used free maxused maxfree nused nfree
77+
Umem: 33374748 7084 33367664 46108 33367632 21 2
78+
```
79+
80+
So with a few improvements, our build of NuttX not only finishes `ostest`, but also does it cleanly!
81+

0 commit comments

Comments
 (0)