אבחון והפצת מוצר מעורב¶
בקשה מתחילה ב־UI, עוברת דרך Task אל DLL טבעי ומסיימת ב־callback. כשהיא נתקעת, stack יחיד אינו מספר את כל הסיפור. נצטרך מזהה עבודה שמופיע בכל המעברים, אירועים שמתארים שינוי מצב וסמלים שמתאימים בדיוק ל־binaries שהופצו. נבנה תחילה ציר זמן פשוט שניתן להשוות ל־trace של ה־runtime ושל המערכת.
הדוגמה הבאה היא תוכנית console מלאה. היא מתעדת התחלה, השלמה וכשל של פעולה, בלי להכניס את תוכן הקובץ או נתוני המשתמש לאירועים.
using System.Diagnostics.Tracing;
long id = 7;
Events.Log.Start(id, 4096);
try
{
await Task.Delay(20);
Events.Log.Completed(id, 4096);
}
catch (Exception error)
{
Events.Log.Failed(id, error.HResult);
throw;
}
Console.WriteLine("done");
[EventSource(Name = "AmitTech-MixedLab")]
sealed class Events : EventSource
{
public static readonly Events Log = new();
[Event(1, Level = EventLevel.Informational)]
public void Start(long id, long bytes) { if (IsEnabled()) WriteEvent(1, id, bytes); }
[Event(2, Level = EventLevel.Informational)]
public void Completed(long id, long bytes) { if (IsEnabled()) WriteEvent(2, id, bytes); }
[Event(3, Level = EventLevel.Error)]
public void Failed(long id, long code) { if (IsEnabled()) WriteEvent(3, id, code); }
}
ה־ID הוא חלק מחוזה התצפית שלנו. במערכת עם מספר בקשות נייצר אותו באופן אטומי ונעביר אותו גם ל־native context. האירוע Completed יתאר תוצאת עבודה, ואירוע נפרד בשם Joined יוכל לתאר שה־callback כבר אינו רץ ומותר לשחרר context. איחוד שני הרגעים תחת "done" אחד מקשה לזהות שחרור מוקדם.
באיסוף עם dotnet-trace נבחר את provider שלנו ואת ספקי ה־runtime הנדרשים, ונרשום את חלון הזמן וה־PID. בווינדוס נוכל לשלב ETW כדי לראות גם פעילות מערכת וקוד native בעזרת כלים מתאימים וסמלים. ה־EventPipe נותן אירועים מנוהלים בלי להיות תחליף לכל אירוע kernel. ב־dump מעורב נשתמש ב־SOS עבור האובייקטים וה־stacks המנוהלים, ובתמיכת ה־debugger עבור DLL טבעי וה־PDB שלו.
נבחין בין שלושה כשלים דומים למשתמש. כשל DllNotFoundException יכול להיות DLL ראשי חסר או תלות שלו שחסרה. כשל EntryPointNotFoundException מצביע על export שאינו מתאים לשם שביקשנו. כשל BadImageFormatException יכול להופיע באי־התאמת פורמט או ארכיטקטורה. החלפת כל אלה בהודעת "הפעולה נכשלה" מאבדת ראיה חשובה; ה־wrapper ישמור את סוג הכשל ואת נתיב החבילה שבחר.
בהפצה נגדיר RID כמו win-x64 או win-arm64, ונחליט אם להפיץ runtime עצמאי או לדרוש runtime מותקן. החבילה צריכה להכיל את התלויות הטבעיות הנכונות לכל יעד. חיפוש DLL דרך תיקייה נוכחית משתנה אינו חוזה הפצה יציב. נוכל להשתמש ב־NativeLibrary.SetDllImportResolver עבור ספרייה שבבעלותנו כדי לבחור נתיב מלא מתוך תיקיית האפליקציה, תוך בדיקת שם הספרייה והרשאות התיקייה.
ה־trimming וה־AOT מוסיפים בדיקה של הנחות על reflection, יצירת קוד ו־marshalling. קוד interop שנוצר בזמן הבנייה יכול לעזור, אבל אינו פוטר מבדיקת ה־artifact הסופי. נריץ תרחישי הצלחה, שגיאת ABI, ביטול ו־shutdown על תיקיית publish נקייה, ולא רק מתוך Visual Studio שמספקת נתיבי חיפוש ותלויות נוחות. שדרוג DLL ייבדק מול לקוח ישן וחדש באמצעות size/version וחוזי status שנשמרים.
נכין חבילת תחקיר עם גרסאות, hashes של binaries, מזהה build, קלט שחזור קטן, trace ודוח ציר זמן. נבדיל בין הראיה שבה הכשל נצפה לבין ההחלטה שיצרה אותו; access violation ב־callback יכול להיגרם מ־Free שבוצע הרבה קודם. התיעוד על הוספת EventSource ועל טעינת ספריות טבעיות מספק את הכלים לבניית תצפית שאפשר לסמוך עליה.