Debugging with Xcode Version 10.1 (10B61). Not possible to examine properties or variables. What am I missing?

I am enjoying Swift. I am a fan. Sadly, I began using the debugger and it is shockingly, embarrassingly bad.

It is simply not possible to observe properties or variables. The most basic use of a debugger. Not usable.

Can someone help me understand how to do proper debugging here?

Thank you,

You'll have to be a little more specific. What exact errors are you seeing? Is it in the debug console or in the variables view?

Example of mine. I stopped on line 68 and taken two steps. Sorry for not providing a simple, reproducible example, but in my experience printing stuff with a debugger is very hit and miss with no recognizable patterns, except one:

DispatchQueue.main.async { [weak self] in
    guard let self = self else { return }
    // stuff breaks here

Yes, I've seen similar issues. Despite very specific claims in the debugging sessions at WWDC '18, Xcode / lldb still has huge issues with breakpoints inside Swift closures and being unable to find captured values. However, I have found that printing all of the frame's variables (frame variable) will get me much of the same info, I just have to dig through a lot of irrelevant stuff.

But yes, the unreliable debugging experience is one of the biggest quality of life issues for Swift right now.

This specific issue with rebinding self is known. It's being tracked here, although there isn't currently any progress. [SR-6156] LLDB: warning: initialization of variable '$__lldb_error_result' · Issue #4384 · apple/llvm-project · GitHub

1 Like

Yep, we know we have issues when self is being re-bound.

Your first example is more mysterious. If you are unable to provide us with a reproducer, could you try to capture some logs:

  • Quit Xcode
  • Add "log enable -f /tmp/lldb.log lldb types" and "log enable -f /tmp/lldb.log lldb expr" to you ~/.lldbinit file
  • Start Xcode and reproduce the issue (with a minimal number of steps if possible, so that the logs cover basically only the failed interaction)
  • Attach the /tmp/lldb.log file to a bug report, either on or in radar.
  • remove the lines from your .lldbinit file.


I just tried that on my example from earlier and unfortunately it gives me 5406 lines of irrelevant stuff followed by (NSAttributedString) messageText = <variable not available> :(
At least I have one more tool to use when debugger breaks! :)

Will do! (47417395 on apple bug reporter) Should I capture more examples of it happening in different places, or is one usually enough?

In our codebase we ended up replacing all guard self = self instances with guard this = self, we'll change them back if this bug gets fixed.

To clarify, I am not trying to do anything esoteric here. Just your most basic inspection of the values of properties, variables, structs, etc. Here is a screenshot for context:

We are looking at the body of an MTKView subclass. I am setting the value of various properties. Again, nothing fancy to see here.

In the panel - below left - where I should be able to inspect these values they are nowhere to be found. Just random hex values for memory addresses. WTF?!?!?

How on earth was this allowed to happen? How can you release a programming language without a suitable debugger? SMH.

Well, your variables view looks normal. and as you say the hex values (which are not random) are addresses. Addresses are often useful in debugging (even Swift). In this case most of the types you in your screenshot are Objective-C and in thus the address is literally the value of the variable.

It's unclear what you don't like about what you are seeing and/or what you are not seeing. If there's a property or local variable you don't see, what happens when you type "frame variable " in the console? Same thing with "p "? If there's any kind of error, can you provide us with the logs I requested above? Or better, a reproducer for your issue?

Hi Fredric,
For comparison let me show you what the Chrome developer tools - and any modern IDE except Swift in Xcode - provides:

Above is a screenshot of a breakpoint set in a Javascript app. On the left is the source code and blue breakpoint. On the left in the "Scope" section are locally scopes items. Here I display the contents of the object named config. This is what a modern, debugger looks like in 2019.

Staring at hex values in a debugger in 2019 is about as archaic and primitive as it gets.


The only difference I can see between your javascript and Xcode examples is that there are no pointers in javascript. In both examples bools are printed as true/false, strings and numbers are printed as decimal numbers.

How would you represent a pointer if not as the address in hex?

I think the point was that the pointer address is literally the least interesting thing about those variables and there should be more useful defaults in Xcode, in addition to general reliability improvements. We shouldn’t have to print every variable in order to get useful information.

1 Like

What kind of information? We already can see the type, and properties of the pointed object are available after clicking the triangle(as in the javascript example)

They aren’t really though. Most of the time it’s just Obj-C internal garbage, not a clean description of the public properties.

I'm looking at rdar://problem/47417395 right now. The symptoms for the messageText failure look like they could be a compiler rather than an LLDB issue. Could you locate the .o build artifact of the source file you are stopped in (or the .dSYM) and run xcrun dwarfdump --name tapAction -p -c [file.o/.dSYM]. And either post it here or attach it to the bugreport?

It looks like the debug info might be missing the location for messageText.

1 Like

I'm in Build/Products/Debug-iphoneos/ Where are the .o files?

The easiest way to find the location is to look at the build log in Xcode and expand the swift compiler invocation for that source file. There should be a -o /path/to/file.o line in it.

If you are in lldb, you can also do:

(lldb) image list -g foo.o

That will list the path to the .o file.

The pointer entry in the locals is always the marker of the container of interesting things, you have to disclose it to see the actual contents. If the data type has some useful one line summary (e.g. the string value of an NSString *) then we show that, but if it doesn't, there isn't anything else useful to put there, so I'm not sure it does any harm to show the pointer value. It allows you to view the memory of the object, etc. So it is useful.

I guess your main point, however, is that when you disclose the container, you only see the ivars and you don't see the computed properties.

We don't as a rule show computed properties for any languages in the variables view in lldb. The reason is that to do that, for every stop the debugger has to make a function call into the debugee per property to fetch the values. That's fragile. For instance if a getter has side-effects (e.g. first time it's called it makes some backing object), then just stopping in the debugger can change the execution flow of your program which is not desirable. And if the getter does something complex like fetch data from a server, then that doesn't work at all well. The latter case requires some "don't present this property in the debugger" annotation, but then you have to know to apply that, and again it makes the whole experience more fragile.

It also slows down display of variable values considerably.

There's been a lot of discussion about this choice (which started with ObjC, BTW) over the years. I certainly see the desirability of showing computed properties. OTOH, I've been told both "other debuggers do this and it causes no problems" and "other debuggers do this and I have to turn it off because it causes lots of problems..." So while it would be definitely be interesting to experiment with adding computed properties to the variables display, it's not something to undertake lightly.

And if there's a property of an object that you really need to see, you can add it as an expression to the locals view. So there is a way to do this on-demand.

On the swift side, pure bridged Objc Objects only have computed properties. Given that we don't show computed properties, the most interesting thing to show for pure bridged ObjC objects is the ObjC side of the value.

1 Like