주식회사 누리아이티

정보자산의 보안강화를 위한 3단계 인증 보안SW(BaroPAM) 전문기업인 누리아이티

▶ Tuxedo/기술자료

truss를 사용한 프로그램 및 오류 분석

누리아이티 2012. 2. 24. 09:38

truss는 시스템 서비스 호출과 인수를 출력하고 상태, 결함 및 신호를 반환하여 프로그램이 어떻게 작동하는지 확인할 수 있는 디버깅 도구입니다. 따라서 truss를 사용하면 쉽게 오류를 디버그하고 프로그램 작동 방법을 이해할 수 있습니다.
 
이 설명은 2.X 버전을 대상으로 작성되었습니다. SunOS 4.X에는 truss와 비슷한 기능을 하고 truss와 비슷한 방법으로 사용할 수 있는 "trace" 명령이 있습니다.
 
truss는 사용 방법이 편리합니다. 가장 간단한 형식으로 명령 앞에 "truss"를 입력하면(인수 포함) 여러 가지 정보가 출력됩니다.
 
기본 설정에 따라 truss 출력은 stdout(정상 출력)가 아닌 stderr(오류 출력)로 출력됩니다. 따라서 쉘 I/O 재지정 토콘에서 쉽게 truss 출력과 프로그램 출력을 구별할 수 있습니다. 또 -o 스위치를 사용하면 truss 출력을 파일로 저장할 수 있습니다.
 
truss에 스위치를 사용하지 않으면 분기되지 않는 프로세스 출력을 추적합니다. -f 스위치를 사용하면 명령에 지정된 프로세스 뿐만 아니라 분기된 하위 프로세스까지 모두 추적합니다.
 
truss에 -p 스위치를 사용하면 실행 중인 프로세스에 대한 추적을 다시 실행하지 않고 시작할 수 있습니다. 의심스러운 프로그램이 잠시 동안 실행되고 나서 충돌이나 문제가 발생할 것 같은 표시나 상태(일종의 출력)가 나올 경우에 이 스위치를 사용하면 좋습니다. 예를 들어, 프로그램이 몇 분 동안 실행된 다음, 간략한 정보가 출력되고 비정상적으로 종료되는 경우가 있습니다. 프로그램을 실행한 다음, 일정한 정보가 출력되면 프로그램이 종료되기 전에 -p 옵션을 사용해서 truss를 실행하십시오. (명령은 "truss -p <프로세스 ID>"입니다.) 그러면 작동을 시작한 후 처음 몇 분 동안의 출력을 기록하지 않아도 truss가 프로그램을 비정상 종료시키는 원인에 대한 단서를 제공합니다.
 
-t 스위치를 사용하면 truss 출력 대상을 지정된 시스템 서비스 호출 세트로 제한할 수 있습니다. 그러면 사용자가 찾아야 할 내용이 어떤 것인지 알고 있을 경우에 출력량을 줄여서 쉽게 답을 찾을 수 있습니다.
 
1.truss를 사용하여 프로그램 실행을 디버그하는 방법.
 
프로그램이 비정상적으로 종료되지만 충돌이 발생하지 않는 경우에는 일반적으로 시스템 서비스 호출에서 오류가 반환되기 때문입니다. 이것은 프로그래밍 오류일 수도 있고 상황에 따른 오류일 수도 있습니다. truss를 사용하면 문제의 원인을 판단할 수 있습니다.
 
또 truss를 사용하면 충돌할 때까지 시스템 서비스 호출을 추적하여 프로그램이 충돌한 위치를 디버그할 수 있습니다. 이렇게 출력된 정보와 소스 코드를 사용하여 프로그램이 충돌한 위치를 판단할 수 있습니다.
 
1) 상황에 따라 오류가 발생하는 프로그램 디버깅.
 
상황에 따라 오류가 발생하는 프로그램의 예로 "No such file or directory"라는 오류가 발생하는 프로그램이 있습니다. truss는 프로그램이 실행한 open() 시스템 서비스 호출을 모두 나열하여 프로그램이 찾는 파일을 표시합니다.
 
예를 들어, 다음 명령을 실행합니다.
 
  cat junk
 
여기서 "junk"는 실제로 존재하지 않는 파일입니다. "cat" 프로그램에 대하여 truss를 실행하면 마지막 부분에 다음과 같이 표시됩니다.
 
  fstat(1, 0xEFFFF6C8)    = 0
  open("junk", O_RDONLY)    Err#2 ENOENT
  sigfillset(0xEF744060)    = 0
  cat: cannot open write(2, " c a t :   c a n n o t  ".., 17) = 17
  write(2, " j u n k", 4)    = 4
  write(2, "\n", 1)    = 1
  lseek(0, 0, SEEK_CUR)    = 56338
  _exit(2)
 
"junk" 파일을 화면에 표시하려면 먼저 이 파일을 열어야 합니다. 위의 "open" 시스템 호출은 첫 번째 인수로 "junk"를 표시하고 두 번째 인수로 O_RDONLY를 표시하여 open() 호출이 읽기 전용 모드로 "junk" 파일을 열기 위한 것임을 나타냅니다. (여기 나오는 인수에 대한 설명은 매뉴얼 페이지 단락 2의 시스템 호출 부분을 참조하십시오.) open() 호출에 대한 반환 결과는 ENOENT 또는 오류 번호 2입니다. 이 상태는 파일을 열 수 없음을 나타내는 "No such file or directory" 오류에 해당합니다. (오류 반환 코드 및 그 의미에 대한 목록은 /usr/include/sys/errno.h 파일을 참조하십시오.)  파일을 열 수 있으면 유효한 파일 기술자("=" 기호와 파일 기술자에 해당하는 번호로 표시)가 반환됩니다.
 
이벤트 순서에서 open() 호출에 실패하면 종료가 시작됩니다. 프로그램 호출 write()가 두 번 나오고 인수부터 write() 호출까지 프로그램이 오류 메시지를 인쇄하는 것을 알 수 있습니다. 다음은 cat 명령으로 파일을 열 수 없을 때 표시되는 메시지입니다.
 
   cat: cannot open junk
 
여기서 두 줄 아래에 마지막 exit()가 있습니다.
 
이 예에서 "cat" 명령은 열 수 없는 파일을 오류 메시지에 표시하는데, 이 메시지는 truss 출력의 open() 호출 인수와 관련이 있습니다. 다른 프로그램에서는 오류 메시지에 파일 이름이 표시되지 않을 수도 있지만, truss 출력의 끝부분에서 open() 호출을 보면 찾을 수 없는 파일이 어떤 파일인지 확인할 수 있습니다.
 
 
2) 프로그래밍 오류가 있는 프로그램 디버깅.
 
먼저 truss 출력에서 반환된 오류 상태를 찾아보고 나서 시스템 서비스 호출의 인수를 확인하면 프로그래밍 오류를 찾을 수 있습니다. 인수가 잘못되거나 필요없는 인수를 지정하면 시스템 서비스를 호출할 수 없습니다. 대부분의 시스템 호출은 프로그램 충돌없이 서서히 중단됩니다. 그러나 이러한 상황이 발생하면 영향을 받는 변수가 예기치 않은 상태가 되어 프로그램이 충돌할 수 있습니다. 예를 들어, 큰 메모리에 malloc() 명령을 실행하면 메모리에 대한 포인터 대신 NULL 포인터가 반환될 수 있습니다. 나중에 프로그램이 NULL 포인터에 액세스하면 프로그램이 충돌합니다. truss 출력에서 malloc() 명령으로부터 반환된 NULL과 여기에 전달된 비정상적으로 큰 인수를 보면 어떤 상황이 발생했는지 알 수 있습니다.
 
 
2.Solaris 프로그램의 작동 확인하기.
 
ps가 어떻게 작동하는지 궁금하십니까? truss를 실행하면 알 수 있습니다. 일정한 작업을 하는 프로그램에 truss를 실행하면 해당 작업을 수행하는 새로운 시스템 호출을 찾을 수 있습니다. 따라서 truss는 중요한 교육용 도구로 사용할 수 있습니다.
 
예를 들어, ps의 경우에는 다음과 같은 내용을 알 수 있습니다.
 
execve("/usr/bin/ps", 0xEFFFF910, 0xEFFFF918)  argc = 1
...
...
...
write(1, "       P I D   T T Y    ".., 26) = 26  (1)
open("/proc", O_RDONLY|O_NDELAY)  = 3  (2)
fcntl(3, F_SETFD, 0x00000001)   = 0
fstat(3, 0xEFFFF760)    = 0
getdents(3, 0x00026928, 1048)   = 972  (3)
open("/proc/00000", O_RDONLY)   = 4  (4)
ioctl(4, PIOCPSINFO, 0x00024C58)  = 0  (5)
close(4)     = 0
open("/proc/00001", O_RDONLY)   = 4
ioctl(4, PIOCPSINFO, 0x00024C58)  = 0
close(4)     = 0
...
...
...
open("/proc/01316", O_RDONLY)   = 4
ioctl(4, PIOCPSINFO, 0x00024C58)  = 0
close(4)     = 0
  1316 pts/1    0:00 ksh
write(1, "     1 3 1 6   p t s / 1".., 25) = 25  (6)
open("/proc/01317", O_RDONLY)   = 4
ioctl(4, PIOCPSINFO, 0x00024C58)  = 0
close(4)     = 0
  1317 pts/1    0:00 truss
write(1, "     1 3 1 7   p t s / 1".., 27) = 27
open("/proc/00905", O_RDONLY)   = 4
ioctl(4, PIOCPSINFO, 0x00024C58)  = 0
close(4)     = 0
...
...
...
_exit(0)
 
이 예에서 ps는 (1) 헤더를 인쇄한 다음, (2) "/proc" 디렉토리를 열고, (3) "/proc" 파일 기술자를 첫 번째 인수로 사용해서 getdents()를 호출하고, (4) "/proc" 디렉토리의 모든 파일을 열기 시작하고, (5) 파일에 ioctl을 실행하여 정보를 얻습니다. (6) 정보가 일정한 조건(이 출력으로는 판단할 수 엄만 ps 명령이 하는 작업을 알면 판단할 수 있음)에 맞으면 프로그램이 결과를 기록합니다. 이 정보가 모두 무엇을 의미할까요? getdents(), "/proc" 및 "PIOCPSINFO"에 대한 자세한 내용은 매뉴얼 페이지를 참조하십시오.
 
 
3.truss는 프로그램 문맥의 환경을 변화시킵니다.
 
truss는 프로그램의 타이밍을 변경시키기 때문에 찾고 있는 문제의 성격이 변경될 수 있습니다. truss는 시스템 호출 엔트리 포인트에서 프로그램 실행을 중단하고, 인수를 검사하고, 시스템 호출이 반환될 때까지 실행을 다시 시작하고, 반환된 상태를 읽는 동안 실행을 다시 중단하고, 다음 이벤트(결함, 신호 또는 시스템 서비스 호출)가 발생할 때까지 실행을 다시 시작하는 순서로 작동합니다. 단일 스레드의 단일 프로세스 프로그램에서는 이것이 큰 문제가 되지 않습니다. 그러나 시간 조건이나 기타 타이밍 관련 문제로 인해 문제가 발생하는 경우에는 truss를 사용하지 않는 것이 좋습니다.
 
 
4.유용한 도구.
 
truss를 사용하면 프로그램의 내부 작동을 확인할 수 있습니다. 이와 같이 truss는 프로그램을 디버그하고 프로그램의 작동을 이해하는 데 좋은 도구입니다.