[lldb] Cannot load underlying module error


We have a longstanding problem – unable to execute any expressions and print variables in lldb in swift. In our project we have a lot of modules and semi-complicated dependency chain, and lldb error depends in which module the execution is now. For example, such error is printed in "leaf" module, that doesn't depend on anything, except system modules:

Error while loading Swift module:
my_application: error: cannot load underlying module for 'SomeThirdPartyStaticLib'

Debug info from this module will be unavailable in the debugger.

error: <EXPR>:3:1: error: use of unresolved identifier 'idx'

And longer errors occur in "root" module:

warning: Swift error in scratch context: error: cannot load underlying module for 'OurFramework'
Shared Swift state for my_application has developed fatal errors and is being discarded.
REPL definitions and persistent names/types will be lost.
// 2 times more above 4 lines

warning: Swift error in fallback scratch context: error: cannot load underlying module for 'OurFramework'

note: This error message is displayed only once. If the error displayed above is due to conflicting search paths to Clang modules in different images of the debugged executable, this can slow down debugging of Swift code significantly, since a fresh Swift context has to be created every time a conflict is encountered.

error: warning: failed to get module "root_module" from AST context:
error: cannot load underlying module for 'OurFramework'

error: error: cannot load underlying module for 'OurFramework'

Couldn't initialize Swift expression evaluator due to previous errors.

Important thing to note: modules SomeThirdPartyStaticLib and OurFramework are importing underlying clang module – I'm suspecting that this has something to do with those errors.

Also, I know that swift's module ast is passing to debugger through ld's -add_ast_path flag, and we correctly passing it, but it looks like underlying module is actually doesn't reach to lldb.

So I'm asking for hints to debug/resolve this issue, and mainly, how does underlying module supposed to reach lldb?

I really need help with this, as it killing our development time and I'm out of ideas. Thanks!

@Adrian_Prantl hi! Saw your comments in topics around swift and lldb, can you help me with this?

This is a common issue even in simple projects without dependencies. I see it while trying to debug Alamofire, which is fun.

Oh, can you create repros for this? Please file a bug, there have been a lot of lldb improvements lately.

I thought I had filed a feedback issue, but I guess not. It's usually as simple as cloning Alamofire, running any test with a completion handler, setting a breakpoint within the handler, and then trying to po the response that comes back. Fails 100% of the time.

(lldb) po resp
error: Couldn't materialize: can't get size of type
error: errored out in DoExecute, couldn't PrepareToExecuteJITExpression

E: In general, most of my lldb usage results in that error or the original mentioned in this thread.

@Jon_Shier ok, that's great! Could you send me the radar/feedback number that contain the exact repro instructions?

Filed FB7538481 about it.

1 Like

@Adrian_Prantl friendly ping :) may be there are other people who can help with this?

As far as I’ve had progressed, it seems like something wrong with framework search path and regular search paths. Some time ago we moved from storing all produced frameworks in one folder to separate folder for each (as we have multiple frameworks with same name) - my bet that it broke debugging. Even thought we pass all needed flags for compilation, it doesn’t fully reach the lldb? That’s just a guess though.

I can reproduce your problem, but I don't see how it is at all related to what @zienag has reported. Do you also see module import failures in AlamoFire?

@zienag Is there any chance you could attach a reproducer to a JIRA ticket for me? It's really difficult to diagnose these issues blindly. If you can't do that, can you post a types log (by adding "log enable lldb types -f /tmp/types.log" into ~/.lldbinit-Xcode and relaunching Xcode and reproducing the error? )

Ok, I'll try to reduce it to smaller case. I'm actually not asking to diagnose the issue, but to provide some hints to where to look at, as we use custom build system and find all the compiler/linker flags through "try and see" mechanism, and I think we just miss some important flags that are actually needed :slight_smile: But that types log looks like a very good start, that I didn't know about, thanks!

Depending on the variable I try printing, I can get the issue I reported, the "variable not found" error, or the cannot load underlying module error. Frankly they all seem related to me, have been around since the beginning in some form for Swift (though the JIT error is new with Xcode 11), and prevent actual debugging, but I'm not an lldb developer, so I don't know.

I'm actually not asking to diagnose the issue, but to provide some hints to where to look at,

The types log :-)

Depending on the variable I try printing, I can get the issue I reported, the "variable not found" error, or the cannot load underlying module error.

The error I could reproduce with AlamoFire was swift remote mirrors (ie. this Swift runtime) not getting at the type info, which should be orthogonal to any module import failures, since these are different subsystems.

It would be great if you could provide a reproducer and/.or type log for the module import failure, too. If possible, however, it would be better to discuss separate issues in separate threads or JIRA tickets, because otherwise there's a risk that a bug becomes unnoticed and won't get fixed because it becomes conflated with an unrelated issue.

I think we've figured it out. Some of static libs we have use objc bridging header, that makes SerializeOptionsForDebugging flip to false:

  // FIXME: This is still a lousy approximation of whether the module file will
  // be externally consumed.
  bool moduleIsPublic =
      !Instance.getMainModule()->hasEntryPoint() &&
      opts.ImplicitObjCHeaderPath.empty() &&
  // Options contain information about the developer's computer,
  // so only serialize them if the module isn't going to be shipped to
  // the public.
  serializationOpts.SerializeOptionsForDebugging =

When we had every framework in "root" build directory (as in Xcode) we had no problem because that single framework search path got pulled to lldb by others, that doesn't have bridging header. When we put frameworks in different directories, some of framework search paths couldn't get to lldb.

Looking at xcode, it passes -Xfrontend -serialize-debugging-options to swift driver no matter what, so we just did the same :slight_smile:

Good to hear that you found a workaround.

From a debugging perspective, at the moment, I would generally recommend against using bridging headers for anything but the main program. Because bridging headers are inherently non-modular, they need to be compiled from source in every debug session and they also are more likely to create conflicts in the debugger when two bridging headers need to be imported into the same context.

Yes, we've completely eliminated them in our code, because they are nothing more than source of troubles and pain :slight_smile: But there is some third/second-party code that uses them, unfortunately.