ورود/ایجاد حساب کاربری
   منوی اصلی
· خانه
· لیست کاربران
· جستجو
· آمار مشاهدات
· آرشیو مقالات


- شرح
· راهنمای نویسندگان
· درباره ما

   همکاری با نشریه
در صورتی که مایل به همکاری با نشریه هستید، می‌توانید در لیست پستی نشریه عضو شده و در جریان امور قرار گیرید. برای اطلاعات بیشتر، اینجا کلیک کنید.

   کاربران
سردبیر
هیچ مدیر کمکی حاضر
همکاران
هیچ مدیر کمکی حاضر
اعضا:
جدیدترین:جدید امروز:0
جدیدترین:جدید دیروز:0
جدیدترین:مجموع:2471
جدیدترین:جدیدترین:
ufumenarayu
اعضا:حاضر
اعضا:اعضا:0
مهمان‌ها:مهمان‌ها:21
مجموع:مجموع:21
کاربران حاضر
هیچ کاربر حاضری وجود ندارد

   ورود کاربران




 


 برای ورود مشکل دارید؟
 ثبت نام کاربران جدید

آشنایی با GDB - قسمت اول

(1857 مجموع کلمات موجود در متن)
(4473 بار مطالعه شده است)  نسخه چاپی

۱. مقدمه

عصای دست برنامه‌نویسان Debugger ها هستند. قبل از پروژه گنو دیباگری به نام sdb وجود داشت که در یونیکس همه از آن استفاده می‌کردند.

برای پیشبرد اهداف گنو،ابتدا تصمیم گرفته شد تا یکسری ابزار‌های Development نوشته شود تا بتوان نرم‌افزار نوشت. زیرا برای ساخت یک بستر FreeSoftware ابزار توسعه پیش‌نیاز است.

GDB که مخفف GNU DeBugger است محصول این نرم‌افزار‌های Development می‌باشد.

این متن برای کار با GDB ،‌شروع بسیار خوبی می‌باشد.

۲. از کجا شروع کنیم؟

برای کار با GDB بهتر است تا یک برنامه دارای مشکل بنویسیم و کامپایل کنیم. بهتر است از تکه کد زیر برای کار با GDB استفاده کنیم:



#include <stdio.h>

#include <stdlib.h>

void filling_array(int array[]);

int main(void)

{

int array[200];

filling_array(array);

exit(EXIT_SUCCESS);

}

void filling_array(int array[])

{

int j;

for (j=0;j<10000;j++)

array[j]=j;

}



مثال شماره یک (test.c)

برای کامپایل کردن یک برنامه که بتوان آن را بعدا دیباگ کرد باید به هنگام کامپایل از گزینه g- استفاده کرد. به مثال زیر توجه کنید:

#gcc -g test.c -o test

گزینه o- به کامپایلر می‌گوید که نام فایل اجرایی نهایی چه باشد. زیرا به صورت پیش‌فرض نام آن را a.out قرار می‌دهد.

حال اگر برنامه را همانند خط زیر اجرا کنیم، دارای پیغام خطای مشهور زیر آن می‌شویم:

mohsen@Linux-Researcher:~/gdb$ ./test

Segmentation fault



این پیغام خطا در تمام موارد کار با حافظه رخ می‌دهد.

برای شروع کار با GDB باید از فرمت زیر استفاده کرد:



gdb programname [corefile]

programname همان نامه برنامه است که می‌خواهیم آن را دیباگ کنیم.که در اینجا همان فایل اجرایی test است.corefile ها که dump file نیز خوانده می‌شوند برای نگهداری یک سری مقدار که در برنامه شما استفاده می‌شود به کار می‌روند. اگر از corefile ها استفاده کنید، کار دیباگ شما آسان‌تر می‌شود.

زمانی باید یک corefile ساخت که برنامه مذکور در حال اجرا باشد. اما برنامه‌ای که همانند مثال فوق کراش کند را نمی‌توان برایش corefile ساخت. راه حلی که خود GDB بدین منظور ارایه می‌کند، دستور generate-core-file می‌باشد که درون خود GDB اجرا می‌شود.سپس با دستور core می‌توان آن corefile را لود کرد:

mohsen@Linux-Researcher:~/gdb$ gdb test

GNU gdb 6.4-debian

Copyright 2005 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB. Type "show warranty" for details.

This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/libthread_db.so.1".



(gdb) run

Starting program: /home/mohsen/gdb/test



Program received signal SIGSEGV, Segmentation fault.

0x080483aa in filling_array (array=0xbfbb84b8) at test.c:16

16 array[j]=j;

(gdb) generate-core-file

Saved corefile core.20764

(gdb) core core.20764

A program is being debugged already. Kill it? (y or n) y

Failed to read a valid object file image from memory.

Core was generated by `/home/mohsen/gdb/test'.

Program terminated with signal 11, Segmentation fault.

Symbols already loaded for /lib/tls/libc.so.6

Symbols already loaded for /lib/ld-linux.so.2

#0 0x080483aa in filling_array (array=0xbfbb84b8) at test.c:16

16 array[j]=j;

(gdb)

ایجاد و استفاده از یک corefile

دیدید که در ابتدا با دستور run برنامه مذکور اجرا می‌شود و سپس شروع به دیباگ می‌شود. مخصوصا در این مثال که برای ساخت corefile حتما باید برنامه در حال اجرا باشد و سپس با دستور generate-core-file یک corefile ساخته و با دستور core آن corefile لود می‌شود.

همه چیز بعد از لود اولیه برنامه بعد از لود corefile شروع می‌شود.

۳. دسترسی به داده و امتحان آنها

دستور backtrace یک درخت از stack برنامه نمایش می‌دهد.البته می‌توانید به جای این دستور، از دستور bt استفاده کنید:

(gdb) bt

#0 0x080483aa in filling_array (array=0xbfbb84b8) at test.c:16

#1 0x08048381 in main () at test.c:9

(gdb)



مشاهده Stack برنامه

دستور list سورس برنامه را نشان می‌دهد. ۲ گونه طرز استفاده از این دستور بسیار رایج است:

list n1,n2

list function_name

در گونه اول شما دو عدد به آن پاس می‌کنید که یک محدوده از خطوطی است که می‌خواهید برایتان چاپ شود. به این نکته توجه داشته باشید که شماره خطوط از یک شروع می‌شود.

در گونه دوم می‌توانید اسم یک تابع را به آن پاس نمایید که برایتان چاپ نماید.

اگر پارامتری به آن پاس نشود، تابعی که در حال حاضر در آن است چاپ می‌شود.

(gdb) list 1,17

1 #include <stdio.h>

2 #include <stdlib.h>

3

4 void filling_array(int array[]);

5

6 int main(void)

7 {

8 int array[200];

9 filling_array(array);

10 exit(EXIT_SUCCESS);

11 }

12 void filling_array(int array[])

13 {

14 int j;

15 for (j=0;j<10000;j++)

16 array[j]=j;

17 }

(gdb) list filling_array

8 int array[200];

9 filling_array(array);

10 exit(EXIT_SUCCESS);

11 }

12 void filling_array(int array[])

13 {

14 int j;

15 for (j=0;j<10000;j++)

16 array[j]=j;

17 }

(gdb)

اگر به دو دستور list بالا نگاه کنید در ابتدا با دو پارامتر ۱و۱۷ به کار گرفته شده است که خطوط ۱ تا ۱۷ را نمایش می‌دهد.

در مورد دوم اسم تابع filling_array به آن پاس داده شده است که فقط آن تابع را نمایش می‌دهد.

به یاد داشته باشید که قسمت main یک برنامه در ++C/C یک تابع با نام main است. و شما می‌توانید آن را به عنوان پارامتر به دستور list پاس کنید.

دستور print محتوای یک متغیر و یا محتوای چیزی را که یک تابع برمی‌گرداند را نمایش می‌دهد:

(gdb) print j

$9 = 2770

(gdb) print $9-1

$10 = 2769

(gdb)



در بالا محتویات متغیر j را نمایش داده‌ایم حتی متغیری که از هر عمل ایجاد می‌شود و با عدد نامگذاری می‌شود را نیز دستکاری کردیم.

برای نمایش آدرس خانه‌های یک آرایه می‌توانیم از دستور زیر استفاده کنیم:

(gdb) print array@10

$29 = {0xbfbb84b8, 0xb7facf92, 0xb7f8c000, 0x1754f, 0xb7fbbff4, 0xbfbb85ec, 0x0, 0x1, 0x2, 0x3}

(gdb)



در واقع با این دستور ۱۰ آدرس ۱۰ خانه اول آرایه array به نمایش درمی‌آید.

برای دیدن محتویات یک آرایه از دستور زیر استفاده می‌کنیم:

(gdb) print array[1]@10

$30 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}



در واقع با دستور بالا، محتویات ۱۰ خانه از شماره یک آرایه array را چاپ کردیم. اگر عدد یک داخل کروشه هر عددی شود،‌ همان خانه شروع برای چاپ محتویات به تعداد عدد بعد از @ می‌باشد. برای دیدن Data Type هر تابع یا متغیر می‌توان از دستور whatis استفاده کرد:

(gdb) whatis j

type = int

(gdb) whatis main

type = int (void)

(gdb) whatis filling_array

type = void (int *)

(gdb) whatis array

type = int *

(gdb)



با دستور whatis در بالا Data Type های متغیر j، تابع main، تابع filling_array و آرایه array را چاپ کردیم.

نکته برنامه نویسی:

استاندارد ANSI می‌گوید که تابع main باید دارای نوعی به غیر از void باشد و اگر برنامه‌نویس نوعی را برای آن در نظر نگیرد، کامپایلر به صورت پیش‌فرض آن را از نوع int حساب می‌کند.

۴- تنظیمات Breakpoint ها

برای ایجاد یک breakpoint در برنامه باید از دستور break با یکی از گرامرهای زیر استفاده کرد:

break linnumber [if expr]

break funcname [if expr]

break file:linenumber

break file:funcname

break *ADDRESS

بهتر است در ابتدا دستور info breakponits را یاد بگیریم، زیرا در هر لحظه نیاز داریم بدانیم تا چه breakpoint هایی در دست اعمال می‌باشند.

کار با breakpoint ها را می‌توان از دستورات زیر شروع کرد:

(gdb) break 16 if j == 199

Breakpoint 1 at 0x804839c: file test.c, line 16.

(gdb) info breakpoints

Num Type Disp Enb Address What

1 breakpoint keep y 0x0804839c in filling_array at test.c:16

stop only if j == 199

(gdb) run

Starting program: /home/mohsen/gdb/test



Breakpoint 1, filling_array (array=0xbfad2b98) at test.c:16

16 for (j=0;j<10000;j++)

(gdb)



در بالا ۳ دستور وارد کرده‌ایم:

ابتدا با break 16 if j == 199 به gdb گفته‌ایم که در خط ۱۶ اگر متغیر j به ۱۹۹ رسید اجرای برنامه را قطع کن.

سپس با دستور info breakpoints اطلاعاتی راجع‌به breakpoint های فعال کسب می‌کنیم.

و در آخر با دستور run دوباره برنامه را اجرا می‌کنیم و دیگر هیچ خطایی مشاهده نمی‌گردد.زیرا نمی‌گذاریم که مقدار آرایه کمتر از تعداد نسبت‌دهی ما برای خانه‌هایش شود.

اگر حالا با وجود ۳ دستور بالا مقدار متغیر j را ببینیم، مقدار زیر به نمایش در خواهد آمد:

(gdb) print j

$1 = 199

(gdb)



دیدید که سر ۱۹۹ برنامه break خورد.

با دستور disable می‌توان هر breakpoint را به صورت inactive درآورد. بدین منظور به سری دستورات زیر نگاه کنید:

(gdb) disable 1

(gdb) info breakpoints

Num Type Disp Enb Address What

1 breakpoint keep n 0x0804839c in filling_array at test.c:16

stop only if j == 199

breakpoint already hit 1 time

(gdb) run

The program being debugged has been started already.

Start it from the beginning? (y or n) y

Starting program: /home/mohsen/gdb/test



Program received signal SIGSEGV, Segmentation fault.

0x080483aa in filling_array (array=0xbfbba9f8) at test.c:16

16 for (j=0;j<10000;j++)

در ابتدا breakpoint شماره یک را با دستور disable به صورت غیر فعال درآوردیم و سپس با دستور info breakpoints اطلاعاتی راجع‌به آنها اخذ کردیم.اگر گزینه بعد از Disp را نگاه کنید،‌ Enb که مخفف Enabling است فیلدی دارای محتویات n به معنی No می‌باشد.یعنی این breakpoint غیر فعال است و سپس با دستور run دیدیم که خطا را می‌توان دید.(یعنی Breakpoint اعمال نشده است)

برای عکس دستور disbale دستور enable وجود دارد.

برای پاک کردن هر breakpoint دستور delete وجود دارد:

(gdb) delete 1

(gdb) info breakpoints

No breakpoints or watchpoints.

(gdb)



می‌بینید که با پاک‌کردن تنها breakpoint ،دیگر هیچ breakpoint ی وجود ندارد.




نویسنده : محسن پهلوان‌زاده mohsen@pahlevanzadeh.org

PDF Version


تمامی مطالب و مقالات این سایت تحت مجوز GNU FDL قرار دارند. بنابراین کپی و ایجاد تغییر در آنها مطابق شرایط این مجوز آزاد می‌باشد.