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

Ingvar Stepanyan
Ingvar Stepanyan

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

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

لقد عرضنا الدعم الأساسي للخطوات وتحدثنا عن الفرص التي يوفّرها لنا استخدام معلومات 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 في DevTools التجارب. افتح "أدوات مطوّري البرامج في 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;color&quot; و&quot;point&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، لأنّ الأرقام التي تحصل عليها لن تمثّل الأداء الفعلي على الإطلاق.

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

لوحة التحليل التي تعرض وظائف 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.

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

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

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

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