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

Ingvar Stepanyan
Ingvar Stepanyan

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

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

لقد عرضنا الدعم الأساسي للخطوات وتحدثنا عن فرص استخدام معلومات DWARF بدلاً من فتح خرائط المصدر لنا في المستقبل:

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

يسرّنا اليوم أن نظهر أنّ الميزات الموعودة تدخل حيز التنفيذ والتقدم الذي أحرزته فِرَق Emscripten وChrome DevTools هذا العام، لا سيما في تطبيقات C وC++.

قبل البدء، يُرجى تذكُّر أنّ هذا لا يزال إصدارًا تجريبيًا من التجربة الجديدة، وعليك استخدام أحدث إصدار من جميع الأدوات على مسؤوليتك، وفي حال واجهتك أي مشاكل، يُرجى الإبلاغ عنها على الرابط https://bugs.chromium.org/p/chromium/issues/entry?template=DevTools+issue.

لنبدأ بنفس مثال 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، تمامًا كما هو الحال في المشاركة الأصلية، لتضمين معلومات تصحيح الأخطاء:

emcc -g temp.c -o temp.html

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

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

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

جزء التجارب ضمن إعدادات &quot;أدوات مطوري البرامج&quot;

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

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

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

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

تم إيقاف أدوات مطوّري البرامج مؤقتًا في الدالة &quot;assert_less&quot; وتعرض قيمتَي &quot;x&quot; و&quot;y&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++:

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

يمكننا بالفعل رؤية جميع المتغيرات على الجانب الأيمن، ولكن لا تتم تهيئة سوى width وheight في الوقت الحالي، لذلك لا يوجد الكثير لفحصه.

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

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

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

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

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

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

تلميح فوق المتغيّر `x` في المصدر الذي يعرض قيمته `3`

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

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

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

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

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

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

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

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

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

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

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

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

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

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

فحص الذاكرة

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

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

قائمة السياق في &quot;env.memory&quot; (الذاكرة) في جزء &quot;النطاق&quot; (Scope) تعرض عنصرًا في &quot;فحص الذاكرة&quot; (env.memory)

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

جزء &quot;Memory Inspector&quot; (أداة فحص الذاكرة) في &quot;أدوات مطوري البرامج&quot; يعرض طريقة عرض سداسية عشرية وASCII للذاكرة

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

إنشاء ملف شخصي لرمز WebAssembly

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

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

لوحة تحليل بيانات تعرض دوال 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://bugs.chromium.org/p/chromium/issues/entry?template=DevTools+issue.

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

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

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

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

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