[lldb-dev] Use lldb.so to create stack trace

Greg Clayton gclayton at apple.com
Tue Jul 28 09:47:36 PDT 2015


> On Jul 27, 2015, at 10:10 PM, Schlottke-Lakemper, Michael <m.schlottke-lakemper at aia.rwth-aachen.de> wrote:
> 
> Hi folks, 
> 
> We are looking for a way to use lldb.so to print out a stack trace programmatically. In our scientific simulation tool (written in C++) ,  we use a custom terminate function where we are already able to print stack traces in case of an abnormal abort, which uses some gcc-specific magic. 
> 
> My idea is to link our tool to lldb.so when compiling for debugging and to use the library to print a stack trace programmatically, including demangled names and correct line numbers. However, I don't really know how to start thus I am wondering
> 
> - has someone here done or seen something like this before? 

Not sure who has done anything like this.
> - is there a tutorial for linking to lldb.so and making use of it through C++? 

At the end of the email is an example of something that will attach to its parent process and iterate through all of the threads/frames.

> - would this also work when compiling with other compilers than clang? 

If the debug info is DWARF, then yes.
> 
> Any help or pointers to relevant projects/documentation etc is greatly appreciated! 

Let me know if this helps.

Code sample:


#include <stdint.h>
#include <stdlib.h>

#if defined(__APPLE__)
#include <LLDB/LLDB.h>
#else
#include "LLDB/SBBlock.h"
#include "LLDB/SBCompileUnit.h"
#include "LLDB/SBDebugger.h"
#include "LLDB/SBFrame.h"
#include "LLDB/SBFunction.h"
#include "LLDB/SBModule.h"
#include "LLDB/SBStream.h"
#include "LLDB/SBSymbol.h"
#include "LLDB/SBTarget.h"
#include "LLDB/SBThread.h"
#include "LLDB/SBProcess.h"
#endif

#include <string>

using namespace lldb;

int
main (int argc, char const *argv[])
{
    // Use a sentry object to properly initialize/terminate LLDB.
    SBDebugger::Initialize();
    
    SBDebugger debugger (SBDebugger::Create());
    
    // Create a debugger instance so we can create a target
    if (!debugger.IsValid())
    {
        fprintf (stderr, "error: failed to create a debugger object\n");
        exit(1);
    }
    
    SBError error;
    
    pid_t pid = getppid(); // Assuming parent process is what we want to backtrace
    SBAttachInfo attach_info(pid);
    
    const char *filename = nullptr;
    const char *target_triple = nullptr;
    const char *platform_name = nullptr;
    bool add_dependent_modules = false;
    SBTarget target = debugger.CreateTarget (filename, target_triple, platform_name, add_dependent_modules, error);
    
    SBStream strm;
    const bool append = false;
    strm.RedirectToFile ("/tmp/stack.txt", append);
    if (target.IsValid())
    {
        SBProcess process = target.Attach (attach_info, error);
        if (process.IsValid())
        {
            // Get the description of the process
            process.GetDescription(strm);
            
            const uint32_t num_threads = process.GetNumThreads();
            for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx)
            {
                lldb::SBThread thread = process.GetThreadAtIndex (thread_idx);
                if (thread.IsValid())
                {
                    // Do one of the following:
                    
                    // 1 - Get a description of the thread
                    thread.GetDescription(strm);
                    // 1 - END
                    
                    // 2 - Do things yourself by using the public API to look a the stop reason
                    lldb::StopReason thread_stop_reason = thread.GetStopReason();
                    switch (thread_stop_reason)
                    {
                    case eStopReasonInvalid:
                    case eStopReasonNone:    // Thread is stopped because other threads were stopped
                    case eStopReasonTrace:   // Thread completed a single instruction step
                        break;
                    case eStopReasonBreakpoint:
                        {
                            uint32_t idx = 0; 
                            uint64_t bp_id, bp_loc_id;
                            do
                            {
                                bp_id = thread.GetStopReasonDataAtIndex(idx++);
                                bp_loc_id = thread.GetStopReasonDataAtIndex(idx++);
                                SBBreakpoint bp = target.FindBreakpointByID(bp_id);
                                SBBreakpointLocation bp_loc = bp.FindLocationByID (bp_loc_id);
                                // Do something with "bp" if desired
                            } while (bp_id != 0);
                        }
                        break;
                    case eStopReasonWatchpoint:
                        {
                            const uint64_t wp_id = thread.GetStopReasonDataAtIndex(0);
                            SBWatchpoint wp = target.FindWatchpointByID(wp_id);
                            // Do something with "wp" if desired
                        }
                        break;
                    case eStopReasonSignal:
                        {
                            const uint64_t signo = thread.GetStopReasonDataAtIndex(0);
                            // Do something with "signo" if desired
                        }
                        break;
                        
                    case eStopReasonException:
                    case eStopReasonExec:
                    case eStopReasonPlanComplete:
                    case eStopReasonThreadExiting:
                    case eStopReasonInstrumentation:
                        break;
                    }
                    
                    char decription[1024];
                    
                    if (thread.GetStopDescription(description, sizeof(decription)))
                    {
                        // Do something with the stop description
                    }
                    // 2 - END
                    
                    // Now iterate through all of the frames in the thread
                    const uint32_t num_frames = thread.GetNumFrames();
                    for (uint32_t frame_idx = 0; frame_idx < num_frames; ++frame_idx)
                    {
                        SBFrame frame = thread.GetFrameAtIndex (frame_idx);
                        if (frame.IsValid())
                        {
                            // Do one of the following:
                            
                            // 1 - Let the frame describe itself
                            frame.GetDescription (strm);
                            // 1 - END
                            
                            // 2 - Do things manually using the SB API
                            lldb::SBModule module = frame.GetModule();
                            lldb::SBCompileUnit compile_unit = frame.GetCompileUnit();
                            lldb::SBFunction function = frame.GetFunction();
                            lldb::SBBlock block = frame.GetBlock();
                            lldb::SBLineEntry line_entry = frame.GetLineEntry();
                            lldb::SBSymbol symbol = frame.GetSymbol();
                            
                            // Now use the above objects
                            
                            // 2 - END
                        }
                    }
                }
            }
            
            process.Detach();
        }
    }
    
    SBDebugger::Terminate();

    return 0;
}




More information about the lldb-dev mailing list