توی این پست قراره سری به gdb بزنیم و کارهای پایهایش رو یاد بگیریم!
همون طور که احتمالا میدونید، gdb رو به عنوان gnu debugger هم میشناسن. در اصل این برنامه برای دیباگ برنامههایی که ما نوشتیم استفاده میشه و از زبانهای مختلفی پشتیبانی میکنه. توی این آموزش با زبان C کار رو پیش میبرم، ولی سعیم بر این هست که چیزایی که میگم اون قدر جزیی نباشن تا به درد سایر زبانها نخورن.
راستی همین اوّل، اگر میخواید ببینید gdb از چه زبانهایی پشتیبانی میکنه، یه راه دم دستیش اینه که توی ترمینالتون بزنید gdb تا برنامه اجرا بشه و بعد توی شل برنامه تایپ کنید set language و دو سه بار TAB رو فشار بدید تا یه لیست از زبانهایی که به طور پیشفرض پشتیبانی میکنه رو چاپ کنه!
خب برای شروع یه برنامه مینویسیم و کامپایلش میکنیم تا با gdb اجراش کنیم. فرض کنید همچین کدی رو توی فایلی به اسم code00.c مینویسم:
#include <stdio.h> int main() { char name[10]; printf("Enter your name: "); scanf("%s", name); printf("Salam %s\n", name); return 0; }
همون طور که میبینید کد سادهس؛ صرفا یک اسمی رو از ورودی میخونه و بهش سلام میکنه. کامپایل کردن برنامهها در حالت عادّی با دستوری مثل دستور زیر انجام میشه:
gcc code00.c -o exec00
امّا موقعی که قراره برنامهمون رو با gdb اجرا کنیم، یک فلگ به این دستور اضافه میشه و به شکل زیر باید برنامه رو کامپایل کنیم:
gcc -g code00.c -o exec00
کار این فلگ در واقع اضافه کردن یک سری نماد به فایل اجرایی هست تا gdb بتونه به خوبی با اون فایل اجرایی کار بکنه. حالا برای اجرای gdb کافیه توی ترمینال این دستور رو اجرا کنیم:
gdb exec00
حالا وارد محیط gdb شدیم! امّا چه طور برنامه رو اجرا و دیباگ کنیم؟!
خب! اوّلین دستوری که باهاش آشنا میشیم دستور run هست. این دستور خیلی ساده برنامهمون رو در محیط gdb اجرا میکنه. الآن اگر این دستور رو وارد کنیم، میبینیم برنامه منتظر میمونه تا ما اسممون رو وارد کنیم! اگر اسممون رو وارد کنیم و Enter بزنیم، برنامه بهمون سلام میکنه و کنترل محیط دوباره به دست پوستهی(شل) gdb برمیگرده.
(gdb) run Starting program: /home/rakeb/tmp/exec00 Enter your name: rakeb Salam rakeb [Inferior 1 (process 17536) exited normally] (gdb)
اگر چندین و چند بار دیگه دستور run رو وارد کنیم هم چنین اتّفاقی میافته.
احتمالا اوّلین انتظاری که از یک دیباگر داریم، این هست که اجازه بده توی نقاطی از کد، break point بذاریم تا وقتی روند اجرای برنامه به این نقاط رسید، متوقّف بشه و بتونیم مقدار متغیّرهای مختلف و... رو چک کنیم. این کار توی gdb به راحتی انجام میشه. break pointها رو میتونیم روی خط مشخّصی از سورس کد برنامه و یا روی اسم یک تابع قرار بدیم. مثلا فرض کنید میخوایم توی همین برنامهی بالا، ابتدای خطی که تابع scanf قرار داره یک break point قرار بدیم! به راحتی دستور زیر رو میزنیم:
(gdb) break code00.c:6
عدد ۶ به خاطر این هست که دستور scanf توی ۶امین خط از کد ما نوشته شده.
البته این روش بیشتر به درد پروژههای بزرگ میخوره که از بیش از یک فایل تشکیل شدن؛ این جا چون تنها فایل برنامهی ما code00.c هست، این دستور هم مثل دستور قبل عمل میکنه:
(gdb) break 6
حالا اگر دستور run رو وارد کنیم، چنین اتّفاقی میافته:
(gdb) run Starting program: /home/rakeb/tmp/exec00 Breakpoint 2, main () at code00.c:6 6 scanf("%s", name); (gdb)
همچین چیزی یعنی این که برنامه توی این خط از کد متوقّف شده. حالا یه سری سوال پیش میآد! چه جوری مقدار متغیّرها رو چک کنیم؟ چه طوری برنامه رو ادامه بدیم؟ و...
اوّل از همه باید گفت که به راحتی با دستور continue میتونیم اجرای برنامه رو از همون نقطهای که متوقّف شده بود ادامه بدیم:
(gdb) continue Continuing. Enter your name: Rakeb Salam Rakeb [Inferior 1 (process 17677) exited normally] (gdb)
امّا عجله نکنید! هنوز برای چاپ کردن مقدار متغیّرها زوده چون هنوز به متغیّری مقداری ندادیم که بخوایم چاپش کنیم. :دی
الآن میخوایم اجرای این دستوری که قبلش وایسادیم(scanf) تموم بشه تا بتونیم مقدار رشتهی name رو چاپ کنیم. این کار رو با دستور next انجام میدیم:
(gdb) run Starting program: /home/rakeb/tmp/exec00 Breakpoint 2, main () at code00.c:6 6 scanf("%s", name); (gdb) next Enter your name: Rakeb 7 printf("Salam %s\n", name); (gdb)
خب همون طوری که میبینید، دستور scanf اجرا میشه و برنامه سر دستور بعدی، یعنی printf دوباره متوقّف میشه.
این جا یه نکتهی کنکوری هم داشت؛ این که همون طور که میبینید break pointهامون بعد از اجرای برنامه پاک نمیشن و همچنان باقی میمونن.
حالا با دستور print به راحتی میتونیم مقدار خونههای آرایهی name رو چاپ کنیم!
(gdb) print name $1 = "Rakeb\000\377\177\000" (gdb)
میبینیم که این دستور مقادیر آرایهی name رو چاپ میکنه. اعدادی که قبلشون \ وجود داره هم در واقع در مبنای ۸ نوشته شدن. مثلا عدد داخل اندیس ۵ رشته، برابر با ۰ هست که نشاندهندهی انتهای رشتهس!
به عنوان آخرین درس از این مطلب، به معرّفی اختصاری دستور set برای تغییر مقدار متغیّرها میپردازیم. تصوّر کنید در ادامهی روند دیباگ، تصمیم میگیری که اسم به جای Rakeb چیز دیگری باشد، مثلا Arshia! در این حالت به راحتی مینویسیم:
(gdb) set name = "Arshia"
و مثلا در ادامه تصمیم میگیریم که حرف آخر Arshia هم بزرگ باشد. به طریق مشابه:
(gdb) set name[5] = 'A'
سپس با ادامهی برنامه، میبینیم که تغییرات ما به خوبی در برنامه اعمال شدهاند:
(gdb) continue Continuing. Salam ArshiA [Inferior 1 (process 18107) exited normally] (gdb)
در نهایت نیز برای خروج از gdb کافی است از دستور quit و یا Ctrl+d استفاده کنید!
در این مطلب تلاش شد تا محتوایی برای آشنایی مقدّماتی با gdb ارائه شود. در مطالب بعدی، تلاش بر این خواهد بود که دستورات کاربردی دیگری بیان شود و البته کاربردهای دیگری از این دستورات نیز به نمایش گذاشته شود.
موفّق باشید! D: