Debugging with GDB: Writing an Xmethod

Next: Inferiors In Python, Previous: Xmethod API, Up: Python API [Contents][Index]

#### 23.2.2.15 Writing an Xmethod

Implementing xmethods in Python will require implementing xmethod matchers and xmethod workers (see Xmethods In Python). Consider the following C`++` class:

    class MyClass
    {
    public:
      MyClass (int a) : a_(a) { }

      int geta (void) { return a_; }
      int operator+ (int b);

    private:
      int a_;
    };

    int
    MyClass::operator+ (int b)
    {
      return a_ + b;
    }

Let us define two xmethods for the class `MyClass`, one replacing the method `geta`, and another adding an overloaded flavor of `operator+` which takes a `MyClass` argument (the C`++` code above already has an overloaded `operator+` which takes an `int` argument). The xmethod matcher can be defined as follows:

    class MyClass_geta(gdb.xmethod.XMethod):
        def __init__(self):
            gdb.xmethod.XMethod.__init__(self, 'geta')

        def get_worker(self, method_name):
            if method_name == 'geta':
                return MyClassWorker_geta()

    class MyClass_sum(gdb.xmethod.XMethod):
        def __init__(self):
            gdb.xmethod.XMethod.__init__(self, 'sum')

        def get_worker(self, method_name):
            if method_name == 'operator+':
                return MyClassWorker_plus()

    class MyClassMatcher(gdb.xmethod.XMethodMatcher):
        def __init__(self):
            gdb.xmethod.XMethodMatcher.__init__(self, 'MyClassMatcher')
            # List of methods 'managed' by this matcher
            self.methods = [MyClass_geta(), MyClass_sum()]

        def match(self, class_type, method_name):
            if class_type.tag != 'MyClass':
                return None
            workers = []
            for method in self.methods:
                if method.enabled:
                    worker = method.get_worker(method_name)
                    if worker:
                        workers.append(worker)

            return workers

Notice that the `match` method of `MyClassMatcher` returns a worker object of type `MyClassWorker_geta` for the `geta` method, and a worker object of type `MyClassWorker_plus` for the `operator+` method. This is done indirectly via helper classes derived from `gdb.xmethod.XMethod`. One does not need to use the `methods` attribute in a matcher as it is optional. However, if a matcher manages more than one xmethod, it is a good practice to list the xmethods in the `methods` attribute of the matcher. This will then facilitate enabling and disabling individual xmethods via the `enable/disable` commands. Notice also that a worker object is returned only if the corresponding entry in the `methods` attribute of the matcher is enabled.

The implementation of the worker classes returned by the matcher setup above is as follows:

    class MyClassWorker_geta(gdb.xmethod.XMethodWorker):
        def get_arg_types(self):
            return None

        def get_result_type(self, obj):
            return gdb.lookup_type('int')

        def __call__(self, obj):
            return obj['a_']

    class MyClassWorker_plus(gdb.xmethod.XMethodWorker):
        def get_arg_types(self):
            return gdb.lookup_type('MyClass')

        def get_result_type(self, obj):
            return gdb.lookup_type('int')

        def __call__(self, obj, other):
            return obj['a_'] + other['a_']

For GDB to actually lookup a xmethod, it has to be registered with it. The matcher defined above is registered with GDB globally as follows:

    gdb.xmethod.register_xmethod_matcher(None, MyClassMatcher())

If an object `obj` of type `MyClass` is initialized in C`++` code as follows:

    MyClass obj(5);

then, after loading the Python script defining the xmethod matchers and workers into `GDBN`, invoking the method `geta` or using the operator `+` on `obj` will invoke the xmethods defined above:

    (gdb) p obj.geta()
    $1 = 5

    (gdb) p obj + obj
    $2 = 10

Consider another example with a C++ template class:

    template <class T>
    class MyTemplate
    {
    public:
      MyTemplate () : dsize_(10), data_ (new T [10]) { }
      ~MyTemplate () { delete [] data_; }

      int footprint (void)
      {
        return sizeof (T) * dsize_ + sizeof (MyTemplate<T>);
      }

    private:
      int dsize_;
      T *data_;
    };

Let us implement an xmethod for the above class which serves as a replacement for the `footprint` method. The full code listing of the xmethod workers and xmethod matchers is as follows:

    class MyTemplateWorker_footprint(gdb.xmethod.XMethodWorker):
        def __init__(self, class_type):
            self.class_type = class_type

        def get_arg_types(self):
            return None

        def get_result_type(self):
            return gdb.lookup_type('int')

        def __call__(self, obj):
            return (self.class_type.sizeof +
                    obj['dsize_'] *
                    self.class_type.template_argument(0).sizeof)

    class MyTemplateMatcher_footprint(gdb.xmethod.XMethodMatcher):
        def __init__(self):
            gdb.xmethod.XMethodMatcher.__init__(self, 'MyTemplateMatcher')

        def match(self, class_type, method_name):
            if (re.match('MyTemplate<[ \t\n]*[_a-zA-Z][ _a-zA-Z0-9]*>',
                         class_type.tag) and
                method_name == 'footprint'):
                return MyTemplateWorker_footprint(class_type)

Notice that, in this example, we have not used the `methods` attribute of the matcher as the matcher manages only one xmethod. The user can enable/disable this xmethod by enabling/disabling the matcher itself.

Next: Inferiors In Python, Previous: Xmethod API, Up: Python API [Contents][Index]