using lldb's backtrace as a library

I’d like to use lldb’s backtrace as a library as opposed to using lldb program, so that I can generate good stack traces inside a C/C++/D program without having to spawn a separate process that would call ‘lldb -p pid’.

How should I proceed? Or, what is the relevant function call?

Actually I just realized I’ve posted a related question before: “process calling lldb to symbolicate its own backtrace”.
However it didn’t seem so easy to do judging from the thread. Has anything changed since then? There should be a simple way to do this important task.

This should get you started:

You will link against LLDB.framework on MacOSX, or liblldb.so on unix, or liblldb.dll on windows.

The you would do:

#if defined(__APPLE__)
#include <LLDB/LLDB.h>
#else
#include "lldb/SBDefines.h"
#include "lldb/SBAddress.h"
#include "lldb/SBBlock.h"
#include "lldb/SBBreakpoint.h"
#include "lldb/SBBreakpointLocation.h"
#include "lldb/SBBroadcaster.h"
#include "lldb/SBCommandInterpreter.h"
#include "lldb/SBCommandReturnObject.h"
#include "lldb/SBCommunication.h"
#include "lldb/SBCompileUnit.h"
#include "lldb/SBData.h"
#include "lldb/SBDebugger.h"
#include "lldb/SBDeclaration.h"
#include "lldb/SBError.h"
#include "lldb/SBEvent.h"
#include "lldb/SBFileSpec.h"
#include "lldb/SBFrame.h"
#include "lldb/SBFunction.h"
#include "lldb/SBHostOS.h"
#include "lldb/SBInstruction.h"
#include "lldb/SBInstructionList.h"
#include "lldb/SBLineEntry.h"
#include "lldb/SBListener.h"
#include "lldb/SBModule.h"
#include "lldb/SBProcess.h"
#include "lldb/SBQueue.h"
#include "lldb/SBQueueItem.h"
#include "lldb/SBSourceManager.h"
#include "lldb/SBStream.h"
#include "lldb/SBStringList.h"
#include "lldb/SBSymbol.h"
#include "lldb/SBSymbolContext.h"
#include "lldb/SBTarget.h"
#include "lldb/SBThread.h"
#include "lldb/SBType.h"
#include "lldb/SBValue.h"
#include "lldb/SBValueList.h"
#endif

using namespace lldb;

int main(int argc, const char * argv[])
{
    if (argc != 2)
    {
        printf ("usage: backtrace <pid>\n");
        exit(0);
    }
    
    SBDebugger::Initialize();
    const bool source_init_files = true;
    SBDebugger debugger = SBDebugger::Create(source_init_files);
    debugger.SetAsync (false); // Set debugger to synchronous mode
    const char *filename = NULL; // Fill this in if you know the filename, else leave NULL
    const char *target_triple = "x86_64";
    const char *platform_name = NULL; // Leave NULL
    bool add_dependent_modules = true; //
    lldb::SBError error;
    
    lldb::pid_t pid = (int)strtol(argv[1], (char **)NULL, 10);
    SBAttachInfo attach_info (pid);
    
    SBTarget target = debugger.CreateTarget (filename, target_triple, platform_name, add_dependent_modules, error);
    
    SBStream stream;
    stream.RedirectToFileHandle(stdout, false);
    
    if (target.IsValid())
    {
        SBProcess process = target.Attach (attach_info, error);
        if (process.IsValid())
        {
            process.GetDescription(stream);
            stream.Printf("\n");
            uint32_t num_threads = process.GetNumThreads();
            for (uint32_t thread_idx=0; thread_idx<num_threads; ++thread_idx)
            {
                SBThread thread = process.GetThreadAtIndex(thread_idx);
                if (thread.IsValid())
                {
                    thread.GetDescription(stream);
                    stream.Printf("\n");
                    uint32_t num_frames = thread.GetNumFrames();
                    for (uint32_t frame_idx=0; frame_idx<num_frames; ++frame_idx)
                    {
                        SBFrame frame = thread.GetFrameAtIndex(frame_idx);
                        frame.GetDescription(stream);
                    }
                }
            }
            
            process.Detach();
        }
        else
        {
            fprintf(stderr, "error: failed to attach to process: %s\n", error.GetCString());
        }
    }
    else
    {
        fprintf(stderr, "error: failed to create target: %s\n", error.GetCString());
    }
        
    SBDebugger::Terminate();
    return 0;
}

No, nothing has changed. lldb only operates in "control other process" mode, not in "observe other process" mode, and controlling yourself is a neat trick you might be able to manage, but would add a lot of complexity for no clear benefit for most of the usages of lldb.

On OSX, you can use CoreSymbolication to take a backtrace of yourself. There are likely similar facilities on other systems.

Jim

backtrace/backtrace_symbols /atos(osx)/addr2line(linux) is not a viable
alternative, as it doesn't give proper/accurate line numbers in many cases,
unlike what lldb provides.