Question about stacktraces using Python

As I’ve been poking around with the Python bindings in CIRCT, I’ve often committed sins that crashed the compiler. In these cases, I haven’t been able to get a good stacktrace out.

To illustrate, here’s some Python that will cause an error:

from mlir.ir import *

with Context() as ctx, Location.unknown():
  m = Module.create()
  with InsertionPoint(m.body):
    ctx.allow_unregistered_dialects = True
    i1 = IntegerType.get_signless(1)
    op1 = Operation.create("example1", [i1])
    op2 = Operation.create("example2", [], [op1.result])
    op1.operation.erase()

  print(m)

This is the complete output of running the above:

$ PYTHONPATH=build/python python test.py
error: 'example1' op operation destroyed but still has uses
LLVM ERROR: operation destroyed but still has uses
Aborted (core dumped)

@jdd added a call to LLVMEnablePrettyStackTrace(), and with that, the output becomes:

$ PYTHONPATH=build/python python test.py
error: 'example1' op operation destroyed but still has uses
LLVM ERROR: operation destroyed but still has uses
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace.
Aborted (core dumped)

It adds the line about submitting a bug report, but I was hoping for a full stacktrace.

Is there something else we are missing here to get out the stacktrace? I’m curious if others have dealt with this.

In terms of setup, I’m testing this with assertions enabled and a debug build. Our Python bindings are attempting to closely track the upstream best practices.

Stacktraces are produced by LLVM when the handler is installed, which you can do by calling llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);.
This is also implicitly done as part of calling InitLLVM, which is what most MLIR/LLVM tools are doing (here for example).

My answer may not be completely satisfying – When I get a crash, I run with gdb --args python my_test.py and bt it. And then, I typically fix the cause of the crash. As a productivity oriented API, the Python bindings should be checking pre-conditions, at least for common things, vs hard crashing.

This works pretty well for me as well.

We could add some “mlirOperationIsSafeToErase” helpers maybe that the bindings could call to check the preconditions, and throw a python exception?

Thanks for the tips. I think InitLLVM and the associated logic is what I was looking for. I will try invoking that from the bindings.

The debugger is definitely a good friend with the bindings work :slight_smile:

We could add some “mlirOperationIsSafeToErase” helpers maybe that the bindings could call to check the preconditions, and throw a python exception?

I think that makes sense in general, and the bindings already have quite good error messages in a lot of cases. I can definitely chip in to improve that as I do dumb things and find these situations.