Various questions about lldbinit and python scripts.

Hi.

Being forced to move from gdb to lldb thanks to XCode 5 dropping
support for gdb, I find myself having to re-implement several commands
I had up and running within gdb.

I use the Qt framework, and in gdb, I had various macros to print the
content of Qt objects such as QString, QMap, QList etc...

I tried to follow the tutorials found there:
http://lldb.llvm.org/tutorial.html

and scripting:
http://lldb.llvm.org/scripting.html

In the python reference documentation:
http://lldb.llvm.org/python-reference.html

there's a link to a template example:
http://llvm.org/svn/llvm-project/lldb/trunk/examples/python/cmdtemplate.py

which according to the documentation should be invoked from lldb with:

command script import /path/to/cmdtemplate.py

When doing so, the module gets properly imported and you see in the
lldb console:

The "framestats" command has been installed, type "help framestats" or
"framestats --help" for detailed help.

However trying to use the command
(lldb) framestats --help

yield:
error: unable to execute script function

I'm just putting this as an example... I've been unable to load *any*
python script within lldb:

(lldb) version
lldb-300.2.47

it always yield the same error "unable to execute script function"..

I tried setting the PYTHONPATH variable to:
/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/Resources/Python

which is where it's located, to no available...

So I'm looking at some pointers on how to use python scripts within lldb.

Ultimately, what I'm trying to achieve is printing the content of a QString.
A QString object has a private member QString::Data* d; where d->size
contains the number of UTF-16 characters
and d->data is an array of ushort (the UTF-16) characters.

In gdb I would "simply" have:

define printqstringdata
    set $d = ('QString::Data'*) $arg0
    set $i = 0
    # abort after a '-1' character, to avoid going on forever when
printing a garbage string
    while $i < $d->size && ($i == 0 || (char)$d->data[$i-1] != -1)
        printf "%c", (char)($d->data[$i++] & 0xff)
    end
    printf "\n"
end

to print the content of the QString, I would call printqstringdata variable.d

doing such loop would be easy in python; provided I managed to get the
script to load and provide and retrieve the QString variable argument.

Any pointers or link to some tutorials will be welcome...

Googling lldb python scripting hasn't provided much help at this stage
unfortunately... It seems no one has bothered writing much python
scripts for lldb yet.... probably too new or not popular enough.

Kind regards
Jean-Yves

Comments below.

Hi.

Being forced to move from gdb to lldb thanks to XCode 5 dropping
support for gdb, I find myself having to re-implement several commands
I had up and running within gdb.

I use the Qt framework, and in gdb, I had various macros to print the
content of Qt objects such as QString, QMap, QList etc...

I tried to follow the tutorials found there:
http://lldb.llvm.org/tutorial.html

and scripting:
http://lldb.llvm.org/scripting.html

In the python reference documentation:
http://lldb.llvm.org/python-reference.html

there's a link to a template example:
http://llvm.org/svn/llvm-project/lldb/trunk/examples/python/cmdtemplate.py

which according to the documentation should be invoked from lldb with:

command script import /path/to/cmdtemplate.py

When doing so, the module gets properly imported and you see in the
lldb console:

The "framestats" command has been installed, type "help framestats" or
"framestats --help" for detailed help.

However trying to use the command
(lldb) framestats --help
yield:
error: unable to execute script function

This works for me currently using the exact source for cmdtemplate.py:

(lldb) command script import ~/Documents/src/lldb/tot/examples/python/cmdtemplate.py
The "framestats" command has been installed, type "help framestats" or "framestats --help" for detailed help.
(lldb) framestats --help
Usage: framestats [options]

This command is meant to be an example of how to make an LLDB command that
does something useful, follows best practices, and exploits the SB API.
Specifically, this command computes the aggregate and average size of the
variables in the current frame and allows you to tweak exactly which variables
are to be accounted in the computation.

Options:
  -h, --help show this help message and exit
  -i, --in-scope in_scope_only = True
  -a, --arguments arguments = True
  -l, --locals locals = True
  -s, --statics statics = True

I'm just putting this as an example... I've been unable to load *any*
python script within lldb:

(lldb) version
lldb-300.2.47

it always yield the same error "unable to execute script function"..

I tried setting the PYTHONPATH variable to:
/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/Resources/Python

You don't need to do this, the LLDB.framework hard links agains the python framework in /System/Library/Frameworks/Python.framework. You only need to set the PYTHONPATH if you want to run any LLDB API through the "lldb" module from python scripts.

Have you installed your own python? Do you see a ton of files when you execute this shell command?:

% find /System/Library/Frameworks/Python.framework

which is where it's located, to no available...

So I'm looking at some pointers on how to use python scripts within lldb.

It should just work as long as you haven't messed with your python installation. We currently assume the system default of Python 2.7.

Ultimately, what I'm trying to achieve is printing the content of a QString.
A QString object has a private member QString::Data* d; where d->size
contains the number of UTF-16 characters
and d->data is an array of ushort (the UTF-16) characters.

In gdb I would "simply" have:

define printqstringdata
   set $d = ('QString::Data'*) $arg0
   set $i = 0
   # abort after a '-1' character, to avoid going on forever when
printing a garbage string
   while $i < $d->size && ($i == 0 || (char)$d->data[$i-1] != -1)
       printf "%c", (char)($d->data[$i++] & 0xff)
   end
   printf "\n"
end

to print the content of the QString, I would call printqstringdata variable.d

You will want to first get python working. Then you will want to implement a python summary function:

http://lldb.llvm.org/varformats.html

See the section labeled "PYTHON SCRIPTING".

doing such loop would be easy in python; provided I managed to get the
script to load and provide and retrieve the QString variable argument.

Any pointers or link to some tutorials will be welcome...

Let me know what the "find /System/Library/Frameworks/Python.framework" shows on your system. There should be a "/System/Library/Frameworks/Python.framework/Versions/2.7" folder with a ton of stuff in it.

Hello

Thanks for answering!... very much appreciated

This works for me currently using the exact source for cmdtemplate.py:

duh.. I feel silly.

I had called the script lldbfunc.py
After I changed:
    debugger.HandleCommand('command script add -f
cmdtemplate.the_framestats_command framestats')

into:
    debugger.HandleCommand('command script add -f
lldbfunc.the_framestats_command framestats')

then it works...

It's a bit of a worry though when the code being run in a file is
dependent on the name of that file !

Have you installed your own python? Do you see a ton of files when you execute this shell command?:

% find /System/Library/Frameworks/Python.framework

30271 files to be precise... with python 2.3 framework all the way down to 2.7

It should just work as long as you haven't messed with your python installation. We currently assume the system default of Python 2.7.

I have some alternative python installed (through macport), but they
are in /opt/local and not in my path.

Only reason I played with PYTHONPATH was while troubleshooting why it
didn't work.

You will want to first get python working. Then you will want to implement a python summary function:

http://lldb.llvm.org/varformats.html

See the section labeled "PYTHON SCRIPTING".

Thanks for the heads up... I'll have a look.

I already got QString to be displayed properly by changing the summary
format with:
{(const char *)$VAR.toUtf8().constData()}:s

not sure if you're familiar with xcode summary format, but that may be
an easier approach than messing with python scripts. Is there a way to
do loops in there (so I could display the content of loop and map
container)..

Thanks again
JY

Hello

Thanks for answering!.. very much appreciated

This works for me currently using the exact source for cmdtemplate.py:

duh… I feel silly.

I had called the script lldbfunc.py
After I changed:
debugger.HandleCommand(‘command script add -f
cmdtemplate.the_framestats_command framestats’)

into:
debugger.HandleCommand(‘command script add -f
lldbfunc.the_framestats_command framestats’)

then it works…

It’s a bit of a worry though when the code being run in a file is
dependent on the name of that file !

This is unfortunately Python’s fault
There are a couple ways that we could work around it.

One is to use file (without the .py extension of course) to know the module name. Now you still somehow depend on your module name, but this dependency is masked by Python itself doing the undignified work of figuring that out for you

Alternatively, you can use the @lldb.command decorator

At the top of your life, just import lldb (which you might be doing anyway), and then you can mark your commands with @lldb.command, as in:

import lldb

@lldb.command(“TheNameOfMyCommandHere")
def MyCommandImplementor(debugger,args,retval,unused):

retval,“Hello world this is me”
retval,args

Enrico Granata
:envelope_with_arrow: egranata@.com
:phone: 27683

Python is not all that complicated :slight_smile:
Here’s an example script that does formatting for arbitrary (pointer,length) pairs.

Since I was lazy I just decided my string type was going to be a std::pair<CharType*,LengthType> as in:

std::pair<const char *,unsigned long> Sutf8 {utf8_buf,utf8_size};
std::pair<const char16_t *,unsigned long> Sutf16 {utf16_buf,utf16_size};
std::pair<const char32_t *,unsigned long> Sutf32 {utf32_buf,utf32_size};
It should be easy to adjust the scripts to any type that exposes the same information through different member variable names.

import lldb
def utf8_summary(value,unused):
pointer = value.GetChildMemberWithName(“first”).GetValueAsUnsigned(0)
length = value.GetChildMemberWithName(“second”).GetValueAsUnsigned(0)
if pointer == 0:
return False
if length == 0:
return ‘""’
error = lldb.SBError()
string_data = value.process.ReadMemory(pointer, length, error)
return ‘"%s"’ % (string_data) # utf8 is safe to emit as-is on OSX

def utf16_summary(value,unused):
pointer = value.GetChildMemberWithName(“first”).GetValueAsUnsigned(0)
length = value.GetChildMemberWithName(“second”).GetValueAsUnsigned(0)

assume length is in bytes - if in UTF16 chars, just multiply by 2

if pointer == 0:
return False
if length == 0:
return ‘""’
error = lldb.SBError()
string_data = value.process.ReadMemory(pointer, length, error)
return ‘"%s"’ % (string_data.decode(‘utf-16’).encode(‘utf-8’)) # utf8 is safe to emit as-is on OSX

def utf32_summary(value,unused):
pointer = value.GetChildMemberWithName(“first”).GetValueAsUnsigned(0)
length = value.GetChildMemberWithName(“second”).GetValueAsUnsigned(0)

assume length is in bytes - if in UTF32 chars, just multiply by 4

if pointer == 0:
return False
if length == 0:
return ‘""’
error = lldb.SBError()
string_data = value.process.ReadMemory(pointer, length, error)
return ‘"%s"’ % (string_data.decode(‘utf-32’).encode(‘utf-8’)) # utf8 is safe to emit as-is on OSX

def __lldb_init_module(debugger,*args):
debugger.HandleCommand(‘type summary add --python-function utf81632.utf8_summary “std::__1::pair<const char *, unsigned long>”’)
debugger.HandleCommand(‘type summary add --python-function utf81632.utf16_summary “std::__1::pair<const char16_t *, unsigned long>”’)
debugger.HandleCommand(‘type summary add --python-function utf81632.utf32_summary “std::__1::pair<const char32_t *, unsigned long>”’)

In the “type summary add” part, you will bind the functions to the type name depending on the string encoding in use (8, 16 or 32)
The functions itself know to use a pointer,length pair to read the correct number of bytes, and do the encoding/decoding that is necessary (on OSX, outputting UTF8 is just safe and will do the right thing, so we are porting any other encoding to UTF8 for display)
This is what the output looks like:

(std::__1::pair<const char *, unsigned long>) Sutf8 = “blah blah blah けほ”
(std::__1::pair<const char16_t *, unsigned long>) Sutf16 = “blah blah blah けほ”
(std::__1::pair<const char32_t *, unsigned long>) Sutf32 = “blah blah blah けほ”

In case you do not know the “bytes per character” for your type, you should simply be asking the pointer’s type for its bye size, and switch on that to pick the right encoding. Or maybe your string type will have a flag telling you the encoding to use. Either way, it all boils down to one of these three functions.
Feel free to ping back if you have any further questions!

Enrico Granata
:envelope_with_arrow: egranata@.com
:phone: 27683

This is unfortunately Python’s fault
There are a couple ways that we could work around it.

One is to use __file__ (without the .py extension of course) to know the
module name. Now you still somehow depend on your module name, but this
dependency is masked by Python itself doing the undignified work of figuring
that out for you

But the name of the module is within a string, so I doubt using
__file__ would work here.
if I was to use __file__ in the argument of HandleCommand you get:

Function __file__.the_framestats_command was not found. Containing
module might be missing.

Alternatively, you can use the @lldb.command decorator

At the top of your life, just import lldb (which you might be doing anyway),
and then you can mark your commands with @lldb.command, as in:

import lldb

@lldb.command(“TheNameOfMyCommandHere")
def MyCommandImplementor(debugger,args,retval,unused):
>>retval,"Hello world this is me"
>>retval,args

will try those... thanks

This is unfortunately Python’s fault
There are a couple ways that we could work around it.

One is to use __file__ (without the .py extension of course) to know the
module name. Now you still somehow depend on your module name, but this
dependency is masked by Python itself doing the undignified work of figuring
that out for you

But the name of the module is within a string, so I doubt using
__file__ would work here.
if I was to use __file__ in the argument of HandleCommand you get:

Function __file__.the_framestats_command was not found. Containing
module might be missing.

You will need to substitute it into a string:

command = "command script add -f %s.the_framestats_command framestats" % (os.path.splitext(os.path.basename(__file__)))

Try using the "@lldb.command(“TheNameOfMyCommandHere")" decorator.