في مؤتمر 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 كمتصفّح التطوير التلقائي. تتيح لك قنوات المعاينة هذه الوصول إلى أحدث ميزات DevTools، وتتيح لك اختبار واجهات برمجة تطبيقات منصات الويب المتطوّرة، وتساعدك في العثور على المشاكل في موقعك الإلكتروني قبل أن يعثر عليها المستخدمون.
التواصل مع فريق "أدوات مطوّري البرامج في Chrome"
استخدِم الخيارات التالية لمناقشة الميزات الجديدة أو التحديثات أو أي شيء آخر مرتبط بـ "أدوات مطوّري البرامج".
- يمكنك إرسال الملاحظات وطلبات الميزات إلينا على crbug.com.
- يمكنك الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" باستخدام رمز خيارات إضافية > مساعدة > الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" في "أدوات مطوّري البرامج".
- يمكنك نشر تغريدة على Twitter على @ChromeDevTools.
- يمكنك إضافة تعليقات على فيديوهات YouTube التي تعرض الميزات الجديدة في "أدوات مطوّري البرامج" أو فيديوهات YouTube التي تعرض نصائح حول "أدوات مطوّري البرامج".