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