تصحيح أخطاء WebAssembly أسرع

Philip Pfaffe
Kim-Anh Tran
Kim-Anh Tran
Eric Leese
Sam Clegg

في مؤتمر 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

ينتج عن هذا الأمر برنامج ثنائي للعبة Wamm بحجم 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 ميغابايت. إذا تمت إضافة الأمر emcc أيضًا إلى time، نجد أنّ تشغيل هذا الأمر على الجهاز استغرق 1.5 ثانية تقريبًا. هذه الأرقام تمثل خط أساس صغير لطيف، لكنها صغيرة جدًا على الأرجح، فلن يلفت انتباهها أحد. وعلى الرغم من ذلك، يمكن لبرنامج تصحيح الأخطاء الثنائي في التطبيقات الحقيقية الوصول بسهولة إلى حجم بالأحجام الطبيعية ويستغرق دقائق في الإنشاء.

تخطي بينارين

عند إنشاء تطبيق 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 أعلاه)، ولكن في بعض الحالات، يمكن أن يكون ذلك مهمًا جدًا. إذا كان قسم "name" في تطبيقك كبيرًا جدًا، وكانت معلومات تصحيح أخطاء Dwarf كافية لتلبية احتياجات تصحيح الأخطاء، قد يكون من المفيد إزالة قسم "name" (الاسم):

$ 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

تجميع ملفات dwo في حزمة DWARF

إن إنشاء حزمة 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 منفصلاً يحتوي على المعلومات المتبقية لتصحيح الأخطاء.

mandelbrot.Wasm ومعلومات تصحيح الأخطاء

يمكنك إلقاء نظرة أيضًا على الجداول الأخرى ضمن هذا الملف، على سبيل المثال في الجدول السطري الذي يعرض تعيين رمز بايت Wasm إلى أسطر C++ (جرِّب استخدام llvm-dwarfdump -debug-line).

يمكننا أيضًا الاطّلاع على معلومات تصحيح الأخطاء الواردة في ملف .dwo المنفصل:

llvm-dwarfdump mandelbrot.dwo

mandelbrot.Wasm ومعلومات تصحيح الأخطاء

النصّ المختصر (TL;DR): ما هي ميزة استخدام عملية تصحيح الأخطاء؟

هناك مزايا عديدة لتقسيم معلومات تصحيح الأخطاء إذا كان أحدها يعمل مع تطبيقات كبيرة:

  1. الربط بسرعة أكبر: لم تعُد أداة الربط بحاجة إلى تحليل معلومات تصحيح الأخطاء بالكامل. تحتاج أدوات الربط عادةً إلى تحليل بيانات DWARF بالكامل في النظام الثنائي. وبإزالة أجزاء كبيرة من معلومات تصحيح الأخطاء إلى ملفات منفصلة، تتعامل أدوات الربط مع البرامج الثنائية الأصغر حجمًا، ما ينتج عنه أوقات ربط أسرع (خاصةً بالنسبة إلى التطبيقات الكبيرة).

  2. تصحيح الأخطاء بشكلٍ أسرع: يمكن لبرنامج تصحيح الأخطاء تخطّي تحليل الرموز الإضافية في ملفات .dwo/.dwp لبعض عمليات البحث عن الرموز. بالنسبة إلى بعض عمليات البحث (مثل الطلبات المتعلّقة بتعيين الأسطر لملفات Wasm-to-C++ )، لا نحتاج إلى فحص بيانات تصحيح الأخطاء الإضافية. يوفّر لنا هذا الوقت، بدون الحاجة إلى تحميل بيانات تصحيح الأخطاء الإضافية وتحليلها.

1: إذا لم يكن لديك إصدار حديث من llvm-objdump على نظامك، وكنت تستخدم emsdk، يمكنك العثور عليه في الدليل emsdk/upstream/bin.

تنزيل قنوات المعاينة

يمكنك استخدام إصدار Canary أو إصدار مطوّري البرامج أو الإصدار التجريبي من Chrome كمتصفّح تلقائي للتطوير. تتيح لك قنوات المعاينة هذه الوصول إلى أحدث ميزات "أدوات مطوري البرامج" واختبار واجهات برمجة التطبيقات المتطورة للأنظمة الأساسية على الويب والعثور على المشاكل في موقعك الإلكتروني قبل المستخدمين.

التواصل مع فريق "أدوات مطوري البرامج في Chrome"

يُرجى استخدام الخيارات التالية لمناقشة الميزات والتغييرات الجديدة في المشاركة أو أي موضوع آخر ذي صلة بـ "أدوات مطوري البرامج".

  • يمكنك إرسال اقتراحات أو ملاحظات إلينا عبر crbug.com.
  • يمكنك الإبلاغ عن مشكلة في "أدوات مطوري البرامج" باستخدام خيارات إضافية   المزيد > مساعدة > الإبلاغ عن مشاكل في "أدوات مطوري البرامج" في "أدوات مطوري البرامج".
  • يمكنك نشر تغريدة على @ChromeDevTool.
  • يمكنك إضافة تعليقات على الميزات الجديدة في فيديوهات YouTube أو نصائح حول أدوات مطوّري البرامج في فيديوهات YouTube حول الميزات الجديدة.