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

Ingvar Stepanyan
Ingvar Stepanyan

الطريق حتى الآن

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

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

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

يسرّنا اليوم عرض الميزات التي وعدناك بها ومستوى التقدّم الذي أحرزه فريقا Emscripten و"أدوات مطوّري البرامج" في Chrome على مدار هذا العام، لا سيما في ما يتعلّق بتطبيقات 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 نفسها كما هو موضح أعلاه لتضمين معلومات debugging، وسأطلب من 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:

أدوات مطوّري البرامج تعرض طريقة تفكيك &quot;mandelbrot.wasm&quot;

لقد عدنا إلى تجربة تصحيح أخطاء 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++

أول بادئة مطابقة هي "wins". إذا كنت على دراية بلغة 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 كناري أو إصدار مطوّري البرامج أو الإصدار التجريبي من المتصفِّح التلقائي للتطوير. وتتيح لك قنوات المعاينة هذه الوصول إلى أحدث ميزات "أدوات مطوري البرامج" واختبار أحدث واجهات برمجة التطبيقات للأنظمة الأساسية للويب والعثور على المشاكل على موقعك الإلكتروني قبل أن يفعلها المستخدمون.

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

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