[Linux:Kernel] 동적 디버그 사용법(dynamic debug howto.txt)

이 문서의 저작권은 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”

Android 개발 시 몇 가지 로그 확인 방법

1. Kernel 에서 pr_debug 등이 안나올 때,
간단히는 맨 위(“#include <linux/kernel.h>”보다 위)에 “#define DEBUG” 추가 혹은  http://www.kernel.org/doc/local/pr_debug.txt 처럼 makefile 수정
DEBUG를 추가했을 때 왜 pr_debug 가 Enable 되는지 보고 싶다면, include/linux/kernel.h 참고

커널 메시지 보기 : 커널이 어느정도 안정적이라면 UART 등을 이용하는 것보다 adb 쉘을 이용하는 것이 더 편하던데..

adb shell cat /proc/kmsg

2. Android 에서 LOGE, LOGW, LOGI, LOGD 가 안나올 때,
맨 위에 “#define LOG_NDEBUG 1” 추가
LOGV 까지 보고 싶을 땐 “#define LOG_NDEBUG 0” 추가
LOG_NDEBUG 가 미치는 영향을 보고 싶다면, system/core/include/cutils/log.h 파일 참고.

logcat 메시지 보기

adb logcat
그러나 로그 메시지 보기에는 역시 DDMS 가 가장 좋다.
로그 TAG를 통해 필터링해서 보는 것이 가장 보기 쉽고 깔끔하다.

아쉬우나마 adb logcat 을 이용해서 필터링 하고 싶다면 다음과 같이 한다.

adb logcat <TAG>:<LOGLEVEL> *:S
예를 들어, CameraTest 는 V 레벨로(CameraTest:V), Camera는 D 레벨로(Camera:D), 나머지는 안나오게 하고 싶다면(*:S) 다음과 같이 한다.

adb logcat CameraTest:V Camera:D *:S

filter-spec 등 adb의 사용법을 더 알고 싶다면 Android Debug Bridge(http://developer.android.com/guide/developing/tools/adb.html)를 참고한다.