tags.sh on Kernel - KAT(4-1)


7 분 소요

Introduce

본 포스팅은 Linux kernel v5.1.0 을 기준으로 작성되었다.

linux/scripts/tags.sh에 존재

작동 조건:

make tags
make cscope
make TAGS

tags는 ctags, TAGS는 etags를 위한 tags 파일, cscope는 cscope 데이터베이스가 생성된다. 그 외로 gtags가 가능하다고 한다. 뭔지 잘 모른다.)

수행을 위해 필요한 환경변수

  • ARCH: tag 등을 생성할 architechture
    • arm64
  • SUBARCH: 대체 언제 생성되는지 모르겠음;; 알려주길 바람.
    • None
  • SRCARCH: linux/arch/ 의 child directory
    • arm64
  • srctree: kernel root directory의 path
    • make를 수행한 directory인 “.(current directory)” 다른 directory에서 수행이 안됨. -f와 -I 옵션을 줄 수는 있는데, Makefile에서 script/tag.sh을 찾지 못함.
  • src: == srctree
  • obj: == make 수행한 directory
make ARCH=arm64 tags cscope

Scenario

다음을 수행하였다고 가정한다.:

make ARCH=arm64 tags cscope

start

  • 341: case문에 돌입한다. $1make에서 넘겨진 인자로 보여진다.
  • 350: case tags를 수행한다.
  • 352: xtags 함수를 호출하고 인자로 ctags라는 문자열을 넘긴다.
    341 case "$1" in
    342       "cscope")
    343             docscope
    344             ;;
    345
    346       "gtags")
    347             dogtags
    348             ;;
    349
    350       "tags")
    351             rm -f tags
    352             xtags ctags
    353             remove_structs=y
    354             ;;
    355
    356       "TAGS")
    357             rm -f TAGS
    358             xtags etags
    359             remove_structs=y
    360             ;;
    361 esac
    

xtags

  • 288: xtags 함수 원형
  • 290: 인자 $1(ctags)의 버전을 출력하여 대소문자 구별 없이 exuberant라는 문자열이 존재하는지 확인. => True
  • 291: exuberant 함수를 호출하고 인자로 $1(ctags)를 전달
    288 xtags()
    289 {
    290       if $1 --version 2>&1 | grep -iq exuberant; then
    291             exuberant $1
    292       elif $1 --version 2>&1 | grep -iq emacs; then
    293             emacs $1
    294       else
    295             all_target_sources | xargs $1 -a
    296       fi
    297 }
    

exuberant

  • 254: ctags를 수행하는 함수 exuberant를 수행한다. 인자로 들어온 $1ctags이다.
  • 256: setup_regex 함수를 호출하며 인자로 exuberant, asm, c 를 넘긴다.
  • 257: all_target_sources 함수를 호출하며, 표준출력된 내용을 ctags($1)의 인자로 넘긴다.(xargs) 즉 all_tager_sources 함수는 파일명이 될 것이다. 실제 태그 생성구문.
  • 258~270: ctags 옵션에서 잘 설명되어 있다.
  • 271: setup_regex에서 만들어낸 regex 배열을 인자로 준다. 매크로를 이용해 만든 함수 등에 대해서도 tag를 만들어주기 위함이다.
  • 273: setup_regex 함수를 호출하며 인자로 exuberant, kconfig 를 넘긴다.
  • 274: all_kconfigs 함수를 호출하며, 표준출력된 내용을 ctags($1)의 인자로 넘긴다.
  • 275: ctags 옵션에서 정확하게 설명되어 있다. kconfig라는 언어는 없기 때문에 regex 배열에 kconfig tag를 생성하는 정규표현식을 setup_regex에서 만든다.
    254 exuberant()
    255 {
    256       setup_regex exuberant asm c
    257       all_target_sources | xargs $1 -a                        \
    258       -I __initdata,__exitdata,__initconst,__ro_after_init  \
    259       -I __initdata_memblock                          \
    260       -I __refdata,__attribute,__maybe_unused,__always_unused \
    261       -I __acquires,__releases,__deprecated,__always_inline \
    262       -I __read_mostly,__aligned,____cacheline_aligned        \
    263       -I ____cacheline_aligned_in_smp                         \
    264       -I __cacheline_aligned,__cacheline_aligned_in_smp     \
    265       -I ____cacheline_internodealigned_in_smp                \
    266       -I __used,__packed,__packed2__,__must_check,__must_hold     \
    267       -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL,ACPI_EXPORT_SYMBOL   \
    268       -I DEFINE_TRACE,EXPORT_TRACEPOINT_SYMBOL,EXPORT_TRACEPOINT_SYMBOL_GPL \
    269       -I static,const                                 \
    270       --extra=+fq --c-kinds=+px --fields=+iaS --langmap=c:+.h \
    271       "${regex[@]}"
    272
    273       setup_regex exuberant kconfig
    274       all_kconfigs | xargs $1 -a                              \
    275       --langdef=kconfig --language-force=kconfig "${regex[@]}"
    276
    277 }
    

setup_regex

  • setup_regex 함수의 원형이다.
  • 223: local 변수로 mode=$1(exuberant), lang, tmp=(), r 총 4개를 선언한다.
  • 224: setup_regex로 들어온 첫 번째 인자(exuberant)를 제거한다. (남은 두 인자는 asm과 c)
  • 226: regex 라는 전역 배열을 선언한다. (regex는 regular expression의 준말로 보인다.)
  • 227: shift하여 제거한 첫 번째 인자를 제외한 두 번째 인자부터 마지막 인자까지 for 문을 수행. for lang in $@; do 와 완전히 동일한 문장이다.
  • 228~232: $lang이 asm 이면 tmp$regex_asm 의 인자 전부를 tmp에 대입, c면 $regex_c, kconfig면 $regex_kconfig를 대입한다.
  • tmp에 들어간 elements들을 순회하는 for문
  • 234: $mode(exuberant) == "exuberant" 이므로 참.
  • 235: regex 배열에 --regex-$lang=${r}b를 추가함.
    • --regex-<LANG>=/regexp/replacement/[kind-spec/][flags]: regexp에 해당하는 문자열을 replacement로 바꿔 tag로 만듬. replacement가 tag 이름 regexp가 표현식, [kind-spec]이 tagfield의 kind에 대한 value(없으면 regex 라는 의미의 r). [flags]는 b 라는 단일 문자에 해당하는 필드로 base regular expression을 의미함.
  • 236~: exuberant ctags를 사용하지 않을 때(etags 등) 사용하는 부분. (분석생략.)
    221 setup_regex()
    222 {
    223       local mode=$1 lang tmp=() r
    224       shift
    225
    226       regex=()
    227       for lang; do
    228             case "$lang" in
    229             asm)       tmp=("${regex_asm[@]}") ;;
    230             c)         tmp=("${regex_c[@]}") ;;
    231             kconfig)   tmp=("${regex_kconfig[@]}") ;;
    232             esac
    233             for r in "${tmp[@]}"; do
    234                   if test "$mode" = "exuberant"; then
    235                         regex[${#regex[@]}]="--regex-$lang=${r}b"
    236                   else
    237                         # Remove ctags /kind-spec/
    238                         case "$r" in
    239                         /*/*/?/)
    240                               r=${r%?/}
    241                         esac
    242                         # Prepend ^[^#] unless already anchored
    243                         case "$r" in
    244                         /^*) ;;
    245                         *)
    246                               r="/^[^#]*${r#/}"
    247                         esac
    248                         regex[${#regex[@]}]="--regex=$r"
    249                   fi
    250             done
    251       done
    252 }
    

regex_lang

ctags를 위한 /kind-spec/ 옵션이 있는 베이직 정규 표현식과 따라야 하는 제한.

  • 정규표현식 수정자 없음
  • \? 대신 {0,1}을 사용해야함. 왜냐하면 etags에서는 ? 문자 자체를 의미하게 될 수 있음.
  • etags에서는 \s가 동작하지 앟음. 스페이스나 [ \t]를 사용해야함.
  • \w는 동작하지만 etags에서 underscores(_)와 일치하지 않음
  • 마지막줄은 해석이 안 되는데 도와주세요.

asm

ENTRY나 _GLOBAL이 붙은 어셈블리 프로시저. 외부 모듈에게 노출하기 위해 사용하는 매크로이다.

c

SYSCALL_DEFINE이나 TRACE_EVENT, DEFINE 등과 같은 매크로를 이용한 함수 선언문에 대해서 tag를 생성해주기 위함이다.

kconfig

kconfig의 tag는 Kconfig 파일에 존재하는 config에 대해서 모든 태그를 생성한다. kconfig에서는 ‘CONFIG_‘가 붙지 않고, 실제 코드에서 #ifdef 등에서 사용될 땐, ‘CONFIG_‘가 붙으므로 붙은 것과 안 붙은 것으로 태그를 두 개 생성한다.

  142 # Basic regular expressions with an optional /kind-spec/ for ctags and
  143 # the following limitations:
  144 # - No regex modifiers
  145 # - Use \{0,1\} instead of \?, because etags expects an unescaped ?
  146 # - \s is not working with etags, use a space or [ \t]
  147 # - \w works, but does not match underscores in etags
  148 # - etags regular expressions have to match at the start of a line;
  149 #   a ^[^#] is prepended by setup_regex unless an anchor is already present
  150 regex_asm=(
  151       '/^\(ENTRY\|_GLOBAL\)(\([[:alnum:]_\\]*\)).*/\2/'
  152 )
  153 regex_c=(
  154       '/^SYSCALL_DEFINE[0-9](\([[:alnum:]_]*\).*/sys_\1/'
  155       '/^BPF_CALL_[0-9](\([[:alnum:]_]*\).*/\1/'
  156       '/^COMPAT_SYSCALL_DEFINE[0-9](\([[:alnum:]_]*\).*/compat_sys_\1/'
  157       '/^TRACE_EVENT(\([[:alnum:]_]*\).*/trace_\1/'
  158       '/^TRACE_EVENT(\([[:alnum:]_]*\).*/trace_\1_rcuidle/'
  159       '/^DEFINE_EVENT([^,)]*, *\([[:alnum:]_]*\).*/trace_\1/'
  160       '/^DEFINE_EVENT([^,)]*, *\([[:alnum:]_]*\).*/trace_\1_rcuidle/'
  161       '/^DEFINE_INSN_CACHE_OPS(\([[:alnum:]_]*\).*/get_\1_slot/'
  162       '/^DEFINE_INSN_CACHE_OPS(\([[:alnum:]_]*\).*/free_\1_slot/'
  163       '/^PAGEFLAG(\([[:alnum:]_]*\).*/Page\1/'
  164       '/^PAGEFLAG(\([[:alnum:]_]*\).*/SetPage\1/'
  165       '/^PAGEFLAG(\([[:alnum:]_]*\).*/ClearPage\1/'
  166       '/^TESTSETFLAG(\([[:alnum:]_]*\).*/TestSetPage\1/'
  167       '/^TESTPAGEFLAG(\([[:alnum:]_]*\).*/Page\1/'
  168       '/^SETPAGEFLAG(\([[:alnum:]_]*\).*/SetPage\1/'
  169       '/\<__SETPAGEFLAG(\([[:alnum:]_]*\).*/__SetPage\1/'
  170       '/\<TESTCLEARFLAG(\([[:alnum:]_]*\).*/TestClearPage\1/'
  171       '/\<__TESTCLEARFLAG(\([[:alnum:]_]*\).*/TestClearPage\1/'
  172       '/\<CLEARPAGEFLAG(\([[:alnum:]_]*\).*/ClearPage\1/'
  173       '/\<__CLEARPAGEFLAG(\([[:alnum:]_]*\).*/__ClearPage\1/'
  174       '/^__PAGEFLAG(\([[:alnum:]_]*\).*/__SetPage\1/'
  175       '/^__PAGEFLAG(\([[:alnum:]_]*\).*/__ClearPage\1/'
  176       '/^PAGEFLAG_FALSE(\([[:alnum:]_]*\).*/Page\1/'
  177       '/\<TESTSCFLAG(\([[:alnum:]_]*\).*/TestSetPage\1/'
  178       '/\<TESTSCFLAG(\([[:alnum:]_]*\).*/TestClearPage\1/'
  179       '/\<SETPAGEFLAG_NOOP(\([[:alnum:]_]*\).*/SetPage\1/'
  180       '/\<CLEARPAGEFLAG_NOOP(\([[:alnum:]_]*\).*/ClearPage\1/'
  181       '/\<__CLEARPAGEFLAG_NOOP(\([[:alnum:]_]*\).*/__ClearPage\1/'
  182       '/\<TESTCLEARFLAG_FALSE(\([[:alnum:]_]*\).*/TestClearPage\1/'
  183       '/^PAGE_TYPE_OPS(\([[:alnum:]_]*\).*/Page\1/'
  184       '/^PAGE_TYPE_OPS(\([[:alnum:]_]*\).*/__SetPage\1/'
  185       '/^PAGE_TYPE_OPS(\([[:alnum:]_]*\).*/__ClearPage\1/'
  186       '/^TASK_PFA_TEST([^,]*, *\([[:alnum:]_]*\))/task_\1/'
  187       '/^TASK_PFA_SET([^,]*, *\([[:alnum:]_]*\))/task_set_\1/'
  188       '/^TASK_PFA_CLEAR([^,]*, *\([[:alnum:]_]*\))/task_clear_\1/'
  189       '/^DEF_MMIO_\(IN\|OUT\)_[XD](\([[:alnum:]_]*\),[^)]*)/\2/'
  190       '/^DEBUGGER_BOILERPLATE(\([[:alnum:]_]*\))/\1/'
  191       '/^DEF_PCI_AC_\(\|NO\)RET(\([[:alnum:]_]*\).*/\2/'
  192       '/^PCI_OP_READ(\(\w*\).*[1-4])/pci_bus_read_config_\1/'
  193       '/^PCI_OP_WRITE(\(\w*\).*[1-4])/pci_bus_write_config_\1/'
  194       '/\<DEFINE_\(RT_MUTEX\|MUTEX\|SEMAPHORE\|SPINLOCK\)(\([[:alnum:]_]*\)/\2/v/'
  195       '/\<DEFINE_\(RAW_SPINLOCK\|RWLOCK\|SEQLOCK\)(\([[:alnum:]_]*\)/\2/v/'
  196       '/\<DECLARE_\(RWSEM\|COMPLETION\)(\([[:alnum:]_]\+\)/\2/v/'
  197       '/\<DECLARE_BITMAP(\([[:alnum:]_]*\)/\1/v/'
  198       '/\(^\|\s\)\(\|L\|H\)LIST_HEAD(\([[:alnum:]_]*\)/\3/v/'
  199       '/\(^\|\s\)RADIX_TREE(\([[:alnum:]_]*\)/\2/v/'
  200       '/\<DEFINE_PER_CPU([^,]*, *\([[:alnum:]_]*\)/\1/v/'
  201       '/\<DEFINE_PER_CPU_SHARED_ALIGNED([^,]*, *\([[:alnum:]_]*\)/\1/v/'
  202       '/\<DECLARE_WAIT_QUEUE_HEAD(\([[:alnum:]_]*\)/\1/v/'
  203       '/\<DECLARE_\(TASKLET\|WORK\|DELAYED_WORK\)(\([[:alnum:]_]*\)/\2/v/'
  204       '/\(^\s\)OFFSET(\([[:alnum:]_]*\)/\2/v/'
  205       '/\(^\s\)DEFINE(\([[:alnum:]_]*\)/\2/v/'
  206       '/\<\(DEFINE\|DECLARE\)_HASHTABLE(\([[:alnum:]_]*\)/\2/v/'
  207       '/\<DEFINE_ID\(R\|A\)(\([[:alnum:]_]\+\)/\2/'
  208       '/\<DEFINE_WD_CLASS(\([[:alnum:]_]\+\)/\1/'
  209       '/\<ATOMIC_NOTIFIER_HEAD(\([[:alnum:]_]\+\)/\1/'
  210       '/\<RAW_NOTIFIER_HEAD(\([[:alnum:]_]\+\)/\1/'
  211       '/\<DECLARE_FAULT_ATTR(\([[:alnum:]_]\+\)/\1/'
  212       '/\<BLOCKING_NOTIFIER_HEAD(\([[:alnum:]_]\+\)/\1/'
  213       '/\<DEVICE_ATTR_\(RW\|RO\|WO\)(\([[:alnum:]_]\+\)/dev_attr_\2/'
  214       '/\<DRIVER_ATTR_\(RW\|RO\|WO\)(\([[:alnum:]_]\+\)/driver_attr_\2/'
  215       '/\<\(DEFINE\|DECLARE\)_STATIC_KEY_\(TRUE\|FALSE\)\(\|_RO\)(\([[:alnum:]_]\+\)/\4/'
  216 )
  217 regex_kconfig=(
  218       '/^[[:blank:]]*\(menu\|\)config[[:blank:]]\+\([[:alnum:]_]\+\)/\2/'
  219       '/^[[:blank:]]*\(menu\|\)config[[:blank:]]\+\([[:alnum:]_]\+\)/CONFIG_\2/'
  220 )

all_target_sources

ctags를 만들어낼 타겟 c/h/S 파일들을 출력

  • $COMPILED_SOURCE == None
  • 112: all_target_sources함수의 원형이다.
  • 114: 컴파일된 소스가 없으므로 (컴파일 하면 참이되는지는 해보지 않았다.) 거짓.
  • 117: all_sources 함수를 수행한다.
    112 all_target_sources()
    113 {
    114       if [ -n "$COMPILED_SOURCE" ]; then
    115             all_compiled_sources
    116       else
    117             all_sources
    118       fi
    119 }
    

all_sources

  • tag를 만들 모든 소스 파일을 찾음.
  • ${SRCARCH} == "arm64"
  • $ALLSOURCE_ARCHS == "arm64"

  • 82: arch/arm64/include 내부의 c/h/S 파일들 출력
  • 83: 거짓
  • 86: include 내부의 c/h/S 파일들 출력
  • 89: find_sources arm64 '*.[chS]'
    • arch/arm64 의 include를 제외한 c/h/S 파일들 출력
  • 91: arch와 include 디렉토리를 제외한 c/h/S 파일들 출력
     80 all_sources()
     81 {
     82       find_arch_include_sources ${SRCARCH} '*.[chS]'
     83       if [ ! -z "$archinclude" ]; then
     84             find_arch_include_sources $archinclude '*.[chS]'
     85       fi
     86       find_include_sources '*.[chS]'
     87       for arch in $ALLSOURCE_ARCHS
     88       do
     89             find_sources $arch '*.[chS]'
     90       done
     91       find_other_sources '*.[chS]'
     92 }
    

find_arch_include_sources

  • $subarchprune == None
  • $ignore == "( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git ) -prune -o ( -name *.mod.c ) -prune -o ( -path tools ) -prune -o"

  • 51: arch/arm64/include 를 include에 저장
  • 54: 그대로 $include
  • 55: arch/arm64/include 디렉토리 내부에 $ignore에 해당하는 파일들을 제외하고 symbolic link 파일이 아닌 인자로 들어온 확장자 파일을 전부 출력.
     48 # find sources in arch/$1/include
     49 find_arch_include_sources()
     50 {
     51       include=$(find ${tree}arch/$1/ $subarchprune \
     52                               -name include -type d -print);
     53       if [ -n "$include" ]; then
     54             archincludedir="$archincludedir $include"
     55             find $include $ignore -name "$2" -not -type l -print;
     56       fi
     57 }
    

find_include_sources

  • 62: include 디렉토리 내부에 $ignore을 제외하고 심볼릭 링크 파일이 아닌 인자로 들어온 확장자 파일을 전부 출력
     59 # find sources in include/
     60 find_include_sources()
     61 {
     62       find ${tree}include $ignore -name config -prune -o -name "$1" \
     63             -not -type l -print;
     64 }
    

find_sources

   75 find_sources()
   76 {
   77       find_arch_sources $1 "$2"
   78 }

find_arch_sources

  • $archincludedir == "arch/arm64/include"

  • 42: $prune == "-wholename arch/arm64/include -prune -o"
  • 44: arch/arm64 에 include 디렉토리만 빼고 심볼릭 링크가 아닌 인자로 들어온 파일을 출력
     38 # find sources in arch/$ARCH
     39 find_arch_sources()
     40 {
     41       for i in $archincludedir; do
     42             prune="$prune -wholename $i -prune -o"
     43       done
     44       find ${tree}arch/$1 $ignore $subarchprune $prune -name "$2" \
     45             -not -type l -print;
     46 }
    

find_other_sources

  • 70: include와 arch 디렉터리, .tmp_로 시작하는 파일들을 제외하고 심볼릭 링크가 아닌 인자로 들어온 파일을 출력
     66 # find sources in rest of tree
     67 # we could benefit from a list of dirs to search in here
     68 find_other_sources()
     69 {
     70       find ${tree}* $ignore \
     71            \( -path ${tree}include -o -path ${tree}arch -o -name '.tmp_*' \) -prune -o \
     72              -name "$1" -not -type l -print;
    

all_kconfigs

타겟 arch와 나머지 디렉토리의 Kconfig 파일을 출력

121 all_kconfigs()
122 {
123       find ${tree}arch/ -maxdepth 1 $ignore \
124              -name "Kconfig*" -not -type l -print;
125       for arch in $ALLSOURCE_ARCHS; do
126             find_sources $arch 'Kconfig*'
127       done
128       find_other_sources 'Kconfig*'
129 }

관련 내용에 대한 질문이나 태클을 환영합니다. 댓글 남겨주세요.



태그: ,

카테고리: ,

작성:

업데이트:

댓글남기기