- প্রকাশিত
জিডিবি চিটশিট ব্যবহার করে সি প্রোগ্রাম ডিবাগ করা: পার্ট ৬ (৬টির মধ্যে) অ্যাডভান্সড সি টপিকস
- লেখক
- লেখক
- নাম
- মো: নাসিম শেখ
- টুইটার
- টুইটার
- @nasimStg
সি কোড লেখা অত্যন্ত ফলপ্রসূ হতে পারে, যা শক্তি এবং নিয়ন্ত্রণ সরবরাহ করে। তবে, দুর্দান্ত শক্তির সাথে আসে দুর্দান্ত দায়িত্ব... এবং অনিবার্যভাবে, বাগ! সেগমেন্টেশন ফল্ট, সূক্ষ্ম মেমরি লিকস, অফ-বাই-ওয়ান ত্রুটি এবং জটিল পয়েন্টার লজিক সি প্রোগ্রাম ডিবাগিংকে একটি চ্যালেঞ্জিং অনুসন্ধানে পরিণত করতে পারে। ভয় পাবেন না, কারণ ডেভেলপারদের একটি শক্তিশালী মিত্র আছে: GDB, জিএনইউ ডিবাগার। এই অপরিহার্য কমান্ড-লাইন টুলটি আপনাকে আপনার চলমান প্রোগ্রামের ভেতরে দেখতে, এর অবস্থা বুঝতে এবং নিয়মতান্ত্রিকভাবে সেই অধরা ত্রুটিগুলি খুঁজে বের করতে সহায়তা করে। এই গাইডটি আপনার সি প্রোগ্রামগুলিকে কার্যকরভাবে ডিবাগ করার জন্য প্রয়োজনীয় জিডিবি কমান্ড এবং কৌশলগুলির মাধ্যমে আপনাকে নিয়ে যাবে।
সুচিপত্র
কেন ডিবাগিং গুরুত্বপূর্ণ (বিশেষ করে সি-তে)
বেশি বিল্ট-ইন সুরক্ষা থাকা অন্যান্য ভাষার মতো নয়, সি আপনাকে মেমরিতে সরাসরি অ্যাক্সেস দেয়, যা শক্তিশালী তবে নিম্নলিখিত ত্রুটিগুলিরও ঝুঁকিপূর্ণ:
- সেগমেন্টেশন ফল্ট: মেমরি অ্যাক্সেস করা যেখানে আপনার উচিত নয় (যেমন, NULL বা ড্যাংলিং পয়েন্টার ডিরেফারেন্স করা, বাফার ওভারফ্লো)।
- মেমরি লিকস: বরাদ্দ করা মেমরি মুক্ত করতে ভুলে যাওয়া।
- পয়েন্টার ত্রুটি: ভুল পয়েন্টার অ্যারিথমেটিক বা ব্যবহার।
- লজিক ত্রুটি: প্রোগ্রামের অ্যালগরিদম বা ফ্লো কন্ট্রোলে ত্রুটি।
সাধারণ printf
স্টেটমেন্টগুলি আপনাকে কেবল কিছুটা সাহায্য করতে পারে। GDB-এর মতো একটি ডিবাগার আপনাকে প্রোগ্রাম চালানো স্থগিত করতে, যখন কিছু ভুল হয় তখন প্রোগ্রামের অবস্থা সঠিকভাবে পরিদর্শন করতে এবং ত্রুটির দিকে নিয়ে যাওয়া ঘটনাগুলির ক্রম বুঝতে দেয়।
ডিবাগিং এর জন্য আপনার কোড কম্পাইল করা
GDB কার্যকরভাবে ব্যবহার করার আগে, আপনাকে ডিবাগিং সিম্বল সহ আপনার সি প্রোগ্রাম কম্পাইল করতে হবে। এই সিম্বলগুলি কম্পাইল করা মেশিন কোডকে আপনার মূল সোর্স কোডের (ফাংশন নাম, ভ্যারিয়েবল নাম, লাইন নম্বর) সাথে সংযুক্ত করে।
আপনার কম্পাইলারে (GCC
-এর মতো) -g
ফ্ল্যাগ ব্যবহার করুন:
gcc -g my_program.c -o my_program
ডিবাগিংয়ের সময় কোন অপ্টিমাইজেশন (-O0
) বা কম অপ্টিমাইজেশন (-O1
) সহ কম্পাইল করার জন্যও অত্যন্ত সুপারিশ করা হয়। উচ্চ অপ্টিমাইজেশন স্তরগুলি (-O2
, -O3
) কোডকে উল্লেখযোগ্যভাবে পুনর্বিন্যস্ত করতে পারে, ফাংশন ইনলাইন করতে পারে এবং ভ্যারিয়েবলগুলি অপ্টিমাইজ করে সরাতে পারে, যা ডিবাগারে এক্সিকিউশন ফ্লো অনুসরণ করা কঠিন করে তোলে এবং সম্ভাব্যভাবে বাগগুলির মূল অবস্থানকে মাস্ক করতে পারে।
gcc -g -O0 my_program.c -o my_program # Ideal for debugging
GDB শুরু এবং বন্ধ করা
আপনার কম্পাইল করা প্রোগ্রাম ডিবাগ করা শুরু করতে, কেবল চালান:
gdb ./my_program
আপনাকে GDB প্রম্পট দিয়ে স্বাগত জানানো হবে: (gdb)
। সমস্ত কমান্ড এখানে প্রবেশ করা হয়।
যেকোনো সময় GDB থেকে বের হতে, quit
বা কেবল q
টাইপ করুন।
(gdb) quit
GDB এর ভিতরে আপনার প্রোগ্রাম চালানো
ডিবাগারের মধ্যে আপনার প্রোগ্রাম চালাতে, run
কমান্ড (বা এর সংক্ষিপ্ত রূপ r
) ব্যবহার করুন:
(gdb) run
আপনার প্রোগ্রামের জন্য কমান্ড-লাইন আর্গুমেন্ট প্রয়োজন হলে, run
-এর পরে সেগুলি সরবরাহ করুন:
(gdb) run argument1 "some argument with spaces" 123
এক্সিকিউশন নিয়ন্ত্রণ করা: ব্রেকপয়েন্ট
ব্রেকপয়েন্টগুলি GDB-কে বলে প্রোগ্রাম এক্সিকিউশন কোথায় থামবে।
ব্রেকপয়েন্ট সেট করা: break
কমান্ড (সংক্ষিপ্ত রূপ b
) ব্যবহার করুন। আপনি বিভিন্ন উপায়ে অবস্থান নির্দিষ্ট করতে পারেন:
_ ফাংশন নাম দ্বারা: break main
_ বর্তমান ফাইলের লাইন নম্বর দ্বারা: break 42
_ ফাইল এবং লাইন নম্বর দ্বারা: break my_other_file.c:100
_ ফাইল এবং ফাংশন নাম দ্বারা: break my_other_file.c:my_function
* ঠিকানা দ্বারা (কম ব্যবহৃত): break *0x4005a0
(gdb) b main
Breakpoint 1 at 0x1149: file my_program.c, line 5.
(gdb) b my_program.c:25
Breakpoint 2 at 0x11a0: file my_program.c, line 25.
ব্রেকপয়েন্ট তালিকাভুক্ত করা: সমস্ত সক্রিয় ব্রেকপয়েন্ট, তাদের নম্বর এবং অবস্থা দেখুন:
(gdb) info breakpoints
# Shorthand: i b
ব্রেকপয়েন্ট পরিচালনা করা:
_ একটি ব্রেকপয়েন্ট মুছে ফেলা: delete <breakpoint_number>
(যেমন, delete 1
) _ সমস্ত ব্রেকপয়েন্ট মুছে ফেলা: delete
_ একটি ব্রেকপয়েন্ট নিষ্ক্রিয় করা (এটি রাখা কিন্তু উপেক্ষা করা): disable <breakpoint_number>
_ একটি নিষ্ক্রিয় ব্রেকপয়েন্ট সক্রিয় করা: enable <breakpoint_number>
শর্তাধীন ব্রেকপয়েন্ট: শুধুমাত্র একটি নির্দিষ্ট শর্ত সত্য হলে বিরতি দিন। লুপ বা নির্দিষ্ট পরিস্থিতিতে ডিবাগ করার জন্য এটি অত্যন্ত দরকারী।
_ সরাসরি সেট করা: break <location> if <condition>
bash (gdb) break my_program.c:50 if i == 100
_ বিদ্যমান ব্রেকপয়েন্টে শর্ত যোগ করা: condition <breakpoint_number> <condition>
bash (gdb) b 65 Breakpoint 3 at 0x...: file my_program.c, line 65. (gdb) condition 3 count > 5 && strcmp(name, "test") == 0
* শর্ত সরানো: condition <breakpoint_number>
(কোন শর্ত নির্দিষ্ট না করে)
কোডের মাধ্যমে ধাপে ধাপে যাওয়া
একবার প্রোগ্রাম ব্রেকপয়েন্টে থামলে, আপনি লাইন বাই লাইন এটি সম্পাদন করতে পারেন:
_ next
(সংক্ষিপ্ত রূপ n
): বর্তমান লাইন সম্পাদন করুন। যদি লাইনে একটি ফাংশন কল থাকে, তবে সেই ফাংশনে প্রবেশ না করে সম্পূর্ণ ফাংশনটি সম্পাদন করুন (স্টেপ ওভার)। _ step
(সংক্ষিপ্ত রূপ s
): বর্তমান লাইন সম্পাদন করুন। যদি লাইনে একটি ফাংশন কল থাকে, তবে সেই ফাংশনে প্রবেশ করুন এবং এর প্রথম লাইনে থামুন। _ finish
: বর্তমানে চলমান ফাংশনটি রিটার্ন না করা পর্যন্ত এক্সিকিউশন চালিয়ে যান। আপনি যদি ভুলবশত এমন একটি ফাংশনে প্রবেশ করেন যা আপনার দরকার নেই, তবে এটি কার্যকর। _ continue
(সংক্ষিপ্ত রূপ c
): পরবর্তী ব্রেকপয়েন্টে পৌঁছানো, একটি সিগন্যাল ঘটা (যেমন ক্র্যাশ), অথবা প্রোগ্রাম শেষ না হওয়া পর্যন্ত স্বাভাবিক এক্সিকিউশন পুনরায় শুরু করুন। * until <line_number>
: বর্তমান স্ট্যাক ফ্রেমের মধ্যে এক্সিকিউশন চালিয়ে যান যতক্ষণ না নির্দিষ্ট লাইন নম্বরে পৌঁছানো যায় বা ফ্রেম থেকে বের হওয়া যায়। লুপ থেকে দ্রুত বেরিয়ে আসার জন্য কার্যকর।
কমান্ডগুলি পুনরাবৃত্তি করা: কোনো কমান্ড টাইপ না করে এন্টার চাপলে সাধারণত শেষ সম্পাদিত কমান্ডটি পুনরাবৃত্তি হয় (বিশেষ করে n
বা s
দিয়ে বারবার স্টেপ করার জন্য খুব কার্যকর)।
ডেটা এবং মেমরি পরিদর্শন করা
আপনার ভ্যারিয়েবলগুলির অবস্থা বোঝা ডিবাগিংয়ের মূল বিষয়।
মান প্রিন্ট করা:
_ print <expression>
(সংক্ষিপ্ত রূপ p
): একটি ভ্যারিয়েবল বা সি এক্সপ্রেশনের মান মূল্যায়ন করে এবং প্রিন্ট করে। bash (gdb) p my_variable $1 = 10 (gdb) p count _ 2 + offset $2 = 45 (gdb) p _pointer_var $3 = {name = 0x.... "Example", value = 123} (gdb) p array[i] $4 = 'X'
_ আপনি আউটপুট ফরম্যাট নির্দিষ্ট করতে পারেন: p/x my_variable
(হেক্স), p/t my_variable
(বাইনারি), p/d my_variable
(দশমিক), p/c my_variable
(ক্যারেক্টার)।
স্বয়ংক্রিয় ডিসপ্লে:
_ display <expression>
: প্রোগ্রাম প্রতিবার থামলে (যেমন, স্টেপ করার পর বা ব্রেকপয়েন্টে পৌঁছানোর পর) এক্সপ্রেশনের মান স্বয়ংক্রিয়ভাবে প্রিন্ট করে। bash (gdb) display i 1: i = 0 (gdb) display /x flags 2: flags = 0x1a
_ info display
: স্বয়ংক্রিয়ভাবে প্রদর্শিত এক্সপ্রেশনগুলি তালিকাভুক্ত করুন। * undisplay <display_number>
: স্বয়ংক্রিয়ভাবে একটি এক্সপ্রেশন প্রদর্শন বন্ধ করুন।
ভ্যারিয়েবল তথ্য:
_ info locals
: বর্তমান ফাংশনের স্ট্যাক ফ্রেমের সমস্ত লোকাল ভ্যারিয়েবলের নাম এবং মান দেখান। _ info args
: বর্তমান ফাংশনে পাস করা আর্গুমেন্টগুলির নাম এবং মান দেখান। * whatis <expression>
: একটি ভ্যারিয়েবল বা এক্সপ্রেশনের ডেটা টাইপ দেখান।
সরাসরি মেমরি পরীক্ষা করা: x
(examine) কমান্ড ব্যবহার করুন: x/<nfu> <address>
_ n
: পুনরাবৃত্তির সংখ্যা (কতগুলি ইউনিট প্রদর্শন করতে হবে)। ডিফল্ট ১। _ f
: ফরম্যাট ক্যারেক্টার (print
-এর মতো): x
(হেক্স), d
(দশমিক), u
(আনসাইনড দশমিক), o
(অক্টাল), t
(বাইনারি), a
(ঠিকানা), c
(ক্যারেক্টার), f
(ফ্লোট), s
(স্ট্রিং), i
(ইনস্ট্রাকশন)। * u
: ইউনিটের আকার: b
(বাই1ট), h
(হাফওয়ার্ড, ২ বাইট), w
(ওয়ার্ড, ৪ বাইট), g
(জায়ান্ট ওয়ার্ড, ৮ বাইট)।
(gdb) x/4xw $rsp # Examine 4 words (w) in hex (x) starting at stack pointer
0x7fffffffdc10: 0x00000001 0x00000000 0x7fffffffdcf8 0x00000000
(gdb) x/s buffer # Examine memory at address 'buffer' as a string (s)
0x404040: "Hello, GDB!"
(gdb) x/12cb &my_struct # Examine 12 bytes (b) as chars (c) at struct address
0x...: 72 'H' 101 'e' 108 'l' 108 'l' 111 'o' 0 '000' 123 '{' 0 '000'
কল স্ট্যাক নেভিগেট করা
যখন আপনার প্রোগ্রাম থামানো হয়, বিশেষ করে ক্র্যাশ করার পর বা নেস্টেড ফাংশন কলের ভেতরে, তখন আপনাকে জানতে হবে কীভাবে এটি সেখানে পৌঁছালো।
_ backtrace
(সংক্ষিপ্ত রূপ bt
): ফাংশন কল স্ট্যাক প্রিন্ট করে। ফ্রেম ০ হল বর্তমান ফাংশন, ফ্রেম ১ হল এটিকে কল করা ফাংশন, ইত্যাদি। bash (gdb) bt #0 my_function (arg=10) at my_program.c:55 #1 0x00005555555552a0 in helper_function (ptr=0x7fffffffdc10) at my_program.c:78 #2 0x00005555555551c0 in main (argc=1, argv=0x7fffffffdd08) at my_program.c:25
_ bt full
: প্রতিটি ফ্রেমের লোকাল ভ্যারিয়েবল সহ ব্যাকট্রেস দেখায়। _ frame <frame_number>
: একটি নির্দিষ্ট স্ট্যাক ফ্রেম নির্বাচন করুন। print
, info locals
, info args
-এর মতো কমান্ডগুলি তখন সেই ফ্রেমের কনটেক্সটে কাজ করবে। _ up <n>
: স্ট্যাকের n
ফ্রেম উপরে যান (main
-এর দিকে)। * down <n>
: স্ট্যাকের n
ফ্রেম নিচে যান (বর্তমানে চলমান ফাংশনের দিকে)।
আরও অ্যাডভান্সড GDB কৌশল
- ওয়াচপয়েন্ট: কোডে কোথায় ঘটছে তা নির্বিশেষে যখন কোনো এক্সপ্রেশনের মান পরিবর্তিত হয় তখন এক্সিকিউশন থামান। অপ্রত্যাশিত ভ্যারিয়েবল পরিবর্তনের উৎস খুঁজে বের করার জন্য এটি অত্যন্ত শক্তিশালী। *
watch <expression>
: যখন এক্সপ্রেশনে লেখা হয় এবং এর মান পরিবর্তিত হয় তখন বিরতি দিন। _rwatch <expression>
: যখন এক্সপ্রেশনটি পড়া হয় তখন বিরতি দিন। _awatch <expression>
: যখন এক্সপ্রেশনটি পড়া বা লেখা হয় তখন বিরতি দিন। _info watchpoints
: সক্রিয় ওয়াচপয়েন্টগুলি তালিকাভুক্ত করুন। _ দ্রষ্টব্য: হার্ডওয়্যার ওয়াচপয়েন্ট (দ্রুততর) সংখ্যায় সীমিত। যদি হার্ডওয়্যার রিসোর্স অতিক্রম করে বা এক্সপ্রেশনের প্রকারের জন্য সমর্থিত না হয় তবে GDB ধীর সফ্টওয়্যার ওয়াচপয়েন্টে ফিরে যেতে পারে। _ ক্যাচপয়েন্ট: C++ ব্যতিক্রম (catch throw
), লাইব্রেরি লোড করা (catch load
), বা সিস্টেম কল (catch syscall write
)-এর মতো নির্দিষ্ট ইভেন্টগুলিতে থামুন। _ চলমান প্রসেসের সাথে সংযুক্ত হওয়া: যদি কোনো প্রোগ্রাম ইতিমধ্যেই চলছে (এবং সম্ভবত আটকে আছে), আপনি পুনরায় চালু না করেই GDB কে সেটির সাথে সংযুক্ত করতে পারেন। 1.ps aux | grep my_program
বাpgrep my_program
ব্যবহার করে প্রসেস আইডি (PID) খুঁজুন। 2.gdb attach <pid>
চালান। 3. আপনার অনুমতির প্রয়োজন হতে পারে; কিছু লিনাক্স সিস্টেমে, আপনাকেptrace_scope
সামঞ্জস্য করতে হতে পারে (যেমন,echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
)। সাবধানে ব্যবহার করুন। _ কোর ডাম্প বিশ্লেষণ করা: যদি আপনার প্রোগ্রাম ক্র্যাশ করে এবং একটি "কোর ডাম্প" ফাইল তৈরি করে (যা ক্র্যাশের সময় প্রোগ্রামের মেমরির অবস্থার তথ্য ধারণ করে), আপনি ক্র্যাশের পর এটি বিশ্লেষণ করতে পারেন। 1. নিশ্চিত করুন যে কোর ডাম্প চালু আছে:ulimit -c unlimited
(প্রোগ্রাম চালানোর আগে আপনার শেলে)। 2. GDB চালান:gdb ./my_program core
(যেখানেcore
হল কোর ডাম্প ফাইল, প্রায়শই কেবলcore
নামে পরিচিত)। 3. ক্র্যাশের সময় অবস্থা পরীক্ষা করতেbt
,frame
,print
,info locals
,x
-এর মতো কমান্ড ব্যবহার করুন। আপনিrun
,next
,step
-এর মতো এক্সিকিউশন কমান্ড ব্যবহার করতে পারবেন না। _ টেক্সট ইউজার ইন্টারফেস (TUI): টার্মিনালে একটি স্প্লিট-স্ক্রিন ভিউ সরবরাহ করে, প্রায়শই সোর্স কোড, অ্যাসেম্বলি, রেজিস্টার বা কমান্ড হিস্টরি GDB প্রম্পটের পাশে দেখায়। _ শুরু করুন:gdb -tui ./my_program
_ TUI চালু/বন্ধ টগল করা:Ctrl+X A
_ লেআউটগুলি পরিবর্তন করা:Ctrl+X 2
(সোর্স/অ্যাসেম্বলি + কমান্ড দেখান),Ctrl+X 1
(শুধুমাত্র সোর্স/অ্যাসেম্বলি দেখান)। _ উইন্ডোগুলি নিয়ন্ত্রণ করতেlayout src
,layout asm
,layout regs
ব্যবহার করুন। ফোকাস সঠিক হলেCtrl+P
/Ctrl+N
বা অ্যারো কী ব্যবহার করে উইন্ডো স্ক্রোল করুন (মাঝে মাঝে ফোকাস পরিবর্তন করতেCtrl+X O
প্রয়োজন)। *.gdbinit
ফাইল: আপনার হোম ডিরেক্টরি বা প্রজেক্ট ডিরেক্টরিতে.gdbinit
নামে একটি ফাইল তৈরি করুন যেখানে কাস্টম কমান্ড বা সেটিংস সংরক্ষণ করা যেতে পারে যা GDB শুরু হওয়ার সময় স্বয়ংক্রিয়ভাবে চলে।
উপসংহার
GDB সি ডেভেলপমেন্টের জন্য একটি অত্যন্ত শক্তিশালী টুল। যদিও এর কমান্ড-লাইন ইন্টারফেস প্রাথমিকভাবে কঠিন মনে হতে পারে, ব্রেকপয়েন্ট সেট করা, এক্সিকিউশন ফ্লো নিয়ন্ত্রণ করা, ভ্যারিয়েবল এবং মেমরি পরিদর্শন করা, এবং কল স্ট্যাক বিশ্লেষণ করার মতো মৌলিক কমান্ডগুলি আয়ত্ত করলে আপনার বাগ খুঁজে বের করার এবং ঠিক করার ক্ষমতা নাটকীয়ভাবে উন্নত হবে। আপনি যখন আরও স্বাচ্ছন্দ্যবোধ করবেন, তখন কন্ডিশনাল ব্রেকপয়েন্ট, ওয়াচপয়েন্ট, কোর ডাম্প বিশ্লেষণ এবং TUI মোড অন্বেষণ করতে দ্বিধা করবেন না। কার্যকর ডিবাগিং একটি গুরুত্বপূর্ণ দক্ষতা, এবং GDB হল সি প্রোগ্রামিং ল্যান্ডস্কেপে আপনার অপরিহার্য সঙ্গী।
- সম্পর্কিত ধারণা: _ সি-তে পয়েন্টার: মেমরি ব্যবস্থাপনা বোঝা _ সি-তে ডাইনামিক মেমরি অ্যালোকেশন * সি প্রোগ্রামিংয়ে এড়াতে হবে এমন সাধারণ ভুলগুলি
- বাহ্যিক সম্পদ: _ GDB ডকুমেন্টেশন _ GDB কুইক রেফারেন্স কার্ড