[Why Programs Fail] Deducing Errors, 오류 연역

1,410 views
1,283 views

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,410
On SlideShare
0
From Embeds
0
Number of Embeds
393
Actions
Shares
0
Downloads
13
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

[Why Programs Fail] Deducing Errors, 오류 연역

  1. 1. 프로그램은<br />왜<br />실패하는가?<br />오류 연역Deducing Errors<br />http://ohyecloudy.com<br />http://cafe.naver.com/architect1<br />
  2. 2. 연역(Deduction)<br />어떤 명제로부터 추론 규칙에 따라 결론을 이끌어 냄. 또는 그런 과정. 일반적인 사실이나 원리를 전제로 하여 개별적인 사실이나 보다 특수한 다른 원리를 이끌어 내는 추리를 이른다. 경험을 필요로 하지 않는 순수한 사유에 의하여 이루어지며 그 전형은 삼단 논법이다.<br />-네이버 국어 사전<br />
  3. 3. 프로그램 분석에서 연역은<br />추상적인 프로그램 코드에서 구체적인 프로그램 실행으로의 추론 기법<br />정적 분석<br />실제로 프로그램을 수행할 필요 없다.<br />
  4. 4. 코드만으로 연역해 낼 수 있는 일반적인 오류들은 무엇인가?<br />이 장의 주제<br />
  5. 5. 값 근원의 격리<br />제어 흐름 이해하기<br />의존성 추적<br />프로그램 슬라이싱<br />코드 악취의 연역<br />정적 분석의 한계<br />정리<br />
  6. 6. 뭔가 불가능한 일이 일어났다면, 그리고 그에 대한 확실한 정보는 그것이 실제로 일어났다는 것뿐이라면, 결과로부터 거슬러 생각해서thinking backward이유들을 찾아내야 한다.<br />- Kernighan &Pike(1999)<br />
  7. 7. 10 INPUT X<br />20 Y = 0<br />30 X = Y<br />40 PRINT “X = “, X<br />항상X = 0 출력<br />거슬러 생각하기<br />실패 원인에 <br />영향을 주는<br />문장을 찾는다.<br />어디에서 비롯되었을까?<br />
  8. 8. 값 근원의 격리<br />제어 흐름 이해하기<br />의존성 추적<br />프로그램 슬라이싱<br />코드 악취의 연역<br />정적 분석의 한계<br />정리<br />
  9. 9. $ gcc –o fibonaccifibonacci.c<br />$ ./fibonacci<br />fib(9) = 55<br />fib(8) = 34<br />fib(7) = 21<br />fib(6) = 13<br />fib(5) = 8<br />fib(4) = 5<br />fib(3) = 3<br />fib(2) = 2<br />fib(1) = 1291239<br />어디에서 비롯되었을까?<br />
  10. 10. #include &lt;stdio.h&gt;<br /> <br />int fib( int n )<br />{<br />int f, f0 = 1, f1 = 1;<br />  while( n &gt; 1 ) {<br /> n = n - 1;<br /> f = f0 + f1;<br /> f0 = f1;<br /> f1 = f;<br /> }<br /> return f;<br />}<br /> <br />int main()<br />{<br />int n = 9;<br />  while( n &gt; 0 ) {<br />printf( &quot;fib(%d) = %d &quot;, n, fib(n) );<br /> n = n - 1;<br /> }<br />  return 0;<br />}<br />값에 영향을<br />미칠 수 있는<br />코드 영역을 식별<br />문장들이 <br />수행되는 <br />순서를 조사<br />
  11. 11. 진입 : fib(n)<br />int f<br />int f0 = 1<br />문장 == 노드<br />간선 == 제어흐름<br /> A  B<br /> A는 B에 앞선다.<br /> B는 A를 따른다.<br />int f1 = 1<br />while( n &gt; 1 )<br />n = n - 1<br />f = f0 + f1<br />f0 = f1<br />f1 = f<br />return f<br />종료<br />
  12. 12. 제어 흐름의 난점들<br />점프와 goto<br />goto 50<br />간접 점프<br />goto x, 함수 포인터<br />동적 분배<br />shape.draw()<br /> rectangle::draw()<br /> circle::draw()<br />예외<br />throw new Exception(“제기랄”)<br />
  13. 13. 값 근원의 격리<br />제어 흐름 이해하기<br />의존성 추적<br />프로그램 슬라이싱<br />코드 악취의 연역<br />정적 분석의 한계<br />정리<br />
  14. 14. 문장이 주는 영향<br />쓰기: 프로그램 상태를 변경<br />v1 = 1<br />제어: 프로그램 카운터 변경<br />while(n&gt;1), if(n&gt;1)<br />
  15. 15. 문장이 받는 영향<br />읽기: 프로그램 상태를 읽는다.<br />v2 = v1 + 1<br />변수 v1이 가리키는 주소에서 값을 읽는다.<br />수행: 프로그램 카운터가 현재 문장을 가리키는가.<br />
  16. 16.
  17. 17. 진입 : fib(n)<br />int f<br />int f0 = 1<br />자료 의존성<br />A의 자료가 B에서 쓰인다.<br />int f1 = 1<br />제어 의존성<br />A는 B의 수행을 제어한다.<br />while( n &gt; 1 )<br />n = n - 1<br />f = f0 + f1<br />f0 = f1<br />f1 = f<br />return f<br />종료<br />
  18. 18. 이 값은 어디로 가는가?<br />진입 : fib(n)<br />int f<br />int f0 = 1<br />n의 자료 의존성이 있는<br />while 문장의 <br />제어 의존성<br />int f1 = 1<br />n의 자료 의존성<br />while( n &gt; 1 )<br />n = n - 1<br />f = f0 + f1<br />f0 = f1<br />결국<br />n의 값은 f, f0, f1의 값을 결정<br />궁극적으로 f값도 결정<br />f1 = f<br />return f<br />종료<br />
  19. 19. 이 값은 어디에서 왔는가?<br />진입 : fib(n)<br />f의 자료 의존성<br />int f<br />int f0 = 1<br />int f1 = 1<br />while( n &gt; 1 )<br />n = n - 1<br />f = f0 + f1<br />f0 = f1<br />f1 = f<br />결국<br />f(1)의 값은 초기화 되지 않은<br />임의의 값이 사용됐다.<br />return f<br />종료<br />
  20. 20. 의존성 활용<br />의존성은 코드를 따라 항해하는데<br />중요한 지침<br />프로그래머들은 코드를 읽으면서<br />암묵적으로 의존성을 판단.<br />각 문장의 효과를 추정하는 것은<br />프로그램 이해의 일부.<br />
  21. 21. CodeSurfer의 의존성 추적<br />
  22. 22. 값 근원의 격리<br />제어 흐름 이해하기<br />의존성 추적<br />프로그램 슬라이싱<br />코드 악취의 연역<br />정적 분석의 한계<br />정리<br />
  23. 23. 슬라이스slice<br />프로그램의 부분집합<br />슬라이싱slicing<br />슬라이스에 관한 연산<br />
  24. 24. 전방 슬라이스forward slice<br />A에 의해 변경된 변수를 읽거나<br />수행 여부가 A에 의해 결정되는<br />모든 문장<br />후방 슬라이스backward slice<br /> B에 영향을 주는 모든 문장<br />A : 슬라이스 기준<br />B : 슬라이스 기준<br />
  25. 25. 0.진입 : fib(n)<br />1.int f<br />2.int f0 = 1<br />3.int f1 = 1<br />4.while(n &gt; 1)<br />5.n = n - 1<br />6.f = f0 + f1<br />7.f0 = f1<br />8.f1 = f<br />9.return f<br />종료<br />
  26. 26. 0.진입 : fib(n)<br />1.int f<br />2.int f0 = 1<br />3.int f1 = 1<br />4.while(n &gt; 1)<br />5.n = n - 1<br />6.f = f0 + f1<br />7.f0 = f1<br />8.f1 = f<br />9.return f<br />종료<br />
  27. 27. 슬라이싱slicing<br />절단chop<br />중추backbone<br /> 입방체dice<br />A전방 슬라이스 기준가 B후방 슬라이스 기준에<br />어떻게 영향을 주는가?<br />0.진입 : fib(n)<br />1.int f<br />2.int f0 = 1<br />3.int f1 = 1<br />f1의 초기값이 f0에 영향을 미칠 수 있는 모든 경로<br />4.while(n &gt; 1)<br />5.n = n - 1<br />6.f = f0 + f1<br />7.f0 = f1<br />8.f1 = f<br />9.return f<br />종료<br />
  28. 28. 슬라이싱slicing<br />절단chop<br />중추backbone<br />입방체dice<br />여러 값들의 계산에 기여하는<br />프로그램 부분들을 찾을 때<br />int main() {<br /> a = read();<br /> b = read();<br /> while (a &lt;= b) {<br /> a = a + 1;<br /> }<br />}<br />int main() {<br />int a, b, sum, mul;<br /> sum = 0;<br />mul = 1;<br /> a = read();<br /> b = read();<br /> while (a &lt;= b) {<br /> sum = sum + a;<br />mul = mul * a;<br /> a = a + 1;<br /> }<br /> write(sum);<br /> write(mul);<br />}<br />
  29. 29. 슬라이싱slicing<br />절단chop<br />중추backbone<br />입방체dice<br />계산된 값들 대부분이 정확하지만<br />일부만 그렇지 않을 때<br />int main() {<br />mul = 1;<br />mul = mul * a;<br /> write(mul);<br />}<br />정확한 변수의 후방 슬라이스<br />– 감염된 변수의 후방 슬라이스<br />= 감염된 값에 기여하는 문장들<br />
  30. 30. 값 근원의 격리<br />제어 흐름 이해하기<br />의존성 추적<br />프로그램 슬라이싱<br />코드 악취의 연역<br />정적 분석의 한계<br />정리<br />
  31. 31. 초기화되지 않은 변수 읽기<br />사용되지 않는 값<br />도달할 수 없는 코드<br />메모리 누수<br />인터페이스 오용<br />널 포인터<br />int go;<br />switch( color ) {<br /> case RED:<br /> case AMBER:<br /> go = 0;<br /> break;<br /> case GREEN:<br /> go = 1;<br /> break;<br />}<br />if( go ) { … }<br />color가 RED or AMBER or GREEN 이 아닐때는?<br />VS : Warning Level 4에서 default가 없다고 경고<br />
  32. 32. 초기화되지 않은 변수 읽기<br />사용되지 않는 값<br />도달할 수 없는 코드<br />메모리 누수<br />인터페이스 오용<br />널 포인터<br />int main(int argc, wchar_t* argv[])<br />{<br /> ... // argc, argv 사용되지 않음<br />return 0;<br />}<br />VS : Warning Level 4에서 참조되지 않는다고 경고<br />
  33. 33. 초기화되지 않은 변수 읽기<br />사용되지 않는 값<br />도달할 수 없는 코드<br />메모리 누수<br />인터페이스 오용<br />널 포인터<br />if( w &gt;= 0 )<br />printf(“w is non-negative ”);<br />else if( w &gt; 0 )<br />printf(“w is positive ”);<br />
  34. 34. 초기화되지 않은 변수 읽기<br />사용되지 않는 값<br />도달할 수 없는 코드<br />메모리 누수<br />인터페이스 오용<br />널 포인터<br />int* readbuf( int size )<br />{<br />int* p = malloc(size*sizeof(int));<br /> for( inti = 0; i &lt; size; ++i )<br /> {<br /> p[i] = readint();<br /> if( p[i] == 0 )<br />return 0;<br /> }<br /> return p;<br />}<br />스마트 포인터, RAII가 널 구원해줄지니…<br />
  35. 35. 초기화되지 않은 변수 읽기<br />사용되지 않는 값<br />도달할 수 없는 코드<br />메모리 누수<br />인터페이스 오용<br />널 포인터<br />void readfile()<br />{<br />intfp = open(file);<br />int size = readint(file);<br /> if (size &lt;= 0)<br />return;<br /> ...<br /> close(fp);<br />}<br />열었으면 닫아야지.<br />RAII가 널 구원해줄지니…<br />
  36. 36. 초기화되지 않은 변수 읽기<br />사용되지 않는 값<br />도달할 수 없는 코드<br />메모리 누수<br />인터페이스 오용<br />널 포인터<br />int *readbuf(int size)<br />{<br />int *p = malloc(size * sizeof(int));<br /> for (int i = 0; i &lt; size; i++) {<br /> p[i] = readint();<br /> if (p[i] == 0)<br /> return 0; // end-of-file<br /> }<br /> return p;<br />}<br />null일수도 있다.<br />
  37. 37. 정적 코드 분석 도구<br />
  38. 38. 값 근원의 격리<br />제어 흐름 이해하기<br />의존성 추적<br />프로그램 슬라이싱<br />코드 악취의 연역<br />정적 분석의 한계<br />정리<br />
  39. 39. int x;<br />for(i=j=k=1;--j||k;k=j?i%j?k:k-j:(j=i+=2));<br />write(x);<br />x가 초기화되지 않고 사용되는가?<br />홀수 완전수가 존재해야 한다.<br />존재하는지 아는 사람은 아직 없음<br />따라서, 보수적 근사에 의존해야 한다.<br />
  40. 40. 보수적 근사conservative approximation<br />배열 a[]의 임의의 변경이 그 배열 a[]의 임의의 읽기에 영향이 미칠 수 있다고 말하는것.<br />필요 이상으로 많은 자료 의존성을 만들어내겠지만 그래도 실제 자료 의존성들이 잘못 간과되는 일은 막아준다.<br />
  41. 41. 보수적 근사가 부정확한 원인들<br />간접 접근<br />변수가 실행시점에 결정된다면?<br />포인터<br />포인터가 가리키는 주소를 정확히 알 수 있는가?<br />함수<br />호출 지점에 직접 삽입하면 정확한 의존성을 알 수 있다.<br />함수가 아주 많다면 비현실적<br />객체지향<br />병렬성<br />
  42. 42. 연역 자체의 위험<br />자체가 추상화에 의한 위험을 안고 있다.<br />코드 불일치의 위험<br />정밀한 구성 관리, 버전 제어를 통한 극복<br />과도한 추상화의 위험<br />어쩔수 없지 뭐<br />부정확함의 위험<br />검증과 관찰을 혼합<br />
  43. 43. 값 근원의 격리<br />제어 흐름 이해하기<br />의존성 추적<br />프로그램 슬라이싱<br />코드 악취의 연역<br />정적 분석의 한계<br />정리<br />
  44. 44. 값 근원들을 격리하려면, 문제의 문장으로부터 의존성들을 거꾸로 추적한다.<br />의존성들을 통해서 코드 악취를 발견할 수 있다. 특히, 초기화되지 않은 변수 사용, 사용되지 않는 값들, 도달할 수 없는 코드 같은 일반적인 오류들을 찾을 수 있다.<br />디버깅을 시작하기 전에 자동적인 검출 도구(컴파일러 등)가 보고한 코드 악취를 제거할 것<br />
  45. 45. 프로그램을 슬라이싱할 때에는, 문장 S로부터 의존성을 따라가면서 다음과 같은 문장들을 모두 찾는다.<br />S에 영향을 받을 수 있는 문장들(전방 슬라이스)<br />S에 영향을 줄 수 있는 문장들(후방 슬라이스)<br />연역만을 사용한다면 코드 불일치, 관련 세부사항의 과도한 추상화, 부정확 같은 위험이 생긴다.<br />어떤 종류의 연역도 멈춤 문제에 제한을 받으며, 그러면 보수적 근사에 의지해야 한다.<br />
  46. 46. 기억에 남는 디버깅<br />부록<br />
  47. 47. 디버그 모드에서는 제대로 작동.<br />릴리즈 모드에서만 작업의 오차가 생긴다.<br />차라리 프로그램이 죽으면 좋으련만.<br />추적 결과 오류 문장을 찾음.<br />
  48. 48. float f;<br />…<br />int a = static_cast&lt;int&gt;(f);<br />
  49. 49. 왜!<br />float  int캐스팅인데<br />소수 부분fractional part 을<br />버리는 게 아니고<br />반올림을 하지???<br />
  50. 50. 컴파일러 옵션에 /QIfist가 있었다.<br />_ftol()를 사용해서 변환하는게 아니라<br />FPU(Floating-Point Unit)의<br />기본 rounding 모델인 반올림을 수행<br />
  51. 51. 끝<br />

×