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

비밀번호

댓글 작성자
 




1  2  3  4  5  6  7  8  [9]  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13397정성태7/23/20233639닷넷: 2134. C# - 문자열 연결 시 string.Create를 이용한 GC 할당 최소화
13396정성태7/22/20233336스크립트: 54. 파이썬 pystack 소개 - 메모리 덤프로부터 콜 스택 열거
13395정성태7/20/20233307개발 환경 구성: 685. 로컬에서 개발 중인 ASP.NET Core/5+ 웹 사이트에 대해 localhost 이외의 호스트 이름으로 접근하는 방법
13394정성태7/16/20233254오류 유형: 873. Oracle.ManagedDataAccess.Client - 쿼리 수행 시 System.InvalidOperationException
13393정성태7/16/20233417닷넷: 2133. C# - Oracle 데이터베이스의 Sleep 쿼리 실행하는 방법
13392정성태7/16/20233288오류 유형: 872. Oracle - ORA-01031: insufficient privileges
13391정성태7/14/20233367닷넷: 2132. C# - sealed 클래스의 메서드를 callback 호출했을 때 인라인 처리가 될까요?
13390정성태7/12/20233338스크립트: 53. 파이썬 - localhost 호출 시의 hang 현상
13389정성태7/5/20233322개발 환경 구성: 684. IIS Express로 호스팅하는 웹을 WSL 환경에서 접근하는 방법
13388정성태7/3/20233511오류 유형: 871. 윈도우 탐색기에서 열리지 않는 zip 파일 - The Compressed (zipped) Folder '[...].zip' is invalid. [1]파일 다운로드1
13387정성태6/28/20233531오류 유형: 870. _mysql - Commands out of sync; you can't run this command now
13386정성태6/27/20233601Linux: 61. docker - 원격 제어를 위한 TCP 바인딩 추가
13385정성태6/27/20233809Linux: 60. Linux - 외부에서의 접속을 허용하기 위한 TCP 포트 여는 방법
13384정성태6/26/20233563.NET Framework: 2131. C# - Source Generator로 해결하는 enum 박싱 문제파일 다운로드1
13383정성태6/26/20233312개발 환경 구성: 683. GPU 런타임을 사용하는 Colab 노트북 설정
13382정성태6/25/20233356.NET Framework: 2130. C# - Win32 API를 이용한 윈도우 계정 정보 (예: 마지막 로그온 시간)파일 다운로드1
13381정성태6/25/20233740오류 유형: 869. Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
13380정성태6/24/20233195스크립트: 52. 파이썬 3.x에서의 동적 함수 추가
13379정성태6/23/20233206스크립트: 51. 파이썬 2.x에서의 동적 함수 추가
13378정성태6/22/20233093오류 유형: 868. docker - build 시 "CANCELED ..." 뜨는 문제
13377정성태6/22/20236872오류 유형: 867. 파이썬 mysqlclient 2.2.x 설치 시 "Specify MYSQLCLIENT_CFLAGS and MYSQLCLIENT_LDFLAGS env vars manually" 오류
13376정성태6/21/20233282.NET Framework: 2129. C# - Polly를 이용한 클라이언트 측의 요청 재시도파일 다운로드1
13375정성태6/20/20232980스크립트: 50. Transformers (신경망 언어모델 라이브러리) 강좌 - 2장 코드 실행 결과
13374정성태6/20/20233110오류 유형: 866. 파이썬 - <class 'AttributeError'> module 'flask.json' has no attribute 'JSONEncoder'
13373정성태6/19/20234398오류 유형: 865. 파이썬 - pymssql 설치 관련 오류 정리
13372정성태6/15/20233109개발 환경 구성: 682. SQL Server TLS 통신을 위해 사용되는 키 길이 확인 방법
1  2  3  4  5  6  7  8  [9]  10  11  12  13  14  15  ...