Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 95. Windbg - .foreach 사용법 [링크 복사], [링크+제목 복사],
조회: 13198
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)

Windbg - .foreach 사용법

다음의 문서에도 나오지만,

Debugger Command Program Examples
; https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-command-program-examples

Commands Output Using .foreach
; https://blogs.msdn.microsoft.com/debuggingtoolbox/2009/03/11/special-commandparsing-strings-files-and-commands-output-using-foreach/

그래도 한글 설명을 해볼까 해서 ^^ 기록으로 남깁니다.

우선, 다음의 명령어를 볼까요?

0:000> .foreach (place { s-[1]w 77000000 L?4000000 5a4d }) { dc place L8 }

명령어 구조는 이렇습니다.

.foreach ([변수] { 명령어1 }) { 명령어2 }

// 명령어1을 실행하고 그 결과를 공백/개행 기준 split 시킨 후 변수에 담아 명령어2를 루프 돌면서 실행
// 따라서,

변수 == place
명령어1 == { s-[1]w 77000000 L?4000000 5a4d }
명령어2 == { dc place L8 }

명령어1의 출력 결과는 다음과 같은 식입니다.

0x77002f96
0x77002f9e
0x77002fa6
0x77002fa8
0x77002faa
0x77002fac
0x77002fae
0x77002fb0
0x77002fb2
0x77002fb4
0x77002fb6

place는 변수, 즉 windbg의 s 명령어로 77000000에서 4000000만큼의 메모리 범위 내에 5a4d를 검색한 결과를 토큰별로 place로 변수를 받고, 이후 dc place L8에서 place 부분이 s 검색으로 찾은 토큰으로 대체되는 것입니다. C# 구문으로 변환해 보자면 다음과 같은 의미와 같습니다.

foreach (var place in s(77000000, 4000000, "5a4d"))
{
    dc place L8;
}

다른 사례를 한번 볼까요? 다음은 sos의 !dso 명령어 출력입니다.

0:000> !dso
OS Thread Id: 0x70ac (0)
ESP/REG  Object   Name
0133EE30 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EE54 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EE60 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EE88 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EEB0 035657a8 System.IO.StreamReader
0133EEB4 035657a8 System.IO.StreamReader
0133EECC 03565b2c System.IO.TextReader+SyncTextReader
0133EEE8 03565b2c System.IO.TextReader+SyncTextReader
0133EF14 03563110 System.String[]
0133EF84 03563110 System.String[]
0133F0DC 03563110 System.String[]
0133F0F8 03563110 System.String[]
0133F630 03561238 System.SharedStatics
0133F644 03561238 System.SharedStatics

여기서 우리가 관심 있는 것은 "Object" 칼럼에 있는 값들입니다. 그런데 이번엔 이전 예제와는 달리 다른 값들도 섞여 있기 때문에 다른 옵션이 필요합니다. 이를 위해 저 출력 결과를 string.Split 메서드로 공백/개행 문자로 분리했다고 가정하시면 됩니다. 그럼 처음 9개의 토큰을 건너뛰어야 합니다.

0:000> !dso
OS Thread Id: 0x70ac (0)
ESP/REG  Object   Name
0133EE30 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
...[생략]...
0133F644 03561238 System.SharedStatics

그러니까, 다음의 9개 토큰을 그냥 지나가야 합니다.

1) OS
2) Thread
3) Id:
4) 0x70ac
5) (0)
6) ESP/REG
7) Object
8) Name
9) 0133EE30

그런 다음, 원하는 토큰이 나오고 이후 다시 2개의 토큰 값들을 건너뛰는 식으로 열람할 수 있습니다.

0:000> !dso
OS Thread Id: 0x70ac (0)
ESP/REG  Object   Name
0133EE30 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EE54 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EE60 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EE88 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EEB0 035657a8 System.IO.StreamReader
0133EEB4 035657a8 System.IO.StreamReader
0133EECC 03565b2c System.IO.TextReader+SyncTextReader
0133EEE8 03565b2c System.IO.TextReader+SyncTextReader
0133EF14 03563110 System.String[]
0133EF84 03563110 System.String[]
0133F0DC 03563110 System.String[]
0133F0F8 03563110 System.String[]
0133F630 03561238 System.SharedStatics
0133F644 03561238 System.SharedStatics

이렇게 처음 토큰들을 건너뛰도록 /pS 옵션이 제공되고, 이후 반복적으로 건너뛸 토큰들을 /ps 옵션으로 제공하면 됩니다. 따라서 다음과 같이 실행하면,

.foreach /pS 9 /ps 2 ( obj { !dso } ){ .echo ${obj} }

화면에는 Object 칼럼에 해당하는 값들이 "obj" 변수에 할당되어 .echo 출력으로 전달되고 이런 출력 결과를 얻게 됩니다.

0356576c
0356576c
0356576c
0356576c
035657a8
035657a8
03565b2c
03565b2c
03563110
03563110
03563110
03563110
03561238
03561238

그렇습니다. .foreach가 제공하는 루프 문 규칙은 꽤나 원시(?)적인 수준입니다. 그래도, 확장의 여지가 있긴 한데 이를 위해 console 응용 프로그램을 이용할 수 있습니다. 가령 !dso 출력 결과에서 "System."으로 시작하는 행의 Object 칼럼만 반환받고 싶다면, 기존의 /pS, /ps 옵션으로는 불가능한데 shell의 도움을 받으면 가능합니다.

이해를 돕기 위해 !dso의 출력 결과를 test.txt 파일로 저장해 보겠습니다.

F:\temp>type test.txt
OS Thread Id: 0x70ac (0)
ESP/REG  Object   Name
0133EE30 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EE54 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EE60 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EE88 0356576c Microsoft.Win32.SafeHandles.SafeFileHandle
0133EEB0 035657a8 System.IO.StreamReader
0133EEB4 035657a8 System.IO.StreamReader
0133EECC 03565b2c System.IO.TextReader+SyncTextReader
0133EEE8 03565b2c System.IO.TextReader+SyncTextReader
0133EF14 03563110 System.String[]
0133EF84 03563110 System.String[]
0133F0DC 03563110 System.String[]
0133F0F8 03563110 System.String[]
0133F630 03561238 System.SharedStatics
0133F644 03561238 System.SharedStatics

이 출력에서 "System."으로 시작하는 행은 다음과 같이 find 명령어를 이용해서 필터링할 수 있습니다.

F:\temp>type test.txt | find "System."
0133EEB0 035657a8 System.IO.StreamReader
0133EEB4 035657a8 System.IO.StreamReader
0133EECC 03565b2c System.IO.TextReader+SyncTextReader
0133EEE8 03565b2c System.IO.TextReader+SyncTextReader
0133EF14 03563110 System.String[]
0133EF84 03563110 System.String[]
0133F0DC 03563110 System.String[]
0133F0F8 03563110 System.String[]
0133F630 03561238 System.SharedStatics
0133F644 03561238 System.SharedStatics

위의 출력 결과를 .foreach로 순회한다고 하면 우선 첫 번째 0133EEB0 항목을 건너뛰고, 035657a8 값을 읽은 후 그 이후부터는 2개의 토큰씩 건너뛰면 됩니다. 위와 같은 과정을 다음과 같은 명령어로 실행할 수 있습니다.

.foreach /pS 1 /ps 2 (obj {.shell -i - -ci "!dso" FIND "System."}){ .echo ${obj}}

명령어가 눈에 안 익을 수 있는데 정형화된 패턴이라고 보시면 됩니다. ".shell -i - -ci" 이후에 windbg 명령을 쓰고 그것의 출력 결과물을 이후의 Shell 명령어(FIND "System.")에 전달해 처리한 결과를 얻는 식입니다.

참고로 위의 명령어 수행 결과로 얻은 출력은 이렇게 됩니다.

035657a8
035657a8
03565b2c
03565b2c
03563110
03563110
03563110
03563110
03561238
03561238
Process

그런데 마지막 "Process"가 왜 나왔을까요? 그 이유는, windbg의 .shell 명령어는 지정된 명령어를 실행 후 ".shell: Process exited" 출력이 붙기 때문입니다. 따라서 {.shell -i - -ci "!dso" FIND "System."} 명령어의 수행 결과는 실제로 다음과 같이 출력되었던 것입니다.

0133EEB0 035657a8 System.IO.StreamReader
0133EEB4 035657a8 System.IO.StreamReader
0133EECC 03565b2c System.IO.TextReader+SyncTextReader
0133EEE8 03565b2c System.IO.TextReader+SyncTextReader
0133EF14 03563110 System.String[]
0133EF84 03563110 System.String[]
0133F0DC 03563110 System.String[]
0133F0F8 03563110 System.String[]
0133F630 03561238 System.SharedStatics
0133F644 03561238 System.SharedStatics
.shell: Process exited

그렇기 때문에 마지막 03561238 토큰을 읽은 후 2개의 토큰(System.SharedStatics, .shell:)을 건너뛰고 "Process"라는 값이 읽혔던 것입니다. 그래도 대개의 경우 마지막에 저렇게 예상치 않은 문자열이 붙는 것은 결과에 크게 영향이 없습니다. 예를 들어, 수행하는 명령어가 ".echo ${obj}"가 아니라 "!do ${obj}"여도 명령어에서 오류만 발생할 뿐 이미 우리가 원한 객체들의 값은 모두 출력했기 때문에 신경 쓸 필요가 없는 것입니다.


이 정도면, 대충 .foreach 사용법이 눈에 들어오시죠! ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 4/30/2018]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 61  62  63  64  65  66  67  68  69  70  71  [72]  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
11848정성태3/17/201911324스크립트: 14. 윈도우 CMD - 파일이 변경된 경우 파일명을 변경해 복사하고 싶다면?
11847정성태3/17/201915212Linux: 7. 리눅스 C/C++ - 공유 라이브러리 동적 로딩 후 export 함수 사용 방법파일 다운로드1
11846정성태3/15/201913586Linux: 6. getenv, setenv가 언어/운영체제마다 호환이 안 되는 문제
11845정성태3/15/201914305Linux: 5. Linux 응용 프로그램의 (C++) so 의존성 줄이기(ReleaseMinDependency) [3]
11844정성태3/14/201915129개발 환경 구성: 434. Visual Studio 2019 - 리눅스 프로젝트를 이용한 공유/실행(so/out) 프로그램 개발 환경 설정 [1]파일 다운로드1
11843정성태3/14/201910863기타: 75. MSDN 웹 사이트를 기본으로 영문 페이지로 열고 싶다면?
11842정성태3/13/201910271개발 환경 구성: 433. 마이크로소프트의 CoreCLR 프로파일러 예제를 Visual Studio CMake로 빌드하는 방법 [1]파일 다운로드1
11841정성태3/13/201910237VS.NET IDE: 132. Visual Studio 2019 - CMake의 컴파일러를 기본 g++에서 clang++로 변경
11840정성태3/13/201911340오류 유형: 526. 윈도우 10 Ubuntu App 환경에서는 USB 외장 하드 접근 불가
11839정성태3/12/201914114디버깅 기술: 124. .NET Core 웹 앱을 호스팅하는 Azure App Services의 프로세스 메모리 덤프 및 windbg 분석 개요 [3]
11838정성태3/7/201916865.NET Framework: 811. (번역글) .NET Internals Cookbook Part 1 - Exceptions, filters and corrupted processes [1]파일 다운로드1
11837정성태3/6/201926618기타: 74. 도서: 시작하세요! C# 7.3 프로그래밍 [10]
11836정성태3/5/201914430오류 유형: 525. Visual Studio 2019 Preview 4/RC - C# 8.0 Missing compiler required member 'System.Range..ctor' [1]
11835정성태3/5/201914192.NET Framework: 810. C# 8.0의 Index/Range 연산자를 .NET Framework에서 사용하는 방법 및 비동기 스트림의 컴파일 방법 [3]파일 다운로드1
11834정성태3/4/201913077개발 환경 구성: 432. Visual Studio 없이 최신 C# (8.0) 컴파일러를 사용하는 방법
11833정성태3/4/201913839개발 환경 구성: 431. Visual Studio 2019 - CMake를 이용한 공유/실행(so/out) 리눅스 프로젝트 설정파일 다운로드1
11832정성태3/4/201910871오류 유형: 524. Visual Studio CMake - rsync: connection unexpectedly closed
11831정성태3/4/201910458오류 유형: 523. Visual Studio 2019 - 새 창으로 뜬 윈도우를 닫을 때 비정상 종료
11830정성태2/26/201910245오류 유형: 522. 이벤트 로그 - Error opening event log file State. Log will not be processed. Return code from OpenEventLog is 87.
11829정성태2/26/201912157개발 환경 구성: 430. 마이크로소프트의 CoreCLR 프로파일러 예제 빌드 방법 - 리눅스 환경 [1]
11828정성태2/26/201918577개발 환경 구성: 429. Component Services 관리자의 RuntimeBroker 설정이 2개 있는 경우 [8]
11827정성태2/26/201912481오류 유형: 521. Visual Studio - Could not start the 'rsync' command on the remote host, please install it using your system package manager.
11826정성태2/26/201912364오류 유형: 520. 우분투에 .NET Core SDK 설치 시 패키지 의존성 오류
11825정성태2/25/201917225개발 환경 구성: 428. Visual Studio 2019 - CMake를 이용한 리눅스 빌드 환경 설정 [1]
11824정성태2/25/201912022오류 유형: 519. The SNMP Service encountered an error while accessing the registry key SYSTEM\CurrentControlSet\Services\SNMP\Parameters\TrapConfiguration. [1]
11823정성태2/21/201913511오류 유형: 518. IIS 관리 콘솔이 뜨지 않는 문제
... 61  62  63  64  65  66  67  68  69  70  71  [72]  73  74  75  ...