BILLmanager 6

GDB debugger

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

  1. Install the software packages:

    CentOS 7, AlmaLinux 9
    yum install -y billmanager-corporate-devel gdb gcc-c++
    Ubuntu 20.04
    apt install -y billmanager-corporate-devel gdb gcc-c++
  2. Start BILLmanager.
  3. 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
Details

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:

Example of a build with debugging symbols disabled
[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

The examples below are for CentOS 7. In other OS, the steps or outputs may be different.

Example of debugging a plug-in library

  1. Create the directory /usr/local/mgr5/src/gdbtest/ with the following structure:

    Directory structure
    src/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
  2. 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.

  3. Start BILLmanager.

    cd /usr/local/mgr5/ && gdb -p $(ps aux | grep billmgr | head -1 | awk '{print $2}')
    
  4. 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)
  5. 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;
  6. To debug the module:
    1. Put a breakpoint at line 16:

      (gdb) b gdbtest.cpp:16
      Breakpoint 1 at 0x7f2bb1304330: file gdbtest.cpp, line 16.
      Details
    2. Continue running BILLmanager:

      (gdb) c
      Continuing.
    3. Open the second terminal window and call the function from module:

      cd /usr/local/mgr5/ && sbin/mgrctl -m billmgr atest
    4. 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.

  1. Set dependencies:

    yum -y install openssl-devel jsoncpp-devel
  2. Build and install the module:

    cd /usr/local/mgr5/src/examples/privatbank/ && make install
  3. 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)
  4. 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
  5. 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.
  1. Set dependencies:

    yum -y install openssl-devel jsoncpp-devel
  2. Build and install the module:

    sh cd /usr/local/mgr5/src/examples/privatbank/ && make install
  3. Run the gdb debugger specifying the executable file:

    cd /usr/local/mgr5/ && gdb cgi/privatbankpayment
  4. 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
  5. 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
  6. 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.