بنية اختبار مسارات الفشل في Windows بـ Application Verifier
· آخر تحديث: · 小村 豪 · تطوير Windows, التحقيق في الأعطال, الكاميرا الصناعيّة, Application Verifier, اختبار المسارات الشاذّة, handle leak
سجل التعديلات (3 تحديثات، آخر تحديث 3 Sep، 2026)
سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.
- أُضيفت روابط الاستشارة الموجودة في الأصل الياباني (consultation_services). ولم يتغيّر نصّ المقالة نفسه. قراءة النسخة السابقة لهذا التحديث (DOI: 10.5281/zenodo.22240811)
- أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
- أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
- النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621333)
هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.
小村 豪 (2026). بنية اختبار مسارات الفشل في Windows بـ Application Verifier. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621333 https://comcomponent.com/ar/blog/2026/03/11/003-application-verifier-abnormal-test-foundation-part2/
- DOI (أحدث نسخة)
- 10.5281/zenodo.21621333
- DOI (هذه النسخة)
- 10.5281/zenodo.22279723
Application Verifier أداة قويّة عندما يُراد إظهار الشذوذ الذي يحدث حول الشيفرة الأصليّة في Windows أو حدود Win32 في وقت أبكر. تنفع خصوصاً في مواضع اختبار شذوذ المقابض وتلف الكومة ومسار الفشل عند انخفاض الموارد، فتُخرج مشكلات لا يراها اختبار المسار العاديّ وحده بسرعة كبيرة.
في الجزء الأوّل تحقيق تعطّل الكاميرا الصناعيّة بعد تشغيل طويل: فصل handle leak رتّبنا حالة تبيّن فيها بعد التحقيق في تطبيق تحكّم يسقط بعد تشغيل طويل أنّ السبب handle leak. لكن تعزيز السجلّات وحده ما زال نصفاً. ما يُراد حقّاً هو التمكّن مسبقاً من تجربة هل، إذا حدث لاحقاً خطأ برمجيّ غير متوقّع يسبّب تسريب ذاكرة أو handle leak أو فشلاً في الوسط أو نسيان تحرير، تبقى الحالة «يمكن معرفة ما حدث».
هنا استُخدم Application Verifier. أداة يمكن إدخال فحوصات وقت التشغيل وfault injection على معالجة تعمل حول الشيفرة الأصليّة في Windows أو حدود Win32. ما ينفع خصوصاً في العمل أنّه يمكن إحداث شكل انكسار يشبه نقص الذاكرة أو نقص الموارد في وقت أبكر، دون استنزاف ذاكرة الجهاز فعلاً.
في الجزء الثاني نرتّب ما هو Application Verifier، وما يمكنه فعله، وكيف يُدمَج في بنية اختبار مسارات الفشل، في سياق تطبيق تحكّم بكاميرا صناعيّة.
المحتويات
- الخلاصة أوّلاً (في جملة)
- ما هو Application Verifier
- 2.1. ماذا يعني في جملة
- 2.2. في أيّ مواضع ينفع
- 2.3. ما الذي يسرّ في العمل
- 2.4. من الحصول إلى التفعيل محليّاً
- ماذا يمكن لـ Application Verifier أن يفعل
- 3.1. Basics: Handles / Heaps / Locks / Memory / TLS وغيرها
- 3.2. Low Resource Simulation: تقديم نقص الذاكرة ونقص الموارد
- 3.3. Page Heap والمصحّح
- 3.4.
!avrf/!htrace/ السجلّات
- لماذا أُدخل في هذه الحالة
- 4.1. الغرض ليس «إيجاد خطأ» فقط
- 4.2. إحداث ظاهرة تشبه نقص الذاكرة
- 4.3. التحقّق من إمكان التتبّع عند شذوذ المقابض
- كيف تُحدَث ظاهرة تشبه نقص الذاكرة أو نقص الموارد
- 5.1. فكرة Low Resource Simulation
- 5.2. ما يمكن إفشاله
- 5.3. طريقة التطبيق في العمل
- كيف يُنظَر إلى شذوذ المقابض
- 6.1. فحص
Handles - 6.2. النظر إلى مكدّس open / close بـ
!htrace - 6.3. كيف يُجمع مع السجلّ الذاتيّ
- 6.1. فحص
- طريقة بناء بنية اختبار المسارات الشاذّة
- 7.1. إمالة وحدة التنفيذ إلى harness
- 7.2. تقسيم قائمة الاختبار
- 7.3. ما يُجمَع
- 7.4. شروط القبول
- 7.5. تنبيهات
- دليل اختيار تقريبيّ
- الخلاصة
- روابط مرجعيّة
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 17، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
1. الخلاصة أوّلاً (في جملة)
- Application Verifier أداة تسهّل إيجاد إساءة الاستخدام وقت التشغيل عند حدود unmanaged / native في Windows
- ما ينفع ليس «إيجاد خطأ» فقط، بل إحداث المسارات الشاذّة التي يصعب ظهورها عادة في وقت أبكر
Handlesلكشف invalid handle، وHeapsلإبراز تلف الكومة، وLow Resource Simulationلـ fault injection لوضع يشبه نقص الذاكرة أو نقص الموارد- إلقاء تحقيق تسريب EXE مقيم طويل الأمد على Application Verifier وحده مسار سيّئ، والجمع مع سجلّ ذاتيّ لـ
Handle Countودورة حياة المورد واقعيّ - في بنية اختبار المسارات الشاذّة، فصل verifier run للمسار العاديّ عن fault injection run أوضح للقراءة
- حتّى عند تجربة DLL، هدف تفعيل Application Verifier هو EXE الاختبار الذي يشغّل ذلك DLL فعليّاً
باختصار، Application Verifier أداة تسحب إلى السطح «الأخطاء الكريهة» حول native / Win32 في Windows. تنسجم كثيراً خصوصاً في عالم تطبيقات التحكّم بالأجهزة حيث يختلط native SDK وP/Invoke وWin32 API بسهولة.
flowchart TB
accTitle: عملان لـ Application Verifier
accDescr: يبيّن أنّ Application Verifier له عملان: كشف إساءة الاستخدام عند الحدود الأصليّة، وتقديم المسارات الشاذّة التي يصعب ظهورها عادة، ويمكن كشف invalid handle وتلف الكومة وحقن وضع يشبه نقص الذاكرة.
av["Application Verifier"] --> detect["كشف إساءة الاستخدام عند الحدود الأصليّة"]
av --> inject["تقديم المسارات الشاذّة الصعبة الظهور"]
detect --> d1["invalid handle وتلف الكومة"]
inject --> d2["حقن وضع يشبه نقص الذاكرة"]
الشكل 1: عمل Application Verifier عمودان: «كشف إساءة الاستخدام» و«تقديم المسارات الشاذّة».
2. ما هو Application Verifier
2.1. ماذا يعني في جملة
Application Verifier أداة تحقّق وقت التشغيل لتطبيقات user-mode في Windows. تراقب استخدام التطبيق الجاري لـ OS API وطريقة تعامله مع الموارد، فتكشف استخداماً مشبوهاً أو تحقن فشلاً عمداً.
بخلاف «التحليل الساكن» أو «اختبار الوحدة»، هي أداة تنظر كيف ينكسر الأمر عند المرور فعليّاً بمسار الشيفرة ذلك. لذا تناسب إبراز مسار الفشل الذي لا يظهر في اختبار الوظائف العاديّ.
flowchart LR
A["harness الاختبار"] --> B["تطبيق التحكّم / غلاف SDK"]
B --> C["Application Verifier"]
C --> D["Win32 API / native DLL / موارد نظام التشغيل"]
C --> E["verifier stop"]
C --> F["خرج المصحّح"]
C --> G["AppVerifier logs"]
B --> H["سجلّ منظَّم ذاتيّ"]
الشكل 2: Application Verifier يراقب تطبيق التحكّم الذي يُشغَّل من harness الاختبار، ويُبقي نتائج الرصد كـ verifier stop وخرج المصحّح وسجلّات.
2.2. في أيّ مواضع ينفع
ما ينفع خصوصاً مواضع كهذه.
- استدعاء native DLL أو SDK كاميرا
- العبور عبر P/Invoke أو COM
- استخدام كثير مباشر أو غير مباشر لـ handle وheap وlock والذاكرة الافتراضيّة
- لا يسقط عادة في المسار السليم، لكن إدارة العمر توشك على الانهيار في المسار الشاذّ فقط
- «فشل غريب أحياناً» يظهر قبل «السقوط»
بالمقابل، ليست أداة لتتبّع object graph في عالم managed خالص. لذلك تنفع كثيراً حتّى في تطبيق C# إذا كانت حدود native SDK أو Win32 سميكة، لكنّها ليست قصّة رؤية تسريب الكومة managed الخالص بهذه الأداة وحدها كلّه.
flowchart TB
accTitle: تمييز المواضع التي ينفع فيها Application Verifier
accDescr: يبيّن التضييق: ينفع في تطبيق سميك الحدود بـ native DLL أو P/Invoke أو Win32، لكنّه ليس أداة لتتبّع object graph في عالم managed خالص.
q{"أيّ طبقة المشكلة؟"}
q -->|"حدود native SDK أو Win32"| yes["Application Verifier ينفع"]
q -->|"object graph managed خالص"| no["خارج النطاق (يُنظَر بأداة أخرى)"]
الشكل 3: الحدّ الفاصل للنفع سماكة حدود native / Win32، وليست أداة ترى عالم managed خالصاً فقط.
2.3. ما الذي يسرّ في العمل
ما يسرّ في العمل تقريباً ثلاثة.
- إيقاف إساءة الاستخدام عند الحدود الأصليّة مبكّراً
- invalid handle
- heap corruption
- lock misuse
- virtual memory API misuse وغيرها
- تقديم شكل انكسار لا يظهر إلا عند انخفاض الموارد
- ما يعادل
mallocيفشل أحياناً CreateEventأوCreateFileيفشل أحياناًVirtualAllocيفشل
- ما يعادل
- سهولة التتبّع بالجمع مع المصحّح
!avrf!htrace!heap -p -a- سجلّ verifier stop
ما يزعج في تطبيقات التحكّم بالأجهزة «عدم معرفة ما حدث في المسار الشاذّ». Application Verifier ينفع كثيراً في تقليل «عدم المعرفة» ذلك.
flowchart TB
accTitle: ثلاث نقاط تسرّ في العمل
accDescr: يبيّن ثلاث فوائد: إيقاف إساءة الاستخدام عند الحدود الأصليّة مبكّراً، وتقديم شكل انكسار لا يظهر إلا عند انخفاض الموارد، وسهولة التتبّع بالجمع مع المصحّح.
av["Application Verifier"] --> b1["إيقاف إساءة الاستخدام مبكّراً"]
av --> b2["تقديم شكل الانكسار"]
av --> b3["سهولة التتبّع بالمصحّح"]
b3 -.-> t["امتدادات مثل avrf وhtrace"]
الشكل 4: السرور في العمل يتجمّع في ثلاث: الكشف المبكّر، وتقديم المسارات الشاذّة، والربط بالمصحّح.
2.4. من الحصول إلى التفعيل محليّاً
نفرغ أوّلاً تجهيز الأداة. إذا سقط هذا، يصير الحديث بعده كلّه نظريّاً.
Application Verifier مرفق مع Windows SDK. لا يدخل مع Windows وحده، لذا يُشغَّل مثبّت SDK ويُؤشَّر «Application Verifier» في شاشة اختيار الميزات. اسم الملفّ التنفيذيّ appverif.exe.
للاستخدام ثلاثة افتراضات.
- أن يكون المستخدم المنفِّذ عضواً في مجموعة Administrators على ذلك الجهاز
- أن ARM64EC خارج نطاق الدعم
- أن هدف التحقّق شيفرة unmanaged (أصليّة)
علاقة الواجهة الرسوميّة وسطر الأوامر تُفهم هكذا فلا تحدث حيرة.
| ماذا يفعل | |
|---|---|
الواجهة الرسوميّة (appverif.exe) |
كتابة اسم EXE المستهدف وتركيب الاختبارات المفعَّلة في السجلّ |
سطر الأوامر (appverif -enable ...) |
كتابة إعداد السجلّ نفسه تماماً بأمر |
| وقت التشغيل | عند تشغيل EXE المستهدف يُنظَر إلى ذلك الإعداد فتُحمَّل DLL الخاصّة بـ verifier وتدخل خطّافات Win32 API |
أي أنّ ما يُفعَل واحد أيّاً استُخدم. الاستخدام: الواجهة الرسوميّة لليدويّ أوّل مرّة، وسطر الأوامر لـ CI أو النصوص.
flowchart TB
accTitle: علاقة الواجهة الرسوميّة وسطر الأوامر
accDescr: يبيّن أنّ الواجهة الرسوميّة وسطر الأوامر يكتبان إعداد السجلّ نفسه فقط، وأنّ EXE المستهدف عند التشغيل ينظر إلى ذلك الإعداد فتُحمَّل DLL الخاصّة بـ verifier وتدخل خطّافات Win32 API.
gui["الواجهة الرسوميّة (appverif.exe)"] --> reg["كتابة الإعداد في السجلّ"]
cli["سطر الأوامر"] --> reg
reg --> boot["المرجع عند تشغيل EXE المستهدف"]
boot --> hook["تحميل DLL الخاصّة بـ verifier والخطّافات"]
الشكل 5: الواجهة الرسوميّة وسطر الأوامر يكتبان إعداد السجلّ نفسه فقط، والخطّافات تدخل عند تشغيل EXE المستهدف.
عمليّة الواجهة الرسوميّة: نقر أيمن في عمود Applications يساراً ثمّ «Add Application» لإضافة EXE المستهدف، وتأشير Basics وغيرها في عمود Tests يميناً ثمّ «Save». الإلغاء: نقر أيمن في عمود Applications نفسه ثمّ «Delete Application» ثمّ «Save».
من هنا يأتي قيدان مهمّان.
- لا يمكن التفعيل لاحقاً على عمليّة جارية. الخطّافات تدخل عند تحميل DLL، لذا الترتيب تعيين ثمّ تشغيل.
- الإعداد يبقى حتّى يُحذف صراحة. إذا تُرك بنيّة «جرّبناه مرّة واحدة»، يستمرّ ذلك EXE على ذلك الجهاز في التشغيل دائماً تحت verifier.
لاحظ أنّ سجلّات الكشف تُحفَظ افتراضيّاً بشكل ثنائيّ في %USERPROFILE%\AppVerifierLogs، ويمكن تحويلها إلى XML بالواجهة الرسوميّة أو سطر الأوامر للتجميع.
flowchart TB
accTitle: ترتيب التفعيل وبقاء الإعداد
accDescr: يبيّن أنّ الخطّافات تدخل عند تحميل DLL فلا يمكن الإضافة لاحقاً إلى عمليّة جارية، وأنّ الترتيب تعيين ثمّ تشغيل، وأنّ الإعداد يبقى حتّى يُحذف صراحة.
set["كتابة الإعداد"] --> launch["تشغيل EXE المستهدف"]
launch --> on["العمل تحت verifier"]
on --> keep["الإعداد يبقى حتّى الحذف"]
keep -.-> warn["الترك يجعل التشغيل دائماً تحت verifier"]
running["عمليّة جارية"] -.-> ng["التفعيل اللاحق غير ممكن"]
الشكل 6: ترتيب «تعيين ثمّ تشغيل» لا يُكسَر، والإعداد يبقى على ذلك الجهاز حتّى يُحذف صراحة.
3. ماذا يمكن لـ Application Verifier أن يفعل
3.1. Basics: Handles / Heaps / Locks / Memory / TLS وغيرها
المجموعة الأساسيّة في Application Verifier هي Basics.
هنا تتجمّع الفحوصات الشائعة في العمل.
| الطبقة | ماذا ينظر إليه | موضع الاستخدام في سياق هذه الحالة |
|---|---|---|
Handles |
استخدام invalid handle | هل وُطئ مقبض أُغلق / تالف |
Heaps |
heap corruption | إبراز تلف مخزن أو use-after-free عند حدود native SDK |
Leak |
موارد لم تُحرَّر عند تفريغ DLL | تأكيد اختبار harness قصير العمر أو حالات تشمل التفريغ |
Locks / SRWLock |
إساءة استخدام lock | تأكيد تسابق reconnect وshutdown |
Memory |
إساءة استخدام VirtualAlloc / MapViewOfFile وغيرها |
تأكيد شذوذ حول مخزن كبير أو ذاكرة مشتركة |
TLS |
إساءة استخدام Thread Local Storage API | تأمين لشيفرة أصليّة معقّدة الحدود بين الخيوط |
Threadpool |
اتّساق threadpool API أو حالة العامل | مساعد عند كثرة callback أو المعالجة اللاتزامنيّة |
النقطة ليست «يمكن الفهم بالقراءة بعد السقوط» بل «إيقاف الاستخدام المشبوه في مكانه». في أعطال نمط التشغيل الطويل ينفع هذا التقديم كثيراً.
flowchart TB
accTitle: فكرة الكشف المبكّر في Basics
accDescr: يبيّن إبراز أعطال نمط التشغيل الطويل في وقت أبكر بإيقاف الاستخدام المشبوه في مكانه، لا بالتخمين من قراءة السجلّ بعد السقوط.
use["استخدام API مشبوه"] --> basics["مجموعة فحوصات Basics"]
basics --> stop["الإيقاف في مكانه"]
stop --> early["إبراز المشكلة في وقت أبكر"]
use -.-> later["سابقاً القراءة بعد السقوط فقط"]
الشكل 7: قيمة Basics تحويل «القراءة بعد السقوط» إلى «الإيقاف في مكانه».
3.2. Low Resource Simulation: تقديم نقص الذاكرة ونقص الموارد
ما ينفع كثيراً في العمل هنا. لأنّه يمكن إحداث ظاهرة قريبة من نقص الذاكرة أو نقص الموارد دون استنزاف RAM فعلاً.
الفكرة بسيطة.
- استدعاء API معيّن
- باحتمال ثابت
- يُفشَل عمداً
بهذا يمكن المرور بمسار خطأ لا يُمَرّ به عادة.
عمليّاً يسهل إحداث ظواهر كهذه عمداً.
- فشل
HeapAllocأوVirtualAlloc - فشل
CreateFile - فشل
CreateEvent - فشل
MapViewOfFile - فشل تخصيص عائلة OLE/COM مثل
SysAllocString
أسهل كثيراً من تعذيب الجهاز كلّه بمحاولة نقص ذاكرة حقيقيّ. وفوق ذلك يمكن إدخال fault injection موجَّهاً إلى DLL معيَّن. في تكوين يختلط فيه غلاف ذاتيّ مع SDK البائع مثل تطبيقات التحكّم بالأجهزة، هذا أنسب للعمل كثيراً.
flowchart TB
accTitle: آليّة Low Resource Simulation
accDescr: يبيّن أنّ إفشال نوع معيّن من استدعاءات API عمداً باحتمال ثابت يمكّن من المرور عمداً بمسار خطأ لا يُمَرّ به عادة، ويمكن أيضاً التضييق إلى DLL معيَّن.
call["استدعاء API"] --> judge{"هل أصاب الاحتمال الثابت؟"}
judge -->|"نعم"| fail["إرجاع فشل عمداً"]
judge -->|"لا"| ok["المعالجة كالعادة"]
fail --> path["إلى مسار خطأ لا يُمَرّ به عادة"]
path -.-> dll["يمكن التضييق إلى DLL معيَّن"]
الشكل 8: هويّة Low Resource Simulation هي fault injection يفشل استدعاء API باحتمال ثابت.
3.3. Page Heap والمصحّح
للنظر إلى تلف الكومة، جمع Heaps مع page heap قويّ.
خصوصاً full page heap ميزته سهولة الإيقاف في لحظة التلف باستخدام guard page.
لكن هذا ثقيل جدّاً. أسهل استخداماً التدوير تحت المصحّح مضيَّقاً إلى سيناريو قريب من إعادة الإنتاج، لا الاستقصاء الطويل.
لذلك كتشغيل، تقسيم كهذا واقعيّ.
- أوّلاً التطبيق الواسع بـ
Basics - إذا صارت الكومة مشبوهة يُستخدَم full page heap
- إذا ثقل أكثر يُنزَل إلى light page heap
- اختبار طويل يعادل الإنتاج يُنظَر إليه أساساً بالسجلّ الذاتيّ
أي أنّ AppVerifier ليس عصا سحريّة كليّة، بل أداة تُبدَّل شفرتها حسب المشهد.
flowchart TB
accTitle: تدفّق اختيار page heap
accDescr: يبيّن التطبيق الواسع بـ Basics أوّلاً، والإيقاف بـ full page heap في لحظة التلف إذا صارت الكومة مشبوهة، والنزول إلى light page heap إذا ثقل أكثر، والنظر أساساً بالسجلّ الذاتيّ في اختبار طويل يعادل الإنتاج.
s1["التطبيق الواسع بـ Basics"] --> s2{"هل الكومة مشبوهة؟"}
s2 -->|"نعم"| s3["الإيقاف بـ full page heap"]
s2 -->|"لا"| s7["الاختبار الطويل أساساً بالسجلّ الذاتيّ"]
s3 --> s4{"هل أثقل من اللازم؟"}
s4 -->|"نعم"| s5["النزول إلى light page heap"]
s4 -->|"لا"| s6["إعادة إنتاج محلّيّة تحت المصحّح"]
الشكل 9: page heap ليس أداة تُستخدَم دائماً، بل تُبدَّل الشفرة حسب المشهد بعد التطبيق الواسع بـ Basics.
3.4. !avrf / !htrace / السجلّات
كلمة أوّلاً. verifier stop الذي ظهر عدّة مرّات حتّى هنا حدث كشف يخرجه Application Verifier عندما يحكم بأنّ «هذا الاستخدام غير سليم». ليس سطر سجلّ عاديّاً، وميزته الكسر في مكانه إذا شُغِّل تحت المصحّح. للـ stop رقم، فيظهر بشكل VERIFIER STOP 00000300. هناك stop يمكن المتابعة منه، وstop لا يمكن المتابعة منه (لا خيار سوى إنهاء العمليّة).
Application Verifier لا ينتهي بإخراج stop. هناك امتدادات مصحّح وسجلّات تسهّل تتبّع ما حدث.
!avrf- النظر إلى إعداد verifier الحاليّ والـ stop الجاري
!htrace- النظر إلى مكدّس open / close / invalid reference للمقبض
!heap -p -a- تتبّع كتلة الكومة التالفة بالجمع مع page heap
- سجلّات AppVerifier
- يمكن إبقاء سجلّ عند حدوث stop
خصوصاً عند تفعيل Handles، يُفعَّل handle tracing تلقائيّاً وهذا ممتنّ.
بهذا يسهل لاحقاً تتبّع «أين فُتح هذا المقبض وأين أُغلق».
flowchart TB
accTitle: التدفّق من verifier stop إلى التحقيق
accDescr: يبيّن أنّ كشف استخدام غير سليم يخرج verifier stop مرقَّماً، ويكسر في مكانه تحت المصحّح، فيُؤكَّد الإعداد والـ stop بـ avrf وتاريخ المقبض بـ htrace.
bad["كشف استخدام غير سليم"] --> stop["verifier stop (مرقَّم)"]
stop --> brk["كسر تحت المصحّح"]
brk --> avrf["تأكيد الإعداد والـ stop بـ avrf"]
brk --> ht["تأكيد تاريخ المقبض بـ htrace"]
stop -.-> cont["stop يمكن المتابعة منه وstop لا يمكن"]
الشكل 10: verifier stop ليس سطر سجلّ عاديّاً، بل تحت المصحّح يكسر في مكانه فيصير نقطة انطلاق التحقيق.
4. لماذا أُدخل في هذه الحالة
4.1. الغرض ليس «إيجاد خطأ» فقط
غرض هذه الحالة لم يكن مجرّد «إيجاد خطأ واحد بـ AppVerifier». بتعبير أكثر عمليّة، ما أُريد تأكيده التالي.
- إذا حدث تسرّب موارد لاحقاً على مسار فشل آخر
- هل يبقى سياق في السجلّ بشكل سليم
- هل يمكن التتبّع حتّى النهاية مع معلومات المصحّح
- هل لا تصير الحالة «ما حدث غير معلوم»
أي أنّه استُخدم لا كـ كاشف فقط، بل كـ اختبار لبنية الرصد.
4.2. إحداث ظاهرة تشبه نقص الذاكرة
إحداث نقص ذاكرة حقيقيّ على جهاز التطوير العاديّ متعب نسبيّاً. وفوق ذلك إذا صار الجهاز كلّه غير مستقرّ، يصير الاختبار نفسه مليئاً بالضجيج.
لذلك اتّجهنا بـ Low Resource Simulation إلى وطء مسار الفشل الذي يُتوقَّع عند نقص الذاكرة أو نقص الموارد عمداً.
بهذا يسهل الإجابة عن أسئلة كهذه.
- إذا فشل
CreateEvent، هل يبقىcameraIdوphaseفي السجلّ - هل يجري clean up بشكل سليم بعد تهيئة ناقصة
- إذا فشل
VirtualAlloc، هل ينكسر الأمر بإعادة المحاولة - هل يعود المقبض عند فشل
CreateFileفي مسار الحفظ
ما يُراد التأكيد عليه أنّ الغرض ليس إحداث الشذوذ نفسه، بل أن يكون شكل الانكسار عند الشذوذ قابلاً للقراءة.
flowchart TB
accTitle: بنية رصد تُؤكَّد بـ fault injection
accDescr: يبيّن وطء الفشل عمداً بـ Low Resource Simulation، وتأكيد هل يبقى سياق في السجلّ، وهل يجري التنظيف، وهل ينكسر الأمر بإعادة المحاولة. الغرض ليس إحداث الشذوذ بل أن يكون شكل الانكسار قابلاً للقراءة.
inject["وطء الفشل عمداً"] --> q1["هل يبقى سياق في السجلّ"]
inject --> q2["هل يجري clean up"]
inject --> q3["هل ينكسر الأمر بإعادة المحاولة"]
q1 --> goal["حالة شكل الانكسار قابل للقراءة"]
q2 --> goal
q3 --> goal
الشكل 11: غرض fault injection ليس إحداث الشذوذ، بل تأكيد هل شكل الانكسار عند الشذوذ قابل للقراءة.
4.3. التحقّق من إمكان التتبّع عند شذوذ المقابض
كما في handle leak الذي ظهر في الجزء الأوّل، حول المقابض يسهل انحراف آخر موضع سقوط عن السبب الحقيقيّ.
لذلك ما أُريد تأكيده كهذا.
- عند خروج invalid handle stop، هل يمكن تتبّع open / close بـ
!htrace - هل يرتبط بـ
resourceId/sessionId/phaseفي السجلّ الذاتيّ - هل يعود handle count بعد الفشل
- عند جعل harness عمليّة قصيرة العمر، هل يسهل رؤية فرق التسريب
إذا ظهر هذا القدر، يمكن الانتقال من مجرّد «ظهر خطأ» إلى «أيّ مسؤوليّة انهارت فيها إدارة العمر».
flowchart TB
accTitle: تأكيد إمكان التتبّع عند شذوذ المقابض
accDescr: يبيّن تأكيد هل يمكن تتبّع open وclose بـ htrace عند خروج invalid handle stop، وهل يرتبط بسياق السجلّ الذاتيّ، وهل يعود handle count، للوصول إلى تحديد المسؤوليّة التي انهارت فيها إدارة العمر.
stop["invalid handle stop"] --> c1["تتبّع open وclose بـ htrace"]
stop --> c2["الربط بسياق السجلّ الذاتيّ"]
stop --> c3["تأكيد عودة handle count"]
c1 --> goal["تحديد المسؤوليّة التي انهارت فيها إدارة العمر"]
c2 --> goal
c3 --> goal
الشكل 12: عند شذوذ المقابض لا نتوقّف عند «ظهر خطأ»، بل نؤكّد إمكان التتبّع حتّى أيّ مسؤوليّة انهارت فيها إدارة العمر.
5. كيف تُحدَث ظاهرة تشبه نقص الذاكرة أو نقص الموارد
5.1. فكرة Low Resource Simulation
Low Resource Simulation هو ما يُسمّى fault injection. الصورة ليست إعادة إنتاج بيئة منخفضة الموارد طبق الأصل، بل خلط اصطناعيّ لفشل API نموذجيّ يحدث عند انخفاض الموارد.
لذلك موضع الاستخدام واضح جدّاً.
- تأكيد التنظيف بعد مسار الفشل
- تأكيد صلابة retry / reconnect
- تأكيد تهيئة يختلط فيها نجاح جزئيّ وفشل جزئيّ
- تأكيد بقاء السجلّ حتّى عند «فشل لا يحدث عادة»
الحيلة هنا عدم إفشال كلّ شيء من البداية. إذا جُمع الكلّ دفعة واحدة تنفجر السجلّات ويضيع «ماذا ننظر إليه».
flowchart TB
accTitle: طريقة تضييق fault injection
accDescr: يبيّن أنّ إفشال كلّ شيء من البداية ينفجر السجلّات فلا يُعرَف ماذا يُنظَر إليه، لذا يُفتَح مضيَّقاً من الفشل القريب من مسار الفشل المراد النظر إليه.
all["إفشال الكلّ من البداية"] --> noise["انفجار السجلّات فلا تُقرأ"]
narrow["فتح الفشل المراد فقط"] --> clear["وضوح ما يُنظَر إليه"]
الشكل 13: حيلة fault injection عدم جمع الكلّ، بل فتح ما يقرب من مسار الفشل المراد النظر إليه أوّلاً.
5.2. ما يمكن إفشاله
في Low Resource Simulation يمكن إفشال أنواع API التالية نموذجيّاً باحتمال.
| النوع | مثال | مثال في تطبيق التحكّم بالأجهزة |
|---|---|---|
Heap_Alloc |
تخصيص كومة | مخزن مؤقّت، بيانات وصف الصورة، تخصيص داخل غلاف SDK |
Virtual_Alloc |
تخصيص ذاكرة افتراضيّة | مخزن إطارات أكبر، مخزن حلقيّ |
File |
CreateFile وغيرها |
open لمسار الحفظ أو ملفّ السجلّ |
Event |
CreateEvent وغيرها |
إشعار جاهزيّة الإطار، مزامنة stop/reconnect |
MapView |
CreateMapView وغيرها |
ذاكرة مشتركة أو memory mapped file |
Ole_Alloc |
SysAllocString وغيرها |
حدود COM / OLE |
Wait |
عائلة WaitForXXX |
حول فشل انتظار المزامنة |
Registry |
الوصول إلى السجلّ | قراءة الإعداد وكتابته أو إعداد حول التعريف |
في العمل، فتح ما يقرب من مسار الفشل المراد النظر إليه في هذه الحالة أوّلاً أهمّ من فتح الكلّ معاً.
5.3. طريقة التطبيق في العمل
كصورة لسطر الأوامر يصير مثلاً هكذا.
appverif /verify CameraHarness.exe
appverif /verify CameraHarness.exe /faults
appverif -enable lowres -for CameraHarness.exe -with heap_alloc=20000 virtual_alloc=20000 file=20000 event=20000
appverif -query lowres -for CameraHarness.exe
لا معنى للنسخ إن لم يُقرأ القصد، لذا نكتب ماذا يفعل كلّ سطر.
| الأمر | ماذا يفعل |
|---|---|
appverif /verify CameraHarness.exe |
تفعيل مجموعة اختبارات Basics لـ CameraHarness.exe |
appverif /verify CameraHarness.exe /faults |
فوق ذلك تفعيل fault injection. لكنّ الهدف OLE_ALLOC وHEAP_ALLOC فقط |
appverif -enable lowres -for CameraHarness.exe -with heap_alloc=20000 ... |
تفعيل lowres (Low Resource Simulation) وتعيين نوع API المراد إفشاله والاحتمال فرداً |
appverif -query lowres -for CameraHarness.exe |
عرض ما هو معيَّن بأيّ احتمال الآن |
appverif /n CameraHarness.exe |
حذف إعداد ذلك EXE (نفس غرض -disable * -for أو -delete settings -for) |
نضبط أيضاً قراءة الوسائط.
- الاحتمال جزء من مليون. ما يمكن تعيينه عدد صحيح من 0 إلى 1,000,000، و
20000هو20000 / 1,000,000أي 2%. ليس «مرّة كلّ 20 ألفاً». وثائق Microsoft أيضاً تذكر-with registry=20000 file=20000كمثال لإفشال API السجلّ والملفّات بـ 2%. - بعد
/faultsيمكن صفّ احتمال ومهلة سماح واسم DLL. الصيغة/faults [احتمال [مهلة بالميلي ثانية [DLL ...]]]. إذا أُغفل الاحتمال يصير 5%، وإذا أُغفلت مهلة السماح تصير 500 ميلي ثانية. مهلة السماح تعني «لا يُدخَل fault خلال هذا الزمن من تشغيل العمليّة»، لمنع فشل المعالجة عند التشغيل نفسها فلا يمكن تجربة شيء. /nإلغاء. اعتبرnبمعنى «no verifier» تقريباً. الأمر المقابل لعدم ترك التفعيل مستمرّاً.
خرج -query lowres يعود تقريباً بهذا الشكل. هنا يمكن تأكيد هل الاحتمال المعيَّن داخل، وهل فُتحت أنواع لم تُقصَد.
Settings for CameraHarness.exe:
Test [lowres] enabled.
Include = *
Exclude =
TimeOut = 2000 (0x7D0)
WAIT = 0 (0x0)
HEAP_ALLOC = 20000 (0x4E20)
VIRTUAL_ALLOC = 0 (0x0)
REGISTRY = 0 (0x0)
FILE = 20000 (0x4E20)
EVENT = 20000 (0x4E20)
MAP_VIEW = 0 (0x0)
OLE_ALLOC = 0 (0x0)
STACKS = false
Include وExclude تضييق الوحدات المستهدفة، وTimeOut الزمن الذي لا يُدخَل فيه fault فور التشغيل. القيمة الافتراضيّة تختلف حسب طريقة إدخال الإعداد، لذا التأكيد بهذا الخرج أوثق من الافتراض.
كفكرة، بهذا الشكل.
- أوّلاً تدوير المسار العاديّ بـ
Basicsفقط - ثمّ إضافة
Low Resource Simulationوالتدوير مع fault injection - عند الحاجة إعطاء احتمال للفشل المراد فقط مثل
fileأوevent - إذا أُريد التوجيه إلى DLL معيَّن، يُدخَل مضيَّقاً إلى ذلك DLL
اختصار /faults مريح، لكن بهذا وحده التركيز على OLE_ALLOC وHEAP_ALLOC.
إذا أُريد النظر إلى مسار فشل CreateFile أو CreateEvent، فأوثق كتابة -enable lowres -with file=... event=... أيضاً.
في تطبيقات التحكّم بالأجهزة، التضييق إلى DLL غلاف الكاميرا أو مسار الحفظ أوضح للقراءة غالباً من نثر fault على التطبيق كلّه.
flowchart TB
accTitle: ترتيب تطبيق fault injection
accDescr: تطبيق مرحليّ: أوّلاً المسار العاديّ بـ Basics فقط، ثمّ التدوير مع إضافة Low Resource Simulation، ثمّ تعيين احتمال للفشل المراد فقط، ثمّ الإدخال مضيَّقاً إلى DLL معيَّن عند الحاجة.
s1["المسار العاديّ بـ Basics فقط"] --> s2["التدوير مع إضافة Low Resource"]
s2 --> s3["تعيين احتمال للفشل المراد فقط"]
s3 --> s4["الإدخال مضيَّقاً إلى DLL معيَّن"]
الشكل 14: لا يُستهدف من البداية، بل يُضيَّق fault injection مرحليّاً من المسار العاديّ بـ Basics.
نضع أيضاً الكتابة العمليّة لـ «التضييق إلى DLL». الوسائط من الثالث فصاعداً في /faults تعيين الوحدة المستهدفة.
appverif /verify CameraHarness.exe /faults 50000 1000 CameraSdkWrapper.dll
بهذا، عند تشغيل CameraHarness.exe، يُفشَل باحتمال 5% (50000 / 1,000,000) العمليّات التي بدأت من CameraSdkWrapper.dll فقط، بعد مرور 1000 ميلي ثانية من التشغيل. اسم الوحدة يُكتَب مع الامتداد، بلا مسار. يمكن تعيين وحدات تُحمَّل غير .dll أيضاً مثل .ocx.
هل التضييق نافذ يُؤكَّد من صفّي Include وExclude في appverif -query lowres -for CameraHarness.exe. إذا بقي * فما زال الهدف العمليّة كلّها.
flowchart TB
accTitle: حركة fault injection المضيَّق إلى DLL
accDescr: يبيّن أنّ تعيين الاحتمال ومهلة السماح والوحدة المستهدفة في وسائط faults يفشل بعد انتهاء مهلة السماح من التشغيل العمليّات التي بدأت من DLL المعيَّن فقط بالاحتمال المعيَّن، ويُؤكَّد التضييق بـ Include وExclude في query.
arg["تعيين الاحتمال ومهلة السماح واسم DLL"] --> grace["لا حقن خلال مهلة السماح من التشغيل"]
grace --> target["فشل العمليّات الصادرة عن DLL المعيَّن فقط"]
target --> check["التأكيد بـ Include وExclude في query"]
الشكل 15: fault injection المضيَّق إلى DLL يفشل بعد انتهاء مهلة السماح العمليّات التي بدأت من الوحدة المعيَّنة فقط.
إذا شُغِّل تحت المصحّح يمكن أيضاً تغيير النطاق في الوسط.
!avrf -trg dll CameraSdkWrapper.dll
!avrf -skp dll VendorSdk.dll
-trg «استهدف هنا»، و-skp «تجاوز هنا». يمكن أيضاً تأكيد إعداد fault injection الحاليّ بـ !avrf -flt، أو النظر إلى مكدّس الفشل المحقون أخيراً بـ !avrf -flt stacks 10.
مثلاً يمكن صنع سيناريوهات كهذه.
- فشل
CreateEventفور بدء reconnect - فشل
CreateFileعند بدء الحفظ - فشل تخصيص مخزن مؤقّت
- فشل
SysAllocStringفي تحويل COM - تأكيد مسار فشل API الانتظار
هذه لا تُوطأ عادة باختبار المسار السليم العاديّ. لذلك قيمة وطئها عمداً موجودة.
6. كيف يُنظَر إلى شذوذ المقابض
6.1. فحص Handles
حول المقابض يُستخدَم أوّلاً Handles.
بهذا يسهل كشف استخدام invalid handle.
ما ينفع نموذجيّاً حوادث كهذه.
- إعادة استخدام مقبض أُغلق
- تمرير قيمة مقبض تالفة
- استخدام مقبض لم يُهيَّأ بعد فشل في الوسط
- لمس من خيط آخر بعد انهيار lifetime
ما يبدو في التشغيل الطويل «خطأ غريب أحياناً فقط» قد يتوقّف في مكانه تحت verifier. هذا التقديم يساعد كثيراً.
flowchart TB
accTitle: حوادث ينفع فيها فحص Handles
accDescr: يبيّن أنّ حوادث مثل إعادة استخدام مقبض أُغلق، وقيمة مقبض تالفة، ومقبض غير مهيَّأ بعد فشل في الوسط، وإساءة استخدام من خيط آخر بانهيار العمر، يمكن إيقافها في مكانها تحت verifier.
a1["إعادة استخدام ما أُغلق"] --> stop["verifier stop في مكانه"]
a2["قيمة handle تالفة"] --> stop
a3["handle غير مهيَّأ"] --> stop
a4["إساءة استخدام بانهيار العمر"] --> stop
الشكل 16: حوادث تمرّ في التشغيل الطويل كـ «خطأ غريب أحياناً» يوقفها فحص Handles في مكانها.
6.2. النظر إلى مكدّس open / close بـ !htrace
ما يمتنّ في Handles انسجامه مع handle tracing.
من هنا يُستخدَم المصحّح، لذا يُدخَل WinDbg أوّلاً. يُوزَّع كـ Debugging Tools for Windows، ويمكن إدخاله من مثبّت Windows SDK نفسه الذي فيه Application Verifier.
windbg -xd av -xd ch -xd sov CameraHarness.exe
!avrf
!htrace 0x00000ABC
خيارات السطر الأوّل ليست تعويذة. الاستثناءات التي يرميها Application Verifier عند الكشف ثلاثة أنواع.
| الخيار | الاستثناء | متى يخرج |
|---|---|---|
av |
انتهاك وصول (0xC0000005) |
عند كشف تجاوز مخزن الكومة |
ch |
مقبض غير صالح (0xC0000008) |
عند كشف استخدام invalid handle |
sov |
فيضان المكدّس (0xC00000FD) |
عند الحكم بنقص المكدّس الابتدائيّ |
و-xd تعيين إمساك ذلك الاستثناء في الفرصة الثانية. الفرصة الأولى يعالجها Application Verifier نفسه لتركيب معلومات stop، لذا مقاطعة المصحّح أوّلاً غير مناسبة. إذا عُيِّن في مصحّح شُغِّل أصلاً، فهو نفسه ضرب sxd av وsxd ch وsxd sov.
ما يُراد النظر إليه بـ !htrace تقريباً هذا.
- أين فُتح ذلك المقبض
- أين أُغلق
- هل أُشير إليه كـ invalid handle
- هل تراكمت open أكثر ممّا يُفترَض
نضع أيضاً شكل الظهور الفعليّ. التالي مثال خرج وارد في الوثائق الرسميّة، وليس من بيئتنا، لكنّ الشكل نفسه.
عند وطء invalid handle يخرج أوّلاً هكذا.
Invalid handle - code c0000008 (first chance)
===================================================
VERIFIER STOP 00000300: pid 0x558: invalid handle exception for current stack trace
C0000008 : Exception code.
0012FBF8 : Exception record. Use .exr to display it.
0012FC0C : Context record. Use .cxr to display it.
00000000 :
===================================================
This verifier stop is continuable.
After debugging it use 'go' to continue.
===================================================
ثمّ بضرب !avrf يظهر ما هو مفعَّل الآن وأيّ stop يحدث. السطر الأخير خلاصة.
0:000> !avrf
Global flags: 00000100
Application verifier global flag is set.
Application verifier settings (00000004):
- no heap checking enabled!
- handle checks
Page heap is not active for this process.
Current stop 00000300 : c0000008 0012fbf8 0012fc0c 00000000 .
Using an invalid handle (either closed or simply bad).
بالنظر إلى تاريخ ذلك المقبض بـ !htrace تصطفّ OPEN / CLOSE / BAD REFERENCE كلٌّ مع مكدّس.
0:000> !htrace 7DC
--------------------------------------
Handle 0x7DC - BAD REFERENCE:
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x010012C1: badhandle!mainCRTStartup+0xE3
--------------------------------------
Handle 0x7DC - CLOSE:
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
--------------------------------------
Handle 0x7DC - OPEN:
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
--------------------------------------
القراءة صريحة: إذا جاء BAD REFERENCE بعد CLOSE فمعناه إعادة استخدام مقبض أُغلق. بالنظر إلى مكدّس OPEN يُعرَف أيضاً أين أُنشئ ذلك المقبض.
ما يزعج في handle leak أو handle misuse أنّ آخر API سقطت ليست السبب الحقيقيّ.
بوجود !htrace يمكن تتبّع تاريخ ذلك المقبض بتفصيل كبير.
flowchart TB
accTitle: طريقة قراءة تاريخ المقبض بـ htrace
accDescr: يبيّن أنّ htrace يصفّ OPEN وCLOSE وBAD REFERENCE للمقبض كلّاً مع مكدّس، وأنّ مجيء BAD REFERENCE بعد CLOSE يعني إعادة استخدام مقبض أُغلق، وأنّ مكدّس OPEN يكشف أيضاً موضع الإنشاء.
open["OPEN (موضع الإنشاء)"] --> close["CLOSE (موضع الإغلاق)"]
close --> bad["BAD REFERENCE"]
bad --> mean["تبيّن إعادة استخدام handle أُغلق"]
open -.-> stack["كلّ سجلّ يحمل مكدّساً"]
الشكل 17: قراءة htrace صريحة: إذا اصطفّ BAD REFERENCE بعد CLOSE فإعادة استخدام مقبض أُغلق.
6.3. كيف يُجمع مع السجلّ الذاتيّ
مع ذلك، Application Verifier وحده لا يكفي. خصوصاً تحقيق تسريب EXE مقيم طويل الأمد بهذه الأداة وحدها متعب جدّاً.
لذلك في العمل يُجمع التالي.
Handle CountدوريّsessionIdresourceIdphase- سجلّ دورة حياة create/open وclose/dispose
- dump وخرج المصحّح عند verifier stop
بهذا يمكن التتبّع مثلاً هكذا.
- بـ heartbeat يتّضح أنّ ميل
Handle Countمشبوه - بسجلّ دورة الحياة يُضيَّق مورد فيه
CreateبلاClose - بـ verifier run يُخرَج invalid handle أو إساءة الاستخدام في وقت أبكر
- بـ
!htraceيُنظَر إلى مكدّس open / close
بهذا الجمع يصير التتبّع أسهل كثيراً.
flowchart TB
accTitle: ترتيب جمع السجلّ الذاتيّ مع verifier
accDescr: يبيّن التتبّع بهذا الترتيب: الانتباه إلى ميل Handle Count بـ heartbeat، وتضييق المورد بلا Close بسجلّ دورة الحياة، وإخراج إساءة الاستخدام في وقت أبكر بـ verifier run، والنظر إلى مكدّس open وclose بـ htrace.
s1["الانتباه إلى ميل Handle Count"] --> s2["تضييق المورد بسجلّ دورة الحياة"]
s2 --> s3["إخراج إساءة الاستخدام في وقت أبكر بـ verifier run"]
s3 --> s4["النظر إلى المكدّس بـ htrace"]
الشكل 18: كشف الميل بالسجلّ الذاتيّ وكشف إساءة الاستخدام بـ verifier، يُربَطان بهذا الترتيب.
7. طريقة بناء بنية اختبار المسارات الشاذّة
7.1. إمالة وحدة التنفيذ إلى harness
Application Verifier لا يمكن تفعيله لاحقاً على عمليّة جارية. تعيين ثمّ تشغيل.
وفوق ذلك يبقى الإعداد حتّى يُحذف صراحة. لذلك في العمل الإمالة إلى harness EXE للاختبار أسهل من التطبيق الإنتاجيّ نفسه.
مثلاً تكوين كهذا.
flowchart LR
A["Scenario Runner"] --> B["CameraHarness.exe"]
B --> C["CameraSdkWrapper.dll"]
C --> D["Vendor SDK"]
B --> E["Structured Log"]
B --> F["Dump / Debugger"]
الشكل 19: تكوين harness يُدار بعمليّة واحدة لكلّ سيناريو. هدف verifier ليس DLL بل harness EXE الذي يشغّله.
بهذا،
- يمكن التدوير بعمليّة واحدة لكلّ سيناريو
- يسهل رؤية فرق التسريب
- يسهل تبديل إعداد AppVerifier بين ON/OFF
- حتّى عند تجربة DLL يمكن التعامل من جانب EXE
هذه الفوائد موجودة.
صورة الأمر هكذا.
appverif /verify CameraHarness.exe
appverif /n CameraHarness.exe
/verify تفعيل Basics، و/n حذف الإعداد (انظر أيضاً جدول 5.3).
التفعيل قبل التشغيل، والإلغاء صراحة.
تدوير هذا بافتراض harness يقلّل أيضاً حوادث الإعداد.
7.2. تقسيم قائمة الاختبار
في بنية اختبار المسارات الشاذّة، عدم فعل الكلّ في مرّة واحدة أفضل. التقسيم تقريباً إلى ثلاثة أوضح للقراءة.
- المسار العاديّ + Basics
- لا حقن فشل
- تأكيد عدم خروج verifier stop
- عائلة fault injection
Low Resource Simulation- إفشال
event/file/heap_alloc/virtual_allocوغيرها عمداً
- عائلة تعميق الكومة
Heaps- full page heap
- إعادة إنتاج محلّيّة تحت المصحّح
بتقسيم هذا، يصعب اختلاط «هل ينكسر في الاستخدام العاديّ» مع «هل ينكسر عند انخفاض الموارد فقط».
خصوصاً بوجود fault injection أو غيابه يتغيّر مسار الشيفرة الممرور به كثيراً. لذلك يُفضَّل تدوير run بلا fault وrun مع fault كليهما.
flowchart TB
accTitle: قائمة اختبار مقسومة إلى ثلاثة
accDescr: يبيّن تقسيم الاختبار إلى ثلاثة: المسار العاديّ زائد Basics بلا حقن، وعائلة fault injection التي تفشل عمداً بـ Low Resource Simulation، وعائلة تعميق الكومة التي تدير full page heap تحت المصحّح.
menu["طريقة تدوير اختبار المسارات الشاذّة"] --> m1["المسار العاديّ وBasics"]
menu --> m2["عائلة fault injection"]
menu --> m3["عائلة تعميق الكومة"]
m1 -.-> p1["تأكيد عدم خروج stop"]
m2 -.-> p2["حقن الفشل المقصود"]
m3 -.-> p3["إعادة إنتاج محلّيّة تحت المصحّح"]
الشكل 20: عدم فعل الكلّ في مرّة واحدة وتقسيم القائمة إلى ثلاثة يمنع اختلاط أين ينكسر الأمر.
7.3. ما يُجمَع
في الحدّ الأدنى يُراد أخذ هذا القدر.
| النوع | المطلوب |
|---|---|
| سجلّ التطبيق | cameraId, sessionId, phase, handleCount, error code |
| حالة العمليّة | Handle Count, Private Bytes, Thread Count |
| معلومات المصحّح | !avrf, !htrace, و!heap -p -a عند الحاجة |
| dump | عند verifier stop أو عند الإنهاء الشاذّ |
| سجلّ AppVerifier | سجلّ stop، وتحويله إلى XML للتجميع عند الحاجة |
عند الحاجة يمكن أيضاً تحويل سجلّ جانب AppVerifier إلى XML وتجميعه. لكن النظر إليه وحده كثيراً ما لا يغلق السبب، لذا افتراض القراءة بمحاذاة السجلّ الذاتيّ أنسب للعمل.
كثرة السجلّات نفسها ليست فضلاً. ارتباط السببيّة لاحقاً هو المهمّ.
7.4. شروط القبول
شرط القبول «لم يسقط» وحده ضعيف أيضاً. في سياق هذه الحالة لزم على الأقلّ التالي.
- عدم خروج verifier stop في المسار العاديّ + Basics
- بقاء الفشل المتوقَّع في السجلّ حتّى مع fault injection
- تنظيف الموارد المهيَّأة ناقصة بشكل سليم
- عودة
Handle Countقرب baseline بعد reconnect / retry - إمكان التتبّع بـ
sessionId/phase/ المكدّس عند خروج verifier stop - عدم صيرورة الفشل «ما حدث غير معلوم»
المهمّ هنا تقييم عدم الانكسار وإمكان التتبّع عند الانكسار منفصلين.
flowchart TB
accTitle: محوران لشروط القبول
accDescr: يبيّن تقييم شروط القبول على محورين: عدم الانكسار بعدم خروج verifier stop في المسار العاديّ وتنظيف الموارد، وإمكان التتبّع عند الانكسار ببقاء الفشل المتوقَّع في السجلّ وإمكان التتبّع بالسياق والمكدّس.
pass["شروط القبول"] --> a["عدم الانكسار"]
pass --> b["إمكان التتبّع عند الانكسار"]
a --> a1["عدم خروج stop"]
a --> a2["تنظيف الموارد"]
b --> b1["بقاء الفشل في السجلّ"]
b --> b2["إمكان التتبّع بالمكدّس"]
الشكل 21: «لم يسقط» وحده ضعيف، ويُقيَّم عدم الانكسار وإمكان التتبّع كمحورين منفصلين.
7.5. تنبيهات
Application Verifier مريح جدّاً، لكنّه ليس سحراً.
- مسار الشيفرة الذي لم يُمَرّ به فعليّاً لا يُتحقَّق منه
- full page heap ثقيل
- قد يخرج stop من جانب SDK طرف ثالث أيضاً
- مسار الشيفرة الممرور به يختلف كثيراً بوجود fault injection أو غيابه
- ليست أداة لتحقيق تسريب كومة managed خالص بهذه الأداة وحدها
لذلك كموقع هكذا.
- الميل الطويل بالسجلّ الذاتيّ والعدّادات
- إساءة الاستخدام عند الحدود الأصليّة بـ Application Verifier
- استعادة السببيّة عند الشذوذ بـ structured log + dump + المصحّح
هذا التقسيم أنسب للعمل.
flowchart TB
accTitle: الصورة الكلّيّة لتقسيم التحقيق
accDescr: يبيّن التقسيم: الميل الطويل بالسجلّ الذاتيّ والعدّادات، وإساءة الاستخدام عند الحدود الأصليّة بـ Application Verifier، واستعادة السببيّة عند الشذوذ بالسجلّ وdump والمصحّح.
q1["الميل الطويل"] --> t1["السجلّ الذاتيّ والعدّادات"]
q2["إساءة الاستخدام عند الحدود الأصليّة"] --> t2["Application Verifier"]
q3["استعادة السببيّة عند الشذوذ"] --> t3["السجلّ وdump والمصحّح"]
الشكل 22: Application Verifier ليس عصا سحريّة كليّة، بل تُقسَم الأداة حسب المراد النظر إليه.
8. دليل اختيار تقريبيّ
- اشتباه invalid handle أو double close
Handles+!htrace
- اشتباه heap corruption / use-after-free
Heaps+ full page heap +!heap -p -a
- إرادة إحداث ظاهرة تشبه نقص الذاكرة أو نقص الموارد
Low Resource Simulation
- انكسار تدريجيّ في التشغيل الطويل
- أوّلاً
Handle Count/Private Bytes/ سجلّ دورة الحياة الذاتيّ
- أوّلاً
- إرادة تجربة DLL
- تفعيل Application Verifier على harness EXE الذي يستدعي ذلك DLL
جمع الكلّ من البداية يصير عادة ضباب سجلّات. تطبيق الشفرة القريبة من مسار الفشل المراد النظر إليه أوضح كثيراً.
9. الخلاصة
موقع Application Verifier متحقّق وقت التشغيل لحدود native / Win32 في Windows. باستخدام Handles / Heaps / Locks / Memory / TLS / Low Resource Simulation وغيرها يمكن وطء مسار الفشل الذي يصعب ظهوره عادة في وقت أبكر.
ما نفع في سياق هذه الحالة سهولة التتبّع بـ !htrace عند شذوذ المقابض، وإمكان إحداث ظاهرة تشبه نقص الذاكرة أو نقص الموارد دون كسر الجهاز كلّه، وتأكيد هل السجلّ الذاتيّ ينفع حقّاً عندئذ.
كطريقة تدوير في العمل، فصل المسار العاديّ + Basics عن عائلة fault injection، وتجهيز harness EXE وتدوير السيناريو بعمليّة قصيرة العمر. فوق ذلك الجمع مع السجلّ الذاتيّ وdump ومعلومات المصحّح، والنظر إلى ميل التسريب الطويل نفسه بعدّادات ذاتيّة. هذا التقسيم.
Application Verifier أداة لـ «الذهاب لاستقبال» الشذوذ نادر الظهور، لا «انتظار حدوثه مصادفة».
في تطبيقات التحكّم بالأجهزة، عدم الانكسار مهمّ، لكن إمكان شرح ما حدث عند الانكسار مهمّ بالقدر نفسه. بهذا المعنى أراها أداة أنسب للعمل كثيراً.
الجزء الأوّل: تحقيق تعطّل الكاميرا الصناعيّة بعد تشغيل طويل: فصل handle leak
10. روابط مرجعيّة
- الجزء الأوّل: تحقيق تعطّل الكاميرا الصناعيّة بعد تشغيل طويل: فصل handle leak
- Application Verifier - Overview
- Application Verifier - Testing Applications
- Application Verifier - Tests within Application Verifier
- Application Verifier - Debugging Application Verifier Stops
- Application Verifier - Features
- !htrace (WinDbg)
- !avrf (WinDbg)
- Download Debugging Tools for Windows
- Download the Windows SDK
- GetProcessHandleCount function (processthreadsapi.h)
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
تحقيق تعطّل الكاميرا الصناعيّة بعد تشغيل طويل: فصل handle leak
نرتّب طريقة النظر إلى تعطّل تطبيق Windows فجأة بعد تشغيل طويل، من زوايا إيجاد handle leak وتصميم السجلّات، بحالة تطبيق تحكّم بكاميرا صناع...
توقّف اتّصال الكاميرا الصناعيّة بسبب إعادة إرسال TCP: السبب والتضييق
نرتّب طريقة تضييق توقّف اتّصال الكاميرا الصناعيّة لعدّة ثوانٍ بسبب إعادة إرسال TCP، مع فقد الحزم وRTO وطوابع RFC1323 ونقاط التحقّق في Wir...
Time Travel Debugging ── تسجيل الأخطاء التي لا تتكرّر في التطبيقات طويلة التشغيل وإرجاعها
خطأ يظهر مرّة في الشهر لا يترك في تفريغ الانهيار سوى نتيجته. سجّل التنفيذ وأرجعه بـ WinDbg Time Travel Debugging (TTD): TTD.exe والمخزن ا...
لماذا يعمل مجلّد مشترك في Windows أحياناً ويفشل أحياناً ── فصل Kerberos وNTLM وبيانات الاعتماد
شخّص انقطاع الوصول المتقطّع إلى مجلّد مشترك في Windows من الأعراض والسجلّات. راجع الأسماء مقابل عناوين IP، والفشل في التطبيق وحده، وكلمات...
القرص عند 100٪: ما الذي ينبغي إيقافه فعليّاً؟ — التمييز بين SysMain وWindows Search وDefender
اعزل استخدام قرص Windows عند 100٪ عبر معدّل النقل وزمن الاستجابة والملفّات. أوقف SysMain مؤقّتاً بأمان، وضيّق نطاق Windows Search، وحلّل ...
دراسة حالة ذات صلة
صفحة دراسة حالة توضّح بنية مشابهة للتشخيص أو تحديد الأولويات أو إعادة التصميم.
دراسة حالة: بنية اختبار مسارات الفشل باستخدام Application Verifier
دراسة حالة لبناء أساس اختبار مسارات الفشل يسهّل التحقيق في الأعطال المستقبلية.
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
التحقيق في الأخطاء وتحليل السبب الجذري
Application Verifier وبنية اختبار الحالات الشاذة موضوع محوري في التحقيق في الأخطاء وتحليل الأسباب، حيث يتقدّم العمل بإعادة إنتاج العطل وتحديد سببه.
الاستشارات التقنية ومراجعة التصميم
إذا أردتم ترتيب المدى الذي ينبغي عنده دمج اختبارات الحالات الاستثنائيّة ونقاط الرصد في التصميم، فيمكن بحث ذلك ضمن الاستشارة التقنيّة ومراجعة التصميم.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- ما هو Application Verifier؟
- أداة تحقّق وقت التشغيل لتطبيقات user-mode في Windows. تراقب استخدام التطبيق الجاري لـ OS API وطريقة تعامله مع الموارد، فتكشف استخداماً مشبوهاً مثل invalid handle أو heap corruption، ويمكنها أيضاً حقن فشل عمداً. بخلاف التحليل الساكن أو اختبار الوحدة، هي أداة تنظر كيف ينكسر الأمر عند المرور فعليّاً بمسار الشيفرة ذلك، لذا تناسب إبراز مسار الفشل الذي لا يظهر في اختبار الوظائف العاديّ.
- هل يمكن إعادة إنتاج نقص الذاكرة بـ Application Verifier؟
- باستخدام Low Resource Simulation يمكن إحداث ظاهرة قريبة من نقص الذاكرة أو نقص الموارد في وقت أبكر دون استنزاف RAM الجهاز فعلاً. الآليّة fault injection: إفشال استدعاءات API مثل HeapAlloc وVirtualAlloc وCreateFile وCreateEvent عمداً باحتمال ثابت. يمكن أيضاً حقن الفشل موجَّهاً إلى DLL معيَّن، فيسهل التعامل حتّى في تكوين يختلط فيه غلاف ذاتيّ مع SDK البائع. لكن إفشال كلّ شيء من البداية يجعل السجلّات غير قابلة للقراءة، لذا الحيلة فتح ما يقرب من مسار الفشل المراد النظر إليه أوّلاً.
- هل يُستخدَم Application Verifier في تحقيق handle leak؟
- بتفعيل فحص Handles يمكن كشف استخدام invalid handle مثل إعادة استخدام مقبض أُغلق، ويُفعَّل handle tracing تلقائيّاً أيضاً فيمكن تتبّع مكدّس open / close لذلك المقبض بـ !htrace. لكن إلقاء تحقيق تسريب EXE مقيم طويل الأمد على Application Verifier وحده غير واقعيّ. الجمع مع تسجيل دوريّ لـ Handle Count وسجلّ ذاتيّ لدورة حياة المورد، بحيث يكشف السجلّ الذاتيّ الميل ويكشف verifier إساءة الاستخدام، أنسب للعمل.
- كيف يُستخدَم Application Verifier لاختبار DLL؟
- هدف تفعيل Application Verifier هو EXE الاختبار الذي يشغّل ذلك DLL فعليّاً. لا يمكن التفعيل لاحقاً على عمليّة جارية، ويلزم التعيين ثمّ التشغيل. والإعداد يبقى حتّى يُحذف صراحة، لذا الإمالة إلى harness EXE للاختبار أسهل من التطبيق الإنتاجيّ نفسه. التدوير بعمليّة واحدة لكلّ سيناريو يسهّل أيضاً رؤية فرق التسريب وتبديل إعداد ON/OFF.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.