Debugging
M4 는 매크로 프로세서이므로 디버깅은 주로 매크로 trace 를 통해 이루어집니다.
디버깅을 위한 설정은 m4 명령 라인에서 -d
, -t
옵션을 이용해 할 수도 있고,
코드의 특정 영역을 trace 하기 위해 traceon
, traceoff
매크로를 사용할 수도 있습니다.
기본적으로 디버깅 정보는 stderr
로 출력되는데 debugfile
매크로를 이용하면
지정한 파일로 저장할 수도 있습니다.
dumpdef
dumpdef
dumpdef ( Names. . . )
매크로의 확장 결과는 void 입니다.
매크로 Names
의 definitions 을 출력합니다.
()
없이 사용하면 현재 위치에서 정의되어 있는 모든 매크로를 알파벳 순서로 출력합니다.
( 기본적으로 stderr 로 출력됩니다 ).
$ m4 <<\@
define(`nshift', `pushdef(`num', eval($1))_$0(shift($@))popdef(`num')')
define(`_nshift', `ifelse( num, 0, `$@', `define(`num', decr(num))$0(shift($@))')')
dumpdef(`nshift', `define')
@
define: <define> <--- built-in 매크로의 출력 형식
nshift: pushdef(`num', eval($1))_$0(shift($@))popdef(`num')
()
없이 사용하면 현재 위치에서 정의되어 있는 모든 매크로를 출력합니다.
$ m4 <<\@
define(`foo', 100)
define(`bar', 200)
dumpdef # () 없이 사용
define(`zoo', 300) # zoo 는 이후에 정의되므로 출력에서 제외됨
@
__file__: <__file__>
__gnu__:
__line__: <__line__>
__program__: <__program__>
__unix__:
bar: 200 # bar: 200
builtin: <builtin>
changecom: <changecom>
changequote: <changequote>
debugfile: <debugfile>
debugmode: <debugmode>
decr: <decr>
define: <define>
defn: <defn>
divert: <divert>
divnum: <divnum>
dnl: <dnl>
dumpdef: <dumpdef>
errprint: <errprint>
esyscmd: <esyscmd>
eval: <eval>
foo: 100 # foo: 100
format: <format>
ifdef: <ifdef>
ifelse: <ifelse>
include: <include>
incr: <incr>
index: <index>
indir: <indir>
len: <len>
m4exit: <m4exit>
m4wrap: <m4wrap>
maketemp: <maketemp>
mkstemp: <mkstemp>
patsubst: <patsubst>
popdef: <popdef>
pushdef: <pushdef>
regexp: <regexp>
shift: <shift>
sinclude: <sinclude>
substr: <substr>
syscmd: <syscmd>
sysval: <sysval>
traceoff: <traceoff>
traceon: <traceon>
translit: <translit>
undefine: <undefine>
undivert: <undivert>
traceon, traceoff
traceon
traceon ( Names. . . )
traceoff
traceoff ( Names. . . )
매크로의 확장 결과는 void 입니다.
traceon
,traceoff
매크로는 특정 위치부터 시작해서 특정 매크로만 trace 하고자 할 때 사용합니다.
특정 매크로만 선택적으로 trace 하고자 할 때는 m4 명령의 -t
옵션을 사용할 수도
있습니다.
traceon(`foo', `bar')
는 선택적으로 foo
, bar
매크로만
trace 를 합니다. ( 이때 foo
, bar
매크로가 현재 정의되어 있을 필요는 없습니다 ).
traceoff(`foo', `bar')
는 선택적으로 foo
, bar
매크로만
trace 를 중단합니다.
()
없이 사용하는 traceon
은 현재 위치에서 정의되어 있는 모든 매크로를 trace 합니다.
이것은 debugmode(t)
를 사용하는 것과 같은 것입니다.
그리고 ()
없이 사용하는 traceoff
는 모든 매크로 trace 를 중단합니다.
이것은 ()
없이 debugmode
를 사용하는 것과 같은 것입니다.
trace 정보는 기본적으로 stderr 로 출력됩니다.
$ m4 <<\@ $ m4 <<\@
define(`foo', `Hello World.') define(`foo', `Hello World.')
define(`echo', `$@') define(`echo', `$@')
traceon # 모든 매크로를 trace traceon(`echo') # echo 매크로만 trace
foo foo
echo(`gnus', `and gnats') echo(`gnus', `and gnats')
@ @
m4trace: -1- foo Hello World.
Hello World. m4trace: -1- echo
m4trace: -1- echo gnus,and gnats
gnus,and gnats
$ m4 <<\@
define(`nshift', `pushdef(`num', eval($1))_$0(shift($@))popdef(`num')')
define(`_nshift', `ifelse( num, 0, `$@', `define(`num', decr(num))$0(shift($@))')')
define(`foreachq', `ifelse(`$2', `', `',
`pushdef(`$1')_$0(`$1', `$3', `', $2)popdef(`$1')')')
define(`_foreachq', `ifelse(`$#', `3', `',
`define(`$1', `$4')$2`'$0(`$1', `$2', nshift(3,$@))')')
# 현재 위치에서 부터
debugmode(`ael')traceon(`foreachq',`_foreachq') # foreachq, _foreachq 매크로만 trace
foreachq(`elem', `111, 222, 333', `Word was: elem
')
traceoff
foreachq(`elem', `foo, bar, zoo', `Word was: elem
')
@
m4trace:12: -1- foreachq(elem, 111, 222, 333, Word was: elem
) -> ifelse(`111, 222, 333', `', `',
`pushdef(`elem')_foreachq(`elem', `Word was: elem
', `', 111, 222, 333)popdef(`elem')')
m4trace:12: -1- _foreachq(elem, Word was: elem
, , 111, 222, 333) -> ifelse(`6', `3', `',
`define(`elem', `111')Word was: elem
`'_foreachq(`elem', `Word was: elem
', nshift(3,`elem',`Word was: elem
',`',`111',`222',`333'))')
Word was: 111
m4trace:12: -1- _foreachq(elem, Word was: elem # 매 단계 _foreachq 매크로의
, 111, 222, 333) -> ifelse(`5', `3', `',
`define(`elem', `222')Word was: elem
`'_foreachq(`elem', `Word was: elem
', nshift(3,`elem',`Word was: elem
',`111',`222',`333'))')
Word was: 222
m4trace:12: -1- _foreachq(elem, Word was: elem # 인수들을 조회할 수 있다.
, 222, 333) -> ifelse(`4', `3', `',
`define(`elem', `333')Word was: elem
`'_foreachq(`elem', `Word was: elem
', nshift(3,`elem',`Word was: elem
',`222',`333'))')
Word was: 333
m4trace:12: -1- _foreachq(elem, Word was: elem
, 333) -> ifelse(`3', `3', `',
`define(`elem', `')Word was: elem
`'_foreachq(`elem', `Word was: elem
', nshift(3,`elem',`Word was: elem
',`333'))')
Word was: foo
Word was: bar
Word was: zoo
debugmode
debugmode
debugmode ( )
debugmode ( Flags )
매크로의 확장 결과는 void 입니다.
debugmode 매크로를 이용해 디버깅 정보를 얼마나 자세히 표시할지 설정할 수가 있습니다.
이것은 m4 명령의 -d
옵션을 이용해 설정하는 것과 동일한 것입니다.
매크로 trace 정보에는 m4trace:
prefix 가 붙고, 그 외 매크로 호출과 관련 없는 정보에는
m4debug:
prefix 가 붙어 출력됩니다.
flag 값을 설정할 때는 앞에 -
or +
를 붙일 수가 있는데
-
를 붙이면 기존 설정값에서 해당 flag 가 제거되고 +
를 붙이면 추가됩니다.
debugmode 매크로를 ()
없이 사용하면 설정된 flag 값들이 clear 됩니다.
( 이것은 -d
옵션을 사용하지 않는 것과 같은 것입니다 ).
인수 값이 없는 debugmode()
형식은 flag 값을 디폴트로 설정합니다.
( 이것은 -d
옵션을 flag 없이 사용하는 것과 같은 것으로 디폴트 값은 aeq
가 됩니다 ).
debug 정보는 기본적으로 stderr 로 출력됩니다.
Flags | Description |
---|---|
a | show actual arguments |
c | show before collect, after collect and after call |
e | show expansion |
f | say current input file name |
i | show changes in input files |
l | say current input line number |
p | show results of path searches |
q | quote values as necessary, with a or e flag |
t | trace for all macro calls, not only traceon'ed |
x | add a unique macro call id, useful with c flag |
V | shorthand for all of the above flags |
q
flag 를 사용하는 이유 )
매크로가 확장될 때는 전달된 인수에서도 prescan 에의해 확장이 일어나고
결과에 대해서도 main scan 에의해 확장이 일어납니다.
인수에 포함된 quotes 은 확장을 방지하는 역할을 하고 한 단계씩 quotes 이 제거됩니다.
quotes 이 제거되면 trace 시에 실제 인수의 개수가 몇 개인지 알 수 없는 경우가 생길 수 있습니다.
이때 q
flag 을 사용하면 인수와 확장 결과에 quotes 을 추가해서 표시해 줍니다.
$ m4 -dtael <<\@
define(`args', `num: $#')
args(`foo', `bar, baz', `zoo') # 실제 인수의 개수는 3 개인데
@
m4trace:1: -1- define(args, num: $#)
# 인수부분 prescan 에 의해
m4trace:3: -1- args(foo, bar, baz, zoo) -> num: 3 # quotes 이 제거되어 4 개로 보인다.
num: 3
-------------------------------------------------
$ m4 -dtaelq <<\@ # "q" 디버그 flag 추가
define(`args', `num: $#')
args(`foo', `bar, baz', `zoo')
@
m4trace:1: -1- define(`args', `num: $#')
m4trace:3: -1- args(`foo', `bar, baz', `zoo') -> `num: 3' # 인수와 확장 결과에 quotes
num: 3 # 을 추가해 표시해 준다.
다음은 quotes 사용과 관련해 발생한 오류를 디버깅을 통해서 해결하는 것입니다.
다음 예제를 보면 aa
매크로 정의에 사용된 값과 ifelse
에서 비교에 사용된
값이 같기 때문에 결과로 111
을 기대하였으나 222
가 출력되고 있습니다.
$ m4 <<\@
define(`aa',`(`')')
ifelse(aa,`(`')', 111, 222)
@
222
먼저 간단히 m4 명령의 -d
옵션을 이용해 디버깅 정보를 출력해 볼 수 있는데
이때 사용된 V
flag 값은 모든 디버깅 flag 을 enable 하는 것과 같습니다.
따라서 필요하지 않는 정보도 많이 출력되고 q
flag ( quote value ) 가 포함되기 때문에
출력되는 value 들에 추가로 quote 이 되어서
실제 quotes 이 어떻게 처리되는지 보기에 어려움이 있습니다.
$ m4 -dV <<\@
define(`aa',`(`')')
ifelse(aa,`(`')', 111, 222)
@
m4debug: input read from stdin
m4trace:stdin:1: -1- id 1: define ...
m4trace:stdin:1: -1- id 1: define(`aa', `(`')') -> ???
m4trace:stdin:1: -1- id 1: define(...)
m4trace:stdin:2: -1- id 2: ifelse ...
m4trace:stdin:2: -2- id 3: aa ...
m4trace:stdin:2: -2- id 3: aa -> ???
m4trace:stdin:2: -2- id 3: aa -> `(`')'
m4trace:stdin:2: -1- id 2: ifelse(`()', `(`')', `111', `222') -> ???
m4trace:stdin:2: -1- id 2: ifelse(...) -> `222'
222
m4debug:stdin:3: input exhausted
따라서 이번에는 t
( 모든 매크로 trace ), a
( 인수값 표시 ), e
( 확장결과 표시 )
l
( 라인넘버 표시 ) flags 을 사용해 trace 를 하면 필요한 정보만 볼 수 있고
실제 m4 에서 quotes 이 어떻게 처리되는지 볼 수 있습니다.
결과를 살펴보면 ifelse
의 비교에 사용된 두 값이 틀리게 나오는 것을 알 수 있습니다.
$ m4 -dtael <<\@
define(`aa',`(`')')
ifelse(aa,`(`')', 111, 222)
@
m4trace:1: -1- define(aa, (`')) # m4trace:1: 에서 1 은 라인넘버
m4trace:2: -2- aa -> (`') # -> 로 표시되는 값이 확장결과
m4trace:2: -1- ifelse((), (`'), 111, 222) -> 222 # () 와 (`') 는 값이 틀리다.
222
-------------------------------
$ m4 <<\@
debugmode(`tael') # -d 옵션 대신에 debugmode 매크로 사용
define(`aa',`(`')') # 동일한 결과가 출력된다.
ifelse(aa,`(`')', 111, 222)
@
m4trace:2: -1- define(aa, (`'))
m4trace:3: -2- aa -> (`')
m4trace:3: -1- ifelse((), (`'), 111, 222) -> 222
222
따라서 ifelse
매크로 확장 결과로 111
이 나오게 하려면 다음과 같이
aa
매크로의 정의를 변경해 주어야 합니다.
$ m4 -dtael <<\@
define(`aa',``(`')'') # 매크로 aa 의 값을 ``(`')'' 로 변경
ifelse(aa,`(`')', 111, 222)
@
m4trace:1: -1- define(aa, `(`')')
m4trace:2: -2- aa -> `(`')'
m4trace:2: -1- ifelse((`'), (`'), 111, 222) -> 111 # 111 이 출력된다.
111
------------------------------------------
1. define(`aa',``(`')'') 에서 prescan 에의해 quotes 이 한 단계 제거되면
매크로 aa 는 `(`')' 로 정의된다.
2. ifelse(aa, `(`')', 111, 222) 는 aa 매크로 정의에 따라
ifelse(`(`')', `(`')', 111, 222) 가 되고
prescan 에 의해 quotes 이 한 단계 제거되면
최종 결과는 ifelse((`'), (`'), 111, 222) 가 되어 111 로 확장된다.
debugfile
debugfile
debugfile ( )
debugfile ( File )
매크로의 확장 결과는 void 입니다.
debug, trace, dumpdef 정보는 기본적으로 stderr 로 출력이 되는데
m4 명령의 --debugfile
옵션이나 debugfile
매크로를 이용하면 지정한 파일로
출력을 저장할 수 있습니다.
저장은 append 형식으로 되는데 지정한 파일을 open 할 수 없을 경우에는 기존 값이 유지됩니다.
debugfile 매크로를 ()
없이 사용하면 다시 출력이 stderr 로 되고,
인수 값이 없는 debugfile()
형식을 사용하면 출력이 discard 됩니다.
$ m4 <<\@
define(`foo', `Hello World.')
define(`echo', `$@')
traceon(`foo', `echo')
foo
echo(`gnus', `and gnats')
@
m4trace: -1- foo # 디폴트는 stderr 로 출력
Hello World.
m4trace: -1- echo
gnus,and gnats
-----------------------------
$ m4 <<\@
define(`foo', `Hello World.')
define(`echo', `$@')
traceon(`foo', `echo')
debugfile(`trace1') # debugfile 을 이용해 trace1 파일로 출력
foo
echo(`gnus', `and gnats')
@
Hello World.
gnus,and gnats
$ cat trace1
m4trace: -1- foo
m4trace: -1- echo