Backtrace — 예외까지 이어지는 동적 호출 스택

Backtrace — 예외까지 이어지는 동적 호출 스택

프로그램에서 예외가 터졌을 때 '어디를 거쳐 여기까지 왔는지'를 알려주는 게 백트레이스(backtrace)예요. Raku의 Backtrace 타입은 예외가 발생한 지점까지 이어지는 동적 호출 스택을 담고 있어요. Backtrace::Frame 객체들의 리스트로 구성되죠.

class Backtrace {}

본문

Backtrace는 동적 호출 스택을 담아요. 보통 예외가 던져진 지점까지의 호출 경로를 말하고, Backtrace::Frame 객체들의 리스트예요. 기본 문자열화는 불필요하거나 혼란스러운 프레임(예: &die 같은 루틴)을 숨겨요. 리스트이므로 개별 요소에도 접근할 수 있어요.

sub zipi { { { die "Something bad happened" }() }() };
try {
    zipi;
}
if ($!) {
    say $!.backtrace[*-1].raku;
}

이 코드는 리스트의 마지막 프레임, 즉 사건이 일어난 지점을 가리키는 프레임을 출력해요.

메서드

method new

multi method new()
multi method new(Int:D $offset)
multi method new(Mu \ex)
multi method new(Mu \ex, Int:D $offset)
multi method new(List:D $bt)
multi method new(List:D $bt, Int:D $offset)

호출 위치를 백트레이스의 출발점으로 삼아 새 백트레이스를 만들어요. $offset을 넘기면 그 오프셋을 사용해요. 객체나 리스트(이미 리스트 형태의 백트레이스를 담은)를 넘기면 현재 코드 대신 그 값을 사용해요.

my $backtrace = Backtrace.new;

method gist

multi method gist(Backtrace:D:)

"Backtrace(42 frames)" 형태의 문자열을 돌려줘요. 숫자는 list 메서드로 얻을 수 있는 프레임 수를 나타내요.

method Str

multi method Str(Backtrace:D:)

백트레이스의 간결한 문자열 표현을 돌려줘요. is hidden-from-backtrace로 표시된 루틴과, 구현에 따라서는 setting의 일부 루틴도 생략해요.

my $backtrace = Backtrace.new;
say $backtrace.Str;

method next-interesting-index

method next-interesting-index(Backtrace:D: Int $idx = 0, :$named, :$noproto, :$setting)

숨겨진 프레임과 기타 설정을 고려한 뒤, 다음 '흥미로운' 프레임의 인덱스를 돌려줘요. $named는 이름이 있는 것만, $noprotoproto를 숨기고, $setting은 setting으로 간주되는 것을 숨겨요.

sub zipi { { { die "Something bad happened" }() }() };
try zipi;
say $!.backtrace.next-interesting-index;           # OUTPUT: «2␤»
say $!.backtrace.next-interesting-index( :named ); #  OUTPUT: «4␤»

method outer-caller-idx

method outer-caller-idx(Backtrace:D: Int $startidx)

현재 프레임을 부른 프레임들의 인덱스를 리스트로 돌려줘요.

sub zipi { { { die "Something bad happened" }() }() };
try zipi;
say $!.backtrace.outer-caller-idx( 4 ); # OUTPUT: «[6]␤»

method nice

method nice(Backtrace:D: :$oneline)

백트레이스를 흥미로운 프레임들의 리스트로 돌려줘요. :$oneline이 설정되면 첫 프레임에서 멈춰요.

sub zipi { { { die "Something bad happened" }() }() };
try zipi;
say $!.backtrace.nice( :oneline ) if $!;
# OUTPUT: «  in sub zipi at /tmp/... line 1␤␤»

method full

multi method full(Backtrace:D:)

숨겨진 프레임, 컴파일러 특유 프레임, setting 프레임까지 포함한 전체 문자열 표현을 돌려줘요.

method list

multi method list(Backtrace:D:)

이 백트레이스의 Backtrace::Frame 객체 리스트를 돌려줘요.

method summary

method summary(Backtrace:D: --> Str:D)

!.is-hidden && (.is-routine || !.is-setting) 필터를 적용한 요약 문자열 표현을 돌려줘요.

sub inner { say Backtrace.new.summary }
sub outer { inner; }
outer;

이 프로그램의 결과는 아래와 같아요:

in method new at SETTING::src/core.c/Backtrace.rakumod line 85
in sub inner at test.raku line 1
in sub outer at test.raku line 2
in block <unit> at test.raku line 3

method concise

method concise(Backtrace:D:)

!.is-hidden && .is-routine && !.is-setting 필터를 적용한 간결한 문자열 표현을 돌려줘요. summary보다 더 엄격하게 루틴만 남겨요.

sub inner { say Backtrace.new.concise }
sub outer { inner; }
outer;

결과:

in sub inner at test.raku line 1
in sub outer at test.raku line 2

method map

multi method map(Backtrace:D: &block --> Seq:D)

각 요소에 대해 &block을 호출하고 반환값을 시퀀스로 모아 돌려줘요.

sub inner { Backtrace.new.map({ say "{$_.file}: {$_.line}" }); }
sub outer { inner; }
outer;

결과:

SETTING::src/core.c/Backtrace.rakumod: 85
SETTING::src/core.c/Backtrace.rakumod: 85
test.raku: 1
test.raku: 2
test.raku: 3
test.raku: 1

method flat

multi method flat(Backtrace:D:)

list와 동일한 백트레이스를 돌려줘요.

출처: Raku 공식 문서 — Backtrace