الفرق بين المراجعتين ل"Node.js/debugger"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(هذه الصفحة من مساهمات هبة فريد)
 
سطر 1: سطر 1:
 
= المنقّح Debugger =
 
= المنقّح Debugger =
درجة الثبات: 2- مستقر
+
درجة الثبات: 2 - مستقر
  
 
كثيرًا ما نحتاج لتنقيح الشيفرات ومراجعتها من الأخطاء، لذلك يتضمن Node.js أداة تنقيح مستقلة يمكن الوصول إليها من خلال محقق V8 inspector وعميل تنقيح مدمج. لاحظ أن عميل تنقيح Node.js هنا هو أداة بسيطة وليست منقّحًا كامل الخصائص، لكنها تكفي في حالة تفحص خطوات تنفيذ الشيفرة.
 
كثيرًا ما نحتاج لتنقيح الشيفرات ومراجعتها من الأخطاء، لذلك يتضمن Node.js أداة تنقيح مستقلة يمكن الوصول إليها من خلال محقق V8 inspector وعميل تنقيح مدمج. لاحظ أن عميل تنقيح Node.js هنا هو أداة بسيطة وليست منقّحًا كامل الخصائص، لكنها تكفي في حالة تفحص خطوات تنفيذ الشيفرة.
  
سنستخدم في مثالنا هذا ملف myscript:<syntaxhighlight lang="javascript">
+
سنستخدم في مثالنا هذا ملف <code>myscript</code>:<syntaxhighlight lang="javascript">
 
// myscript.js
 
// myscript.js
 
‎global.x = 5;
 
‎global.x = 5;
سطر 14: سطر 14:
  
  
</syntaxhighlight>كما ترى، المثال المستخدم هنا بسيط، حيث يبدأ في السطر الأول بتحديد متغير عام x بقيمة ثم في السطر الثاني يستخدم الدالة setTimeout‎()‎‎ في ظهور كلمة world بعد انتظار 1000 مللي ثانية (1 ثانية)، وينتهي بظهور hello في السطر الأخير (وفي هذه الحالة ستظهر على الفور قبل كلمة world التي ستتأخر لمدة ثانية).
+
</syntaxhighlight>كما ترى، المثال المستخدم هنا بسيط، حيث يبدأ في السطر الأول بتحديد متغير عام <code>x</code> بقيمة <code>5</code>، ثم في السطر الثاني يستخدم الدالة <code>setTimeout‎()‎‎</code> في ظهور كلمة <code>world</code> بعد انتظار 1000 مللي ثانية (1 ثانية)، وينتهي بظهور <code>hello</code> في السطر الأخير (وفي هذه الحالة ستظهر على الفور قبل كلمة <code>world</code> التي ستتأخر لمدة ثانية).
  
سنضع جملة debugger;‎ في النص لتفعيل نقطة توقف breakpoint في هذا الموقع من الشيفرة:<syntaxhighlight lang="javascript">
+
سنضع جملة <code>debugger;‎</code> في النص لتفعيل نقطة توقف <code>breakpoint</code> في هذا الموقع من الشيفرة:<syntaxhighlight lang="javascript">
 
// myscript.js
 
// myscript.js
 
global.x = 5;
 
global.x = 5;
سطر 26: سطر 26:
  
  
</syntaxhighlight>والآن، دعنا نشغل المنقّح، ابدأ تطبيق Node.js مع الخيار inspect ثم مسار ملف الشيفرة المُراد استخدامه، وهو في حالتنا myscript.js:<syntaxhighlight lang="text">
+
</syntaxhighlight>والآن، دعنا نشغل المنقّح، ابدأ تطبيق Node.js مع الخيار <code>inspect</code> ثم مسار ملف الشيفرة المُراد استخدامه، وهو في حالتنا myscript.js:<syntaxhighlight lang="text">
 
$ node inspect script_folder/myscript.js
 
$ node inspect script_folder/myscript.js
 
< Debugger listening on ws://127.0.0.1:9229/2c9204a9-3569-40d3-a1e0-1e90cf2e893a
 
< Debugger listening on ws://127.0.0.1:9229/2c9204a9-3569-40d3-a1e0-1e90cf2e893a
سطر 45: سطر 45:
 
debug>
 
debug>
  
</syntaxhighlight>كما رأينا توقف المنقّح تلقائيًا في بداية تنفيذ الشيفرة، ثم أدخلنا أمر cont للاستمرار في تنفيذ الشيفرة حتى نقطة التوقف التالية، فظهرت أولًا كلمة hello وانتظر المنقّح لمدة 1 ثانية قبل التوقف عند نقطة التوقف في السطر الثالث، لأنها موضوعة بداخل دالة setTimeout()‎. بينما لو وضعنا نقطة توقف في السطر الثاني من الشيفرة:<syntaxhighlight lang="javascript">
+
</syntaxhighlight>كما رأينا توقف المنقّح تلقائيًا في بداية تنفيذ الشيفرة، ثم أدخلنا أمر <code>cont</code> للاستمرار في تنفيذ الشيفرة حتى نقطة التوقف التالية، فظهرت أولًا كلمة <code>hello</code> وانتظر المنقّح لمدة 1 ثانية قبل التوقف عند نقطة التوقف في السطر الثالث، لأنها موضوعة بداخل دالة <code>setTimeout()‎</code>. بينما لو وضعنا نقطة توقف في السطر الثاني من الشيفرة:<syntaxhighlight lang="javascript">
 
// myscript.js
 
// myscript.js
 
global.x = 5;
 
global.x = 5;
سطر 55: سطر 55:
 
console.log('hello');
 
console.log('hello');
  
</syntaxhighlight>وقمنا بإعادة تنفيذ الشيفرة (بعد حفظ التعديلات) عن طريق أمر restart، ثم إدخال أمر cont للاستمرار بعد أول سطر، نجد أنه قد توقف في السطر الثاني بالفعل:<syntaxhighlight lang="text">
+
</syntaxhighlight>وقمنا بإعادة تنفيذ الشيفرة (بعد حفظ التعديلات) عن طريق أمر <code>restart</code>، ثم إدخال أمر <code>cont</code> للاستمرار بعد أول سطر، نجد أنه قد توقف في السطر الثاني بالفعل:<syntaxhighlight lang="text">
 
debug> restart
 
debug> restart
 
< Debugger listening on ws://127.0.0.1:9229/e6ccb551-d2aa-451f-81e7-0e5f09e0fb1c
 
< Debugger listening on ws://127.0.0.1:9229/e6ccb551-d2aa-451f-81e7-0e5f09e0fb1c
سطر 73: سطر 73:
 
debug>
 
debug>
  
</syntaxhighlight>لندخل الأمر cont للاستمرار في تنفيذ الشيفرة حتى نقطة التوقف التالية:<syntaxhighlight lang="text">
+
</syntaxhighlight>لندخل الأمر <code>cont</code> للاستمرار في تنفيذ الشيفرة حتى نقطة التوقف التالية:<syntaxhighlight lang="text">
 
debug> cont
 
debug> cont
 
< hello
 
< hello
سطر 84: سطر 84:
 
debug>
 
debug>
  
</syntaxhighlight>لتقييم واختبار الشيفرة عن بعد، يمكنك استخدام وحدة REPL عن طريق إدخال أمر repl ببساطة:<syntaxhighlight lang="text">
+
</syntaxhighlight>لتقييم واختبار الشيفرة عن بعد، يمكنك استخدام وحدة [[Node.js/repl|REPL]] عن طريق إدخال أمر <code>repl</code> ببساطة:<syntaxhighlight lang="text">
 
break in script_folder/myscript.js:5
 
break in script_folder/myscript.js:5
 
   3 debugger;
 
   3 debugger;
سطر 108: سطر 108:
 
</syntaxhighlight>كما ترى، في وضع repl، يمكنك معرفة قيمة المتغيرات، وتحديد متغيرات جديدة، وتغيير قيمة المتغيرات، وما إلى ذلك، هذه الوحدة تمكنك من تقييم الكود بشكل تفاعلي، للعودة إلى المنقّح، اضغط Ctrl+c.
 
</syntaxhighlight>كما ترى، في وضع repl، يمكنك معرفة قيمة المتغيرات، وتحديد متغيرات جديدة، وتغيير قيمة المتغيرات، وما إلى ذلك، هذه الوحدة تمكنك من تقييم الكود بشكل تفاعلي، للعودة إلى المنقّح، اضغط Ctrl+c.
  
أدخل next للانتقال إلى السطر التالي مباشرة. أما الضغط على زر الإدخال Enter دون كتابة أوامر يعيد تكرار آخر أمر مكتوب.<syntaxhighlight lang="text">
+
أدخل <code>next</code> للانتقال إلى السطر التالي مباشرة. أما الضغط على زر الإدخال Enter دون كتابة أوامر يعيد تكرار آخر أمر مكتوب.<syntaxhighlight lang="text">
 
debug> next
 
debug> next
 
break in script_folder/myscript.js:6
 
break in script_folder/myscript.js:6
سطر 145: سطر 145:
 
console.log('hello');
 
console.log('hello');
  
</syntaxhighlight>كما ترى، أضفنا دالة جديدة numbers مع الوسيط تزيد قيمة متغير x العام وتعيد قيمة num، وأضفت نقطة توقف في السطر الثاني عشر.
+
</syntaxhighlight>كما ترى، أضفنا دالة جديدة <code>numbers</code> مع الوسيط <code>x</code>، تزيد قيمة متغير <code>x</code> العام وتعيد قيمة <code>num</code>، وأضفت نقطة توقف في السطر الثاني عشر.
  
لنعد تشغيل الشيفرة مجددًا باستخدام restart، ثم نصل إلى أول نقطة توقف باستخدام cont، ثم ندخل في وضع repl:<syntaxhighlight lang="text">
+
لنعد تشغيل الشيفرة مجددًا باستخدام <code>restart</code>، ثم نصل إلى أول نقطة توقف باستخدام <code>cont</code>، ثم ندخل في وضع <code>repl</code>:<syntaxhighlight lang="text">
 
debug> repl
 
debug> repl
 
Press Ctrl + C to leave debug repl
 
Press Ctrl + C to leave debug repl
سطر 166: سطر 166:
 
>
 
>
  
</syntaxhighlight>كما ترى، عندما استدعينا الدالة numbers()‎ دون وسيط، فظهر خطأ. بينما عندما مررنا المتغير x كوسيط، فأعيدت قيمة المتغير، وترى أن x الآن أصبح 8 بعد استدعاء الدالة ثلاث مرات.
+
</syntaxhighlight>كما ترى، عندما استدعينا الدالة <code>numbers()</code>‎ دون وسيط، فظهر خطأ. بينما عندما مررنا المتغير <code>x</code> كوسيط، فأعيدت قيمة المتغير، وترى أن <code>x</code> الآن أصبح <code>8</code> بعد استدعاء الدالة ثلاث مرات.
  
دعنا نعود إلى وضع المنقّح مجددًا (لاحظ أنه في وضع repl يسبق المؤشر علامة ‎< فقط، بينما في وضع المنقّح يسبقه علامة ‎>debug) وذلك بالضغط على ctrl + c كما أسلفنا سابقًا:<syntaxhighlight lang="text">
+
دعنا نعود إلى وضع المنقّح مجددًا (لاحظ أنه في وضع repl يسبق المؤشر علامة ‎<code><</code> فقط، بينما في وضع المنقّح يسبقه علامة <code>‎>debug</code>) وذلك بالضغط على Ctrl+c كما أسلفنا سابقًا:<syntaxhighlight lang="text">
 
debug> cont
 
debug> cont
 
< world
 
< world
سطر 181: سطر 181:
 
debug>
 
debug>
  
</syntaxhighlight>ذهبنا إلى نقطة التوقف التالية، ترى في الناتج أنه طُبِعَ الرقم 8 بدلًا من 5 أي أن التغييرات التي أجريناها في وضع repl ما زالت جارية.
+
</syntaxhighlight>ذهبنا إلى نقطة التوقف التالية، ترى في الناتج أنه طُبِعَ الرقم <code>8</code> بدلًا من <code>5</code> أي أن التغييرات التي أجريناها في وضع <code>repl</code> ما زالت جارية.
  
يمكنك أيضًا تنفيذ أي تعبير في المنقّح بسهولة باستخدام exec قبل التعبير المطلوب كما ترى تاليًا:<syntaxhighlight lang="text">
+
يمكنك أيضًا تنفيذ أي تعبير في المنقّح بسهولة باستخدام <code>exec</code> قبل التعبير المطلوب كما ترى تاليًا:<syntaxhighlight lang="text">
 
> 8   debugger;
 
> 8   debugger;
 
   9  console.log('world');
 
   9  console.log('world');
سطر 196: سطر 196:
 
2
 
2
 
debug>
 
debug>
</syntaxhighlight>يمكنك بدء الشيفرة من جديد باستخدام أمر run، أما أمر restart يعيد فتح الشيفرة في حالة إجراء أية تعديلات على الملف:<syntaxhighlight lang="text">
+
</syntaxhighlight>يمكنك بدء الشيفرة من جديد باستخدام أمر <code>run</code>، أما أمر <code>restart</code> يعيد فتح الشيفرة في حالة إجراء أية تعديلات على الملف:<syntaxhighlight lang="text">
 
> 1 (function (exports, require, module, __filename, __dirname) { // myscript.js
 
> 1 (function (exports, require, module, __filename, __dirname) { // myscript.js
 
  2 global.x = 5;
 
  2 global.x = 5;
سطر 217: سطر 217:
 
debug>
 
debug>
  
</syntaxhighlight>لا يقتصر تحديد نقاط التوقف من داخل الملف فقط، بل يمكنك أيضًا تحديدها من داخل المنقّح. لتحديد السطر الحالي كنقطة توقف أدخل setBreakpoint()‎ :<syntaxhighlight lang="text">
+
</syntaxhighlight>لا يقتصر تحديد نقاط التوقف من داخل الملف فقط، بل يمكنك أيضًا تحديدها من داخل المنقّح. لتحديد السطر الحالي كنقطة توقف أدخل <code>setBreakpoint()</code>‎ :<syntaxhighlight lang="text">
 
debug> setBreakpoint()
 
debug> setBreakpoint()
 
 4  x++;
 
 4  x++;
سطر 256: سطر 256:
 
  11   console.log('times!');
 
  11   console.log('times!');
  
</syntaxhighlight>كما تلاحظ، فإن نقاط التوقف المحددة مسبقًا من المنقّح تُميّز بعلامة (*) مباشرة قبل رقم السطر. يمكنك أيضًا تحديد نقطة التوقف في سطر محدد setBreakpoint(line)‎ حيث line هنا هي رقم السطر، أو عند أول استدعاء لتعبير معين setBreakpoint('numbers()')‎. كما يمكنك إزالة نقطة التوقف باستخدام الدالة clearBreakpoint('filename', line)‎ إذ إنَّ filename هو اسم الملف و line هو رقم السطر، ففي المثال التالي نزيل نقطة التوقف في السطر التاسع من ملف myscript.js:<syntaxhighlight lang="javascript">
+
</syntaxhighlight>كما تلاحظ، فإن نقاط التوقف المحددة مسبقًا من المنقّح تُميّز بعلامة (*) مباشرة قبل رقم السطر. يمكنك أيضًا تحديد نقطة التوقف في سطر محدد <code>setBreakpoint(line)</code>‎ حيث <code>line</code> هنا هي رقم السطر، أو عند أول استدعاء لتعبير معين <code>setBreakpoint('numbers()')‎</code>. كما يمكنك إزالة نقطة التوقف باستخدام الدالة <code>clearBreakpoint('filename', line)‎</code> إذ إنَّ <code>filename</code> هو اسم الملف و <code>line</code> هو رقم السطر، ففي المثال التالي نزيل نقطة التوقف في السطر التاسع من ملف myscript.js:<syntaxhighlight lang="javascript">
 
clearBreakpoint('myscript.js', 9)
 
clearBreakpoint('myscript.js', 9)
</syntaxhighlight>لنتعمق قليلًا في التقدم داخل الشيفرة، أولًا أجرينا تعديلًا بسيطًا على الشيفرة وأضفنا استدعاءً لدالة numbers:<syntaxhighlight lang="javascript">
+
</syntaxhighlight>لنتعمق قليلًا في التقدم داخل الشيفرة، أولًا أجرينا تعديلًا بسيطًا على الشيفرة وأضفنا استدعاءً لدالة <code>numbers</code>:<syntaxhighlight lang="javascript">
 
// myscript.js
 
// myscript.js
  
سطر 278: سطر 278:
 
console.log('hello');
 
console.log('hello');
  
</syntaxhighlight>ثم نعيد تشغيل الشيفرة من خلال أمر restart، فالاستمرار إلى نقطة التوقف الثانية:<syntaxhighlight lang="text">
+
</syntaxhighlight>ثم نعيد تشغيل الشيفرة من خلال أمر <code>restart</code>، فالاستمرار إلى نقطة التوقف الثانية:<syntaxhighlight lang="text">
 
break in script_folder/myscript.js:8
 
break in script_folder/myscript.js:8
 
   6 }
 
   6 }
سطر 301: سطر 301:
 
  12   console.log('times!');
 
  12   console.log('times!');
  
</syntaxhighlight>ذكرنا سابقًا أنه لكي نذهب إلى السطر التالي في التنفيذ، ندخل أمر next، أما للذهاب إلى داخل الدالة التالية ندخل أمر step، فكما ترى، عندما أدخلنا next ذهبنا مباشرة إلى السطر التالي، لكن عندما أدخلنا step ذهبنا إلى داخل دالة console:<syntaxhighlight lang="text">
+
</syntaxhighlight>ذكرنا سابقًا أنه لكي نذهب إلى السطر التالي في التنفيذ، ندخل أمر <code>next</code>، أما للذهاب إلى داخل الدالة التالية ندخل أمر <code>step</code>، فكما ترى، عندما أدخلنا <code>next</code> ذهبنا مباشرة إلى السطر التالي، لكن عندما أدخلنا <code>step</code> ذهبنا إلى داخل دالة <code>console</code>:<syntaxhighlight lang="text">
 
debug> next
 
debug> next
 
break in script_folder/myscript.js:8
 
break in script_folder/myscript.js:8
سطر 345: سطر 345:
 
  11   console.log(numbers(x));
 
  11   console.log(numbers(x));
 
  12   console.log('times!');
 
  12   console.log('times!');
</syntaxhighlight>يمكنك أيضًا تخطي بقية الدالة الحالية إلى السطر التالي للاستدعاء مباشرة عن طريق out:<syntaxhighlight lang="text">
+
</syntaxhighlight>يمكنك أيضًا تخطي بقية الدالة الحالية إلى السطر التالي للاستدعاء مباشرة عن طريق <code>out</code>:<syntaxhighlight lang="text">
 
break in script_folder/myscript.js:10
 
break in script_folder/myscript.js:10
 
   8  debugger;
 
   8  debugger;
سطر 367: سطر 367:
 
  13   debugger;
 
  13   debugger;
 
debug>
 
debug>
</syntaxhighlight>ولكي تعرض نصوص الشيفرة التي تسبق وتلي نقطة التوقف الحالية، أدخل أمر list(5)‎ إذ إنَّ 5 هي عدد السطور التي ستُعرَض قبل وبعد نقطة التوقف:<syntaxhighlight lang="text">
+
</syntaxhighlight>ولكي تعرض نصوص الشيفرة التي تسبق وتلي نقطة التوقف الحالية، أدخل أمر <code>list(5)‎</code> إذ إنَّ <code>5</code> هي عدد السطور التي ستُعرَض قبل وبعد نقطة التوقف:<syntaxhighlight lang="text">
 
break in script_folder/myscript.js:11
 
break in script_folder/myscript.js:11
 
   9  console.log('world');
 
   9  console.log('world');
سطر 387: سطر 387:
 
  16 });
 
  16 });
 
debug>
 
debug>
</syntaxhighlight>لكن هل يجب عليّ الدخول في وضع repl أو استخدام أمر exec كلما أردت معاينة قيمة متغير أو دالة ما؟ بالطبع لا، هنا يظهر دور الراصدات.
+
</syntaxhighlight>لكن هل يجب عليّ الدخول في وضع <code>repl</code> أو استخدام أمر <code>exec</code> كلما أردت معاينة قيمة متغير أو دالة ما؟ بالطبع لا، هنا يظهر دور الراصدات.
  
 
= الراصدات Watchers =
 
= الراصدات Watchers =
 
يمكننا رصد قيم تعابير الدوال والمتغيرات أثناء التنقيح، إذ يتم تقييم وعرض كل تعبير موجود في قائمة الراصدات عند كل نقطة توقف مباشرة قبل شيفرة نقطة التوقف.
 
يمكننا رصد قيم تعابير الدوال والمتغيرات أثناء التنقيح، إذ يتم تقييم وعرض كل تعبير موجود في قائمة الراصدات عند كل نقطة توقف مباشرة قبل شيفرة نقطة التوقف.
  
لرصد تعبير معين، أضف التعبير إلى قائمة الراصدات عن طريق أمر watch('expr')‎ إذ إنَّ expr هو التعبير المُراد رصده، كما نرى من المثال التالي:<syntaxhighlight lang="text">
+
لرصد تعبير معين، أضف التعبير إلى قائمة الراصدات عن طريق أمر <code>watch('expr')‎</code> إذ إنَّ <code>expr</code> هو التعبير المُراد رصده، كما نرى من المثال التالي:<syntaxhighlight lang="text">
 
debug> watch('x')
 
debug> watch('x')
 
debug> run
 
debug> run
سطر 430: سطر 430:
 
debug>
 
debug>
  
</syntaxhighlight>أدخلنا x إلى قائمة الراصدات، ثم أعدنا تنفيذ الشيفرة، فظهرت رسالة خطأ بعدم وجود متغير x في نقطة التوقف الأولى التلقائية، لأنه لم يُعرَّف المتغير بعد، ثم في نقطة التوقف الثانية، أظهرت أولًا قيمة x قبل شيفرة نقطة التوقف، وفي نقطة التوقف الثالثة، أظهرت قيمة x وقد تغيرت بعد استدعاء دالة numbers.
+
</syntaxhighlight>أدخلنا <code>x</code> إلى قائمة الراصدات، ثم أعدنا تنفيذ الشيفرة، فظهرت رسالة خطأ بعدم وجود متغير <code>x</code> في نقطة التوقف الأولى التلقائية، لأنه لم يُعرَّف المتغير بعد، ثم في نقطة التوقف الثانية، أظهرت أولًا قيمة <code>x</code> قبل شيفرة نقطة التوقف، وفي نقطة التوقف الثالثة، أظهرت قيمة <code>x</code> وقد تغيرت بعد استدعاء دالة <code>numbers</code>.
  
يمكنك بالطبع إضافة أكثر من عنصر إلى قائمة الراصدات، أضفنا هنا دالة numbers وأعدنا تشغيل الشيفرة:<syntaxhighlight lang="text">
+
يمكنك بالطبع إضافة أكثر من عنصر إلى قائمة الراصدات، أضفنا هنا دالة <code>numbers</code> وأعدنا تشغيل الشيفرة:<syntaxhighlight lang="text">
 
debug> watch('numbers')
 
debug> watch('numbers')
 
debug> restart
 
debug> restart
سطر 472: سطر 472:
 
  14 console.log('hello');
 
  14 console.log('hello');
  
</syntaxhighlight>نلاحظ هنا أن قائمة الراصدات لم تُظهر أي قيمة للدالة numbers في أي من نقاط التوقف السابقة بل اكتفت بذكر أنها دالة، وهذا لأنني لم أضفها بالشكل الصحيح، يجب إضافة الدالة بهذا الشكل numbers()‎ مع وضع وسيط في حالة ما إذا تطلبت الدالة ذلك. انظر المثال التالي:<syntaxhighlight lang="text">
+
</syntaxhighlight>نلاحظ هنا أن قائمة الراصدات لم تُظهر أي قيمة للدالة <code>numbers</code> في أي من نقاط التوقف السابقة بل اكتفت بذكر أنها دالة، وهذا لأنني لم أضفها بالشكل الصحيح، يجب إضافة الدالة بهذا الشكل <code>numbers()‎</code> مع وضع وسيط في حالة ما إذا تطلبت الدالة ذلك. انظر المثال التالي:<syntaxhighlight lang="text">
 
debug> watch('numbers(x)')
 
debug> watch('numbers(x)')
 
debug> watch('numbers(3)')
 
debug> watch('numbers(3)')
سطر 522: سطر 522:
 
  13 }, 1000);
 
  13 }, 1000);
 
  14 console.log('hello');
 
  14 console.log('hello');
</syntaxhighlight>الآن، أظهرت قائمة الراصدات قيمة دالة numbers بالمتغيرات المختلفة التي وضعناها في عدة نقاط توقف. الآن، لنزل numbers من قائمة الراصدات باستخدام unwatch :<syntaxhighlight lang="text">
+
</syntaxhighlight>الآن، أظهرت قائمة الراصدات قيمة دالة <code>numbers</code> بالمتغيرات المختلفة التي وضعناها في عدة نقاط توقف. الآن، لنزل <code>numbers</code> من قائمة الراصدات باستخدام <code>unwatch</code> :<syntaxhighlight lang="text">
 
debug> unwatch('numbers')
 
debug> unwatch('numbers')
 
debug> run
 
debug> run
سطر 569: سطر 569:
 
  14 console.log('hello');
 
  14 console.log('hello');
  
</syntaxhighlight>يمكنك أيضًا عرض الراصدات عن طريق أمر watchers:<syntaxhighlight lang="text">
+
</syntaxhighlight>يمكنك أيضًا عرض الراصدات عن طريق أمر <code>watchers</code>:<syntaxhighlight lang="text">
 
>13   debugger;
 
>13   debugger;
 
  14 }, 1000);
 
  14 }, 1000);
سطر 579: سطر 579:
 
debug>
 
debug>
  
</syntaxhighlight>لإغلاق المنقّح، أدخل ‎.exit (لا تنس النقطة قبل كلمة exit):<syntaxhighlight lang="text">
+
</syntaxhighlight>لإغلاق المنقّح، أدخل <code>‎.exit</code> (لا تنس النقطة قبل كلمة <code>exit</code>):<syntaxhighlight lang="text">
 
  11   console.log('times!');
 
  11   console.log('times!');
 
>12   debugger;
 
>12   debugger;
سطر 592: سطر 592:
  
 
=== التقدم في الشيفرة ===
 
=== التقدم في الشيفرة ===
* cont أو c: الاستمرار في تنفيذ الكود حتى نقطة التوقف التالية.
+
* <code>cont</code> أو <code>c</code>: الاستمرار في تنفيذ الكود حتى نقطة التوقف التالية.
* next أو n: الانتقال إلى الخطوة التالية مباشرة في تنفيذ الكود.
+
* <code>next</code> أو <code>n</code>: الانتقال إلى الخطوة التالية مباشرة في تنفيذ الكود.
* step أو s: التعمق في الدالة الفرعية التالية مباشرة.
+
* <code>step</code> أو <code>s</code>: التعمق في الدالة الفرعية التالية مباشرة.
* out أو o: الانتقال إلى الخطوة التالية في مسار التنفيذ الرئيسي.
+
* <code>out</code> أو <code>o</code>: الانتقال إلى الخطوة التالية في مسار التنفيذ الرئيسي.
* pause: إيقاف تنفيذ الكود (مثل زر الإيقاف في أدوات المطوّر في متصفحات الويب).
+
* <code>pause</code>: إيقاف تنفيذ الكود (مثل زر الإيقاف في أدوات المطوّر في متصفحات الويب).
  
 
=== نقاط التوقف ===
 
=== نقاط التوقف ===
* setBreakpoint()‎ أو sb()‎: وضع نقطة توقف في السطر الحالي.
+
* <code>setBreakpoint()‎</code> أو <code>sb()‎</code>: وضع نقطة توقف في السطر الحالي.
* setBreakpoint(line)‎ أو sb(line)‎:وضع نقطة توقف في سطر معين، حيث line هو رقم السطر.
+
* <code>setBreakpoint(line)‎</code> أو <code>sb(line)‎</code>: وضع نقطة توقف في سطر معين، حيث <code>line</code> هو رقم السطر.
* setBreakpoint(function()')‎ أو sb(...)‎: وضع نقطة التوقف عند أول استدعاء لدالة معينة، حيث function() هي الدالة التي نريد استدعاءها.
+
* <code>setBreakpoint(function())‎</code> أو <code>sb(...)</code>‎: وضع نقطة التوقف عند أول استدعاء لدالة معينة، حيث <code>function()‎</code> هي الدالة التي نريد استدعاءها.
* setBreakpoint('script.js', 1)‎ أو sb(...)‎: وضع نقطة التوقف في سطر معين من ملف محدد، وهو في هذا المثال السطر الأول من الملف script.js.
+
* <code>setBreakpoint('script.js', 1)‎</code> أو <code>sb(...)‎</code>: وضع نقطة التوقف في سطر معين من ملف محدد، وهو في هذا المثال السطر الأول من الملف script.js.
* clearBreakpoint('script.js', 1)‎ أو cb(...)‎: إزالة نقطة التوقف من سطر معين في ملف محدد، وهو في هذا المثال السطر الأول من الملف script.js.
+
* <code>clearBreakpoint('script.js', 1)‎</code> أو <code>cb(...)‎</code>: إزالة نقطة التوقف من سطر معين في ملف محدد، وهو في هذا المثال السطر الأول من الملف script.js.
يمكنك أيضًا تحديد نقطة توقف في ملف (وحدة [module]) لم يُحمَّل بعد. في المثال التالي، حدَّدنا نقطة توقف في ملف mod.js في السطر 2، طبعًا سيظهر لك في البداية تحذير أن ملف mod.js لم يُحمَّل بعد، يمكنك تجاهله:<syntaxhighlight lang="text">
+
يمكنك أيضًا تحديد نقطة توقف في ملف (وحدة [[[Node.js/modules|module]]]) لم يُحمَّل بعد. في المثال التالي، حدَّدنا نقطة توقف في ملف mod.js في السطر 2، طبعًا سيظهر لك في البداية تحذير أن ملف mod.js لم يُحمَّل بعد، يمكنك تجاهله:<syntaxhighlight lang="text">
 
$ node inspect main.js
 
$ node inspect main.js
 
< Debugger listening on ws://127.0.0.1:9229/4e3db158-9791-4274-8909-914f7facf3bd
 
< Debugger listening on ws://127.0.0.1:9229/4e3db158-9791-4274-8909-914f7facf3bd
سطر 626: سطر 626:
  
 
=== إظهار المعلومات ===
 
=== إظهار المعلومات ===
* backtrace أو bt: طباعة قائمة تتبع إطار التنفيذ الحالي (current execution frame).
+
* <code>backtrace</code> أو <code>bt</code>: طباعة قائمة تتبع إطار التنفيذ الحالي (current execution frame).
* list(5)‎: إظهار الشيفرة بعدد من السطور قبل وبعد نقطة التوقف الحالية (حيث 5 هي عدد السطور قبل وبعد).
+
* <code>list(5)‎</code>: إظهار الشيفرة بعدد من السطور قبل وبعد نقطة التوقف الحالية (حيث 5 هي عدد السطور قبل وبعد).
* watch(expr)‎: إضافة تعبير إلى قائمة الراصدات.
+
* <code>watch(expr)</code>‎: إضافة تعبير إلى قائمة الراصدات.
* unwatch(expr)‎: إزالة تعبير من قائمة الراصدات.
+
* <code>unwatch(expr)‎</code>: إزالة تعبير من قائمة الراصدات.
* watchers: عرض قائمة الراصدات.
+
* <code>watchers</code>: عرض قائمة الراصدات.
* repl: تفعيل وحدة REPL للمنقّح لتقييم تعبيرٍ برمجيٍ في سياق الشيفرة التي يجري تنقيحها.
+
* <code>repl</code>: تفعيل وحدة [[Node.js/repl|REPL]] للمنقّح لتقييم تعبيرٍ برمجيٍ في سياق الشيفرة التي يجري تنقيحها.
* exec expr: تنفيذ تعبير في سياق الشيفرة التي يجري تنفيذها.
+
* <code>exec expr</code>: تنفيذ تعبير في سياق الشيفرة التي يجري تنفيذها.
  
 
=== أدوات التحكم بالتنفيذ ===
 
=== أدوات التحكم بالتنفيذ ===
* run: تشغيل الشيفرة (تعمل تلقائيًا عند بداية المنقّح).
+
* <code>run</code>: تشغيل الشيفرة (تعمل تلقائيًا عند بداية المنقّح).
* restart: إعادة تشغيل الشيفرة.
+
* <code>restart</code>: إعادة تشغيل الشيفرة.
* kill: قتل الشيفرة نهائيًا.
+
* <code>kill</code>: قتل الشيفرة نهائيًا.
  
 
=== أوامر متنوعة ===
 
=== أوامر متنوعة ===
* scripts عرض جميع الملفات التي تم تحميلها.
+
* <code>scripts</code>: عرض جميع الملفات التي تم تحميلها.
* version عرض رقم إصدار V8 المستخدمة.
+
* <code>version</code>: عرض رقم إصدار V8 المستخدمة.
  
 
== الاستخدام المتقدم ==
 
== الاستخدام المتقدم ==
  
 
=== دمج محقق V8 مع node.js ===
 
=== دمج محقق V8 مع node.js ===
يسمح دمج محقق V8 باستخدام أدوات المطور في متصفح كروم لملفات Node.js من أجل التنقيح والتشخيص. حيث يستخدم بروتوكول أدوات مطور كروم.
+
يسمح دمج محقق V8 باستخدام أدوات المطور في متصفح Chrome لملفات Node.js من أجل التنقيح والتشخيص. حيث يستخدم بروتوكول أدوات مطور Chrome.
  
يمكن تفعيل محقق   بتمرير الخيار ‎--inspect عند بدء تطبيق Node.js. ومن الممكن أيضًا توفير منفذ مخصص باستخدام هذا الخيار (على سبيل المثال ‎--inspect-9222 سيقبل بتوصيلات أدوات المطور على منفذ 9222).
+
يمكن تفعيل محقق  Chrome بتمرير الخيار <code>‎--inspect</code> عند بدء تطبيق Node.js. ومن الممكن أيضًا توفير منفذ مخصص باستخدام هذا الخيار (على سبيل المثال <code>‎--inspect-9222</code> سيقبل بتوصيلات أدوات المطور على منفذ 9222).
  
للتوقف عند السطر الأول من شيفرة التطبيق، مرر الخيار ‎--inpect-brk بدلًا من ‎--inspect<syntaxhighlight lang="text">
+
للتوقف عند السطر الأول من شيفرة التطبيق، مرر الخيار <code>‎--inpect-brk</code> بدلًا من <code>‎--inspect</code>:<syntaxhighlight lang="text">
 
$ node --inspect index.js
 
$ node --inspect index.js
 
Debugger listening on 127.0.0.1:9229.
 
Debugger listening on 127.0.0.1:9229.
سطر 656: سطر 656:
 
   chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/dc9010dd-f8b8-4ac5-a510-c1a114ec7d29
 
   chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/dc9010dd-f8b8-4ac5-a510-c1a114ec7d29
  
</syntaxhighlight>يوفر لك المنقّح رابطًا تستطيع الدخول إليه في Chrome للبدء بعملية التنقيح باستخدام أدوات المطور في كروم.
+
</syntaxhighlight>يوفر لك المنقّح رابطًا تستطيع الدخول إليه في Chrome للبدء بعملية التنقيح باستخدام أدوات المطور في Chrome.
  
 
لاحظ أنه في المثال العلوي، تم إنتاج المعرف الفريد العالمي UUID الموجود في نهاية الرابط dc9010dd-f8b8-4ac5-a510-c1a114ec7d29 بشكل مؤقت، إذ ستغير في كل جلسة تنقيح.
 
لاحظ أنه في المثال العلوي، تم إنتاج المعرف الفريد العالمي UUID الموجود في نهاية الرابط dc9010dd-f8b8-4ac5-a510-c1a114ec7d29 بشكل مؤقت، إذ ستغير في كل جلسة تنقيح.
  
 
== مصادر ==
 
== مصادر ==
* صفحة Debugger في توثيق Node.js الرسمي.
+
* [https://nodejs.org/dist/latest-v10.x/docs/api/debugger.html صفحة Debugger في توثيق Node.js الرسمي].
 +
[[تصنيف:Node.js]]

مراجعة 15:01، 30 يوليو 2018

المنقّح Debugger

درجة الثبات: 2 - مستقر

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

سنستخدم في مثالنا هذا ملف myscript:

// myscript.js
global.x = 5;
setTimeout(() => {
 console.log('world');
}, 1000);

console.log('hello');

كما ترى، المثال المستخدم هنا بسيط، حيث يبدأ في السطر الأول بتحديد متغير عام x بقيمة 5، ثم في السطر الثاني يستخدم الدالة setTimeout‎()‎‎ في ظهور كلمة world بعد انتظار 1000 مللي ثانية (1 ثانية)، وينتهي بظهور hello في السطر الأخير (وفي هذه الحالة ستظهر على الفور قبل كلمة world التي ستتأخر لمدة ثانية). سنضع جملة debugger;‎ في النص لتفعيل نقطة توقف breakpoint في هذا الموقع من الشيفرة:

// myscript.js
global.x = 5;
setTimeout(() => {
 debugger;
 console.log('world');
}, 1000);
console.log('hello');

والآن، دعنا نشغل المنقّح، ابدأ تطبيق Node.js مع الخيار inspect ثم مسار ملف الشيفرة المُراد استخدامه، وهو في حالتنا myscript.js:

$ node inspect script_folder/myscript.js
< Debugger listening on ws://127.0.0.1:9229/2c9204a9-3569-40d3-a1e0-1e90cf2e893a
< For help see https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in script_folder/myscript.js:1
> 1 (function (exports, require, module, __filename, __dirname) { // myscript.js
  2 global.x = 5;
  3 setTimeout(() => {
debug> cont
< hello
break in script_folder/myscript.js:4
  2 global.x = 5;
  3 setTimeout(() => {
> 4   debugger;
  5  console.log('world');
  6 }, 1000);
debug>

كما رأينا توقف المنقّح تلقائيًا في بداية تنفيذ الشيفرة، ثم أدخلنا أمر cont للاستمرار في تنفيذ الشيفرة حتى نقطة التوقف التالية، فظهرت أولًا كلمة hello وانتظر المنقّح لمدة 1 ثانية قبل التوقف عند نقطة التوقف في السطر الثالث، لأنها موضوعة بداخل دالة setTimeout()‎. بينما لو وضعنا نقطة توقف في السطر الثاني من الشيفرة:

// myscript.js
global.x = 5;
debugger;
setTimeout(() => {
 debugger;
 console.log('world');
}, 1000);
console.log('hello');

وقمنا بإعادة تنفيذ الشيفرة (بعد حفظ التعديلات) عن طريق أمر restart، ثم إدخال أمر cont للاستمرار بعد أول سطر، نجد أنه قد توقف في السطر الثاني بالفعل:

debug> restart
< Debugger listening on ws://127.0.0.1:9229/e6ccb551-d2aa-451f-81e7-0e5f09e0fb1c
< For help see https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in script_folder/myscript.js:1
> 1 (function (exports, require, module, __filename, __dirname) { // myscript.js
  2 global.x = 5;
  3 debugger;
debug> cont
break in script_folder/myscript.js:3
  1 (function (exports, require, module, __filename, __dirname) { // myscript.js
  2 global.x = 5;
> 3 debugger;
  4 setTimeout(() => {
  5  debugger;
debug>

لندخل الأمر cont للاستمرار في تنفيذ الشيفرة حتى نقطة التوقف التالية:

debug> cont
< hello
break in script_folder/myscript.js:5
  3 debugger;
  4 setTimeout(() => {
> 5   debugger;
  6  console.log('world');
  7 }, 1000);
debug>

لتقييم واختبار الشيفرة عن بعد، يمكنك استخدام وحدة REPL عن طريق إدخال أمر repl ببساطة:

break in script_folder/myscript.js:5
  3 debugger;
  4 setTimeout(() => {
> 5   debugger;
  6  console.log('world');
  7 }, 1000);
debug> repl
Press Ctrl + C to leave debug repl
> x
5
> 4 * x
20
> y = 4
4
> y * x
20
> x = 3
3
> y * x
12

كما ترى، في وضع repl، يمكنك معرفة قيمة المتغيرات، وتحديد متغيرات جديدة، وتغيير قيمة المتغيرات، وما إلى ذلك، هذه الوحدة تمكنك من تقييم الكود بشكل تفاعلي، للعودة إلى المنقّح، اضغط Ctrl+c. أدخل next للانتقال إلى السطر التالي مباشرة. أما الضغط على زر الإدخال Enter دون كتابة أوامر يعيد تكرار آخر أمر مكتوب.

debug> next
break in script_folder/myscript.js:6
  4 setTimeout(() => {
  5  debugger;
> 6   console.log('world');
  7 }, 1000);
  8 console.log('hello');
debug>
< world
break in script_folder/myscript.js:7
  5  debugger;
  6  console.log('world');
> 7 }, 1000);
  8 console.log('hello');
  9 });
debug>

لنقم ببعض التعديلات في الشيفرة:

// myscript.js

global.x = 5;
function numbers(num) {
 x++;
 return num;
}

setTimeout(() => {
 debugger;
 console.log('world');
 console(numbers(x));
 console.log('times!');
 debugger;
}, 1000);

console.log('hello');

كما ترى، أضفنا دالة جديدة numbers مع الوسيط x، تزيد قيمة متغير x العام وتعيد قيمة num، وأضفت نقطة توقف في السطر الثاني عشر. لنعد تشغيل الشيفرة مجددًا باستخدام restart، ثم نصل إلى أول نقطة توقف باستخدام cont، ثم ندخل في وضع repl:

debug> repl
Press Ctrl + C to leave debug repl
> x
5
> numbers()
undefined
> x
6
> numbers(x)
6
> x
7
> numbers('text')
'text'
> x
8
>

كما ترى، عندما استدعينا الدالة numbers()‎ دون وسيط، فظهر خطأ. بينما عندما مررنا المتغير x كوسيط، فأعيدت قيمة المتغير، وترى أن x الآن أصبح 8 بعد استدعاء الدالة ثلاث مرات. دعنا نعود إلى وضع المنقّح مجددًا (لاحظ أنه في وضع repl يسبق المؤشر علامة ‎< فقط، بينما في وضع المنقّح يسبقه علامة ‎>debug) وذلك بالضغط على Ctrl+c كما أسلفنا سابقًا:

debug> cont
< world
< 8
< times!
break in script_folder/myscript.js:12
 10   console.log(numbers(x));
 11   console.log('times!');
>12   debugger;
 13 }, 1000);
 14 console.log('hello');
debug>

ذهبنا إلى نقطة التوقف التالية، ترى في الناتج أنه طُبِعَ الرقم 8 بدلًا من 5 أي أن التغييرات التي أجريناها في وضع repl ما زالت جارية. يمكنك أيضًا تنفيذ أي تعبير في المنقّح بسهولة باستخدام exec قبل التعبير المطلوب كما ترى تاليًا:

> 8   debugger;
  9  console.log('world');
  10   numbers(x);
debug> exec numbers(x)
5
debug> exec x
6
debug> exec x = 2
2
debug> exec numbers(x)
2
debug>

يمكنك بدء الشيفرة من جديد باستخدام أمر run، أما أمر restart يعيد فتح الشيفرة في حالة إجراء أية تعديلات على الملف:

> 1 (function (exports, require, module, __filename, __dirname) { // myscript.js
  2 global.x = 5;
  3 function numbers(num) {
debug> cont
< hello
break in script_folder/myscript.js:8
  6 }
  7 setTimeout(() => {
> 8   debugger;
  9  console.log('world');
  10  console.log(numbers(x));
debug> next
break in script_folder/myscript.js:9
  7 setTimeout(() => {
  8  debugger;
> 9   console.log('world');
  10  console.log(numbers(x));
  11  console.log('times!');
debug>

لا يقتصر تحديد نقاط التوقف من داخل الملف فقط، بل يمكنك أيضًا تحديدها من داخل المنقّح. لتحديد السطر الحالي كنقطة توقف أدخل setBreakpoint()‎ :

debug> setBreakpoint()
 4  x++;
 5  return num;
 6 }
 7 setTimeout(() => {
 8  debugger;
> 9   console.log('world');
 10   console.log(numbers(x));
 11   console.log('times!');
 12   debugger;
 13 }, 1000);
 14 console.log('hello');
debug> run
< Debugger listening on ws://127.0.0.1:9229/f2d3a89b-215d-4d9c-a5d0-3c9903ae610d
< For help see https://nodejs.org/en/docs/inspector
< Debugger attached.
Warning: script '/script_folder/myscript.js' was not loaded yet.
1 breakpoints restored.
Break on start in script_folder/myscript.js:1
> 1 (function (exports, require, module, __filename, __dirname) { // myscript.js
  2 global.x = 5;
  3 function numbers(num) {
debug> cont
< hello
break in script_folder/myscript.js:8
  6 }
  7 setTimeout(() => {
> 8   debugger;
* 9   console.log('world');
 10   console.log(numbers(x));
debug> cont
break in script_folder/myscript.js:9
  7 setTimeout(() => {
  8  debugger;
> 9   console.log('world');
 10   console.log(numbers(x));
 11   console.log('times!');

كما تلاحظ، فإن نقاط التوقف المحددة مسبقًا من المنقّح تُميّز بعلامة (*) مباشرة قبل رقم السطر. يمكنك أيضًا تحديد نقطة التوقف في سطر محدد setBreakpoint(line)‎ حيث line هنا هي رقم السطر، أو عند أول استدعاء لتعبير معين setBreakpoint('numbers()')‎. كما يمكنك إزالة نقطة التوقف باستخدام الدالة clearBreakpoint('filename', line)‎ إذ إنَّ filename هو اسم الملف و line هو رقم السطر، ففي المثال التالي نزيل نقطة التوقف في السطر التاسع من ملف myscript.js:

clearBreakpoint('myscript.js', 9)

لنتعمق قليلًا في التقدم داخل الشيفرة، أولًا أجرينا تعديلًا بسيطًا على الشيفرة وأضفنا استدعاءً لدالة numbers:

// myscript.js

global.x = 5;
function numbers(num) {
 x++;
 return num;
}

setTimeout(() => {
 debugger;
 console.log('world');
 numbers(x);
 console.log(numbers(x));
 console.log('times!');
 debugger;
}, 1000);

console.log('hello');

ثم نعيد تشغيل الشيفرة من خلال أمر restart، فالاستمرار إلى نقطة التوقف الثانية:

break in script_folder/myscript.js:8
  6 }
  7 setTimeout(() => {
> 8   debugger;
  9  console.log('world');
 10   numbers(x);
debug> next
break in script_folder/myscript.js:9
  7 setTimeout(() => {
  8  debugger;
> 9   console.log('world');
 10   numbers(x);
 11   console.log(numbers(x));
debug>
< world
break in script_folder/myscript.js:10
  8  debugger;
  9  console.log('world');
>10   numbers(x);
 11   console.log(numbers(x));
 12   console.log('times!');

ذكرنا سابقًا أنه لكي نذهب إلى السطر التالي في التنفيذ، ندخل أمر next، أما للذهاب إلى داخل الدالة التالية ندخل أمر step، فكما ترى، عندما أدخلنا next ذهبنا مباشرة إلى السطر التالي، لكن عندما أدخلنا step ذهبنا إلى داخل دالة console:

debug> next
break in script_folder/myscript.js:8
  6 }
  7 setTimeout(() => {
> 8   debugger;
  9  console.log('world');
 10   numbers(x);
debug> step
break in script_folder/myscript.js:9
  7 setTimeout(() => {
  8  debugger;
> 9   console.log('world');
 10   numbers(x);
 11   console.log(numbers(x));
debug>
break in bootstrap_node.js:316
314       enumerable: true,
315       get() {
>316         return wrappedConsole;
317       }
318     });
debug>
break in bootstrap_node.js:316
 314       enumerable: true,
 315       get() {
>316         return wrappedConsole;
 317       }
 318     });
debug>
break in script_folder/myscript.js:9
  7 setTimeout(() => {
  8  debugger;
> 9   console.log('world');
 10   numbers(x);
 11   console.log(numbers(x));
debug> next
< world
break in script_folder/myscript.js:10
  8  debugger;
  9  console.log('world');
>10   numbers(x);
 11   console.log(numbers(x));
 12   console.log('times!');

يمكنك أيضًا تخطي بقية الدالة الحالية إلى السطر التالي للاستدعاء مباشرة عن طريق out:

break in script_folder/myscript.js:10
  8  debugger;
  9  console.log('world');
>10   numbers(x);
 11   console.log(numbers(x));
 12   console.log('times!');
debug>
break in script_folder/myscript.js:4
  2 global.x = 5;
  3 function numbers(num) {
> 4   x++;
  5  return num;
  6 }
debug> out
break in script_folder/myscript.js:11
  9  console.log('world');
 10   numbers(x);
>11   console.log(numbers(x));
 12   console.log('times!');
 13   debugger;
debug>

ولكي تعرض نصوص الشيفرة التي تسبق وتلي نقطة التوقف الحالية، أدخل أمر list(5)‎ إذ إنَّ 5 هي عدد السطور التي ستُعرَض قبل وبعد نقطة التوقف:

break in script_folder/myscript.js:11
  9  console.log('world');
 10   numbers(x);
>11   console.log(numbers(x));
 12   console.log('times!');
 13   debugger;
debug> list(5)
  6 }
  7 setTimeout(() => {
  8  debugger;
  9  console.log('world');
 10   numbers(x);
>11   console.log(numbers(x));
 12   console.log('times!');
 13   debugger;
 14 }, 1000);
 15 console.log('hello');
 16 });
debug>

لكن هل يجب عليّ الدخول في وضع repl أو استخدام أمر exec كلما أردت معاينة قيمة متغير أو دالة ما؟ بالطبع لا، هنا يظهر دور الراصدات.

الراصدات Watchers

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

لرصد تعبير معين، أضف التعبير إلى قائمة الراصدات عن طريق أمر watch('expr')‎ إذ إنَّ expr هو التعبير المُراد رصده، كما نرى من المثال التالي:

debug> watch('x')
debug> run
< Debugger listening on ws://127.0.0.1:9229/f9a9db17-725b-48d1-9c16-de2b5564498b
< For help see https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in script_folder/myscript.js:1
Watchers:
  0: x =
    ReferenceError: x is not defined
        ...
> 1 (function (exports, require, module, __filename, __dirname) { // myscript.js
  2 global.x = 5;
  3 function numbers(num) {
debug> cont
< hello
break in script_folder/myscript.js:8
Watchers:
  0: x = 5
  6 }
  7 setTimeout(() => {
> 8   debugger;
  9  console.log('world');
 10   console.log(numbers(x));
debug> cont
< world
< 5
< times!
break in script_folder/myscript.js:12
Watchers:
  0: x = 6
 10   console.log(numbers(x));
 11   console.log('times!');
>12   debugger;
 13 }, 1000);
 14 console.log('hello');
debug>

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

debug> watch('numbers')
debug> restart
< Debugger listening on ws://127.0.0.1:9229/f9a9db17-725b-48d1-9c16-de2b5564498b
< For help see https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in script_folder/myscript.js:1
Watchers:
  0: x =
    ReferenceError: x is not defined
        ...
  1: numbers = [Function: numbers]
> 1 (function (exports, require, module, __filename, __dirname) { // myscript.js
  2 global.x = 5;
  3 function numbers(num) {
debug> cont
< hello
break in script_folder/myscript.js:8
Watchers:
  0: x = 5
  1: numbers = [Function: numbers]
  6 }
  7 setTimeout(() => {
> 8   debugger;
  9  console.log('world');
 10   console.log(numbers(x));
debug> cont
< world
< 5
< times!
break in script_folder/myscript.js:12
Watchers:
  0: x = 6
  1: numbers = [Function: numbers]
 10   console.log(numbers(x));
 11   console.log('times!');
>12   debugger;
 13 }, 1000);
 14 console.log('hello');

نلاحظ هنا أن قائمة الراصدات لم تُظهر أي قيمة للدالة numbers في أي من نقاط التوقف السابقة بل اكتفت بذكر أنها دالة، وهذا لأنني لم أضفها بالشكل الصحيح، يجب إضافة الدالة بهذا الشكل numbers()‎ مع وضع وسيط في حالة ما إذا تطلبت الدالة ذلك. انظر المثال التالي:

debug> watch('numbers(x)')
debug> watch('numbers(3)')
debug> run
< Debugger listening on ws://127.0.0.1:9229/061a973f-98c0-46c9-a792-b3e640b07ae6
< For help see https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in script_folder/myscript.js:1
Watchers:
  0: x =
    ReferenceError: x is not defined
        ...
  1: numbers = [Function: numbers]
  2: numbers(x) =
    ReferenceError: x is not defined
        ...
  3: numbers(3) =
    ReferenceError: x is not defined
        ...
> 1 (function (exports, require, module, __filename, __dirname) { // myscript.js
  2 global.x = 5;
  3 function numbers(num) {
debug> cont
< hello
break in script_folder/myscript.js:8
Watchers:
  0: x = 5
  1: numbers = [Function: numbers]
  2: numbers(x) = 5
  3: numbers(3) = 3
  6 }
  7 setTimeout(() => {
> 8   debugger;
  9  console.log('world');
 10   console.log(numbers(x));
debug> cont
< world
< 7
< times!
break in script_folder/myscript.js:12
Watchers:
  0: x = 8
  1: numbers = [Function: numbers]
  2: numbers(x) = 8
  3: numbers(3) = 3
 10   console.log(numbers(x));
 11   console.log('times!');
>12   debugger;
 13 }, 1000);
 14 console.log('hello');

الآن، أظهرت قائمة الراصدات قيمة دالة numbers بالمتغيرات المختلفة التي وضعناها في عدة نقاط توقف. الآن، لنزل numbers من قائمة الراصدات باستخدام unwatch :

debug> unwatch('numbers')
debug> run
< Debugger listening on ws://127.0.0.1:9229/c908d8a3-51f6-4acc-9e58-ee95223cbcfd
< For help see https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in script_folder/myscript.js:1
Watchers:
  0: x =
    ReferenceError: x is not defined
        ...
  1: numbers(x) =
    ReferenceError: x is not defined
       ...
  2: numbers(3) =
    ReferenceError: x is not defined
      ...
> 1 (function (exports, require, module, __filename, __dirname) { // myscript.js
  2 global.x = 5;
  3 function numbers(num) {
debug> cont
< hello
break in script_folder/myscript.js:8
Watchers:
  0: x = 5
  1: numbers(x) = 5
  2: numbers(3) = 3
  6 }
  7 setTimeout(() => {
> 8   debugger;
  9  console.log('world');
 10   console.log(numbers(x));
debug> cont
< world
< 7
< times!
break in script_folder/myscript.js:12
Watchers:
  0: x = 8
  1: numbers(x) = 8
  2: numbers(3) = 3
 10   console.log(numbers(x));
 11   console.log('times!');
>12   debugger;
 13 }, 1000);
 14 console.log('hello');

يمكنك أيضًا عرض الراصدات عن طريق أمر watchers:

>13   debugger;
 14 }, 1000);
 15 console.log('hello');
debug> watchers
  0: x = 11
  1: numbers(x) = 11
   2: numbers(3) = 3
debug>

لإغلاق المنقّح، أدخل ‎.exit (لا تنس النقطة قبل كلمة exit):

 11   console.log('times!');
>12   debugger;
 13 }, 1000);
 14 console.log('hello');
debug> .exit
$

مرجع الأوامر

التقدم في الشيفرة

  • cont أو c: الاستمرار في تنفيذ الكود حتى نقطة التوقف التالية.
  • next أو n: الانتقال إلى الخطوة التالية مباشرة في تنفيذ الكود.
  • step أو s: التعمق في الدالة الفرعية التالية مباشرة.
  • out أو o: الانتقال إلى الخطوة التالية في مسار التنفيذ الرئيسي.
  • pause: إيقاف تنفيذ الكود (مثل زر الإيقاف في أدوات المطوّر في متصفحات الويب).

نقاط التوقف

  • setBreakpoint()‎ أو sb()‎: وضع نقطة توقف في السطر الحالي.
  • setBreakpoint(line)‎ أو sb(line)‎: وضع نقطة توقف في سطر معين، حيث line هو رقم السطر.
  • setBreakpoint(function())‎ أو sb(...)‎: وضع نقطة التوقف عند أول استدعاء لدالة معينة، حيث function()‎ هي الدالة التي نريد استدعاءها.
  • setBreakpoint('script.js', 1)‎ أو sb(...)‎: وضع نقطة التوقف في سطر معين من ملف محدد، وهو في هذا المثال السطر الأول من الملف script.js.
  • clearBreakpoint('script.js', 1)‎ أو cb(...)‎: إزالة نقطة التوقف من سطر معين في ملف محدد، وهو في هذا المثال السطر الأول من الملف script.js.

يمكنك أيضًا تحديد نقطة توقف في ملف (وحدة [[[Node.js/modules|module]]]) لم يُحمَّل بعد. في المثال التالي، حدَّدنا نقطة توقف في ملف mod.js في السطر 2، طبعًا سيظهر لك في البداية تحذير أن ملف mod.js لم يُحمَّل بعد، يمكنك تجاهله:

$ node inspect main.js
< Debugger listening on ws://127.0.0.1:9229/4e3db158-9791-4274-8909-914f7facf3bd
< For help, see: https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in main.js:1
> 1 (function (exports, require, module, __filename, __dirname) { const mod = require('./mod.js');
  2 mod.hello();
  3 mod.hello();
debug> setBreakpoint('mod.js', 22)
Warning: script 'mod.js' was not loaded yet.
debug> c
break in mod.js:22
 20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
 21
>22 exports.hello = function() {
 23   return 'hello from module';
 24 };
debug>

إظهار المعلومات

  • backtrace أو bt: طباعة قائمة تتبع إطار التنفيذ الحالي (current execution frame).
  • list(5)‎: إظهار الشيفرة بعدد من السطور قبل وبعد نقطة التوقف الحالية (حيث 5 هي عدد السطور قبل وبعد).
  • watch(expr)‎: إضافة تعبير إلى قائمة الراصدات.
  • unwatch(expr)‎: إزالة تعبير من قائمة الراصدات.
  • watchers: عرض قائمة الراصدات.
  • repl: تفعيل وحدة REPL للمنقّح لتقييم تعبيرٍ برمجيٍ في سياق الشيفرة التي يجري تنقيحها.
  • exec expr: تنفيذ تعبير في سياق الشيفرة التي يجري تنفيذها.

أدوات التحكم بالتنفيذ

  • run: تشغيل الشيفرة (تعمل تلقائيًا عند بداية المنقّح).
  • restart: إعادة تشغيل الشيفرة.
  • kill: قتل الشيفرة نهائيًا.

أوامر متنوعة

  • scripts: عرض جميع الملفات التي تم تحميلها.
  • version: عرض رقم إصدار V8 المستخدمة.

الاستخدام المتقدم

دمج محقق V8 مع node.js

يسمح دمج محقق V8 باستخدام أدوات المطور في متصفح Chrome لملفات Node.js من أجل التنقيح والتشخيص. حيث يستخدم بروتوكول أدوات مطور Chrome.

يمكن تفعيل محقق  Chrome بتمرير الخيار ‎--inspect عند بدء تطبيق Node.js. ومن الممكن أيضًا توفير منفذ مخصص باستخدام هذا الخيار (على سبيل المثال ‎--inspect-9222 سيقبل بتوصيلات أدوات المطور على منفذ 9222).

للتوقف عند السطر الأول من شيفرة التطبيق، مرر الخيار ‎--inpect-brk بدلًا من ‎--inspect:

$ node --inspect index.js
Debugger listening on 127.0.0.1:9229.
To start debugging, open the following URL in Chrome:
   chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/dc9010dd-f8b8-4ac5-a510-c1a114ec7d29

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

لاحظ أنه في المثال العلوي، تم إنتاج المعرف الفريد العالمي UUID الموجود في نهاية الرابط dc9010dd-f8b8-4ac5-a510-c1a114ec7d29 بشكل مؤقت، إذ ستغير في كل جلسة تنقيح.

مصادر