記一次 .NET 某新能源材料檢測系統 崩潰分析

来源:https://www.cnblogs.com/huangxincheng/archive/2023/12/11/17894104.html
-Advertisement-
Play Games

一:背景 1. 講故事 上周有位朋友找到我,說他的程式經常會偶發性崩潰,一直沒找到原因,自己也抓了dump 也沒分析出個所以然,讓我幫忙看下怎麼回事,那既然有 dump,那就開始分析唄。 二:Windbg 分析 1. 到底是哪裡的崩潰 一直跟蹤我這個系列的朋友應該知道分析崩潰第一個命令就是 !ana ...


一:背景

1. 講故事

上周有位朋友找到我,說他的程式經常會偶發性崩潰,一直沒找到原因,自己也抓了dump 也沒分析出個所以然,讓我幫忙看下怎麼回事,那既然有 dump,那就開始分析唄。

二:Windbg 分析

1. 到底是哪裡的崩潰

一直跟蹤我這個系列的朋友應該知道分析崩潰第一個命令就是 !analyze -v ,讓windbg幫我們自動化異常分析。


0:033> !analyze -v
CONTEXT:  (.ecxr)
rax=00000039cccff2d7 rbx=00000039c85fc2b0 rcx=00000039cccff2d8
rdx=0000000000000000 rsi=0000000000000000 rdi=00000039c85fbdc0
rip=00007ffb934b1199 rsp=00000039c85fc550 rbp=00000039c85fc5b8
 r8=0000000000000000  r9=00000039c85fce90 r10=0000000000000009
r11=0000000000000080 r12=0000000000000000 r13=00000039c85fdaf0
r14=00007ffb933d12b0 r15=0000022939e68440
iopl=0         nv up ei pl nz ac pe cy
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010211
clr!Frame::HasValidVTablePtr+0x2a:
00007ffb`934b1199 488b39          mov     rdi,qword ptr [rcx] ds:00000039`cccff2d8=????????????????
Resetting default scope

STACK_TEXT:  
00000039`c85fc550 00007ffb`934b7107     : 00007ffb`933140d0 00007ffb`933140d0 00000000`00000000 00000000`00000000 : clr!Frame::HasValidVTablePtr+0x2a
00000039`c85fc600 00007ffb`933d3427     : 00000000`00000000 00000000`00000000 00007ffb`93c641e0 00007ffb`93c64c48 : clr!GCToEEInterface::GcScanRoots+0x2f2
00000039`c85fdac0 00007ffb`933d1843     : 00000000`00000000 00007ffb`00000000 00000000`00000000 00000000`00000001 : clr!WKS::gc_heap::mark_phase+0x197
00000039`c85fdb70 00007ffb`933d1762     : 00000000`00000001 00000039`00000000 00000000`00000000 00000000`00000001 : clr!WKS::gc_heap::gc1+0xa3
00000039`c85fdbd0 00007ffb`933d1539     : 00000000`00000001 00000000`00000000 00000229`00af0f88 00000000`00000000 : clr!WKS::gc_heap::garbage_collect+0x54c
00000039`c85fdc50 00007ffb`933d5f51     : 00000000`00000578 00007ffb`00000000 00000229`01ee5200 00000039`c85fdca0 : clr!WKS::GCHeap::GarbageCollectGeneration+0x10d
00000039`c85fdcb0 00007ffb`933d838c     : 00000229`01ee5288 00000000`00000030 00000229`2328ff18 00000229`2328ff18 : clr!WKS::gc_heap::trigger_gc_for_alloc+0x2d
00000039`c85fdcf0 00007ffb`9333a88b     : 00000000`00000030 00000000`00000008 00000000`00000000 00007ffb`00000000 : clr!WKS::GCHeap::Alloc+0x2a9
00000039`c85fdd50 00007ffb`9333a465     : ffffffc6`37a021c8 00000039`c85fded0 00000039`c85fde20 00000039`c85fdf00 : clr!SlowAllocateString+0x8b
...

從卦中的調用棧來看,有如下兩點信息:

  • GC 觸發了

上面的mark_phase表示當前 GC 正在標記階段,後面的GcScanRoots表示 GC正線上程棧上尋找根對象。

  • 崩潰點在 clr 中

看到崩潰在clr的 clr!Frame::HasValidVTablePtr 方法中真的有點不敢相信,從崩潰點的彙編代碼 rdi,qword ptr [rcx] 來看,貌似 rcx 沒有分配到物理記憶體,可以用 !address rcx 驗證下。


0:033> !address rcx

Usage:                  Free
Base Address:           00000039`ccb00000
End Address:            00000039`cce00000
Region Size:            00000000`00300000 (   3.000 MB)
State:                  00010000          MEM_FREE
Protect:                00000001          PAGE_NOACCESS
Type:                   <info not present at the target>


Content source: 0 (invalid), length: 1fbd28

尼瑪,真的好無語,這個rcx=00000039cccff2d8 所處的記憶體居然是一個 MEM_FREE,訪問它自然會拋異常,現在很迷茫的是這玩意是 GC 的內部邏輯,按理說不會有這種異常,難道是 CLR 自己的 bug 嗎?

三: 真的是 CLR 的 bug 嗎

1. 分析 CLR 源碼

要想尋找真相,就必須要理解崩潰處的 CLR 源碼了,這裡拿coreclr做參考,首先從 clr!Frame::HasValidVTablePtr+2a 處說起,這個方法大概就是用來判斷 Frame 類的虛方法表指針是否有效,簡化後的代碼如下:


// static
bool Frame::HasValidVTablePtr(Frame * pFrame)
{
    TADDR vptr = pFrame->GetVTablePtr();
    if (vptr == HelperMethodFrame::GetMethodFrameVPtr())
        return true;

    if (vptr == DebuggerSecurityCodeMarkFrame::GetMethodFrameVPtr())
        return true;
    if (s_pFrameVTables->LookupValue(vptr, (LPVOID) vptr) == (LPVOID) INVALIDENTRY)
        return false;

    return true;
}

這裡簡單說下什麼是虛方法表,如果一個類通過各種渠道擁有了虛方法後,那這個類的第一個欄位就是 虛方法表指針,這個指針所指向的虛方法表中存放著每個虛方法的入口地址,畫個圖大概是這樣。

有了這張圖再讓chatgpt寫一段C++代碼驗證下。


#include <iostream>

using namespace std;

// 父類
class Animal {
private:
	int age;
public:
	virtual void makeSound() {
		cout << "The animal makes a sound" << endl;
	}
};

// 子類
class Cat : public Animal {
public:
	void makeSound() override {
		cout << "The cat meows" << endl;
	}
};

int main() {

	// 使用父類指針指向子類對象,調用子類重寫的方法
	Animal* animal = new Cat();
	animal->makeSound(); // 輸出 "The cat meows"
	return 0;
}

上圖中的00219b60就是虛方法表指針,後面的0021100a就是虛方法地址了。

有了這些鋪墊之後,可以得知是在提取frame虛方法指針的時候,這個地址已被釋放導致崩潰的。

2. frame來自於哪裡

通過在 coreclr 源碼中一頓梳理,發現它是 Thread 類的第四個欄位,偏移是0x10,參考代碼如下:


PTR_GSCookie Frame::SafeGetGSCookiePtr(Frame* pFrame)
{
	Frame::HasValidVTablePtr(pFrame)
}

BOOL StackFrameIterator::Init(Thread* pThread,
	PTR_Frame   pFrame,
	PREGDISPLAY pRegDisp,
	ULONG32     flags)
{
	m_crawl.pFrame = m_pThread->GetFrame();
	m_crawl.SetCurGSCookie(Frame::SafeGetGSCookiePtr(m_crawl.pFrame));
}

0:008> dt coreclr!Thread
   +0x000 m_stackLocalAllocator : Ptr64 StackingAllocator
   +0x008 m_State          : Volatile<enum Thread::ThreadState>
   +0x00c m_fPreemptiveGCDisabled : Volatile<unsigned long>
   +0x010 m_pFrame         : Ptr64 Frame

觀察源碼大概就知道了 Frame 是棧幀的表示,標記階段要在每個線程中通過 m_pThread->GetFrame 方法來獲取爬棧的起始點。

到這裡我們知道了 m_pFrame 有問題,那它到底屬於哪個線程呢?

3. 尋找問題 Thread

要想尋找問題線程,可以自己寫個腳本,判斷下 ThreadOBJ+0x10 = rcx(00000039cccff2d8) 即可。


function invokeScript() {

    var lines = exec("!t").Skip(8);

    for (var line of lines) {
        var t_addr = line.substr(15, 16);

        var commandText = "dp " + t_addr + " L8";
        log(commandText);

        var output = exec(commandText);

        for (var line2 of output) {
            log(line2);
        }

        log("--------------------------------------")
    }
}

從卦中數據看終於給找到了,原來是有一個OSID=744的線程意外退出導致棧空間被釋放引發的,真的無語了。

接下來的問題是這個線程是用來幹嘛的,它做了什麼?

4. 778號線程是何方神聖

到這裡要給大家一點遺憾了,778號線程已經退出了,棧空間都被釋放了,在dump中不可能找到它生前做了什麼,不過最起碼我們知道如下幾點信息:

  • 它是一個由 C# 創建的托管線程
  • 它是一個非 線程池線程
  • 它肯定是某種原因意外退出的

要想知道這個線程生前做了什麼,最好的辦法就是用 perfview 捕獲線程創建和退出的 ETW 事件,到那一天定會水落石出!!!

四:總結

這次生產事故,我感覺用戶CLR都有責任,托管線程的棧空間都釋放了,為什麼 CLR 在觸發 GC 時還要去爬它的棧導致崩潰的發生,這真的是一個很有意思的dump。

圖片名稱
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一.什麼是Tomact? Tomcat是一個開源免費的輕量級Web伺服器,它是一個軟體程式,主要功能是提供網上信息瀏覽服務,對HTTP協議的操作進行封裝,使得程式員不必對協議進行操作,讓Web開發更加便捷。 Tomcat官網 二.Tomcat的安裝 註意:由於Tomcat依賴Java,請在Tomca ...
  • PlayEdu —— 一個適用於搭建企業內部培訓平臺的開源系統。採用前後端分離的模式,後端基於 Java + SpringBoot3 + MySQL 開發,前端採用 React18 為核心框架。 ...
  • 在Qt中,信號與槽(Signal and Slot)是一種用於對象之間通信的機制。是Qt框架引以為傲的一項機制,它帶來了許多優勢,使得Qt成為一個強大且靈活的開發框架之一。信號與槽的關聯通過`QObject::connect`函數完成。這樣的機制使得對象能夠以一種靈活而鬆散耦合的方式進行通信,使得組... ...
  • EF Core 提供兩種主要方法來保持 EF Core 模型和資料庫架構同步。至於我們應該選用哪個方法,請確定你是希望以 EF Core 模型為準還是以資料庫為準。 如果希望以 EF Core 模型為準,請使用遷移。 對 EF Core 模型進行更改時,此方法會以增量方式將相應架構更改應用到資料庫, ...
  • .NET Core MVC基礎之返迴文件類型📄 前言 上一篇文章講了基礎的返回類型,這篇文章講解如何返迴文件類型給瀏覽器下載。 系列文章 .NET MVC基礎之頁面傳值方式 通過圖片流來返回圖片 返回類型介紹 在返迴文件之前,要知道MIME 類型,這裡介紹常用的幾種: text/plain:純文本 ...
  • 模型創建分為正向工程(CodeFirst)與反向工程(DbFirst). 正向工程的模型配置也可以創建任意的資料庫關係對象,如:欄位,欄位說明,表,索引,外鍵等等。 可在派生上下文中替代 OnModelCreating 方法,並使用 ModelBuilder API 來配置模型。 此配置方法最為有效 ...
  • 1. 快速入門 創建新的控制台項目 dotnet new console -o EFGetStarted cd EFGetStarted 安裝 Entity Framework Core 要安裝 EF Core,請為要作為目標對象的 EF Core 資料庫提供程式安裝程式包。 本教程使用 SQLit ...
  • 在開發過程中有時候我們需要對現有對象進行克隆,我們可以使用DeepCloner類庫來完成這個需求功能。 DeepCloner 類庫 功能: 深度克隆: DeepCloner 提供深度克隆對象的能力,遞歸地克隆對象圖中的所有引用類型屬性。 迴圈引用處理: 能夠處理對象圖中的迴圈引用,防止無限遞歸。 自 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...