آشنایی با 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
|