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

비밀번호

댓글 작성자
 




... 91  [92]  93  94  95  96  97  98  99  100  101  102  103  104  105  ...
NoWriterDateCnt.TitleFile(s)
11635정성태8/1/201818673오류 유형: 472. C# 컴파일 오류 - Your project is not referencing the ".NETFramework,Version=v3.5" framework.
11634정성태8/1/201821619.NET Framework: 790. .NET Thread 상태가 Cooperative일 때 GC hang 현상 재현 방법파일 다운로드1
11633정성태7/29/201825545Graphics: 15. Unity - shader의 World matrix(unity_ObjectToWorld)를 수작업으로 구성 [2]파일 다운로드1
11632정성태7/28/201827878Graphics: 14. C# - Unity에서 캐릭터가 바라보는 방향을 기준으로 카메라의 위치 이동 및 회전하는 방법
11631정성태7/27/201829830Graphics: 13. Unity로 실습하는 Shader (9) - 투명 배경이 있는 텍스처 입히기 [1]
11630정성태7/27/201825003개발 환경 구성: 391. (GitHub 등과 직접 연동해) 소스 코드 디버깅을 쉽게 해 주는 SourceLink [3]
11629정성태7/26/201823777.NET Framework: 789. C# 컴파일 옵션 - Check for arithmetic overflow/underflow [2]
11628정성태7/25/201825629Graphics: 12. Unity로 실습하는 Shader (8) - 다중 패스(Multi-Pass Shader)
11627정성태7/25/201820019개발 환경 구성: 390. C# - 컴파일러 옵션 OSS signing / Public Signing
11626정성태7/25/201818389오류 유형: 471. .C++ 함수를 const로 바꾼 경우 C2440 컴파일 오류가 발생한다면?
11625정성태7/24/201817592Math: 49. GeoGebra 기하 (25) - 타원의 중심점 찾기파일 다운로드1
11624정성태7/24/201822006개발 환경 구성: 389. C# - 재현 가능한 빌드(reproducible builds) == Deterministic builds [4]
11623정성태7/24/201821429Math: 48. C# - 가우시안 함수의 이산형(discrete) 커널 값 생성파일 다운로드1
11622정성태7/23/201821600개발 환경 구성: 388. Windows 환경에서 Octave 패키지 설치하는 방법
11621정성태7/23/201819215VC++: 127. 멤버 함수에 대한 포인터를 외부에서 호출하는 방법파일 다운로드1
11620정성태7/22/201822473Graphics: 11. Unity로 실습하는 Shader (7) - Blur (평균값, 가우스, 중간값) 필터 [1]파일 다운로드1
11619정성태7/21/201821506Graphics: 10. Unity로 실습하는 Shader (6) - Mosaic Shading
11618정성태7/20/201818573개발 환경 구성: 387. 삼성 오디세이(Odyssey) 노트북의 운영체제를 새로 설치하는 방법
11617정성태7/20/201819377Team Foundation Server: 50. TFS 소스 코드 관리 기능 (5) - "Rollback", "Rollback Entire Changeset"
11616정성태7/17/201818734Graphics: 9. Unity Shader - 전역 변수의 초기화
11615정성태7/17/201823086.NET Framework: 788. RawInput을 이용한 키보드/마우스 입력 모니터링파일 다운로드1
11614정성태7/17/201825292Graphics: 8. Unity Shader - Texture의 UV 좌표에 대응하는 Pixel 좌표
11613정성태7/16/201821602Graphics: 7. Unity로 실습하는 Shader (5) - Flat Shading
11612정성태7/16/201820585Windows: 148. Windows - Raw Input의 Top level collection 의미
11611정성태7/15/201820802Graphics: 6. Unity로 실습하는 Shader (4) - 퐁 셰이딩(phong shading)
11610정성태7/15/201817338Graphics: 5. Unity로 실습하는 Shader (3) - 고로 셰이딩(gouraud shading) + 퐁 모델(Phong model) + Texture
... 91  [92]  93  94  95  96  97  98  99  100  101  102  103  104  105  ...