質問

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!

役に立ちましたか?

解決

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

他のヒント

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.

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.

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top