[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