|
| 1 | +# Expose demangle function in Runtime module |
| 2 | + |
| 3 | +* Proposal: [SE-0498](0498-runtime-demangle.md) |
| 4 | +* Authors: [Konrad'ktoso'Malawski](https://github.com/ktoso), [Alejandro Alonso](https://github.com/Azoy) |
| 5 | +* Review Manager: [Steve Canon](https://github.com/stephentyrone) |
| 6 | +* Status: **Active Review (November 4 ..< 18)** |
| 7 | +* Implementation: [PR #84788](https://github.com/swiftlang/swift/pull/84788) |
| 8 | +* Review: |
| 9 | + * Previous [pitch](https://forums.swift.org/t/demangle-function/25416/16) |
| 10 | + |
| 11 | +## Introduction |
| 12 | + |
| 13 | +Swift symbols are subject to name mangling. These mangled names then show up in backtraces and other profiling tools. Mangled names may look something like this `$sSS7cStringSSSPys4Int8VG_tcfC` and often end up visible to developers, unless they are demangled before displaying. |
| 14 | + |
| 15 | +In manu situations, it is much preferable to demangle the identifiers before displaying them. For example, the previously shown identifier would can be demangled as `Swift.String.init(cString: Swift.UnsafePointer<Swift.Int8>) -> Swift.String`, which is a nice human-readable format, that a Swift developer can easily understand. |
| 16 | + |
| 17 | +This proposal introduces a new API that allows calling out to the Swift runtime's demangler, without leaving the process. |
| 18 | + |
| 19 | +## Motivation |
| 20 | + |
| 21 | +Currently, many tools that need to display symbol names to developers are forced to create a process and execute the `swift-demangle` tool, or use unofficial runtime APIs to invoke the runtime's demangler. |
| 22 | + |
| 23 | +Neither of these approaches are satisfactionary, because either we are paying a high cost for creating processes, or we're relying on unofficial APIs. |
| 24 | + |
| 25 | +This proposal introduces an official `demangle(:String) -> String?` function that offers a maintained and safe way to call the Swift demangled from a running Swift application. |
| 26 | + |
| 27 | +## Proposed solution |
| 28 | + |
| 29 | +We propose to introduce two `demangle` functions in the `Runtime` module: |
| 30 | + |
| 31 | +A simple demangle method, returning an optional `String`: |
| 32 | + |
| 33 | +```swift |
| 34 | +public func demangle(_ mangledName: String) -> String? |
| 35 | +``` |
| 36 | + |
| 37 | +The demangling function supports all valid Swift symbols. Valid Swift 5.0 and later symbols begin with `$s` (preceded by an optional platform-specific prefix). |
| 38 | + |
| 39 | +And an overload which accepts an `UTF8Span` into which the demangled string can be written: |
| 40 | + |
| 41 | +```swift |
| 42 | +public func demangle( |
| 43 | + _ mangledName: borrowing UTF8Span, |
| 44 | + into output: inout OutputSpan<UTF8.CodeUnit> |
| 45 | +) -> DemanglingResult |
| 46 | + |
| 47 | +public enum DemanglingResult: Equatable { |
| 48 | + case success |
| 49 | + case failed |
| 50 | + case truncated(Int) |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +The span accepting API is necessary for performance sensitive use-cases, which attempt to demangle symbols in process, before displaying or sending them for further processing. In those use-cases it is common to have a known maximum buffer size into which we are willing to write the demangled representation. |
| 55 | + |
| 56 | +The output from this API is an `OutputSpan` of `UTF8.CodeUnit`s, and it may not necessarily be well-formed UTF8, because of the potential of truncation happening between two code units which would render the UTF8 invalid. |
| 57 | + |
| 58 | +If the demangled representation does not fit the preallocated buffer, the demangle method will return `truncated(actualSize)` such that developers can determine by how much the buffer might need to be increased to handle the complete demangling. |
| 59 | + |
| 60 | +To construct an `UTF8Span` or valid `String` from the `OutputSpan` you can do the following: |
| 61 | + |
| 62 | +```swift |
| 63 | +var demangledOutputSpan: OutputSpan<UTF8.CodeUnit> = ... |
| 64 | + |
| 65 | +if demangle("$sSG", into: &demangledOutputSpan) == .success { |
| 66 | + let utf8 = try UTF8Span(validating: demangledOutputSpan.span) |
| 67 | + let demangledString = String(copying: utf8) |
| 68 | + print(demangledString) // Swift.RandomNumberGenerator |
| 69 | +} |
| 70 | +``` |
| 71 | + |
| 72 | +### Demangling format |
| 73 | + |
| 74 | +While the mangled strings are part of Swift ABI and can therefore not really change on platforms with stable ABI, the demangled representation returned by the `demangle` functions is _not guaranteed to be stable in any way_. |
| 75 | + |
| 76 | +The demangled representation may change without any warning, during even patch releases of Swift. The returned strings should be treated mostly as nicer to present to developers human readable representations, and it is not a goal to provide any form of guarantee about the exact shape of these. |
| 77 | + |
| 78 | +## Future directions |
| 79 | + |
| 80 | +### Customizing demangling behavior with options |
| 81 | + |
| 82 | +We intentionally do not offer customization of demangling modes in this initial revision of this API. The Swift demangler does offer various options about hot demangling should be performed, however we are not confident enough in selecting a subset of those options to offer as API at this point. |
| 83 | + |
| 84 | +Adding those options will be possible in future revisions, by adding an additional `options: DemanglingOptions` parameter to the demangle functions introduced in this proposal. |
| 85 | + |
| 86 | +## Source compatibility |
| 87 | + |
| 88 | +This proposal is purely additive. |
| 89 | + |
| 90 | +## ABI compatibility |
| 91 | + |
| 92 | +This proposal is purely additive. |
| 93 | + |
| 94 | +## Implications on adoption |
| 95 | + |
| 96 | +The runtime demangling func becoming an official entry point will help prevent libraries call swift internals. |
| 97 | + |
| 98 | +## Alternatives considered |
| 99 | + |
| 100 | +### Do nothing |
| 101 | + |
| 102 | +Not exposing this demangling capabilities officially, would result in tools authors continuing to use |
| 103 | +unofficial ways to get to this API. |
| 104 | + |
| 105 | +It also means that further locking down access to `swift_` APIs may be difficult, |
| 106 | +as they are crucial and load bearing for some tools (such as *continuous profiling*). |
| 107 | +E.g. recent versions of Swift issue the following warning when accessing some of its `swift_` namespaced runtime functions: |
| 108 | + |
| 109 | +> symbol name 'swift_...' is reserved for the Swift runtime and cannot be directly referenced without causing unpredictable behavior; this will become an error |
| 110 | +
|
| 111 | +So if we wanted to further lock down such uses, including `swift_demangle`, it would be preferable to offer an official solution instead. |
| 112 | + |
| 113 | +### Expose from Swift module |
| 114 | + |
| 115 | +Previously, this was considered to expose from just the `Swift` module, however the `Runtime` |
| 116 | +module is much more aligned with the use and utility of this function. |
| 117 | + |
| 118 | +Demangling is already used by the `Backtrace` type which is located in the Runtime module, |
| 119 | +so the demangling functions should be exposed from the same place. |
| 120 | + |
| 121 | +## Acknowledgments |
| 122 | + |
| 123 | +Thanks to [Alejandro Alonso](https://github.com/Azoy), who did an initial version of this pitch many years ago, and this proposal is heavily based on his initial pitch. |
0 commit comments