Giới thiệu
Trong lab này, chúng ta sẽ giới thiệu về GNU Debugger (gdb) và khám phá cách sử dụng nó để gỡ lỗi các chương trình C. gdb là một công cụ mạnh mẽ cho phép bạn kiểm tra trạng thái của một chương trình đang chạy, đặt điểm dừng và từng bước thực hiện code để xác định và khắc phục sự cố. Chúng ta sẽ bắt đầu bằng cách đảm bảo rằng gdb đã được cài đặt trên container Ubuntu 22.04 Docker của chúng ta, sau đó tạo một chương trình C đơn giản có lỗi runtime và sử dụng gdb để gỡ lỗi nó. Cuối cùng, chúng ta sẽ trình bày cách gỡ lỗi một chương trình C đa luồng bằng gdb.
Giới thiệu về gdb và Cách sử dụng
Trong bước này, chúng ta sẽ giới thiệu về GNU Debugger (gdb) và khám phá cách sử dụng nó để gỡ lỗi các chương trình C. gdb là một công cụ mạnh mẽ cho phép bạn kiểm tra trạng thái của một chương trình đang chạy, đặt điểm dừng và từng bước thực hiện code để xác định và khắc phục sự cố.
Đầu tiên, hãy đảm bảo rằng gdb đã được cài đặt trong container Ubuntu 22.04 Docker của chúng ta:
sudo apt-get update
sudo apt-get install -y gdb
Ví dụ đầu ra:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
libasan6 libubsan1 python3-gdb
Suggested packages:
gdb-multiarch
The following NEW packages will be installed:
gdb libasan6 libubsan1 python3-gdb
0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
Need to get 3,470 kB of archives.
After this operation, 13.5 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Bây giờ, hãy tạo một chương trình C đơn giản để gỡ lỗi. Tạo một file tên example.c
trong thư mục ~/project
với nội dung sau:
#include <stdio.h>
int main() {
int a = 5;
int b = 0;
int c = a / b;
printf("The result is: %d\n", c);
return 0;
}
Chương trình này sẽ cố ý chia cho zero, gây ra lỗi runtime.
Để gỡ lỗi chương trình này bằng gdb, hãy làm theo các bước sau:
- Biên dịch chương trình với flag
-g
để bao gồm các symbol gỡ lỗi:
gcc -g -o example example.c
- Khởi động trình gỡ lỗi gdb và load chương trình đã biên dịch:
gdb ./example
- Trong dấu nhắc gdb, đặt điểm dừng tại hàm
main
:
(gdb) break main
Breakpoint 1 at 0x11a6: file example.c, line 4.
- Chạy chương trình:
(gdb) run
Starting program: /home/labex/project/example
- Khi chương trình chạm đến điểm dừng, bạn có thể kiểm tra các biến, từng bước thực hiện code và gỡ lỗi sự cố:
Breakpoint 1, main () at example.c:4
4 int a = 5;
(gdb) print a
$1 = 5
(gdb) next
5 int b = 0;
(gdb) print b
$2 = 0
(gdb) next
6 int c = a / b;
Như bạn có thể thấy, khi chương trình cố gắng chia cho zero, nó sẽ kích hoạt một lỗi runtime có thể được bắt và gỡ lỗi bằng gdb.
Gỡ lỗi một chương trình C đơn giản với gdb
Trong bước này, chúng ta sẽ sử dụng gdb để gỡ lỗi một chương trình C đơn giản có lỗi runtime.
Đầu tiên, hãy tạo một file C mới tên simple.c
trong thư mục ~/project
với nội dung sau:
#include <stdio.h>
int main() {
int x = 10;
int y = 0;
int z = x / y;
printf("The result is: %d\n", z);
return 0;
}
Chương trình này sẽ cố ý chia cho zero, gây ra lỗi runtime.
Bây giờ, hãy biên dịch chương trình với flag -g
để bao gồm các symbol gỡ lỗi:
gcc -g -o simple simple.c
Tiếp theo, khởi động trình gỡ lỗi gdb và load chương trình đã biên dịch:
gdb ./simple
Trong dấu nhắc gdb, đặt điểm dừng tại hàm main
:
(gdb) break main
Breakpoint 1 at 0x11a6: file simple.c, line 4.
Bây giờ, chạy chương trình:
(gdb) run
Starting program: /home/labex/project/simple
Khi chương trình chạm đến điểm dừng, bạn có thể kiểm tra các biến, từng bước thực hiện code và gỡ lỗi sự cố:
Breakpoint 1, main () at simple.c:4
4 int x = 10;
(gdb) print x
$1 = 10
(gdb) next
5 int y = 0;
(gdb) print y
$2 = 0
(gdb) next
6 int z = x / y;
Như bạn có thể thấy, khi chương trình cố gắng chia cho zero, nó sẽ kích hoạt một lỗi runtime có thể được bắt và gỡ lỗi bằng gdb.
Gỡ lỗi một chương trình C đa luồng với gdb
Trong bước này, chúng ta sẽ sử dụng gdb để gỡ lỗi một chương trình C đa luồng có race condition.
Đầu tiên, hãy tạo một file C mới tên multithreaded.c
trong thư mục ~/project
với nội dung sau:
#include <stdio.h>
#include <pthread.h>
int shared_variable = 0;
void* thread_function(void* arg) {
for (int i = 0; i < 1000000; i++) {
shared_variable++;
}
return NULL;
}
int main() {
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, thread_function, NULL);
pthread_create(&thread2, NULL, thread_function, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Final value of shared_variable: %d\n", shared_variable);
return 0;
}
Chương trình này tạo hai thread, cả hai đều tăng một biến shared lên 1.000.000 lần. Do race condition, giá trị cuối cùng của biến shared có thể không phải là 2.000.000 như mong đợi.
Bây giờ, hãy biên dịch chương trình với flag -g
để bao gồm các symbol gỡ lỗi:
gcc -g -o multithreaded multithreaded.c -lpthread
Tiếp theo, khởi động trình gỡ lỗi gdb và load chương trình đã biên dịch:
gdb ./multithreaded
Trong dấu nhắc gdb, đặt điểm dừng ở đầu hàm thread_function
:
(gdb) break thread_function
Breakpoint 1 at 0x11b6: file multithreaded.c, line 7.
Bây giờ, chạy chương trình:
(gdb) run
Starting program: /home/labex/project/multithreaded
Khi chương trình chạm đến điểm dừng, bạn có thể kiểm tra các biến, từng bước thực hiện code và gỡ lỗi race condition:
Breakpoint 1, thread_function (arg=0x0) at multithreaded.c:7
7 for (int i = 0; i < 1000000; i++) {
(gdb) print shared_variable
$1 = 0
(gdb) step
8 shared_variable++;
(gdb) print shared_variable
$2 = 1
Bằng cách từng bước thực hiện code và kiểm tra biến shared, bạn có thể quan sát race condition và xác định sự cố trong chương trình đa luồng.
Tóm tắt
Trong lab này, chúng ta đã giới thiệu về GNU Debugger (gdb) và khám phá cách sử dụng nó để gỡ lỗi các chương trình C. Đầu tiên, chúng ta đảm bảo rằng gdb đã được cài đặt trên container Ubuntu 22.04 Docker, sau đó tạo một chương trình C đơn giản có lỗi runtime để gỡ lỗi. Chúng ta đã biên dịch chương trình với flag -g
để bao gồm các symbol gỡ lỗi, khởi động trình gỡ lỗi gdb, đặt điểm dừng tại hàm main
và chạy chương trình. Khi chương trình chạm đến điểm dừng, chúng ta có thể kiểm tra các biến, từng bước thực hiện code và gỡ lỗi sự cố.
Lab này bao gồm các kiến thức cơ bản về sử dụng gdb để gỡ lỗi một chương trình C đơn giản, cũng như gỡ lỗi một chương trình C đa luồng. Bằng cách làm theo các hướng dẫn từng bước, người học có được kinh nghiệm thực tế trong việc sử dụng công cụ gỡ lỗi mạnh mẽ này.