[lldb-dev] lldb: how can I make it reliably scriptable?
Greg Clayton
gclayton at apple.com
Fri Feb 20 13:53:42 PST 2015
One more fix:
% svn commit source/Target/Process.cpp
Sending source/Target/Process.cpp
Transmitting file data .
Committed revision 230066.
This ensures if you ask the process for its state, that it will respond with eStateStopped.
> On Feb 20, 2015, at 1:02 PM, Greg Clayton <gclayton at apple.com> wrote:
>
> 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
>
>
> _______________________________________________
> 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