Kernel Memory Leak Detector
소개(Introduction)
kmemleak은 tracing garbage collector와 비슷한 방식으로 possible kernel memory leak을 찾을 수 있게 해준다. 차이점은 orphan object(메모리 누수가 의심되는 객체)들은 해제되지 않고 /sys/kernel/debug/kmemleak 을 통해 report된다는 것 뿐이다. valgrind tool(memcheck --leak-check)도 user space 어플리케이션의 memory leak을 찾기 위해 비슷한 방법을 사용한다. kmemleak은 x86, arm, powerpc, sparc, sh, microblaze, ppc, mips, s390, metga, tile에서 지원한다.
Kmemleak provides a way of detecting possible kernel memory leaks in a way similar to a tracing garbage collector, with the difference that the orphan objects are not freed but only reported via /sys/kernel/debug/kmemleak. A similar method is used by the Valgrind tool (memcheck --leak-check) to detect the memory leaks in user-space applications. Kmemleak is supported on x86, arm, powerpc, sparc, sh, microblaze, ppc, mips, s390, metag and tile.
사용법(Usage)
우선 kernel menuconfig의 Kernel hacking에서 CONFIG_DEBUG_KMEMLEAK이 활성화되어야 한다. 커널 스레드는 10분마다(기본값) 메모리를 스캔하고 새롭게 찾은 unreferenced object의 갯수를 출력한다. possible memory leak에 대한 자세한 정보를 출력하기 위해서는 아래와 같이 한다.
CONFIG_DEBUG_KMEMLEAK in "Kernel hacking" has to be enabled. A kernel thread scans the memory every 10 minutes (by default) and prints the number of new unreferenced objects found. To display the details of all the possible memory leaks:
# mount -t debugfs nodev /sys/kernel/debug/
# cat /sys/kernel/debug/kmemleak
즉시 메모리를 스캔하기 위해서는 아래와 같이 한다.
To trigger an intermediate memory scan:
# echo scan > /sys/kernel/debug/kmemleak
possible memory leak의 목록을 제거하려면 아래와 같이 한다.
To clear the list of all current possible memory leaks:
# echo clear > /sys/kernel/debug/kmemleak
/sys/kernel/debug/kmemleak을 다시 읽으면 새로운 possible memory leak를 볼 수 있다. orphan object들은 할당되는 순서대로 정렬되어 출력된다는 것을 유념하자. 그리고 목록의 첫 객체는 뒤이은 객체를 orphan object로 report하게 만들수도 있다. memory scanning parameter는 동작중에 /sys/kernel/debug/kmemleak에 값을 쓰면 수정될 수 있다. 아래에 나열된 파라미터를 지원한다.
New leaks will then come up upon reading /sys/kernel/debug/kmemleak again. Note that the orphan objects are listed in the order they were allocated and one object at the beginning of the list may cause other subsequent objects to be reported as orphan. Memory scanning parameters can be modified at run-time by writing to the /sys/kernel/debug/kmemleak file. The following parameters are supported:
- off
- kmemleak 기능을 비활성화함 (irreversible)
- stack=on
- 태스크 스택을 스캔하도록 함 (default)
- stack=off
- 태스크 스택의 스캔하지 않도록함
- scan=on
- 주기적인 메모리 스캔을 시작 함 (default)
- scan=off
- 주기적인 메모리 스캔을 멈춤
- scan=<secs>
- 주기적인 메모리 스캔의 주기를 초단위로 설정함(기본값은 600, 0을 주면 자동 스캔이 정지된다.)
- scan
- 메모리를 스캔함
- clear
- 현재의 memory leak이 추정되는 object list를 클리어함. 모든 보고된 unreferenced object를 grey로 마킹하거나 kmemleak이 비활성화되었다면 kmemleak object를 모두 free한다.
- dump=<addr>
- addr 에서 찾은 오브젝트에 대한 정보를 출력함
kmemleak은 커널 커맨드라인에 "kmemleak=off"를 전달하면 부팅타임에 비활성화될 수 있다.
Kmemleak can also be disabled at boot-time by passing "kmemleak=off" on the kernel command line.
메모리는 kmemleak이 초기화되기 전에 할당되거나 해제될 수도 있다. 이런 정보는 early log buffer에 기록된다. log buffer의 크기는 CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE 옵션에 의해 결정된다.
Memory may be allocated or freed before kmemleak is initialised and these actions are stored in an early log buffer. The size of this buffer is configured via the CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE option.
만약 CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF가 활성화되어 있다면, kmemleak은 기본적으로 비활성화된다. 하지만 커맨드라인에 "kmemleak=on" 를 전달해서 이 기능을 활성화할 수 있다.
If CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF are enabled, the kmemleak is disabled by default. Passing "kmemleak=on" on the kernel command line enables the function.
기본 알고리즘(Basic Algorithm)
kmalloc(), vmalloc(), kmem_cache_alloc() API 같은 메모리 할당은 포인터들과 크기, 스택 트레이스 같은 추가적인 정보들과 함께 레드블랙트리에 저장되어 추적된다. 연관된 메모리 해제 함수에서는 추적하던 포인터를 kmemleak 자료구조에서 제거한다.
The memory allocations via kmalloc, vmalloc, kmem_cache_alloc and friends are traced and the pointers, together with additional information like size and stack trace, are stored in a rbtree. The corresponding freeing function calls are tracked and the pointers removed from the kmemleak data structures.
할당된 메모리 블럭은 해당 블럭의 시작 주소 혹은 블럭 안의 어느영역이든 가리키는 포인터가 메모리나 saved register에서 스캔되지 않는다면 고아 객체이라고 취급된다. 이것은 커널이 할당된 블럭의 주소를 메모리 해제 함수에게 넘길 방법이 없음을 의미한다. 따라서 이런 메모리 블럭은 릭이라고 할 수있다.
An allocated block of memory is considered orphan if no pointer to its start address or to any location inside the block can be found by scanning the memory (including saved registers). This means that there might be no way for the kernel to pass the address of the allocated block to a freeing function and therefore the block is considered a memory leak.
스캔 알고리즘 절차는 아래와 같다.
The scanning algorithm steps:
- 모든 오브젝트를 white(계속 white로 남아있게 되면 나중에 고아로 취급한다)로 마킹한다.
mark all objects as white (remaining white objects will later be considered orphan) - 메모리 스캔은 데이터 섹션과 스택에서부터 시작한다. 레드블렉 트리에 저장된 주소와 데이터섹션과 스택의 값을 비교한다. 만약 white 오브젝트를 가리키는 포인터가 데이터 섹션이나 스택에서 발견되면, 오브젝트는 gray 리스트에 추가된다.
scan the memory starting with the data section and stacks, checking the values against the addresses stored in the rbtree. If a pointer to a white object is found, the object is added to the gray list. - gray 설정이 끝날때까지 매칭되는 주소에 맞는 gray 오브젝트를 찾는다.(어떤 white 오브젝트는 gray 오브젝트가 되고 gray 리스트의 끝에 추가된다)
scan the gray objects for matching addresses (some white objects can become gray and added at the end of the gray list) until the gray set is finished. - 남은 오브젝트는 고아로 취급되고 /sys/kernel/debug/kmemleak에 보고된다.
the remaining white objects are considered orphan and reported via /sys/kernel/debug/kmemleak.
몇몇 할당된 메모리 블럭은 포인터들을 커널의 내부 자료구조에 가지고 있다. 이 메모리 블럭들은 고아로 감지되지 않는다. 이런 상황을 피하기 위해, kmemleak은 해당 메모리 블럭이 릭으로 감지되지 않기 위해 필요한 블럭 영역 안의 주소를 가리키는 값의 개수를 저장할 수 있다. 하나의 예가 __vmalloc()이다.
Some allocated memory blocks have pointers stored in the kernel's internal data structures and they cannot be detected as orphans. To avoid this, kmemleak can also store the number of values pointing to an address inside the block address range that need to be found so that the block is not considered a leak. One example is __vmalloc().
역주) 할당받은 메모리의 가상 주소를 값으로 가지고 있는 커널의 자료 구조(특정 필드)가 있는 경우이다. vmalloc의 경우에는 vm_struct 구조체의 addr 필드가 할당된 가상 주소를 가지고 있다. 따라서 별도로 처리하지 않으면 릭이라고 감지되지 않을것이므로 별도 처리가 필요하다.
kmemleak을 이용해 특정섹션 테스트하기 (Testing specific sections with kmemleak)
초기 부팅시에 /sys/kernel/debug/kmemleak 결과가 상당히 광범위 할 수 있다. 개발중이라 코드에 버그가 많은 상황도 동일하다. 이런 상황을 해결하기 위해 임시방편으로 clear 명령어를 써서 모든 보고된 unreferenced 오브젝트를 /sys/kernel/debug/kmemleak 출력에서 제거할 수 있다.
Upon initial bootup your /sys/kernel/debug/kmemleak output page may be quite extensive. This can also be the case if you have very buggy code when doing development. To work around these situations you can use the 'clear' command to clear all reported unreferenced objects from the /sys/kernel/debug/kmemleak output.
clear 명령어 이후에 scan 명령어를 사용하면 새로운 unreferenced 오브젝트들을 찾을 수 있다. 이 명령어는 코드의 특정 섹션을 테스트하는데 도움이 된다.
By issuing a 'scan' after a 'clear' you can find new unreferenced objects; this should help with testing specific sections of code.
특정 중요 영역을 테스트하기 위해서는 아래와 같이 하면 된다:
To test a critical section on demand with a clean kmemleak do:
# echo clear > /sys/kernel/debug/kmemleak
... test your kernel or modules ...
# echo scan > /sys/kernel/debug/kmemleak
그리고 스캔 결과는 아래와 같이 얻는다:
Then as usual to get your report with:
# cat /sys/kernel/debug/kmemleak
kmemleak 오브젝트 해제하기(Freeing kmemleak internal objects)
사용자 혹은 치명적인 에러로 인해 kmemleak이 비활성화된 이후에도 이전에 찾은 memory leaks을 접근하도록 하기 위해, kmemleak이 비활성화되어도 내부 kmemleak 오브젝트는 해제되지 않는다. 이러한 오브젝트가 실제 메모리의 상당 부분을 차지할 수 있다. 이 경우에는 아래와 같이 메모리를 회수 할 수 있다.
To allow access to previously found memory leaks after kmemleak has been disabled by the user or due to an fatal error, internal kmemleak objects won't be freed when kmemleak is disabled, and those objects may occupy a large part of physical memory. In this situation, you may reclaim memory with:
# echo clear > /sys/kernel/debug/kmemleak
Kmemleak API
함수의 프로토타입은 include/linux/kmemleak.h 헤더파일을 확인하자.
See the include/linux/kmemleak.h header for the functions prototype.
kmemleak_init - kmemleak을 초기화함
kmemleak_alloc - 메모리 블럭의 할당을 알림(notify of a memory block allocation)
kmemleak_alloc_percpu - percpu 메모리 블럭의 할당을 알림(notify of a percpu memory block allocation)
kmemleak_free - 메모리 블럭의 해제를 알림(notify of a memory block freeing)
kmemleak_free_part - 메모리 블럭의 부분해제를 알림(notify of a partial memory block freeing
kmemleak_free_percpu - percpu 메모리 블럭의 해제를 알림(notify of a percpu memory block freeing)
kmemleak_update_trace - 오브젝트를 할당시의 콜스택을 갱신함(update object allocation stack trace)
kmemleak_not_leak - 오브젝트가 leak이 아니라고 설정함(mark an object as not a leak)
kmemleak_ignore - 오브젝트를 스캔하지 않거나 leak이라고 보고하지 않음(do not scan or report an object as leak)
kmemleak_scan_area - 스캔 영역을 추가함(add scan areas inside a memory block)
kmemleak_no_scan - 메모리 블럭을 스캔하지 않도록 함(do not scan a memory block)
kmemleak_erase - erase an old value in a pointer variable
kmemleak_alloc_recursive - as kmemleak_alloc but checks the recursiveness
kmemleak_free_recursive - as kmemleak_free but checks the recursiveness
역주) 커널 버전 4.16 기준으로 아래 함수들이 추가되었다.
kmemleak_vmalloc() - vmalloc()으로 메모리 블럭이 할당됨을 알림
kmemleak_ignore_phys() - kmemleak_igonore()와 비슷하지만 물리주소를 인자로 전달하는것이 다름.
kmemleak_free_recursive() - ??
false positives/negative 처리하기
false negative는 실제 고아 오브젝트이지만 오브젝트를 가리키는 값이 스캔되기 때문에 kmemleak에 의해 보고되지 않는다. false negative를 줄이기 위해 kmemleak은 kmemleak_ignore, kmemleak_scan_area, kmemleak_no_scan 및 kmemleak_erase 함수를 제공한다 (위 참조).
The false negative are real memory leaks (orphan objects) but not reported by kmemleak because values found during the memory scanning point to such objects. To reduce the number of false negative, kmemleak provides the kmemleak_ignore, kmemleak_scan_area, kmemleak_no_scan and kmemleak_erase functions (see above).
또한 태스크 스택은 false negative의 양을 증가시킬 수 있으므로 기본적으로 이곳에 대한 scan은 활성화되지 않는다. false positives는 메모리 릭으로 잘못 보고된 오브젝트를 말한다. 릭이 아닌 것으로 알려진 오브젝트를 위해 kmemleak은 kmemleak_not_leak 함수를 제공합니다.
The task stacks also increase the amount of false negative and their scanning is not enabled by default. The false positives are objects wrongly reported as being memory leaks (orphan). For objects known not to be leaks, kmemleak provides the kmemleak_not_leak function.
kmemleak_ignore는 또한 메모리 블록이 다른 포인터를 포함하지 않는 것으로 알려져 있다면 더 이상 스캔되지 않도록 하기 위해서도 사용될 수 있다. 보고된 릭 중의 일부는 CPU 레지스터 또는 스택에 일시적으로 저장된 포인터로 인해 일시적으로 릭일 수 있다. 특히 SMP 시스템에서는 그럴 확률이 더 높다. kmemleak은 메모리 릭으로 보고될 오브젝트의 최소 보존 기간을 나타내는 MSECS_MIN_AGE (기본값은 1000)를 정의한다.
The kmemleak_ignore could also be used if the memory block is known not to contain other pointers and it will no longer be scanned. Some of the reported leaks are only transient, especially on SMP systems, because of pointers temporarily stored in CPU registers or stacks. Kmemleak defines MSECS_MIN_AGE (defaulting to 1000) representing the minimum age of an object to be reported as a memory leak.
역주)
false positive - memory leak이 아닌데 leak이라고 보고되는 경우
false negative - memory leak인데 leak으로 보고가 안되는 경우
true positive - memory leak을 정상 보고한 경우
true negative - memory leak이 아닌 것을 보고 안하는 경우
한계와 단점(Limitations and Drawbacks)
가장 큰 단점은 메모리 할당과 해제의 성능 저하이다. 다른 penalty를 피하기 위해 /sys/kernel/debug/kmemleak 파일을 읽을 때만 메모리 검사가 수행된다. 어쨌든 이 도구는 성능이 가장 중요한 요구 사항이 아닌 상황에서 디버깅을 목적으로 사용한다.
The main drawback is the reduced performance of memory allocation and freeing. To avoid other penalties, the memory scanning is only performed when the /sys/kernel/debug/kmemleak file is read. Anyway, this tool is intended for debugging purposes where the performance might not be the most important requirement.
알고리즘을 단순하게 유지하기 위해 kmemleak은 블록의 주소 범위 안에 있는 모든 주소를 가리키는 값을 검색한다. 이로 인해 잘못된 false negative가 증가 할 수 있다. 그러나 결국 진짜 메모리릭은 나타날 수 있다. false negative의 또 다른 원인은 포인터가 아닌 곳에 저장된 데이터이다. 향후 버전에서 kmemleak는 할당 된 구조에서 포인터 멤버만 스캔 할 수 있다.
To keep the algorithm simple, kmemleak scans for values pointing to any address inside a block's address range. This may lead to an increased number of false negative. However, it is likely that a real memory leak will eventually become visible. Another source of false negative is the data stored in non-pointer values. In a future version, kmemleak could only scan the pointer members in the allocated structures.
이 기능은 위에서 설명한 많은 잘못된 false negative case를 해결한다. 이 도구는 false positive를 보고할 수 있다. 할당 된 블록을 해제 할 필요가 없는 경우(init_call 함수의 일부 경우) 이거나 포인터가 일반적인 container_of 매크로가 아닌 다른 메소드에 의해 계산되거나 포인터가 kmemleak에 의해 스캔되지 않은 위치에 저장되는 사례가 있다. 페이지 단위 할당과 ioremap은 tracing하지 않는다.
This feature would solve many of the false negative cases described above. The tool can report false positives. These are cases where an allocated block doesn't need to be freed (some cases in the init_call functions), the pointer is calculated by other methods than the usual container_of macro or the pointer is stored in a location not scanned by kmemleak. Page allocations and ioremap are not tracked.
'커널 번역(기사,문서)' 카테고리의 다른 글
[번역] scheduler/completion.txt (0) | 2018.09.08 |
---|---|
[번역] scheduler/sched-arch.txt (0) | 2018.09.08 |
[번역] scheduler/sched-rt-group.txt (0) | 2018.09.08 |
[번역] scheduler/sched-domains.txt (0) | 2018.09.07 |
[번역] lwn - CFS bandwidth control (0) | 2018.09.07 |