이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL license).
Documentation/dynamic-debug-howto.txt
번역: 양정석<dasomoli@gmailREMOVETHIS.com>
소개
====
이 문서는 어떻게 동적 디버그 (Dynamic Debug:dyndbg) 기능을 사용하는지 설명합니다.
동적 디버그는 여러분이 추가적인 커널 정보를 얻을 수 있도록 동적으로 커널 코드를 켜고/끌
수 있도록 설계되었습니다. 현재는 CONFIG_DYNAMIC_DEBUG가 설정되어 있다면, 모든
pr_debug()/dev_dbg() 그리고 print_hex_dump_debug()/
print_hex_dump_bytes() 호출은 호출하는 지점마다(per-callsite) 동적으로 켜질
수 있습니다.
CONFIG_DYNAMIC_DEBUG가 설정되어 있지 않으면, print_hex_dump_debug()는
그저 print_hex_dump(KERN_DEBUG)의 축약입니다.
print_hex_dump_debug()/print_hex_dump_bytes()에서 만약 그것이 변하지 않는
문자열이라면 형식 문자열(format string)은 그 ‘prrefix_str’ 인자입니다;
또는 ‘prefix_str’ 이 빌드 시 동적인 경우라면 “hexdump”.
동적 디버그는 더 유용한 기능들도 가집니다:
* 0이나 1의 조합으로 매칭해서 디버깅 문장을 끄고 켤 수 있도록 하는 간단한 쿼리 언어:
– 소스 파일 이름
– 함수 이름
– (행 번호의 범위를 포함하는) 행 번호
– 모듈 이름
– 형식 문자열
* debugfs 제어 파일 제공: 여러분을 안내하는데 도움이 되도록, 알려진 디버그 문장들의
완전한 목록을 보이도록 읽혀질 수 있는 <debugfs>/dynamic_debug/control.
동적 디버그 동작 제어하기
=========================
pr_debug()/dev_dbg()의 동작은 이 기능의 용도에 따라 ‘debugfs’ 파일 시스템 내의
제어 파일에 쓰는 것을 통해 제어됩니다. 이어서, 우리는 제어 파일을 알아봅니다:
<debugfs>/dynamic_debug/contorl. 예를 들어, 여러분이 소스 파일 ‘svcsock.c’의
1603번째 행에서 출력을 켜기를 원한다면, 간단히 이렇게 하세요:
nullarbor:~ # echo ‘file svcsock.c line 1603 +p’ >
<debugfs>/dynamic_debug/control
여러분이 문법을 틀린다면, 쓰기가 실패할 것입니다:
nullarbor:~ # echo ‘file svcsock.c wtf 1 +p’ >
<debugfs>/dynamic_debug/control
-bash: echo: write error: Invalid argument
동적 디버그 동작 보기
=====================
여러분은 다음을 통해 현재 설정된 모든 디버그 문장의 동작을 볼 수 있습니다:
nullarbor:~ # cat <debugfs>/dynamic_debug/control
# filename:lineno [module]function flags format
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:323 [svcxprt_rdma]svc_rdma_cleanup =_ “SVCRDMA Module Removed, deregister RPC RDMA transport\012”
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:341 [svcxprt_rdma]svc_rdma_init =_ “\011max_inline : %d\012”
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:340 [svcxprt_rdma]svc_rdma_init =_ “\011sq_depth : %d\012”
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:338 [svcxprt_rdma]svc_rdma_init =_ “\011max_requests : %d\012”
…
여러분은 또한 이 데이터에 표준 유닉스 텍스트 조작 필터를 적용할 수 있습니다. 예를 들면,
nullarbor:~ # grep -i rdma <debugfs>/dynamic_debug/control | wc -l
62
nullarbor:~ # grep -i tcp <debugfs>/dynamic_debug/control | wc -l
42
세번째 열은 현재 각 디버그 문장을 호출하는 곳마다의 켜짐 플래그(이 플래그의 정의는
아래를 보세요)를 보여줍니다. 아무 플래그도 설정되지 않은, 기본 값은 “=_” 입니다.
그래서 여러분은 모든 디버그 문장 호출처를 기본값이 아닌 플래그로 볼 수 있습니다:
nullarbor:~ # awk ‘$3 != “=_”‘ <debugfs>/dynamic_debug/control
# filename:lineno [module]function flags format
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c:1603 [sunrpc]svc_send p “svc_process: st_sendto returned %d\012”
명령 언어 레퍼런스
==================
어휘적인 수준에서, 하나의 명령은 스페이스나 탭으로 나뉘어진 단어 순서로 구성됩니다.
그래서 이들은 모두 동등합니다:
nullarbor:~ # echo -c ‘file svcsock.c line 1603 +p’ >
<debugfs>/dynamic_debug/control
nullarbor:~ # echo -c ‘ file svcsock.c line 1603 +p ‘ >
<debugfs>/dynamic_debug/control
nullarbor:~ # echo -n ‘file svcsock.c line 1603 +p’ >
<debugfs>/dynamic_debug/control
명령 제출은 write() 시스템 호출로 경계지어집니다. 여러 명령은 ‘;’ 나 ‘\n’ 으로
나뉘어져서 함께 쓰여질 수 있습니다.
~# echo “func pnpacpi_get_resources +p; func pnp_assign_mem +p” \
> <debugfs>/dynamic_debug/control
여러분의 쿼리 집합이 크다면, 역시 이를 일괄적으로 수행할 수 있습니다:
~# cat query-batch-file > <debugfs>/dynamic_debug/control
다른 방법은 와일드카드를 사용하는 것입니다. 매치 규칙은 ‘*’ (0개나 그 이상의 문자들과
매치)과 ‘?’ (정확히 하나의 문자와 매치)를 지원합니다. 예를 들면, 여러분은 모든 usb
드라이버들을 매치시킬 수 있습니다:
~# echo “file drivers/usb/* +p” > <debugfs>/dynamic_debug/control
문법적인 수준에서, 매치 명세(match-spec)들, 그 뒤에 플래그 변경 명세(flags-spec)
의 순서로 구성됩니다.
command ::= match-spec* flags-spec
매치 명세들은 플래그 변경 명세를 어디에 적용해야 하는지를 위해 알려진 pr_debug()
호출처의 부분 집합을 고르는데 사용됩니다. 각 짝들 사이에 암묵적인 AND로 하나의 쿼리처럼
그들을 생각하세요. 매치 명세의 빈 목록은 모든 디버그 문장 호출처를 선택함을 알아두세요.
매치 명세는 비교되는 호출처의 속성을 제어하는 하나의 키워드, 그리고 비교될 하나의 값으로
구성됩니다. 가능한 키워드들은:
match-spec ::= ‘func’ string |
‘file’ string |
‘module’ string |
‘format’ string |
‘line’ line-range
line-range ::= lineno |
‘-‘lineno |
lineno’-‘ |
lineno’-‘lineno
// 알림: line-range 스페이스를 포함할 수 없습니다. 예를 들면,
// “1-30″은 유효한 범위이지만, “1 – 30″은 아닙니다.
lineno ::= unsigned-int
각 키워드의 뜻은:
func
주어진 문자열은 각 호출처의 함수 이름과 비교됩니다 예를 들면:
func svc_tcp_accept
file
주어진 문자열은 전체 경로 이름이나 소스 루트의(src-root) 상대 경로 이름, 또는 각
호출처의 소스 파일의 경로를 제외한 이름(basename)과 비교됩니다. 예를 들면:
file svcsock.c
file kernel/freezer.c
file /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c
module
주어진 문자열은 각 호출처의 모듈 이름과 비교됩니다. 모듈 이름은 “lsmod” 에서 보여지는
문자열입니다. 즉, 디렉토리나 .ko 접미사가 없고, ‘-‘는 ‘_’로 변경됩니다. 예를 들면:
module sunrpc
module nfsd
format
주어진 문자열은 동적 디버그 형식 문자열 내에서 검색됩니다. 그 문자열은 전체 형식과
매치될 필요는 없고 오직 일부만 매치되면 된다는 것을 알아두세요. 공백 문자들과 다른
특수 문자들은 C의 8진수 문자 이스케이프 \ooo 표현을 사용해서 쓰여질 수 있습니다.
예를 들면, 스페이스 문자는 \040 입니다. 다른 방식으로, 문자열을 쌍따옴표 문자(“) 나
따옴표(‘)로 둘러쌀 수 있습니다.
예제:
format svcrdma: // NFS/RDMA 서버 pr_debug 들에서 많음
format readahead // readahead 캐시 안의 pr_debug 들
format nfsd:\040SETATTR // 공백 문자로 형식을 매치하는 한가지 방법
format “nfsd: SETATTR” // 공백 문자로 형식을 매치하는 더 깔끔한 방법
format ‘nfsd: SETATTR’ // 공백 문자로 형식을 매치하는 또 다른 방법
line
주어진 행 번호 또는 행 번호의 범위는 각 pr_debug() 호출처의 행 번호와 비교됩니다.
하나의 행 번호는 호출처 행 번호와 정확히 매치됩니다. 행 번호의 범위는 처음과 마지막의
행 번호 안의 호출처와 매치됩니다. 첫번째 수가 없는 것은 파일의 첫번째 줄을 의미하고,
행 번호가 없는 것은 파일의 마지막 줄을 의미합니다. 예를 들면:
line 1603 // 정확히 1603번 줄
line 1600-1605 // 1600번에서 1605번 줄까지의 여섯 줄
line -1605 // 1번 줄에서 1605번 줄까지의 1605 줄
line 1600- // 1600번 줄에서 파일의 끝까지의 모든 줄
플래그 명세는 하나 또는 그 이상의 플래그 성질이 따르는 하나의 변경 동작으로 구성됩니다.
변경 동작은 하나의 성질입니다:
– 주어진 플래그 제거
+ 주어진 플래그 추가
= 주어진 플래그로 플래그를 설정
플래그들은 다음과 같습니다:
p pr_debug() 호출처 켜기
f 출력된 메시지 안에 함수 이름을 포함
l 출력된 메시지 안에 행 번호를 포함
m 출력된 메시지 안에 모듈 이름을 포함
t 인터럽트 컨텍스트에서 생성되지 않은 메시지 안에 스레드 ID 포함.
_ 플래그가 설정되지 않음. (또는 입력 상의 다른 것들로 설정)
print_hex_dump_debug()와 print_hex_dump_bytes()에서는, 오직 ‘p’ 플래그가
다른 플래그들이 무시되는 것을 의미합니다.
보여줄 때, 플래그들은 ‘=’이 앞에 옵니다(연상 기호: 무슨 플래그가 현재 동일한지).
Note the regexp ^[-+=][flmpt_]+$ matches a flags specification.
To clear all flags at once, use “=_” or “-flmpt”.
정규표현 ^[-+=][flmpt_]+$ 는 플래그 명세와 매치함을 알아두세요.
모든 플래그를 없애기 위해서, “=_” 나 “-flmpt”를 사용하세요.
부트 프로세스 동안의 디버그 메시지
==================================
부트 프로세스 동안의 코어 코드와 빌트인 모듈 디버그 메시지를 활성화 하기 위해서, 유저 공간과
debugfs 가 존재하기 전에도, dyndbg=”QUERY”, module.dyndbg=”QUERY”, 또는
ddebug_query=”QUERY”(ddebug_query는 dyndbg에 의해 구식이 되었고, 더 이상 사용하지
않습니다)를 사용할 수 있습니다. QUERY는 위에 설명된 문법을 따르지만, 1023 문자를 넘을 수
없습니다. 여러분의 부트로더는 제한으로 더 적은 수를 쓰고 있을 수 있습니다.
이들 dyndbg 파라미터들은 ddebug 테이블이 처리된 후에, arch_initcall 의 일부로
바로 처리됩니다. 그래서 여러분은 이 부트 파라미터를 통해 이 arch_initcall 이후에
모든 코드 안의 디버그 메시지를 켤 수 있습니다.
예를 들면, x86 시스템 상에서 ACPI 활성화는 subsys_initcall 이고,
dyndbg=”file ec.c +p”
는 여러분의 머신(일반적으로 랩탑)이 임베디드 컨트롤러를 가진다면, ACPI 셋업동안 초기 임베디드
컨트롤러 트랜잭션을 보여줄 것입니다.
PCI (또는 다른 장치들의) 초기화는 또한 디버깅 목적의 이 부트 파라미터 사용의 유력한
후보들입니다.
foo 모듈이 빌트-인이 아니라면, foo.dyndbg 는 여전히 부트 타임에 아무 효과 없이 처리될
것입니다만, 모듈이 이후에 로딩될 때 처리될 것입니다. dyndbg_query= 과 순수한 dyndbg= 은
부트 시에만 처리됩니다.
모듈 초기화 시점의 디버그 메시지
================================
“modprobe foo”가 호출될 때, modprobe는 foo.params를 위해 /proc/cmdline을 스캔하고,
“foo.”을 벗겨서 제거하고, 이를 modprobe args 나 /etc/modprob.d/*.conf 파일 안에
주어진 파라미터들과 함께 다음 순서로 커널에게 넘긴다:
1. # /etc/modprobe.d/*.conf를 통해 주어진 파라미터들
options foo dyndbg=+pt
options foo dyndbg # +p로 기본
2. # boot args에서 주어진 foo.dyndbg, “foo.” 는 벗겨져서 제거되어 전달됨
foo.dyndbg=” func bar +p; func buz +mp”
3. # modprobe로의 args
modprobe foo dyndbg==pmf # 이전 설정을 덮어 씀
이들 dyndbg 쿼리들은 가장 마지막에 말한 것이 마지막에 적용되도록, 순서대로 적용됩니다.
이것은 boot args가 /etc/modprobe.d(정확하게는 1이 시스템 전체, 2는 커널이나 부트에
의존적임)로부터 이들을 수정하거나 덮어 쓰도록, 그리고 modeprbe args 가 둘 다를 덮어쓰도록
합니다.
foo.dyndbg=”QUERY” 형식 안에서, 쿼리는 반드시 “foo 모듈”을 제외해야 합니다.
“foo”는 param-name 으로부터 추출되고, “QUERY” 내의 각 쿼리에 적용되고, 오직
각 타입의 1 match-spec만 적용됩니다.
dyndbg 옵션은 “가짜” 모듈 파라미터입니다. 이는 다음을 의미합니다:
– 모듈들은 그를 명시적으로 정의할 필요가 없습니다.
– 모든 모듈은 그들이 pr_debug를 사용하든 말든, 아무 말 없이 이를 얻을 수 있습니다.
– 그것은 /sys/module/$module/parameters 안에 나타나지 않습니다. 이를 보기 위해서,
제어 파일을 grep 하거나, /proc/cmdline 을 검사하세요.
CONFIG_DYNAMIC_DEBUG 커널에서는 디버그 메시지가 더이상 필요하지 않다면, 부트-타임에
주어진 (또는 컴파일 동안 -DDEBUG 플래그에 의해 켜진) 어떠한 셋팅도 이후에 sysfs
인터페이스를 통해 끌 수 있습니다.
echo “module module_name -p” > <debugfs>/dynamic_debug/control
예제
====
// svcsock.c 파일의 1603번째 줄의 메시지를 켜기
nullarbor:~ # echo -n ‘file svcsock.c line 1603 +p’ >
<debugfs>/dynamic_debug/control
// svcsock.c 파일 안의 모든 메시지 켜기
nullarbor:~ # echo -n ‘file svcsock.c +p’ >
<debugfs>/dynamic_debug/control
// NFS 서버 모듈의 모든 메시지 켜기
nullarbor:~ # echo -n ‘module nfsd +p’ >
<debugfs>/dynamic_debug/control
// svc_process() 함수 안의 모든 12 메시지를 켜기
nullarbor:~ # echo -n ‘func svc_process +p’ >
<debugfs>/dynamic_debug/control
// svc_process() 함수 안의 모든 12 메시지를 끄기
nullarbor:~ # echo -n ‘func svc_process -p’ >
<debugfs>/dynamic_debug/control
// NFS가 호출하는 READ, READLINK, READDIR 그리고, READDIR+를 위한 메시지 켜기
nullarbor:~ # echo -n ‘format “nfsd: READ” +p’ >
<debugfs>/dynamic_debug/control
// “usb” 문자열을 포함하는 경로의 메시지 켜기
nullarbor:~ # echo -n ‘*usb* +p’ > <debugfs>/dynamic_debug/control
// 모든 메시지 켜기
nullarbor:~ # echo -n ‘+p’ > <debugfs>/dynamic_debug/control
// 모든 켜진 메시지에 모듈과 함수 추가
nullarbor:~ # echo -n ‘+mf’ > <debugfs>/dynamic_debug/control
// boot-args 예제, 가독성을 위해 줄바꿈과 주석을 넣음
Kernel command line: …
// dyndbg=값 처리 안에서 어떻게 되어가고 있는지 보기
dynamic_debug.verbose=1
// 2 빌트인 안의 pr_debug 켜기, #cmt 는 제거됨
dyndbg=”module params +p #cmt ; module sys +p”
// 나중에 로딩되는 모듈 안의 2 함수 안의 pr_debug를 켜기
pc87360.dyndbg=”func pc87360_init_device +p; func pc87360_find +p”