[lldb-dev] lldb: how can I make it reliably scriptable?

Greg Clayton gclayton at apple.com
Fri Feb 20 13:02:35 PST 2015


Fixed with:

% svn commit source/Target/Process.cpp
Sending        source/Target/Process.cpp
Transmitting file data .
Committed revision 230060.

Please download the top of tree sources, build the Release build and make sure it works for you. Thanks of the info so that we could track down and fix this issue.

Greg Clayton

> On Feb 20, 2015, at 12:53 PM, Greg Clayton <gclayton at apple.com> wrote:
> 
> 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()
> 
> 
> _______________________________________________
> lldb-dev mailing list
> lldb-dev at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/lldb-dev





More information about the lldb-dev mailing list