SqueakByExample:6.5

From 흡혈양파의 번역工房
Revision as of 05:23, 16 August 2012 by Onionmixer (talk | contribs) (SBE 디버거 페이지 추가)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

디버거(Debugger)

디버거는 틀림없이 스퀵 도구 스위트(Squeak tool suite)에서 가장 강력한 도구입니다 이것은 디버깅에만 사용되는 것이 아니라, 새로운 코드를 작성하는 작업에도 쓰입니다. 디버거를 나타내려면, bug를 타이핑함으로써 시작합니다..


Squeak comment.png브라우저를 사용하여, 클래스 String(문자열)에 다음 메소드를 추가합니다:


그림 6.24: PreDebugWindow는 버그를 알립니다.


메소드 6.1 Buggy 메소드

suffix
  "assumes that I'm a file name, and answers my suffix, the part after the last dot"
  | dot dotPosition |
  dot := FileDirectory dot.
  dotPosition := (self size to: 1 by: --1) detect: [ :i | (self at: i) = dot ].
   self copyFrom: dotPosition to: self size


물론 우리는 이러한 trivial 메소드가 작동할 것을 확신하며, 그러므로, SUnit test를 작성하는 대신에, 워크스페이스에서 "readme.txt" 접미사를 타이핑하고 print it (p) 할 것입니다. 놀랍지 않습니까? 기대했던 답변 ‘txt’를 얻는 대신에, 그림 6.24에서 보이는 것처럼, PreDebugWindow가 팝업 됩니다.

PreDebugwindow는 어떤 에러가 발생하였는지를 우리에게 말해주는 타이틀바(title-bar)를 갖고 있으며, 에러를 발생시켰던 메시지의 스텍 흔적(a stack trace)을 보여드립니다. 흔적(the trace)의 아래에서 시작한, UndefinedObject»DoIt는, 우리가 워크스페이스에서 ‘readme.txt’ 접미사를 선택할 때 컴파일되고 실행된 코드를 나타내고, 스퀵에게 print it 을 요청합니다. 이 코드는 물론, ByteString 오브젝트(‘readme.txt’)에 메시지 suffix를 발송했습니다. 이 작업은 실행을 위해 클래스 String에 suffix 메소드를 상속되도록 하며, 모든 정보는 스텍 흔적(the stack trace) ByteString(String)»suffix의 다음 라인에 인코딩 됩니다. 스텍을 불러일으키면, 우리는 발송된 suffix는 detect:. . . 되고 마침내 발송된 detect:ifNone 가 errorNotFound 되었음을 볼 수 있습니다.

점(dot)을 발견하지 못했습니다. 이유를 알아보기위해서는 디버거가 필요하기때문에 Debug 버튼을 클릭합니다.


Squeak comment.png여러분은 the stack trace의 라인들 중 아무 라인이나 클릭하여, 디버거를 열 수 있습니다. 디버거는 the corresponding method(대응 메소드)에 이미 포커스된 상태로 열릴 것입니다.


그림 6.25에 디버거가 보입니다; 이 디버거는 처음에 매우 어렵게 보이지만, 사용하기가 꽤 쉽습니다. 타이틀 바와 상단 패널은 우리가 PreDebugWindow 에서 보았던 것들과 매우 유사해 보입니다.


그림 6.25: 디버거


그럼에도 불구하고, 디버거(the debugger)는 메소드 브라우저와 스텍흔적(the stack trace)을 결합하여, 여러분이 스텍흔적에서 라인을 선택할 때, 대응 메소드(the corresponding method)가 아래의 패널에 보이게 만듭니다. 에러를 발생시킨 실행이, 여전히 여러분의 이미지에 있게 하고, 대기 상태에 있는 것을 인식하는 것이 중요합니다. 스택 추적의 각 라인은 실행을 지속시키기 위해 필요한 모든 정보를 포함하는 실행스텍(execution stack)에 프레임을 나타냅니다. 이것은 그것들의 인스턴스 변수들과 실행 메소드들의 모든 임시 변수들과 함께 연산에 관여된 모든 오브젝트를 포함합니다.

그림 6.25에서, 우리는 상단 패널에서 detect:ifNone: method를 선택하였습니다. 메소드 바디는 중앙 패널에 표시되며, 메시지 주위에 파랑색으로 강조된 value는 메시지 value에 발송된 현재 값을 보여드리며, 답변을 기다리고 있는 상태입니다.

디버거의 아래에 있는 4개의 패널은 실제로 두 개의 miniinspector입니다. 워크스페이스 패널들 없이) 왼쪽의 인스펙터(inspector)는 중앙 패널(the center pane)에서 self라 작명된 현재 오브젝트를 보여드립니다. 여러분이 스텍 프레임(stack frames)을 선택함에 따라, self의 아이덴티티는 변경될 수 있으며, 그리고 self- 인스펙터(the self-inspector)의 컨텐츠도 변경될 수 있습니다. 아래 왼쪽 패널에서 여러분이 self를 클릭하면 self가 인터벌( 10 to:1 by—1),이며 우리가 기대했던 것이라는 것을 알게 될 것입니다. 워크스페이스 패널은 디버거의 미니 인스펙터에 필요하지 않습니다. 그 이유는 모든 변수들은 메소드 패널의 범위(scope)에 있게 될 것이므로, 이 패널에서 타이핑을 하거나 또는 표현식들을 선택하고 그것들을 평가하는 작업을 자유롭게 할 수 있습니다. 여려분은 항상, 메뉴 또는 CMD-i를 사용하여 여러분의 변경사항들을 cancel (l)할 수 있습니다.

오른쪽에 있는 인스펙터는 현재 상황에서의(current context) 임시 변수를 보여드립니다. 그림 6.25에서, value는 파라미터 exceptionBlock에 발송되었습니다.

이 파라미터의 현재 값을 보기 위해, 컨텍스트 인스펙터(context inspector)에서 exceptionBlock을클릭 합니다. 이것은 여러분에게 exceptionBlock가 [self errorNotFound: ...]임을 말해드릴 것입니다. 그러므로 우리가 대응하는 에러메시지를 보게 되는 것은 놀랄만한 일이 아닙니다. 만약 여러분히 우연히 미니 인스펙터에 보이는 변수들 중의 하나에 풀-인스펙터(full inspector) 또는 익스플로러를 열기를 원하신다면, 변수의 이름에 더블클릭을 하거나 또는 변수의 이름을 선택하고 노랑 버튼 메뉴에서 inspect (i) 또는 explore (I) 를 선택하셔야 합니다. 이 작업은 여러분이 다른 코드를 실행하는 동안 변수가 변하는 방식을 보기를 원하실 때 유용합니다.

메소드 창 보기로 돌아가면, 우리는 문자열 ‘readme.txt’에 있는 dot를 찾기 위해 메소드의 끝에서 두 번째 라인을 기대했으며, 그리고 그 실행은 결코 마지막 라인에 도달하지 못했다는 것을 알 수 있습니다. 스퀵은 우리가 실행한 다음 뒤로 가도록 허용하지 않으며, 메소드를 다시 시작하여, 오브젝트를 변형하지 않고 새로운 오브젝트를 만드는 작업에 매우 잘 들어맞도록 안내합니다.


Squeak comment.pngRestart를 클릭하면, 현재 메소드의 첫 번째 표현(statement)로 돌아가는 실행의 중심 자리를 보게 될 것입니다. 파랑색 강조는 다음 메시지가 do:에 발송될 것이라는 것을 보여드립니다. (그림 6.26을 보십시오.)


IntoOver는 실행을 위한 두 개의 다른 방법을 제공합니다. 만약 여러분이 Over 버튼을 클릭하면, 스퀵은 에러가 없을 경우, 첫 번째 단계에서 현재 메시지 발송[the current message-send, (이번 경우엔 do):]을 실행합니다. 그러므로 Over는 현재 메소드에서 다음 메시지 발송(the next message-send)으로 데려갈 것이며, 그것은 값(value)이 됩니다: 이 방법은 정확히 우리가 시작했었던 방법이기에 이 지면에서 더 많은 도움이 필요 없을 것입니다. 우리가 해야 할 작업은 do:가 우리가 찾고 있는 문자를 찾지 못하는 이유를 알아내는 것입니다.


Squeak comment.pngOver를 클릭하고 그림 6.26에 보이는 상황으로 돌아가기 위해 Restart를 클릭합니다.


Squeak comment.pngInto를 클릭하면, 스퀵은 이번 경우에 Collection»do:.인 강조된 메시지 발송(message-send)에 대응하는 메소드로 들어갑니다.

그럼에도 불구하고, 이 작업도 많은 도움이 되지는 않습니다: 우리는 Collection»do가 망가지지 않았다는 것에 대해 어느 정도 자신감이 있습니다. 버그는 우리가 스퀵에게 요청한 작업에 있게 될 확률이 높습니다. Through는 이번 사례에서 사용할 적합한 버튼입니다: 우리는 do: 그 자체의 상세한 내용들을 무시하기를 원하고 인수 block(the argument block)의 실행에 집중할 것입니다.


그림 6.26: detect: ifNone: method를 다시 시작한 후의 디버거입니다.


Squeak comment.pngThrough를 클릭합니다. 컨텍스트 창(the context window)에서 여러분이 하는 방식으로 each를 선택합니다. 여러분은 do: 메소드가 실행하는 10에서 부터 시작하는 each 카운트(count)를 보실 수 있어야만 합니다.

each가 7이 될 때, 우리는 ifTrue:block이 실행될 것을 기대할 수 있지만, 그것은 실행되지 않습니다. 무엇이 틀렸는지 보기 위해, 그림 6.27에 묘사된 것 처럼, value의 Into 실행으로 이동합니다..

Into를 클릭한 다음, 우리는 그림 6.28에 보이는 위치에 있는 우리 자신을 발견할 것입니다. 이 장면은 마치 우리가 suffix 메소드로 돌아간 것처럼 보이며, 그 이유는 우리가 현재, detect: 에 대한 인수로서 제공된 suffix인 블록을 실행하고 있기 때문입니다. 만약 여러분이 컨텍스트 인스펙터(the context inspector)에서 i를 선택하셨다면, 여러분은 그것의 현재 값을 보실 수 있을 것이며, 여러분이 만약 지금까지 잘 따라오셨다면, 그 값은 반드시 7이 되어야 합니다. 그 다음 여러분은, self-인스펙터로부터 self의 대응 구성요소(the corresponding element)를 선택할 수 있습니다. 그림 6.28에서, 여러분은 문자열의(string) 구성요소(element) 7이 문자(character) 46임을 볼 수 있으며, 이것은 실제로 dot 입니다. 만약 여러분이 컨텍스트 인스펙터(the context inspector)에서 dot를 선택하셨다면, 그 dot의 값이 '.' 임을 보시게 될 것입니다. 그리고 지금 여러분은 왜 이 값들이 동일하지 않은지를 알 수 있습니다: dot가 문자열(String)인 것에 대 대비, ‘readme.txt’의 일곱 번째 문자는 물론 문자(Character)입니다.

이제 우리는 버그(bug)를 보게 됩니다, 버그수정(fix)은 명확합니다: 우리는 검색을 시작하기 전에 dot를 문자(Character)로 변환시켜야만 합니다.


그림 6.27: Through the do: method 의 단계를 여러 번 밟은 후의 디버거


그림 6.28: 'readme.txt' at: 7 가 dot와 동일하지 않은 이유를 보여주는 디버거


그림 6.29: 디버거에서 suffix 메소드를 변경하기: 내부 블록(inner block)으로부터 나가기(exit) 확인(confirmation)을 요청


Squeak comment.png디버거 오른쪽에 있는 코드를 변경하면, 할당이(the assaignment) dot := FileDirectory dot asCharacter 를 읽고 변경사항을 accept 하게 됩니다.


우리가 detect: 내부의 블록 내부에 코드를 실행하고 있기 때문에 여러 개의 스텍 프레임(stack frames)은 이 변경을 만들기 위해 포기되어야만 할 것입니다. 스퀵은 이 작업이 우리가 원하는 것인지를 물어보며(그림 6.29를 보십시오) 만약 우리가 yes를 클릭한다고 가정하면, 새로운 메소드를 저장(그리고 컴파일)할 것입니다.


Squeak comment.pngRestart를 클릭하면 Proceed됩니다: 디버거 창이 사라질 것이며, 표현식 'readme.txt' suffix의 평가가 완료되고, 그리고 답변 ‘.txt’를 인쇄하게 될 것입니다.


이 답변이 정확합니까? 안타깝게도 우리는 확신 있게 말할 수 없습니다. Suffix(접미사)가 .txt 가 되어야 할까요 또는 txt가 되어야 할까요? suffix에 있는 메소드 주석은 아주 정확한 것은 아닙니다. 이 어느정도 이러한 문제를 피하는 방법은 답변을 정의하는 SUnit test를 작성하는 것입니다.


메소드 6.2: suffix(접미사) 메소드를 위한 단순한 테스트

testSuffixFound
  self assert: 'readme.txt' suffix = 'txt'


워크스페이스에서 동일한 테스트를 실행하는 것보다 좀더 많은 노력이 필요했지만, Sunit을 사용하는 것은 실행 가능한 문서로서 테스트를 저장하고, 다른 사람들이 그 테스트를 실행하기 쉽게 만듭니다. 더욱이, 만약 여러분이 메소드 6.2를 클래스 StingTest에 추가하고, Sunit과 함게 그 테스트 스위트(test suite)를 실행하였다면, 디버깅 에러로 매우 신속하게 돌아갈 수 있을 것입니다. Sunit은 실패하는 주장(the failing assertion)에 디버그를 열지만, 여러분은 단지 스텍으로 다시 돌아가 한 프레임을 낮추기만 하시면 되며, 테스트를 Restart 하고, suffix 메소드로 Into 하면, 우리가 그림 6.30에서 하였던 것처럼, 에러를 수정할 수 있습니다. 그 다음, 두 번째로 해야 할 작업은 단지 Sunit Test Runner에 있는 Run Failures 버튼을 클릭하고, 지금 패스중인 테스트를 확인(confirm)하는 것입니다.


여기에 더 나은 테스트 가 있습니다:

메소드 6.3 suffix 메소드를 위한 더 나은 테스트

testSuffixFound
  self assert: 'readme.txt' suffix = 'txt'.
  self assert: 'read.me.txt' suffix = 'txt'


왜 이 테스트가 더 나을 까요? 그 이유는 이 테스트는 타켓 문자열(String)에 하나의 점(dot) 이상의 것이 있을 경우, reader에게 메소드가 무엇을 해야 할지를 알려주기 때문입니다.

디버거에 에러와 주장 실패(assertion failures)를 잡아내는 작업 뿐만 아니라, 디버거에 들어가는 몇 가지 다른 방법들이 있습니다. 만약 여러분이 무한 루프(infinite loop)에 들어가는 코드를 실행하면, 그 코드를 인터럽트하고, 'CMD- 를 (이것은 여러분이 어디서 영어를 배웠는 가에 따라 그것은 마침표 또는 하나의 구두점으로 부를 수 있습니다)[1]타이핑하여 연산 중에 디버거를 열 수 있습니다. 여러분은 또한 self halt를 삽입하기 위해 의심스러운 코드를 편집할 수 있습니다.그러므로 예를 들어, 우리가 다음과 같이 읽힐 suffix 메소드를 편집할 수 있을 것입니다.


메소드 6.4 suffix 메소드에 halt 삽입하기

suffix
  "assumes that I'm a file name, and answers my suffix, the part after the last dot"
  | dot dotPosition |
  dot := FileDirectory dot asCharacter.
  dotPosition := (self size to: 1 by: --1) detect: [ :i | (self at: i) = dot ].
  self halt.
   self copyFrom: dotPosition to: self size


우리가 이 메소드를 실행할 때, self halt의 실행은 우리가 진행할 수 있는 장소에 pre-debugger를 불러오거나 또는 디버거로 들어가 변수들을 보고, 연산을 진행하며(step), 코드를 수정시킵니다. 이것이 디버거에 관한 모든 내용이지만, suffix 메소드에 관한 모든 내용은 아닙니다. 초기 버그는 타켓 문자열(the target string)에 아무 dot도 없으며 suffix 메소드는 에러를 발생시킬 것이라는 것을 알게 해드렸어야 합니다.


그림 6.30: 디버거에서 suffix 메소드 변경하기: SUnit assertion failure 이후, the off-by one 에러를 수정하기.


이것은 우리가 원하는 동작이 아니므로, 이번 사례에서 어떤 일이 일어나야 할지를 지정하기 위한 두 번째 텍스트 (second text)를 추가합시다.


메소드 6.5: 메소드를 위한 두 번째 테스트: 타켓은 suffix(접미사)를 갖고 있지 않습니다.

testSuffixNotFound
  self assert: 'readme' suffix = ''


Squeak comment.png메소드 6.5를 클래스 StingTest에 있는 테스트 스위트(the test suite)에 추가하고, 그 테스트가 에러를 발생시기는 것을 지켜봅니다. SUnit에서 에러가 있는 테스트를 선택하여 디버거에 들어가고, 코드를 수정하여, 테스트가 패스되게 합니다. 이 작업을 가장 쉽고 분명하게 하는 방법은, 두 번째 인수 블록(block)은 단순히 문자열 크기(the string size)를 리턴하는 위치인 detect: ifNone으로 detect: 메시지를 교체하는 것입니다.


우리는 7장에서 SUnit에 관하여 좀더 배울 것입니다.


Notes

  1. CMD-SHIFT-를 타이핑하여 언제든지 응급 디버거(emergency debugger)를 불러올 수 있다는 것을 아는 것 또한 중요합니다.