[lldb-dev] lldb: how can I make it reliably scriptable?
Greg Clayton
gclayton at apple.com
Fri Feb 20 12:53:31 PST 2015
I found the race condition. Seems Process::LoadCore() doesn't ensure the target is stopped before it returns:
Crashed Thread: 7
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: EXC_I386_GPFLT
Thread 0:: Dispatch queue: com.apple.main-thread
0 _lldb.so 0x000000010df4d161 std::__1::weak_ptr<lldb_private::Process>::lock() const + 1
1 _lldb.so 0x000000010fcc1d63 lldb_private::UnwindLLDB::DoGetFrameInfoAtIndex(unsigned int, unsigned long long&, unsigned long long&) + 75
2 _lldb.so 0x000000010fd88c8b lldb_private::Unwind::GetFrameInfoAtIndex(unsigned int, unsigned long long&, unsigned long long&) + 61
3 _lldb.so 0x000000010fd87766 lldb_private::StackFrameList::GetFramesUpTo(unsigned int) + 286
4 _lldb.so 0x000000010fd882c1 lldb_private::StackFrameList::GetFrameAtIndex(unsigned int) + 185
5 _lldb.so 0x000000010fd63060 lldb_private::Thread::GetSelectedFrame() + 48
6 _lldb.so 0x000000010df4008b lldb::SBThread::GetSelectedFrame() + 203
7 _lldb.so 0x000000010dff19e5 _wrap_SBThread_GetSelectedFrame(_object*, _object*) + 128
8 org.python.python 0x000000010ce7c7c9 PyEval_EvalFrameEx + 14387
9 org.python.python 0x000000010ce7f60e 0x10cdf5000 + 566798
10 org.python.python 0x000000010ce7c3e3 PyEval_EvalFrameEx + 13389
11 org.python.python 0x000000010ce7f60e 0x10cdf5000 + 566798
12 org.python.python 0x000000010ce7c3e3 PyEval_EvalFrameEx + 13389
13 org.python.python 0x000000010ce7f60e 0x10cdf5000 + 566798
14 org.python.python 0x000000010ce7c3e3 PyEval_EvalFrameEx + 13389
15 org.python.python 0x000000010ce78d62 PyEval_EvalCodeEx + 1413
16 org.python.python 0x000000010ce787d7 PyEval_EvalCode + 54
17 org.python.python 0x000000010ce987bd 0x10cdf5000 + 669629
18 org.python.python 0x000000010ce98860 PyRun_FileExFlags + 133
19 org.python.python 0x000000010ce983fd PyRun_SimpleFileExFlags + 769
20 org.python.python 0x000000010cea9b23 Py_Main + 3051
21 libdyld.dylib 0x00007fff87fa25c9 start + 1
Thread 7 Crashed:
0 _lldb.so 0x000000010df4d17c std::__1::weak_ptr<lldb_private::Process>::lock() const + 28
1 _lldb.so 0x000000010fcc1d63 lldb_private::UnwindLLDB::DoGetFrameInfoAtIndex(unsigned int, unsigned long long&, unsigned long long&) + 75
2 _lldb.so 0x000000010fd88c8b lldb_private::Unwind::GetFrameInfoAtIndex(unsigned int, unsigned long long&, unsigned long long&) + 61
3 _lldb.so 0x000000010fd8795e lldb_private::StackFrameList::GetFramesUpTo(unsigned int) + 790
4 _lldb.so 0x000000010fd87246 lldb_private::StackFrameList::ResetCurrentInlinedDepth() + 46
5 _lldb.so 0x000000010fda1bcf lldb_private::Thread::ShouldStop(lldb_private::Event*) + 719
6 _lldb.so 0x000000010fda7790 lldb_private::ThreadList::ShouldStop(lldb_private::Event*) + 488
7 _lldb.so 0x000000010fd77f19 lldb_private::Process::ShouldBroadcastEvent(lldb_private::Event*) + 379
8 _lldb.so 0x000000010fd754c5 lldb_private::Process::HandlePrivateEvent(std::__1::shared_ptr<lldb_private::Event>&) + 365
9 _lldb.so 0x000000010fd7865b lldb_private::Process::RunPrivateStateThread() + 511
10 _lldb.so 0x000000010fd7810f lldb_private::Process::PrivateStateThread(void*) + 9
11 libsystem_pthread.dylib 0x00007fff81357268 _pthread_body + 131
12 libsystem_pthread.dylib 0x00007fff813571e5 _pthread_start + 176
13 libsystem_pthread.dylib 0x00007fff8135541d thread_start + 13
Thread 7 crashed with X86 Thread State (64-bit):
rax: 0x0000000000000001 rbx: 0x000000012a2ce798 rcx: 0x0000000000000000 rdx: 0x0000000000000000
rdi: 0x000000012a2ce798 rsi: 0x10000000000000f0 rbp: 0x000000012a2ce780 rsp: 0x000000012a2ce770
r8: 0x0000000000000000 r9: 0x000000012a2cf000 r10: 0x00007f8fca82f400 r11: 0x00007f8fca82fdd0
r12: 0x0000000000000000 r13: 0x00007f8fc951b9a0 r14: 0x10000000000000f0 r15: 0x000000012a2cea10
rip: 0x000000010df4d17c rfl: 0x0000000000010206 cr2: 0x000000010fea75f8
The issue is we have a read/write lock that controls when stuff can be done to a process. Multiple threads can request that the run lock stay stopped, and only one can request that it runs. So the problem is we call lldb::SBThread::GetSelectedFrame() on the main thread while the private state thread is still finding out that the process has stopped. lldb::SBThread::GetSelectedFrame() tries to get the run lock and keep the process stopped, and it succeeds because LoadCore wasn't synchronously making sure the core file was ready to be played with.
So the solution is to make sure that SBTarget::LoadCore() is a synchronous call that has the target stopped _before_ it returns. Then the process will be setup and ready to allow requests.
A work around for now is to call time.sleep(1) after calling LoadCore() to give the Process::PrivateStateThread() time to get settled. Other race conditions could cause the run lock to no be acquired:
lldb::SBFrame
SBThread::GetSelectedFrame ()
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
SBFrame sb_frame;
StackFrameSP frame_sp;
Mutex::Locker api_locker;
ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker);
if (exe_ctx.HasThreadScope())
{
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock()))
{
}
else
{
if (log)
log->Printf ("SBThread(%p)::GetSelectedFrame() => error: process is running",
static_cast<void*>(exe_ctx.GetThreadPtr()));
}
And an error would be returned and an empty SBFrame would be returned.
I'll have a fix committed shortly.
Greg
> On Feb 20, 2015, at 11:18 AM, Paul Smith <paul at mad-scientist.net> wrote:
>
> #!/usr/bin/env python
>
> import lldb
> import sys
>
> def load_core(debugger, exe, core):
> """
>
> @param exe: Path to the executable binary
> @param core: Path to the core
> @rtype: SBProcess
> @return:
> """
> target = debugger.CreateTargetWithFileAndArch(exe, lldb.LLDB_ARCH_DEFAULT)
> """ @type : SBTarget """
> if target:
> process = target.LoadCore(core)
> """ @type : SBProcess """
> if process:
> return process
>
> raise Exception("Could not load core")
> raise Exception("Could not create target")
>
>
> def print_version(process):
> """
> @type process: SBProcess
> """
> thread = process.GetSelectedThread()
> """ @type : SBThread """
> if thread:
> frame = thread.GetSelectedFrame()
> """ @type : SBFrame """
> if frame:
> version = frame.EvaluateExpression('globals->version.data')
> """ @type : SBValue """
> if version:
> err = lldb.SBError()
> version_string = process.ReadCStringFromMemory(int(version.GetValue(), 16), 512, err)
> if err.Success():
> print("*** Version: " + version_string)
> else:
> print("*** Error extracting version")
> print("")
> return
>
> raise Exception("Invalid expression result, can't extract version")
> raise Exception("Invalid frame, can't extract version")
> raise Exception("Invalid thread, can't extract version")
>
>
> def print_all_backtraces(process):
> """
> @type process: SBProcess
> """
> print("*** All Threads Backtrace:")
> for thread in reversed(process.threads):
> if thread:
> print_backtrace(thread)
> else:
> print("ERROR: Failed to print backtrace for a thread")
>
>
> def print_backtrace(thread, include_label=False):
> """
> @type thread: SBThread
> """
> if include_label:
> print("*** Backtrace:")
>
> print("Thread %d (core thread %d):" % (thread.GetIndexID(), thread.GetThreadID()))
> for frame in thread:
> if frame:
> print(frame)
> else:
> print("ERROR: Failed to print a frame")
>
> print("")
>
>
> def print_environment(process):
> """
> @type process: SBProcess
> """
> print("*** Environment:")
> print(' ' + '\n '.join(read_null_term_array(process, 'globals->environment->envp')))
> print("")
>
>
> def read_null_term_array(process, expr, max_length=1000):
> """
> @type process: SBProcess
> """
> thread = process.GetSelectedThread()
> """ @type : SBThread """
> if thread:
> frame = thread.GetSelectedFrame()
> """ @type : SBFrame """
> if frame:
> array_value = frame.EvaluateExpression(expr)
> """ @type : SBValue """
> if array_value:
> array_address = array_value.GetValueAsUnsigned()
> idx = 0
> ptr_err = lldb.SBError()
> array_data = []
> for i in xrange(0, max_length):
> element_ptr = process.ReadPointerFromMemory(array_address + idx * array_value.GetByteSize(),
> ptr_err)
> if ptr_err.Success():
> if element_ptr == 0:
> break
>
> val_err = lldb.SBError()
> element = process.ReadCStringFromMemory(element_ptr, 1024*10, val_err)
> if val_err.Success():
> array_data.append(element)
> else:
> print("ERROR: Unable to read value at address %s" % (str(element_ptr)))
>
> idx += 1
> else:
> print("ERROR: Unable to read pointer at address %s" %
> (str(array_address + idx * array_value.GetByteSize()),))
> break
>
> return array_data
>
> raise Exception("Unable to read array address for %s" % (expr,))
> raise Exception("Invalid frame for %s" % (expr,))
> raise Exception("Invalid thread for %s" % (expr,))
>
>
> def main():
> if len(sys.argv) != 3:
> print("usage: python %s core_file exe_file" % (sys.argv[0],))
> exit(1)
>
> core = sys.argv[1]
> exe = sys.argv[2]
>
> debugger = lldb.SBDebugger.Create()
> """ @type : SBDebugger """
>
> process = load_core(debugger, exe, core)
> if process:
> print_version(process)
> print_backtrace(process.GetSelectedThread(), True)
> print_all_backtraces(process)
> print_environment(process)
> else:
> print("Failed to debug core " + core)
>
> if __name__ == '__main__':
> main()
More information about the lldb-dev
mailing list