ما هو الـ stacktrace؟
في لغة الجافا، تنقسم ذاكرة الـ JVM إلى عدة أقسام، ومنها ما يسمى بالـ stack. عند استدعاء دالة س (method) فإنه يُنشأ frameبداخله معلومات الدالة س ويوضع هذا الـ frame أعلى الـ stack. وعند الانتهاء من تنفيذ الدالة س، يتم إخراج الـ stack frame الخاص بالدالة س من الـ stack ويرجع مسار التنفيذ (flow of control) إلى الدالة السابقة والتي استدعت الدالة س.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class Main { public static void main(String[] args) { x(); } public static void x() { y(); } public static void y() { // ... } } |
شكل الـ stack سيكون كالتالي:
بدايةً يقوم الـ JVM بإنشاء الـ main thread ثم يقوم باستدعاء دالة ()main ويضع frame خاص بهذه الدالة في الـ stack. خلال تنفيذ دالة الـ ()main سيقوم باستدعاء الدالة ()x وتباعاً يضع frame خاص بها في الـ stack. وخلال تنفيذ الدالة ()x يستدعي الدالة ()yويضع frame في الـ stack. عند الانتهاء من تنفيذ الدالة ()y يقوم الـ JVM بإزالة الـ frame الموجود بأعلى الـ stack وهو الـ frameالخاص بالدالة ()y. بعد ذلك يعود لاستكمال تنفيذ الدالة ()x ويزيل الـ frame الخاص بها عند الانتهاء من تنفيذها. وأخيراً يعود للدالة()main ويكمل تنفيذها، وعند الانتهاء منها يزيل آخر frame موجود في الـ stack ويقوم بإنهاء الـ main tread (ليسس شرطاً أن ينتهي البرنامج بانتهاء الـ main thread).
ما هو الـ stacktrace؟
الـ stacktrace باختصار هو سلسلة من الاستدعاءات للدوال method invocations مطابقة للـ stack frames. يستخدم الـ stacktrace عادةً في معالجة الأخطاء واكتشاف مصدرها. لنأخذ مثال بسيط لتتضح الصورة:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import java.io.IOException; public class Main { public static void main(String[] args) throws Exception { x(); } public static void x() throws Exception { y(); } public static void y() throws IOException { throw new IOException("IOException in y()"); } } |
يتم طباعة الـ stacktrace على الشاشة في حالتين:
– إما أن نمسك الـ exception في try and catch وبداخل الـ catch نطبع الـ stacktrace عن طريق الدالة ()e.printStackTraceحيث أن e هو مرجع للخطأ reference for the exception.
– أو نقوم برمي الـ exception وتحويله (exception propagation) إلى الدالة التي استدعت الدالة الحالية، حتى يصل الـ exception إلى آخر frame في الـ stack كما فعلنا في المثال الأخير.
برنامج الجافا في المثال الأخير سيطبع الـ stacktrace التالي:
1 2 3 4 |
Exception in thread "main" java.io.IOException: IOException in y() at Main.y(Main.java:17) at Main.x(Main.java:12) at Main.main(Main.java:7) |
وكقاعدة عامة في أي stacktrace:
– السطر الأول يتكون من ٣ معلومات مهمة: اسم الـ thread، ونوع الـ exception، ورسالة الخطأ (إن وجدت). في المثال السابق اسم الـthread كان main، ونوع الـ exception كان java.io.IOException، ورسالة الخطأ كانت ()IOExcpetion in y.
– السطر الثاني يكون دائماً مصدر الـ exception، وفي الكود يكون المصدر هو الـ constructor الخاص بالـ exception. في المثال السابق كان مصدر الخطأ في السطر ١٧ وهو ;(“()throw new IOException(“IOException in y.
– السطر الأخير يحتوي على اسم الـ method التي تكون مدخل الـ thread وبدايته. في المثال السابق، مدخل الـ thread هو الدالة main.
الـ stacktrace مع Caused by
أحياناً يكون خطأ ما سببه خطأ آخر، ولذلك يمكن في لغة الجافا ربط exception معين على أنه سبب لـ exception آخر عن طريق تمريره كـ parameter في الـ constructor أو عن طريق استخدام الدالة ()initCause على مرجع الـ exception الجديد. وربما الخطأ س سببه الخطأ ص، والخطأ ص سببه الخطأ ع .. وهكذا. الـ stacktrace الناتج سيحتوي على معلومات الـ exception الجديد وجميع الـexceptions المتسببة فيه مفصولةة فيما بينهم بالجملة Caused by. المثال التالي سيوضح ذلك:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import java.io.IOException; public class Main { public static void main(String[] args) throws Exception { x(); } public static void x() throws Exception { try { y(); } catch(IOException e) { // ex is caused by e Exception ex = new Exception("Exception in x()"); ex.initCause(e); throw ex; // or // throw new Exception("Exception in x()", e) } } public static void y() throws IOException { throw new IOException("IOException in y()"); } } |
والـ stacktrace الناتج يكون كالتالي:
1 2 3 4 5 6 7 |
Exception in thread "main" java.lang.Exception: Exception in x() at Main.x(Main.java:19) at Main.main(Main.java:7) Caused by: java.io.IOException: IOException in y() at Main.y(Main.java:30) at Main.x(Main.java:14) ... 1 more |
نفس القاعدة السابقة تنطبق على الـ stacktrace الحالي باستثناء أنه لا يكتب اسم الـ thread في الـ exception الموجود بعد Caused by. أيضاً يوجد اختلاف طفيف هنا وهو السطر الأخير. السطر المفقود هذا يمكنك الحصول عليه من الـ exception السابق له. المثال الآتي سيوضح كيف يمكنك الحصول على السطور المفقودة من الـ stacktrace:
لنفرض أنه لدينا الـ stacktrace التالي:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Exception in thread "main" ExceptionA at A5 at A4 at A3 at A2 at A1 Caused by: ExceptionB at B6 at B5 at B4 ... 3 more Caused by: ExceptionC at C8 at C7 at C6 ... 5 more |
كقاعدة عامة، إذا كان المفقود س من السطور في exception معين، نأخذ آخر س من السطور من الـ exception السابق له. وبالتالي الـstacktrace الكامل سيكون بالشكل التالي:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Exception in thread "main" ExceptionA at A5 at A4 at A3 at A2 at A1 Caused by: ExceptionB at B6 at B5 at B4 at A3 at A2 at A1 Caused by: ExceptionC at C8 at C7 at C6 at B5 at B4 at A3 at A2 at A1 |
الـ stacktrace مع javac options -g & -g:none
عند عمل compile بالأمر javac -g فإن الـ stacktrace يكون محتوياً على أرقام الأسطر كما في الأمثلة السابقة، بينما لو تم عملcompile بالأمر javac -g:none فإن الـ stacktrace للمثال الأخير سيكون كالتالي:
1 2 3 4 5 6 |
Exception in thread "main" java.lang.Exception: Exception in x() at Main.x(Unknown Source) at Main.main(Unknown Source) Caused by: java.io.IOException: IOException in y() at Main.y(Unknown Source) ... 2 more |
لاحظ الاختلاف!
الـ stacktrace بدون exception
يمكن في لغة الجافا الحصول على stacktrace من أي مكان في الكود وطباعته مباشرة، دون الحاجة إلى exception. لاحظ المثال التالي:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class Main { public static void main(String[] args) { x(); } public static void x() { y(); } public static void y() { Thread currentThread = Thread.currentThread(); StackTraceElement[] elements = currentThread.getStackTrace(); for(StackTraceElement element : elements) { System.out.println(element); } } } |
والناتج سيكون كالتالي:
1 2 3 4 |
java.lang.Thread.getStackTrace(Unknown Source) Main.y(Main.java:16) Main.x(Main.java:10) Main.main(Main.java:5) |
أداة البحث
التصنيفات
- أمن معلومات (1)
- برمجة (7)
أضف تعليقك