Search code examples
cxcodelldb

lldb in xcode detects integer called I to be a complex number


I have a C code, within which an int I gets declared and initialized. When I'm debugging within xcode, if I try to print the value of I, xcode tries to find a complex number:

(lldb) p I
error: <lldb wrapper prefix>:43:31: expected unqualified-id
using $__lldb_local_vars::I;
                          ^
<user expression 3>:1760:11: expanded from here
#define I _Complex_I
      ^
<user expression 3>:7162:20: expanded from here
#define _Complex_I ( __extension__ 1.0iF )

When I try the same thing (stopping at the same exact line in the code) in the command line, without using xcode, it works fine:

(lldb) p I
(int) $0 = 56

I'm loading the following libraries:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

which shouldn't even include complex numbers, no? I definitely don't have a macro that defines I to be the complex variable. The one I run in xcode, I compile with the default xcode tools. The one I run in the command line, I use gcc. Is this the difference, somehow? Is xcode including more libraries than I ask it to? Why is this happening and how can I prevent it?

Edit: I should also add that the variable explorer in xcode shows the value of I correctly, as an integer.


Solution

  • $__lldb_local_vars is an artificial namespace that lldb injects into the wrapper it sets up for your expression before compilation so that clang can find the frame's local variables and their types. The problem comes as others have noted because we also run the preprocessor when compiling your expression, and your variable name collides with a preprocessor symbol in the expression context.

    Normally, debug information does not record macros at all, so you aren't seeing the complex.h version of I from your own use of it in your code. Rather, you are seeing the I macro because something has caused the Darwin module to be imported into lldb's expression context.

    That can happen in two ways, either because you explicitly asked for it by running:

    (lldb) expr @import Darwin
    

    or because you built this program with -fmodules and your code imported the Darwin module by inserting a statement like the above.

    Doing this by hand is a common trick explicitly to make #defines from the module visible to the expression parser. Since it is the visibility of the macro that is causing problems, then you will have to stop doing that if you want this expression to succeed.

    OTOH, if lldb is doing this because the debug information recorded that some part of you code imported this module, you can turn off the behavior by putting:

    settings set target.auto-import-clang-modules 0
    

    in your ~/.lldbinit and restarting your debug session.

    BTW, the p command (or the expression command that p is an alias for) evaluates the text you provide it as a regular expression using the language and in the context of the current frame, with as much access to symbols, defines and the like as lldb can provide. Most users also want to be able to access class information that might not be directly visible in the current frame, so it tends to cast as wide a net as possible looking for symbols and types in order to enable this.

    It is a very powerful feature, but as you are seeing sometimes the desire to provide this wide access for expressions can cause conflicting definitions. And anyway, it is way more powerful than needed just to view a local variable.

    lldb has another command: frame var (convenient alias v) that prints local variable values by directly accessing the memory pointed to by the debug information and presenting it using the type from the debug info. It supports a limited subset of C-like syntax for subelement reference; you can use * to dereference, . or -> and if the variable is an array [0] etc...

    So unless you really do need to run an expression (for instance to access a computed property or call another function), v will be faster and because its implementation is simpler and more direct, it will have less chance of subtle failures than p.

    If you also want to access the object definition of some ObjC or Swift local variable, the command vo or frame var -O will fetch the description of the local variable it finds using the v method.