Demangle Function

Over on the Crash backtraces thread, there's some discussion using Swift's demangler to help pretty up backtraces into a human readable format. Right now the standard library doesn't include a demangle function, but the Swift runtime already includes such function. We can make use of that and incorporate it into the standard library. A naive API for this can look like:

public func demangle(_ mangledName: String) -> String

however, the demangle function offers a bit more functionality we can take advantage of. For example, instead of returning a new string, we could pass a pre-allocated buffer that it could write to:

public func demangle(
  _ mangledName: String,
  info buffer: UnsafeMutableBufferPointer<Int8>
)

There's an extra flags option that the demangler takes, but right now there currently aren't any flags. If in the future the demangler ever takes optional flags, we could introduce a new overload with a flags parameter. Other ideas for how to accommodate this case would be greatly appreciated.

Would love to hear thoughts and opinions!

4 Likes

I'd be open to including some demangling function in the stdlib. I'm not really sure we'd want to make it any more complicated than a simple (String) -> String function, unless there are clear performance implications around the use of the function. Which may be the case if it's to be heavily used in certain domains.

It might make sense to offer both of these as overloads so that for the most part people can use the simple (String) -> String, but those that need to write into buffers can do so.

Moreso than performance, in a crash situation you may want to avoid memory allocation, as @IanPartridge notes:

So having a low-level form that reads and writes out of raw buffers would be useful, alongside the high-level String -> String interface.

3 Likes

Something that is also worth discussing, should we append the Swift 5 mangled prefix in the case that someone doesn't provide it?

// Si = Swift.Int
demangle("Si") // returns Si
demangle("$sSi") // returns Swift.Int

We could've implicitly appended $s to the beginning of the first to provide a successful demangle. Does it also make sense to return the mangled name in the case of failure? Should this return String? instead?

The demangler tries to be forgiving with input, so if you have leading _, miss a leading $, use an obsolete _T* prefix or anything like that it still produces results. It seems reasonable to keep that with this interface.

It looks like the current demangler allocates as it goes, then copies the result into the provided buffer. But that could be improved, I guess.

The demangler can take a pre-allocated stack buffer, but yeah, it will attempt to reallocate if it runs out of space. We could maybe add a mode that gives up and give a best effort rendering of what it's done so far instead.

Doesn't this code always copy?

Ah, it uses the preallocated buffer to build the AST nodes, but maybe not for the final rendering. Yeah, we should fix that.

Maybe I'm reading this wrong, but shouldn't it be written something like:

// Copy into the provided buffer.
_swift_strlcpy(outputBuffer, result.c_str(), *outputBufferSize);

// Indicate a failure if the result did not fit and was truncated
// by setting the required outputBufferSize.
if (*outputBufferSize < result.length() + 1) {
  *outputBufferSize = result.length() + 1;
}

that way you don't write past the given allocated buffer? This also begs the question for better error recovery for the into buffer version if they want to know how much to allocate for the full output.

Yeah it would be nice if we could:

  • when we start a thread allocate a buffer for use for our demangling
  • and when when crash use that buffer space for the demangler.

This being in addition to a simple "String -> String" API is fine I think.

Same applies for buffer space for where to store the backtrace to begin with, though this we can do all in the potential "better backtraces" library.

1 Like