Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 95. Windbg - .foreach 사용법 [링크 복사], [링크+제목 복사],
조회: 20307
글쓴 사람
정성태 (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

비밀번호

댓글 작성자
 




... 151  152  153  154  155  156  [157]  158  159  160  161  162  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
1124정성태9/17/201126419.NET Framework: 240. System.Collections.ArrayList가 .NET 4.5에서 지원이 안된다??? [2]
1123정성태9/17/201165266Windows: 53. 2가지 모드의 Internet Explorer 10과 ActiveX [6]
1122정성태9/16/201132929Windows: 52. 새롭게 지원되는 WinRT 응용 프로그램 [7]
1121정성태9/12/201127743Java: 5. WTP 내에서 서블릿을 실행하는 환경
1120정성태9/11/201127665.NET Framework: 239. IHttpHandler.IsReusable 속성 이야기파일 다운로드1
1119정성태9/11/201126758Java: 4. 이클립스에 WTP SDK가 설치되지 않는다면? [2]
1118정성태9/11/201138418Java: 3. 이클립스에서 서블릿 디버깅하는 방법 [4]
1117정성태9/9/201125669제니퍼 .NET: 17. 제니퍼 닷넷 적용 사례 (2) - 웹 애플리케이션 hang의 원인을 알려주다.
1116정성태9/8/201156724Java: 2. 자바에서 "Microsoft SQL Server JDBC Driver" 사용하는 방법
1115정성태9/4/201130271Java: 1. 닷넷 개발자가 처음 실습해 본 서블릿
1114정성태9/4/201134751Math: 2. "Zhang Suen 알고리즘(세선화, Thinning/Skeletonization)"의 C# 버전 [4]파일 다운로드1
1113정성태9/2/201134352개발 환경 구성: 129. Hyper-V에 CentOS 설치하기
1112정성태9/2/201151097Linux: 1. 리눅스 <-> 윈도우 원격 접속 프로그램 사용 [3]
1111정성태8/29/201125463제니퍼 .NET: 16. 적용 사례 (1) - DB Connection Pooling을 사용하지 않았을 때의 성능 저하를 알려주다. [1]
1110정성태8/26/201126847오류 유형: 136. RDP 접속이 불연속적으로 끊기는 문제
1109정성태8/26/201129722오류 유형: 135. 어느 순간 Active Directory 접속이 안되는 문제
1108정성태8/22/201131176오류 유형: 134. OLE/COM Object Viewer - DllRegisterServer in IVIEWERS.DLL failed. [1]
1107정성태8/21/201129020디버깅 기술: 43. Windows Form의 Load 이벤트에서 발생하는 예외가 Visual Studio에서 잡히지 않는 문제
1106정성태8/20/201127302웹: 26. FailedRequestTracing 설정으로 인한 iisexpress.exe 비정상 종료 문제
1105정성태8/19/201127245.NET Framework: 238. Web Site Model 프로젝트에서 Trace.WriteLine 출력이 dbgview.exe에서 확인이 안 되는 문제파일 다운로드1
1104정성태8/19/201127458웹: 25. WebDev보다 IIS Express가 더 나은 점 - 다중 가상 디렉터리 매핑 [1]
1103정성태8/19/201133361오류 유형: 133. WCF 포트 바인딩 실패 오류 - TCP error(10013) [1]
1102정성태8/19/201131057Math: 1. 방탈출3 - Room 10의 '중복가능한 조합' 문제를 위한 C# 프로그래밍 [2]파일 다운로드1
1101정성태8/19/201129705.NET Framework: 237. WCF AJAX 서비스와 JavaScript 간의 DateTime 연동 [1]파일 다운로드1
1100정성태8/17/201128821.NET Framework: 236. SqlDbType - DateTime, DateTime2, DateTimeOffset의 차이점파일 다운로드1
1099정성태8/15/201128260오류 유형: 132. 어느 순간 갑자기 접속이 안 되는 TFS 서버
... 151  152  153  154  155  156  [157]  158  159  160  161  162  163  164  165  ...