• [技巧]如何获得某个callstack所在线程的线程号?


    检查dump文件的时候, 我们经常会使用下面的命令来获得所有线程上的调用栈

    ~*e !clrstack

    输出结果举例如下:

    OS Thread Id: 0x1b30 (15)
    Child-SP         RetAddr          Call Site
    0000000034a9ed00 000006424e611fc0 System.Threading.WaitHandle.WaitOne(Int64, Boolean)
    0000000034a9ed40 00000642782f174b System.DirectoryServices.Protocols.PartialResultsRetriever.ThreadRoutine()
    0000000034a9ed90 000006427838956d System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
    0000000034a9ede0 000006427f602322 System.Threading.ThreadHelper.ThreadStart()
    OS Thread Id: 0x10fc (16)
    Unable to walk the managed stack. The current thread is likely not a
    managed thread. You can run !threads to get a list of managed threads in
    the process
    OS Thread Id: 0x26f8 (17)
    Child-SP         RetAddr          Call Site
    00000000431bf060 0000064274a56477 System.Threading.WaitHandle.WaitAny(System.Threading.WaitHandle[], Int32, Boolean)
    00000000431bf0c0 00000642782f174b System.Net.TimerThread.ThreadProc()
    00000000431bf190 000006427838956d System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
    00000000431bf1e0 000006427f602322 System.Threading.ThreadHelper.ThreadStart()

    在这个结果输出里, 可以很轻易地看出线程号15, 16, 17.

    然而, 当检查非托管调用栈的时候, 这个线程号却很难看到. 命令:

    ~*e k

    输出片段如下:

    Child-SP          RetAddr           Call Site
    00000000`34f1f6d8 00000000`77efb416 ntdll!ZwWaitForSingleObject+0xa [d:\nt\base\…\usrstubs.asm @ 180]
    00000000`34f1f6e0 00000000`77efb480 ntdll!RtlpWaitOnCriticalSection+0x240 [d:\nt\….c @ 617]
    00000000`34f1f760 00000000`35f7da11 ntdll!RtlEnterCriticalSection+0xa9 [d:\nt\…\resource.c @ 876]
    00000000`34f1f790 00000000`35f7dc8b ONETUTIL!COWSHeap::RunLeakDetectionInternal+0x281
    00000000`34f1f7e0 00000000`35f7e081 ONETUTIL!COWSMemmgr::AcquireLocalHeap+0x13
    00000000`34f1f820 00000000`3561c500 ONETUTIL!COWSMemmgr::InitMemmgrForThread+0x4d
    00000000`34f1f880 00000000`3000670d OWSSVR!GetExtensionVersion+0x5b14
    00000000`34f1f900 00000000`35febb0d OWSTIMER!VTimerProcessRequest+0xa1 [d:\….cpp @ 225]
    00000000`34f1fc30 00000000`35fece4e ONETUTIL!COWSThreadWithHeap::WalkHeap+0x1fd
    00000000`34f1fc80 00000000`781337d7 ONETUTIL!COWSThreadWithHeap::Uninitialize+0x97e
    00000000`34f1ff20 00000000`78133894 msvcr80!_callthreadstartex+0x17 [f:\dd\vctools\crt_…\crt\src\threadex.c @ 348]
    00000000`34f1ff50 00000000`77d6b71a msvcr80!_threadstartex+0x84 [f:\dd\vctools…\crt\src\threadex.c @ 326]
    00000000`34f1ff80 00000000`00000000 kernel32!BaseThreadStart+0x3a [d:\nt\…\support.c @ 795]
    Child-SP          RetAddr           Call Site
    00000000`35c1f6d8 00000000`77efb416 ntdll!ZwWaitForSingleObject+0xa [d:\nt\base\…\usrstubs.asm @ 180]
    00000000`35c1f6e0 00000000`77efb480 ntdll!RtlpWaitOnCriticalSection+0x240 [d:\nt\….c @ 617]
    00000000`35c1f760 00000000`35f7da11 ntdll!RtlEnterCriticalSection+0xa9 [d:\nt\base….c @ 876]
    00000000`35c1f790 00000000`35f7dc8b ONETUTIL!COWSHeap::RunLeakDetectionInternal+0x281
    00000000`35c1f7e0 00000000`35f7e081 ONETUTIL!COWSMemmgr::AcquireLocalHeap+0x13
    00000000`35c1f820 00000000`3561c500 ONETUTIL!COWSMemmgr::InitMemmgrForThread+0x4d
    00000000`35c1f880 00000000`3000670d OWSSVR!GetExtensionVersion+0x5b14
    00000000`35c1f900 00000000`35febb0d OWSTIMER!VTimerProcessRequest+0xa1 [d:\….cpp @ 225]
    00000000`35c1fc30 00000000`35fece4e ONETUTIL!COWSThreadWithHeap::WalkHeap+0x1fd
    00000000`35c1fc80 00000000`781337d7 ONETUTIL!COWSThreadWithHeap::Uninitialize+0x97e
    00000000`35c1ff20 00000000`78133894 msvcr80!_callthreadstartex+0x17 [f:\dd…\crt…threadex.c @ 348]
    00000000`35c1ff50 00000000`77d6b71a msvcr80!_threadstartex+0x84 [f:\dd\…\crt\src\t…adex.c @ 326]
    00000000`35c1ff80 00000000`00000000 kernel32!BaseThreadStart+0x3a [d:\…base\…support.c @ 795]

    那么如何才能得到这个这两个调用栈的线程号码呢?

    答案的命令如下:

    ~*e !teb;k

    这个命令的意思是遍历所有的线程, 先输出线程的TEB(Thread Environment Block)数据结构, 再输出调用栈.

    根据Mark E. Russinvoich的Windows Internals 5th Edition的P378页的讲解, TEB结构中是存有Thread ID这个信息的, 位置在Active RPC handle的正上方. 书中截图如下:

    image

    让我们检查一下在我们的WinDBG中的输出结果吧.

    TEB at 000007ffffe66000
        ExceptionList:        0000000000000000
        StackBase:            0000000034f20000
        StackLimit:           0000000034f1c000
        SubSystemTib:         0000000000000000
        FiberData:            0000000000001e00
        ArbitraryUserPointer: 0000000000000000
        Self:                 000007ffffe66000
        EnvironmentPointer:   0000000000000000
        ClientId:             0000000000000490 . 00000000000023e8
        RpcHandle:            0000000000000000
        Tls Storage:          0000000000000000
        PEB Address:          000007fffffde000
        LastErrorValue:       0
        LastStatusValue:      c0150008
        Count Owned Locks:    0
        HardErrorMode:        0
    Child-SP          RetAddr           Call Site
    00000000`34f1f6d8 00000000`77efb416 ntdll!ZwWaitForSingleObject+0xa [d:\nt\….asm @ 180]
    00000000`34f1f6e0 00000000`77efb480 ntdll!RtlpWaitOnCriticalSection+0x240 [d:\nt\base….c @ 617]
    00000000`34f1f760 00000000`35f7da11 ntdll!RtlEnterCriticalSection+0xa9 [d:\nt\base\….c @ 876]
    00000000`34f1f790 00000000`35f7dc8b ONETUTIL!COWSHeap::RunLeakDetectionInternal+0x281
    00000000`34f1f7e0 00000000`35f7e081 ONETUTIL!COWSMemmgr::AcquireLocalHeap+0x13
    00000000`34f1f820 00000000`3561c500 ONETUTIL!COWSMemmgr::InitMemmgrForThread+0x4d
    00000000`34f1f880 00000000`3000670d OWSSVR!GetExtensionVersion+0x5b14
    00000000`34f1f900 00000000`35febb0d OWSTIMER!VTimerProcessRequest+0xa1 [d:\….cpp @ 225]
    00000000`34f1fc30 00000000`35fece4e ONETUTIL!COWSThreadWithHeap::WalkHeap+0x1fd
    00000000`34f1fc80 00000000`781337d7 ONETUTIL!COWSThreadWithHeap::Uninitialize+0x97e
    00000000`34f1ff20 00000000`78133894 msvcr80!_callthreadstartex+0x17 [f:\dd\….c @ 348]
    00000000`34f1ff50 00000000`77d6b71a msvcr80!_threadstartex+0x84 [f:\dd\…\crt\src….c @ 326]
    00000000`34f1ff80 00000000`00000000 kernel32!BaseThreadStart+0x3a [d:\nt\base\……c @ 795]
    TEB at 000007ffffe64000
        ExceptionList:        0000000000000000
        StackBase:            0000000035c20000
        StackLimit:           0000000035c1c000
        SubSystemTib:         0000000000000000
        FiberData:            0000000000001e00
        ArbitraryUserPointer: 0000000000000000
        Self:                 000007ffffe64000
        EnvironmentPointer:   0000000000000000
        ClientId:             0000000000000490 . 000000000000107c
        RpcHandle:            0000000000000000
        Tls Storage:          0000000000000000
        PEB Address:          000007fffffde000
        LastErrorValue:       0
        LastStatusValue:      c0150008
        Count Owned Locks:    0
        HardErrorMode:        0
    Child-SP          RetAddr           Call Site
    00000000`35c1f6d8 00000000`77efb416 ntdll!ZwWaitForSingleObject+0xa [d:\….asm @ 180]
    00000000`35c1f6e0 00000000`77efb480 ntdll!RtlpWaitOnCriticalSection+0x240 [d:\nt\…\reso….c @ 617]
    00000000`35c1f760 00000000`35f7da11 ntdll!RtlEnterCriticalSection+0xa9 [d:\nt\…\ntdll\…rce.c @ 876]
    00000000`35c1f790 00000000`35f7dc8b ONETUTIL!COWSHeap::RunLeakDetectionInternal+0x281
    00000000`35c1f7e0 00000000`35f7e081 ONETUTIL!COWSMemmgr::AcquireLocalHeap+0x13
    00000000`35c1f820 00000000`3561c500 ONETUTIL!COWSMemmgr::InitMemmgrForThread+0x4d
    00000000`35c1f880 00000000`3000670d OWSSVR!GetExtensionVersion+0x5b14
    00000000`35c1f900 00000000`35febb0d OWSTIMER!VTimerProcessRequest+0xa1 [d:\…\timerecb…@ 225]
    00000000`35c1fc30 00000000`35fece4e ONETUTIL!COWSThreadWithHeap::WalkHeap+0x1fd
    00000000`35c1fc80 00000000`781337d7 ONETUTIL!COWSThreadWithHeap::Uninitialize+0x97e
    00000000`35c1ff20 00000000`78133894 msvcr80!_callthreadstartex+0x17 [f:\dd\vctools\…\crt\src\t…ex.c @ 348]
    00000000`35c1ff50 00000000`77d6b71a msvcr80!_threadstartex+0x84 [f:\dd\vctools\crt_bld\…\crt\src\threadex.c @ 326]
    00000000`35c1ff80 00000000`00000000 kernel32!BaseThreadStart+0x3a [d:\nt\…\…c @ 795]

    这里的ClientId就应该是线程号了, 长得很奇怪是么? 呵呵, 让我们来找到它们的数字号码吧.

    0000000000000490 . 00000000000023e8 = 490.23e8

    0000000000000490 . 000000000000107c = 490.107c

    输入命令

    0:039> ~
       0  Id: 490.16ec Suspend: 1 Teb: 000007ff`fffdc000 Unfrozen
       1  Id: 490.1884 Suspend: 1 Teb: 000007ff`fffd8000 Unfrozen
       2  Id: 490.1200 Suspend: 1 Teb: 000007ff`fffa2000 Unfrozen
       3  Id: 490.a10 Suspend: 1 Teb: 000007ff`fff9e000 Unfrozen
    …………
      26  Id: 490.1c4c Suspend: 1 Teb: 000007ff`ffe90000 Unfrozen
      27  Id: 490.1b88 Suspend: 1 Teb: 000007ff`ffe86000 Unfrozen
      28  Id: 490.290c Suspend: 1 Teb: 000007ff`ffe72000 Unfrozen
    …………

      34  Id: 490.2b64 Suspend: 1 Teb: 000007ff`ffeec000 Unfrozen
      35  Id: 490.2360 Suspend: 1 Teb: 000007ff`ffed8000 Unfrozen
      36  Id: 490.a2c Suspend: 1 Teb: 000007ff`fffd6000 Unfrozen
      …………

      62  Id: 490.2b04 Suspend: 0 Teb: 000007ff`ffebc000 Unfrozen
      63  Id: 490.2004 Suspend: 1 Teb: 000007ff`ffeba000 Unfrozen
      64  Id: 490.24a8 Suspend: 0 Teb: 000007ff`ffeb8000 Unfrozen
    …………

      72  Id: 490.9bc Suspend: 1 Teb: 000007ff`ffea6000 Unfrozen
      73  Id: 490.2494 Suspend: 1 Teb: 000007ff`ffea4000 Unfrozen
      74  Id: 490.2724 Suspend: 1 Teb: 000007ff`ffea2000 Unfrozen
    …………
      92  Id: 490.29c8 Suspend: 1 Teb: 000007ff`ffe78000 Unfrozen
      93  Id: 490.1da8 Suspend: 1 Teb: 000007ff`ffe76000 Unfrozen
    …………

      98  Id: 490.2650 Suspend: 1 Teb: 000007ff`ffe6a000 Unfrozen
      99  Id: 490.23e8 Suspend: 1 Teb: 000007ff`ffe66000 Unfrozen
     100  Id: 490.107c Suspend: 1 Teb: 000007ff`ffe64000 Unfrozen
    101  Id: 490.1b98 Suspend: 1 Teb: 000007ff`ffe62000 Unfrozen
    102  Id: 490.2bd4 Suspend: 1 Teb: 000007ff`ffe60000 Unfrozen
    ……………

    108  Id: 490.a5c Suspend: 0 Teb: 000007ff`ffe4e000 Unfrozen
    109  Id: 490.980 Suspend: 1 Teb: 000007ff`ffe4a000 Unfrozen

    我们终于找到了, 线程号码为99和100.

    验证一下吧

    0:099> ~99s;k
    ntdll!ZwWaitForSingleObject+0xa:
    00000000`77ef047a c3              ret
    Child-SP          RetAddr           Call Site
    00000000`34f1f6d8 00000000`77efb416 ntdll!ZwWaitForSingleObject+0xa [d:…asm @ 180]
    00000000`34f1f6e0 00000000`77efb480 ntdll!RtlpWaitOnCriticalSection+0x240 [d:….c @ 617]
    00000000`34f1f760 00000000`35f7da11 ntdll!RtlEnterCriticalSection+0xa9 [d:\nt\….c @ 876]
    00000000`34f1f790 00000000`35f7dc8b ONETUTIL!COWSHeap::RunLeakDetectionInternal+0x281
    00000000`34f1f7e0 00000000`35f7e081 ONETUTIL!COWSMemmgr::AcquireLocalHeap+0x13
    00000000`34f1f820 00000000`3561c500 ONETUTIL!COWSMemmgr::InitMemmgrForThread+0x4d
    00000000`34f1f880 00000000`3000670d OWSSVR!GetExtensionVersion+0x5b14
    00000000`34f1f900 00000000`35febb0d OWSTIMER!VTimerProcessRequest+0xa1 [d:…@ 225]
    00000000`34f1fc30 00000000`35fece4e ONETUTIL!COWSThreadWithHeap::WalkHeap+0x1fd
    00000000`34f1fc80 00000000`781337d7 ONETUTIL!COWSThreadWithHeap::Uninitialize+0x97e
    00000000`34f1ff20 00000000`78133894 msvcr80!_callthreadstartex+0x17 [f:\…\…@ 348]
    00000000`34f1ff50 00000000`77d6b71a msvcr80!_threadstartex+0x84 [f:\dd….c @ 326]
    00000000`34f1ff80 00000000`00000000 kernel32!BaseThreadStart+0x3a [d:\….c @ 795]

  • 相关阅读:
    go语言中通过http访问需要认证的api
    Mysql两个time类型计算时间相减
    gorm中数据库datetime类型的映射和time.Time的格式化
    最详细的六种装饰器写法,学不会你找我!
    深度学习中的四种激活函数
    看完这篇文章,相信我,你已经掌握正则表达式了!
    新手还在问学Python应该看什么书,老手已经进来下载了(附100本pdf电子书下载)
    Python轻松实现一个毕业生信息管理系统!
    Python一键搞定批量合成PDF
    网易云10万+音乐竟然能用Python一键下载!
  • 原文地址:https://www.cnblogs.com/awpatp/p/2185885.html
Copyright © 2020-2023  润新知