في Chrome Dev Summit 2020، عرضنا للمرة الأولى على الويب ميزة تصحيح الأخطاء في Chrome لتطبيقات WebAssembly. ومنذ ذلك الحين، استثمر الفريق الكثير من الطاقة في توسيع نطاق تجربة المطوّرين لتشمل التطبيقات الكبيرة وحتى الضخمة. في هذه المشاركة، سنعرض لك المقابض التي أضفناها (أو أجريناها) في الأدوات المختلفة وكيفية استخدامها.
تصحيح الأخطاء القابل للتطوير
لنتابع من حيث توقّفنا في مشاركتنا لعام 2020. في ما يلي المثال الذي كنا ننظر إليه في ذلك الوقت:
#include <SDL2/SDL.h>
#include <complex>
int main() {
// Init SDL.
int width = 600, height = 600;
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window;
SDL_Renderer* renderer;
SDL_CreateWindowAndRenderer(width, height, SDL_WINDOW_OPENGL, &window,
&renderer);
// Generate a palette with random colors.
enum { MAX_ITER_COUNT = 256 };
SDL_Color palette[MAX_ITER_COUNT];
srand(time(0));
for (int i = 0; i < MAX_ITER_COUNT; ++i) {
palette[i] = {
.r = (uint8_t)rand(),
.g = (uint8_t)rand(),
.b = (uint8_t)rand(),
.a = 255,
};
}
// Calculate and draw the Mandelbrot set.
std::complex<double> center(0.5, 0.5);
double scale = 4.0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
std::complex<double> point((double)x / width, (double)y / height);
std::complex<double> c = (point - center) * scale;
std::complex<double> z(0, 0);
int i = 0;
for (; i < MAX_ITER_COUNT - 1; i++) {
z = z * z + c;
if (abs(z) > 2.0)
break;
}
SDL_Color color = palette[i];
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderDrawPoint(renderer, x, y);
}
}
// Render everything we've drawn to the canvas.
SDL_RenderPresent(renderer);
// SDL_Quit();
}
ما زال مثالاً بسيطًا إلى حد ما، ولن ترى على الأرجح أيًا من المشكلات الحقيقية التي قد تواجهها في تطبيق كبير، ولكن لا يزال بإمكاننا توضيح الميزات الجديدة. يمكنك إعداد هذه الميزة وتجربتها بسرعة وسهولة.
في المشاركة الأخيرة، ناقشنا كيفية تجميع هذا المثال وتصحيح الأخطاء فيه. لنكرّر ذلك مرة أخرى، ولكن لنلقِ نظرة أيضًا على //performance//:
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH
ينتج عن هذا الأمر ملف ثنائي wasm بحجم 3 ميغابايت. ويتمثل الجزء الأكبر من ذلك، كما هو متوقّع، في معلومات تصحيح الأخطاء. يمكنك التحقّق من ذلك باستخدام أداة llvm-objdump
[1] على سبيل المثال:
$ llvm-objdump -h mandelbrot.wasm
mandelbrot.wasm: file format wasm
Sections:
Idx Name Size VMA Type
0 TYPE 0000026f 00000000
1 IMPORT 00001f03 00000000
2 FUNCTION 0000043e 00000000
3 TABLE 00000007 00000000
4 MEMORY 00000007 00000000
5 GLOBAL 00000021 00000000
6 EXPORT 0000014a 00000000
7 ELEM 00000457 00000000
8 CODE 0009308a 00000000 TEXT
9 DATA 0000e4cc 00000000 DATA
10 name 00007e58 00000000
11 .debug_info 000bb1c9 00000000
12 .debug_loc 0009b407 00000000
13 .debug_ranges 0000ad90 00000000
14 .debug_abbrev 000136e8 00000000
15 .debug_line 000bb3ab 00000000
16 .debug_str 000209bd 00000000
يعرض لنا هذا الإخراج جميع الأقسام الواردة في ملف wasm الذي تم إنشاؤه، ومعظمها أقسام WebAssembly عادية، ولكن هناك أيضًا عدة أقسام مخصّصة يبدأ اسمها بـ .debug_
. هذا هو المكان الذي يحتوي فيه الملف الثنائي على معلومات تصحيح الأخطاء. إذا أضفنا جميع الأحجام، نرى أنّ معلومات تصحيح الأخطاء تشكّل 2.3 ميغابايت تقريبًا من ملفنا الذي يبلغ حجمه 3 ميغابايت. إذا time
نا أيضًا الأمر emcc
، نلاحظ أنّه استغرق تشغيله على جهازنا 1.5 ثانية تقريبًا. تشكّل هذه الأرقام مرجعًا جيدًا، ولكنّها صغيرة جدًا لدرجة أنّه من المحتمل ألا يهتم بها أحد. في التطبيقات الفعلية، يمكن أن يصل حجم ملف الترميز الثنائي لأداة تصحيح الأخطاء بسهولة إلى وحدات غيغابايت ويتطلّب إنشاءه دقائق.
تخطّي Binaryen
عند إنشاء تطبيق wasm باستخدام Emscripten، تكون إحدى خطوات الإنشاء النهائية هي تشغيل أداة تحسين Binaryen. Binaryen هي مجموعة أدوات للمجمِّع تعمل على تحسين ملفات WebAssembly الثنائية (أو ما شابهها) وإجازتها. إنّ تشغيل Binaryen كجزء من عملية الإنشاء باهظ التكلفة إلى حدٍ ما، ولكنّه مطلوب فقط في حالات معيّنة. بالنسبة إلى عمليات الإنشاء لأغراض تصحيح الأخطاء، يمكننا تسريع وقت الإنشاء بشكل كبير إذا تجنّبنا الحاجة إلى عمليات Binaryen. إنّ الخطوة الأكثر شيوعًا المطلوبة في Binaryen هي لتشريع توقيعات الدوالّ التي تتضمّن قيمًا صحيحة بسعة 64 بت. يمكننا تجنُّب ذلك من خلال تفعيل دمج WebAssembly BigInt باستخدام -sWASM_BIGINT
.
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
لقد وضعنا علامة -sERROR_ON_WASM_CHANGES_AFTER_LINK
على وجه التحديد. ويساعد في رصد الحالات التي يعمل فيها Binaryen ويعيد كتابة الملف الثنائي بشكل غير متوقّع. بهذه الطريقة، يمكننا التأكد من أنّنا نبقى على المسار السريع.
على الرغم من أنّ مثالنا صغير نسبيًا، لا يزال بإمكاننا رؤية تأثير تخطّي Binaryen. وفقًا time
، يتم تنفيذ هذا الأمر في أقل من ثانية واحدة، أي أسرع بنصف ثانية من السابق.
تعديلات متقدّمة
تخطّي فحص ملف الإدخال
عند ربط مشروع Emscripten، عادةً ما يفحص emcc
كل ملفات ومكتبات عناصر الإدخال. ويُجري ذلك من أجل تنفيذ تبعيات دقيقة بين دوال مكتبة JavaScript والرموز الأصلية في برنامجك. بالنسبة إلى المشاريع الأكبر حجمًا، يمكن أن يؤدي هذا المسح الإضافي لملفات الإدخال (باستخدام llvm-nm
) إلى زيادة وقت الربط بشكل كبير.
من الممكن بدلاً من ذلك تشغيل -sREVERSE_DEPS=all
الذي يطلب من emcc
تضمين جميع التبعيات الأصلية المحتمَلة لدوالّ JavaScript. يتسبب هذا في زيادة طفيفة في حجم الرمز البرمجي، ولكنه يمكن أن يسرع أوقات الربط ويمكن أن يكون مفيدًا لعمليات تصحيح الأخطاء.
بالنسبة إلى مشروع صغير مثل المثال الذي نعرضه، لا يحدث هذا فرقًا كبيرًا، ولكن إذا كان لديك مئات أو حتى آلاف ملفات العناصر في مشروعك، يمكن أن يؤدي ذلك إلى تحسين أوقات الربط بشكل ملحوظ.
إزالة قسم "الاسم"
في المشاريع الكبيرة، خاصةً تلك التي تستخدم الكثير من نماذج C++، يمكن أن يكون قسم "الاسم" في WebAssembly كبيرًا جدًا. في المثال الذي نعرضه، يمثّل هذا الجزء جزءًا صغيرًا فقط من حجم الملف الإجمالي (راجِع نتيجة llvm-objdump
أعلاه)، ولكن يمكن أن يكون مهمًا جدًا في بعض الحالات. إذا كان قسم "الاسم" في تطبيقك كبيرًا جدًا، وكانت معلومات تصحيح الأخطاء في DWARF كافية لاحتياجات تصحيح الأخطاء، قد يكون من المفيد إزالة قسم "الاسم":
$ emstrip --no-strip-all --remove-section=name mandelbrot.wasm
سيؤدي ذلك إلى إزالة قسم "الاسم" في WebAssembly مع الاحتفاظ بأقسام تصحيح الأخطاء DWARF.
انشطار تصحيح الأخطاء
حيث لا تتطلب البرامج الثنائية التي تحتوي على الكثير من بيانات تصحيح الأخطاء الضغط على وقت الإصدار فحسب، بل أيضًا على وقت تصحيح الأخطاء. يحتاج مصحِّح الأخطاء إلى تحميل البيانات وإنشاء فهرس لها، حتى يتمكّن من الردّ بسرعة على طلبات البحث، مثل "ما هو نوع المتغيّر المحلي x؟".
تسمح لنا ميزة تقسيم تصحيح الأخطاء بتقسيم معلومات تصحيح الأخطاء لملف ثنائي إلى جزأين: جزء يبقى في الملف الثنائي وجزء آخر مضمّن في ملف منفصل يُعرف باسم ملف رمز DWARF (.dwo
). ويمكن تفعيلها من خلال تمرير العلامة -gsplit-dwarf
إلى Emscripten:
$ emcc -sUSE_SDL=2 -g -gsplit-dwarf -gdwarf-5 -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
في ما يلي الأوامر المختلفة والملفات التي يتم إنشاؤها من خلال عملية الترجمة بدون بيانات تصحيح الأخطاء، ومع بيانات تصحيح الأخطاء، وأخيرًا مع كلّ من بيانات تصحيح الأخطاء وميزة "تقسيم تصحيح الأخطاء".
عند تقسيم بيانات DWARF، يتم وضع جزء من بيانات تصحيح الأخطاء مع الملف الثنائي، في حين يتم وضع الجزء الأكبر في ملف mandelbrot.dwo
(كما هو موضّح أعلاه).
بالنسبة إلى mandelbrot
، لدينا ملف مصدر واحد فقط، ولكن بشكلٍ عام تكون المشاريع أكبر من هذا الملف وتتضمّن أكثر من ملف واحد. تُنشئ ميزة "تقسيم تصحيح الأخطاء" ملف .dwo
لكل منها. لكي يتمكّن الإصدار التجريبي الحالي من أداة تصحيح الأخطاء (0.1.6.1615) من تحميل معلومات تصحيح الأخطاء المُقسَّمة هذه، يجب تجميع كل هذه المعلومات في حزمة تُعرف باسم حزمة DWARF (.dwp
) على النحو التالي:
$ emdwp -e mandelbrot.wasm -o mandelbrot.dwp
يتميز إنشاء حزمة DWARF من الكائنات الفردية بميزة أنك تحتاج فقط إلى تقديم ملف واحد إضافي! نحن نعمل حاليًا على تحميل جميع العناصر الفردية أيضًا في إصدار لاحق.
ما هو DWARF 5؟
ربما لاحظت أننا تسللنا علامة أخرى إلى الأمر emcc
أعلاه، -gdwarf-5
. إن تمكين الإصدار 5 من رموز DWARF، والذي لا يمثل الإصدار الافتراضي حاليًا، هو خدعة أخرى لمساعدتنا في بدء تصحيح الأخطاء بشكل أسرع. وباستخدامه، يتم تخزين معلومات معيّنة في الملف الثنائي الرئيسي لم يتم تضمينها في الإصدار التلقائي 4. على وجه التحديد، يمكننا تحديد المجموعة الكاملة من ملفات المصدر من خلال الملف الثنائي الرئيسي فقط. ويسمح ذلك لبرنامج تصحيح الأخطاء بتنفيذ إجراءات أساسية مثل عرض شجرة المصدر بالكامل وضبط نقاط التوقف بدون تحميل بيانات الرموز الكاملة وتحليلها. وهذا يجعل تصحيح الأخطاء باستخدام الرموز المقسَّمة أسرع بكثير، لذلك نستخدم دائمًا علامتَي سطر الأوامر -gsplit-dwarf
و-gdwarf-5
معًا.
باستخدام تنسيق تصحيح الأخطاء DWARF5، يمكننا أيضًا الوصول إلى ميزة مفيدة أخرى. ويقدّم فهرس أسماء في بيانات تصحيح الأخطاء التي سيتم إنشاؤها عند تمرير العلامة -gpubnames
:
$ emcc -sUSE_SDL=2 -g -gdwarf-5 -gsplit-dwarf -gpubnames -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
أثناء جلسة تصحيح الأخطاء، غالبًا ما يتم البحث عن الرموز من خلال البحث عن عنصر حسب الاسم، مثلاً عند البحث عن متغيّر أو نوع. يعمل فهرس الأسماء على تسريع عملية البحث هذه من خلال الإشارة مباشرةً إلى وحدة التحويل البرمجي التي تعرّف هذا الاسم. بدون فهرس الأسماء، سيكون مطلوبًا إجراء بحث شامل في بيانات تصحيح الأخطاء بالكامل للعثور على وحدة الترجمة الصحيحة التي تحدّد العنصر المُعنوَن الذي نبحث عنه.
لمحبي الاستكشاف: الاطّلاع على بيانات تصحيح الأخطاء
يمكنك استخدام llvm-dwarfdump
لإلقاء نظرة على بيانات DWARF. لنجرّب ذلك:
llvm-dwarfdump mandelbrot.wasm
يمنحنا ذلك نظرة عامة على "وحدات الترجمة" (أي الملفات المصدر بشكل تقريبي) التي تتوفّر لدينا معلومات تصحيح أخطاء لها. في هذا المثال، تتوفّر لدينا معلومات تصحيح الأخطاء mandelbrot.cc
فقط. ستُعلمنا المعلومات العامة بأنّ لدينا وحدة هيكلية، ما يعني أنّ لدينا بيانات غير مكتملة في هذا الملف، وأنّ هناك ملفًا منفصلاً بتنسيق .dwo
يحتوي على معلومات تصحيح الأخطاء المتبقية:
يمكنك أيضًا الاطّلاع على الجداول الأخرى ضمن هذا الملف، مثل جدول السطور الذي يعرض تعيين رمز wasm الثنائي إلى سطور C++ (جرِّب استخدام llvm-dwarfdump -debug-line
).
يمكننا أيضًا الاطّلاع على معلومات تصحيح الأخطاء الواردة في ملف .dwo
المنفصل:
llvm-dwarfdump mandelbrot.dwo
النصّ المختصر: ما هي ميزة استخدام ميزة "تقسيم تصحيح الأخطاء"؟
هناك عدة مزايا لتقسيم معلومات تصحيح الأخطاء إذا كان المستخدم يعمل مع تطبيقات كبيرة:
ربط أسرع: لم يعُد المُجمِّع بحاجة إلى تحليل معلومات تصحيح الأخطاء بالكامل. يحتاج الرابطون عادةً إلى تحليل بيانات DWARF بالكامل في الملف الثنائي. من خلال إزالة أجزاء كبيرة من معلومات تصحيح الأخطاء إلى ملفات منفصلة، يتعامل الرابطون مع ملفات ثنائية أصغر حجمًا، ما يؤدي إلى تسريع أوقات الربط (خاصةً في التطبيقات الكبيرة).
تصحيح الأخطاء بشكل أسرع: يمكن لأداة تصحيح الأخطاء تخطّي تحليل الرموز الإضافية في ملفات
.dwo
/.dwp
لبعض عمليات البحث عن الرموز. بالنسبة إلى بعض عمليات البحث (مثل الطلبات بشأن تعيين السطر لملفات wasm إلى C++)، لا نحتاج إلى الاطّلاع على بيانات تصحيح الأخطاء الإضافية. وهذا يوفر لنا الوقت، ولا نحتاج إلى تحميل بيانات تصحيح الأخطاء الإضافية وتحليلها.
1: إذا لم يكن لديك إصدار حديث من llvm-objdump
على نظامك، وكنت تستخدم emsdk
، يمكنك العثور عليه في الدليل emsdk/upstream/bin
.
تنزيل قنوات المعاينة
ننصحك باستخدام إصدار Canary أو Dev أو الإصدار التجريبي من Chrome كمتصفّح التطوير التلقائي. تتيح لك قنوات المعاينة هذه الوصول إلى أحدث ميزات "أدوات مطوري البرامج"، واختبار واجهات برمجة التطبيقات الأكثر تطوّرًا للأنظمة الأساسية للويب، كما تساعدك في العثور على المشاكل على موقعك الإلكتروني قبل أن يكتشفها المستخدمون.
التواصل مع فريق "أدوات مطوّري البرامج في Chrome"
استخدِم الخيارات التالية لمناقشة الميزات الجديدة أو التحديثات أو أي شيء آخر مرتبط بـ "أدوات مطوّري البرامج".
- يمكنك إرسال الملاحظات وطلبات الميزات إلينا على crbug.com.
- يمكنك الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" باستخدام رمز خيارات إضافية > مساعدة > الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" في "أدوات مطوّري البرامج".
- يمكنك نشر تغريدة على Twitter على @ChromeDevTools.
- يمكنك إضافة تعليقات على فيديوهات YouTube التي تعرض الميزات الجديدة في "أدوات مطوّري البرامج" أو فيديوهات YouTube التي تعرض نصائح حول "أدوات مطوّري البرامج".