上位机业务卡死问题windbg分析

问题背景

今年 7 月份,现场反馈了一个上位机卡死的问题:运行的流程一直没有结束,但日志却没有持续输出,只剩心跳包在打印。

当时我们抓取了两个 dump 文件,打印了所有线程的调用堆栈,试图从中定位卡死的位置。然而分析完所有线程后,始终没有找到业务线程的踪影。

由于情况紧急必须尽快修复,我们只能结合卡住前的日志信息,用了一个晚上逐行分析代码,最终找到了卡死的代码位置。但这也留下了一个疑惑:卡死的地方明明是一个 while 循环,为什么 WinDbg 打印的所有线程调用堆栈中却找不到这个函数?

过了一个多月,最近终于闲下来,可以仔细钻研一下这个问题了。

复现 Demo

以下是我简化后的 demo 代码:

c# 复制代码
private void TestBtn_Click(object sender, RoutedEventArgs e)
{
    Task.Run(async () =>
    {
        await TestFun();
    });
}

bool flag = false;

async Task TestFun()
{
    while (true)
    {
        await Task.Delay(100);
        if (flag)
        {
            Debug.WriteLine("Flag is true, exiting loop.");
            break;
        }
    }
}

代码逻辑很简单:点击 Test 按钮后,通过线程池线程执行 TestFun 函数。需要注意的是,TestFun 并不是一个普通函数,而是一个异步函数 ------在 while 循环中交替执行 await Task.Delay(100) 和业务代码。

现象复现与原因分析

运行程序、点击按钮后,用任务管理器抓取 dump,再用 WinDbg 分析所有线程的调用堆栈------果然,没有找到任何正在执行 TestFun 的线程。

原因就在"异步"二字上:

异步函数在编译时会被编译器转换为一个状态机 。当执行到 await Task.Delay 时,当前线程会被释放 回线程池,此时它要么空闲,要么去执行其他任务。状态机会挂起等待,直到 Task.Delay 完成后,再由某个线程池线程(不一定是原来的线程)继续执行后续代码。

因此,抓取 dump 的那一刻,如果状态机正好处于 Task.Delay 的等待阶段,就没有任何一个线程 的调用堆栈上能看到 TestFun------它根本不在任何线程上运行。这正是当初在 dump 中找不到业务线程的根本原因。

生产环境如何定位

那么,生产环境遇到这类问题该如何定位呢?答案是使用 SOS 扩展的 !dumpasync 命令,它可以枚举出当前所有处于挂起状态的异步状态机:

xml 复制代码
0:000> !dumpasync
STACK 1
000002550ba48670 00007ffe05e64998 ( ) System.Threading.Tasks.Task+DelayPromise
  000002550b9e2480 00007ffe05e67bb0 (0) WhileLoopDemo.MainWindow+<TestFun>d__3 @ 7ffe05d8a200
    000002550b9e24d0 00007ffe05e685e0 (0) WhileLoopDemo.MainWindow+<<TestBtn_Click>b__1_0>d @ 7ffe05d89ee0
      000002550b9df440 00007ffe05e49cd8 ( ) System.Threading.Tasks.UnwrapPromise<System.Threading.Tasks.VoidTaskResult>

从输出可以清晰地看到异步调用链:TestFun 状态机(<TestFun>d__3)正挂起在 Task.DelayPromise 上,其上层是按钮点击事件的 lambda 状态机。即便没有线程正在执行它,异步调用链依然一览无余。

总结

以后再遇到"程序卡死但 dump 中找不到业务线程"的问题时,不妨换个思路:

  1. 先怀疑业务代码中是否存在 while 死循环 (尤其是配合 await 的异步循环);
  2. 除了查看常规的线程调用堆栈,别忘了用 !dumpasync 查看异步状态机------真正的"案发现场"可能根本不在任何线程上。
相关推荐
SimonKing3 小时前
Java 图片处理还在用 ImageIO?这个库让你代码从 30 行变 3 行
java·后端·程序员
涛涛ing4 小时前
一周之内,前端 AI 编程的格局被彻底改写了三次
前端·程序员
学不可以已18 小时前
Windows 开启/禁用 ping/icmp 回显服务
程序员
AskHarries18 小时前
Twitter 增长技巧
程序员
AIOps打工人20 小时前
【AIOPS】当运维 Agent 开始自己学:证据补全才是自学习的真门槛
程序员·架构
SimonKing1 天前
GenOffice上手指南:免费替代Word+PPT+Excel的AI办公神器
java·后端·程序员
Hilaku1 天前
Bun 真的能取代 Node.js 吗?
前端·javascript·程序员
顶级自由人2 天前
本地正常、线上正常,为什么一个 Hook 仍会报错?
前端·javascript·程序员
Hilaku2 天前
技术好就能升职是前端圈最大的谎言!
前端·javascript·程序员