Hello, Jim

The code I was debugging in described case is GCD-related stuff so the issue 
with locks could be the cause. But issue is gone once we select thread/frame - 
that's weird.

On Jun 11, 2013, at 3:38 AM, [email protected] wrote:

> One of the big dangers with evaluating expressions that run code in the 
> debugee is that the code you run might try to acquire some locked resource 
> that is held by another thread in the program.  That will cause the 
> expression evaluation to deadlock.  
> 
> One solution is to run the expression evaluation with a timeout, and if it 
> takes too long, cancel the expression, clean up its stack and return an 
> error.  However, that can also be dangerous, for instance if the expression 
> you run successfully acquires lock A, tries to get lock B, but that is held 
> on another thread, so it deadlocks.  If you cancel the expression evaluation 
> at that point, you will leave lock A stranded, and your program going to 
> grind to a halt as different threads all try to get that resource.
> 
> The obvious solution to this problem is to run all threads when you do, 
> expression evaluation.  However, that's not what most users want, they would 
> find it disconcerting to have expression evaluation on one thread to cause 
> some other thread to make progress.
> 
> lldb's solution to this problem is that when we run expressions, we first try 
> running just the thread on which the expression is evaluated, but with some 
> timeout (which is user settable.)  If the expression evaluation times out on 
> the one thread, and the "run_others" parameter to RunThreadPlan is passed in 
> as true, we will interrupt the evaluation, and then restart with all threads 
> running.  So it is very possible that expression evaluation could cause 
> another thread to get a chance to execute, which could in turn hit a 
> breakpoint, or crash, or whatever else a thread might do while executing...
> 
> Short of the ability to track all locks in the system (a capability which 
> most OS'es don't provide), this is the safest way around this problem.  It 
> will fail if the expression you are running tries to acquire a non-recursive 
> lock held on the thread which runs the code.  We could work around even that 
> if we made up a debugger thread in the debugee for running expressions, 
> though that would fail if any of the code in the expression tried to access 
> thread specific data.  I don't know whether it is possible to fake a thread's 
> thread specific data so it looks like it belongs to another thread.  If 
> that's possible, we could probably make that work.  OTOH, this particular 
> problem doesn't seem to occur that often.
> 
> Anyway, again without being able to play around with the locks in a program 
> directly, I can't see a way to run general expressions that might acquire 
> locked resources without allowing all threads to run, at least as a fallback.

What does it mean for debugger's front-end? What shall we see after evaluation? 
Threads will stop in different frames and do we need to refetch all info about 
all threads/frames/variables to display correct data?

Thank you Jim, for such a detailed clarification about LLDB internals. I'll try 
to investigate the issue deeper and write back if I have some questions.

> 
> Jim
> 
> On Jun 10, 2013, at 4:18 PM, Andrey Zaytsev <[email protected]> 
> wrote:
> 
>> Hi Jim,
>> 
>> You're right that it uses correct context of frame specified. But as far as 
>> I can see, real execution is running in other thread. May it cause something 
>> like EXC_ARM_BREAKPOINT?
>> And what run_others parameter of Process::RunThreadPlan() does?
>> 
>> 
>> On Jun 11, 2013, at 2:07 AM, [email protected] wrote:
>> 
>>> That is not the intention of the design, and also not what I see:
>>> 
>>> (lldb) source list -f foo.c -l 1
>>>  1          #include <stdio.h>
>>>  2          
>>>  3          int
>>>  4          foo (int input)
>>>  5          {
>>>  6            int local_var = input * 5;
>>>  7            printf ("Local var: %d.\n", local_var);
>>>  8            return local_var;
>>>  9          }
>>>  10         
>>>  11         int
>>> (lldb) 
>>>  12         main (int argc, char **argv)
>>>  13         {
>>>  14           int local_var = argc;
>>>  15           printf ("Foo returns: %d.\n", foo (local_var));
>>>  16           return 1;
>>>  17         }
>>> (lldb) b s -p "return local_var"
>>> Breakpoint 1: where = foo`foo + 41 at foo.c:8, address = 0x0000000100000ef9
>>> (lldb) run
>>> Process 98518 launched: '/private/tmp/foo' (x86_64)
>>> Local var: 5.
>>> Process 98518 stopped
>>> * thread #1: tid = 0x3663ec, function: foo , stop reason = breakpoint 1.1
>>>   frame #0: 0x0000000100000ef9 foo`foo at foo.c:8
>>>  5          {
>>>  6            int local_var = input * 5;
>>>  7            printf ("Local var: %d.\n", local_var);
>>> -> 8          return local_var;
>>>  9          }
>>>  10         
>>>  11         int
>>> 
>>> So I am in foo, there is a "local_var" in frame 0, where its value is 5, 
>>> and in frame 1 where its value is 1.  So I do:
>>> 
>>> (lldb) script
>>> Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>>>>> frame_0 = lldb.thread.GetFrameAtIndex(0)
>>>>>> print frame_0.EvaluateExpression("local_var")
>>> (int) $0 = 5
>>>>>> frame_1 = lldb.thread.GetFrameAtIndex(1)
>>>>>> print frame_1.EvaluateExpression("local_var")
>>> (int) $1 = 1
>>>>>> quit()
>>> 
>>> SBFrame::EvaluateExpression, at least in this example, works on the SBFrame 
>>> that you call it on.
>>> 
>>> Note, here I am using lldb.thread, which is INDEED the command 
>>> interpreter's currently selected thread.  I just did that to shorten the 
>>> example.  If you wanted to get the process/thread/frame independent of 
>>> entities selected in the Command Interpreter, then you should use the 
>>> accessors on SBDebugger, SBTarget, SBProcess and SBThread.  The Python docs 
>>> explicitly state that you should NOT use lldb.process/lldb.thread or 
>>> lldb.frame anywhere but in the interactive script interpreter.  They aren't 
>>> even guaranteed to be set in other contexts.
>>> 
>>> Anyway, if you have some example (not using 
>>> lldb.process/lldb.thread/lldb.frame) where the EvaluateExpression on a 
>>> SBFrame object is evaluating the expression is the context of some other 
>>> frame, please file a bug and we will take a look.
>>> 
>>> Jim
>>> 
>>> 
>>> On Jun 10, 2013, at 1:46 PM, Andrey Zaytsev <[email protected]> 
>>> wrote:
>>> 
>>>> Hello everyone.
>>>> 
>>>> I just realised thing which leads to crash of debuggee in some cases. We 
>>>> had a bug in our tracker: http://youtrack.jetbrains.com/issue/OC-7389
>>>> 
>>>> We have some system of value renderers. Each renderer(e.g for 
>>>> NSCollections) evaluates some stuff to get info about collection elements. 
>>>> So does a number of Summary and Synthetic Providers too.
>>>> 
>>>> In SB-API it is implemented with EvaluateExpression function. One of the 
>>>> ways we can evaluate expression is to call 
>>>> lldb::SBFrame::EvaluateExpression() member function. Actually it performs 
>>>> execution on selected thread/frame. But not on the frame we call 
>>>> EvaluateExpression function on. It's very not obvious and in my opinion 
>>>> buggy. Usage of API in this way leads to crashes of debuggee process like 
>>>> in the ticket above. So crashes not only attempt to evaluate expression 
>>>> but attempt to get local variables with dynamic types if it executes 
>>>> target as well.
>>>> 
>>>> So workaround for us was to select specified thread/frame before doing 
>>>> evaluation. So does interpreter's expr command.
>>>> 
>>>> 
>>>> -- 
>>>> Andrey Zaytsev
>>>> Software Developer
>>>> JetBrains, Inc
>>>> http://www.jetbrains.com/
>>>> "Develop with pleasure!"
>>>> 
>>>> _______________________________________________
>>>> lldb-dev mailing list
>>>> [email protected]
>>>> http://lists.cs.uiuc.edu/mailman/listinfo/lldb-dev
> 

_______________________________________________
lldb-dev mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/lldb-dev

Reply via email to