تصحيح أخطاء WebAssembly باستخدام أدوات حديثة

Ingvar Stepanyan
Ingvar Stepanyan

المسار الذي تم اتّباعه حتى الآن

قبل عام، أعلن Chrome عن الدعم الأولي لتصحيح أخطاء WebAssembly الأصلية في أدوات مطوري البرامج في Chrome.

لقد شرحنا كيفية استخدام ميزة "الخطوات الأساسية" وتحدثنا عن الفرص التي يوفّرها لنا استخدام معلومات DWARF بدلاً من خرائط المصدر في المستقبل:

  • حلّ أسماء المتغيّرات
  • أنواع الطباعة الجميلة
  • تقييم التعبيرات في اللغات المصدر
  • …وغير ذلك الكثير

واليوم، يسعدنا تسليط الضوء على الميزات التي وعدنا بها في الواقع والتقدم الذي أحرزه فريق Emscripten وChrome DevTools خلال هذا العام، وخاصةً في ما يتعلق بتطبيقات C وC++.

قبل البدء، يُرجى العِلم أنّ هذا الإصدار لا يزال تجريبيًا للتجربة الجديدة، وعليك استخدام أحدث إصدار من جميع الأدوات على مسؤوليتك الخاصة، وإذا واجهت أي مشاكل، يُرجى الإبلاغ عنها على الرابط التالي: https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350.

لنبدأ بنفس مثال C البسيط كما في المرة الأخيرة:

#include <stdlib.h>

void assert_less(int x, int y) {
  if (x >= y) {
    abort();
  }
}

int main() {
  assert_less(10, 20);
  assert_less(30, 20);
}

لتجميعه، نستخدم أحدث إصدار من Emscripten ونُمرِّر علامة -g، تمامًا كما هو الحال في المشاركة الأصلية، لتضمين معلومات debugging:

emcc -g temp.c -o temp.html

يمكننا الآن عرض الصفحة التي تم إنشاؤها من خادم HTTP على المضيف المحلي (مثل serve)، و فتحها في أحدث إصدار من Chrome Canary.

سنحتاج هذه المرة أيضًا إلى إضافة مساعدة تتكامل مع Chrome DevTools وتساعده في فهم جميع معلومات تصحيح الأخطاء المُشفَّرة في ملف WebAssembly. يُرجى تثبيتها من خلال الانتقال إلى هذا الرابط: goo.gle/wasm-debugging-extension

ستحتاج أيضًا إلى تفعيل تصحيح أخطاء WebAssembly في التجارب ضمن أدوات مطوّري البرامج. افتح "أدوات مطوري البرامج في Chrome" وانقر على رمز الترس () في أعلى يسار لوحة "أدوات مطوري البرامج"، وانتقِل إلى لوحة التجارب، ثم ضَع علامة في المربّع تصحيح أخطاء WebAssembly: تفعيل دعم DWARF.

لوحة &quot;التجارب&quot; في إعدادات &quot;أدوات مطوّري البرامج&quot;

عند إغلاق الإعدادات، ستقترح عليك "أدوات مطوّري البرامج" إعادة تحميل نفسها لتطبيق الإعدادات، لذا لنفعل ذلك. لقد انتهينا من عملية الإعداد لمرة واحدة.

يمكننا الآن الرجوع إلى لوحة المصادر وتفعيل الإيقاف المؤقت عند الاستثناءات (رمز ⏸)، ثم وضع علامة في المربّع إيقاف مؤقت عند رصد الاستثناءات وإعادة تحميل الصفحة. من المفترض أن تلاحظ إيقاف أدوات مطوري البرامج مؤقتًا عند أحد الاستثناءات:

لقطة شاشة للوحة &quot;المصادر&quot; توضّح كيفية تفعيل ميزة &quot;الإيقاف المؤقت عند رصد الاستثناءات&quot;

يتوقف المسار تلقائيًا عند رمز ربط تم إنشاؤه بواسطة Emscripten، ولكن يمكنك على اليمين الاطّلاع على عرض تسلسل استدعاء الدوال البرمجية الذي يمثّل تسلسل استدعاء الدوال البرمجية للخطأ، ويمكنك الانتقال إلى سطر C الأصلي الذي استدعى abort:

تم إيقاف &quot;أدوات المطوّر&quot; مؤقتًا في الدالة assert_less وعرض قيم x وy في عرض &quot;النطاق&quot;

الآن، إذا اطّلعت على عرض النطاق، يمكنك الاطّلاع على الأسماء الأصلية وقيم المتغيّرات في رمز C/C++، ولن يكون عليك بعد الآن معرفة معنى الأسماء المشوّهة مثل $localN وعلاقتها بالرمز المصدر الذي كتبته.

وهذا لا ينطبق على القيم الأساسية مثل الأعداد الصحيحة فحسب، بل ينطبق أيضًا على الأنواع المركّبة مثل البنى والفئات والصفائف وغيرها.

دعم الكتابة التفاعلية

لنلقِ نظرة على مثال أكثر تعقيدًا لتوضيح ذلك. هذه المرة، سنرسم منشورًا هندسيًا من نوع ماندلبروت باستخدام الرمز البرمجي التالي لبرنامج C++:

#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();
}

يمكنك ملاحظة أنّ هذا التطبيق لا يزال صغيرًا إلى حدٍ ما، فهوملف واحد يحتوي على 50 سطرًا من الرموز البرمجية، ولكنني أستخدم هذه المرة أيضًا بعض واجهات برمجة التطبيقات الخارجية، مثل مكتبة SDL للرسومات بالإضافة إلى الأرقام المعقدة من مكتبة C++ العادية.

سأجمعه باستخدام العلامة -g نفسها كما هو موضح أعلاه لتضمين معلومات تصحيح الأخطاء، وسأطلب أيضًا من Emscripten توفير مكتبة SDL2 والسماح بذاكرة بحجم عشوائي:

emcc -g mandelbrot.cc -o mandelbrot.html \
     -s USE_SDL=2 \
     -s ALLOW_MEMORY_GROWTH=1

عندما أزور الصفحة التي تم إنشاؤها في المتصفح، يمكنني رؤية الشكل الكسري الجميل مع بعض الألوان العشوائية:

صفحة العرض الترويجي

عندما أفتح أدوات مطوري البرامج، مرة أخرى، يمكنني رؤية ملف C++ الأصلي. في هذه المرة، لن يظهر خطأ في الرمز البرمجي (يُحسَن)، لذا لنضع نقطة رصد في بداية الرمز البرمجي بدلاً من ذلك.

عند إعادة تحميل الصفحة مرة أخرى، سيتوقف مصحِّح الأخطاء مؤقتًا داخل مصدر C++ مباشرةً:

تم إيقاف &quot;أدوات مطوّري البرامج&quot; مؤقتًا عند طلب &quot;SDL_Init&quot;

يمكننا الآن الاطّلاع على جميع متغيّراتنا على اليمين، ولكن تمّت بدء width وheight فقط في الوقت الحالي، لذا ليس هناك الكثير مما يمكن فحصه.

لنضع نقطة توقّف أخرى داخل حلقة ماندلبروت الرئيسية، ونستأنف التنفيذ للانتقال إلى الأمام قليلاً.

تم إيقاف أدوات المطوّرين مؤقتًا داخل الحلقات المُدمجة

في هذه المرحلة، تم ملء palette ببعض الألوان العشوائية، ويمكننا توسيع النطاق نفسه، بالإضافة إلى SDL_Color الفردية وفحص مكوّناتها للتأكّد من أنّه كل شيء يبدو جيدًا (على سبيل المثال، يتم ضبط قناة "الشفافية" دائمًا على مستوى الشفافية الكامل). وبالمثل، يمكننا توسيع الأجزاء الحقيقية والتخيّلية للعدد المركّب المخزّن في المتغيّر center والتحقّق منها.

إذا كنت تريد الوصول إلى موقع إلكتروني مُدمَج بعمق يصعب بالتالي التنقّل إليه من خلال عرض النطاق، يمكنك استخدام تقييم وحدة التحكّم أيضًا. تجدر الإشارة إلى أنّ التعبيرات الأكثر تعقيدًا من C++ ليست متاحة بعد.

لوحة وحدة تحكُّم تعرض نتيجة `palette[10].r`

لنستأنف التنفيذ عدة مرات ونرى كيف يتم تغيير x الداخلي أيضًا، إما من خلال الاطّلاع على عرض النطاق مرة أخرى، أو من خلال إضافة اسم المتغيّر إلى قائمة المراقبة، أو تقييمه في وحدة التحكّم، أو من خلال التمرير فوق المتغيّر في رمز المصدر:

تلميح فوق المتغيّر `x` في المصدر يُظهر قيمته `3`

من هنا، يمكننا متابعة عبارات C++ أو التطرق إليها، وملاحظة كيف تتغير المتغيرات الأخرى أيضًا:

تلميحات الأدوات وطريقة عرض النطاق التي تُظهر قيم &quot;اللون&quot; و&quot;النقطة&quot; ومتغيّرات أخرى

حسنًا، يعمل كل هذا بشكلٍ رائع عندما تتوفّر معلومات تصحيح الأخطاء، ولكن ماذا لو أردنا تصحيح أخطاء رمز لم يتم إنشاؤه باستخدام خيارات تصحيح الأخطاء؟

تصحيح أخطاء WebAssembly الأوّلي

على سبيل المثال، طلبنا من Emscripten توفير مكتبة SDL مُسبقة الإنشاء لنا، بدلاً من تجميعها بأنفسنا من المصدر، لذا ليس هناك طريقة لبرنامج تصحيح الأخطاء للعثور على المصادر المرتبطة، على الأقل حاليًا. لنطّلِع مرة أخرى على SDL_RenderDrawColor:

أدوات المطوّرين تعرِض عرض التفكيك لملف mandelbrot.wasm

نعود إلى تجربة تصحيح أخطاء WebAssembly الأولية.

قد يبدو هذا الأمر مخيفًا بعض الشيء، وهو ليس شيئًا يحتاج إليه معظم مطوّري الويب مطلقًا، ولكن قد تحتاج أحيانًا إلى تصحيح أخطاء مكتبة تم إنشاؤها بدون معلومات تصحيح الأخطاء، سواءً كانت مكتبة تابعة لجهة خارجية لا يمكنك التحكّم فيها أو لأنّك تواجه أحد الأخطاء التي تحدث في مرحلة الإنتاج فقط.

للمساعدة في هذه الحالات، أجرينا أيضًا بعض التحسينات على تجربة debugging الأساسية.

أولاً، إذا سبق لك استخدام تصحيح أخطاء WebAssembly الأوّلي، قد تلاحظ أنّ عملية التفكيك بالكامل تظهر الآن في ملف واحد، ما يُغنيك عن تخمين الوظيفة التي يتوافق معها إدخال المصادر wasm-53834e3e/ wasm-53834e3e-7.

مخطّط جديد لإنشاء الأسماء

لقد أجرينا أيضًا تحسينات على الأسماء في طريقة عرض التفكيك. في السابق، كان يظهر أمامك فقط فهارس رقمية، أو لا يظهر أي اسم على الإطلاق في حال استخدام الدوالّ.

ننشئ الآن الأسماء بالطريقة نفسها التي تستخدمها أدوات التفكيك الأخرى، وذلك باستخدام التلميحات من قسم أسماء WebAssembly، ومسارات الاستيراد/التصدير، وأخيرًا، في حال تعذّر تنفيذ أي إجراء آخر، يتم إنشاء الأسماء استنادًا إلى النوع والفهرس الخاص بالعنصر، مثل $func123. كما ترى في لقطة الشاشة أعلاه كيف يساعد ذلك في الحصول على تسلسلات تسلسل استدعاء الدوال البرمجية وتفكيكها أكثر قابلية للقراءة.

عندما لا تتوفّر معلومات عن النوع، قد يكون من الصعب فحص أي قيم غير القيم الأساسية. على سبيل المثال، ستظهر المؤشرات كأرقام صحيحة عادية، بدون أي طريقة لمعرفة ما يتم تخزينه في الذاكرة.

فحص الذاكرة

في السابق، كان بإمكانك توسيع عنصر ذاكرة WebAssembly فقط، الذي يُمثّله الرمز env.memory في عرض النطاق، للبحث عن ملف شخصي بايت فردي. كان هذا الإجراء مناسبًا في بعض السيناريوهات البسيطة، ولكنه لم يكن مناسبًا بشكل خاص للتوسيع ولم يسمح بإعادة تفسير البيانات بتنسيقات أخرى غير قيم البايت. أضفنا ميزة جديدة للمساعدة في حلّ هذه المشكلة أيضًا: أداة فحص الذاكرة.

إذا نقرت بزر الماوس الأيمن على env.memory، من المفترَض أن يظهر لك الآن خيار جديد يُسمى فحص الذاكرة:

قائمة السياقات في &quot;env.memory&quot; في لوحة &quot;النطاق&quot; تعرِض عنصر &quot;فحص الذاكرة&quot;

بعد النقر على هذا الخيار، سيظهر أداة فحص الذاكرة، والتي يمكنك من خلالها فحص ذاكرة WebAssembly في العرض الثنائي العشري وASCII، والانتقال إلى عناوين معيّنة، بالإضافة إلى تفسير البيانات بتنسيقات مختلفة:

لوحة &quot;أداة فحص الذاكرة&quot; في &quot;أدوات مطوّري البرامج&quot; تعرض عرضًا سداسيًا وعرضًا بترميز ASCII للذاكرة

السيناريوهات المتقدّمة والتنبيهات

تحليل رمز WebAssembly

عند فتح أدوات مطوّري البرامج، يتم "تقسيم" رمز WebAssembly إلى إصدار غير محسَّن لتفعيل تصحيح الأخطاء. هذا الإصدار أبطأ بكثير، ما يعني أنّه لا يمكنك الاعتماد على console.time وperformance.now وطرق أخرى لقياس سرعة الرمز البرمجي أثناء فتح DevTools، لأنّ الأرقام التي تحصل عليها لن تمثّل الأداء الفعلي على الإطلاق.

بدلاً من ذلك، عليك استخدام لوحة الأداء في DevTools، والتي ستُشغّل الرمز البرمجي بالسرعة الكاملة وتوفّر لك تحليلاً تفصيليًا للوقت الذي تمّ إنفاقه في الدوالّ المختلفة:

لوحة التحليل التي تعرض وظائف Wasm المختلفة

بدلاً من ذلك، يمكنك تشغيل تطبيقك مع إغلاق أدوات مطوّري البرامج، وفتحها بعد الانتهاء من فحص وحدة التحكّم.

سنعمل على تحسين سيناريوهات التحليل في المستقبل، ولكن في الوقت الحالي من الضروري أن تكون على دراية بها. لمعرفة المزيد من المعلومات عن سيناريوهات التدريج في WebAssembly، يمكنك الاطّلاع على مستنداتنا حول مسار تجميع WebAssembly.

الإنشاء وتصحيح الأخطاء على أجهزة مختلفة (بما في ذلك Docker أو المضيف)

عند إنشاء التطبيق في Docker أو على جهاز افتراضي أو على خادم إنشاء عن بُعد، من المحتمل أن تواجه حالات لا تتطابق فيها مسارات الملفات المصدر المستخدَمة أثناء عملية الإنشاء مع المسارات في نظام الملفات الخاص بك الذي يتم فيه تشغيل "أدوات مطوّري تطبيقات Chrome". في هذه الحالة، ستظهر الملفات في لوحة المصادر ولكن لن يتم تحميلها.

لحلّ هذه المشكلة، نفّذنا وظيفة تعيين مسار في خيارات إضافة C/C++. ويمكنك استخدامه لإعادة ربط المسارات العشوائية و مساعدة أدوات المطوّرين في تحديد مصادر البيانات.

على سبيل المثال، إذا كان المشروع على جهاز المضيف ضمن مسار C:\src\my_project، ولكن تم إنشاؤه داخل حاوية Docker حيث تم تمثيل هذا المسار على أنّه /mnt/c/src/my_project، يمكنك إعادة ربطه مرة أخرى أثناء تصحيح الأخطاء من خلال تحديد هذه المسارات كبادئات:

صفحة الخيارات في إضافة تصحيح أخطاء C/C++

تكون البادئة الأولى التي تمت مطابقتها هي "البادئة الفائزة". إذا كنت على دراية بأداة تصحيح أخطاء C++ الأخرى، يشبه هذا الخيار الأمر set substitute-path في GDB أو إعداد target.source-map في LLDB.

تصحيح أخطاء النُسخ المحسَّنة

كما هو الحال مع أي لغات أخرى، يعمل تصحيح الأخطاء على أفضل نحو في حال كانت ميزة التحسينات متوقفة. قد تؤدي التحسينات إلى تضمين الدوالّ في بعضها أو إعادة ترتيب الرمز أو إزالة أجزاء من الرمز تمامًا، وقد يؤدي كل ذلك إلى إثارة ارتباك لدى مصحِّح الأخطاء وبالتالي لديك بصفتك المستخدم.

إذا لم تكن تمانع في تجربة تصحيح أخطاء أكثر محدودية وكنت لا تزال تريد تصحيح أخطاء إصدار محسّن، ستعمل معظم التحسينات على النحو المقصود، باستثناء تضمين الدوالّ. نخطّط لمعالجة الصعوبات المتبقية في المستقبل، ولكن في الوقت الحالي، يُرجى استخدام -fno-inline لإيقافها عند الترجمة باستخدام أي تحسينات على مستوى -O، على سبيل المثال:

emcc -g temp.c -o temp.html \
     -O3 -fno-inline

فصل معلومات تصحيح الأخطاء

تحفظ معلومات تصحيح الأخطاء الكثير من التفاصيل حول الرمز البرمجي والأنواع والمتغيّرات والدوالّ والنطاقات والمواقع الجغرافية المحدّدة، أي أيّ شيء قد يكون مفيداً لأداة تصحيح الأخطاء. ونتيجةً لذلك، يمكن أن يكون غالبًا أكبر من الرمز نفسه.

لتسريع تحميل وتجميع وحدة WebAssembly، قد تحتاج إلى تقسيم معلومات تصحيح الأخطاء هذه إلى ملف WebAssembly منفصل. لإجراء ذلك في Emscripten، عليك تمرير علامة -gseparate-dwarf=… مع اسم الملف المطلوب:

emcc -g temp.c -o temp.html \
     -gseparate-dwarf=temp.debug.wasm

في هذه الحالة، سيخزِّن التطبيق الرئيسي اسم ملف فقط temp.debug.wasm، وستتمكّن إضافة المساعِد من تحديد موقعه وتحميله عند فتح "أدوات المطوّر".

وعند استخدامها مع تحسينات كما هو موضّح أعلاه، يمكن استخدام هذه الميزة لشحن إصدارات إنتاج محسّنة تقريبًا من تطبيقك، وتصحيح الأخطاء فيها لاحقًا باستخدام ملف من جهة محلية. في هذه الحالة، علينا أيضًا إلغاء عنوان URL المخزّن لمساعدة الإضافة في العثور على الملف الجانبي، على سبيل المثال:

emcc -g temp.c -o temp.html \
     -O3 -fno-inline \
     -gseparate-dwarf=temp.debug.wasm \
     -s SEPARATE_DWARF_URL=file://[local path to temp.debug.wasm]

سيتوفّر المزيد من المعلومات قريبًا.

لقد اطّلعت على الكثير من الميزات الجديدة.

بفضل كل عمليات الدمج الجديدة هذه، تصبح أدوات مطوري البرامج في Chrome أداة قابلة للتطبيق وفعّالة لتصحيح الأخطاء ليس فقط لـ JavaScript، بل أيضًا لتطبيقات C وC++ ، ما يسهّل أكثر من أي وقت مضى الحصول على التطبيقات التي تم إنشاؤها باستخدام مجموعة متنوعة من التقنيات وإتاحتها على شبكة ويب مشتركة بين الأنظمة الأساسية.

مع ذلك، لم تنتهِ رحلتنا بعد. في ما يلي بعض الأمور التي سنعمل عليها من الآن فصاعدًا:

  • إزالة المشاكل في تجربة تصحيح الأخطاء
  • إضافة إمكانية استخدام أدوات تنسيق الأنواع المخصّصة
  • العمل على تحسينات في ميزة التحليل لتطبيقات WebAssembly
  • إتاحة تغطية الرمز البرمجي لتسهيل العثور على الرموز غير المستخدَمة
  • تحسين التوافق مع التعبيرات في تقييم وحدة التحكّم
  • إتاحة الميزة للمزيد من اللغات
  • …وغير ذلك

في الوقت الحالي، يُرجى مساعدتنا من خلال تجربة الإصدار التجريبي الحالي على الرمز البرمجي الخاص بك والإبلاغ عن أي مشاكل تم رصدها على الرابط التالي: https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350.

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

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

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

يمكنك استخدام الخيارات التالية لمناقشة الميزات أو التحديثات الجديدة أو أي معلومات أخرى متعلّقة بـ "أدوات مطوري البرامج".