ada_answers_gem_logo_fourth
Next Next

Gem #122: Breakpoint Commands — Part 2

Let’s get started…

We previously considered a bank that was managing only one account. Now suppose that this example has been extended to support several 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 only happen on opened accounts, we need to keep a record of the accounts that have been opened already. When we only had one account, the state was a simple Boolean variable; now it is a list of accounts, which is more complicated to express with only convenience variables. The Python interface of GDB can be used to bypass this limitation.

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

with Accounts; use Accounts;

procedure P is
begin
   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, it hides 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 executed whenever an operation on an account is scheduled. Create a new file named 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 accounts that have been opened already, and two hooks to be executed when Open_Account and Pay are called.

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

(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 these commands in GDB using the source command and attach them to their corresponding breakpoints.

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

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

(gdb) run
Starting program: C:\home\guitton\GIT\GDB\builds\gems\3\p.exe 
[New Thread 1872.0x638]
Breakpoint 2, accounts.pay (from=alice, to=bob, cash=20) at accounts.adb:16
16            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 more information, we invite you to consult the dedicated section on those features in the GDB user’s guide.

Attached Files

Jerome Guitton
AdaCore

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).

 
 

Comments on Gems are now closed.