The GDB debugger (GNU debugger) executes the program code line by line to detect errors that occur in the process. GDB allows you to change values of variables, set checkpoints and stop conditions of the running program.
With GDB, you can:
- set conditions that can affect the behavior of the program;
- start execution of the program with the specified conditions;
- specify the conditions under which the program execution will be stopped;
- explore the reasons for the program stop;
- change the program code to eliminate the effects of one error and continue detecting others.
Preparing the environment and starting the debugger
-
Install the software packages:
CentOS 7, AlmaLinux 9yum install -y billmanager-corporate-devel gdb gcc-c++
Ubuntu 20.04apt install -y billmanager-corporate-devel gdb gcc-c++
- Start BILLmanager.
-
Connect to the BILLmanager process:
gdb -p $(ps aux | grep billmgr | head -1 | awk '{print $2}')
Debugging symbols
Debugging is possible if the build is done with debugging symbols. For more information about project build, see the article Low-level interaction, C++ plug-ins.
To check for the presence of debugging symbols, execute the command:
objdump -x {PATH_TO_SO_FILE} | grep debug
If debugging symbols are present in the build, the result will be as follows:
[root@gdb-bill privatbank]# objdump -x /usr/local/mgr5/libexec/pmprivatbank.so | grep debug
26 .debug_aranges 000010d0 0000000000000000 0000000000000000 0001daf5 2**0
27 .debug_info 0002325b 0000000000000000 0000000000000000 0001ebc5 2**0
28 .debug_abbrev 00000e4c 0000000000000000 0000000000000000 00041e20 2**0
29 .debug_line 00002ae3 0000000000000000 0000000000000000 00042c6c 2**0
30 .debug_str 00034dad 0000000000000000 0000000000000000 0004574f 2**0
31 .debug_ranges 00001120 0000000000000000 0000000000000000 0007a4fc 2**0
0000000000000000 l d .debug_aranges 0000000000000000 .debug_aranges
0000000000000000 l d .debug_info 0000000000000000 .debug_info
0000000000000000 l d .debug_abbrev 0000000000000000 .debug_abbrev
0000000000000000 l d .debug_line 0000000000000000 .debug_line
0000000000000000 l d .debug_str 0000000000000000 .debug_str
0000000000000000 l d .debug_ranges 0000000000000000 .debug_ranges
[root@gdb-bill privatbank]#
The WITHOUT_DEBUG=yes environment variable is responsible for the absence of debugging symbols when building BILLmanager modules. For example, for the pmprivatbank module, when built without debugging symbols, the following result will be obtained:
[root@gdb-bill privatbank]# WITHOUT_DEBUG=yes make install
Create folder .build/.obj
Create folder .build/.dep
Compiling pmprivatbank.cpp
Create symbolic link to external libs
Create symbolic link for local libs
Building wrapper backend pmprivatbank
Create /usr/local/mgr5/libexec/pmprivatbank.so ...
Compiling privatbankpayment.cpp
Building wrapper backend privatbankpayment
Create /usr/local/mgr5/libexec/privatbankpayment.so ...
Compiling privatbankresult.cpp
Building wrapper backend privatbankresult
Create /usr/local/mgr5/libexec/privatbankresult.so ...
Shutdown billmgr ... done
[root@gdb-bill privatbank]# objdump -x /usr/local/mgr5/libexec/pmprivatbank.so | grep debug
[root@gdb-bill privatbank]#
Examples
Example of debugging a plug-in library
-
Create the directory /usr/local/mgr5/src/gdbtest/ with the following structure:
Directory structuresrc/gdbtest/ ├── gdbtest.cpp ├── Makefile └── xml └── billmgr_mod_gdbtest.xml 1 directory, 3 files
File contents src/gdbtest/xml/billmgr_mod_gdbtest.xml<?xml version="1.0" encoding="UTF-8"?> <mgrdata> <library name="libgdbtest"/> </mgrdata>
File contents src/gdbtest/gdbtest.cpp#include <api/action.h> #include <mgr/mgrlog.h> #include <mgr/mgraccess.h> MODULE("gdbtest"); using namespace isp_api; class aTest : public StdListAction { public: aTest() : StdListAction("atest", MinLevel(lvAdmin)) {} void List(Session& ses) const override { int a; } }; MODULE_INIT(aTest, "billmgr") { new aTest; }
[root@gdb-bill mgr5]# cat src/gdbtest/Makefile MGR=billmgr PLUGIN=gdbtest LIB += libgdbtest libgdbtest_SOURCES += gdbtest.cpp libgdbtest_LDADD = -lmgrdb -lispapi include ../isp.mk
-
Build and install the module with the command:
cd /usr/local/mgr5/src/gdbtest/ && make install
Result of command execution:[root@gdb-bill gdbtest]# cd /usr/local/mgr5/src/gdbtest/ && cp -f billmgr_mod_gdbtest.xml ../../etc/xml/ && make install DISTDIR=/usr/local/mgr5 Create folder .build/.obj Create folder .build/.dep Compiling gdbtest.cpp gdbtest.cpp: In member function ‘virtual void aTest::List(isp_api::Session&) const’: gdbtest.cpp:19:7: warning: unused variable ‘a’ [-Wunused-variable] int a; ^ Create symbolic link to external libs Create symbolic link for local libs Building library libgdbtest Create library ../../lib/libgdbtest.so Shutdown billmgr ... done
BILLmanager will be stopped when the module is being built.
-
Start BILLmanager.
cd /usr/local/mgr5/ && gdb -p $(ps aux | grep billmgr | head -1 | awk '{print $2}')
-
Connect to BILLmanager:
cd /usr/local/mgr5/ && gdb -p $(ps aux | grep billmgr | head -1 | awk '{print $2}')
If the connection is successful, the following output and a prompt to enter commands will be displayed (gdb):
Reading symbols from lib/Libgdbtest.so…done. Loaded symbols for lib/libgdbtest…so 0x00007fc9d087be9d in nanosleep () from /lib64/libpthread.so.0 Missing separate debuginfos, use: debuginfo-install coremanager-5.399.0-2307201251307784. el7.x86_64 (gdb)
-
Check that the debugging symbols for the module are loaded. To do this, run:
(gdb) list gdbtest.cpp:1
#include <api/action.h> #include <mgr/mgrlog.h> #include <mgr/mgraccess.h> MODULE("gdbtest"); using namespace isp_api;
- To debug the module:
-
Put a breakpoint at line 16:
(gdb) b gdbtest.cpp:16 Breakpoint 1 at 0x7f2bb1304330: file gdbtest.cpp, line 16.
Details -
Continue running BILLmanager:
(gdb) c Continuing.
-
Open the second terminal window and call the function from module:
cd /usr/local/mgr5/ && sbin/mgrctl -m billmgr atest
-
Return to the terminal window with the gdb debugger, where the program has reached a breakpoint:
Breakpoint 1, aTest::List (this=0x37264a0, ses=...) at gdbtest.cpp:17 17 } (gdb)
At the breakpoint, you can view the value of variables with the following command:
(gdb) p a $1 = 0
Details
-
Example of debugging the payment module
The pmprivatbank module provided in the billmanager-devel/billmanager-corporate-devel developer packages is used for the example.
You can adapt this example for the service processing modules you are developing.
-
Set dependencies:
yum -y install openssl-devel jsoncpp-devel
-
Build and install the module:
cd /usr/local/mgr5/src/examples/privatbank/ && make install
-
Run the gdb debugger with the executable file specified:
cd /usr/local/mgr5/ && gdb paymethods/pmprivatbank
Result of command execution:[root@gdb-bill mgr5]# cd /usr/local/mgr5/ && gdb paymethods/pmprivatbank GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7 Copyright (C) 2013 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-redhat-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /usr/local/mgr5/libexec/wrapper...(no debugging symbols found)...done. (gdb)
-
Set the breakpoint at the required location in the source code. If the response contains "Make breakpoint pending on future shared library load?", send "y".
(gdb) b pmprivatbank.cpp:18 No symbol table is loaded. Use the "file" command. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (pmprivatbank.cpp:18) pending.
Details -
Run the executable file with the required parameters:
(gdb) run --command features Starting program: /usr/local/mgr5/paymethods/pmprivatbank --command features [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". process 7807 is executing new program: /usr/local/mgr5/libexec/wrapper Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.x86_64 libgcc-4.8.5-44.el7.x86_64 libstdc++-4.8.5-44.el7.x86_64 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1".gdbrun --command featuresё Breakpoint 1, payment::PrivatBank::PrivatBank (this=0x6473a0) at pmprivatbank.cpp:18 18 feature_map[PAYMENT_FEATURE_REDIRECT] = true; Missing separate debuginfos, use: debuginfo-install coremanager-5.399.0-2307201251307784.el7.x86_64
If successful, GDB will stop at line 18 and wait for commands to be entered.
Example of debugging CGI components of the payment module
The pmprivatbank module provided in the billmanager-devel/billmanager-corporate-devel developer packages is used for the example.
CGI components may require additional environment variables for their operation:
- HTTP_COOKIE — contains various cookies, including cookies with the user session;
- QUERY_STRING — contains the query parameters.
-
Set dependencies:
yum -y install openssl-devel jsoncpp-devel
-
Build and install the module:
sh cd /usr/local/mgr5/src/examples/privatbank/ && make install
-
Run the gdb debugger specifying the executable file:
cd /usr/local/mgr5/ && gdb cgi/privatbankpayment
-
Specify the environment variables inside GDB required for the module to work:
(gdb) set env HTTP_COOKIE=billmgrses5=57f5aa8ce144c9e664b11508 (gdb) set env QUERY_STRING=elid=1
Details -
Set the breakpoint at the required location in the source code. If the response contains "Make breakpoint pending on future shared library load?", send "y".
(gdb) b privatbankpayment.cpp:29 No symbol table is loaded. Use the "file" command. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (privatbankpayment.cpp:29) pending.
Details -
Run the executable file:
(gdb) run Starting program: /usr/local/mgr5/cgi/privatbankpayment [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". process 14507 is executing new program: /usr/local/mgr5/libexec/wrapper [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". [New Thread 0x7fffed2cc700 (LWP 14511)] [Thread 0x7fffed2cc700 (LWP 14511) exited] Content-Type: text/html Set-Cookie: paymentcgises5=db19b8d7648b77196003677f; path=/mancgi/; HttpOnly; max-age=86400 Breakpoint 1, PrivatBankPayment::Process (this=0x646cc0) at privatbankpayment.cpp:29 29 string payment = "amt=" + Payment("amount") + "&ccy=" + Currency("iso") + "&details=" + Payment("description") + "&ext_details=" + Payment("number") + "&pay_way=privat24&order=" + Payment("id") + "&merchant=" + Method("merchid"); Missing separate debuginfos, use: debuginfo-install coremanager-5.399.0-2307201251307784.el7.x86_64
If successful, GDB will stop at line 29 and wait for commands to be entered. After the stop, you can view:
-
backtrace:
(gdb) bt #0 PrivatBankPayment::Process (this=0x646cc0) at privatbankpayment.cpp:29 #1 0x00007ffff698ad55 in payment::PaymentCgi::Execute(int, char**) () from /usr/local/mgr5/lib/libpaymentcgi.so #2 0x00007ffff6bc713e in ispmain (argc=1, argv=0x7fffffffe438) at privatbankpayment.cpp:65 #3 0x0000000000401af2 in main ()
-
listing:
(gdb) list 24 25 virtual void Process() { 26 string post_url = "https://api.privatbank.ua/p24api/ishop"; 27 28 // “amt=15.25&ccy=UAH&details=книга Будь здоров!&ext_details=1000BDN01&pay_way=privat24&order=000AB1502ZGH&merchant= 75482”, 29 string payment = "amt=" + Payment("amount") + "&ccy=" + Currency("iso") + "&details=" + Payment("description") + "&ext_details=" + Payment("number") + "&pay_way=privat24&order=" + Payment("id") + "&merchant=" + Method("merchid"); 30 string signature = str::hex::Encode(mgr_hash::sha1(str::hex::Encode(mgr_hash::md5(payment + Method("passwd"))))); 31 32 string description = str::Trim(Payment("description")); 33 if (description.empty())
- specific variable value:
(gdb) p post_url $1 = "https://api.privatbank.ua/p24api/ishop"
For more details, refer to the documentation GDB: The GNU Project Debugger.