Yo Apple: Xcode debugging Swift is *STILL* horribly broken

I've been reporting this problem for a long time now, and it's so bad I wonder if I'm just doing something wrong. Radar ID FB11236399. I've just added a much smaller code example that should be fairly easy to test.

But generally speaking, Xcode can't inspect local Swift variables, or step over await calls.

It's been this way as long as I can remember (since the introduction of async/await). Surely, if everyone was running into this, it would be fixed by now? Am I doing something wrong? It doesn't matter if it's Intel or Apple Silicon, macOS or iOS.

Hopefully someone from Apple/LLDB/Xcode will notice this message.

Thanks!

21 Likes

If you just need to inspect a variable rather than a full expression, you can try v variableName as a fallback. That will avoid invoking the parser, which seems to be where a lot of lldb’s Swift debugging issues live.

2 Likes

Alas, no better. v variableName seems to be what the contextual menu "Print Description" does:

(lldb) v req
(Foundation.URLRequest) req = <no location, value may have been optimized out>

po is slightly different:

(lldb) po req
error: expression failed to parse:
error: <EXPR>:8:1: error: cannot find 'req' in scope
req
^~~

Even frame variab doesn't work right. It dumps a bunch of stuff with values, and a few things without. Whether nor not a value exists in this output corresponds to what it can do via all the other methods (Xcode’s variables pane, contextual menu, or po v commands).

It really is like the thing got optimized out, but you can see -oNone in the build transcript.

This doesn't seem like a parser issue to me.

(lldb) frame v
(String) inLogin = "rmann@test.com"
(String) inPassword = "testpass"
(<redacted>) self = 0x0000600001ebce80 {
  session = 0x000000012fd07160 {…}
  baseURL = "http://localhost:8080/api"
  apiKey = "aldknca98s457qo24nfpo9u8a9hartap9a0s8ejl4kysdrt"
  config = {
    deviceID = 181CD0FD-D242-4794-BD48-8C4837C336E5
    token = "a2999a27017bded26f11d18dc4647fae3970eccf0c25e6f7608028a5d0d09578"
    selectedOrgID = nil
  }
  deviceName = "API Unit Test"
}
(Foundation.Data) json = 148 bytes
([String : Any]) obj = 4 key/value pairs {
  [0] = {
    key = "login"
    value = "rmann@test.com"
  }
  [1] = {
    key = "password"
    value = "testpass"
  }
  [2] = {
    key = "deviceName"
    value = "API Unit Test"
  }
  [3] = {
    key = "deviceID"
    value = "181CD0FD-D242-4794-BD48-8C4837C336E5"
  }
}
(<MyApp>.LoginRequest) loginRequest = <no location, value may have been optimized out>

(Foundation.URLRequest) req = <no location, value may have been optimized out>

(Foundation.URL) url = <no location, value may have been optimized out>

(Int) statusCode = <variable not available>

(URLResponse) resp = <variable not available>

(Foundation.Data) data = <variable not available>

(lldb) 
3 Likes

I feel this! It's one of the reasons I have way to many prints in the code just to be able to properly debug. The most annoying part if this feature stops working on a trap, then it's impossible to inspect the local states which caused it.

8 Likes

Yup, I've had to stop using the debugger too since it never sees any of my variables

8 Likes

Q: How many lldb-rpc-servers does it take to change a light bulb?

A:

error: expression failed to parse:
error: <EXPR>:3:1: error: cannot find 'lightBulb' in scope
13 Likes

I can feel your pain, but IMO it‘s lldb, not Xcode, that has been broken since at the time of Swift 5.5 when async was introduced. It has never fully recovered from that. Inspecting variables breaks very often, even in non-async functions. Breakpoints are often erratic in coroutines and stepping in or out lands you in completely unexpected frames.

I, too, have resorted to print-debugging since then.

5 Likes

Yes, this is exactly what I’ve experienced. I go back and forth on posting a similar topic over on the LLVM forum, but because it’s specific to Swift, Apple has their own fork of LLVM, and Apple has more of an obligation to make it work (even if that means working with LLVM to improve things).

3 Likes

Swift support is part of mainline lldb; it’s not specific to any Apple fork.

2 Likes

Okay, I posted over on the LLVM forum.

1 Like

Swift support is part of mainline lldb; it’s not specific to any Apple fork.

To clarify: the Swift plugin for LLDB only exists in the apple/llvm-project fork on GitHub, because it depends on the Swift compiler. It's technically not part of the LLVM project at this time.

2 Likes

Thanks for bringing this to our attention. Based on the symptoms this looks like a problem in the debug info generation in the Swift compiler and not in LLDB. I'll take a look at the testcase that you provided.

Ah, Apple fork vs. Apple-internal fork. :blush:

2 Likes

Yeah, they let me know as much over on the LLVM forum. However, someone PMed me asking for Radar numbers. I gave them half a dozen dating as far back as 2015 (Xcode 7), with the most recent update with reproducing project being the other day. I hope that means someone will spend time on it.

3 Likes

I took a look at your test and I can reproduce this behavior on the Swift 5.7 branch. The good news is that I cannot reproduce the same behavior on the 5.8 branch, but I haven't analyzed what's actually failing on 5.7 yet.

5 Likes

It really is like the thing got optimized out, but you can see -oNone in the build transcript.

I have not confirmed this yet, but this is most likely what's actually happening. Debug info for variables in async functions goes through a compilation pipeline that is much more similar to how debug info for optimized code is handled and thus can be affected by similar problems.

4 Likes

This is exciting news, I'm really glad to hear someone is looking at this! If I added the current nightly 5.8 builds to my Xcode, would I be happier or unhappier? :smiley:

FWIW, I tried installing the recent 5.8 build (jan 20), and when I try to build my iOS app:

Undefined symbols for architecture arm64:
  "___isPlatformVersionAtLeast", referenced from:
      swift::adoptTaskVoucher(swift::AsyncTask*) in libswiftCompatibility56.a(Actor.cpp.o)
      swift::restoreTaskVoucher(swift::AsyncTask*) in libswiftCompatibility56.a(Actor.cpp.o)

Yeah, the toolchains don’t work for iOS. Haven’t for a while now.

I experience all these same problems on macOS (I spend a lot of time lately working on a couple of different Vapor servers). The problems inspecting local variables and stepping through code are just as bad.

1 Like