[Linux:Kernel] 커널 메모리 누수 검출기

이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL license).

Documentation/kmemleak.txt

번역: 양정석(dasomoli@gmailREMOVETHIS.com)

커널 메모리 누수 검출기
=======================

소개
—-

Kmemleak은 주인없는 객체들을 해제하지는 않고 그저 /sys/kernel/debug/kmemleak을
통해 보고하는 차이가 있는 가비지 컬렉터 추적
(http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29#Tracing_garbage_collectors)
과 비슷한 방법으로, 가능한 커널 메모리 누수를 검출하는 방법을 제공합니다. 유저-공간의
애플리케이션에서 메모리 누수를 검출하는 데 Valgrind 도구(memcheck –leak-check)가
유사한 방법을 사용합니다.

지원되는 아키텍처를 위해 lib/Kconfig.debug 안의 DEBUG_KMEMLEAK 의존성을 체크하세요.

사용법
——

“Kernel hacking” 안의 CONFIG_DEBUG_KMEMLEAK이 켜져야 합니다. 한 커널 스레드가
메모리를 (기본값으로) 매 10분마다 스캔하고, 찾은 새로운 참조되지 않는 객체들의
수를 출력합니다. 모든 가능한 메모리 누수의 자세할 사항을 보기 위해서는:

  # mount -t debugfs nodev /sys/kernel/debug/
  # cat /sys/kernel/debug/kmemleak

중간에 메모리 스캔을 작동시키려면:

  # echo scan > /sys/kernel/debug/kmemleak

모든 현재의 가능한 메모리 누수의 목록을 지우려면:

  # echo clear > /sys/kernel/debug/kmemleak

그려면 새로운 누수는 /sys/kernel/debug/kmemleak을 다시 읽음으로써 나올 것입니다.

주인없는 객체들은 그들이 할당되었던 순서로 나열되고, 목록의 맨 앞의 한 객체는
주인없는 것으로 보고되는 다른 이어지는 객체들의 원인이 될 것입니다.

메모리 스캐닝 파라미터들은 런타임에 /sys/kernel/debug/kmemleak 파일에 씀으로써
바뀔 수 있습니다. 다음 파라미터들이 지원됩니다:

  off           – kmemleak 끄기 (되돌릴 수 없음)
  stack=on      – 태스크 스택 스캐닝 켜기 (기본값)
  stack=off     – 태스크 스택 스캐닝 끄기
  scan=on       – 자동 메모리 스캐닝 스레드 시작하기 (기본값)
  scan=off      – 자동 메모리 스캐닝 스레드 멈추기
  scan=<secs>   – 자동 메모리 스캐닝 주기를 초로 설정
                  (기본 600, 0은 자동 스캐닝 멈춤)
  scan          – 한 번의 메모리 스캔 작동
  clear         – 현재 메모리 누수 혐의자들의 목록 지우기, 모든 현재 보고된
                  참조되지 않는 객체를 회색으로 표시함으로써 끝
  dump=<addr>   – <주소>에서 찾아진 객체에 대한 정보를 덤프

Kmemleak은 또한 부팅 시간에 “kmemleak=off”를 커널 커맨드 라인에 전달함으로써
꺼질 수도 있습니다.

kmemleak이 초기화되기 전에 메모리는 할당되거나 해제될 것이고, 이들 동작은 초기
로그 버퍼 안에 저장될 것입니다.이 버퍼의 크기는
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE 옵션을 통해 설정됩니다.

기본 알고리즘
————-

kmalloc, vmalloc, kmem_cache_alloc과 그 친구들을 통한 메모리 할당은 추적되고,
그 포인터들은, 크기나 스택 추적같은 추가 정보와 함께 prio 검색 트리 안에
저장됩니다. 그에 맞는 해제 함수 호출들은 기록되고 그 포인터들은 kmemleak 자료
구조로부터 제거됩니다.

메모리의 할당된 블록이 (저장된 레지스터들을 포함하는) 메모리 스캐닝을 통해
그 시작 주소나 블록 안의 어떤 위치로의 어떤 포인터도 찾을 수 없다면, 주인없는
것으로 여겨집니다. 이것은 커널이 할당된 블록의 주소를 해제하는 함수로 넘길 수
있는 방법이 없을 것이고, 그래서 그 블록이 메모리 누수라고 여겨진다는 것을
의미합니다.


스캐닝 알고리즘 단계:

  1. 모든 객체를 하얗게 표시 (남은 하얀 객체들은 나중에 주인없는 것으로 여겨질
     것 입니다.)
  2. 데이터 섹션과 스택들의 메모리 시작을 스캔, prio 검색 트리 안에 저장된 주소들에
     대조되는 값을 검사. 하얀 객체로의 포인터가 있다면, 그 객체는 회색 목록에
     추가합니다.
  3. 회색 집합이 끝날 때까지 매칭되는 주소들을 위해 회색 객체들을 스캔(어떤
     하얀 객체들은 회색이 되고, 회색 목록의 끝에 추가됩니다)
  4. 남아있는 하얀 객체들은 주인없는 것으로 여겨지고, /sys/kernel/debug/kmemleak
     을 통해 보고됩니다.

어떤 할당된 메모리 블록들은 커널의 내부 자료 구조 안에 저장된 포인터들을 가지고,
그들은 주인없는 것으로 검출될 수 없습니다. 이를 피하기 위해서, kmemleak은
또한, 찾아질 필요가 있는 블록 주소 범위 내의 주소를 가리키는 값의 수를 저장할
수 있어서 그 블록은 누수로 여겨지지 않습니다. 한 예로 __vmalloc()이 있습니다.

kmemleak으로 특정 섹션 테스트하기
———————————

초기 부팅 상에서 여러분의 /sys/kernel/debug/kmemleak 출력 페이지는 좀
많을 것입니다. 이것은 또한 여러분이 개발을 할 때 매우 버그가 많은 코드를
가진다면 있을 수 있습니다. 이런 상황에서 일하기 위해서
/sys/kernel/debug/kmemleak 출력으로부터 모든 보고된 참조되지 않는 객체를
지우기 위해서 여러분은 ‘clear’ 명령을 사용할 수 있습니다. ‘clear’ 후의
‘scan’을 통해, 여러분은 새로운 참조되지 않는 객체들을 찾을 수 있습니다;
이것은 코드의 특정 섹션을 테스팅하는 데 도움이 될 것입니다.

깨끗한 kmemleak으로 하고 싶을 때 크리티컬 섹션을 테스트하기 위해서는
이렇게 하세요:

  # echo clear > /sys/kernel/debug/kmemleak
  … 여러분의 커널이나 모듈을 테스트 …
  # echo scan > /sys/kernel/debug/kmemleak

그 후, 여느 때처럼 여러분의 보고서를 얻기 위해서:

  # cat /sys/kernel/debug/kmemleak

Kmemleak API
————

함수들의 프로토 타입은 /include/linux/kmemleak.h 헤더를 보세요.

kmemleak_init            – kmemleak 초기화
kmemleak_alloc           – 하나의 메모리 블록 할당 알림
kmemleak_alloc_percpu    – 하나의 percpu 메모리 블록 할당 알림
kmemleak_free            – 하나의 메모리 블록 해제 알림
kmemleak_free_part       – 하나의 부분 메모리 블록 해제 알림
kmemleak_free_percpu     – 하나의 percpu 메모리 블록 해제 알림
kmemleak_not_leak        – 누수가 아닌 것으로 객체 표시
kmemleak_ignore          – 스캔하지 않거나, 누수로 객체를 보고하지 않음
kmemleak_scan_area       – 메모리 블록내의 스캔 지역 추가
kmemleak_no_scan         – 메모리 블록 스캔하지 않음
kmemleak_erase           – 포인터 변수 안의 이전 값을 지움
kmemleak_alloc_recursive – kmemleak_alloc과 같지만 재귀성 검사
kmemleak_free_recursive  – kmemleak_free와 같지만 재귀성 검사

거짓 검출/미검출의 처리
———————–

거짓 미검출은 진짜 메모리 누수(주인없는 개체)가 메모리 스캐닝 중에 찾은 값들이
객체들을 가리키고 있어서 kmemleak에 의해 보고되지 않는 것입니다.
거짓 미검출의 수를 줄이기 위해서, kmemleak은 kmemleak_ignore,
kmemleak_scan_area, kmemleak_no_scan, 그리고 kmemleak_erase 함수들
(위를 보세요)을 제공합니다. 태스크 스택은 또한 거짓 미검출의 양을 늘려서,
그들의 스캐닝은 기본값으로 켜지지 않습니다.

거짓 검출은 객체들이 메모리 누수(주인없음)이 된 것처럼 잘못 보고되는 것입니다.
누수가 아닌 것으로 알려진 객체들을 위해서, kmemleak은 kmemleak_not_leak
함수를 제공합니다. kmemleak_ignore 또한 그 메모리 블록이 다른 포인터들을
포함하는 것으로 알려지지 않았다면 사용될 수 있고, 더 이상 스캔되지 않을
것 입니다.

보고된 누수들 중 어떤 것들은 CPU 레지스터나 스택 안에 임시적으로 저장되는
포인터들 때문에, 특히 SMP 시스템 상에서, 그저 일시적입니다. Kmemleak은
메모리 누수로 보고되는 객체의 최소 나이를 표시하는 (기본은 1000인)
MSECS_MIN_AGE를 정의합니다.

제한과 결점
———–

주요 결점은 메모리 할당과 해제의 성능을 떨어뜨린 다는 것 입니다. 다른 불이익을
피하기 위해서, 메모리 스캐닝은 /sys/kernel/debug/kmemleak 파일을 읽을 때만
수행됩니다. 어쨌든, 이 도구는 성능이 가장 중요한 요구 사항이 아닌 곳에서의
디버깅을 목적으로 합니다.

알고리즘을 간단히 유지하기 위해서, kmemleak은 한 블록의 주소 범위 안의
어떤 주소를 가리키는 값들을 스캔합니다. 이것은 거짓 미검출의 수를 늘릴 것입니다.
그러나, 그것은 진짜 메모리 누수가 결국 보여지게 되도록 할 것입니다.

거짓 미검출의 다른 출처는 포인터가 아닌 값들안에 저장된 데이터입니다. 이후
버전에서는 kmemleak이 할당된 구조체 안의 포인터 멤버만 스캔할 수 있을 것입니다.
이 기능은 위에서 설명한 많은 거짓 미검출을 해결할 것 입니다.

이 도구는 거짓 검출을 보고할 수 있습니다. 할당된 블록이 해제를 필요로 하지 않는
곳(init_call 함수 안의 어떠한 경우), 그 포인터가 보통의 container_of
매크로가 아닌 다른 방법으로 계산되는 곳, 또는 그 포인터가 kmemleak이 스캔하지
않는 위치 안에 저장된 포인터인 곳 같은 경우들이 있습니다.

페이지 할당과 ioremap은 추적하지 않습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다