قراءة ملفّات تفريغ الأعطال عبر WinDbg + SOS ── مدخل عمليّ للتحليل بعد الجمع
· آخر تحديث: · غو كومورا · WinDbg, SOS, ملفّ تفريغ الأعطال, .NET, CSharp, تصحيح الأخطاء, PDB, تحقيق الأعطال, الاستشارات التقنيّة
في المقال السابق «مدخل إلى جمع ملفّات تفريغ أعطال Windows» رتَّبنا الأمر حتّى مرحلة «جمع» ملفّات التفريغ باستخدام WER LocalDumps وProcDump وMiniDumpWriteDump. لكنّ مجرّد جمع ملفّ التفريغ لا يُخبرنا بشيء. لا يصبح مادّة للتحقيق إلّا بعد أن نحرِّك أيدينا فعليّاً ونفكَّ شفرة «أيّ خيط» و«لماذا» تعطَّل، أو «ما الذي» يستمرّ في الإمساك بالذاكرة.
في هذا المقال، وكاستكمال لمقال الجمع، نركِّز على خطوات قراءة ملفّ التفريغ الذي جُمِع فعليّاً باستخدام WinDbg وامتداد SOS. نتناول التثبيت وضبط الرموز، وتحميل امتداد SOS الضروريّ لتطبيقات .NET، و«ما الذي يُنظَر إليه وكيف يُحكَم عليه» بأوامر نموذجيّة مثل !clrstack و!dumpheap -stat، وأمر !analyze -v للأعطال الأصليّة، وصولاً إلى التمييز بين استخدامه واستخدام dotnet-dump analyze الذي لا يحتاج WinDbg.
1. الخلاصة أوّلاً
- الأداة الرئيسيّة لتحليل ملفّات التفريغ هي WinDbg (الإصدار الحاليّ، المعروف سابقاً بـ WinDbg Preview). يمكن الحصول عليه عبر
winget install Microsoft.WinDbgأو من Microsoft Store، ويعمل على معماريّتَي x64 وARM64 في Windows 10 Anniversary Update (1607) فما بعده / Windows 11.1 - حتّى دون تحميل الرموز (PDB)، تعمل أوامر مثل
!clrstackو!dumpheap -statو!gcrootكما هي اعتماداً على بيانات CLR الوصفيّة وبيانات الكومة. ما يُفقَد هو اسم ملفّ مصدر الكود المُدار ورقم السطر، واسم رمز الأطر الأصليّة. لكن إن أردتَ التتبّع حتّى سطر المصدر فالأمر مختلف، لذا القاعدة الثابتة هي تمرير كلٍّ من خادم الرموز العامّ لمايكروسوفت وموقع PDB الخاصّ بالشركة إلى_NT_SYMBOL_PATH.2 - في ملفّات تفريغ تطبيقات .NET (Framework / Core / 5+)، لا تظهر المعلومات المُدارة إلّا بعد تحميل امتداد SOS. لا يمكن تتبّع كود C# بأمر
k(عرض المكدّس) الأصليّ وحده.3 - توجد ثلاثة أنماط تحقيق نموذجيّة. إن كان السبب استثناءً:
!clrstack←!pe، إن كانت الذاكرة تتزايد باستمرار:!dumpheap -stat←!gcroot، إن كان عطلاً أصليّاً: ابدأ بـ!analyze -v. - كخيار لا يحتاج WinDbg، توجد
dotnet-dump analyze. يمكن استخدام معظم أوامر SOS كما هي، لكنّها لا تتعامل مع أطر المكدّس الأصليّة. للتحقيق المُدار الخالص الذي لا يتداخل فيه DLL أصليّ أو COM، تثبيت هذه الأداة أخفّ.4 - الخطوات المشروحة هنا هي «كيفيّة القراءة» وليست «كيفيّة الجمع». راجع مقال الجمع بخصوص طريقة الحصول على ملفّ التفريغ (WER / ProcDump /
MiniDumpWriteDump)، وراجع «تصميم يترك السجلّات وملفّ التفريغ عند وقوع العطل» بخصوص تصميم مطابقتها مع السجلّات.
2. تثبيت WinDbg وتمرير الرموز
2.1 التثبيت
يمكن تثبيت الإصدار الحاليّ من WinDbg بإحدى الطريقتين التاليتين.1
winget install Microsoft.WinDbg
يُثبَّت المحرّك نفسه أيضاً عبر Microsoft Store، والأوامر والامتدادات وسير العمل مشتركة. يُحدَّث تلقائيّاً بعد التثبيت (في الخلفيّة عند التثبيت عبر Store أو المباشر، وعبر winget upgrade Microsoft.WinDbg في حالة winget)، لذا لن تقلقك كثيراً اختلافات السلوك الناتجة عن اختلاف الإصدار.1
2.2 فتح ملفّ التفريغ
windbg -z C:\CrashDumps\MyApp\MyApp_20260702_101500.dmp
-z خيار لتشغيل البرنامج مع تحديد ملفّ التفريغ. يمكن تحقيق الأمر نفسه من الواجهة الرسوميّة عبر «File > Open Dump File».
2.3 ضبط مسار الرموز
يُحدَّد الموقع الذي يبحث فيه مصحِّح أخطاء Windows عن ملفّات الرموز (PDB) عبر متغيّر البيئة _NT_SYMBOL_PATH، أو عبر أمر .sympath داخل الجلسة.2 الشكل الأساسيّ في العمل الفعليّ هو تمرير كلٍّ من خادم الرموز العامّ لمايكروسوفت وموقع PDB الخاصّ بالشركة.
.symfix C:\Symbols\Microsoft
.sympath+ C:\Symbols\MyApp
.reload
.symfixاختصار يضبط المسار إلى خادم الرموز العامّ لمايكروسوفت (https://msdl.microsoft.com/download/symbols) مع ذاكرة تخزين مؤقّت محليّة محدَّدة. تُنزَّل رموز DLL القياسيّة لنظام التشغيل تلقائيّاً من هنا.5.sympath+يضيف موقع PDB الخاصّ بالشركة إلى المسار الحاليّ. يجب توفير PDB الخاصّ بكود الشركة بنفسك، فهو غير موجود على خادم رموز مايكروسوفت.- أعِد التحميل عبر
.reload، وتحقَّق من حالة الرموز في قائمة الوحدات (modules).
في حال الضبط الدائم عبر متغيّر البيئة، يكون الشكل كالتالي. هذا الأسلوب مناسب للتحليل التلقائيّ في CI أو خوادم البناء.
set _NT_SYMBOL_PATH=srv*C:\Symbols\Microsoft*https://msdl.microsoft.com/download/symbols;C:\Symbols\MyApp
يمكن التحقّق من قراءة الرموز بشكل صحيح بإخراج قائمة الوحدات عبر أمر lm (loaded modules)، ومعرفة ما إذا كانت الوحدة المستهدَفة قد أصبحت pdb symbols. إن بقيت على حالة deferred، فالرموز لم تُحلّ بعد.
3. تحميل امتداد SOS
لا تظهر «محتويات الكومة المُدارة» و«أطر مكدّس C#» و«محتويات كائن الاستثناء» في ملفّات تفريغ تطبيقات .NET بأوامر WinDbg الأصليّة وحدها. امتداد SOS (Son of Strike) يسدّ هذه الفجوة. يمكن عبر أوامر SOS إجراء تحقيق الكومة، وكشف تلف الكومة، وعرض أنواع البيانات الداخليّة لبيئة التشغيل، بل وحتّى فهم حالة الكود المُدار قيد التنفيذ.3
3.1 الفرق باختلاف بيئة التشغيل
يختلف مصدر بيئة التشغيل وSOS الواجب تحميلهما باختلاف كون التطبيق المستهدَف .NET Framework أو .NET (Core) / .NET 5+.
| الهدف | جوهر بيئة التشغيل | أمر التحميل |
|---|---|---|
| .NET Framework | clr.dll |
.loadby sos clr |
| .NET Core / .NET 5+ | coreclr.dll |
.loadby sos coreclr |
.loadby أمر يبحث عن DLL الامتداد (sos.dll) الموجود في المجلّد نفسه الذي توجد فيه الوحدة المحدَّدة (clr أو coreclr) ويحمِّله. ميزته أنّه يلتقط بشكل مؤكَّد إصدار SOS المطابق للبيئة التي جُمِع منها ملفّ التفريغ، دون الحاجة لكتابة المسار الكامل.6
في WinDbg وcdb من الإصدار 10.0.18317.1001 فما بعده، عند اكتشاف أنّ العمليّة المستهدَفة تحمِّل coreclr.dll (أو libcoreclr.so في Linux/macOS)، يُحمَّل امتداد .NET تلقائيّاً من Microsoft Extension Gallery.6 وموضع .loadby المذكورة أعلاه هو كتابتها يدويّاً عندما لا ينجح التحميل التلقائيّ، أو عند استخدام إصدار قديم من المصحِّح.
3.2 عند عدم العثور على SOS
في البيئات التي لا يعمل فيها التحميل التلقائيّ، يمكن التثبيت محليّاً عبر أداة dotnet-sos.
dotnet tool install --global dotnet-sos
dotnet-sos install
بعد التثبيت، يمكن أيضاً تحميله يدويّاً في WinDbg كالتالي (قد يلزم هذا في المصحِّحات القديمة).7
.load %USERPROFILE%\.dotnet\sos\sos.dll
3.3 التحقّق من نجاح التحميل
!sos.help
أو جرِّب !Threads إن كان الهدف من سلسلة Core، أو !sosstatus إن كان من سلسلة Framework، فإن رجعت معلومات دون خطأ فالتحميل ناجح. إن فشل الأمر هنا بخطأ من نوع Unable to find module، فالسبب غالباً هو مسار الرموز أو عدم تطابق بيئة التشغيل (كاختلاف عدد البتّات أو الإصدار بين بيئة جمع ملفّ التفريغ وبيئة التشغيل المحليّة). الحالات التي يُعلَق فيها هنا قبل الوصول إلى أوامر الفصل التالي ليست نادرة في العمل الفعليّ أيضاً.
4. قراءة الاستثناءات والمكدّس ── !clrstack و!pe
هذه هي الخطوة الأولى لملفّ تفريغ تعطَّل باستثناء غير معالَج.
!threads
أوّلاً، تحقَّق عبر !Threads (اسمها المستعار clrthreads في بيئة lldb) من قائمة الخيوط المُدارة، وعمود Exception الخاصّ بكلّ خيط.8 إن وُجد خيط يحمل استثناءً، انتقل إليه.
~5s
!clrstack
يعرض !CLRStack تتبّع المكدّس (stack trace) للكود المُدار فقط.9 إن أردتَ رؤية المعطيات (arguments) والمتغيّرات أيضاً، أضِف -a (اختصار يجمع -l و-p).
!clrstack -a
- إن ظهرت طرق (methods) كود الشركة مصفوفة، يمكن مباشرةً قراءة «أين» و«عبر أيّ مسار استدعاء» حدث التعطّل. ظهور اسم ملفّ المصدر ورقم السطر يعود إلى أنّ الرموز مقروءة بشكل صحيح (الفصل 2). بما أنّ
CLRStackتُعدِّد الأطر المُدارة مباشرةً من بيانات CLR الوصفيّة، فإنّ وجود الرموز أو عدمه لا يؤثِّر على ظهور الأطر من عدمه. ما يُفقَد عند نقص الرموز هو اسم ملفّ المصدر ورقم السطر فقط، ولا يُحذَف الإطار نفسه أبداً.9 - إن لم يظهر ولا إطار واحد من أطر كود الشركة، فالمشتبه به ليس نقص الرموز، بل الآتي: كون الخيط المُختار خيطاً آخر لا يحمل الاستثناء (خطأ في اختيار الخيط)، أو أنّ التعطّل حدث في الجانب الأصليّ فقط ولا توجد أطر مُدارة أصلاً، أو أنّ نوع ملفّ التفريغ (كـ Mini مثلاً) لا يتضمّن معلومات المكدّس في تلك اللحظة بشكل كافٍ.
بعد ذلك، لننظر إلى كائن الاستثناء نفسه.
!pe
يعرض !PrintException (اختصاره !pe) عند عدم التحديد آخر استثناء أُلقيَ في الخيط الحاليّ. يمكن الحصول على اسم النوع، والرسالة، والاستثناء الداخليّ (يُعرَض عبر -nested)، وحتّى نصّ تتبّع المكدّس.10 كلّما كان الاستثناء لا يُخبِر بشيء من اسم نوعه وحده، كـ System.NullReferenceException، ازدادت الحاجة إلى مطابقته مع قيم المتغيّرات المحليّة الظاهرة عبر !clrstack -a.
5. تتبّع الكومة والتسريبات ── !dumpheap -stat و!gcroot
هذه هي الأوامر المحوريّة في تحقيقات من نوع «الذاكرة تتزايد تدريجيّاً ويحدث التعطّل بعد ساعات إلى أيّام». كتبنا بالتفصيل عن المرحلة السابقة، وهي التمييز بين انتظار GC والتسريب الحقيقيّ، في «التمييز بين انتظار GC وتسريب الذاكرة في .NET». هذا المقال استكمال لذلك، ويقابل الجزء الذي يتعمّق حتّى «ما الذي يُمسِك بها» بقراءة ملفّ تفريغ واحد.
!dumpheap -stat
يعرض خيار -stat ملخّصاً إحصائيّاً فقط للكومة المُدارة. تُصفّ الأنواع تقريباً بترتيب تنازليّ حسب عدد الحالات (instances) والحجم الإجماليّ، فنحدِّد أوّلاً «النوع الذي يهيمن بالكمّيّة».11 السلسلتان اللتان تُشاهدان كثيراً في العمل الفعليّ هما التاليتان.
- تزايد فئة (class) الأعمال نفسها (مثلاً: مئات الآلاف من
MyApp.Models.Customer) ── يوجد مرجع قويّ (strong reference) يستمرّ في الإمساك بها في مكان ما - كثرة
System.Stringأو المصفوفات فقط بشكل مفرط ── غالباً ما يكون السبب هو البيانات الداخليّة لفئة الأعمال الظاهرة في الأعلى، والأسرع غالباً هو الاشتباه بجانب فئة الأعمال أوّلاً قبل النظر إلى كلّ حالة (instance) على حدة
بعد تضييق النطاق، احصل على عنوان الحالة الفرديّة.
!dumpheap -type MyApp.Models.Customer
ثمّ ابحث عن سبب بقاء ذلك الكائن دون أن يُجمَع بواسطة GC.
!gcroot 000001a2b3c4d5e0
يبحث !GCRoot في الكومة المُدارة وجدول المقابض (handle table) بأكملهما، ويكشف الجذور (roots) التي تصل إلى الكائن المحدَّد (كالمتغيّرات على المكدّس، والحقول الساكنة، ومقابض GC وغيرها).12 إن ظهر في المخرَجات حقل ساكن للتخزين المؤقّت أو اشتراك في معالِج حدث، فذلك مرشَّح للسبب المتمثِّل في نسيان إلغاء الاشتراك. الكود التالي، من نوع «إبقاء العنصر في التخزين المؤقّت دون تحريره»، مثال نموذجيّ.
public static class CustomerCache
{
// قاموس ساكن لا يوجد له مسار لإلغاء العناصر، ويستمرّ في التزايد فقط
private static readonly Dictionary<int, Customer> _cache = new();
public static void Add(Customer c) => _cache[c.Id] = c;
}
إذا ظهر في مخرَجات !gcroot حاوٍ ساكن مثل CustomerCache، فذلك مادّة لدراسة إضافة صلاحيّة زمنيّة أو حدّ أقصى لعدد العناصر في جانب الكود، أو التحوّل إلى WeakReference.11
6. التحليل التلقائيّ للأعطال الأصليّة ── !analyze -v
في الأعطال الأصليّة (كانتهاك الوصول (access violation) مثلاً) التي يتداخل فيها DLL بلغة C++ أو COM أو SDK من جهة مورِّد، ابدأ بهذا الأمر أوّلاً.
!analyze -v
!analyze أمر امتداد يجري تحليلاً تلقائيّاً للتعطّل والاستثناء، ويصبح العرض تفصيليّاً بإضافة -v.13 العناصر الثلاثة التي ينبغي النظر إليها خصوصاً في المُخرَج هي التالية.
EXCEPTION_CODE/BUGCHECK_STR: نوع الشذوذ (انتهاك الوصول، فيضان المكدّس، وغيرهما)FAULTING_IP/FOLLOWUP_IP: عنوان التعليمة التي تعطَّلت فعليّاً، واسم الوحدة والدالّة المقابلَينMODULE_NAME/IMAGE_NAME: هل موضع التعطّل وحدة خاصّة بالشركة أم DLL تابع لجهة خارجيّة
إن كان التعطّل داخل DLL تابع لمورِّد وليس وحدة خاصّة بالشركة، فتتبّع ما بعد تلك النقطة يحتاج إلى PDB الخاصّ بالمورِّد (وغالباً لا يمكن الحصول عليه). النتيجة الواقعيّة في العمل الفعليّ هي الرجوع إلى جهة الاستدعاء (آخر معطيات مرَّرها كود الشركة) والاشتباه في «هل كانت القيمة الممرَّرة غير صحيحة». تحقَّق عبر حقل STACK_TEXT في !analyze -v ممّا استدعاه كود الشركة مباشرةً قبل التعطّل.
يمكن استخدام !analyze أيضاً في غير ملفّات التفريغ التي وقع فيها استثناء. عند الاشتباه بتجمّد (hang)، بعد اختيار الخيط المستهدَف، ينفِّذ الأمر التالي تحليلاً لعلاقات الحظر (blocking) بين الخيوط.
!analyze -hang
7. خيار لا يحتاج WinDbg ── dotnet-dump analyze
إن أردتَ فحص الكود المُدار فقط لـ .NET Core / .NET 5+ (دون تداخل DLL أصليّ أو COM)، فإنّ dotnet-dump الأخفّ من WinDbg خيار أيضاً.
dotnet tool install --global dotnet-dump
dotnet-dump analyze C:\CrashDumps\MyApp\MyApp_20260702_101500.dmp
يفتح الأمر الفرعيّ analyze جلسة تفاعليّة مُثبَّت فيها SOS مسبقاً، ويمكن استخدام معظم الأوامر المذكورة حتّى الآن مثل clrstack وdumpheap وgcroot كما هي دون بادئة !.4
معيار التمييز بين الاستخدامَين كالتالي.
| المعيار | WinDbg + SOS | dotnet-dump analyze |
|---|---|---|
| أطر المكدّس الأصليّة | تظهر | لا تظهر (مُدارة فقط)4 |
التحليل التلقائيّ الأصليّ عبر !analyze -v |
متاح | غير متاح |
| ملفّات تفريغ Linux | يمكن تحليلها بـ WinDbg على Windows (استخدام إصدار x64 لملفّات x64، وx64 لملفّات Arm64، وx86 لملفّات x86)14 | مدعوم (استخدام أداة بنفس عدد بتّات المنصّة)14 |
| ملفّات تفريغ macOS | غير مدعوم (دعم WinDbg لملفّات تفريغ Linux لا يشمل macOS) | مدعوم (.NET 5 فما بعده)4 |
| خفّة الاعتماد | مُثبِّت أو winget | أمر واحد عبر dotnet global tool |
| الدمج في CI متعدّد المنصّات | يتطلّب جهداً | سهل |
الخطّ الفاصل في العمل الفعليّ هو: WinDbg إن كان «يُحتمَل تداخل COM أو P/Invoke أو DLL أصليّ»، وdotnet-dump analyze إن كان «تحقيقاً في تسريب ذاكرة لكود مُدار خالص، مع الرغبة في التشغيل على CI أو عدّة منصّات». بما أنّ الأداتين تشتركان في نظام أوامر SOS، فإنّ الأوامر التي تُتعلَّم في إحداهما تعمل تقريباً كما هي في الأخرى. انتبه إلى أنّه عند التعامل مع ملفّ تفريغ مُلتقَط على macOS، لا يكون WinDbg خياراً متاحاً، فيصبح dotnet-dump (أو LLDB) هو الخيار الوحيد.
8. لا شيء يبدأ دون قراءة الرموز
بعض الخطوات المذكورة حتّى الآن تعمل بشكل معقول حتّى دون تحميل الرموز (PDB) بشكل صحيح. كما كُتب في الفصل 4، تقرأ !clrstack و!dumpheap -stat و!gcroot بيانات CLR الوصفيّة وبيانات الكومة مباشرةً، لذا تُعرَض الأطر ومعلومات الأنواع نفسها حتّى دون PDB. ما يُفقَد بغياب PDB هو اسم ملفّ مصدر الكود المُدار ورقم السطر، واسم رمز الأطر والوحدات الأصليّة (يُعرَض العنوان فقط بدل اسم الدالّة). في المواقف التي لا يتوفَّر فيها سوى ملفّ التفريغ والملفّ التنفيذيّ، وتريد فيها فقط «فهم ما حدث في البداية»، لا حرج في البدء بـ !threads ← !clrstack قبل التعثّر في البحث عن PDB. لكن إن أردتَ التتبّع حتّى سطر المصدر لتحديد موضع السبب، فالأمر مختلف. أضفنا في الفصل 2 مسار PDB الخاصّ بالشركة إلى .sympath+، لكن في العمل الفعليّ، يحدث كثيراً أنّ مجرّد «عدم معرفة أين يوجد PDB المقابل لملفّ EXE/DLL الموزَّع» يوقف التحقيق دون الوصول إلى تحديد سطر المصدر، وذلك أكثر تواتراً ممّا كُتب في مقال الجمع.
بخصوص ما يحمله PDB نفسه وما لا يحمله، وPortable PDB، وSource Link (آليّة تُضمِّن بيانات إدارة المصدر الوصفيّة داخل التجميعة (assembly) وتتيح للمصحِّح جلب المصدر عند نقطة الالتزام (commit) المطابقة مباشرةً)، جمعناها كلّها في مقال واحد بعنوان «ما هو PDB».15 إذا أردتَ دمج تحليل ملفّات التفريغ في تشغيل مستمرّ، فإنّ حفظ PDB لكلّ عمليّة بناء وتفعيل Source Link أمر تحضيريّ لا يقلّ أهمّيّة عن إعداد الجمع نفسه. إهمال هذا يجعلك تتحسّس طريقك مستعيناً بالعناوين وأسماء الأنواع فقط دون أيّ ظهور لسطر المصدر في مخرَجات !clrstack.
9. مثال فعليّ ── تحليل ملفّ تفريغ في تحقيق تسريب مقابض (Handle)
المقال الذي كتبناه سابقاً «تحقيق في تعطّل كاميرا صناعيّة بعد تشغيل طويل - فصل تسريب المقابض» هو حالة تحقيق في تطبيق تحكّم بكاميرا صناعيّة يتعطَّل فجأة بعد تشغيل طويل، وكان الجاني الرئيسيّ تسريب مقابض (Handle) لا تسريب ذاكرة. يفيد ملفّ التفريغ في هذا النوع من التحقيق عند اجتماع العناصر التالية.
- التأكّد عبر
!dumpheap -statمن أنّ جانب الكومة المُدارة سليم (عدد الحالات وحجمها لكلّ نوع لا يتزايدان باستمرار) - إن كان عدد مقابض العمليّة وحدها هو المتزايد رغم ذلك، يمكن الفصل بأنّ المُسرَّب ليس كائناً مُداراً بل مقبض نظام تشغيل (OS Handle) (كالملفّات والأحداث والمقابض التي يحجزها SDK الكاميرا داخليّاً)
- إن بقي كائن غلاف (wrapper) مُدار يحمل
SafeHandle، تتبَّع جذر GC لذلك الغلاف عبر!gcroot، وحدِّد «الموضع الذي بقي فيه مرجع رغم أنّه كان ينبغي تحريره»
بعبارة أخرى، يعمل !dumpheap -stat كنقطة تفرّع للحكم على «هل الزيادة في الكومة المُدارة أم لا»، وحالما يتّضح أنّها ليست كذلك، ينتقل محور التحقيق إلى أدوات كشف الشذوذ عند الحدود الأصليّة مثل Application Verifier. تناولنا كيفيّة بناء أساس اختبار الحالات الشاذّة هذا في «بناء أساس اختبار الحالات الشاذّة لـ Windows باستخدام Application Verifier». تقسيم الأدوار هو أنّ تحليل ملفّ التفريغ يكشف «الحالة الحاصلة الآن»، بينما يجعل Application Verifier «الشذوذ يتكرّر مسبقاً»، والاستخدام المشترك بينهما هو القاعدة الثابتة في تحقيقات أعطال الأنظمة العاملة لفترات طويلة.
10. الخلاصة
«قراءة» ملفّ تفريغ الأعطال تحتاج وقتاً أطول للإتقان من «جمعه»، لكنّ الأنماط ليست كثيرة.
- ثبِّت WinDbg ومرِّر كلّاً من خادم الرموز العامّ لمايكروسوفت وPDB الخاصّ بالشركة إلى مسار الرموز (الفصل 2)
- حمِّل امتداد SOS إن كان تطبيق .NET (
.loadby sos clr/.loadby sos coreclr، أو التحميل التلقائيّ. الفصل 3) - ابدأ بـ
!clrstack←!peإن كان السبب استثناءً، و!dumpheap -stat←!gcrootإن كانت الذاكرة تتزايد، و!analyze -vإن كان عطلاً أصليّاً (الفصول 4 إلى 6) - إن كان الاستخدام تحقيقاً مُداراً خالصاً، ادرُس أيضاً
dotnet-dump analyzeالأخفّ (الفصل 7)
وأساس كلّ هذا هو إدارة PDB والرموز. إذا قرَّرتَ مع إعداد جمع ملفّات التفريغ حفظ PDB لكلّ عمليّة بناء وتفعيل Source Link في الوقت نفسه، فسيتغيَّر وقت التحقيق كثيراً عند وقوع عطل فعليّ. إن كان التحليل الداخليّ صعباً أو لا يتوفّر له وقت، يمكننا نحن إجراء التحليل إن أرسلتَ لنا مجموعة ملفّات التفريغ والسجلّات.
مقالات ذات صلة
- مدخل إلى جمع ملفّات تفريغ أعطال Windows - WER/ProcDump/WinDbg
- تصميم يترك السجلّات وملفّ التفريغ عند تعطّل تطبيقات Windows
- التمييز بين انتظار GC وتسريب الذاكرة في .NET
- ما هو PDB (قاعدة بيانات البرنامج)
- تحقيق في تعطّل كاميرا صناعيّة بعد تشغيل طويل - فصل تسريب المقابض
- بناء أساس اختبار الحالات الشاذّة لـ Windows باستخدام Application Verifier
مجالات الاستشارة ذات الصلة
تتعامل شركة Komura Soft LLC مع تحقيق أسباب الأعطال بالجمع بين ملفّات تفريغ الأعطال والسجلّات، وفصل الأعطال التي لا تظهر إلّا بعد تشغيل طويل، والاستشارة حول تصميم نظام حفظ وتحليل ملفّات التفريغ وPDB نفسه.
المراجع
-
Microsoft Learn، Install the Windows debugger. حول طريقة تثبيت WinDbg عبر winget / Microsoft Store، وأنظمة التشغيل المدعومة (Windows 10 1607 فما بعده وWindows 11) والمعماريّات (x64 وARM64)، وسلوك التحديث التلقائيّ. ↩ ↩2 ↩3
-
Microsoft Learn، Symbol path for Windows debuggers. حول طريقة ضبط مسار الرموز عبر متغيّر البيئة
_NT_SYMBOL_PATH، وإمكانيّة ضبط المسار الافتراضيّ إلى خادم الرموز العامّ عبر أمر.symfix. ↩ ↩2 -
Microsoft Learn، SOS debugging extension. حول إمكانيّة استخدام امتداد SOS لجمع معلومات الكومة المُدارة، وكشف تلف الكومة، وعرض أنواع البيانات الداخليّة لبيئة التشغيل، وأنّ الصيغة في WinDbg هي
![command]. ↩ ↩2 -
Microsoft Learn، Dump collection and analysis utility (dotnet-dump). حول أنّ
dotnet-dump analyzeيوفِّر جلسة تفاعليّة يمكن فيها استخدام أوامر SOS كما هي، لكن بما أنّه ليس مصحِّح أخطاء أصليّاً فلا يمكنه عرض أطر المكدّس الأصليّة، وأنّ دعم macOS يبدأ من .NET 5. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn، Microsoft public symbol server. حول صيغة مسار الرموز
srv*DownstreamStore*https://msdl.microsoft.com/download/symbols، والضبط مع ذاكرة تخزين مؤقّت محليّة عبر.symfix. ↩ -
Microsoft Learn، Debugging Managed Code Using the Windows Debugger. حول أنّ بيئة تشغيل .NET Framework هي
clr.dll، وبيئة تشغيل .NET Core/.NET 5+ هيcoreclr.dll، وتحميل الامتداد من مجاورة الوحدة عبر.loadby، والتحميل التلقائيّ في WinDbg من الإصدار 10.0.18317.1001 فما بعده. ↩ ↩2 -
Microsoft Learn، SOS installer (dotnet-sos). حول تثبيت امتداد SOS محليّاً عبر
dotnet-sos install، وأمر التحميل اليدويّ في إصدارات المصحِّح القديمة. ↩ -
Microsoft Learn، SOS debugging extension - Commands. حول أنّ أمر
Threads(اسمه المستعارclrthreadsفي بيئة lldb) يعرض قائمة تضمّ معرِّف كلّ خيط ونطاقه وآخر استثناء أُلقيَ فيه وغير ذلك. ↩ -
Microsoft Learn، SOS debugging extension - Commands. حول أنّ أمر
CLRStackيعرض تتبّع المكدّس للكود المُدار فقط، وأنّ خيار-aيعرض المتغيّرات المحليّة والمعطيات معاً، وأنّ الرموز (SYMOPT_LOAD_LINES) تؤثِّر فقط على إمكانيّة عرض اسم ملفّ المصدر ورقم السطر، ولا علاقة لها بعرض الإطار نفسه. ↩ ↩2 -
Microsoft Learn، SOS debugging extension - Commands. حول أنّ أمر
PrintException(اختصارهpe) يعرض عند حذف العنوان آخر استثناء أُلقيَ في الخيط الحاليّ، وأنّه يمكن عرض الاستثناءات المتداخلة أيضاً عبر-nested. ↩ -
Microsoft Learn، Debug a memory leak in .NET وDump collection and analysis utility (dotnet-dump) - Analyze memory leaks and allocations. حول عرض
dumpheap -statللإحصاء المُجمَّل حسب النوع (عدد الحالات والحجم الإجماليّ)، وطريقة تقدّم التحقيق انطلاقاً منه. ↩ ↩2 -
Microsoft Learn، SOS debugging extension - Commands. حول أنّ أمر
GCRootيبحث في الكومة المُدارة وجدول المقابض بأكملهما، ويكشف المراجع (الجذور) إلى الكائن المحدَّد. ↩ -
Microsoft Learn، Using the !analyze Extension و!analyze (WinDbg). حول التحليل التلقائيّ للتعطّل والاستثناء عبر
!analyze -v، ومعنى الحقول الواردة في المُخرَج مثلFAULTING_IPوMODULE_NAME، وأمر!analyze -hangالموجَّه لتحقيق التجمّد (hang). ↩ -
Microsoft Learn، Debug Linux dumps. حول إمكانيّة تحليل ملفّات تفريغ Linux على Windows بـ WinDbg أو dotnet-dump، وضرورة استخدام إصدار الأداة المطابق لعدد بتّات بيئة الجمع (x64/Arm64/x86). ↩ ↩2
-
Microsoft Learn، Source Link. حول آليّة تُضمِّن بيانات إدارة المصدر الوصفيّة داخل التجميعة عند إنشاء حزمة NuGet، وتتيح للمصحِّح الوصول مباشرةً إلى كود المصدر عند لحظة البناء. ↩
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
مدخل إلى سجلّ أحداث Windows وETW ── وضع سجلّات تطبيق الأعمال على آليّة نظام التشغيل القياسيّة
سجلّ الأحداث وETW طبقتان مختلفتان يراهما القائمون على التشغيل وأدوات نظام التشغيل القياسيّة. نشرح كيفيّة التمييز بين الوسائل الثلاث بما ف...
PDB (قاعدة بيانات البرنامج) ما هي؟ ── فهم معلومات التصحيح والرموز (symbols) وSource Link
نُنظّم من منظور عمليّ ما هو PDB (Program Database / قاعدة بيانات البرنامج)، وما الذي يحتويه وما لا يحتويه، والعلاقة بين Debug / Release، ...
تعدد اللغات في تطبيقات WinForms/WPF ── الممارسة العملية لملفات resx وتجميعات الأقمار الصناعية (Satellite Assemblies) وتبديل الثقافة (Culture)
نستعرض من منظور عملي تعدد اللغات في تطبيقات سطح المكتب على Windows، بما في ذلك الفرق بين CurrentCulture وCurrentUICulture، وآلية الموارد ...
ملف CSV ليس مجرد نص عادي ── الممارسة العملية لملفات CSV في تطبيقات C# (ترميز الأحرف وتوافق Excel والحماية من الحقن)
نستعرض بمنظور عملي الأنماط النموذجية للأعطال في إدخال وإخراج CSV بتطبيقات الأعمال ── التحليل اليدوي باستخدام Split(',')، وتشوّه النصوص في...
لا تُحِط HttpClient بـ using ── الممارسة العمليّة للاتّصال عبر HTTP في تطبيقات C# للأعمال (أنماط الإنشاء، وضبط المهلة، وإعادة المحاولة)
إنشاء HttpClient داخل using في كلّ مرّة يؤدّي إلى استنزاف المقابس (sockets)، وجعله static يجعله لا يتابع تغيّر DNS ── نشرح آليّة هاتين ال...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- بأيّ أمر ينبغي البدء في قراءة ملفّ تفريغ أعطال تطبيق .NET؟
- توجد ثلاثة أنماط للتحقيق. إن تعطَّل التطبيق باستثناء غير معالَج، حدِّد الخيط (thread) الحامل للاستثناء عبر !threads، ثمّ اعرض المكدّس (stack) المُدار عبر !clrstack، وتحقَّق من نوع كائن الاستثناء ورسالته والاستثناء الداخليّ عبر !pe. إن كان التحقيق في تزايد مستمرّ للذاكرة، حدِّد الأنواع (types) ذات الكمّيّة الكبيرة عبر !dumpheap -stat، ثمّ اكشف عبر !gcroot الجذور (roots) التي تُمسِك بتلك الكائنات (كالحقول الساكنة (static) أو اشتراكات معالِجات الأحداث). أمّا الأعطال الأصليّة (Native) فابدأ فيها بالتحليل التلقائيّ عبر !analyze -v.
- كيف يُحمَّل امتداد SOS في WinDbg؟
- بالنسبة لـ .NET Framework فالأمر هو .loadby sos clr، وبالنسبة لـ .NET Core/.NET 5+ فهو .loadby sos coreclr. يقوم WinDbg من الإصدار 10.0.18317.1001 فما بعده بتحميل SOS تلقائيّاً عند اكتشاف أنّ العمليّة المستهدَفة تحمِّل coreclr.dll. وفي البيئات التي لا يعمل فيها التحميل التلقائيّ، يمكن تثبيته محليّاً عبر أداة dotnet-sos وتحميله يدويّاً عبر .load. يُتحقَّق من نجاح التحميل بمعرفة ما إذا كانت !sos.help أو !Threads تُرجِعان معلومات دون خطأ.
- هل يمكن تحليل ملفّ التفريغ دون وجود PDB (الرموز)؟
- يمكن ذلك إلى حدّ ما. تقرأ !clrstack و!dumpheap -stat و!gcroot بيانات CLR الوصفيّة (metadata) وبيانات الكومة (heap) مباشرةً، لذا تُعرَض الأطر (frames) ومعلومات الأنواع حتّى دون PDB. ما يُفقَد هو اسم ملفّ المصدر ورقم السطر في الكود المُدار، واسم رمز الأطر الأصليّة (Native). إن أردتَ التتبّع حتّى سطر المصدر لتحديد موضع السبب، يلزم تمرير كلٍّ من خادم الرموز العامّ لمايكروسوفت وموقع PDB الخاصّ بالشركة إلى مسار الرموز. حفظ PDB لكلّ عمليّة بناء (build) وتفعيل Source Link أمران مهمّان تشغيليّاً.
- كيف نميّز بين استخدام dotnet-dump analyze وWinDbg؟
- المعيار هو: WinDbg إن كان يُحتمَل تدخّل COM أو P/Invoke أو DLL أصليّة، وdotnet-dump analyze إن كان التحقيق في كود مُدار خالص. يمكن تثبيت dotnet-dump بأمر واحد عبر dotnet global tool، ويمكن استخدام معظم أوامر SOS مثل clrstack وdumpheap كما هي، لكنّه لا يتعامل مع أطر المكدّس الأصليّة (Native) ولا يمكن استخدام !analyze -v فيه. ملفّات التفريغ المُلتقَطة على macOS لا يمكن تحليلها بـ WinDbg، لذا يكون الخيار الوحيد dotnet-dump أو LLDB. بما أنّ الأداتين تشتركان في نظام أوامر SOS، فإنّ الأوامر التي تتعلّمها في إحداهما تعمل تقريباً في الأخرى أيضاً.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.
روابط عامة