writing an extension command in Python

Hi.

I guess this is more of a user question than a dev question, but since
there is no user mailing list I hope that it’s OK to post here!

I want to write an extension command in Python that would work something
like this:

  (lldb) showimage 300 200 <some-expr-that-evaluates-to-a-pointer>

which grabs the data at the pointer and goes off and writes it to a file
as a PNG, or pops up a window showing the image, or something like that.

How do I go about writing the extension? I see there is this disasm.py
example, but that seems to be controlling lldb from Python rather than
registering an extension to be called from the lldb prompt.

Also, is there documentation yet on how to use the API? The C++ headers
in include/lldb/API don’t seem to have doxygen comments. (I can
probably fumble my way through working out the Python API from the C++
one, if it is documented.)

Thanks!

Cameron

Hi Cameron,

Could you elaborate a bit more on what you are trying to do?

"showimage ..." is not an lldb command. Is it a Python function that you have defined in a Python module and wish to import/call?

lldb include a fairly complete embedded Python interpreter. To get to the Python interpreter from the lldb command line prompt you can either type "script <some-python-command>" to execute a one line Python command, or you can just type "script" to drop into an interactive Python interpreter (with the lldb API pre-loaded).

If you can give me more details as to what you are trying to do, I can probably answer your question.

-- Caroline
ctice@apple.com

Hi Caroline.

Caroline Tice:

Could you elaborate a bit more on what you are trying to do?

"showimage ..." is not an lldb command. Is it a Python function that
you have defined in a Python module and wish to import/call?

Yes. I want to be able to type “showimage …” at the lldb prompt and
have that run some Python code that does the image showing.

lldb include a fairly complete embedded Python interpreter. To get
to the Python interpreter from the lldb command line prompt you
can either type "script <some-python-command>" to execute a one
line Python command, or you can just type "script" to drop into an
interactive Python interpreter (with the lldb API pre-loaded).

Ah, I see: so I would write a Python function and then set up an alias
so that “showimage” would call it? How do I ensure that my Python
function is available to the Python interpreter within lldb? Should I
put it in a particular directory?

If you can give me more details as to what you are trying to do, I can
probably answer your question.

I also need to know how my Python script can get access to the debugger.
Is there some global variable that is an SBDebugger object for the
debugger that is running, which my script can use?

Thanks,

Cameron

You could currently do this by writing a ShowImage() function in python and then dropping into the embedded python interpreter within LLDB:

(lldb) breakpoint set --file make_image.c --line 123
(lldb) run
stop at breakpoint...
(lldb) script

Now you are in the script interpreter with access to the current program. You should be able to get ahold of the current process and thread and then evaluate an expression:

debugger = lldb.SBDebugger().FindDebuggerWithID(lldb.debugger_unique_id)
target = debugger.GetCurrentTarget()
process = target.GetProcess()
thread = process.GetThreadAtIndex (0)

if thread.IsValid():
    frame = thread.GetFrameAtIndex (0)
    if frame.IsValid():
        image_data_expr = frame.EvaluateExpression ("my_struct->image_data");
        image_size_expr = frame.EvaluateExpression ("my_struct->image_size");

  if image_data_expr.GetError().Success() && image_size_expr.GetError().Success():
    image_addr = int(image_data_expr.GetValue(frame), 16);
    image_size = int(image_size_expr.GetValue(frame), 16);
    lldb::SBError error;
    process.ReadMemory (image_addr, image_bytes, image_size, error);

    ... then save the bytes to a file and then call some display functions....

We need to make convenience variables in the embedded interpreter to make getting the current target/process/thread/frame much easier, but this should at least be a start. The one issue is that in our SBProcess::ReadMemory() function call the C++ API looks like:

    size_t
    SBProcess::ReadMemory (addr_t addr, void *buf, size_t size, lldb::SBError &error);

I am not sure how SWIG will handle the "void *buf". We might need to add some conversion functions so that any functions that take a "void *buf, size_t size" pair of arguments, can take a reference to a python array that can be filled in.

If wrote the above code in a function, you could them import your module and just call your function with a few parameters.

Greg Clayton

Greg Clayton:

Now you are in the script interpreter with access to the current
program. You should be able to get ahold of the current process and
thread and then evaluate an expression:

debugger = lldb.SBDebugger().FindDebuggerWithID(lldb.debugger_unique_id)

Thanks for the sample code, Greg! It looks like the above line is what
I need to get going.


I am not sure how SWIG will handle the "void *buf". We might need to
add some conversion functions so that any functions that take a "void
*buf, size_t size" pair of arguments, can take a reference to a python
array that can be filled in.

Yeah, maybe. I’ll see how I go and if I need to tweak some SWIG
directives I’ll let you know.

If wrote the above code in a function, you could them import your
module and just call your function with a few parameters.

Great, so I just need to put my module on the Python path and then
import it.

Thanks,

Cameron

OK, I am making some progress. In my .lldbinit file I have

  script import ImageData

and in ImageData.py it tries to register an alias for itself when it is
loaded

  __GetDebugger().HandleCommand("commands alias showimage script ImageData.ShowImageARGB(%1, %2, \"%3\")")

so that I can type commands like this:

  (lldb) showimage 300 200 myStruct->mImageData

This doesn’t work; when I try to use the showimage alias the Python
interpreter complains about a literal "%1" that it’s trying to parse.
Is there a way I can escape or unescape appropriately so that the alias
arguments get passed to my Python function call?

This way of registering an alias for my function seems unideal, though,
because it won’t work if the expression that I use contains spaces
(since it will get split up into %3, %4, etc.) or if it includes double
quotes, since that will mess with the quoting I’m using to pass the
expression in as a string. Are there better ways to handle this? Is
there a way to register my command with the SBCommandIntepreter so that
it can feel more like a built-in command?

Thanks!

Cameron

OK, I am making some progress. In my .lldbinit file I have

script import ImageData

and in ImageData.py it tries to register an alias for itself when it is
loaded

__GetDebugger().HandleCommand("commands alias showimage script ImageData.ShowImageARGB(%1, %2, \"%3\")")

That particular alias command (as you discovered) will not work because the 'script' command takes raw input, so the % arguments do not get interpreted as place-holders but as part of the raw input text.

You could try something like:

_GetDebugger().HandleCommand("commands alias showimage script ImageData.ShowImageARGB")

(lldb) showimage (300, 200, "myStruct->mImageData")

I believe this will work. (It worked in my test case).

so that I can type commands like this:

(lldb) showimage 300 200 myStruct->mImageData

This doesn’t work; when I try to use the showimage alias the Python
interpreter complains about a literal "%1" that it’s trying to parse.
Is there a way I can escape or unescape appropriately so that the alias
arguments get passed to my Python function call?

This way of registering an alias for my function seems unideal, though,
because it won’t work if the expression that I use contains spaces
(since it will get split up into %3, %4, etc.) or if it includes double
quotes, since that will mess with the quoting I’m using to pass the
expression in as a string. Are there better ways to handle this? Is
there a way to register my command with the SBCommandIntepreter so that
it can feel more like a built-in command?

There are definitely plans to eventually add the ability to register your own commands more like built-in commands (it's been on my to-do list for a while), but I'm not sure when we will get around to implementing that.

Thanks!

Cameron

If you need/want any more help with any of this stuff, please let me know; I will be more than happy to help you.

-- Caroline
ctice@apple.com