· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
Emacs Gdb Etags Cscope

이 글은 프로그래머 입장에서 Emacs, GDB, etags, cscope를 어떻게 함께 쓸 수 있는 지에 대하여 설명합니다. (Emacs를 몰라도 상관없습니다.) 또, 원문이 후배들을 위해 쓴 글이라, 다소 반말, 협박 투로 되어 있으니 이 점 양해 바랍니다. ([http]신성국)




1. 준비사항


일단 Emacs는 설치되어 있다고 가정한다 -- Red Hat Linux의 경우, binary는 /usr/bin에, data는 /usr/share/emacs에 설치된다.

아래 두 section은 모두 CapsLock 키를 Control 키로 쓰기 위한 것이다. (Emacs를 쓰기 위해 반드시 해야 함) 만약 Happy Hacking Keyboard를 쓰고 있다면 할 필요 없음.

1.1. X Window System Key 설정


어떤 목적으로 Emacs를 쓰던지 상관없이 반드시 해야 할 setting 중 하나는 keyboard 설정이다. 일단 여러분의 키보드에서 알파벳 'A' key 왼쪽에 있는 키를 보기 바란다. 이 키가 'Control' 키라면 이 단계를 넘어가도 상관없다.

여러분이 쓰고 있는 대부분의 키보드는 이 키가 'CapsLock'일 것이다. 이 경우, 이 키를 'Control' key로 바꾸어야 한다. -- Emacs의 모든 단축키는 알파벳 'A' key 왼쪽의 키가 'Control' key라고 가정한 다음 만들어 진 것이다. 따라서 'Control'이 다른 곳에 위치할 경우, 키 입력에 상당한 무리가 따르며, Emacs가 싫어지는 가장 큰 이유가 될 것이다.

xmodmap 프로그램을 써서 여러분의 key layout을 바꿀 수 있다. 일단 terminal을 하나 띄운 후, xmodmap을 실행하면 다음과 비슷한 출력을 얻을 수 있다:

$ xmodmap
xmodmap:  up to 4 keys per modifier, (keycodes in parentheses):

shift       Shift_L (0x32),  Shift_R (0x3e)
lock        Caps_Lock (0x42)
control     Control_L (0x25),  Control_R (0x6d)
mod1        Alt_L (0x40),  Meta_L (0x73),  Alt_R (0x71),  Meta_R (0x74)
mod2        Num_Lock (0x4d)
mod3
mod4        Meta_L (0x73),  Meta_R (0x74)
mod5
$ _
위의 출력 결과는 여러분의 결과와 어느 정도 다를 수 있다는 것에 주의하기 바란다. 일단 가장 중요한 라인은 "lock"으로 시작하는 라인이다:

lock        Caps_Lock (0x42)

윗줄을 설명하면, "lock"은 "CapsLock" 기능을 하는 것으로, 현재 keycode 42번인 Caps_Lock key가 그 역할을 한다는 것이다.

다음 명령을 수행하여, Caps_Lock을 "lock"에서 빼버리자:
$ xmodmap -e "remove lock = Caps_Lock"
(결과가 궁금하면 다시 한번 아무런 인자없이 xmodmap을 실행하면 된다.) 그 다음, Caps_Lock을 "control"에 추가하자:
$ xmodmap -e "add control = Caps_Lock"
이제 xmodmap을 실행하면 lock과 control line이 다음과 같음을 알 수 있다.

$ xmodmap
xmodmap:  up to 4 keys per modifier, (keycodes in parentheses):

...
lock
control     Control_L (0x25),  Control_R (0x6d),  Caps_Lock (0x42)
...

$ _

이제, X Window는 여러분의 "CapsLock"키를 "control"로 해석하게 된다.

매번 이런 식으로 바꿀 수는 없는 노릇이다. 따라서 위의 과정을 script에 기록했다가 X가 시작할 때마다 자동으로 읽어오게 해야 한다.

Red Hat 계열이라면 $HOME/.Xmodmap 파일에 다음과 같이 기록해 두면 된다.

  remove lock = Caps_Lock
  add control = Caps_Lock

마지막으로 Emacs를 실행시켜서 keyboard setting을 확인해 보라. CapsLock key를 써서 C-x C-c를 눌러 보자. Emacs가 종료하게 되면 성공이다.

앞으로 Control 키를 누를 필요가 있다면 꼭 CapsLock key를 써서 누르기 바란다.

좀 더 자세한 사항을 원한다면 2 xmodmap man page, 3 Consistent Keyboard Configuration을 참고하기 바란다.

1.2. Linux Console에서 Key 설정


위의 xmodmap은 X Window System에서만 쓸 수 있는 프로그램이다. 따라서, X를 띄우기 전에는 쓸 수 없다. loadkeys/showkey/dumpkeys 프로그램을 쓰면 마찬가지로 CapsLock key를 Control 기능으로 바꿀 수 있다.

under construction

좀 더 자세한 사항을 원한다면 4 Linux Console HOWTO, 3 Consistent Keyboard Configuration을 참고하기 바란다.

  • 추가 : 윈도우에서 원격 접속하여 사용하실 경우 위의 방법으로 CapsLock 키를 변경하실수 없습니다. 해결책으로 저는 KeyTweak이라는 프로그램을 사용하였습니다. 참고하시기 바랍니다. - soma815
  • Mac OS X 사용자의 경우 KeyRemap4MacBook 패키지 + 확장패키지(PCKeyboardHack)를 설치하시면 CapsLock키를 원하는 키로 리매핑 가능합니다. - jcy0228

2. Learn Emacs keys


이제 Emacs를 쓰기 위한 기본적인 설정은 끝났다. 이제 Emacs를 쓰기 위한 기본적인 키들을 배워야 한다.

Emacs의 거의 모든 기능은 control key나 meta key로 시작한다. 당연히 control key로는 여러분이 흔히 써왔던 왼쪽 control key, 오른쪽 control key를 포함하며, 지금까지 작업했던 결과로 CapsLock key도 포함한다. (이 CapsLock key를 control key로 쓸 것을 강력히! 추천한다.) Meta key는 흔히 UNIX keyboard에서 "<>"로 표시된 키, 또는 일반 PC용 keyboard라면, Alt키가 대신 사용된다.

이 글과 Emacs 매뉴얼에서 사용하는 표기법으로, "C-" 접두어는 control key를 같이 누르라는 뜻이며, "M-" 접두어는 meta key를 같이 누르라는 뜻이다. 물론 "C-M-"은 control key와 meta key를 동시에 눌러야 한다는 뜻이다. 또한 tab key나 space bar, return key, escape key 그리고 function key를 나타낼 때에는 각각 <TAB>, <SPC>, <RET>, <ESC>, <F1>등과 같이 표시할 것이다. 몇 가지 예를 들면 다음과 같다:

키 입력 설명
C-x C-f control key를 누른 상태에서, `x'를 누르고 (control key를 떼지 않고) `f'를 누른다.
C-x o control key를 누른 상태에서, `x'를 누르고 (control key를 떼고) `o'를 누른다.
C-_ control key를 누른 상태에서 `_'(밑줄)을 누른다. 대개의 키보드에서 밑줄은 <SHIFT>와 '-' key를 눌러야 하므로, 결국 이 키는 control key와 shift key를 누른 상태에서 '-'를 눌러야 한다.
M-x compile<RET> meta key를 누르고 `x'를 누른 다음 (meta key를 떼고) `compile'을 치고 <RET>을 누른다.

자 이제 여기까지 배웠으면, Emacs를 실행시키고, "C-h t"를 눌러 tutorial을 열고 읽어보기 바란다. 위 tutorial을 읽을 때, 아래 키 목록은 반드시 외워서 익숙하게 쓸 줄 알아야 하는 키들이므로 꼭 외워 쓰기 바란다:

키 입력 설명
C-x C-c Emacs 종료
C-v 다음 페이지
M-v 이전 페이지
C-l 현재 줄을 창의 가운데 부분으로 보여줌
C-p 커서 up
C-n 커서 down
C-f 커서 right
C-b 커서 left
C-a 커서를 줄의 맨 처음으로
C-e 커서를 줄의 맨 마지막으로
M-f 다음 단어로
M-b 이전 단어로
C-d 한 글자 지우기
M-d 한 단어 지우기
C-k 현재 커서 위치에서 줄 끝까지 지우기
C-<SPC> 현재 커서 위치 마크(mark)하기
M-w 마크에서 현재 커서 위치까지 블럭 지정(copy)
C-w 마크에서 현재 커서 위치까지 지우고 블럭 지정(cut)
C-y 붙여 넣기(paste)
C-_ undo
C-g 명령 입력 취소 (undo 아님)
C-x C-s 파일 저장하기(save)
C-x C-w 다른 이름으로 파일 저장하기(save as...)
C-x C-f 파일 불러오기(load)
C-x k 버퍼 지우기(kill)
C-x b 다음 버퍼로 전환
C-x C-b 버퍼 목록 보여주기
C-x 2 창을 두개로 나누기
C-x 1 현재 창을 제외한 다른 창 닫기
C-x o 다른 창으로 커서 이동

다시 한번 강조하지만 위 키들은 기본적으로 쓰이는 키들이므로 꼭 외워서 능숙하게 쓸 수 있게 되어야 한다. Tutorial을 다 읽었고, 위 키들을 외웠다면 다음 단계로 넘어가기 바란다.

한가지 더, 다시 한번 강조하지만, Emacs에서는 키보드 알파벳 'A' key 왼편에 위치하고 있는 키가 control key라고 가정하고 단축키들이 디자인되어 있으므로, PC keyboard 사용자라면, 반드시 여기에 위치하고 있는 CapsLock key를 control key로 바꾸어 작업하기 바란다.

효과적인 커서 움직임을 위해 손가락의 배치를 다음과 같이 하자:

  • 왼손 새끼손가락은 control에, (CapsLock 키인거 설명 안해도 되겠지?)

  • 오른손 검지는 'N' key에, (C-n)
  • 오른손 약지는 'P' key에, (C-p)
  • 오른손 중지는 'K' key에, (C-k)

그리고 때에 따라서

  • 왼손 중지는 'F' key 또는 'E' key에, (C-e 또는 C-f)
  • 왼손 검지는 'B' key 또는 'V' key에, (C-b 또는 C-v)
  • 왼손 약지는 'E' key 또는 'A' key에, (C-e 또는 C-a)

키보드에 따라 다르지만,

  • 왼손 엄지는 Meta key에,
  • 오른손 엄지는 Spacebar에

놓고 쓰기 바란다. 물론 항상 이렇게 손가락을 위치시키란 것은 아니다. 단지 위에 나온 키를 입력 할 때에는 지정한 손가락으로 입력하는 것이 편할 것이다. 익숙한 vi 사용자들은 hjkl key가 화살표보다 더 편하다고 하지만, 웃기는 소리! Emacs가 훨씬 편하다! 입 아프다. 써 보면 알겠지..



3. Compiling and Debugging


내가 Emacs를 애용하는 가장 큰 이유는 compiling/debugging이 어떤 editor보다 강력하기 때문이다. 무슨 말인지 잘 모르겠으면 따라해보기 바란다.

  • X에서 terminal을 띄운 다음 다음 명령을 실행한다:

 
 $ emacs hello.c & 

  • 그 다음, 간단한 hello, world 프로그램을 작성한다, printf()의 마지막에 ';'이 빠진 것은 일부러 한 것이니 그대로 입력하기 바란다:

  #include <stdio.h>

  int
  main(void)
  {
    printf("hello, world\n")
      return 0;
  }
  • 프로그램을 작성할 때, 들여쓰기를 할 경우, 멍청하게 space를 치지 말기 바란다.

Emacs는 알아서 들여쓰기를 판단해서 빈 칸을 넣어준다. 예를 들어

  {
  printf("hello, world")

를 쓴 다음, printf가 있는 줄에 커서를 위치시키고, tab key를 눌러보라. 중요한 것은 커서가 꼭 맨 앞에만 있을 필요가 없다는 것이다. 커서가 그 줄의 어디에 있던지에 상관없이 tab key를 누르면 바로 들여쓰기를 수행하게 된다.

  • 다 작성했으면, 이제 저장(C-x C-s)하자.

  • 이제 compile할 때다. M-x compile <RET>을 치면 compile 명령을 입력할 수 있게 (emacs 맨 아래의 mini buffer에) 나오는데 기본적으로 "make -k "로 되어 있을 것이다. C-a C-k를 눌러 이것을 지우고, gcc hello.c <RET>를 입력하자.

  • 그러면 이제 Emacs 창이 두개로 나뉘어지고, 반대편에 compile 결과가 나오게 된다. 아까 소스를 입력할 때 ';'를 빠뜨렸으므로 당연히 error가 발생하게 된다. C-x `를 입력하면 Emacs가 자동적으로 첫번째 에러가 발생한 곳으로 커서를 옮겨준다. 에러가 여러 개라면 C-x `를 계속 눌러주면 계속해서 다음 에러가 발생한 곳으로 커서를 옮겨준다. (주의: '가 아니라 `이다.)

  • 다 고쳤으면 다시 M-x compile<RET>으로 컴파일하자.

  • 컴파일이 끝났으면, C-x o로 컴파일한 윈도우로 커서를 옮긴 다음, M-x shell<RET>을 실행하자. 이제 다음과 같이 실행하면 제대로 동작하는 프로그램을 볼 수 있을 것이다:

  $ ./a.out
  hello, world
  $ _

4. Emacs Debugger Mode (gdb)


Emacs는 내부에 debugger(gdb)를 실행시킬 수 있는 능력이 있다. 원래 gdb는 아주 간단한 user interface만을 가지고 있어서 바로 쓰기에는 매우 번거롭다. 그러나 emacs debugger mode로 gdb를 돌리면, 매우 편리하게 쓸 수 있다. 먼저 아래 프로그램을 emacs로 작성하자.

주의: 지금까자 착실히 따라왔다면, C-x C-f buggy.c<RET>을 입력해서 새로 buggy.c를 편집하면 된다.

  #include <stdio.h>

  void swap(int *, int *);

  int
  main(void)
  {
    int i = 0;
    int j = 1;

    printf("i = %d, j = %d\n", i, j);
    swap(&i, &i);

    printf("i = %d, j = %d\n", i, j);
    return 0;
  }

  static void
  do_swap(int *lhs, int *rhs, int *tmp)
  {
    tmp = 0;
    *tmp = *lhs;
    *lhs = *rhs;
    *rhs = *tmp;
  }

  void
  swap(int *lhs, int *rhs)
  {
    int t;
    do_swap(lhs, rhs, &t);
  }

다 입력했으면 C-x C-s를 눌러 저장하고, M-x compile<RET>을 눌러 컴파일 명령을 입력할 수 있게 되면, C-a C-k를 눌러 기존 컴파일 명령을 지우고, gcc -g buggy.c<RET>을 입력해서 컴파일하자. "-g" 옵션을 빼먹지 마!

그 다음, 컴파일 결과를 보여주는 창으로 커서를 옮긴 다음(c-x o), shell을 띄우고(M-x shell<RET>) 실행해보자:

  $ ./a.out
  i = 0, j = 1
  Segmentation fault
  $ _

주의할 것은, 상황에 따라서 "Segmentation fault"로 끝나지 않을 수도 있다. 아뭏든 정상적으로 끝나진 않을 것이다. 이제 M-x gdb<RET>을 누른 다음 a.out<RET>을 입력하자.

이제 친숙한 (gdb) prompt를 볼 수 있을 것이다. (바로 앞에서 a.out<RET> 대신 바로 <RET>을 눌러 gdb를 실행했다면, 지금 file a.out<RET>을 입력할 수도 있다.)

그 다음 바로 run<RET>을 실행하자.

  (gdb) run
  Starting program: /home/cinsk/src/a.out
  i = 0, j = 1

  Program received signal SIGSEGV, Segmentation fault.
  0x0804839e in do_swap ()
  (gdb) _

이 순간 debugger mode 창 반대쪽에는 do_swap() 함수의 내용이 나오고, segment fault가 일어난 바로 그 코드 앞에는 조그마한 표시(삼각형)가 나올 것이다. 바로 이것이 Emacs debugger mode가 가진 기능 중 하나임. (만약 코드가 표시되지 않는다면 아까 컴파일할 때 '-g' option을 빼먹은 경우이므로 다시 하기 바란다.)

이제 bt<RET>으로 대충 프로그램 흐름이 어떻게 흘러가는가 살펴보자.

  (gdb) bt
  #0  0x0804839e in do_swap ()
  #1  0x080483ce in swap ()
  #2  0x0804836c in main ()
  #3  0x420156a4 in __libc_start_main () from /lib/tls/libc.so.6
  (gdb) _

장담하건대, bug의 99%는 이 backtrace(bt) 명령 만으로도 충분히 해결할 수 있다. 위의 결과를 보면, 일단 네번째 줄은 무시하고, (세번째 줄) main()이 swap()을 불렀고 swap()이 do_swap()을 불렀다는 것을 알 수 있다. 즉, segment fault가 난 곳이 바로 이 근처라는 것을 알려주는 것이다.

그러면 br do_swap<RET>을 입력해서 do_swap()에 breakpoint를 걸고 run<RET>을 입력해서 다시 한 번 돌려보자.

  (gdb) br do_swap
  Breakpoint 1 at 0x804838f: file buggy.c, line 21.
  (gdb) r
  The program being debugged has been started already.
  Start it from the beginning? (y or n) y
  Starting program: /home/cinsk/src/a.out
  i = 0, j = 1

  Breakpoint 1, do_swap (lhs=0xbfffeb24, rhs=0xbfffeb24, tmp=0xbfffeb04)
      at buggy.c:21
  (gdb) _

이제 n<RET>을 입력해서 next 명령을 수행하면 소스 코드 왼편에 코드의 흐름을 나타내는 삼각형 표시가 다음 줄을 가리키는 것을 볼 수 있다. n<RET> 대신 C-c C-n을 써서 next 명령을 수행할 수도 있다. 그럼 step 명령 단축키는 무엇일까? 곰이 아니라면 C-c C-s인 것을 짐작할 수 있을테니 넘어간다.

   {
     tmp = 0;
  >  *tmp = *lhs;
     *lhs = *rhs;
     *rhs = *tmp;
   }

여기까지 왔고, 또 Emacs창에 보이는 소스를 보면 바로 무엇이 잘못된 것인지 금방 알 수 있을 것이다. 적어도 C 언어가 서툴지 않는다면. 그럼 "tmp = 0"을 지우고, (현재 커서가 debugger mode 창에 위치하고 있을테니, C-x o를 눌러서 소스가 나와있는 반대편 창으로 커서를 옮긴 다음, 커서를 "tmp = 0;" 줄에 위치시키고 C-a C-k C-k를 눌러서 그 줄을 지운다.) C-x C-s를 눌러 저장한다. 그 다음으로, 컴파일한 다음(M-x compile<RET><RET>), 반대편 창으로 넘어가서(C-x o), gdb 버퍼로 전환한다 (C-x b<RET>).

새로 컴파일했으므로 새로 파일을 읽어와야 한다. gdb prompt에서 "file a.out<RET>"을 입력해서 다시 실행 파일을 읽어온다:

  (gdb) file a.out
  A program is being debugged already.  Kill it? (y or n) y

  Load new symbol table from "a.out"? (y or n) y
  Reading symbols from a.out...done.
  (gdb) _

그리고 다시 run 시켜서(r <RET>), do_swap의 내용을 유심히 바라보기 바란다.

  (gdb) r
  Starting program: /home/cinsk/src/a.out
  i = 0, j = 1

  Breakpoint 1, do_swap (lhs=0xbfffda34, rhs=0xbfffda34, tmp=0xbfffda14)
      at buggy.c:21
  (gdb) _

일단, do_swap()에 전달된 처음 두개의 인자, lhs와 rhs가 같은 주소를 가리키고 있음을 알 수 있다. gdb print 명령(p)으로 자세히 인자 값들을 조사해보자.

  (gdb) p *lhs
  $1 = 0
  (gdb) p *rhs
  $2 = 0
  (gdb) p lhs
  $3 = (int *) 0xbfffda34
  (gdb) p rhs
  $4 = (int *) 0xbfffda34
  (gdb) _

무엇이 문제일까? do_swap()을 부른 함수가 첫번째 인자와 두번째 인자에 같은 주소를 넘겨준 것이다! 다시 한번 backtrace 명령(bt)으로 call stack을 조사해보자:

  (gdb) bt
  #0  do_swap (lhs=0xbfffda34, rhs=0xbfffda34, tmp=0xbfffda14) at buggy.c:21
  #1  0x080483c7 in swap (lhs=0xbfffda34, rhs=0xbfffda34) at buggy.c:30
  #2  0x0804836c in main () at buggy.c:12
  #3  0x420156a4 in __libc_start_main () from /lib/tls/libc.so.6
  (gdb) _

아하! 위를 잘 살피면 buggy.c의 30번째 줄에 있는 swap 함수가 do_swap()을 불렀는데, 그 때 do_swap()에 전달된 처음 두 인자가 같은 주소값을 넘겨줬다는 것을 확실히 알 수 있다. 마찬가지로 main()이 swap()을 부를 때 같은 주소값을 넘겨 준 것도 알 수 있다.

이제 stack frame을 한 단계 올라가는 up 명령(up)을 실행시키자.

  (gdb) up
  #1  0x080483c7 in swap (lhs=0xbfffda34, rhs=0xbfffda34) at buggy.c:30
  (gdb) _

이 때, 소스 창에는 커서가 swap() 함수의 30번째 줄로 이동한 것을 알 수 있다. (Emacs debugger mode의 또 하나 좋은 기능!)

swap()에 별 이상이 없으므로 다시 up 명령으로 한 단계 올라가자.

  (gdb) up
  #2  0x0804836c in main () at buggy.c:12
  (gdb) _

이제 커서는 main() 함수에서 swap()을 부른 줄 (12번째 줄)을 가리키고 있을 것이다. 자 여기서 버그 발견!

  swap(&i, &i);
를, 아래와 같이 바꾼다:

  swap(&i, &j);
자, 이제 다시 반복 작업만 남았다. 저장하고(C-x C-s), 컴파일하고(M-x compile), shell을 띄워서(M-x shell) 실행 파일 실행해보고, 이상이 없는가 확인해보자.

여기까지 Emacs debugger mode에서 gdb를 쓰는 법을 아주 간단히 훑어 보았다. 좀 더 자세한 내용을 원한다면:

  • Emacs debugger mode 창에 커서를 위치시키고 M-x describe-mode<RET>을 쳐 보기 바란다. 그러면 반대편 창에 Emacs debugger mode에 관한 설명이 나올 것이다.

  • Emacs Info page를 참고하라. 터미널에서 "info emacs"를 쳐 넣고, building->debugger 항목을 보라. 또는 Emacs에서 M-x info를 실행, emacs->building->debugger 항목을 봐도 된다. (후자를 권장함)

  • gdb info page를 참고하라. 위와 마찬가지로 stand-alone program인 info를 써도 되고, M-x info를 쓸 수도 있다.

5. etags

etags와 emacs의 관계는 ctags와 vi의 관계라 할 수 있다. etags는 여러 언어 파일들을 읽어 tag table을 만들어주며, emacs에서 이 tag table을 바탕으로 source들을 자유로이 navigate 할 수 있다. 예를 들어 주어진 함수 이름으로 함수의 정의를 찾는다는지, 변수 이름이 쓰인 곳을 찾는 등의 일을 할 수 있다.

일단 소스 디렉토리 루트를 지정해야 한다. 여러분이 작업 중인 소스가 있다면 그 소스를 써 보기를 권장하고, 그렇지 않다면 일반적으로 /usr/src/linux에 설치되어 있는 Linux kernel source를 써 보자. 여기서는 /usr/src/linux에 설치되어 있는 Linux kernel source를 가지고 예를 보여줄 것이다.

일단 etags는 작업 결과를 현재 디렉토리에 `TAGS'라는 파일로 기록한다.

주의! 이 글은 /usr/src/linux를 대상으로 하기 때문에, 일반 사용자는 이 디렉토리에 쓰기 권한이 없을 것이다. 따라서 etag 관련 작업을 하려면 root로 작업하던지, 아니면, 현재 디렉토리를 다른 곳으로 바꾸어야 할 것이다.

etags가 소스 파일을 분석할 때 현재 디렉토리만 대상으로 하기 때문에 거의 모든 경우 find 명령과 함께 쓴다:

  $ pwd
  /home/cinsk/src
  $ find -H /usr/src/linux -name '*.[csSh]' | etags -

즉 find 명령이 /usr/src/linux에 있는 *.c *.s *.S *.h에 대한 목록을 etags에 파이프로 넘기면, etags는 이를 stdin으로 받아서, 현재 디렉토리에 TAGS란 파일로 (이 예에서는 /home/cinsk/src/TAGS) 기록한다.

root 사용자라면 다음과 같이 /usr/src/linux/TAGS로 기록하는 것이 더 편할 것이다:

  # cd /usr/src/linux
  # find . -name '*.[csSh]' | etags -

이제 emacs에서 한 소스 파일을 불러오자. C-x C-f로 /usr/src/linux/include/linux/sched.h를 불러오자.

그 다음 적절한 함수 이름을 하나 찾아보자 (예를 들어 C-s sched_init으로 sched_init() 함수의 선언을 찾을 수 있다).

커서를 sched_init에 위치시키고, M-.를 눌러보자. 그러면 mini-buffer에 다음과 같이 메시지가 보인다:

  Find tag: (default sched_init) _

즉 다른 이름을 이 곳에 써 넣을 수도 있고, 바로 <RET>을 치면 default 값인 `sched_init'을 쓰게 된다.

아직 emacs에게 tag table인 TAGS가 어디에 있는지 알려주지 않았으므로 emacs는 tag table의 위치를 묻게 된다. 이때 아까 TAGS가 저장되어 있는 곳을 써 주면, emacs는 자동으로 sched_init() 함수가 위치한 곳을 보여준다.

만약 emacs가 보여준 소스가 원하는 것이 아니라면 다음 두 명령으로 같은 이름으로 계속 찾을 수 있다:

키 입력 설명
C-u M-. 다음으로 match되는 tag를 보여준다.
C-u - M-. 이전에 찾은 tag를 보여준다.

나는 위의 단축키를 별로 좋아하지 않아서 다음 네 줄을 $HOME/.emacs에 기록하여, "C-u M-.", "C-u - M-." 대신 "M-]", "M-["을 쓴다:

  (fset 'find-next-tag "\C-u\256")        ; macro for C-u M-.
  (fset 'find-prev-tag "\C-u-\256")       ; macro for C-u - M-.
  (global-set-key "\M-]" 'find-next-tag)
  (global-set-key "\M-[" 'find-prev-tag)

tag에 관한 좀 더 자세한 사항은 emacs info 페이지의 Tags section을 보기 바란다.

6. cscope

emacs/etags도 꽤 쓸만한 소스 탐색 기능을 제공하지만 emacs/cscope는 한층 더 발전된 소스 탐색 기능을 제공한다. emacs/etags가 emacs의 built-in package 형식인 반면, cscope는 별도의 패키지를 설치해야 하므로 조금 번거롭다.

일단 cscope의 홈페이지에 가서 cscope를 설치하자 -- http://cscope.sourceforge.net/

다음으로 설치하는 과정을 아래에 보인다 (아래에서 X.Y는 cscope의 버전에 따라 다름, 현재 15.4).

  # tar -xvzf cscope-X.Y.tar.gz
  # cd cscope-X.Y
  # ./configure
  # make install

"make install"을 하기 위해서는 root로 로긴할 필요가 있을 것이다. 다음으로 위에 이어서 다음과 같은 과정도 필요하다.

  # cd contrib
  # cd xcscope
  # cp cscope-indexer /usr/local/bin
  # cp xcscope.el /usr/share/emacs/site-lisp/

위 과정에서 /usr/share/emacs/site-lisp/의 위치는 emacs가 설치된 곳에 따라 다르지만, emacs -batch 명령을 수행해서 힌트를 얻을 수 있다:

  $ emacs -batch
  Loading /usr/share/emacs/site-lisp/site-start.d/aspell-init.el (source)...
  Loading /usr/share/emacs/site-lisp/site-start.d/lang.emacs.el (source)...
  Loading /usr/share/emacs/site-lisp/lang/lang-ko.el (source)...
  Loading /usr/share/emacs/site-lisp/site-start.d/po-mode-init.el (source)...
  $ _

이는 system-wide 설정 방법이며, root 권한을 얻을 수 없다면 cscope를 여러분의 디렉토리에 설치하고 (아까 설치할 때 "./configure --prefix=$HOME"을 쓴다든지..), cscope-indexer도 여러분의 $PATH에 들어 있는 곳, 예를 들면 $HOME/bin에 설치하고, 마지막으로 `xcscope.el'도 emacs load path에 추가해야 한다; emacs load path에 관한 것은 1을 참고하기 바란다.

마지막으로 아까 복사한 `xcscope.el'을 열어서 읽어보고 어떻게 $HOME/.emacs 파일을 수정해야 하는지 읽어보고 적용하기 바란다.

여기까지 끝났다면 emacs를 실행시키자.

cscope가 제대로 동작하기 위해서는 cscope database를 만들어야 한다; emacs에서 M-x cscope-index-file<RET>을 입력하고 원하는 디렉토리(/usr/src/linux)를 입력한다. (참고. /usr/src/linux는 꽤 큰 소스이므로 상당한 시간이 필요할 것이다.)

위 작업이 끝났다면 /usr/src/linux/에 cscope.out과 cscope.files라는 파일이 만들어 질 것이다.

이제 etags를 배울때와 마찬가지로 /usr/src/linux/include/linux/sched.h를 열자(C-x C-f). 다음으로 sched_init에 커서를 위치시키고 "C-c s d"로 원하는 정의를 찾을 수 있다 (아래 단축키 참고).

  • 터미널 모드가 아닌 emacs를 쓰고 있다면 소스 파일을 편집할 때 메뉴에 cscope 메뉴가 뜨는 것을 볼 수 있다. 여기서 cscope 관련 명령을 실행할 수도 있다 (단축키를 외우는 것이 싫다면..)

  • 기본적으로 다음과 같은 단축키들이 제공된다 (자세한 것은 `xcscope.el'의 주석을 꼭!!!! 한 번 읽어보기 바란다):

키 입력 설명
C-c s s Find symbol.
C-c s d Find global definition.
C-c s g Find global definition (alternate binding).
C-c s G Find global definition without prompting.
C-c s c Find functions calling a function.
C-c s C Find called functions (list functions called from a function).
C-c s t Find text string.
C-c s e Find egrep pattern.
C-c s f Find a file.
C-c s i Find files #including a file.

etags/cscope를 잘 쓰기만 하면 웬만한 source 분석은 무난히 해결할 수 있지만, 좀 더 강력하거나 편집 기능 보다 source 분석 전용 툴을 찾고 있다면, 아래 툴을 써 보기 바란다:





sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2010-12-28 16:32:57
Processing time 0.0031 sec