Embedded Swift Debugging - LLDB Internal Assert When Printing Strings

Hello!

I have been playing around with Embedded Swift for a couple weeks now, and one of the things I've been trying out is the debugger support. In the embedded world, coredumps, gdb, and C are king - I wanted to see how lldb + embedded swift compares.

However, I've run into a lot of problems, and searching both the Swift forums and Google has yielded shockingly few results.

As a minimal example, I have this very simple Embedded Swift program, designed to test our ability to inspect structs in the debugger.

struct Greeting {
  var greeting: String
}

struct StaticGreeting {
  var greeting: StaticString
}

struct Rect {
  var x: Int32
  var y: Int32
  var w: Int32
  var h: Int32
}

@_cdecl("swift_bp")
public func swift_bp() {}

@_cdecl("swift_say_hello")
public func swift_say_hello() {
  let localNum = 1337
  let structWithNums = Rect(x: 31, y: 7, w: 32, h: 64)
  let localStaticString = StaticString("Static string test")
  let structWithStaticString = StaticGreeting(greeting: "StaticHowdy")
  let localString = String("This is a test")
  let structWithString = Greeting(greeting: "Howdy")
  swift_bp()
}

I run the debugger like

lldb --arch arm out/hello_world.elf \
		--one-line "gdb-remote 1234"
(lldb) target create --arch=arm "out/hello_world.elf"
Current executable set to '[...]/out/hello_world.elf' (arm).
(lldb) gdb-remote 1234
Process 1 stopped
* thread #1, stop reason = signal SIGTRAP
	frame #0: 0x00000044 hello_world.elf`_start at startup.c:10
   7   	    }
   8   	}
   9
-> 10  	void _start() {
   11  	    main();
   12  	    _hang();
   13  	}
Target 0: (hello_world.elf) stopped.
(lldb) version
lldb version 21.0.0 (https://github.com/swiftlang/llvm-project.git revision bff1370bd79c9833d255e3837c77e8d2dff1901f)
Apple Swift version 6.3-dev (LLVM bff1370bd79c983, Swift 57cf4ce563f700b)
(lldb) b swift_bp
Breakpoint 1: 2 locations.
(lldb) c
Process 1 resuming
Process 1 stopped
* thread #1, stop reason = breakpoint 1.2
	frame #0: 0x00000568 hello_world.elf`swift_bp() at app_main.swift:17:25
   14  	}
   15
   16  	@_cdecl("swift_bp")
-> 17  	public func swift_bp() {}
   18
   19  	@_cdecl("swift_say_hello")
   20  	public func swift_say_hello() {
Target 0: (hello_world.elf) stopped.
(lldb) s
Process 1 stopped
* thread #1, stop reason = step in
	frame #0: 0x00000690 hello_world.elf`swift_say_hello() at app_main.swift:27:3
   24  	  let structWithStaticString = StaticGreeting(greeting: "StaticHowdy")
   25  	  let localString = String("This is a test")
   26  	  let structWithString = Greeting(greeting: "Howdy")
-> 27  	  swift_bp()
   28  	}
Target 0: (hello_world.elf) stopped.
(lldb) p localNum
(Int) 1337
(lldb) p structWithNums
(app_main.Rect)  (x = 31, y = 7, w = 32, h = 64)
(lldb) p localStaticString
(StaticString) "Static string test"
(lldb) p structWithStaticString
(app_main.StaticGreeting)  (greeting = "StaticHowdy")
(lldb) p localString
(String) <cannot decode string: unexpected layout (count)>
warning: (arm) [...]/out/hello_world.elf '/var/folders/6m/zv5t5r411zj0m_k7vplhr1p40000gn/C/clang/ModuleCache/2FESEAI5PZZ60/SwiftShims-3UQX09UC1O02Q.pcm' does not exist
warning: (arm) [...]/out/hello_world.elf Unable to locate module needed for external types.
Debugging will be degraded due to missing types. Rebuilding the project will regenerate the needed module files.
warning: TypeSystemSwiftTypeRef::operator(): had to engage SwiftASTContext fallback for type $eSSD
(lldb) p structWithString
Assertion failed: (m_initialized_search_path_options && m_initialized_clang_importer_options && "search path options must be initialized before ClangImporter"), function GetASTContext, file SwiftASTContext.cpp, line 3658.
LLDB diagnostics will be written to /var/folders/6m/zv5t5r411zj0m_k7vplhr1p40000gn/T/diagnostics-064863
Please include the directory content when filing a bug report
make: *** [debug-lldb] Abort trap: 6

As you can see, it's very easy to crash the debugger, and we cannot print any strings.

It also fails if you try and have any expression as part of the print, with the same error.

(lldb) p localStaticString.print()
Assertion failed: (m_initialized_search_path_options && m_initialized_clang_importer_options && "search path options must be initialized before ClangImporter"), function GetASTContext, file SwiftASTContext.cpp, line 3658.
LLDB diagnostics will be written to /var/folders/6m/zv5t5r411zj0m_k7vplhr1p40000gn/T/diagnostics-f0f318
Please include the directory content when filing a bug report
make: *** [debug-lldb] Abort trap: 6

I tried with a number of different versions of Swift (via Swiftly), and we hit the same assert on all versions I tested - except on 6.1, where we exit with a clean error message (but are still unable to print the string).

A couple questions for folks here working with embedded swift:

  1. Is this the expected behaviour?
  2. Is this being actively worked on?

Thanks so much!

2 Likes

@Adrian_Prantl

Here LLDB is failing to initialize the Swift compiler instance for the expression parser. You can bypass this entirely by running settings set symbols.swift-enable-ast-context false or putting it into your lldbinit file. This will disable the expression evaluator, but expression evaluation isn’t really supported for embedded Swift at the moment.

More concerning is why LLDB tried use the compiler to resolve Swift.String. This might indicate that LLDB couldn’t find DWARF debug info for your standard library. Can you confirm that (llvm-)dwarfdump hello_world.elf –name '$eSSD' does not come back empty?

Hi Adrian!

Super appreciate the fast response :).

On my system:

$ dwarfdump out/hello_world.elf --name '$eSSD'
out/hello_world.elf:	file format elf32-littlearm

If I run settings set symbols.swift-enable-ast-context false,
as you suggest, that does indeed fix the crashes! That makes
the debugger much more usable.

However, it means we truely have no expression evaluation
whatsoever. You can't even do basic pointer math or cast types:

(lldb) p/x (char*)localStaticString

error: could not initialize Swift compiler
run 'swift-healthcheck' for more details

I also discovered that you can attempt to print all the local
variables without crashing by running frame variables or
fr v. That gives some interesting output:

(lldb) fr v
(Int) localNum = 1337
(app_main.Rect) structWithNums = (x = 31, y = 7, w = 32, h = 64)
(StaticString) localStaticString = "Static string test"
(app_main.StaticGreeting) structWithStaticString = (greeting = "StaticHowdy")
(String) localString = <cannot decode string: unexpected layout (count)>
(app_main.Greeting) structWithString = <could not initialize Swift compiler, run swift-healthcheck for more info>

warning: TypeSystemSwiftTypeRef::operator(): had to engage SwiftASTContext fallback for type $eSSD

Perhaps most interesting, it gives us the ability to run
swift-healthcheck, which we couldn't before (since lldb
hard crashed). Maybe the output here is interesting to you:

==== LLDB swift-healthcheck log. ===
This file contains the configuration of LLDB's embedded Swift compiler to help diagnosing module import and search path issues. The swift-healthcheck command is meant to be run *after* an error has occurred.
lldb version 21.0.0 (https://github.com/swiftlang/llvm-project.git revision bff1370bd79c9833d255e3837c77e8d2dff1901f)
Apple Swift version 6.3-dev (LLVM bff1370bd79c983, Swift 57cf4ce563f700b)
[LLDBTypeInfoProvider] Could not find clang debug type info for s13_StringObjectV03AnyB0a
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration(SwiftASTContext*)0x956000600:
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Swift/C++ interop                : off
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Swift/Objective-C interop        : on
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Architecture                     : arm64-apple-macosx26.0
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   SDK path                         : /Applications/Xcode_26.0.0_17A324_fb.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Runtime resource path            : /Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Runtime library paths            : (2 items)
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     /Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift/embedded
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     /usr/lib/swift
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Runtime library import paths     : (2 items)
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     /Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift/embedded
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     /Applications/Xcode_26.0.0_17A324_fb.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/usr/lib/swift
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Framework search paths           : (0 items)
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Import search paths              : (0 items)
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Extra clang arguments            : (4 items)
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     -target
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     arm64-apple-macosx26.0
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     -gmodules
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     -gdwarf-4
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Plugin search options            : (1 items)
SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     -external-plugin-path /Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift/host/plugins#/Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/bin/swift-plugin-server

I've no idea what the expected output is here, but it's
interesting to me that lldb thinks the Swift code was
compiled with -target arm64-apple-macosx26.0.
For what it's worth, we compile the Swift piece of
the code (app_main.swift) with

swiftc -c app_main.swift -o out/app_main.o \
	-g --target=armv6m-none-none-eabi \
	-enable-experimental-feature Embedded -wmo \
	-parse-as-library -Xfrontend -disable-stack-protector

I think this is the hottest lead we have right now. It is possible that there is a String implementation LLDB is lacking a data formatter for or (more likely) there is a debug info bug that makes it impossible for the formatter to resolve all fields of the string.

Could you run

(lldb) log enable lldb types

(lldb) v localString

in a fresh lldb session and post the output? It would allow us to understand which of the two options it is.

Thanks Adrian! Based on my reading of the output, it looks like it's probably the second one: [LLDBTypeInfoProvider] Could not find clang debug type info for s13_StringObjectV03AnyB0a.

Any idea why? As above, other types, such as custom structs and StaticString, work fine.

lldb --arch arm out/hello_world.elf \
		--one-line "gdb-remote 1234"
(lldb) target create --arch=arm "out/hello_world.elf"
Current executable set to '[...]/out/hello_world.elf' (arm).
(lldb) gdb-remote 1234
Process 1 stopped
* thread #1, stop reason = signal SIGTRAP
	frame #0: 0x00000044 hello_world.elf`_start at startup.c:10
   7   	    }
   8   	}
   9
-> 10  	void _start() {
   11  	    main();
   12  	    _hang();
   13  	}
Target 0: (hello_world.elf) stopped.
(lldb) b swift_bp
Breakpoint 1: where = hello_world.elf`swift_bp + 4 at app_main.swift:17:9, address = 0x00000490
(lldb) log enable lldb types
[...]
(lldb) n
Process 1 stopped
* thread #1, stop reason = step over
	frame #0: 0x0000074a hello_world.elf`swift_say_hello() at app_main.swift:29:3
   26  	  let localString = String("This is a test")
   27  	  let structWithString = Greeting(greeting: "Howdy")
   28  	  swift_bp()
-> 29  	  print(localNum)
   30  	  // We can't print structs in embedded swift, so just assign them
   31  	  // to _ instead to avoid unused variable warnings. We want these
   32  	  // variables to exist even though we don't "use" them so that we
Target 0: (hello_world.elf) stopped.
(lldb) v localString
 Initializing a 32-bit reflection context (arm---eabi) for "Swift only"
 AddModuleToReflectionContext: failed to get start address for "hello_world.elf".
 Could not determine file descriptor kind for type: $eBoD
 [LLDBTypeInfoProvider] Looking up debug type info for s13_StringObjectV03AnyB0a
 [LLDBTypeInfoProvider] Could not find clang debug type info for s13_StringObjectV03AnyB0a
 Couldn't compute size of type $eSSD using Swift language runtime: Could not find reflection metadata for type
no TypeInfo for field type: (struct Swift._StringGuts)

 TypeSystemSwiftTypeRef::operator()(): Engaging SwiftASTContext fallback for type $eSSD
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::CreateInstance(Target)
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::CreateInstance() -- Using precise SDK: MacOSX.sdk
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::CreateInstance() -- Module triple: "arm---eabi"
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::CreateInstance() -- Prefer module triple.
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::CreateInstance() -- Underspecified triple arm---eabi.
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::SetTriple() -- Cannot initialize Swift with an unknown OS
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::GetSDKPath() -- Host SDK path: "/Applications/Xcode_26.0.0_17A324.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk" (XcodeSDK: MacOSX.sdk)
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::CreateInstance() -- Using SDK: /Applications/Xcode_26.0.0_17A324.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk
 trying ePathTypeSwiftDir: /Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift
 found Swift resource dir via ePathTypeSwiftDir': /Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::ConfigureModuleValidation() -- PCM validation is disabled
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::InitializeSearchPathOptions() -- Setting SDK path "/Applications/Xcode_26.0.0_17A324.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk"
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::GetASTContext() -- Using Clang module cache path: /var/folders/6m/zv5t5r411zj0m_k7vplhr1p40000gn/C/clang/ModuleCache
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::GetASTContext() -- SDK version: 26.0
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::GetASTContext() -- Using prebuilt Swift module cache path: /Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift/macosx/prebuilt-modules
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::GetASTContext() -- Swift module loading mode forced to PreferSerialized
 SwiftASTContext(module: "app_main", cu: "app_main.swift") Module import remark: loaded module 'SwiftShims'; source: '/Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift/shims/module.modulemap', loaded: '/var/folders/6m/zv5t5r411zj0m_k7vplhr1p40000gn/C/clang/ModuleCache/2C8DOCPNA6MPX/SwiftShims-3UQX09UC1O02Q.pcm'
 SwiftASTContext(module: "app_main", cu: "app_main.swift") Module import remark: loaded module 'Swift'; source: '/Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift/embedded/Swift.swiftmodule/arm64-apple-macos.swiftmodule', loaded: '/Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift/embedded/Swift.swiftmodule/arm64-apple-macos.swiftmodule'
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration(SwiftASTContext*)0xb71448600:
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Swift/C++ interop                : off
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Swift/Objective-C interop        : on
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Architecture                     : arm64-apple-macosx26.0
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   SDK path                         : /Applications/Xcode_26.0.0_17A324.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Runtime resource path            : /Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Runtime library paths            : (2 items)
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     /Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift/embedded
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     /usr/lib/swift
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Runtime library import paths     : (2 items)
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     /Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift/embedded
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     /Applications/Xcode_26.0.0_17A324.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/usr/lib/swift
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Framework search paths           : (0 items)
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Import search paths              : (0 items)
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Extra clang arguments            : (4 items)
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     -target
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     arm64-apple-macosx26.0
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     -gmodules
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     -gdwarf-4
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --   Plugin search options            : (1 items)
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::LogConfiguration() --     -external-plugin-path /Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/lib/swift/host/plugins#/Users/[...]/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-11-03-a.xctoolchain/usr/bin/swift-plugin-server
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::ReconstructType("$eSSD") -- not cached, searching
 SwiftASTContext(module: "app_main", cu: "app_main.swift")::ReconstructType("$eSSD") -- found Swift.String
 TypeSystemSwiftTypeRef::operator(): had to engage SwiftASTContext fallback for type $eSSD
 GetIndexOfChildMemberWithName failed for type $eSSD
 TypeSystemSwiftTypeRef::GetIndexOfChildMemberWithName(): Engaging SwiftASTContext fallback for type $eSSD
 TypeSystemSwiftTypeRef::GetIndexOfChildMemberWithName: had to engage SwiftASTContext fallback for type $eSSD
 TypeSystemSwiftTypeRef::GetNumChildren(): Engaging SwiftASTContext fallback for type $eSSD
 TypeSystemSwiftTypeRef::GetNumChildren: had to engage SwiftASTContext fallback for type $eSSD
 TypeSystemSwiftTypeRef::operator()(): Engaging SwiftASTContext fallback for type $eSSD
 Had to engage SwiftASTContext fallback for type $eSSD, field #0.
 ::GetInstanceVariableOffset_Metadata() -- ivar_name = _guts, type = Swift.String
 [GetMemberVariableOffset] asked to resolve offset for member _guts
 [MemberVariableOffsetResolver] offset discovered = 0
 [GetMemberVariableOffset] offset of _guts is 0
 ::GetInstanceVariableOffset_Metadata() -- for _guts: 0
 TypeSystemSwiftTypeRef::operator(): had to engage SwiftASTContext fallback for type $eSSD
 ::GetInstanceVariableOffset_Metadata() -- ivar_name = _object, type = Swift._StringGuts
 [GetMemberVariableOffset] asked to resolve offset for member _object
 [MemberVariableOffsetResolver] offset discovered = 0
 [GetMemberVariableOffset] offset of _object is 0
 ::GetInstanceVariableOffset_Metadata() -- for _object: 0
(String) localString = <cannot decode string: unexpected layout (count)>
warning: TypeSystemSwiftTypeRef::operator(): had to engage SwiftASTContext fallback for type $eSSD
(lldb)

Embedded Swift uses an entirely different mechanism to convey debug info to the debugger. In regular Swift LLDB uses Swift reflection metadata do do type layout, but Embedded Swift doesn’t have reflection metadata, so the Swift compiler encodes the same information in DWARF debug info and LLDB parses that. There are bugs or gaps in the implementation where DWARF doesn’t have the same fidelity as reflection metadata and you probably found one of them.

CC @augusto2112