جستجو در تالارهای گفتگو
در حال نمایش نتایج برای برچسب های 'دیباگ'.
1 نتیجه پیدا شد
-
بارها بوده که برنامهای را نوشتهایم، روند کامپایل و اجرا به خوبی و خوشی انجام میشود. امّا در مرحلهٔ اجرای برنامه، خروجیهای نامناسبی پدیدار میشود. که متأسفانه چیزی نیستند که ما میخواهیم. خب برای حل این مشکل دو راه پیشرو میباشد : بازبینی کد و انجام تست برای یافتن محل مشکل. استفاده از ابزارهای خطایابی (Debugging). بازبینی کد و انجام تست برای یافتن محل مشکل برای اینکار فریورکها و کتابخانههای زیادی موجود میباشد، مثلاً کتابخانهٔ تستنویسی (Test Case) به اسم Catch2. کار با این کتاخانه بسیار آسان است. برای مثال تابع زیر را در نظر داشتهباشید : unsigned int Factorial( unsigned int number ) { return number <= 1 ? number : Factorial(number-1)*number; } ما میخواهیم بدانیم که آیا این تابع خروجی مناسب را دارد یا خیر، درصورتیکه از catch2 استفاده میکنید، کافیست که طبق راهنمای README.md هدرفایل catch.cpp را دریافت و به برنامهٔ خود اضافه کنید : #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" unsigned int Factorial( unsigned int number ) { return number <= 1 ? number : Factorial(number-1)*number; } TEST_CASE( "Factorials are computed", "[factorial]" ) { REQUIRE( Factorial(1) == 1 ); REQUIRE( Factorial(2) == 2 ); REQUIRE( Factorial(3) == 6 ); REQUIRE( Factorial(10) == 3628800 ); } و بهصورت نمونهٔ کد بالا از catch2 استفاده میکنیم. طبق اسناد با تعریف ماکروی CATCH_CONFIG_MAIN ما یک تابع main توسط خود catch2 تعریف میکنیم. و کافیه که فقط این سورس را کامپایل و اجرا کنیم : $> g++ -o catch2Test main.cpp $> $> ./catch2Test =============================================================================== All tests passed (4 assertions in 1 test case) و خب مسلماً درصورتیکه خطایی باشد در این آزمایشات معلوم میگردد، فریمورکهای دیگری مانند Google Test نیز موجود میباشد. استفاده از ابزارهای خطایابی (Debugging) در این روش شما برنامهٔ خود را تحت برنامهٔ دیگری اجرا و اقدام به خطایابی میکنید، یکی از برنامههای خطایابی معروف و آزاد، برنامهٔ GNU Debugger میباشد. از این برنامه برای خطایابی در زبانهای : Ada Assembly C ++C D Fortran Go Objective-C OpenCL Modula-2 Pascal Rust استفاده میشود. برای نصب میتوانید از مدیربستهٔ سیستمعامل خود استفاده کنید : $> apt install gdb gdb-doc و یا اینکه از این پیوند برنامه را دریافت و اقدام به کامپایل/نصب آن کنید. نکته : دقت کنید که همیشه نباید حتماً خطایی در برنامه باشد تا اقدام به خطایابی کنیم، گاهی هم نیاز است که روند کار برنامه را به این روش با استفاده از ابزارهای مشابه پیگیری کنیم. (In fact: reverse engineering) حال اقدام به Debug کردن این برنامهٔ کوتاه مینماییم : #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> static int data = 0x02A; int main (void){ int stack = 0x2FF; pid_t pid; switch (pid = fork()){ case -1: perror("pid = fork()"); exit(EXIT_FAILURE); case 0: data |= 0x02B; stack &= data; break; } fprintf(stdout, "[%s]\v [PID=%ld] [data=%d] [stack=%d].\n", (pid == 0) ? "child" : "parent", (long) getpid(), data, stack); exit(EXIT_SUCCESS); } قبلاز اینکه برنامهای را برای دیباگ کردن داخل GDB استفاده کنیم، نیاز است که برای کمک به این روند اطلاعاتی مانند مکان Source Code برنامه را به فایل باینری خود اضافه کنیم. برای اینکار کافیست که از فلگ -g یا -ggdb یا -g3 استفاده کنیم. این پیوند را برای اطلاعات بیشتر مطالعه کنید. به اینصورت برنامه را کامپایل و برای دیباگ کردن آماده میکنیم : $> gcc -o output -ggdb main.c توجه کنید که سورس برنامه را از مکانش تغییر ندهید. حال برنامهٔ gdb را با نوشتن اسم gdb در shell فراخوانی میکنیم : $> gdb GNU gdb (Debian 8.2.1-2) 8.2.1 Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word". (gdb) برای اینکه فایل باینری را باز کنیم باید از دستور file استفاده کنیم. همچنین میتوانیم به اینصورت نیز برنامهٔ gdb را به همراه فایل باینری خود اجرا نماییم : $> gdb output خب، در این قسمت میتوانیم با استفاده از دستور run برنامهٔ خودمان را اجرا کنیم و درصورتیکه نیاز باشد آرگومانهایی را نیز ارسال کنیم. وقتی دستور run را وارد میکنیم برنامهٔ ما اجرا میشود و خاتمه میابد. زمانیکه نیاز داریم روند اجرای برنامه در نقطههای مشخصی متوقف شود باید از Break Point استفاده کنیم. برای قرار دادن Break Point در خطهای مختلف برنامه از دستور break به اضافهٔ شمارهٔ خط سورس برنامه استفاده میکنیم. برای اینکار بد نیست که اطلاعاتی نیز درمورد سورسکد خود داشتهباشیم مثلاً همزمان بتوانیم سورس را نیز مشاهده کنیم، درصورتیکه از ادیتور Emacs استفاده میکنید میتوانید M+x را زده و gdb را اجرا کنید. که یک بافر در سمت چپ برای شما باز میکند (درصورتیکه دکمههای Ctrl + X و Ctrl + 3 را زده باشید). در غیر اینصورت به دو روش دیگر میتوانید سورس خود را هنگام دیباگ مشاهده کنید، در روش اوّل استفاده از دستور list به اضافهٔ دو آرگومان میباشد : آرگومان اوّل مشخص کنندهٔ خط شروع است، و آرگومان دوّم مشخص کننده خط پایان هست. مثلاً با فراخوانی list 2, 10 از خط دو تا ده را نمایش میدهد : (gdb) list 2, 10 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <unistd.h> 5 6 static int data = 0x02A; 7 8 int 9 main (void){ 10 int stack = 0x2FF; میتوانید فقط یک آرگومان ارسال کنید، که به اندازه ی مقدار متغیر listsize که پیشفرض ده میباشد (میتوانید با استفاده از دستور set مقدار را تغییر دهید) را نمایش میدهد : (gdb) list 2 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <unistd.h> 5 6 static int data = 0x02A; 7 8 int 9 main (void){ 10 int stack = 0x2FF; حالات دیگهای هم دارد که میتوانید با وارد کردن help list متوجه شوید. امّا روش بهتری (روش دوّم) نیز برای دیدن سورس برنامه به همراه دیباگ کردن وجود دارد، میتوانید از رابط TUI استفاده کنید. برای استفاده از این رابط دکمههای Ctrl + X و Ctrl + A یا < وارد کرده و ReturnKey (یا همان Enter) را بزنید. در این قسمت شما به راحتی میتوانید هم سورس برنامهٔ خودتان را ببینید و هم برنامه را دیباگ کنید. با یکبار اجرا کردن برنامه توسط دستور run سورس برنامهٔ شما بارگذاری میشود. برویم به سراغ Break Point گذاشتن، برای مثال ما میخواهیم یک Break Point بر سر خط ۱۰ و ۱۳ بگذاریم : (gdb) break 11 Breakpoint 1 at 0x555555555185: file main.c, line 13. (gdb) break 10 Breakpoint 2 at 0x55555555517e: file main.c, line 10. (gdb) دوباره برنامه را با وارد کردن دستور run اجرا میکنیم، تا اجرای برنامه اینبار در برخورد با اوّلین Break Point متوقف شود : در این توقف، ما میتوانیم با استفاده از دستور print محتویات متغیرهارا مشاهده کنیم : (gdb) print data $1 = 42 (gdb) print stack $2 = 21845 دستور print قابلیتهای جالبی دارد : (gdb) print 12 / 2 $1 = 6 (gdb) print sizeof(int) $2 = 4 (gdb) print &data $3 = (int *) 0x4a60f0 <data> (gdb) مقادیری که به ترتیب در سمت چپ شمارهگذاری شدهاند درواقع اسم متغیرهایی هستنند که خروجی در آن قرار گرفته است : (gdb) print $1 $4 = 42 (gdb) print $4 $5 = 42 (gdb) همچنین با استفاده از دستور delete میتوانیم یک Break Point را حذف کنیم. برای ادامه دادن به روند اجرای برنامه تا Break Point بعدی از دستور continue و برای رفتن به خط بعدی از دستور next استفاده میکنیم.بعد از اجرای دستور next دقت کنید سریعاً به خط 23 رفته و فراخوانی تابع سیستمی fork() را رها میکند. به خاطر اینکه دستور next کاری به توابعی که فراخوانی کردهاید، در اینجا فراخوان سیستمی fork() ، ندارد و دستورات سورس شما را ادامه میدهد؛ امّا درصورتیکه از step یا stepi استفاده بکنید وارد دستورات شده و دستورات توابع شما را پیمایش میکند: (gdb) next [child] [PID=563] [data=43] [stack=43]. [Detaching after fork from child process 563] (gdb) نکته : رابط TUI زیاد قوی نمیباشد، لذا درصورتیکه خروجی چاپ شود تنظیمات صفحه نمایش را به هم میزد میتوانید با زدن Ctrl + L خروجیهای اضافه را از بین ببرید. برای دیباگ کردن یک fork() میتوانید مقدار follow-fork-mode را ویرایش کنید : (gdb) set follow-fork-mode child (gdb) run Starting program: /tmp/output Breakpoint 2, main () at main.c:10 (gdb) next Breakpoint 1, main () at main.c:13 (gdb) step [Attaching after process 2387 fork to child process 2403] [New inferior 2 (process 2403)] [Detaching after fork from parent process 2387] [Inferior 1 (process 2387) detached] [Switching to process 2403] main () at main.c:13 (gdb) همچنین با استفاده از دستور disassemble میتوانید سورس اسمبلی یکی تابع را مشاهده کنید : (gdb) disassmble main Dump of assembler code for function main: 0x0000000000401b4d <+0>: push %rbp 0x0000000000401b4e <+1>: mov %rsp,%rbp 0x0000000000401b51 <+4>: push %rbx 0x0000000000401b52 <+5>: sub $0x18,%rsp => 0x0000000000401b56 <+9>: movl $0x2ff,-0x14(%rbp) 0x0000000000401b5d <+16>: callq 0x43c9b0 <fork> 0x0000000000401b62 <+21>: mov %eax,-0x18(%rbp) 0x0000000000401b65 <+24>: cmpl $0xffffffff,-0x18(%rbp) 0x0000000000401b69 <+28>: je 0x401b73 <main+38> 0x0000000000401b6b <+30>: cmpl $0x0,-0x18(%rbp) 0x0000000000401b6f <+34>: je 0x401b89 <main+60> 0x0000000000401b71 <+36>: jmp 0x401ba2 <main+85> 0x0000000000401b73 <+38>: lea 0x7c48e(%rip),%rdi # 0x47e008 0x0000000000401b7a <+45>: callq 0x408bd0 <perror> 0x0000000000401b7f <+50>: mov $0x1,%edi 0x0000000000401b84 <+55>: callq 0x408010 <exit> 0x0000000000401b89 <+60>: mov 0xa4561(%rip),%eax # 0x4a60f0 <da --Type <RET> for more, q to quit, c to continue without paging-- لینک منبع را برای ادامهٔ داستان دنبال کنید :). موفق و پیروز باشید.?