12

In Python, I have a a variable var of type gdb.Value that corresponds to a C++ struct.

The struct has a method void foo().

I can evaluate this expression var['foo']. But var['foo']\() will complain saying

RuntimeError: Value is not callable (not TYPE_CODE_FUNC)

I believe the value type will be gdb.TYPE_CODE_METHOD (not sure, but var['foo'].type.code returns 16) in my case.

So I guess the question is:

Does python API support calls to class methods, and if not, is there a workaround?

Thanks!

user202729
  • 3,358
  • 3
  • 25
  • 36
aayupov
  • 267
  • 1
  • 10
  • Alternatively perhaps the last solution in [c++ - Calling a lambda function in GDB - Stack Overflow](https://stackoverflow.com/questions/24329925/calling-a-lambda-function-in-gdb/70254108#70254108) applies, call lambda is special case where it's `operator()`. – user202729 Mar 10 '22 at 05:48

4 Answers4

10

OK, I think I was able to do what I want using the Tom's advice and another workaround.

The problem I need an extra workaround was (as I mentioned in the comment above) that I didn't have the variable name in order to compose a string of form: myval.method() to pass to gdb.parse_and_eval.

So the workaround for that one is to get the address of the variable and then cast it to the type and then add a method call to the string.

Both type and address exist in python api for gdb.Value. So the solution looks like the following:

    eval_string = "(*("+str(self.val.type)+"*)("+str(self.val.address)+")).method()"
    return gdb.parse_and_eval(eval_string);
LoneWanderer
  • 3,058
  • 1
  • 23
  • 41
aayupov
  • 267
  • 1
  • 10
  • I tried to use this trick, but found it to be very unstable in the project that could benefit from it: 50% of my gdb session have failed using it. Can't wait for a GDB fix :) – LoneWanderer Mar 24 '19 at 05:11
  • Wrapping the `type` in single quote might work better for mangled names with spaces or similar. – user202729 Dec 13 '21 at 15:37
  • Because the `str` may output something like `0x555556567094 ` it's better to use `str(hex(...))` instead. – user202729 Dec 13 '21 at 15:42
  • @LoneWanderer Do you have some example files that? There might be some workarounds (if there's no address of the variable it's possible to dynamically allocate memory in gdb and call that; but it won't mutate the original value, works in most cases) – user202729 Dec 26 '21 at 03:40
  • Unfortunatly no : I moved out on other projects since. Sry. – LoneWanderer Dec 26 '21 at 08:17
3

It's just a missing feature that nobody has implemented yet. You might see if it is in bugzilla, and, if not, file a bug.

A typical workaround is to substitute the "this" argument's value into a string and make the call via gdb.parse_and_eval. This usually works but is, of course, distinctly second-best.

Tom Tromey
  • 21,507
  • 2
  • 45
  • 63
  • Tom, thanks for the response. I'd like to try the workaround you suggest, but here is my problem. I use the python api for pretty printing. In the class I register for pretty printing, I will only get the value of the variable in the ctor and I don't have the variable name I don't think unless the gdb.Value has it. In std::string example below, "val" is the gdb.Value and not variable name available, right? class StdStringPrinter(object): "Print a std::string" def __init__(self, val): self.val = val def to_string(self): return self.val['_M_dataplus']['_M_p'] – aayupov Apr 01 '14 at 17:08
  • 1
    Here is a very similar request on the bugzilla for our reference: [link](https://sourceware.org/bugzilla/show_bug.cgi?id=13326) – aayupov Apr 01 '14 at 17:17
1

Method call is not (yet?) supported by GDB. https://gdb.sourceware.narkive.com/KVTUIAvl/c-method-call-for-a-gdb-value-object-from-within-python

However function call is supported. If you have opportunity to make a function wrapper which will call your method:

void func(mystruct& arg) { arg.method(); }

Then you can find this func via global gdb search, and call:

sym = gdb.lookup_global_symbol('namespace::func(mystruct&)')
result = sym.value()(this.val)

This method does not require taking address of the variable, which fails 50% of the time due to compiler optimizations.

  • This only works for non-templated structs, unfortunately. In cases such as `template struct A {}; template void function(A)` you would have to specify for example `print 'function'(a)`, type `function[TAB][TAB]` to see the list. – user202729 Mar 10 '22 at 04:42
  • Unfortunately, gdb has some bugs that prevent the correct overload from being detected in a few rare cases. – user202729 Mar 10 '22 at 05:29
  • For the template issue, it can be partially circumvented by making friend function inside the struct (need instantiation to use from gdb); however `gdb.parse_and_eval` may still return the wrong overload. – user202729 Mar 10 '22 at 05:37
  • Issues: ■ if the function is `template`, to call it you have to specify the `` part explicitly. Alternatively, just overload the functions e.g. if it takes in a `struct`, make it `friend` of that struct. ■ also, Python API cannot use this (`gdb.parse_and_eval(function)` will return one arbitrary overload), but can use convenience variable, see https://stackoverflow.com/a/71419746/5267751 for an example. ■ in a few rare cases (function returning `std::string`) the resolved overload is wrong. – user202729 Mar 10 '22 at 06:49
0

These methods applies to static methods too.

  1. Use convenience variable.

    gdb.set_convenience_variable("tmp",
            gdb.parse_and_eval("c").address
            )
    gdb.execute("print $tmp->f()")
    
  2. If the type is known, you can do:

    gdb.parse_and_eval("C::f")(
            gdb.parse_and_eval("c").address
    )
    

    (alternatively use the mangled name _ZN1C1fEv. nm --demangle can be used for this for example)

  3. If only the type of the target function is known, you can do

    cftype=gdb.parse_and_eval("C::f").type  # ← this is known
    
    gdb.parse_and_eval("c")["f"].cast(cftype)(
            gdb.parse_and_eval("c").address
    )
    
  4. Some flexibility is not allowed by gdb, but no guarantee:

    cftype=gdb.parse_and_eval("* (void (*) (void*)) 0").type
    
    gdb.parse_and_eval("c")["f"].cast(cftype)(
            gdb.parse_and_eval("c").address
    )
    

(if it's static function you don't need to "fake" it as void const*, can also do directly in C++ code)


Example C++ code:

#include<iostream>

struct C{
    int x;
    void f() {
        ++x;
        std::cout<<x<<"\n";
    }
};

int main(){
    C c{5};
    c.f();
    __builtin_trap();
}

In addition, if the struct is local however, you'll have to apply some workarounds described in https://stackoverflow.com/a/70254108/5267751.

Example C++ code

int main(){
    struct C{
        int x;
        void f() {
            ++x;
            std::cout<<x<<"\n";
        }
    };
    
    C c{5};
    c.f();
    __builtin_trap();
}

Example Python code

gdb.parse_and_eval("'main::C::f'")(gdb.parse_and_eval("c").address)

Alternatively use _ZZ4mainEN1C1fEv.

user202729
  • 3,358
  • 3
  • 25
  • 36