GDB Extension to directly access attributes of Object and ObjectRef

Hi all,

I’ve created a small gdb extension to find types and access attributes of the Object and ObjectRef subclasses.

What I mean by that is, say there is an AddNode passed to a function as a PrimExpr and while debugging this function, there is no easy way to find that it actually belongs to an AddNode. Trying whatis object or ptype object just prints that it’s a PrimExpr.

I found a rather hacky way to do this by accessing the deleter_ member of the enclosing object and the template argument to the Handler contains the proper type (a sample output for AddNode is as shown below:)

$65 = (tvm::runtime::Object::FDeleter) 0x7fffaf6c9300 <tvm::runtime::SimpleObjAllocator::Handler<tvm::tir::AddNode>::Deleter_(tvm::runtime::Object*)>

I created a separate post asking if there’s a better way to find the types of an object, and if I find any answers I’ll update my extension.

Anyway, I took this hack to find the type and created a small extension that I’ve pushed to https://github.com/anirudhsundar/tvm-gdb-commands.

It provides 4 new commands (all with tvm_* prefix),

tvm_dump - just saves from typing print tvm::Dump(...)

tvm_type - extracts the type from the deleter_ handler, and prints it out

tvm_attr - Uses tvm_type to allow us to recursively access attributes of a tvm object For example

(gdb) tvm_attr for_node.body.seq[0].body.then_case
access string '((tvm::tir::IfThenElseNode*)((tvm::tir::ForNode*)((tvm::tir::SeqStmtNode*)((tvm::tir::ForNode*)for_node).body).seq[0]).body).then_case'
Type of object: 'tvm::tir::StoreNode'
Cb[(((j.outer*64) + (i*n)) + j.inner.s)] = 0h

prints out the then_case member of the if condition present in that for body.

tvm_fields - prints out the attributes/fields available for a given object expression. For example,

(gdb) tvm_fields for_node.body.seq[0].body
tvm::tir::StmtNode      condition       then_case       else_case       _type_key       _type_final     _type_child_slots

prints out the list of fields of the IfThenElseNode, from which we can find that there are attributes like then_case and else_case

Do let me know if there are better ways to do what I’m doing, and be as brutal as needed with the feedback.

Thanks, Anirudh

Edit: Just a quick update, I had to refactor my git history due to a small mistake, so if you need to pull new changes, you might have to pull with git pull --force to force update history beyond fast-forward

5 Likes

I remember @Lunderberg also has some GDB hacks.

Yes, @Lunderberg’s extension actually removes the python specific frames from the backtrace as far as I understand, and actually it was that extension that gave me the inspiration to create this one.

Ooh, that is fantastic, and gives really nice fine-grained information. I had done some playing around with adding a custom printer to gdb.pretty_printers which delegates to tvm::PrettyPrint for any ObjectRef, but it still has some bugs that need working out.

How well does it run when recovering from segfaults? That’s one of the main issues I’ve run into, since using gdb.parse_and_eval or gdb.execute to call tvm functions can fail if the program just segfaulted.

Thanks for the feedback, and the idea of hooking up the pretty_printers seems interesting, but I didn’t know about pretty_printers, so I just ended up registering a command that just internally calls tvm::Dump()

I did try it out with a couple of cases with segfault, and I was always able to print the node attributes and types. Internally all it does is access the object members directly (with the proper casts) and the only function call involved here is the tvm::Dump() used in the tvm_dump command, which seemed to work for the simple cases I tried.

BTW, this is my first time working with GDB python API, so if you happen to look at the code and find anything that can be improved, do let me know, (or go ahead and create a PR).

It might be nice to see extensions like this get their own repository, something like apache/tvm-tools or tlc-pack/tvm-tools.

1 Like

This is just fantastic!! Thank you for the super awesome contribution!!

That was my initial thought as well. It would be great to have a common location for all these tools. I only know of a couple of others like the gdb extension for stack traces by @Lunderberg and the ffi-navigator by @tqchen, but maybe there are others as well that I don’t know about and a common repo would help with finding these tools as well.

Thanks a lot for the great feedback, appreciate it.

Looking through it, it looks really good! I think the one thing I would change would be moving get_object_access_str and get_attribute_fields from free functions to static methods of a common base class, then making TVMGetDerivedType, TVMAccessRuntimeAttr, TVMFields inherit from that class.

In normal python code, I prefer free functions for utility methods. Unfortunately, gdb loads extension modules by eval-ing the code, not by importing it, and so every extension module exists within the same namespace. By having the utility methods bundled into the class definition, there isn’t a chance for accident name collisions.

Definitely agreed. That would give a common location that would be easier for new developers to find, and for contributions to spread.

Thanks for the great feedback, I did not know that GDB eval's the code. I made the changes you suggested, and created a utility class TVMUtils and added both the free functions into that class as @staticmethods. One thing I wasn’t sure though (and wanted to check before I push the changes) was the question of why we would need to inherit from this class, since the class only contains 2 static methods and can be accessed by the class name directly.

The only reason I thought this might be better would be for future use, when there might come a need for stateful methods to be defined in the utility class. Let me know if you had any other specific reasons as well and I’ll push those changes tomorrow.

Thanks

I’ve refactored the code and pushed it.

This is pretty great but I am having a bit of trouble using it. When I do tvm_attr I can’t see the pretty printed body of the nodes:

On an outer loop:

- exec tvm_dump op
$36 = void

-exec tvm_attr op.body
access string '((tvm::tir::ForNode*)op).body'
Type of object: 'tvm::tir::ForNode'

-exec tvm_fields op.body
tvm::tir::StmtNode	loop_var	min	extent	kind	body	thread_binding	annotations	_type_key	_type_final	_type_child_slots

-exec tvm_attr op.body.min
 access string '((tvm::tir::ForNode*)((tvm::tir::ForNode*)op).body).min'
 Type of object: 'tvm::IntImmNode'

On the innermost loop:

-exec tvm_attr op.body
access string '((tvm::tir::ForNode*)op).body'
Type of object: 'tvm::tir::StoreNode

In your example you also had a 3rd line of output saying

Cb[(((j.outer*64) + (i*n)) + j.inner.s)] = 0h

Do you have an idea why this does not show up in my case ?

I have not yet encountered a case like that, but from your examples it looks like tvm::Dump(...) does not seem to print anything for you.

tvm_dump seems to run fine since there is no error message and it shows the return type as void. One guess I have is that since tvm::Dump(...) prints to stderr and nothing seems to be printed on the screen for you, it might be a case where your stderr is getting redirected elsewhere.

If you provide some input on how you’re executing gdb, it might help debug further.