Summaries work well when one is able to navigate through an expression path.
In order for LLDB to do so, appropriate debugging information must be available.
Some types are opaque, i.e. no knowledge of their internals is provided.
When that's the case, expression paths do not work correctly.
In other cases, the internals are available to use in expression paths, but they
do not provide a user-friendly representation of the object's value.
For instance, consider an STL vector, as implemented by the GNU C++ Library:
(lldb) frame variable numbers -T
(std::vector<int>) numbers = {
(std::_Vector_base<int, std::allocator<int> >) std::_Vector_base<int, std::allocator<int> > = {
(std::_Vector_base<int, std::allocator&tl;int> >::_Vector_impl) _M_impl = {
(int *) _M_start = 0x00000001001008a0
(int *) _M_finish = 0x00000001001008a8
(int *) _M_end_of_storage = 0x00000001001008a8
}
}
}
Here, you can see how the type is implemented, and you can write a summary for that implementation
but that is not going to help you infer what items are actually stored in the vector.
What you would like to see is probably something like:
(lldb) frame variable numbers -T
(std::vector<int>) numbers = {
(int) [0] = 1
(int) [1] = 12
(int) [2] = 123
(int) [3] = 1234
}
Synthetic children are a way to get that result.
The feature is based upon the idea of providing a new set of children for a variable that replaces the ones
available by default through the debug information. In the example, we can use synthetic children to provide
the vector items as children for the std::vector object.
In order to create synthetic children, you need to provide a Python class that adheres to a given interface
(the word is italicized because Python has no explicit notion of interface, by that word we mean a given set of methods
must be implemented by the Python class):
class SyntheticChildrenProvider:
def __init__(self, valobj, internal_dict):
this call should initialize the Python object using valobj as the variable to provide synthetic children for
def num_children(self):
this call should return the number of children that you want your object to have
def get_child_index(self,name):
this call should return the index of the synthetic child whose name is given as argument
def get_child_at_index(self,index):
this call should return a new LLDB SBValue object representing the child at the index given as argument
def update(self):
this call should be used to update the internal state of this Python object whenever the state of the variables in LLDB changes.[1]
def has_children(self):
this call should return True if this object might have children, and False if this object can be guaranteed not to have children.[2]
[1] This method is optional. Also, it may optionally choose to return a value (starting with LLDB SVN rev153061/LLDB-134). If it returns a value, and that value is
True
, LLDB will be allowed to cache the children and the children count it previously obtained, and will not return to the provider class to ask. If nothing,
None
, or anything other than
True
is returned, LLDB will discard the cached information and ask. Regardless, whenever necessary LLDB will call
update
.
[2] This method is optional, and LLDB will honor it starting with SVN rev166495. While implementing it in terms of
num_children
is acceptable, implementors are encouraged to look for optimized coding alternatives whenever reasonable. For an example, see the
std::list
providers shipping with LLDB.
For examples of how synthetic children are created, you are encouraged to look at examples/synthetic in the LLDB trunk.
You may especially want to begin looking at this example to get
a feel for this feature, as it is a very easy and well commented example.
The design pattern consistently used in synthetic providers shipping with LLDB
is to use the
__init__
to store the SBValue instance as a part of
self
. The
update
function is then used
to perform the actual initialization.
Once a synthetic children provider is written, one must load it into LLDB before it can be used.
Currently, one can use the LLDB script
command to type Python code interactively,
or use the command script import fileName
command to load Python code from a Python module
(ordinary rules apply to importing modules this way). A third option is to type the code for
the provider class interactively while adding it.
For example, let's pretend we have a class Foo
for which a synthetic children provider class
Foo_Provider
is available, in a Python module contained in file ~/Foo_Tools.py
. The following interaction
sets Foo_Provider
as a synthetic children provider in LLDB:
(lldb) command script import ~/Foo_Tools.py
(lldb) type synthetic add Foo --python-class Foo_Tools.Foo_Provider
|
(lldb) frame variable a_foo
(Foo) a_foo = {
x = 1
y = "Hello world"
}
Currently, in LLDB top of tree, synthetic children providers are enabled for
std::vector<T>
, std::list<T>
and std::map<K,V>
both in the version provided by libstdcpp and by libcxx.
Synthetic children extend summary strings by enabling a new special variable: ${svar
.
This symbol tells LLDB to refer expression paths to the
synthetic children instead of the real ones. For instance,
(lldb) type summary add --expand -x "std::vector<" --summary-string "${svar%#} items"
|
(lldb) frame variable numbers
(std::vector<int>) numbers = 4 items {
(int) [0] = 1
(int) [1] = 12
(int) [2] = 123
(int) [3] = 1234
}
In some cases, if LLDB is unable to use the real object to get a child specified in an expression path, it will automatically refer to the
synthetic children. While in summaries it is best to always use ${svar
to make your intentions clearer, interactive debugging
can benefit from this behavior, as in:
(lldb) frame variable numbers[0] numbers[1]
(int) numbers[0] = 1
(int) numbers[1] = 12
Unlike many other visualization features, however, the access to synthetic children only works when using frame variable
, and is
not supported in expression
:
(lldb) expression numbers[0]
Error [IRForTarget]: Call to a function '_ZNSt33vector<int, std::allocator<int> >ixEm' that is not present in the target
error: Couldn't convert the expression to DWARF
The reason for this is that classes might have an overloaded operator []
, or other special provisions
and the expression
command ignores synthetic children when evaluating its arguments.
Filters are a solution to the display of complex classes.
At times, classes have many member variables but not all of these are actually
necessary for the user to see.
A filter will solve this issue by only letting the user see those member
variables he cares about. Of course, the equivalent of a filter can be implemented easily
using synthetic children, but a filter lets you get the job done without having to write
Python code.
For instance, if your class Foobar
has member variables named A
thru Z
, but you only need to see
the ones named B
, H
and Q
, you can define a filter:
(lldb) type filter add Foobar --child B --child H --child Q
|
(lldb) frame variable a_foobar
(Foobar) a_foobar = {
(int) B = 1
(char) H = 'H'
(std::string) Q = "Hello world"
}
When doing Objective-C development, you may notice that some of your variables
come out as of type id
(for instance, items extracted from NSArray
).
While this does not influence the ability of the runtime to send messages to them, it could make it impossible for LLDB
to determine the actual formatters for that object, given its type-based algorithm.
The debugger, however, can dynamically discover the type of an Objective-C
variable, much like the runtime itself does when invoking a selector. In order
to let LLDB do that, however, a special option to frame variable
is
required: --dynamic-type
.
--dynamic-type
can have one of three values:
no-dynamic-values
: the default, prevents dynamic type discovery
no-run-target
: enables dynamic type discovery as long as running
code on the target is not required
run-target
: enables code execution on the target in order to perform
dynamic type discovery
If you specify a value of either no-run-target
or run-target
,
LLDB will detect the dynamic type of your variables and show the appropriate formatters
for them. As an example:
(lldb) frame variable ns_string --dynamic-type no-run-target --show-types
|
(id, dynamic type: __NSCFString) ns_string = 0x00000001001183d0 @"An NSString saying hello world"
Because LLDB uses a detection algorithm that does not need to invoke any functions
on the target process, no-run-target
is enough for this to work.
As a final sidenote on this, LLDB is currently able to provide a summary string for NSString
that shows the content of the string, without requiring you to run code on the target
process. This features requires you to enable the AppKit category (see below for details). The
Python code for this formatter is at
CFString.py (the script is well commented, but intricate and might not be obvious, lacking
working experience with Cocoa and the LLDB API).
Categories are a way to group related formatters. For instance, LLDB itself groups
the formatters for the C++ STL objects in a category named gnu-libstdc++
.
Basically, categories act like containers in which to store formatters for a same library
or OS release.
By default, several categories are created in LLDB:
default
: this is the category where every formatter ends up, unless another category is specified
objc
: formatters for basic and common Objective-C types that do not specifically depend on Mac OS X
gnu-libstdc++
: formatters for std::string, std::vector, std::list and std::map as implemented by libstdcpp
libcxx
: formatters for std::string, std::vector, std::list and std::map as implemented by libcxx
system
: truly basic types for which a formatter is required
AppKit
: Cocoa classes
CoreFoundation
: CF classes
CoreGraphics
: CG classes
CoreServices
: CS classes
VectorTypes
: compact display for several vector types
If you want to use a custom category for your formatters, all the
type ... add
(except for
type format add
),
provide a
--category
(
-w
) option, that names the category to add the formatter to.
To delete the formatter, you then have to specify the correct category.
Categories can be in one of two states: enabled and disabled. A category is initially disabled,
and can be enabled using the type category enable
command. To disable an enabled category,
the command to use is type category disable
.
The order in which categories are enabled or disabled
is significant, in that LLDB uses that order when looking for formatters. Therefore, when you enable a category, it becomes
the second one to be searched (after default
, which always stays on top of the list). The default categories are enabled in such a way that the search order is:
- default
- objc
- CoreFoundation
- AppKit
- CoreServices
- CoreGraphics
- gnu-libstdc++
- libcxx
- VectorTypes
- system
As said, gnu-libstdc++
and libcxx
contain formatters for C++ STL
data types. system
contains formatters for char*
and char[]
, which reflect the behavior
of older versions of LLDB which had built-in formatters for these types. Because now these are formatters, you can even
replace them with your own if so you wish.
There is no special command to create a category. When you place a formatter in a category, if that category does not
exist, it is automatically created. For instance,
(lldb) type summary add Foobar --summary-string "a foobar" --category newcategory
|
automatically creates a (disabled) category named newcategory.
Another way to create a new (empty) category, is to enable it, as in:
(lldb) type category enable newcategory
|
However, in this case LLDB warns you that enabling an empty category has no effect. If you add formatters to the
category after enabling it, they will be honored. But an empty category per se does not change the way any
type is displayed. The reason the debugger warns you is that enabling an empty category might be a typo, and you
effectively wanted to enable a similarly-named but not-empty category.
While the rules for finding an appropriate format for a
type are relatively simple (just go through typedef
hierarchies), searching other formatters goes through
a rather intricate set of rules. Namely, what happens is that LLDB
starts looking in each enabled category, according to the order in which
they were enabled (latest enabled first). In each category, LLDB does
the following:
- If there is a formatter for the type of the variable,
use it
- If this object is a pointer, and there is a formatter
for the pointee type that does not skip pointers, use
it
- If this object is a reference, and there is a
formatter for the referred type that does not skip
references, use it
- If this object is an Objective-C class and dynamic types are enabled,
look for a formatter for the dynamic type of the object. If dynamic types are disabled,
or the search failed, look for a formatter for the declared type of the object
- If this object's type is a typedef, go through
typedef hierarchy (LLDB might not be able to do this if
the compiler has not emitted enough information. If the
required information to traverse typedef hierarchies is
missing, type cascading will not work. The
clang compiler,
part of the LLVM project, emits the correct debugging
information for LLDB to cascade). If at any level of the hierarchy
there is a valid formatter that can cascade, use it.
- If everything has failed, repeat the above search,
looking for regular expressions instead of exact
matches
If any of those attempts returned a valid formatter to be used,
that one is used, and the search is terminated (without going to look
in other categories). If nothing was found in the current category, the next
enabled category is scanned according to the same algorithm. If there are no
more enabled categories, the search has failed.
Warning: previous versions of LLDB defined cascading to mean
not only going through typedef chains, but also through inheritance chains.
This feature has been removed since it significantly degrades performance.
You need to set up your formatters for every type in inheritance chains to which
you want the formatter to apply.