使用WinDbg分析死锁

本文介绍了一个简单的死锁程序创建方法及如何使用WinDbg工具进行死锁分析。通过创建.dump文件并利用WinDbg命令如~*e!clrstack、!runaway等,可以详细了解各线程的状态及锁的持有情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

创建死锁程序

using System;
using System.Threading;


namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            new Program().Test1();
        }

        private void Test1()
        {
            lock (this)
            {
                Console.WriteLine("Enter Test1");
                new Thread(() =>
                    {
                        Thread.Sleep(2000);
                        Test2();
                    }).Start();
                Console.ReadKey();
                Console.WriteLine("Exit Test1");
            }
        }

        private void Test2()
        {
            lock (this)
            {
                Console.WriteLine("Test2");
            }
        }
    }
}


运行程序后(不要点击键盘)创建.dump文件


使用WinDbg进行分析

执行~*e!clrstack查看所有持有和等待锁的线程(的下一条要执行的代码)

0:000> ~*e!clrstack
OS Thread Id: 0x26d4 (0)
        Child SP               IP Call Site
000000000045ea38 000007ffe9ab2c5a [InlinedCallFrame: 000000000045ea38] Microsoft.Win32.Win32Native.ReadConsoleInput(IntPtr, InputRecord ByRef, Int32, Int32 ByRef)
000000000045ea38 000007ffd1e3f64e [InlinedCallFrame: 000000000045ea38] Microsoft.Win32.Win32Native.ReadConsoleInput(IntPtr, InputRecord ByRef, Int32, Int32 ByRef)
000000000045e9f0 000007ffd1e3f64e DomainNeutralILStubClass.IL_STUB_PInvoke(IntPtr, InputRecord ByRef, Int32, Int32 ByRef)
000000000045eb30 000007ffd1ea9961 System.Console.ReadKey(Boolean)
000000000045ec50 000007ff7b5501b1 ConsoleApplication1.Program.Test1() [f:\Documents\Visual Studio 2012\Projects\2012test\ConsoleApplication1\Program.cs @ 25]
000000000045ecc0 000007ff7b5500a8 ConsoleApplication1.Program.Main() [f:\Documents\Visual Studio 2012\Projects\2012test\ConsoleApplication1\Program.cs @ 11]
000000000045efd0 000007ffdac338f3 [GCFrame: 000000000045efd0] 
OS Thread Id: 0x2b74 (1)
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
Failed to start stack walk: 80070057
OS Thread Id: 0x2a60 (2)
        Child SP               IP Call Site
000000001af8f8c8 000007ffe9ab319b [DebuggerU2MCatchHandlerFrame: 000000001af8f8c8] 
OS Thread Id: 0x1978 (3)
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
Failed to start stack walk: 80070057
OS Thread Id: 0x242c (4)
        Child SP               IP Call Site
000000001ba6e9d8 000007ffe9ab319b [GCFrame: 000000001ba6e9d8] 
000000001ba6eb18 000007ffe9ab319b [GCFrame: 000000001ba6eb18] 
000000001ba6eb58 000007ffe9ab319b [HelperMethodFrame_1OBJ: 000000001ba6eb58] System.Threading.Monitor.Enter(System.Object)
000000001ba6ec50 000007ff7b550300 ConsoleApplication1.Program.Test2() [f:\Documents\Visual Studio 2012\Projects\2012test\ConsoleApplication1\Program.cs @ 33]
000000001ba6ecb0 000007ffd16afb65 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000001ba6ee10 000007ffd16af8c9 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000001ba6ee40 000007ffd16af887 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
000000001ba6ee90 000007ffd16c2fe1 System.Threading.ThreadHelper.ThreadStart()
000000001ba6f1a8 000007ffdac338f3 [GCFrame: 000000001ba6f1a8] 
000000001ba6f4d8 000007ffdac338f3 [DebuggerU2MCatchHandlerFrame: 000000001ba6f4d8] 


执行!runaway查看占有锁的线程运行了多长时间

0:000> !runaway
 User Mode Time
  Thread       Time
   4:242c      0 days 0:00:00.000
   3:1978      0 days 0:00:00.000
   2:2a60      0 days 0:00:00.000
   1:2b74      0 days 0:00:00.000
   0:26d4      0 days 0:00:00.000


执行.time看进程运行了多长时间

0:000> .time
Debug session time: Mon May 20 17:22:13.000 2013 (UTC + 8:00)
System Uptime: 0 days 7:14:13.190
Process Uptime: 0 days 0:00:11.000
  Kernel time: 0 days 0:00:00.000
  User time: 0 days 0:00:00.000


执行!threads看所有的线程统计情况

0:000> !threads
ThreadCount:      3
UnstartedThread:  0
BackgroundThread: 1
PendingThread:    0
DeadThread:       0
Hosted Runtime:   no
                                                                                                        Lock  
       ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1 26d4 00000000004fe850    2a020 Preemptive  0000000002826698:0000000002827FD0 00000000004d2ab0 2     MTA 
   2    2 2a60 00000000005056a0    2b220 Preemptive  0000000000000000:0000000000000000 00000000004d2ab0 0     MTA (Finalizer) 
   4    3 242c 000000000052aca0  202b020 Preemptive  0000000002828010:0000000002829FD0 00000000004d2ab0 0     MTA 

可看到线程0有两个锁

执行!syncblk查看哪些线程拿到了锁

0:000> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner
    2 000000000052b978            3         1 00000000004fe850 26d4   0   0000000002822e88 ConsoleApplication1.Program
-----------------------------
Total           2
CCW             0
RCW             0
ComClassFactory 0
Free            0

可看到线程“00000000004fe850 ”拿到了锁,再通过!threads命令得到其线程逻辑号为0,即可切换至该线程,并通过!clrstack打印出其运行堆栈。

备注

  •   若表现为界面死掉,就直接找UI线程(STA线程就一个)
  •   堆栈分为本地堆栈与托管堆栈,线程号也有本地线程号与托管线程号
  •   可使用!dso (DumpStackObjects)查看当前堆栈的所有对象
  •   可使用 !analyze -v 分析挂掉线程的详细情形,错误原因

转载于:https://www.cnblogs.com/beta2013/archive/2013/05/20/3377264.html

WinDBG 是个非常强大的调试器,它设计了极其丰富的功能来支持各种调试任务,包括用户 态调试、 内核态调试、 调试转储文件、 远程调试等等。 WinDBG 具有非常大的灵活性和可扩展性, 用来满足各种各样的调试需求,比如用户可以自由定义调试事件的处理方式,编写调试扩展模块 来定制和补充 WinDBG 的调试功能。 尽管 WinDBG 是个典型的窗口程序, 但是它的大多数调试功能还是以手工输入命令的方式来 工作的。目前版本的 WinDBG 共提供了 20 多条标准命令, 140 多条元命令( Meta-commands), 和难以计数的大量扩展命令。学习和灵活使用这些命令是学习 WinDBG 的关键,也是难点。 上一章我们从设计的角度分析WinDBG ,本章将从使用(用户)的角度介绍 WinDBG 。我 们先介绍工作空间的概念和用法(第 1 节),然后介绍命令的分类和不同种类的命令提示符(第 2 节)。 第 3 节介绍不同的调试模式, 也就是如何与不同特征的调试目标建立调试会话。 第 4 节介绍 上下文的概念和在调试时应该如何切换和控制上下文。第 5 节介绍调试事件和如何定制调试事件 的处理方式。 从第 6 节到第 9 节我们将分别介绍如何在 WinDBG 中完成典型的调试操作, 比如控 制调试目标(第 6 节)、设置断点(第 7 节)、观察栈(第 8 节)以及如何观察和修改数据(第 9 节)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值