[lldb-dev] Frame documentation

Greg Clayton gclayton at apple.com
Tue Dec 2 13:39:33 PST 2014


> On Nov 29, 2014, at 2:51 PM, Jose H <jose.francisco.hevia at gmail.com> wrote:
> 
> Hello,
> 
> Continuing with my GUI program, where can I find information or
> documentation about the frame format and what everything represents?
> 
> There is a lack of documentation in the code, it makes a lot of
> assumptions about what every thing means. I wonder where this
> assumptions come from.
> 
> For instance, what SBLineEntry means? The documentation lacks detail.
> 
> If I do:
> line_entry = frame.GetLineEntry();
> 
> then:
> uint32_t line = line_entry.GetLine();
> SBFileSpec source = line_entry.GetFileSpec();
> 
> I suppose line_enty represents the line entry of the frame in the
> source code, and source the source code original file.

When you take an address, like the frame's PC value, you can lookup its symbol context. A symbol context is represented by a SBSymbolContext object.

So you can get a frame:

SBFrame frame = thread.GetFrameAtIndex(0);

Now you can get its symbol context:

SBSymbolContext sc = frame.GetSymbolContext(what_to_get);

A symbol context is a context within the symbol and executable files. 

The symbol will come from a module:

lldb::SBModule module = sc.GetModule ();


The following information will be valid if you have debug info (DWARF) and you have debug info for the address that you are stopped at:

lldb::SBCompileUnit cu = sc.GetCompileUnit ();
lldb::SBFunction func = sc.GetFunction ();
lldb::SBBlock block = sc.GetBlock ();
lldb::SBLineEntry line_entry = sc.GetLineEntry ();

The following info will be valid if you have a symbol table in your executable:

lldb::SBSymbol symbol sc.GetSymbol ();

The symbol context represents all of the objects that represent the address that you looked up, in this case it will be the object that represent where you stopped (the frame PC). Each item may or may not be valid and each can be checked with a <obj>.IsValid() call. For example, if you don't have debug info for libc.so, you might end up with a SBSymbol object for "malloc" and a SBModule for "libc.so", and if you don't have debug info the cu, fun, block and line_entry will be invalid.


When getting a symbol context from a frame you can specify exactly what you would like to get from the symbol context by filling in "what_to_get" with a mask from the following bits:

    typedef enum SymbolContextItem
    {
        eSymbolContextTarget     = (1u << 0), ///< Set when \a target is requested from a query, or was located in query results
        eSymbolContextModule     = (1u << 1), ///< Set when \a module is requested from a query, or was located in query results
        eSymbolContextCompUnit   = (1u << 2), ///< Set when \a comp_unit is requested from a query, or was located in query results
        eSymbolContextFunction   = (1u << 3), ///< Set when \a function is requested from a query, or was located in query results
        eSymbolContextBlock      = (1u << 4), ///< Set when the deepest \a block is requested from a query, or was located in query results
        eSymbolContextLineEntry  = (1u << 5), ///< Set when \a line_entry is requested from a query, or was located in query results
        eSymbolContextSymbol     = (1u << 6), ///< Set when \a symbol is requested from a query, or was located in query results
        eSymbolContextEverything = ((eSymbolContextSymbol << 1) - 1u)  ///< Indicates to try and lookup everything up during a query.
    } SymbolContextItem;

LLDB will only fetch what you ask it to fetch and it will lazily parse debug info in order to achieve the results.

Most people just get everything from a frame:

SBSymbolContext sc = frame.GetSymbolContext(lldb:: eSymbolContextEverything);


The compile unit represents the source file that the frame is in. The function represents the concrete function (like "main").  The block is the deepest lexical block that the frame's PC is stopped within. These lexical blocks all contain variables and the SBBlock might represent an inlined function. Inlined functions are just blocks that provide additional info:

const char *inlined_name = block.GetInlinedName ();

You can also find the call site file and line:

    lldb::SBFileSpec
    SBBlock::GetInlinedCallSiteFile () const;

    uint32_t 
    SBBlock::GetInlinedCallSiteLine () const;

    uint32_t
    SBBlock::GetInlinedCallSiteColumn () const;

The block you are stopped in might be a lexical block within a function. 

Lets say you are in a a block that is inside an inlined version of std::vector<int>::push_back() within the function "main":

Something like:

/tmp/main.c:

1 int main()
2 {
3   std::vector<int> ints;
4   ints.push_back(12);
5 }

/usr/include/vector:


200 std::vector<T> {
201    void push_back(const T &t)
202    {
203        int x;
204        {       <<< frame's block points to this lexical block
205            int y;
206            ... <<< PC is stopped here
207        }
208    }
209 }

Your "block" would have no name and would represent the lexical block at line 204 in /usr/include/vector. 
The line table entry in "line_entry" would point to line 206 of /usr/include/vector.
The "func" would represent the "main" function.

So if you ask a frame what function name should represent it you would need to ask "block" to get its containing inlined block. So to find the actual name you want to show to the user, you can use code like:

SBBlock inline_block = block.GetContainingInlinedBlock();

const char *function_name = NULL;
if (inline_block.IsValid())
{
    // We have an inlined function
    function_name = inline_block.GetInlinedName()
}
else if (func.IsValid())
{
    // The frame is a concrete function
    function_name = func.GetName();
}
else if (symbol.IsValid()
{
    function_name = symbol.GetName();
}

Of course this is a very common thing to do to a frame, so this is already done for you with a helper function:

const char *function_name = frame.GetFunctionName();

The SBFrame will do all the work for you.

Inlined frames are all represented by new SBFrame objects, so in the above example, if we get the next frame:

SBFrame frame2 = thread.GetFrameAtIndex(1);

frame2 will have:

function = "main"
block = block from main.c:2 and this block will have no containing inlined block
line_entry = main.c:4


> 
> I could follow all this code with script and guess most of the
> meaning, but it is a lot of work just manually traversing all possible
> combinations to know what everything means.
> 
> Is there any better way?

Ask here, and we need to improve the documentation, that is for sure.
> 
> I want to travel around functions instead of code. I mean I want to
> display just a function and the line offset of the current program
> counter in source code with the start of the function as reference,
> probably pc-offset works with this.

For this you will do the following for each frame:

uint32_t num_frames = thread.GetNumFrames();
for (i = 0...num_frames)
{
    SBFrame frame = thread.GetFrameAtIndex(i);
    const char *name = frame.GetFunctionName();
    SBLineEntry line_entry = frame.GetLineEntry();
    // Display info from name and line_entry
    SBValueList variables = frame.GetVariables (true, // arguments
                  				true, // locals
				                true, // statics variables
                  				true, // only get variables that are in scope
				                use_dynamic); // Set to one of the values from lldb::DynamicValueType

}
> 
> Also I need to display all the variables of the frame, and to know the
> type of every variable, the address pointers point to and a fast way
> to access those pointers(I mean getting the real address, not the
> ascii representation).

These are all represented by the values in the "variables" list which is of type SBValueList.

for (v = 0...variables.GetSize())
{
    SBValue var = variables.GetValueAtIndex(v);
    const char *type_name = var.GetDisplayTypeName();
    const char *name = var.GetName();
    const char *value = var.GetValue();
    const char *summary = var.GetSummary();
    const char *location = var.GetLocation();
    // You will put these into a tree view of course and allow the entry to be expanded of the value has children
    const bool might_have_children = var.MightHaveChildren();

}

Later, you can get all child values of "var" by calling:
    for (c = 0...var.GetNumChildren())
    {
       SBValue child = var.GetChildAtIndex(c);
       ....
    }

Hopefully this explains all you need to do. Let me know if anything needs clarification.

Greg Clayton





More information about the lldb-dev mailing list