Create TOC

레이블이 debugging인 게시물을 표시합니다. 모든 게시물 표시
레이블이 debugging인 게시물을 표시합니다. 모든 게시물 표시

2006년 4월 25일

Debugging/Map파일 해석

Visual C++ 설정을 제대로 맞추었다면 빌드시 map파일이 생성된다.

최악의 경우 map파일만을 사용해서 문제 원인을 파악해야 하는데 map파일 해석 방법을 알아두면 유용하다.

예제 소스

우선 아래와 같은 프로그램이 있다고 하면

// HelloMap.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

const char *HELLO = "Hello, World";

void foo()
{
	for (int i = 0; i < 10; i++)
	{
		printf("%d\n", i);
	}
}

void bar(int x)
{
	char *buf = const_cast<char *>(HELLO);

	buf[0] = 'K';

	for (int i = 0; i < x; i++)
	{
		printf("%d\n", i);
	}

	printf("%s", buf);

	foo();
}

int main(int argc, char* argv[])
{
	printf("%s!\n", HELLO);
	bar(20);
	return 0;
}

예제에서 생성된 map

생성된 map파일의 예는 아래와 같다.

 HelloMap

 Timestamp is 444e24fb (Tue Apr 25 22:32:43 2006)

 Preferred load address is 00400000

 Start         Length     Name                   Class
 0001:00000000 00000232H .text                   CODE
 0002:00000000 00000044H .idata$5                DATA
 0002:00000048 00000054H .rdata                  DATA
 0002:0000009c 00000014H .idata$2                DATA
 0002:000000b0 00000014H .idata$3                DATA
 0002:000000c4 00000044H .idata$4                DATA
 0002:00000108 000000eeH .idata$6                DATA
 0002:000001f6 00000000H .edata                  DATA
 0003:00000000 00000004H .CRT$XCA                DATA
 0003:00000004 00000004H .CRT$XCZ                DATA
 0003:00000008 00000004H .CRT$XIA                DATA
 0003:0000000c 00000004H .CRT$XIZ                DATA
 0003:00000010 00000010H .data                   DATA
 0003:00000020 0000001cH .bss                    DATA

  Address         Publics by Value              Rva+Base     Lib:Object

 0001:00000000       ?foo@@YAXXZ                00401000 f   HelloMap.obj
 0001:00000019       ?bar@@YAXH@Z               00401019 f   HelloMap.obj
 0001:00000057       _main                      00401057 f   HelloMap.obj
 0001:00000076       _printf                    00401076 f   MSVCRT:MSVCRT.dll
 0001:0000007c       _mainCRTStartup            0040107c f   MSVCRT:crtexe.obj
 0001:0000018c       __exit                     0040118c f   MSVCRT:MSVCRT.dll
 0001:00000192       __XcptFilter               00401192 f   MSVCRT:MSVCRT.dll
 0001:00000198       _exit                      00401198 f   MSVCRT:MSVCRT.dll
 0001:0000019e       ___p___initenv             0040119e f   MSVCRT:MSVCRT.dll
 0001:000001a4       ___getmainargs             004011a4 f   MSVCRT:MSVCRT.dll
 0001:000001aa       __initterm                 004011aa f   MSVCRT:MSVCRT.dll
 0001:000001b0       __setdefaultprecision      004011b0 f   MSVCRT:fp8.obj
 0001:000001c2       ___setusermatherr          004011c2 f   MSVCRT:MSVCRT.dll
 0001:000001c8       __matherr                  004011c8 f   MSVCRT:merr.obj
 0001:000001cb       __setargv                  004011cb f   MSVCRT:dllargv.obj
 0001:000001cc       ___p__commode              004011cc f   MSVCRT:MSVCRT.dll
 0001:000001d2       ___p__fmode                004011d2 f   MSVCRT:MSVCRT.dll
 0001:000001d8       __onexit                   004011d8 f   MSVCRT:atonexit.obj
 0001:00000204       _atexit                    00401204 f   MSVCRT:atonexit.obj
 0001:00000216       ___set_app_type            00401216 f   MSVCRT:MSVCRT.dll
 0001:00000220       __except_handler3          00401220 f   MSVCRT:MSVCRT.dll
 0001:00000226       __controlfp                00401226 f   MSVCRT:MSVCRT.dll
 0001:0000022c       ___dllonexit               0040122c f   MSVCRT:MSVCRT.dll
 0002:00000000       __imp__printf              00402000     MSVCRT:MSVCRT.dll
 0002:00000004       __imp___exit               00402004     MSVCRT:MSVCRT.dll
 0002:00000008       __imp___XcptFilter         00402008     MSVCRT:MSVCRT.dll
 0002:0000000c       __imp__exit                0040200c     MSVCRT:MSVCRT.dll
 0002:00000010       __imp____p___initenv       00402010     MSVCRT:MSVCRT.dll
 0002:00000014       __imp____getmainargs       00402014     MSVCRT:MSVCRT.dll
 0002:00000018       __imp___initterm           00402018     MSVCRT:MSVCRT.dll
 0002:0000001c       __imp____setusermatherr    0040201c     MSVCRT:MSVCRT.dll
 0002:00000020       __imp___adjust_fdiv        00402020     MSVCRT:MSVCRT.dll
 0002:00000024       __imp____p__commode        00402024     MSVCRT:MSVCRT.dll
 0002:00000028       __imp____p__fmode          00402028     MSVCRT:MSVCRT.dll
 0002:0000002c       __imp____set_app_type      0040202c     MSVCRT:MSVCRT.dll
 0002:00000030       __imp___except_handler3    00402030     MSVCRT:MSVCRT.dll
 0002:00000034       __imp___controlfp          00402034     MSVCRT:MSVCRT.dll
 0002:00000038       __imp____dllonexit         00402038     MSVCRT:MSVCRT.dll
 0002:0000003c       __imp___onexit             0040203c     MSVCRT:MSVCRT.dll
 0002:00000040       \177MSVCRT_NULL_THUNK_DATA 00402040     MSVCRT:MSVCRT.dll
 0002:0000006c       ??_C@_0N@PLDO@Hello?0?5World?$AA@ 0040206c     HelloMap.obj
 0002:0000007c       ??_C@_03HMFC@?$CFd?6?$AA@  0040207c     HelloMap.obj
 0002:00000080       ??_C@_02DILL@?$CFs?$AA@    00402080     HelloMap.obj
 0002:00000084       ??_C@_04MIBI@?$CFs?$CB?6?$AA@ 00402084     HelloMap.obj
 0002:0000009c       __IMPORT_DESCRIPTOR_MSVCRT 0040209c     MSVCRT:MSVCRT.dll
 0002:000000b0       __NULL_IMPORT_DESCRIPTOR   004020b0     MSVCRT:MSVCRT.dll
 0003:00000000       ___xc_a                    00403000     MSVCRT:cinitexe.obj
 0003:00000004       ___xc_z                    00403004     MSVCRT:cinitexe.obj
 0003:00000008       ___xi_a                    00403008     MSVCRT:cinitexe.obj
 0003:0000000c       ___xi_z                    0040300c     MSVCRT:cinitexe.obj
 0003:00000010       ?HELLO@@3PBDB              00403010     HelloMap.obj
 0003:00000014       ___defaultmatherr          00403014     MSVCRT:merr.obj
 0003:00000020       __dowildcard               00403020     MSVCRT:wildcard.obj
 0003:00000024       __newmode                  00403024     MSVCRT:_newmode.obj
 0003:00000028       __commode                  00403028     MSVCRT:xncommod.obj
 0003:0000002c       __fmode                    0040302c     MSVCRT:xtxtmode.obj
 0003:00000030       ___onexitend               00403030     <common>
 0003:00000034       ___onexitbegin             00403034     <common>
 0003:00000038       __adjust_fdiv              00403038     <common>

 entry point at        0001:0000007c

 Static symbols


Line numbers for .\Release\HelloMap.obj(D:\MyData\My Projectz\SDK\HelloMap\HelloMap.cpp) segment .text

     9 0001:00000000    12 0001:00000003    14 0001:00000018    17 0001:00000019
    24 0001:00000033    27 0001:00000044    29 0001:0000004e    30 0001:00000056
    33 0001:00000057    35 0001:00000068    36 0001:00000072    37 0001:00000074

map 해석

crash 주소 찾기

이 프로그램을 실행하면 그대로 crash가 일어난다.

프로그램이 crash가 일어났을 경우 crash가 발생한 주소를 얻을 수 있다. 주소를 확인해보면 아래와 같다.

crash가 발생한 함수 찾기

map파일에서 0x40102e 보다 주소가 뒤에 있는(rvs+base가 큰) 함수를 찾는다. _main이다.

그렇다면 에러는 바로 전 함수인 ?bar@@YAXH@Z 함수에서 발생되었다. 우선 함수 이름을 알아보자. Visual C++에 있는 UNDNAME.exe 프로그램을 사용한다.

C:\Program Files\Microsoft Visual Studio\Common\Tools>undname ?bar@@YAXH@Z
Microsoft(R) Windows NT(R) Operating System
UNDNAME Version 5.00.1768.1Copyright (C) Microsoft Corp. 1981-1998

>> ?bar@@YAXH@Z == bar

crash가 발생한 함수는 bar 함수이다.

source와 line 번호 찾기

이제 line 번호를 찾자. map 파일에서 line 번호는 아래와 같은 형식으로 되어있다.

9 0001:00000000

맨 앞이 line 번호, 뒤가 offset 정보다. 오류가 발생한 주소로 offset 정보를 계산하면 아래와 같다.

offset = 오류주소 - preferred load address - 0x1000

이같은 식으로 계산하면 40102e - 400000 - 1000 = 2e 가 되며 2e가 offset이 된다.

bar함수의 object 이름은 HelloMap.obj 이므로 눈치상 HelloMap.cpp에 속한 함수라는것을 알 수 있다.

map 파일의 HelloMap.cpp line 정보에 나와있는 offset 중에서 2e근처의 값을 찾는다. 여기서 주의할점은 작은 값을 찾아야 한다.

17 0001:00000019

19가 가장 근접한 값이다. 즉 bar 함수는 HelloMap.cpp의 19번째 줄부터 시작한다.

With WinDbg

WinDbg를 이용해 프로그램을 실행해보자.

esi가 가리키는 주소에 값을 쓰려다 AV가 발생했다. esi에 어떤 값이 있는지 register를 확인해보자.

esi는 언제 설정되었는지 보기 위해서 어셈코드를 보자.

esi에 00403010 값이 그대로 대입 된다. 혹시 map파일에 뭔가 있는지 찾아보자

?HELLO@@3PBDB 라는 이름이 붙어있다. undname으로 실제 이름을 알아내면

C:\Program Files\Microsoft Visual Studio\Common\Tools>UNDNAME.EXE ?HELLO@@3PBDB
Microsoft(R) Windows NT(R) Operating System
UNDNAME Version 5.00.1768.1Copyright (C) Microsoft Corp. 1981-1998

>> ?HELLO@@3PBDB == HELLO

HELLO라는 함수가 있나? 그런데 HELLO의 Address 부분이 아까 bar 함수와 다르게 0003 으로 시작한다. HELLO의 Address인 0003:00000010 이 어느 section 인지 map파일 처음을 보면 HELLO는 .data 영역에 있다. 전역변수가 된다.

오류가 발생한 부분의 어셈을 보면 {{|mov byte ptr , 0x4b|}}, 즉 HELLO 는 전역변수이면서 포인터 변수라고 유추할 수 있다. 원래는 memory에서 0x00403010 에 저장된 4byte를 읽어와야 하지만 windbg가 그 작업까지 해줬다.

결국 0x0040206c 에 어떤 값을 저장하려다 AV가 발생했다. HELLO와 같은 방법으로 map 파일을 뒤지면 ??_C@_0N@PLDO@Hello?0?5World?$AA@ 이런 녀석이 나오고 unname을 사용해보면 string 이라고만 나온다. 즉 0040206c에 있는 값은 string literal 이라고 유추할 수 있다.

결론을 내리면 bar 함수 시작 부분에 전역 변수 HELLO가 가리키는 주소에 어떤 값을 쓰려고 하는 코드가 존재하며 HELLO가 가리키는 주소는 string literal 이기 때문에 AV가 발생한 것으로 보인다.

Debugging/VC6 설정

좀더 편한 디버깅을 위해서는 Release Build 에도 몇가지 옵션을 조정해주는 것이 좋다.

Compiler 옵션

Compiler 옵션은 Project - Settings - C/C++ 탭에서 Project Settings에 넣는다.

옵션설명
/W4경고 레벨을 최대로
/WX경고를 에러로.
/ZiPDB 생성을 위해서 넣는다.
/GFstring literal을 read only 상태로 만든다. 이 옵션 없이 빌드하면 string literal을 수정할 수 있다.

Link옵션

Linker 옵션은 Project - Settings - Link 탭에서 Project Settings에 넣는다.

옵션설명
/release /debugpdb 생성을 위해 넣는다. /debug 만 있으면 생성하지만 /release 옵션이 없으면 WinDbg에서 symbol을 맞출 때 checksum 경고를 보여주기 때문에 시간이 오래 걸린다.
/pdbtype:conpdb를 하나로 만들어준다.
/map:map파일명map파일을 생성한다. 보통 map파일이름은 <project이름>.map 이다.
/mapinfo:EXPORTSexport symbol을 표시한다.
/mapinfo:LINESline 정보를 포함한다.

2005년 12월 27일

WinDbg/Userdump

다운로드

WinDbg/DrWatson32

  1. drwtsn32 -i
  2. drwtsn32을 실행하고 아래처럼 설정

덤프 파일은 아래 경로에 저장된다.(기본값)

C:\Documents and Settings\All Users\Application Data\Microsoft\Dr Watson\user.dmp

WinDbg/Kernel Memory Dump 설정

내 컴퓨터에서

속성 -> 고급 -3 시작 및 복구 의 설정 화면에서 아래 그림처럼 설정 후 재부팅하면 된다.

WinDbg

Symbol setting

OS 심볼 설정

.symfix c:\symbol\winsym

기타 심볼

적당한 폴더에 pdb를 넣고 File-Symbol File Path 메뉴에서 추가로 등록해준다.

기본 명령

  • k : for stack
  • ~ : list all thread
  • ~ <tid> : changes to specific thread number
  • ~*kb : lists the stacks for all threads in a process
  • ~<tid>kb : list the stack for <tid> thread
  • g : go
  • ln <address> : address로부터 근접한 함수를 찾아줌. symbol 필요. stack dump를 떠서 손으로 call stack을 추척할때 필요하다.
  • dump /f <filename> : create memory dump
  • lm : list module. see .hh lm
  • lmvm 모듈명 : 모듈에 대한 정보를 얻는다.
  • .hh <command> : help
  • dt 구조체명 [주소] : 구조체의 실제 구성내용을 보여준다. [주소] 가 생략되면 prototype을, [주소] 가 주어지면 구조체의 멤버변수 값을 보여준다.

덤프 분석

커널 덤프 경우

기본 OS 설정에서는 BSOD 발생시 풀 덤프를 만들어주지 않는다. BSOD를 분석하기 위해서는 반드시 풀 덤프가 생기도록 Kernel Dump 설정 해줘야 한다.

  1. ''!analyze -v'' 명령을 통해서 나온 오류 코드를 도움말에서 찾아서 분석한다.
  2. !processs 이녀석이 관련된 process.

그 외에 Kernel Memory Space Analyzer도 도움이 된다.

User 덤프 경우

Dr.Watson32UserDumpUserDump는 업데이트가 되지 않기 때문에 SysInternals의 procdump를 사용하는 것이 더 좋다를 사용해서 풀 덤프를 받아온다.

Interactive Debugging

example) 임의의 모듈에 break 걸기

  1. process attach
  2. break
  3. x user32!Message* : 현재 attach한 process가 user32 를 load 하고 있을때 user32의 Message 로 시작하는 함수 목록을 출력
  4. bp <주소> 또는 bp 모듈명!함수이름 : 위에서 원하는 함수에 대한 주소를 얻어서 break. break point 목록은 bl, 지울때는 bc
  5. bu <module>!<function> : module이 load되기 전에 breakpoint를 설정하고 싶을 때 사용한다.
  6. g

원격 디버깅

문제가 발생하는 PC와 개발자가 사용하는 PC에 각각 WinDbg를 설치하고 문제가 되는 PC에서 아래와 같이 WinDbg를 실행한다.

windbg.exe -server tcp:port=사용할포트

그 뒤에 개발자 PC에서 아래와 같이 입력하면 개발자 PC에서 원격으로 디버깅을 할 수 있다.

windbg.exe -remote tcp:server=WinDbg서버IP,port=포트

Call stack 해석

WinDbg의 call stack에서는 parameter를 3개까지밖에 표시해 주지 않는다. 추가적인 parameter는 clientEBP 값을 가지고 계산하면 된다.

  • 첫번째 인자는 EBP + 0x8
  • 두번째 인자는 EBP + 0xc

이런식으로 주소를 4씩 증가시키면서 보면 된다.

메모리 특정 영역 값이 변경될때 break 걸기

ba w4 address

유용한 링크

2002년 3월 8일

Debugging/Note

Debugging?

  • Debugger를 이용하여 문제를 해결하는 과정
    • Debugging 분류
    • Kernel mode debugging
      • Drivers, File Systems, Operation System components
      • 주로 BOD 발생
      • SoftIce나 WinDbg 원격 디버깅 추천
    • User Mode debugging
      • Crash, Hang, Leak,...
  • Debugging 방법
    • Dump debugging - dump file 분석해서 원인 찾기.
    • Interactive debugging - process 에 debugger 를 attach해서 실시간 디버깅.

보통 생각할 수 있는 문제 종류

  • Crash - 대부분은 AV(Access Violation), 젤 잡기 쉬움
  • Hang - CPU 100%, dead lock, I/O pending 등등
  • Leak - memory, resource 등등
  • 그외 논리 오류 등등

Debugging을 위해서 필요한것?

  • Debugger
  • symbol(보통 PDB)
  • source code

위에서부터 순차적으로 중요함. 심볼만 있어도 무지 편함. Release build 시에도 debug symbol을 생성하게 해두는것이 중요!!! 하다못해 map 파일이라도 만들것. map 파일이라도 있으면 address 로 부터 함수명을 찾을 수 있음.

Microsoft Debugging Tools

high cpu issue

  1. PerfMon 등으로 범인 thead 찾을것.
  2. userdump 들 이용해서 덤프떠서 확인하던지, debugger를 붙여서 해당 쓰레드를 조사할 것.

memory leak issue

Q268343 Umdhtools.exe: How to Use Umdh.exe to Find Memory Leaks

  1. PerfMon 으로 진짜 memory leak 인지 확인한다.
  2. GFlags 옵션을 조정한다.
  3. 재부팅
  4. DH -p <pid> -b -g -f dumpA.txt
  5. DH -p <pid> -b -g -f dumpB.txt
  6. 위 4, 5 식으로 여러개 뜬 다음
dhcmp dumpA.txt dumpB.txt result.txt

이런식으로 각 메모리 덤프를 비교해서 어딘지 찾아본다.

그외

Link

Q196755 HOWTO: Determine the Location of a Crash

VC 6.0을 사용할 때 유용한 매크로

C-runtime library를 msvcrt로 하고 release 에서도 PDB 만들고 모든 빌드에서 map 파일 만들게 한다.

Sub ProjectSettingStandard()
	'DESCRIPTION: 프로젝트 환경을 만든다.
	Dim proj
	Dim confs
	Dim config
	Dim release_map
	Dim debug_map
	Dim debug_name

	Set proj = ActiveProject
	Set confs = proj.Configurations

	' map 파일 이름을 만든다.
	release_map = "/map:" + Chr(34) + "Release/"+ proj.Name + ".map" + Chr(34)
	debug_map = "/map:" + Chr(34) + "Debug/"+ proj.Name + ".map" + Chr(34)

	' release, debug를 판단할 이름 생성.
	debug_name = proj.Name + " - Win32 Debug"

	' Application.Project.Configurations.Configuration.AddToolSettings "mfc", "2"
	' None : 0(FOR SDK), MFC Static Libraries : 1, MFC Shared Dll : 2

	If proj.Type = "Build" Then
		For i = 1 To confs.Count
			Set config = confs(i)
			' Common Settings

			' include path add
			config.AddToolSettings "cl.exe", "/I " + Chr(34) + "../inc" + Chr(34)

			' lib path add
			config.AddToolSettings "link.exe",  "/libpath:" + Chr(34) + "../lib" + Chr(34)

			' remove compiler options
			config.RemoveToolSettings "cl.exe", "/O2"
			config.RemoveToolSettings "cl.exe", "/ZI"

			' add compiler options
			config.AddToolSettings "cl.exe", "/Od /Gm /Zi"

			' add linker options
			config.AddToolSettings "link.exe", "/debug /OPT:REF"

			If config.Name = debug_name Then
				' Debug 일때
				config.AddToolSettings "cl.exe", "/MDd"
				config.AddToolSettings "link.exe", debug_map
			Else
				' Release 일때
				config.AddToolSettings "cl.exe", "/MD"
				config.AddToolSettings "link.exe", release_map
			End If
		Next
	End If
End Sub

알아두면 유용한 macro(C/C++)

  • __FILE__ : 현재 파일(VC, GCC)
  • __LINE__ : 현재 라인(VC, GCC)
  • __FUNCTION__ : 현재 함수(VC 에서 되는지 잘 모름. GCC 에서는 문제 없음)
  • NDEBUG : 릴리즈 빌드(VC, GCC)

2000년 5월 26일

gdb

조건

gdb로 디버깅하기 위해서는 gcc에 -g 옵션을 주고 컴파일해야 한다.

실행

gdb <실행파일명>

종료

quit

변수값 확인

print 변수명

Break/Watchpoints

breakpoint 설정

간단 설정

break <줄번호|함수명>

조건부 설정

break <줄번호> if <조건>

이미 설정된 breakpoint에 조건을 설정하려면 아래와 같이 하면 된다.

condition <break 번호> <조건>

breakpoint 목록 보기

info break

watchpoint 설정

watch <변수명|조건>

한줄씩 실행

한줄씩 실행. 함수 호출시 함수 내부로 들어가지 않는다.

next or n

한줄씩 실행. 함수 호출시 함수 내부로 들어간다.

step or s

계속 실행

continue

Stack trace

stack 출력

bt

출력 형식은 아래와 같다.

#frame번호 주소 in 함수명(인자) at 파일명:줄번호

예)

(gdb) bt
#0  0x80483ea in sum (a=8, b=8) at main.c:7
#1  0x8048435 in main (argc=1, argv=0xbffff9c4) at main.c:21

frame 이동

frame <frame번호>
up or down

지역변수 확인

info locals

예) backtrace 에서 특정 stack의 지역변수를 확인할때?

(gdb) frame 1
(gdb) info locals

Attach Process

gdb <실행파일명> <pid>

Palm/POSE와 gdb를 이용한 디버깅

prc-tools를 사용하면 m68k-palm-gdb를 사용해서 디버깅을 해야 한다.

디버깅을 위한 빌드

-g 옵션을 이용해 빌드한다.

# m68k-palmos-gcc -g -c hello.c -o hello.o
# m68k-palmos-gcc -g hello.o -o hello

이렇게 빌드하면 hello.prchello가 생성된다.

POSE 실행

POSE를 실행한다.

gdb 실행 및 설정

아래와 같이 m68k-palm-gdb를 실행한다.

# m68k-palmos-gdb hello
GNU gdb 5.3
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Thpe "show copyping" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
this GDB was configured as "--host=i686-pc-cygwin --target=m68k-palmos"...
(gdb)

gdb prompt 에서 아래와 같이 입력한다.

(gdb) target pilot localhost:6414
Remote debugging under PalmOS using localhost:6414
Waiting... (Press Ctrl-C to connect to halted machine)

디버깅

위 단계를 모두 거쳤으면 POSE에 hello.prc 를 설치하고 실행한다. hello.prc를 실행하는 순간 gdb에 아래와 같은 화면이 나온다.

Program received signal SIGSTOP, Stopped (signal).
0x00045888 in PilotMain (cmd=3, cmdPBP=0xa2260000, launchFlags=68) at hello.c:20
20 {
(gdb)

이제 디버깅을 하면 된다.