Most of the times, summary strings prove good enough for the job of summarizing
the contents of a variable. However, as soon as you need to do more than picking
some values and rearranging them for display, summary strings stop being an
effective tool. This is because summary strings lack the power to actually perform
some computation on the value of variables.
To solve this issue, you can bind some Python scripting code as a summary for
your datatype, and that script has the ability to both extract children variables
as the summary strings do and to perform active computation on the extracted
values. As a small example, let's say we have a Rectangle class:
class Rectangle
{
private:
int height;
int width;
public:
Rectangle() : height(3), width(5) {}
Rectangle(int H) : height(H), width(H*2-1) {}
Rectangle(int H, int W) : height(H), width(W) {}
int GetHeight() { return height; }
int GetWidth() { return width; }
};
Summary strings are effective to reduce the screen real estate used by
the default viewing mode, but are not effective if we want to display the
area, perimeter and length of diagonal of Rectangle
objects
To obtain this, we can simply attach a small Python script to the Rectangle
class, as shown in this example:
(lldb) type summary add -P Rectangle
Enter your Python command(s). Type 'DONE' to end.
def function (valobj,dict):
height_val = valobj.GetChildMemberWithName('height')
width_val = valobj.GetChildMemberWithName('width')
height_str = height_val.GetValue()
width_str = width_val.GetValue()
height = int(height_str)
width = int(width_str)
area = height*width
perimeter = 2*height + 2*width
diag = sqrt(height*height+width*width)
return 'Area: ' + str(area) + ', Perimeter: ' + str(perimeter) + ', Diagonal: ' + str(diag)
DONE
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> from math import sqrt
>>> quit()
(lldb) frame variable
(Rectangle) r1 = Area: 20, Perimeter: 18, Diagonal: 6.40312423743
(Rectangle) r2 = Area: 72, Perimeter: 36, Diagonal: 13.416407865
(Rectangle) r3 = Area: 16, Perimeter: 16, Diagonal: 5.65685424949
|
In this scenario, you need to enter the interactive interpreter to import the
function sqrt() from the math library. As the example shows, everything you enter
into the interactive interpreter is saved for you to use it in scripts. This way
you can define your own utility functions and use them in your summary scripts if
necessary.
In order to write effective summary scripts, you need to know the LLDB public
API, which is the way Python code can access the LLDB object model. For further
details on the API you should look at this page, or at
the LLDB doxygen documentation when it becomes available.
As a brief introduction, your script is encapsulated into a function that is
passed two parameters: valobj
and dict
.
dict
is an internal support parameter used by LLDB and you should
not use it.
valobj
is the object encapsulating the actual
variable being displayed, and its type is SBValue. The most important thing you can
do with an SBValue is retrieve its children objects, by calling
GetChildMemberWithName()
, passing it the child's name as a string, or ask
it for its value, by calling GetValue()
, which returns a Python string.
If you need to delve into several levels of hierarchy, as you can do with summary
strings, you can use the method GetValueForExpressionPath()
, passing it
an expression path just like those you could use for summary strings (one of the differences
is that dereferencing a pointer does not occur by prefixing the path with a *
,
but by calling the Dereference()
method on the returned SBValue).
If you need to access array slices, you cannot do that (yet) via this method call, and you must
use GetChildMemberWithName()
querying it for the array items one by one.
Also, handling custom formats is something you have to deal with on your own.
Other than interactively typing a Python script there are two other ways for you
to input a Python script as a summary:
- using the --python-script option to
type summary add
and typing the script
code as an option argument; as in:
(lldb) type summary add --python-script "height =
int(valobj.GetChildMemberWithName('height').GetValue());width =
int(valobj.GetChildMemberWithName('width').GetValue());
return 'Area: ' + str(height*width)" Rectangle
|
- using the
--python-function
(-F
) option to type summary add
and giving the name of a
Python function with the correct prototype. Most probably, you will define (or have
already defined) the function in the interactive interpreter, or somehow
loaded it from a file.