מבוא לאיסוף crash dump ב-Windows - WER, ProcDump, WinDbg
· עודכן בתאריך: · Go Komura · פיתוח Windows, חקירת תקלות, crash dump, WER, ProcDump, WinDbg
כשמתחילים לראות אפליקציית Windows ש”נופלת מדי פעם”, יש הרבה מצבים שאי אפשר לעקוב אחריהם רק עם לוגים.
בפרט קשים המקרים הבאים:
- קורה רק בסביבת הלקוח
- הודעת החריגה נתפסה, אבל חסר הקשר הקריאה
- מעורבים לא רק ה-managed בצד C# / .NET, אלא גם COM, P/Invoke, DLL נייטיבי, SDK של ספק חיצוני
- קורס רק אחרי הרצה ארוכה
במקרים כאלה, ה-crash dump יעיל. אם שומרים לקובץ את מצב התהליך ברגע הקריסה, אפשר לקרוא בדיעבד את קוד החריגה, את מחסנית ה-thread שקרס, את המודולים שהיו טעונים, ואת חלק מהזיכרון או כולו.
ב-Windows, קל יותר לחשוב בסדר הזה: קודם LocalDumps של WER, ואם צריך - ProcDump של Sysinternals, ואם רוצים שליטה נוספת - MiniDumpWriteDump. המאמר הזה מסדר את הצעד הראשון באיסוף crash dump, על בסיס אפליקציות שולחן עבודה ל-Windows, אפליקציות שרצות ברקע, שירותי Windows וכלי חיבור לציוד.
flowchart TB
accTitle: הסדר לחשיבה על אמצעי איסוף
accDescr: תרשים שמראה שבאיסוף crash dump ב-Windows, קל יותר לחשוב בסדר הזה - קודם LocalDumps של WER, ואם צריך ProcDump של Sysinternals, ואם רוצים שליטה נוספת MiniDumpWriteDump.
w1["קודם WER LocalDumps"] --> w2["אם צריך, ProcDump"]
w2 --> w3["ואם רוצים שליטה נוספת, MiniDumpWriteDump"]
איור 1: מתחילים מהפיצ’ר המובנה ומוסיפים כלים רק ככל שחסר.
מונחים שחוזרים לאורך המאמר הזה
לפני שממשיכים, נקבע אותם בקצרה. אם הם נשארים מעורפלים, הפרקים הבאים יהיו לא ברורים.
| מונח | משמעות |
|---|---|
| PDB | קובץ מידע ניפוי באגים שנוצר בזמן הבנייה. טבלת המרה שממירה כתובת לשם פונקציה או מספר שורה |
| סמלים (symbols) | מידע התאמה בין כתובת לשם. מסופק מ-PDB או משרת סמלים. בלעדיו, מחסנית הקריאות היא רשימת כתובות בלבד |
| first chance exception | השלב מיד אחרי שחריגה מתרחשת, לפני שה-handler של האפליקציה עיבד אותה. אם האפליקציה תופסת אותה ב-catch, העיבוד ממשיך |
| second chance exception | השלב שבו האפליקציה לא הצליחה לטפל בחריגה, והתהליך בדרך לסיום כחריגה לא מטופלת. בדרך כלל זה מה שמכונה “נפל” |
| postmortem debugger | debugger שה-OS מפעיל אוטומטית בזמן קריסה. נרשם כהתנהגות של המחשב כולו בזמן קריסה |
| minidump / full dump | ההבדל בכמות הזיכרון שנכלל ב-dump. מפורט בפרק 7 |
1. קודם כל - המסקנה
הנה קודם כל הנקודות החשובות ביותר.
- ראשית, בטוח יותר להגדיר את WER LocalDumps לפי אפליקציה. בלי כלי נוסף, אפשר להשאיר dump מקומי אחרי קריסה.
- בחקירה בשטח עם שיעור שחזור נמוך, או כשרוצים לראות גם first chance exception / hang, משתמשים ב-ProcDump.
- איסוף עצמאי כדאי לשקול אחרון. מספיק לשקול
MiniDumpWriteDumpרק כשמתעורר צורך אמיתי. - חשוב לא פחות מה-dump עצמו הוא שמירת ה-PDB והקבצים הבינאריים המופצים. גם אם יש dump, בלי סמלים כמות המידע הקריא קטנה משמעותית.
- full dump חזק, אבל גם הגודל וסיכון דליפת מידע רגיש חזקים. חשוב לקבוע מראש מיקום אחסון, מספר שמורים, הרשאות גישה, ונוהל שיתוף.
ההרכב המומלץ לשלב ההתחלה מתמצה בערך כך.
| סביבה | ההרכב הראשוני |
|---|---|
| מחשב פיתוח / בדיקה | הגדרת WER LocalDumps לפי אפליקציה, בהתחלה DumpType=2 שהוא full dump |
| סביבת לקוח / מחשב בשטח | בחירה בין DumpType=1 ל-2 לפי היקף וצורכי סודיות. הוספת ProcDump רק במקרה הצורך |
| הרצה ארוכה או חקירת hang | בנוסף ל-WER, לשקול -h או -e 1 של ProcDump |
| רוצים לכלול UI ייחודי או לוגים מצורפים | איסוף עצמאי עם MiniDumpWriteDump, בהנחה של תהליך נפרד |
בקיצור: קודם WER, אחר כך ProcDump, ולבסוף איסוף עצמאי. אם מתחילים בסדר הפוך, בדרך כלל התכנון מכביד.
מפת הידע של המאמר
באיסוף crash dump באפליקציית Windows, קודם מכניסים את WER LocalDumps שניתן להגדיר לפי אפליקציה בלי כלי נוסף, מוסיפים ProcDump כשרוצים לראות גם hang או first chance exception או לנטר תהליך שכבר רץ, ורק כשעולה צורך בפונקציית אבחון עצמאית שוקלים איסוף עצמאי עם MiniDumpWriteDump - זה הסדר הבטוח. סוג ה-dump כרוך בפשרה בין עומק המידע לגודל בין minidump ל-full dump, ותהליך CLR של .NET נאסף כ-full גם בציון MiniPlus של ProcDump. בלי בדיקת הרשאת הכתיבה לתיקיית היעד, האיסוף עצמו נכשל בשקט, וה-dump שהתקבל הופך למידע קריא רק בשילוב עם PDB וניתוח ב-WinDbg.
flowchart LR
accTitle: מפת הידע של איסוף crash dump ב-Windows (WER / ProcDump / WinDbg)
accDescr: תרשים שמראה ש-WER LocalDumps, ProcDump ו-MiniDumpWriteDump הם אמצעי איסוף crash dump שכדאי לשקול בסדר הזה, ושבחירת סוג ה-dump, בדיקת ה-ACL של יעד השמירה, וניתוח עם WinDbg יחד עם PDB הם רצף אחד.
wer_localdumps["WER LocalDumps"]
procdump["ProcDump"]
windbg["WinDbg"]
minidumpwritedump["MiniDumpWriteDump"]
crash_dump["crash dump (תמונת קריסה)"]
minidump["minidump"]
fulldump["full dump"]
miniplus_dump["MiniPlus dump"]
native_boundary_crash_investigation["חקירת הסיבה בגבול הנייטיב"]
dump_folder_acl["ACL של תיקיית שמירת ה-dump"]
first_chance_exception["first chance exception"]
second_chance_exception["second chance exception"]
hang_detection["זיהוי תקיעה"]
postmortem_debugger["Postmortem debugger"]
pdb["PDB (מסד נתוני התוכנית)"]
custom_diagnostic_report["מנגנון עצמי לשמירת מידע אבחוני"]
notmyfault["NotMyFault"]
wer_localdumps -->|"צריך לקדום ל"| procdump
procdump -->|"צריך לקדום ל"| minidumpwritedump
wer_localdumps -->|"מממש את"| crash_dump
procdump -->|"מממש את"| crash_dump
wer_localdumps -.->|"משתמש ב"| minidump
wer_localdumps -.->|"משתמש ב"| fulldump
procdump -.->|"משתמש ב"| fulldump
procdump -.->|"משתמש ב"| miniplus_dump
fulldump -->|"מענה מומלץ ל"| native_boundary_crash_investigation
minidump -->|"שימוש לא מומלץ ל"| native_boundary_crash_investigation
wer_localdumps -.->|"מחייב"| dump_folder_acl
procdump -.->|"משתמש ב"| first_chance_exception
procdump -.->|"משתמש ב"| second_chance_exception
procdump -.->|"משתמש ב"| hang_detection
wer_localdumps -->|"שימוש לא מומלץ ל"| hang_detection
procdump -.->|"מממש את"| postmortem_debugger
wer_localdumps -->|"צריך לקדום ל"| postmortem_debugger
crash_dump -.->|"מחייב"| pdb
crash_dump -->|"נבדק באמצעות"| windbg
minidumpwritedump -->|"מענה מומלץ ל"| custom_diagnostic_report
notmyfault -->|"שימוש לא מומלץ ל"| wer_localdumps
בתרשים, קו מלא מציין קשר שמתקיים תמיד וקו מקווקו מציין קשר מותנה (תנאי ההתקיימות מפורטים בהסבר של כל קשר בעמוד המפורט). רשימת כל הקשרים (סך הכול 21, עם אסמכתה ורמת ודאות) והגדרות המושגים המרכזיים מרוכזות בעמוד המפורט של מפת הידע (ביפנית). נתונים: JSON-LD / Turtle
2. מה אפשר ללמוד מ-crash dump
crash dump הוא “תצלום סטילס של הרגע ההוא”. יותר קרוב לתצלום זירת תאונה מאשר למצלמת אבטחה.
לכן, המידע הבא בדרך כלל קל למדי להשגה.
- באיזה קוד חריגה קרס
- איזה thread קרס
- מחסנית הקריאות באותו רגע
- אילו מודולים היו טעונים
- בהתאם לכמות הזיכרון שנכלל, מצב ה-heap ותוכן האובייקטים
מצד שני, יש דברים שה-dump לבדו נוטה שלא לספק.
- ציר הזמן שהוביל לשם
- מגמת עלייה מכמה שעות קודם
- מצב חיצוני של תקשורת או ציוד
- הקלט או ההקשר העסקי שקדם לכך
לכן, בפועל, הבסיס הוא לא לנסות להסתפק ב-dump לבדו, אלא לשלב אותו עם לוגים ו-heartbeat.
flowchart TB
accTitle: שילוב dump ולוגים
accDescr: תרשים שמראה ש-crash dump חזק במיוחד במצב הרגע עצמו, כמו תצלום זירת תאונה, ולוגים ו-heartbeat משלימים את ציר הזמן והמצב החיצוני, ולכן הבסיס הוא לשלב את שניהם בחקירה.
d1["crash dump (תצלום סטילס)"] --> mix["חוקרים בשילוב"]
d2["לוגים ו-heartbeat (ציר זמן)"] --> mix
d1 -.-> s1["חזק בקוד חריגה ובמחסנית"]
d2 -.-> s2["חזק בהתקדמות עד לרגע ההוא"]
איור 2: ה-dump שהוא תצלום סטילס והלוג שהוא ציר זמן חזקים בתחומים שונים לגמרי.
3. מבט כללי על שיטות האיסוף
ב-Windows, ארבע השיטות שכדאי להכיר בשלב ההתחלה לאיסוף dump הן אלה.
| שיטה | מתאימה למקרה | חוזקה | נקודה לתשומת לב |
|---|---|---|---|
| WER LocalDumps | איסוף קריסות שרוצים ראשית להשאיר קבוע | תקן של Windows. נוח להגדרה לפי אפליקציה | בעיקר מתאים לקריסות. חלש ב-hang ובתנאים עדינים |
| ProcDump | חקירה עם שיעור שחזור נמוך, hang, first chance exception | טריגרים רבים. נוח להפעיל בשטח | דורש הפעלת כלי חיצוני |
| יצירת dump מ-Task Manager | רוצים לתפוס ידנית את המצב הנוכחי | ניתן לתפוס באופן מיידי דרך GUI | לא איסוף אוטומטי |
MiniDumpWriteDump |
רוצים לבנות פונקציית אבחון עצמאית | קל לשלב לוגים מצורפים ומטא-דאטה ייחודית | מימוש רשלני עלול לגרום לתקלה נוספת |
מה שהכי חשוב למתחילים הוא לקבוע קודם באילו תנאים, לאן, ובאיזה גודל אוספים - לפני שבוחרים “עם מה” אוספים.
flowchart TB
accTitle: מה שקובעים לפני בחירת הכלי
accDescr: תרשים שמראה שבאיסוף crash dump חשוב לקבוע קודם באילו תנאים, לאן, ובאיזה גודל אוספים, ורק אחר כך לעבור לבחירת הכלי.
c1["באילו תנאים אוספים"] --> firstq["3 דברים שקובעים קודם"]
c2["לאן מוציאים"] --> firstq
c3["באיזה גודל"] --> firstq
firstq --> tool["ורק אז בוחרים כלי"]
איור 3: אחרי שקובעים תנאים, יעד וגודל, בחירת הכלי כבר לא מבלבלת.
4. ההמלצה הראשונה - WER LocalDumps
4.1 ערכי הרישום הראשונים לבדיקה
ל-Windows Error Reporting (WER) יש פיצ’ר בשם LocalDumps, ששומר dump ברמת user mode לוקלית אחרי קריסה. מכיוון שלא צריך להפיץ כלי נוסף, זה נוח למדי כצעד ראשון.
המפתח הבסיסי הוא כאן.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps
אפשר להניח כאן הגדרה גלובלית, אבל בפועל נוח יותר לרכז אותה במפתח משנה לפי אפליקציה.
flowchart TB
accTitle: היכן ממקמים את הגדרת LocalDumps
accDescr: תרשים שמראה שמתחת למפתח LocalDumps אפשר להניח הגדרה גלובלית, אבל בפועל נוח יותר לרכז אותה במפתח משנה לפי אפליקציה כמו MyApp.exe.
key1["מפתח LocalDumps"] --> g1["הגדרה גלובלית ישירות תחתיו"]
key1 --> a1["מפתח משנה לפי אפליקציה"]
a1 -.-> a2["נוח יותר בפועל"]
איור 4: אותו מפתח, אבל ריכוז לפי מפתח משנה של שם האפליקציה מצמצם את היקף ההשפעה.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\MyApp.exe
שלושת הערכים הראשונים לבדיקה הם:
| ערך | משמעות | המלצה ראשונית |
|---|---|---|
DumpFolder |
יעד פלט ה-dump | להקצות תיקייה ייעודית |
DumpCount |
מספר שמורים | להתחיל מ-5 עד 10 |
DumpType |
0=מותאם אישית, 1=מיני, 2=מלא | בהתחלה 2, ואם הגודל בעייתי - 1 |
4.2 דוגמה להגדרה לפי אפליקציה
לדוגמה, עבור MyApp.exe, אם רוצים להשאיר עד 10 dump מלאים תחת C:\CrashDumps\MyApp, אפשר להגדיר כך בהתחלה.
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\MyApp.exe" /f
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\MyApp.exe" /v DumpFolder /t REG_EXPAND_SZ /d "C:\CrashDumps\MyApp" /f
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\MyApp.exe" /v DumpCount /t REG_DWORD /d 10 /f
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\MyApp.exe" /v DumpType /t REG_DWORD /d 2 /f
הדוגמה הזו כוללת ארבע נקודות חשובות.
- מוגבל ל-
MyApp.exeולא גלובלי - יעד הפלט מופרד לתיקייה ייעודית
- בהתחלה full dump
- מספר השמורים מוגבל ל-10
4.3 בדיקה שההגדרה עובדת
אחרי שמכניסים את ההגדרה, בטוח יותר לוודא לפחות פעם אחת בסביבת בדיקה לפני שממתינים להתרחשות טבעית בייצור.
4 הדברים שכדאי לבדוק:
- האם
.dmpנוצר בתיקייה המצופה - האם הגודל תואם את התכנון התפעולי
- האם ניתן לפתוח ב-WinDbg
- האם ה-crash נראה בלוג ה-Application ב-Event Viewer
flowchart TB
accTitle: זרימת האימות אחרי ההגדרה
accDescr: תרשים שמראה שאחרי הכנסת ההגדרה, לפני ההמתנה להתרחשות טבעית בייצור, בודקים פעם אחת בסביבת בדיקה - מפילים בכוונה, בודקים אם ה-dump וגודלו כמצופה, בודקים פתיחה ב-WinDbg, ובודקים את הלוג ב-Event Viewer.
v1["מכניסים את ההגדרה"] --> v2["מפילים בכוונה בסביבת בדיקה"]
v2 --> v3["בודקים אם ה-dump נוצר ומה גודלו"]
v3 --> v4["בודקים אם ניתן לפתוח ב-WinDbg"]
v4 --> v5["בודקים את הלוג ב-Event Viewer"]
איור 5: לפני ההמתנה להתרחשות טבעית בייצור, מוודאים פעם אחת בסביבת בדיקה.
4.4 קוד מינימלי להפלה מכוונת
גם אם אומרים “לוודא”, אי אפשר לחכות לקריסה אמיתית - זה לא בדיקה ממשית. נוח להכין מראש EXE קטן שמפיל את עצמו בכוונה לצורך הבדיקה.
ב-.NET, מספיקה אפליקציית console שרק זורקת חריגה לא מטופלת. חריגה לא מטופלת מ-managed ב-.NET מסיימת את התהליך, ולכן היא הופכת אוטומטית ליעד עבור WER.
// CrashTest.csproj: <TargetFramework>net8.0</TargetFramework>
using System;
using System.Threading;
internal static class Program
{
private static void Main()
{
Console.WriteLine($"PID={Environment.ProcessId} / 3 秒後に落とします。");
Thread.Sleep(3000);
throw new InvalidOperationException("intentional crash for dump collection test");
}
}
אם רוצים לבדוק את הצד הנייטיבי, קרוב יותר לגרום ל-access violation (0xC0000005). מוסיפים volatile כדי שהאופטימיזציה לא תמחק את הפעולה.
// crash_test.cpp / C++17 / MSVC
int main()
{
volatile int* p = nullptr;
*p = 1; // כאן מתרחש STATUS_ACCESS_VIOLATION
return 0;
}
יש נקודה אחת שקל לפספס כאן.
שם מפתח המשנה של LocalDumps חייב להיות זהה לשם קובץ ה-EXE שמפילים כעת. אם יוצרים רק את המפתח של MyApp.exe ומפילים את CrashTest.exe, ברור שלא ייווצר dump. בזמן הבדיקה, או יוצרים מפתח זמני עבור CrashTest.exe, או בודקים דרך ההגדרה הגלובלית.
flowchart TB
accTitle: המלכודת של התאמת שם מפתח המשנה
accDescr: תרשים שמראה ששם מפתח המשנה של LocalDumps חייב להתאים לשם קובץ ה-EXE שמפילים בפועל, ולכן אם יוצרים רק את המפתח של MyApp.exe ומפילים CrashTest.exe, לא ייווצר dump, ובזמן הבדיקה יוצרים מפתח זמני או בודקים דרך ההגדרה הגלובלית.
t1["יוצרים רק את המפתח של MyApp.exe"] --> t2["מפילים את CrashTest.exe"]
t2 --> t3["לא נוצר dump"]
t3 -.-> t4["מתאימים את שם המפתח לשם ה-EXE שמפילים"]
איור 6: פער בין שם מפתח המשנה לשם ה-EXE הוא מלכודת נפוצה שגורמת לבדיקה להיכשל בשקט.
גם NotMyFault של Sysinternals עולה לעיתים קרובות ככלי “להפיל בכוונה”, אבל הוא מיועד להפיל או לגרום ל-hang למערכת Windows עצמה כדי ליצור dump של מסך כחול, ודורש הרשאת מנהל. עבור אימות LocalDumps של אפליקציית user mode, EXE קטן עצמאי כמו למעלה בטוח וודאי יותר.
5. מקרים שבהם משתמשים ב-ProcDump
לעיתים קרובות WER מספיק, אבל יש גם מקרים שבהם ProcDump שימושי.
- רוצים להימנע ממצב קבוע ברישום
- רוצים לנטר רק תהליך שכבר רץ
- רוצים לנטר רק מההפעלה הבאה ואילך
- רוצים לראות first chance exception
- רוצים לתפוס hang
- רוצים לאסוף לפי מונה ביצועים או תנאי מסוים
5.1 האפשרויות הנפוצות
אם מצמצמים לאלה שנפוצות בשלב ההתחלה, מספיק לזכור את אלה כדי להתמודד עם רוב המצבים ב-ProcDump.
| אפשרות | משמעות |
|---|---|
-ma |
full dump |
-mp |
MiniPlus dump |
-mc <Mask> |
dump מותאם אישית. מציינים bitmask הקסדצימלי של MINIDUMP_TYPE |
-e |
dump בחריגה לא מטופלת |
-e 1 |
dump בחריגת first chance / second chance |
-h |
dump בחלון שנתקע |
-w |
המתנה להפעלת התהליך היעד |
-x |
הפעלת התהליך היעד וניטורו |
-n |
מספר ה-dump המרבי |
-accepteula |
אישור אוטומטי של אישור ה-EULA הראשוני |
5.2 דוגמאות פקודה נפוצות
תהליך שכבר רץ, full dump בחריגה לא מטופלת
procdump -accepteula -ma -e 1234 C:\CrashDumps\MyApp
המתנה להפעלה הבאה, full dump בחריגה לא מטופלת
procdump -accepteula -ma -e -w MyApp.exe C:\CrashDumps\MyApp
מפעילים בעצמנו וממשיכים לנטר
procdump -accepteula -ma -e -x C:\CrashDumps\MyApp MyApp.exe
רוצים לתפוס גם first chance exception
procdump -accepteula -ma -n 3 -e 1 MyApp.exe C:\CrashDumps\MyApp
רוצים לתפוס hang
procdump -accepteula -h MyApp.exe C:\CrashDumps\MyApp
5.3 למה -i הוא לא הצעד הראשון
ל-ProcDump יש גם שימוש שבו רושמים אותו כ-postmortem debugger עם -i. זה חזק, אבל חודר להתנהגות המחשב כולו בזמן קריסה, ולכן זה כבד מדי כצעד ראשון בשלב ההתחלה.
לכן, נוח יותר להתחיל מ-הגדרת WER לפי אפליקציה, או מ--w / -x / ציון PID של ProcDump.
flowchart TB
accTitle: למה -i לא הצעד הראשון
accDescr: תרשים שמראה שה-i- של ProcDump הוא שימוש חזק שרושם postmortem debugger, אבל הוא חודר להתנהגות המחשב כולו בזמן קריסה, ולכן בשלב ההתחלה נוח יותר להתחיל מהגדרת WER לפי אפליקציה או מ-w, x, וציון PID של ProcDump.
i1["רישום עם -i של ProcDump"] --> i2["חודר להתנהגות המחשב כולו"]
i2 -.-> i3["כבד מדי לצעד הראשון בשלב ההתחלה"]
i4["מתחילים בהיקף של אפליקציה בודדת"] --> i5["הגדרת WER לפי אפליקציה"]
i4 --> i6["-w, -x, או ציון PID של ProcDump"]
איור 7: מתחילים מכניסה שהיקף השפעתה מוגבל לאפליקציה בודדת.
6. איך חושבים על שימוש ב-MiniDumpWriteDump לאיסוף עצמאי
איסוף עצמאי מתאים למקרים כמו אלה.
- רוצים כפתור “שמור מידע אבחוני” מה-UI
- רוצים לצרף יחד עם ה-dump גם לוגים, הגדרות ו-trace ID
- רוצים לרכז גם תהליכי בן או תהליכי עזר קשורים
- רוצים להוסיף מיסוך או דחיסה ייחודיים לפני העלאה
ה-API המרכזי כאן הוא MiniDumpWriteDump.
עם זאת, יש כאן קצת אופי מיוחד. שתי הנקודות שהכי חשוב לא לפספס בהתחלה הן:
- אם אפשר, לקרוא מתהליך נפרד מזה שנוצר ממנו ה-dump
- להתייחס למשפחת ה-API של DbgHelp כמניחה single-thread
flowchart TB
accTitle: שתי הנקודות שלא כדאי לפספס באיסוף עצמאי
accDescr: תרשים שמראה שבאיסוף עצמאי עם MiniDumpWriteDump, שתי הנקודות שחשוב לא לפספס הן לקרוא אם אפשר מתהליך נפרד, ולהתייחס למשפחת DbgHelp כמניחה single-thread.
md1["איסוף עצמאי עם MiniDumpWriteDump"] --> md2["קוראים מתהליך נפרד"]
md1 --> md3["DbgHelp מניח single-thread"]
md2 -.-> md4["מימוש רשלני גורם לתקלה"]
md3 -.-> md4
איור 8: האופי המיוחד של איסוף עצמאי מתרכז בשתי נקודות - מאיפה קוראים ואיזה thread.
7. איך בוחרים בין minidump, full dump וגודל ביניים
זו נקודה שהרבה אנשים מתלבטים בה. הנה טבלה לבחירה מעשית.
| סוג | מתאים למקרה | יתרון | נקודה לתשומת לב |
|---|---|---|---|
| minidump | רוצים להטמיע קודם באופן רחב, רוצים לשתף בקלות | קטן, קל להעברה | חלש בשחזור עומק המצב |
| full dump | רוצים לתת עדיפות לחקירת הסיבה, חשד בגבול נייטיב או ב-heap | הרבה מידע נגיש | גדול, סיכון דליפת מידע רגיש גבוה |
| MiniPlus / Custom | mini לא מספיק, full כבד מדי | איזון בין השניים | דורש ידע לכוונון |
ההמלצה למתחילים די פשוטה.
- במחשב פיתוח / בדיקה - full dump
- בסביבת הלקוח - בוחרים בין mini ל-full לפי תנאי התפעול
- כשיש חשד להשחתת זיכרון, DLL נייטיבי, COM, P/Invoke, או תקלת מצב אחרי הרצה ארוכה, עדיף לנטות לכיוון full
flowchart TB
accTitle: הבחירה הראשונית בסוג ה-dump
accDescr: תרשים שמראה שבמחשב פיתוח או בדיקה בוחרים full dump, בסביבת הלקוח בוחרים בין mini ל-full לפי תנאי התפעול, וכשיש חשד להשחתת זיכרון, גבול נייטיב או תקלת מצב אחרי הרצה ארוכה - נוטים לכיוון full.
e1{"באיזו סביבה אוספים"}
e1 -->|"מחשב פיתוח / בדיקה"| f1["full dump"]
e1 -->|"סביבת לקוח"| f2["mini או full לפי תנאי תפעול"]
f2 -.->|"חשד להשחתה או גבול נייטיב"| f1
איור 9: כשמתלבטים, קובעים לפי הסביבה, וככל שהחשד חזק יותר - נוטים ל-full.
7.1 איך בפועל מציינים MiniPlus / Custom
רק לגבי השורה השלישית בטבלה, אופן הציון קצת פחות ברור, ולכן נוסיף הבהרה.
במקרה של WER LocalDumps, קובעים את DumpType ל-0 (מותאם אישית), ומכניסים ל-CustomDumpFlags שילוב סיביות של MINIDUMP_TYPE. CustomDumpFlags נבדק רק כש-DumpType=0, וברירת המחדל היא 0x00000121 (שילוב של MiniDumpWithDataSegs, MiniDumpWithUnloadedModules ו-MiniDumpWithProcessThreadData).
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\MyApp.exe" /v DumpType /t REG_DWORD /d 0 /f
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\MyApp.exe" /v CustomDumpFlags /t REG_DWORD /d 0x121 /f
במקרה של ProcDump, -mp הוא MiniPlus, ו--mc <Mask> הוא מותאם אישית.
procdump -accepteula -mp -e MyApp.exe C:\CrashDumps\MyApp
MiniPlus, למרות שמו, בתוכן שלו קרוב למדי ל-full. לפי התיעוד, הוא כולל את כל הזיכרון הפרטי (private), וגם את כל ה-image / mapped memory שהוא read/write, ומעליהם מוציא רק את שטח הזיכרון הפרטי המקסימלי אם הוא עולה על 512MB, כדי לצמצם את הגודל. התוצאה היא מצב שבו “מפורט כמו full dump, אבל בגודל של 10%-75% ממנו”.
עם זאת, יש שתי הערות.
- בתהליכי CLR, בגלל מגבלות בניפוי באגים, גם ציון
-mpיגרום לאיסוף כ-full (-ma). לצפות לצמצום גודל דרך MiniPlus באפליקציית .NET לרוב לא יתממש - אם המניע לצמצום הגודל הוא “רוצים להפחית דליפת מידע רגיש”, עדיף לחשוב לכיוון minidump ולא לכיוון MiniPlus - זה ישיר יותר
flowchart TB
accTitle: המיקום והמגבלה של MiniPlus
accDescr: תרשים שמראה ש-MiniPlus כולל כמעט את כל הזיכרון הפרטי, מוציא רק אזורי זיכרון פרטי ענקיים, ולכן קטן ומפורט יותר מ-full, אבל בתהליכי CLR הוא נאסף כ-full בכל זאת.
mp1["איסוף עם MiniPlus"] --> mp2["כולל כמעט את כל הזיכרון הפרטי"]
mp2 --> mp3["מוציא רק אזורי זיכרון פרטי ענקיים"]
mp3 --> mp4["קטן יותר ומפורט יותר מ-full"]
mp1 -.->|"במקרה של תהליך CLR"| mp5["נאסף בפועל כ-full"]
איור 10: תוכן MiniPlus קרוב ל-full, ובאפליקציית .NET לא מקבלים את צמצום הגודל.
8. דברים שכדאי לקבוע מראש בתפעול
איסוף dump נוטה להיכשל בגלל תפעול הרבה יותר מאשר בגלל המימוש. הנה הדברים שכדאי לקבוע מראש.
8.1 איך שומרים PDB וקבצים בינאריים
זו הנקודה הכי חשובה.
- הגרסה המדויקת של ה-EXE / DLL שהופצו
- ה-PDB התואם לגרסה הזו
- ה-commit ה-pipeline של הבנייה שממנו נוצר
- מידע גרסה של המתקין וקבצי ההפצה
8.2 לאן מוציאים וכמה שומרים
full dump יכול להיות גדול למדי. בטוח יותר לקבוע מראש את יעד הפלט ואת מדיניות השמירה.
- לא להשאיר אחסון ישירות תחת כונן המערכת
- להפריד לתיקייה ייעודית
- להגביל עם
DumpCountאו-n - להפריד בין אחסון ראשוני לאחסון ארוך טווח
8.3 מי רשאי לצפות
ל-full dump יש סיכוי שיתערבב בו מידע רגיש או מידע אישי.
- הגדרות ב-plaintext
- connection string
- token או פרטי הזדהות
- מידע עסקי שטופל ממש לפני כן
- נתיבי קבצים או שמות משתמשים
לכן, יש צורך לקבוע גם “מי רשאי לגעת” יחד עם תכנון “האיסוף” עצמו.
flowchart TB
accTitle: 3 דברים שקובעים מראש בתפעול
accDescr: תרשים שמראה שבאיסוף crash dump, שנוטה להיכשל בגלל תפעול יותר מאשר בגלל מימוש, קובעים מראש את שמירת ה-PDB והקבצים הבינאריים, את יעד הפלט ומספר השמורים, ואת מדיניות הגישה של מי רשאי לצפות.
op1["שמירת PDB וקבצים בינאריים"] --> op4["קובעים מראש"]
op2["יעד פלט ומספר שמורים"] --> op4
op3["מי רשאי לצפות"] --> op4
op4 -.-> op5["נוטה להיכשל יותר בתפעול מאשר במימוש"]
איור 11: כישלון באיסוף dump נובע לרוב מהיעדר החלטות תפעוליות מראש.
9. מסלול הניתוח המהיר ביותר אחרי שהתקבל dump
אחרי שמקבלים dump, מה שעושים ראשית פשוט למדי.
9.1 מתקינים את WinDbg
היום קל להתקין את WinDbg דרך Microsoft Store או winget.
winget install Microsoft.WinDbg
9.2 פותחים את ה-dump
windbg -z C:\CrashDumps\MyApp\MyApp_YYMMDD_HHMMSS.dmp
שם הקובץ כאן הוא השם שברירת המחדל של ProcDump נותנת. שם ברירת המחדל של ProcDump הוא PROCESSNAME_YYMMDD_HHMMSS.dmp, ואפשר להשתמש כמציין החלפה ב-PROCESSNAME, PID, EXCEPTIONCODE, YYMMDD, HHMMSS.
מצד שני, WER LocalDumps יוצר קבצים עם שם שונה מ-ProcDump. כללי השם לא מפורטים ב-Microsoft Learn, ולכן במקום לנחש את השם, ודאי יותר להסתכל על תיקיית הפלט ממוינת לפי תאריך עדכון.
dir /o-d "C:\CrashDumps\MyApp\*.dmp"
אם לא הגדרתם DumpFolder, יעד ברירת המחדל הוא %LOCALAPPDATA%\CrashDumps. עם זאת, קריסה של שירות יוצאת לתיקיית הפרופיל של חשבון ההרצה. עבור שירות System, זה %WINDIR%\System32\Config\SystemProfile, ועבור Network Service / Local Service, זה תחת %WINDIR%\ServiceProfiles. אם נדמה ש”ה-dump לא נוצר”, זה המקום הראשון לחשוד בו.
flowchart TB
accTitle: איפה מחפשים כשה-dump לא נמצא
accDescr: תרשים שמראה שכשלא הגדירו DumpFolder, ברירת המחדל היא CrashDumps תחת LOCALAPPDATA, וקריסה של שירות יוצאת לתיקיית הפרופיל של חשבון ההרצה, ולכן כשה-dump לא נמצא, שם הוא המקום הראשון לחשוד בו.
fq1["ה-dump לא נמצא"] --> fq2{"באיזו צורה זה רץ"}
fq2 -->|"אפליקציה רגילה"| fp1["CrashDumps תחת LOCALAPPDATA"]
fq2 -->|"שירות"| fp2["תיקיית הפרופיל של חשבון ההרצה"]
fp2 -.-> fp3["SystemProfile או ServiceProfiles"]
איור 12: יעד ברירת המחדל שונה לפי החשבון, ולכן חושדים קודם בהיכן מחפשים.
9.3 מגדירים סמלים
קודם מפעילים גישה לסמלים הציבוריים של Microsoft, ואז מוסיפים את מיקום ה-PDB שלכם.
.symfix C:\Symbols\Microsoft
.sympath+ C:\Symbols\MyApp
.reload
9.4 קודם רואים את הניתוח האוטומטי
!analyze -v
ואז, בודקים לפי הסדר:
- מה קוד החריגה
- מהו ה-faulting module
- עד כמה הקוד שלכם נראה במחסנית
- האם יש המתנה או חסימה חשודה בשרשורים אחרים חוץ מה-thread של החריגה
flowchart TB
accTitle: מסלול הניתוח המהיר ביותר אחרי שהתקבל dump
accDescr: תרשים שמראה שמתקינים WinDbg, פותחים את ה-dump, מגדירים סמלים ציבוריים ואת ה-PDB שלכם, ואז רואים ראשית את הניתוח האוטומטי של analyze -v, ואחר כך בודקים בסדר את קוד החריגה, את ה-faulting module, את הופעת הקוד שלכם ואת החסימות ב-threads אחרים.
an1["מתקינים WinDbg"] --> an2["פותחים את ה-dump"]
an2 --> an3["מגדירים סמלים ו-PDB"]
an3 --> an4["קודם רואים ניתוח אוטומטי"]
an4 --> an5["קוראים בסדר את קוד החריגה ואת המחסנית"]
איור 13: פתיחה, מעבר דרך הסמלים, והתחלה בקריאת הניתוח האוטומטי - זהו המסלול המהיר ביותר.
10. מוקשים נפוצים
10.1 ה-dump התקבל, אבל אין PDB
זה קורה למדי הרבה. גם אם איסוף ה-dump הצליח, חומר הקריאה חסר. עדיף להכניס את תכנון שמירת ה-PDB באותו זמן שמכניסים את הגדרת האיסוף.
10.2 לא בודקים את ה-ACL של DumpFolder
בשירותים ותהליכים עם הפרדת הרשאות, קל שכאן ייכשל בשקט. קודם מוודאים אם “התהליך הזה באמת יכול לכתוב”. גם Microsoft Learn כותב שאם משתמשים בנתיב שאינו ברירת המחדל, יש לוודא ש”ל-ACL יש הרשאה שהתהליך שקרס יכול לכתוב אליה”.
את ה-ACL הנוכחי אפשר לראות עם icacls.
icacls C:\CrashDumps\MyApp
אם חסרה הרשאת כתיבה, מוסיפים M (שינוי) לחשבון ההרצה. תיקיית ה-dump זקוקה לאותה הרשאה גם עבור הקבצים תחתיה, ולכן מוסיפים (OI) ו-(CI) כדי שההרשאה תעבור בירושה.
rem לדוגמה: שירות שרץ תחת Network Service
icacls C:\CrashDumps\MyApp /grant "NT AUTHORITY\NETWORK SERVICE:(OI)(CI)M"
(OI) הוא ציון להעביר את ה-ACE בירושה לקבצים תחתיה, ו-(CI) להעביר בירושה לתיקיות תחתיה. היקף הירושה הוא בעצם “מי יכול לקרוא את ה-dump”, ולכן לפני שמוסיפים אותה כדאי לוודא שוב שהיא לא סותרת את המדיניות שנקבעה בפרק 8.3.
flowchart TB
accTitle: זרימת בדיקת ה-ACL של DumpFolder
accDescr: תרשים שמראה שבשירותים ותהליכים עם הפרדת הרשאות קל שהכתיבה תיכשל בשקט, ולכן בודקים את ה-ACL הנוכחי עם icacls, ואם צריך מוסיפים לחשבון ההרצה הרשאת שינוי עם ירושה, ומתאימים את היקף הירושה למדיניות הצפייה.
ac1["בודקים את ה-ACL הנוכחי עם icacls"] --> ac2{"חשבון ההרצה יכול לכתוב?"}
ac2 -->|"לא יכול"| ac3["מוסיפים הרשאת שינוי עם ירושה"]
ac2 -->|"יכול"| ac4["ממשיכים כמו שהוא"]
ac3 -.-> ac5["מתאימים את היקף הירושה למדיניות הצפייה"]
איור 14: בדיקת יכולת הכתיבה ובדיקת מי יכול לקרוא נעשות באותו מקום.
10.3 להמשיך להוציא full dump ישירות לכונן המערכת בייצור
זו תקלת נפח קלאסית. מגבילים מראש מספר שמורים ומפרידים את יעד הפלט.
10.4 לנסות לראות גם hang רק עם WER
WER LocalDumps חזק בעיקר לקריסות. יש מצבים שבהם hang ו-first chance exception מתאימים יותר ל-ProcDump.
10.5 להשאיר -e 1 פעיל תמיד ולהיגרר לסופת חריגות
first chance exception נוח, אבל בדרך כלל יש הרבה מהם. מציאותי להגביל את הכמות, להשאיר פעיל לזמן קצר, ולהגביל את היעד.
11. סיכום
crash dump הוא נקודת תצפית חזקה מאוד עבור תקלות עם שיעור שחזור נמוך. בפרט באפליקציית Windows שבה מעורבים COM, P/Invoke, DLL נייטיבי או הרצה ארוכה, יש ערך בכך שקובעים מראש “מה נשאר כשזה נופל”.
הסדר המומלץ פשוט.
- קודם מכניסים WER LocalDumps לפי אפליקציה
- מוסיפים ProcDump אם צריך
- אם רוצים שליטה נוספת, משתמשים ב-
MiniDumpWriteDumpבהנחה של תהליך נפרד
אם מתקדמים בסדר הזה, קשה לטעות בגדול.
12. מקורות
-
[ユーザーモード ダンプの収集 - Win32 apps Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/wer/collecting-user-mode-dumps) -
[ProcDump v11.1 - Sysinternals Microsoft Learn](https://learn.microsoft.com/en-us/sysinternals/downloads/procdump) -
[MiniDumpWriteDump function (minidumpapiset.h) - Win32 Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/nf-minidumpapiset-minidumpwritedump) -
[User-mode dump files - Windows drivers Microsoft Learn](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/user-mode-dump-files) -
[ユーザー モード ダンプ ファイルの分析 - Windows drivers Microsoft Learn](https://learn.microsoft.com/ja-jp/windows-hardware/drivers/debugger/analyzing-a-user-mode-dump-file) -
[Windows デバッガーをインストールする - Windows drivers Microsoft Learn](https://learn.microsoft.com/ja-jp/windows-hardware/drivers/debugger/) -
[Symbol path for Windows debuggers - Windows drivers Microsoft Learn](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/symbol-path) -
[!analyze (WinDbg) - Windows drivers Microsoft Learn](https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/-analyze) -
[Troubleshoot processes by using Task Manager - Windows Server Microsoft Learn](https://learn.microsoft.com/en-us/troubleshoot/windows-server/support-tools/support-tools-task-manager) -
[Enabling Postmortem Debugging - Windows drivers Microsoft Learn](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/enabling-postmortem-debugging) -
[MINIDUMP_TYPE enumeration (minidumpapiset.h) - Win32 Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ne-minidumpapiset-minidump_type) -
[icacls - Windows commands Microsoft Learn](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/icacls) -
[NotMyFault - Sysinternals Microsoft Learn](https://learn.microsoft.com/en-us/sysinternals/downloads/notmyfault)
מאמרים קשורים
מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.
תכנון שמירת יומנים ו-dump בקריסת יישום Windows
המאמר מסדר איך לשלב יומן רגיל, סמן קריסה סופי, WER LocalDumps ותהליך ניטור, כדי שגם כשיישום Windows קורס מחריגה בלתי צפויה או מבאג בתוכני...
תשתית בדיקות למקרי קצה ב-Windows עם Application Verifier
מהו Application Verifier: מסודר כאן יחד עם בניית תשתית בדיקות למקרי קצה ב-Windows באמצעות Handles, Heaps, Low Resource Simulation ו-!h...
חקירת קריסה בהפעלה ממושכת של מצלמה תעשייתית — פרק דליפת ה-handle
איך בוחנים אפליקציית Windows שקורסת בפתאומיות אחרי הפעלה ממושכת: דרך מקרה של אפליקציית בקרת מצלמה תעשייתית, מסודר כאן איך למצוא דליפת han...
הסיבה לכך ששידור חוזר ב-TCP עוצר תקשורת עם מצלמה תעשייתית, ואיך מבודדים אותה
איך מבודדים עצירות של כמה שניות בתקשורת עם מצלמה תעשייתית שנגרמות משידור חוזר ב-TCP: אובדן מנות, RTO, חותמות הזמן של RFC1323 ונקודות הבדי...
המלכודות של יישום תקשורת טורית - עד לתכנון חיבור מחדש ויומן
המאמר מסדר, מנקודת מבט מעשית, את המלכודות שכדאי להימנע מהן ביישום תקשורת טורית לחיבור ציוד ובקרת מכשירי מדידה - מבניית מסגרות, timeout, ...
נושאים קשורים
העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.
נושאים טכניים ב-Windows
שער לנושאי פיתוח Windows, חקירת תקלות וניצול נכסים קיימים.
שירותים הקשורים לנושא הזה
המאמר קשור ישירות לשירותים הבאים.
חקירת תקלות ואיתור גורמים
התהליך של איתור שורש הבעיה בעזרת שילוב של crash dump, לוגים ותנאי שחזור מתאים היטב לחקירת תקלות וניתוח שורש. בפרט בקריסות שקורות רק בשטח או תקלות אחרי הרצה ארוכה, עצם תכנון התצפית הוא קריטי.
ייעוץ טכני וסקירת תכנון
אם רוצים לסדר מה אוספים בסביבת ייצור, איך משלבים dump ולוגים בתכנון, ועד מדיניות הרשאות ואחסון, זה נושא שקל להתקדם בו כייעוץ טכני וסקירת תכנון.
שאלות נפוצות
שאלות נפוצות בפניות בנושא המאמר.
- מה זה crash dump? מה אפשר ללמוד ממנו?
- זהו שמירה לקובץ של מצב התהליך ברגע הקריסה - כמו תצלום סטילס מזירת תאונה. אפשר לבדוק בדיעבד את קוד החריגה, את ה-thread שקרס ואת מחסנית הקריאות שלו, את המודולים שהיו טעונים, ובהתאם לכמות הזיכרון שנכלל אף את תוכן האובייקטים ב-heap. מצד שני, ציר הזמן שהוביל לשם, והמצב החיצוני של תקשורת וציוד, נוטים להיות חסרים - ולכן בפועל משלבים dump עם לוגים ו-heartbeat.
- איפה מגדירים את LocalDumps של WER?
- מגדירים תחת מפתח הרישום HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps. בפועל נוח יותר לרכז את ההגדרה במפתח משנה לפי אפליקציה, כמו MyApp.exe, ולא בהגדרה גלובלית. שלושת הערכים שכדאי לבדוק ראשונים הם DumpFolder ליעד הפלט, DumpCount למספר השמורים, ו-DumpType לסוג ה-dump - כך אפשר להשאיר dump מקומי אחרי קריסה בלי להפיץ כלי נוסף.
- כדאי לבחור minidump או full dump?
- קו מנחה: במחשבי פיתוח ובדיקה - full dump (DumpType=2), ובסביבת הלקוח - בוחרים minidump (DumpType=1) או full dump לפי היקף וצרכי סודיות. כשיש חשד להשחתת זיכרון, DLL נייטיבי, COM, P/Invoke, או תקלת מצב אחרי הרצה ארוכה, עדיף לנטות לכיוון full. עם זאת, ה-full dump גדול בגודלו, ויש בו סיכון שמידע רגיש כמו connection string או token יתערב, ולכן צריך לקבוע מראש מיקום אחסון, מספר שמורים והרשאות גישה.
- מתי משתמשים ב-ProcDump?
- מכיוון ש-WER LocalDumps מיועד בעיקרו לקריסות, ProcDump מתאים כשרוצים לראות גם hang או first chance exception, כשרוצים להימנע ממצב קבוע ברישום, או כשרוצים לנטר רק תהליך שכבר רץ. האפשרויות האופייניות הן -ma ל-full dump, -e לאיסוף בחריגה לא מטופלת, -h לזיהוי hang, ו-w להמתנה להפעלה. -e 1 שמכוון ל-first chance exception נוטה לייצר כמות גדולה, ולכן מציאותי להשתמש בו לזמן קצר ומוגבל, עם הגבלת -n.