Gem #122: Breakpoint Commands — Part 2

by Jerome Guitton —AdaCore

Let’s get started…

We previously considered a bank that was managing only one account.Now suppose that this example has been extended to supportseveral accounts and transactions between these accounts:

package Accounts is   type Customer is (Alice, Bob);   procedure Open_Account (C : Customer; Cash : Integer);   procedure Pay (From, To : Customer; Cash : Integer);end Accounts;

To check that no accounts are opened twice and that transactions onlyhappen on opened accounts, we need to keep a record of the accountsthat have been opened already. When we only had one account, the statewas a simple Boolean variable; now it is a list of accounts, which ismore complicated to express with only convenience variables. ThePython interface of GDB can be used to bypass this limitation.

As a first step, let’s describe the whole program. It containsa main procedure that opens two accounts and schedules a set oftransactions between them:

with Accounts; use Accounts;procedure P isbegin   Open_Account (Alice, 50);   Open_Account (Bob, 40);   Pay (From => Bob,   To => Alice, Cash => 45);   Pay (From => Alice, To => Bob,   Cash => 20);   Pay (From => Bob,   To => Alice, Cash => 45);   Pay (From => Alice, To => Bob,   Cash => 20);end P;

As for the body of Accounts, it offers no difficulty. Oddly enough, ithides a transaction in its elaboration:

package body Accounts is   type Account is limited record      Balance : Integer;   end record;   Bank : array (Customer) of Account;   procedure Open_Account (C : Customer; Cash : Integer) is   begin      Bank (C).Balance := Cash;   end Open_Account;   procedure Pay (From, To : Customer; Cash : Integer) is   begin      Bank (To).Balance := Bank (To).Balance + Cash;      Bank (From).Balance := Bank (From).Balance - Cash;   end Pay;   --  At elaboration time, a suspicious operation is scheduled;   --  Alice secretly pays a bribe to Bob:begin   Pay (From => Alice, To => Bob, Cash => 20);end Accounts;

We will now implement, in Python, two hooks that should be executedwhenever an operation on an account is scheduled. Create a new filenamed hooks.py with the following code:

current_customers = []def open_account_hook():    global current_customers    c = str(gdb.parse_and_eval('c'))    if c in current_customers:        print "error: account initialized twice"    else:        print "(info) %s opens an account" % c        current_customers.append(c)        gdb.execute("continue")def pay_hook():    global current_customers    f = str(gdb.parse_and_eval('from'))    t = str(gdb.parse_and_eval('to'))    if not f in current_customers:        print "error: %s tries to pay before opening an account" % f    elif not t in current_customers:        print "error: %s cannot be paid before opening an account" % t    else:        cash = str(gdb.parse_and_eval('cash'))        print "(info) %s gives %s $%s" % (f, t, cash)        gdb.execute("continue")

This defines three entities: a global list that records the accountsthat have been opened already, and two hooks to be executed whenOpen_Account and Pay are called.

Both get the value of the parameters using the functiongdb.parse_and_eval (defined in the Python API) to check that the operation isvalid. The documentation of this function, as well as the documentationof any Python entities, can be accessed from GDB using Python’s command-linehelp:

(gdb) python help(gdb.parse_and_eval)Help on built-in function parse_and_eval in module gdb:parse_and_eval(...)    parse_and_eval (String) -> Value.    Parse String as an expression, evaluate it, and return the result as a Value.

Now, we just need to register thesecommands in GDB using the source command and attach them to theircorresponding breakpoints.

(gdb) source hooks.py(gdb) break open_accountBreakpoint 1 at 0x4015b4: file accounts.adb, line 11.(gdb) commands>python open_account_hook()>end(gdb) break payBreakpoint 2 at 0x4015f6: file accounts.adb, line 16.(gdb) commands>python pay_hook()>end

This detects that the first transaction is indeed invalid, asit happens before the accounts are opened:

(gdb) runStarting program: C:\home\guitton\GIT\GDBuilds\gems\3\p.exe [New Thread 1872.0x638]Breakpoint 2, accounts.pay (from=alice, to=bob, cash=20) at accounts.adb:1616            Bank (To).Balance := Bank (To).Balance + Cash;error: alice tries to pay before opening an account

The Python API provides an advanced programming interface for GDB,with facilities for browsing through backtraces, threads, symbols, etc. For moreinformation, we invite you to consult the dedicated section on those featuresin the GDB user’s guide.

breakpoint_commands_2.zip

About the Author

Jerome joined AdaCore in 2002 after completing his studies at the Ecole Nationale des Télécommunations (Paris, France), during which he had already worked with the company on one of its many reseach projects, namely PolyORB. His enthusiasm has remained undimmed during these six years and he has worked on a variety of projects, as well as acquiring expertise in debuggers and cross technologies. He has recently led the effort to port GNAT Pro to various cross targets (VxWorks 6, ELinOS).