Practice/Debugging

버젼 관리 시스템을 사용하여 문제를 해결하기

초반에는 문제의 원인을 충분히 넓게 잡아라."에서 중요한 디버깅 기법 하나를 간단하게 언급하고 넘어갔다.

분석의 과정이 없이 추론에 근거하여 새로 추가된 원격 기능과 캐싱 알고리즘만을 의심했다.
코드를 수정해도 상태가 개선되지 않았다.
... 중략 ...
이전 버젼과 차이점을 면밀하게 비교하면서 비로서 문제의 원인을 발견했다.
같은 환경과 같은 기능(즉, 로컬 파일 읽기)에서도 새 버젼에서 동일한 문제가 일어났다.
즉, 새로운 기능이 동작할 때 발생하는 것이 아니란 의미이다.
로깅 시스템을 리펙토링을 했는데, 이와 연관이 깊을 것으로 추정했고 다양한 테스트로 이를 검증했다.

- 2014/07/15 - [Practice/Debugging] - 초반에는 문제의 원인을 충분히 넓게 잡아라.


버젼 콘트롤 시스템(VCS)[각주:1]를 잘 활용하는 기법을 말하지 않았다.
가장 기본은 문제가 처음 발생된 변경 이력을 찾는 것이다.


버젼 별로 문제 상황을 재현하면서 범위를 좁혀나간다.
버젼 23에서 재현되었다면, 버젼 22, 버젼 21 로 차차 올라가면서 발생하지 않는 버젼을 찾는다.
버젼 19에서 문제가 나타나지 않았다면, 버젼 19와 20를 비교 분석해본다.


문제가 되는 버젼의 범위를 대강 파악할 수 있다면 시간을 많이 단축할 수 있다.
문제를 둘러싼 환경(적용 시기나 장비 등)을 잘 분석하면 많은 도움이 된다.
버그가 발생하기 시작한 시기를 조사하면, 문제의 코드(또는 환경 변화)가 발생한 때는 대략 파악할 수 있다.
장비에 설치된 버젼이 다르고 장비마다 오류 상황이 다르다면 문제가 되는 구간이 많이 좁혀진다.


커밋 로그를 잘 작성했다면 이를 최대한 활용한다.
문제가 될만한 변경 이력을 참고하면 원인을 빨리 추측할 수 있다.
원인으로 지목된 커밋 로그의 앞/뒤 버젼에서 문제가 재현되는지 확인한다.[각주:2]


  1. SCM(Source Code Management)이라고도 불린다. [본문으로]
  2. "<a href="http://unipro.tistory.com/156" target="_blank">현상을 분석하여 문제의 원인을 찾아내라</a>"에서 언급했듯이 문제를 재현하여 원인을 확실하게 증명해야 한다. [본문으로]
저작자 표시 변경 금지
신고
Practice/VLC

VLC 미디어 재생의 데이타 흐름 다이어그램

아래는 VLC로 미디어 파일을 재생할 경우 구성되는 요소와 데이터의 흐름을 나타낸 다이어그램이다.
read는 받는 쪽에서 데이터를 가져오는 방식이고
send/play는 보내는 쪽에서 밀어내는 방식이다.
thread간에는 fifo(queue)로 이용하는 데이터를 전달한다.



VLC의 객체 간의 구성을 데이타 흐름과 같이 구성해보면 다음과 같다.
실선은 객체의 부모/자식의 관계,
두꺼운 점선(···)은 데이타의 흐름과 관련된 구성(연결관계),
점선(---)은 객체의 일부 구성(포함관계)을 나타낸다.



2013/06/17 - [Practice/FFmpeg] - FFmpeg 데이타 흐름 다이어그램


저작자 표시 변경 금지
신고

'Practice > VLC' 카테고리의 다른 글

VLC 미디어 재생의 데이타 흐름 다이어그램  (0) 2016.12.16
Practice/Linux

쉘스크립트에서 빈 문자열 검사 방법

쉘스크립트에서 빈 문자열의 검사할 때, 'test' 명령어와 '-z' 옵션을 사용한다.

test -z string

string이 빈 문자열이면 참을 반환한다.
'test'와 동일한 기능의 다른 표현으로 '['가 있다.[각주:1]


그런데, 오래된 고수들이 작성한 많은 쉘스크립트를 보면 아래와 같이 사용하는 것을 심심치 않게 본다.

if [ x$1 == x ]; then
    echo $1
fi

단순하게 빈 문자열과 비교하거나

if [ $1 == "" ]; then
    echo $1
fi

'-z' 옵션-빈 문자열을 검사하는 옵션-을 사용하지 않은 이유가 뭘까?

if [ -z $1 ]; then
    echo $1
fi


'-z' 옵션의 경우, $1이 "-n", "-p" 등처럼 옵션 문자열처럼 들어올 경우 문제가 될 수 있다.
예를 들어, $1이 문자열 "-n" 일 경우, 'test -z $1'이 'test -z -n'으로 처리될 수 있는데,
옵션에 따라서 옛날 버젼의 test에서는 문제가 될 수 있다.
반면, 'x$1 == x'의 방법은 'x-n == x'로 해석되어 문제를 일으키지 않는다.


'[ $1 == "" ]' 로 비교하는 경우, $1이 빈 문자열이면, [ == "" ]으로 평가되어 실행된다.
'=='의 왼쪽 피연산자가 존재하지 않음으로, "==: unexpected operator"와 같은 오류를 발생한다.
반면, 'x$1 == x'의 방법은 'x == x'로 평가됨으로 이러한 문제에서 자유롭다.


Bash를 대상으로 작성한다면, 이런 문제에서 좀 더 자유로운 확장 검사 명령어인 '[['를 사용하는 것을 추천한다.
여기서 예로 든 어떤 방법을 사용하다고 하더라고 문제가 되지 않는다.
확장 검사 명령어에 대한 것은 아래 참고 자료 링크를 참고한다.


참고 자료:
KLDP 포럼: bash스크립트에서 빈문자열 체크 방법
확장 검사 명령어


  1. 사실 '['는 마치 문법처럼 보이나, 실은 'ls', 'rm'과 같은 명령어이다. <pre class="brush: plain">$ ls -l /usr/bin/[ -rwxr-xr-x 1 root root 51920 Feb 18 2016 /usr/bin/[* </pre> '['는 끝에 오는 ']'를 연산의 내용으로 취급하지 않는다. [본문으로]
저작자 표시 변경 금지
신고
Practice/Lisp

Common Lisp으로 구현한 트리(Tree)를 중위 순회(In-order Traversal)하는 반복자(Iterator)

사소해 보이는 연산 뒤에 숨어있는 것 은 인턴 전화 면접에서 깨달은 내용을 담고 있다.
이 글은 인턴 면접을 소재로 하는데,
면접의 내용 중에 트리(Tree)를 중위 순회(Inorder Traversal)하는 반복자를 구현하는 것이 있다.
이 글의 필자는 C++[각주:1]로 구현하였다.


평소 업무에서 Common Lisp을 쓸 기회가 없어서 자꾸 잊어버린다.
도구를 갈고 닦지 않으면 녹슬기 마련이다.
간단하게 트리의 중위 순회를 Common Lisp으로 작성해봤다.


순회 순서


  1. 왼쪽의 하위 트리를 순회한다.
  2. 자신(중간)
  3. 오른쪽의 하위 트리를 순회한다.


순회할 첫번째 노드 구하기 : begin

  • 최상위 노드의 가장 왼쪽에 있는 노드가 첫번째 노드이다.
    구현은 왼쪽 하위 노드를 재귀적으로 찾는다.
(defun begin (node)
  (if left-node
    (begin left-node)
    node))


다음 방문할 노드 얻기 : next

  • 순회 순서에 의해서 자신 이후에는 오른쪽 하위 트리를 순회한다.
    따라서, 다음 노드는 오른쪽 하위 트리의 가장 왼쪽의 노드이다.
    (begin (right node))
  • 오른쪽 하위 트리가 없다면, 이 단계에서는 더 이상 방문할 곳이 없다.
    가장 가까운 상위 단계의 중간 노드를 찾는다.[각주:2]
    (mid node)
(defun next (node)
  (if right-node
    (begin right-node)
    (mid node)))


다음에 순회할 중간 노드 찾기 : mid

  • 오른쪽(순서 3)에서 올라왔다면, 이 단계에서는 더 이상 방문할 곳이 없다.
    (if (eq node (right parent-node))
      ;; ...
      )
    다시, 가장 가까운 상위 단계의 중간 노드를 찾는다.
    (mid mid-node)
  • 아니라면, 왼쪽(순서 1)에서 올라왔다.
    순회 순서에 따라서 중간 노드를 반환한다.
    mid-node
(defun mid (node)
  (when mid-node
    (if (eq node (right mid-node))
      (mid mid-node)
      mid-node)))


최종 코드

(defun begin (node)
  (let ((left-node (left node)))
    (if left-node
      (begin left-node)
      node)))

(defun next (node)
  (when node
    (labels ((mid (node)
               (let ((mid-node (parent node)))
                 (when mid-node
                   (if (eq node (right mid-node))
                     (mid mid-node)
                     mid-node)))))
      (let ((right-node (right node)))
        (if right-node
          (begin right-node)
          (mid node))))))


2016/01/13 - [Practice/Lisp] - 암호 문자열 감추기 문제 풀이 : Common Lisp, Scheme

2013/08/20 - [Theory/Data Structure] - GLib의 GTree로 알아보는 균형 이진 트리 알고리즘


  1. C라고도 볼 수 있는데, 필자가 연산자 오버로딩을 언급해서 C++로 보았다. [본문으로]
  2. 상위 단계로 올라갈 때, <ul style="list-style-type: disc;"> <li>(순서 1)이라면, 중간 노드를 찾는 것이고,</li> <li>(순서 3)이라면, 다시 상위 단계로 올라가서 이를 반복하게 된다.</li> </ul> 결과는 <ul style="list-style-type: disc;"> <li>순회하지 않은 <b>중간 노드</b>를 찾거나</li> <li><b>중간 노드</b>를 못찾거나 (왜? 모두 방문했으니까)</li> </ul> 둘 중에 하나이다.<br /> 위 과정은 mid 함수 구현에서 다시 설명한다. [본문으로]
저작자 표시 변경 금지
신고
Practice/Lisp

Lisp에서 클로저(closures)

클로저란 함수가 lexical[각주:1] 환경(environment)의 변수를 참조하는 것을 말한다.


예를 들면, 아래 코드를 살펴보자.

(defparameter *plus-10* (let ((x 10)) (lambda (y) (+ x y))))

X와 Y를 더하는 함수 객체를 만들고, *PLUS-10*이 이를 가르키게 한다.


Lexical 변수 X는 LET 구문 블럭이 생성한 환경에 갇혀서 외부에서는 접근할 수 없다.
LET 구문 블럭이 종료되었다는 것은 실행 흐름이 LET이 생성한 환경에서 벗어났다는 것을 의미한다.
그럼에도 불구하고, *PLUS-10*를 호출하면 X 변수를 참조할 수 있다.

CL-USER> (funcall *plus-10* 20)
30


함수 객체가 다른 곳(예제에서는 dynamic 환경의 *PLUS-10*)에서 참조되고 있으면,
이 함수 객체가 속한 환경 역시 사라지지 않는다.
변수(예제에서는 X)를 만나면 자신이 속한 환경을 거슬러 올라가며 찾는다.


함수의 호출 과정에서도 lexical 변수를 생성한다.
함수의 환경을 생성하고, 이 환경에 메인 함수의 인자를 생성하여, 전달받은 값을 참조하게 한다.


이것을 응용하면 좀 더 재미있는 함수를 만들어볼 수 있다.

(defun plus-x (x) (lambda (y) (+ x y)))

PLUS-X 함수는 고정된 어떤 값을 더하는 함수를 자유자재로 만들 수 있다.[각주:2]
15를 더하는 함수를 다음과 같이 만들 수 있다.

(defparameter *plus-15* (plus-x 15)


이와 같이 lexical 환경에서 정의된 함수가 lexical 변수를 참조하는 것을 클로저라고 한다.


2016/02/18 - [Practice/Lisp] - Lisp에서 lexical과 dynamic 변수 타입


  1. lexical이 무엇인지는 <a href="http://unipro.tistory.com/190" target="_blank">Lisp에서 lexical과 dynamic 변수 타입</a>를 참고하라. [본문으로]
  2. curring과 비슷한 개념이라고 생각하면 되겠다. [본문으로]
저작자 표시 변경 금지
신고
 [ 1 ]  [ 2 ]  [ 3 ]  [ 4 ]  [ 5 ]  [ 6 ]  [ 7 ]  [ ··· ]  [ 36 ] 

알림

이 블로그는 구글에서 제공한 크롬에 최적화 되어있고, 네이버에서 제공한 나눔글꼴이 적용되어 있습니다.

카운터

Today : 12
Yesterday : 192
Total : 178,923