Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 95. Windbg - .foreach 사용법 [링크 복사], [링크+제목 복사],
조회: 13205
글쓴 사람
정성태 (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)
11898정성태5/16/201913977VC++: 130. C++ string의 c_str과 data 함수의 차이점 [3]
11897정성태5/16/201920651오류 유형: 536. Visual Studio - "Developer Pack"을 설치했는데도 "대상 프레임워크" 목록에 나오지 않는 경우 [2]
11896정성태5/15/201915365개발 환경 구성: 440. C#, C++ - double의 Infinity, NaN 표현 방식파일 다운로드1
11895정성태5/12/201913771.NET Framework: 832. ML.NET Model Builder - 회귀(Regression), 다중 분류(Multi-class classification) 예제파일 다운로드1
11894정성태5/10/201914921VS.NET IDE: 135. Visual Studio - ML.NET Model Builder 소개 [5]
11893정성태5/10/201912521오류 유형: 535. C# 6.0 이상의 문법을 컴파일 시 오류가 발생한다면?
11892정성태5/10/201912563웹: 38. HTTP Cookie의 expires 시간 형식(RFC7231)
11891정성태5/9/201915046.NET Framework: 831. (번역글) .NET Internals Cookbook Part 12 - Memory structure, attributes, handles
11890정성태5/8/201910789개발 환경 구성: 439. "Visual Studio Enterprise is required to execute the test." 메시지와 관련된 코드 기록
11889정성태5/8/201911650개발 환경 구성: 438. mstest, QTAgent의 로그 파일 설정 방법
11888정성태5/8/201926887.NET Framework: 830. C# - 비동기 호출을 취소하는 CancellationToken의 간단한 예제 코드 [1]파일 다운로드1
11887정성태5/8/201913376.NET Framework: 829. C# - yield 문을 사용할 수 있는 메서드의 조건
11886정성태5/7/201912436오류 유형: 534. mstest.exe 실행 시 "Visual Studio Enterprise is required to execute the test." 오류 [2]
11885정성태5/7/20199398오류 유형: 533. mstest.exe 실행 시 "File extension specified '.loadtest' is not a valid test extension." 오류 발생
11884정성태5/5/201913081.NET Framework: 828. C# DLL에서 Win32 C/C++처럼 dllexport 함수를 제공하는 방법 - 두 번째 이야기
11883정성태5/3/201917402.NET Framework: 827. C# - 인터넷 시간 서버로부터 받은 시간을 윈도우에 적용하는 방법파일 다운로드1
11882정성태5/2/201914313.NET Framework: 826. (번역글) .NET Internals Cookbook Part 11 - Various C# riddles파일 다운로드1
11881정성태4/28/201914556오류 유형: 532. .NET Core 프로젝트로 마이그레이션 시 "CS0579 Duplicate 'System.Reflection.AssemblyCompanyAttribute' attribute" 오류 발생
11880정성태4/25/201911246오류 유형: 531. 이벤트 로그 오류 - Task Scheduling Error: m->NextScheduledSPRetry 1547, m->NextScheduledEvent 1547
11879정성태4/24/201916485.NET Framework: 825. (번역글) .NET Internals Cookbook Part 10 - Threads, Tasks, asynchronous code and others파일 다운로드2
11878정성태4/22/201914474.NET Framework: 824. (번역글) .NET Internals Cookbook Part 9 - Finalizers, queues, card tables and other GC stuff파일 다운로드1
11877정성태4/22/201914454.NET Framework: 823. (번역글) .NET Internals Cookbook Part 8 - C# gotchas파일 다운로드1
11876정성태4/21/201914269.NET Framework: 822. (번역글) .NET Internals Cookbook Part 7 - Word tearing, locking and others파일 다운로드1
11875정성태4/21/201914634오류 유형: 530. Visual Studo에서 .NET Core 프로젝트를 열 때 "One or more errors occurred." 오류 발생
11874정성태4/20/201914653.NET Framework: 821. (번역글) .NET Internals Cookbook Part 6 - Object internals파일 다운로드1
11873정성태4/19/201913597.NET Framework: 820. (번역글) .NET Internals Cookbook Part 5 - Methods, parameters, modifiers파일 다운로드1
... 61  62  63  64  65  66  67  68  69  [70]  71  72  73  74  75  ...