Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

windbg - !threads 출력 결과로부터 닷넷 관리 스레드(System.Threading.Thread) 객체를 구하는 방법

모든 스레드를 열거하는 가장 쉬운 방법으로 !threads 명령어가 있습니다.

0:017> !threads
ThreadCount: 298
UnstartedThread: 0
BackgroundThread: 298
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
                                      PreEmptive   GC Alloc           Lock
       ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
  26    1  fd4 00117830   1808220 Enabled  406dc694:406dc6b8 07c6bf90     1 MTA (Threadpool Worker)
  44    2  dd8 001345c8      b220 Enabled  00000000:00000000 000de1d8     0 MTA (Finalizer)
  45    3  db8 03f8fc18    80a220 Enabled  00000000:00000000 000de1d8     0 MTA (Threadpool Completion Port)
  46    4  ddc 03fe6c78      1220 Enabled  00000000:00000000 000de1d8     0 Ukn
  47    5  de8 05450cd0      b220 Enabled  4ff022a0:4ff022b0 03fe7258     0 MTA
  48    6  dc8 058a38e0   200b220 Enabled  4fe61b04:4fe620d8 03fe7258     0 MTA
  49    7  a9c 07592be8   200b220 Enabled  3c477948:3c478044 03fe7258     0 MTA
  50    8  dbc 075935d8      b220 Enabled  4412dd90:4412dd90 03fe7258     0 MTA
  51    9  5b8 07592308   200b220 Enabled  00000000:00000000 03fe7258     0 MTA
  52    a 16c8 07625850    80a220 Enabled  00000000:00000000 000de1d8     0 MTA (Threadpool Completion Port)
  24    b 1794 07626358   180a220 Enabled  00000000:00000000 000de1d8     0 MTA (Threadpool Worker)
  53    c 1738 0762dfe0      b220 Enabled  1616926c:16169274 03fe7258     0 MTA
  54    d 1678 0766cc40   200b220 Enabled  00000000:00000000 03fe7258     0 MTA
  55    e 10e4 0766bd10   200b220 Enabled  00000000:00000000 03fe7258     0 MTA
  56    f   6c 0766dd68   200b220 Enabled  00000000:00000000 03fe7258     0 MTA
  57   10 1330 0766f550   180b220 Enabled  4ef9a2c0:4ef9a2c4 07c6bf90     1 MTA (Threadpool Worker)
  58   11  208 07670718   188b224 Enabled  43445c70:43445d60 07f41fc8     1 MTA (Threadpool Worker)
...[생략]...

그런데 이 정보들로부터 System.Threading.Thread 타입을 구할 수 있는 방법이 없습니다. (혹시 쉽게 구하는 방법을 아시는 분은 덧글 부탁드립니다. ^^)

물론 다소 불편하지만 우회 방법은 있습니다. 관리 힙을 전부 뒤져서 System.Threading.Thread 타입인 것을 찾는 것인데 시작은 우선 -mt에 해당하는 MethodTable 값을 찾아야 합니다. 이에 대해서는 전에도 설명했었는데,

windbg - 닷넷 메모리 덤프에서 전역 객체의 내용을 조사하는 방법
; https://www.sysnet.pe.kr/2/0/11460

위의 내용에 따라, 다음의 명령어로 가능합니다.

0:017> !name2ee mscorlib!System.Threading.Thread
Module: 02d32010 (mscorlib.dll)
Token: 0x02000154
MethodTable: 043117dc
EEClass: 02db3280
Name: System.Threading.Thread

이렇게 구한 MethodTable == 0x43117dc를 dumpheap 명령어에 전달하면, 관리 힙에 할당되어 있는 System.Threading.Thread 타입의 인스턴스를 모두 열람해 줍니다.

0:017> !DumpHeap -mt  043117dc
------------------------------
Heap 0
 Address       MT     Size
100b1c24 043117dc       56     
1019450c 043117dc       56     
101ad1b4 043117dc       56     
101ad5b4 043117dc       56     
101ad804 043117dc       56     
101ada00 043117dc       56     
10271920 043117dc       56     
10283be8 043117dc       56     
10283f3c 043117dc       56     
10284180 043117dc       56     
10284494 043117dc       56     
103334f8 043117dc       56     
10354f28 043117dc       56     
10441584 043117dc       56     
10451968 043117dc       56     
1053a824 043117dc       56     
105c7fe4 043117dc       56     
106139b0 043117dc       56     
10613d04 043117dc       56     
10613f48 043117dc       56     
1061425c 043117dc       56   
...[생략]...

출력 결과에 나온 각각의 Address 값을 덤프해 보면,

0:017> !do 100b1c24 
Name: System.Threading.Thread
MethodTable: 043117dc
EEClass: 02db3280
Size: 56(0x38) bytes
 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
05d011f4  4000640        4 ....Contexts.Context  0 instance 141c6858 m_Context
05d0fb08  4000641        8 ....ExecutionContext  0 instance 31df6ee0 m_ExecutionContext
02d3914c  4000642        c        System.String  0 instance 00000000 m_Name
04310bb4  4000643       10      System.Delegate  0 instance 00000000 m_Delegate
05f0e400  4000644       14    System.Object[][]  0 instance 1084adc0 m_ThreadStaticsBuckets
0493cc30  4000645       18       System.Int32[]  0 instance 1084addc m_ThreadStaticsBits
0431677c  4000646       1c ...ation.CultureInfo  0 instance 00000000 m_CurrentCulture
0431677c  4000647       20 ...ation.CultureInfo  0 instance 00000000 m_CurrentUICulture
02d36d5c  4000648       24        System.Object  0 instance 00000000 m_ThreadStartArg
0431698c  4000649       28        System.IntPtr  1 instance   117830 DONT_USE_InternalThread
043c9a54  400064a       2c         System.Int32  1 instance        2 m_Priority
043c9a54  400064b       30         System.Int32  1 instance        1 m_ManagedThreadId
0493e488  400064c      16c ...LocalDataStoreMgr  0   shared   static s_LocalDataStoreMgr
    >> Domain:Value  000de1d8:00000000 03fe7258:1a0b1d2c 0778f8c0:00000000 07c6bf90:00000000 07e6d3d0:00000000 07eb6318:00000000 07f41fc8:00000000 080977e0:00000000 0812b6f0:00000000 0814feb8:00000000 082a2228:1d154ca0 083bea88:00000000 2c9ba550:00000000 0835c508:00000000 2cc681a0:00000000 2d157008:00000000 2d84be60:00000000 2d8b07b8:00000000 2d9d1c10:00000000 2da783e0:00000000 2daf2008:00000000 2db19ff0:00000000 08359a40:00000000 2dc1eea8:1476a408 2dd59f48:00000000 2ddfa9b0:00000000 2cc617e8:00000000 2df14d18:00000000 <<
02d36d5c  400064d      170        System.Object  0   shared   static s_SyncObject
    >> Domain:Value  000de1d8:100b1c18 03fe7258:100b5334 0778f8c0:140b2f24 07c6bf90:141c91c4 07e6d3d0:190b422c 07eb6318:142ec160 07f41fc8:143e1aec 080977e0:101b45b4 0812b6f0:1028a610 0814feb8:1e0b4510 082a2228:10377148 083bea88:1c0e63b8 2c9ba550:1047b448 0835c508:10552bcc 2cc681a0:18116530 2d157008:14380520 2d84be60:18219410 2d8b07b8:1069b6f8 2d9d1c10:182f6058 2da783e0:107c3890 2daf2008:12109898 2db19ff0:10625e0c 08359a40:1713b278 2dc1eea8:1c24aa14 2dd59f48:1a19071c 2ddfa9b0:1d1c87ac 2cc617e8:1f1b7e34 2df14d18:1d2e5a0c <<

DONT_USE_InternalThread 값이 !threads 목록에 있는 것과 같다는 것을 알 수 있습니다.

그런데... 스레드가 한두 개라면 모를까, 일반적인 프로그램에서 그 많은 스레드의 객체를 일일이 저렇게 뒤져보는 것도 일입니다. 따라서 이런 경우에는 pykd의 도움을 받아 스크립트로 만들어 두면 됩니다. 관련 스크립트의 힌트는 다음에서 이미 설명했었습니다.

windbg - 풀 덤프에서 .NET 스레드의 상태를 알아내는 방법
; https://www.sysnet.pe.kr/2/0/11269

따라서 이 글에 저 스크립트를 적용하면 다음과 같이 모든 Thread 타입의 관련 값을 열거할 수 있습니다.

0:006> .foreach ($t {!dumpheap -mt 043117dc -short}) {  .printf " Thread Obj ${$t} and the Thread Id is %N \n",poi(${$t}+4c) }

또는 pykd를 이용하면 좀 더 구체적으로 결과를 뽑아낼 수 있습니다.

windbg에서 python 스크립트 실행하는 방법 - pykd
; https://www.sysnet.pe.kr/2/0/11227

즉, 다음과 같이 thread ID를 기준으로 System.Threading.Thread 객체를 구할 수 있습니다.

from pykd import *

findId = "7670718"

def getItem(text, key):
    for line in text.splitlines():
        if key in line:
            result = line.split(":")[1].strip()
    return result

def GetManagedId(text):
    for line in text.splitlines():
        items = line.split()
        if len(items) >= 7 and items[7] == "DONT_USE_InternalThread":
            return items[6]

    return "NOT FOUND"

outputText = pykd.dbgCommand("!name2ee mscorlib!System.Threading.Thread")

mtThread = getItem(outputText, "MethodTable:")

outputText = pykd.dbgCommand("!DumpHeap -mt " + mtThread)

for line in outputText.splitlines():
    items = line.split()
    if len(items) == 3:
        threadAddress = items[0].strip()
        threadText = pykd.dbgCommand("!do " + threadAddress)

        managedId = GetManagedId(threadText)

        if managedId != "NOT FOUND":
            if managedId == findId:
                dprintln(threadText)
                break

dprintln ("END")

그런데... 시간이 좀 오래 걸린다는 흠이 있긴 합니다. ^^ 참고로, 위의 명령어를 응용하면 Managed Thread Id 값을 알고 있는 상태에서 그것의 System.Threading.Thread 인스턴스를 구하는 것도 가능합니다.

from pykd import *

findId = "17"

def getItem(text, key):
    for line in text.splitlines():
        if key in line:
            result = line.split(":")[1].strip()
    return result

def GetManagedId(text):
    for line in text.splitlines():
        items = line.split()
        if len(items) >= 7 and items[7] == "m_ManagedThreadId":
            return items[6]

    return "NOT FOUND"

outputText = pykd.dbgCommand("!name2ee mscorlib!System.Threading.Thread")

mtThread = getItem(outputText, "MethodTable:")

outputText = pykd.dbgCommand("!DumpHeap -mt " + mtThread)

for line in outputText.splitlines():
    items = line.split()
    if len(items) == 3:
        threadAddress = items[0].strip()
        threadText = pykd.dbgCommand("!do " + threadAddress)

        managedId = GetManagedId(threadText)

        if managedId != "NOT FOUND":
            if managedId == findId:
                dprintln(threadText)
                break

dprintln ("END")




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







[최초 등록일: ]
[최종 수정일: 4/9/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)
12107정성태1/10/202010699.NET Framework: 877. C# - 프로세스의 모든 핸들을 열람 - 두 번째 이야기
12106정성태1/8/202012125VC++: 136. C++ - OSR Driver Loader와 같은 Legacy 커널 드라이버 설치 프로그램 제작 [1]
12105정성태1/8/202010783디버깅 기술: 153. C# - PEB를 조작해 로드된 DLL을 숨기는 방법
12104정성태1/7/202011471DDK: 9. 커널 메모리를 읽고 쓰는 NT Legacy driver와 C# 클라이언트 프로그램 [4]
12103정성태1/7/202014179DDK: 8. Visual Studio 2019 + WDK Legacy Driver 제작- Hello World 예제 [1]파일 다운로드2
12102정성태1/6/202011812디버깅 기술: 152. User 권한(Ring 3)의 프로그램에서 _ETHREAD 주소(및 커널 메모리를 읽을 수 있다면 _EPROCESS 주소) 구하는 방법
12101정성태1/5/202011145.NET Framework: 876. C# - PEB(Process Environment Block)를 통해 로드된 모듈 목록 열람
12100정성태1/3/20209162.NET Framework: 875. .NET 3.5 이하에서 IntPtr.Add 사용
12099정성태1/3/202011474디버깅 기술: 151. Windows 10 - Process Explorer로 확인한 Handle 정보를 windbg에서 조회 [1]
12098정성태1/2/202011064.NET Framework: 874. C# - 커널 구조체의 Offset 값을 하드 코딩하지 않고 사용하는 방법 [3]
12097정성태1/2/20209627디버깅 기술: 150. windbg - Wow64, x86, x64에서의 커널 구조체(예: TEB) 구조체 확인
12096정성태12/30/201911634디버깅 기술: 149. C# - DbgEng.dll을 이용한 간단한 디버거 제작 [1]
12095정성태12/27/201913045VC++: 135. C++ - string_view의 동작 방식
12094정성태12/26/201911195.NET Framework: 873. C# - 코드를 통해 PDB 심벌 파일 다운로드 방법
12093정성태12/26/201911204.NET Framework: 872. C# - 로딩된 Native DLL의 export 함수 목록 출력파일 다운로드1
12092정성태12/25/201910619디버깅 기술: 148. cdb.exe를 이용해 (ntdll.dll 등에 정의된) 커널 구조체 출력하는 방법
12091정성태12/25/201912134디버깅 기술: 147. pdb 파일을 다운로드하기 위한 symchk.exe 실행에 필요한 최소 파일 [1]
12090정성태12/24/201910827.NET Framework: 871. .NET AnyCPU로 빌드된 PE 헤더의 로딩 전/후 차이점 [1]파일 다운로드1
12089정성태12/23/201911519디버깅 기술: 146. gflags와 _CrtIsMemoryBlock을 이용한 Heap 메모리 손상 여부 체크
12088정성태12/23/201910500Linux: 28. Linux - 윈도우의 "Run as different user" 기능을 shell에서 실행하는 방법
12087정성태12/21/201910927디버깅 기술: 145. windbg/sos - Dictionary의 entries 배열 내용을 모두 덤프하는 방법 (do_hashtable.py) [1]
12086정성태12/20/201912997디버깅 기술: 144. windbg - Marshal.FreeHGlobal에서 발생한 덤프 분석 사례
12085정성태12/20/201910728오류 유형: 586. iisreset - The data is invalid. (2147942413, 8007000d) 오류 발생 - 두 번째 이야기 [1]
12084정성태12/19/201911378디버깅 기술: 143. windbg/sos - Hashtable의 buckets 배열 내용을 모두 덤프하는 방법 (do_hashtable.py) [1]
12083정성태12/17/201912624Linux: 27. linux - lldb를 이용한 .NET Core 응용 프로그램의 메모리 덤프 분석 방법 [2]
12082정성태12/17/201912474오류 유형: 585. lsof: WARNING: can't stat() fuse.gvfsd-fuse file system
... [61]  62  63  64  65  66  67  68  69  70  71  72  73  74  75  ...