365. Implementing an interception framework - Profiler API

בהמשך לפעמים הקודמות,

עוד דרך לממש Framework לInterception היא דרך מטורפת המשתמשת בAPI של הProfiler.

קצת רקע למי שלא מכיר:

בVisual Studio קיימת אפשרות לבצע Profiling לתוכנות שלנו בMode שנקרא Instrumentation.

מה שהדבר הזה עושה זה משנה את הDLLים שלנו כך שמתווסף לפני ואחרי קריאה לכל פונקציה מדידה של הזמן שלקח לפונקציה לרוץ.

מסתבר שלProfiler הזה יש API המאפשר לו לבצע את המניפולציות האלה על הDLLים שלנו, ואפשר להשתמש בו כדי לעשות מניפולציות משלנו.

יש הרבה Frameworkים שיודעים לנצל את הAPI הזה ולעשות איתו ניסים ונפלאות, למשל קיים הFramework לבדיקות בשם TypeMock המאפשר להתערב בכל היצירות של האובייקטים מבלי לשנות את הקוד המורץ, על מנת לאפשר ביצוע Mockים (ראו גם טיפ מספר 350) לאובייקטים מבלי לשנות את הקוד. (כלומר, קריאה, למשל, לnew לא תקרא לConstructor, אלא לפונקציה של הUnit Test שתחזיר את הMock הרצוי)


אז מה הקשר לInterception? ניתן להשתמש בAPI של הProfiler על מנת לבצע “קסמים” כאלה שישנו את הקוד המקומפל שלנו כך שיבצע את מה שאנחנו מעוניינים שיקרה.

היתרון של הסיפור הזה זה שזה נותן לנו הרבה מאוד כוח לעשות מה שאנחנו רוצים.

יש גם כמה חסרונות:

  • החסרון הראשון הוא שהAPI הזה לא כל כך מתועד וקשה להשתמש בו (הוא גם כתוב בCOM)
  • החסרון השני הוא שהדבר הזה לא כל כך נתמך, ולכן כל פעם שמתחלפת גרסה של Visual Studio, החבר’ה בTypeMock עובדים קשה על להדביק את הקצב ולהתאים את עצמם לגרסה החדשה.

לא מצאתי באינטרנט Framework של Interception שמשתמש בProfiler API. אני מניח שאין מאחר ומדובר בהרבה עבודה קשה בליצור כלי כזה, וכבר יש אלטרנטיביות מעולות, כגון PostSharp.

אם מישהו מוצא משהו, אני אשמח לשמוע.

כאשר יוצא לך לעשות Profiling בMode הזה, נכתב לך בOutput Window טקסט מהסוג הזה:

Profiling started.
Instrumenting C:\Projects\MyProfilingApplication\MyProfilingApplication\obj\x86\Debug\MyProfilingApplication.exe in place
Info VSP3049: Small functions will be excluded from instrumentation.
Microsoft (R) VSInstr Post-Link Instrumentation 10.0.40219 x86
Copyright (C) Microsoft Corp. All rights reserved.
File to Process:
C:\Projects\MyProfilingApplication\MyProfilingApplication\obj\x86\Debug\MyProfilingApplication.exe –> C:\Projects\MyProfilingApplication\MyProfilingApplication\obj\x86\Debug\MyProfilingApplication.exe
Original file backed up to C:\Projects\MyProfilingApplication\MyProfilingApplication\obj\x86\Debug\MyProfilingApplication.exe.orig
Successfully instrumented file C:\Projects\MyProfilingApplication\MyProfilingApplication\obj\x86\Debug\MyProfilingApplication.exe.
Instrumenting C:\Projects\MyProfilingApplication\MyProfilingApplication.Dependency\obj\Debug\MyProfilingApplication.Dependency.dll in place
Info VSP3049: Small functions will be excluded from instrumentation.
Microsoft (R) VSInstr Post-Link Instrumentation 10.0.40219 x86
Copyright (C) Microsoft Corp. All rights reserved.
File to Process:
C:\Projects\MyProfilingApplication\MyProfilingApplication.Dependency\obj\Debug\MyProfilingApplication.Dependency.dll –> C:\Projects\MyProfilingApplication\MyProfilingApplication.Dependency\obj\Debug\MyProfilingApplication.Dependency.dll
Original file backed up to C:\Projects\MyProfilingApplication\MyProfilingApplication.Dependency\obj\Debug\MyProfilingApplication.Dependency.dll.orig
Successfully instrumented file C:\Projects\MyProfilingApplication\MyProfilingApplication.Dependency\obj\Debug\MyProfilingApplication.Dependency.dll.
Warning VSP2013: Instrumenting this image requires it to run as a 32-bit process. The CLR header flags have been updated to reflect this.
Profiling process ID 6568 (MyProfilingApplication).

מה שאנחנו רואים בעצם זה שהDLL שונה וגובה על ידי הProfiler. אם נסתכל על הDLL ששונה בReflector נראה משהו כזה:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static void Main(string[] args)
{
_CAP_Enter_Function_Managed((int) Microsoft.VisualStudio.Instrumentation.g_fldMMID_2D71B909-C28E-4fd9-A0E7-ED05264B707A, 0x6000005, 0);
_CAP_StartProfiling_Managed((int) Microsoft.VisualStudio.Instrumentation.g_fldMMID_2D71B909-C28E-4fd9-A0E7-ED05264B707A, 0x6000005, 0xa000011);
_CAP_StopProfiling_Managed((int) Microsoft.VisualStudio.Instrumentation.g_fldMMID_2D71B909-C28E-4fd9-A0E7-ED05264B707A, 0x6000005, 0);
MyClass class2 = new MyClass();
_CAP_StartProfiling_Managed((int) Microsoft.VisualStudio.Instrumentation.g_fldMMID_2D71B909-C28E-4fd9-A0E7-ED05264B707A, 0x6000005, 0xa000012);
class2.Test("Foo");
_CAP_StopProfiling_Managed((int) Microsoft.VisualStudio.Instrumentation.g_fldMMID_2D71B909-C28E-4fd9-A0E7-ED05264B707A, 0x6000005, 0);
_CAP_StartProfiling_Managed((int) Microsoft.VisualStudio.Instrumentation.g_fldMMID_2D71B909-C28E-4fd9-A0E7-ED05264B707A, 0x6000005, 0xa000013);
_CAP_StopProfiling_Managed((int) Microsoft.VisualStudio.Instrumentation.g_fldMMID_2D71B909-C28E-4fd9-A0E7-ED05264B707A, 0x6000005, 0);
Console.ReadLine();
_CAP_Exit_Function_Managed((int) Microsoft.VisualStudio.Instrumentation.g_fldMMID_2D71B909-C28E-4fd9-A0E7-ED05264B707A, 0x6000005, 0);
}

כאשר הקוד המקורי היה נראה כך:

1
2
3
4
5
6
private static void Main(string[] args)
{
MyClass instance = new MyClass();
instance.Test("Foo");
Console.ReadLine();
}

יש פה כמה קריאות כאלה לProfiling שלא ברור למה הוא עושה (הוא עושה Start ומיד לאחר מכן Stop), אבל אפשר לראות שלפונקציה Test הוא כן עושה Profiling (בודק כמה זמן לוקח לה לרוץ)

ככה הוא עושה על כל הTarget Projects/Binaries שאנחנו נותנים לו, וכך בעצם הוא יודע כמה זמן בערך לוקח לכל קריאה לרוץ. (שים לב שזה זמן יחסי, שהרי הקריאות לProfiling מאטות את הפונקציות האמיתיות. בין השאר, הדבר הזה לא משקף באופן אמין ביצועים של דברים שקשורים לIO כגון עבודה עם Streamים וכו’, להם עדיף להשתמש בMode של Sampling)

המשך יום עם ממשק מדידה טוב.

שתף