This is an updated version of the https://reviews.llvm.org/D113789 patch with the following changes: - We no longer modify modification times of the cache files - Use LLVM caching and cache pruning instead of making a new cache mechanism (See DataFileCache.h/.cpp) - Add signature to start of each file since we are not using modification times so we can tell when caches are stale and remove and re-create the cache file as files are changed - Add settings to control the cache size, disk percentage and expiration in days to keep cache size under control This patch enables symbol tables to be cached in the LLDB index cache directory. All cache files are in a single directory and the files use unique names to ensure that files from the same path will re-use the same file as files get modified. This means as files change, their cache files will be deleted and updated. The modification time of each of the cache files is not modified so that access based pruning of the cache can be implemented. The symbol table cache files start with a signature that uniquely identifies a file on disk and contains one or more of the following items: - object file UUID if available - object file mod time if available - object name for BSD archive .o files that are in .a files if available If none of these signature items are available, then the file will not be cached. This keeps temporary object files from expressions from being cached. When the cache files are loaded on subsequent debug sessions, the signature is compare and if the file has been modified (uuid changes, mod time changes, or object file mod time changes) then the cache file is deleted and re-created. Module caching must be enabled by the user before this can be used: symbols.enable-lldb-index-cache (boolean) = false (lldb) settings set symbols.enable-lldb-index-cache true There is also a setting that allows the user to specify a module cache directory that defaults to a directory that defaults to being next to the symbols.clang-modules-cache-path directory in a temp directory: (lldb) settings show symbols.lldb-index-cache-path /var/folders/9p/472sr0c55l9b20x2zg36b91h0000gn/C/lldb/IndexCache If this setting is enabled, the finalized symbol tables will be serialized and saved to disc so they can be quickly loaded next time you debug. Each module can cache one or more files in the index cache directory. The cache file names must be unique to a file on disk and its architecture and object name for .o files in BSD archives. This allows universal mach-o files to support caching multuple architectures in the same module cache directory. Making the file based on the this info allows this cache file to be deleted and replaced when the file gets updated on disk. This keeps the cache from growing over time during the compile/edit/debug cycle and prevents out of space issues. If the cache is enabled, the symbol table will be loaded from the cache the next time you debug if the module has not changed. The cache also has settings to control the size of the cache on disk. Each time LLDB starts up with the index cache enable, the cache will be pruned to ensure it stays within the user defined settings: (lldb) settings set symbols.lldb-index-cache-expiration-days <days> A value of zero will disable cache files from expiring when the cache is pruned. The default value is 7 currently. (lldb) settings set symbols.lldb-index-cache-max-byte-size <size> A value of zero will disable pruning based on a total byte size. The default value is zero currently. (lldb) settings set symbols.lldb-index-cache-max-percent <percentage-of-disk-space> A value of 100 will allow the disc to be filled to the max, a value of zero will disable percentage pruning. The default value is zero. Reviewed By: labath, wallace Differential Revision: https://reviews.llvm.org/D115324
524 lines
18 KiB
C++
524 lines
18 KiB
C++
//===-- Mangled.cpp -------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Core/Mangled.h"
|
|
|
|
#include "lldb/Core/DataFileCache.h"
|
|
#include "lldb/Core/RichManglingContext.h"
|
|
#include "lldb/Target/Language.h"
|
|
#include "lldb/Utility/ConstString.h"
|
|
#include "lldb/Utility/DataEncoder.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/Logging.h"
|
|
#include "lldb/Utility/RegularExpression.h"
|
|
#include "lldb/Utility/Stream.h"
|
|
#include "lldb/lldb-enumerations.h"
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Demangle/Demangle.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
using namespace lldb_private;
|
|
|
|
static inline bool cstring_is_mangled(llvm::StringRef s) {
|
|
return Mangled::GetManglingScheme(s) != Mangled::eManglingSchemeNone;
|
|
}
|
|
|
|
#pragma mark Mangled
|
|
|
|
Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef const name) {
|
|
if (name.empty())
|
|
return Mangled::eManglingSchemeNone;
|
|
|
|
if (name.startswith("?"))
|
|
return Mangled::eManglingSchemeMSVC;
|
|
|
|
if (name.startswith("_R"))
|
|
return Mangled::eManglingSchemeRustV0;
|
|
|
|
if (name.startswith("_D"))
|
|
return Mangled::eManglingSchemeD;
|
|
|
|
if (name.startswith("_Z"))
|
|
return Mangled::eManglingSchemeItanium;
|
|
|
|
// ___Z is a clang extension of block invocations
|
|
if (name.startswith("___Z"))
|
|
return Mangled::eManglingSchemeItanium;
|
|
|
|
return Mangled::eManglingSchemeNone;
|
|
}
|
|
|
|
Mangled::Mangled(ConstString s) : m_mangled(), m_demangled() {
|
|
if (s)
|
|
SetValue(s);
|
|
}
|
|
|
|
Mangled::Mangled(llvm::StringRef name) {
|
|
if (!name.empty())
|
|
SetValue(ConstString(name));
|
|
}
|
|
|
|
// Convert to pointer operator. This allows code to check any Mangled objects
|
|
// to see if they contain anything valid using code such as:
|
|
//
|
|
// Mangled mangled(...);
|
|
// if (mangled)
|
|
// { ...
|
|
Mangled::operator void *() const {
|
|
return (m_mangled) ? const_cast<Mangled *>(this) : nullptr;
|
|
}
|
|
|
|
// Logical NOT operator. This allows code to check any Mangled objects to see
|
|
// if they are invalid using code such as:
|
|
//
|
|
// Mangled mangled(...);
|
|
// if (!file_spec)
|
|
// { ...
|
|
bool Mangled::operator!() const { return !m_mangled; }
|
|
|
|
// Clear the mangled and demangled values.
|
|
void Mangled::Clear() {
|
|
m_mangled.Clear();
|
|
m_demangled.Clear();
|
|
}
|
|
|
|
// Compare the string values.
|
|
int Mangled::Compare(const Mangled &a, const Mangled &b) {
|
|
return ConstString::Compare(a.GetName(ePreferMangled),
|
|
b.GetName(ePreferMangled));
|
|
}
|
|
|
|
// Set the string value in this objects. If "mangled" is true, then the mangled
|
|
// named is set with the new value in "s", else the demangled name is set.
|
|
void Mangled::SetValue(ConstString s, bool mangled) {
|
|
if (s) {
|
|
if (mangled) {
|
|
m_demangled.Clear();
|
|
m_mangled = s;
|
|
} else {
|
|
m_demangled = s;
|
|
m_mangled.Clear();
|
|
}
|
|
} else {
|
|
m_demangled.Clear();
|
|
m_mangled.Clear();
|
|
}
|
|
}
|
|
|
|
void Mangled::SetValue(ConstString name) {
|
|
if (name) {
|
|
if (cstring_is_mangled(name.GetStringRef())) {
|
|
m_demangled.Clear();
|
|
m_mangled = name;
|
|
} else {
|
|
m_demangled = name;
|
|
m_mangled.Clear();
|
|
}
|
|
} else {
|
|
m_demangled.Clear();
|
|
m_mangled.Clear();
|
|
}
|
|
}
|
|
|
|
// Local helpers for different demangling implementations.
|
|
static char *GetMSVCDemangledStr(const char *M) {
|
|
char *demangled_cstr = llvm::microsoftDemangle(
|
|
M, nullptr, nullptr, nullptr, nullptr,
|
|
llvm::MSDemangleFlags(
|
|
llvm::MSDF_NoAccessSpecifier | llvm::MSDF_NoCallingConvention |
|
|
llvm::MSDF_NoMemberType | llvm::MSDF_NoVariableType));
|
|
|
|
if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) {
|
|
if (demangled_cstr && demangled_cstr[0])
|
|
LLDB_LOGF(log, "demangled msvc: %s -> \"%s\"", M, demangled_cstr);
|
|
else
|
|
LLDB_LOGF(log, "demangled msvc: %s -> error", M);
|
|
}
|
|
|
|
return demangled_cstr;
|
|
}
|
|
|
|
static char *GetItaniumDemangledStr(const char *M) {
|
|
char *demangled_cstr = nullptr;
|
|
|
|
llvm::ItaniumPartialDemangler ipd;
|
|
bool err = ipd.partialDemangle(M);
|
|
if (!err) {
|
|
// Default buffer and size (will realloc in case it's too small).
|
|
size_t demangled_size = 80;
|
|
demangled_cstr = static_cast<char *>(std::malloc(demangled_size));
|
|
demangled_cstr = ipd.finishDemangle(demangled_cstr, &demangled_size);
|
|
|
|
assert(demangled_cstr &&
|
|
"finishDemangle must always succeed if partialDemangle did");
|
|
assert(demangled_cstr[demangled_size - 1] == '\0' &&
|
|
"Expected demangled_size to return length including trailing null");
|
|
}
|
|
|
|
if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) {
|
|
if (demangled_cstr)
|
|
LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr);
|
|
else
|
|
LLDB_LOGF(log, "demangled itanium: %s -> error: failed to demangle", M);
|
|
}
|
|
|
|
return demangled_cstr;
|
|
}
|
|
|
|
static char *GetRustV0DemangledStr(const char *M) {
|
|
char *demangled_cstr = llvm::rustDemangle(M, nullptr, nullptr, nullptr);
|
|
|
|
if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) {
|
|
if (demangled_cstr && demangled_cstr[0])
|
|
LLDB_LOG(log, "demangled rustv0: {0} -> \"{1}\"", M, demangled_cstr);
|
|
else
|
|
LLDB_LOG(log, "demangled rustv0: {0} -> error: failed to demangle", M);
|
|
}
|
|
|
|
return demangled_cstr;
|
|
}
|
|
|
|
static char *GetDLangDemangledStr(const char *M) {
|
|
char *demangled_cstr = llvm::dlangDemangle(M);
|
|
|
|
if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) {
|
|
if (demangled_cstr && demangled_cstr[0])
|
|
LLDB_LOG(log, "demangled dlang: {0} -> \"{1}\"", M, demangled_cstr);
|
|
else
|
|
LLDB_LOG(log, "demangled dlang: {0} -> error: failed to demangle", M);
|
|
}
|
|
|
|
return demangled_cstr;
|
|
}
|
|
|
|
// Explicit demangling for scheduled requests during batch processing. This
|
|
// makes use of ItaniumPartialDemangler's rich demangle info
|
|
bool Mangled::DemangleWithRichManglingInfo(
|
|
RichManglingContext &context, SkipMangledNameFn *skip_mangled_name) {
|
|
// Others are not meant to arrive here. ObjC names or C's main() for example
|
|
// have their names stored in m_demangled, while m_mangled is empty.
|
|
assert(m_mangled);
|
|
|
|
// Check whether or not we are interested in this name at all.
|
|
ManglingScheme scheme = GetManglingScheme(m_mangled.GetStringRef());
|
|
if (skip_mangled_name && skip_mangled_name(m_mangled.GetStringRef(), scheme))
|
|
return false;
|
|
|
|
switch (scheme) {
|
|
case eManglingSchemeNone:
|
|
// The current mangled_name_filter would allow llvm_unreachable here.
|
|
return false;
|
|
|
|
case eManglingSchemeItanium:
|
|
// We want the rich mangling info here, so we don't care whether or not
|
|
// there is a demangled string in the pool already.
|
|
if (context.FromItaniumName(m_mangled)) {
|
|
// If we got an info, we have a name. Copy to string pool and connect the
|
|
// counterparts to accelerate later access in GetDemangledName().
|
|
context.ParseFullName();
|
|
m_demangled.SetStringWithMangledCounterpart(context.GetBufferRef(),
|
|
m_mangled);
|
|
return true;
|
|
} else {
|
|
m_demangled.SetCString("");
|
|
return false;
|
|
}
|
|
|
|
case eManglingSchemeMSVC: {
|
|
// We have no rich mangling for MSVC-mangled names yet, so first try to
|
|
// demangle it if necessary.
|
|
if (!m_demangled && !m_mangled.GetMangledCounterpart(m_demangled)) {
|
|
if (char *d = GetMSVCDemangledStr(m_mangled.GetCString())) {
|
|
// If we got an info, we have a name. Copy to string pool and connect
|
|
// the counterparts to accelerate later access in GetDemangledName().
|
|
m_demangled.SetStringWithMangledCounterpart(llvm::StringRef(d),
|
|
m_mangled);
|
|
::free(d);
|
|
} else {
|
|
m_demangled.SetCString("");
|
|
}
|
|
}
|
|
|
|
if (m_demangled.IsEmpty()) {
|
|
// Cannot demangle it, so don't try parsing.
|
|
return false;
|
|
} else {
|
|
// Demangled successfully, we can try and parse it with
|
|
// CPlusPlusLanguage::MethodName.
|
|
return context.FromCxxMethodName(m_demangled);
|
|
}
|
|
}
|
|
|
|
case eManglingSchemeRustV0:
|
|
case eManglingSchemeD:
|
|
// Rich demangling scheme is not supported
|
|
return false;
|
|
}
|
|
llvm_unreachable("Fully covered switch above!");
|
|
}
|
|
|
|
// Generate the demangled name on demand using this accessor. Code in this
|
|
// class will need to use this accessor if it wishes to decode the demangled
|
|
// name. The result is cached and will be kept until a new string value is
|
|
// supplied to this object, or until the end of the object's lifetime.
|
|
ConstString Mangled::GetDemangledName() const {
|
|
// Check to make sure we have a valid mangled name and that we haven't
|
|
// already decoded our mangled name.
|
|
if (m_mangled && m_demangled.IsNull()) {
|
|
// Don't bother running anything that isn't mangled
|
|
const char *mangled_name = m_mangled.GetCString();
|
|
ManglingScheme mangling_scheme =
|
|
GetManglingScheme(m_mangled.GetStringRef());
|
|
if (mangling_scheme != eManglingSchemeNone &&
|
|
!m_mangled.GetMangledCounterpart(m_demangled)) {
|
|
// We didn't already mangle this name, demangle it and if all goes well
|
|
// add it to our map.
|
|
char *demangled_name = nullptr;
|
|
switch (mangling_scheme) {
|
|
case eManglingSchemeMSVC:
|
|
demangled_name = GetMSVCDemangledStr(mangled_name);
|
|
break;
|
|
case eManglingSchemeItanium: {
|
|
demangled_name = GetItaniumDemangledStr(mangled_name);
|
|
break;
|
|
}
|
|
case eManglingSchemeRustV0:
|
|
demangled_name = GetRustV0DemangledStr(mangled_name);
|
|
break;
|
|
case eManglingSchemeD:
|
|
demangled_name = GetDLangDemangledStr(mangled_name);
|
|
break;
|
|
case eManglingSchemeNone:
|
|
llvm_unreachable("eManglingSchemeNone was handled already");
|
|
}
|
|
if (demangled_name) {
|
|
m_demangled.SetStringWithMangledCounterpart(
|
|
llvm::StringRef(demangled_name), m_mangled);
|
|
free(demangled_name);
|
|
}
|
|
}
|
|
if (m_demangled.IsNull()) {
|
|
// Set the demangled string to the empty string to indicate we tried to
|
|
// parse it once and failed.
|
|
m_demangled.SetCString("");
|
|
}
|
|
}
|
|
|
|
return m_demangled;
|
|
}
|
|
|
|
ConstString Mangled::GetDisplayDemangledName() const {
|
|
return GetDemangledName();
|
|
}
|
|
|
|
bool Mangled::NameMatches(const RegularExpression ®ex) const {
|
|
if (m_mangled && regex.Execute(m_mangled.GetStringRef()))
|
|
return true;
|
|
|
|
ConstString demangled = GetDemangledName();
|
|
return demangled && regex.Execute(demangled.GetStringRef());
|
|
}
|
|
|
|
// Get the demangled name if there is one, else return the mangled name.
|
|
ConstString Mangled::GetName(Mangled::NamePreference preference) const {
|
|
if (preference == ePreferMangled && m_mangled)
|
|
return m_mangled;
|
|
|
|
// Call the accessor to make sure we get a demangled name in case it hasn't
|
|
// been demangled yet...
|
|
ConstString demangled = GetDemangledName();
|
|
|
|
if (preference == ePreferDemangledWithoutArguments) {
|
|
if (Language *lang = Language::FindPlugin(GuessLanguage())) {
|
|
return lang->GetDemangledFunctionNameWithoutArguments(*this);
|
|
}
|
|
}
|
|
if (preference == ePreferDemangled) {
|
|
if (demangled)
|
|
return demangled;
|
|
return m_mangled;
|
|
}
|
|
return demangled;
|
|
}
|
|
|
|
// Dump a Mangled object to stream "s". We don't force our demangled name to be
|
|
// computed currently (we don't use the accessor).
|
|
void Mangled::Dump(Stream *s) const {
|
|
if (m_mangled) {
|
|
*s << ", mangled = " << m_mangled;
|
|
}
|
|
if (m_demangled) {
|
|
const char *demangled = m_demangled.AsCString();
|
|
s->Printf(", demangled = %s", demangled[0] ? demangled : "<error>");
|
|
}
|
|
}
|
|
|
|
// Dumps a debug version of this string with extra object and state information
|
|
// to stream "s".
|
|
void Mangled::DumpDebug(Stream *s) const {
|
|
s->Printf("%*p: Mangled mangled = ", static_cast<int>(sizeof(void *) * 2),
|
|
static_cast<const void *>(this));
|
|
m_mangled.DumpDebug(s);
|
|
s->Printf(", demangled = ");
|
|
m_demangled.DumpDebug(s);
|
|
}
|
|
|
|
// Return the size in byte that this object takes in memory. The size includes
|
|
// the size of the objects it owns, and not the strings that it references
|
|
// because they are shared strings.
|
|
size_t Mangled::MemorySize() const {
|
|
return m_mangled.MemorySize() + m_demangled.MemorySize();
|
|
}
|
|
|
|
// We "guess" the language because we can't determine a symbol's language from
|
|
// it's name. For example, a Pascal symbol can be mangled using the C++
|
|
// Itanium scheme, and defined in a compilation unit within the same module as
|
|
// other C++ units. In addition, different targets could have different ways
|
|
// of mangling names from a given language, likewise the compilation units
|
|
// within those targets.
|
|
lldb::LanguageType Mangled::GuessLanguage() const {
|
|
lldb::LanguageType result = lldb::eLanguageTypeUnknown;
|
|
// Ask each language plugin to check if the mangled name belongs to it.
|
|
Language::ForEach([this, &result](Language *l) {
|
|
if (l->SymbolNameFitsToLanguage(*this)) {
|
|
result = l->GetLanguageType();
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
// Dump OBJ to the supplied stream S.
|
|
Stream &operator<<(Stream &s, const Mangled &obj) {
|
|
if (obj.GetMangledName())
|
|
s << "mangled = '" << obj.GetMangledName() << "'";
|
|
|
|
ConstString demangled = obj.GetDemangledName();
|
|
if (demangled)
|
|
s << ", demangled = '" << demangled << '\'';
|
|
else
|
|
s << ", demangled = <error>";
|
|
return s;
|
|
}
|
|
|
|
// When encoding Mangled objects we can get away with encoding as little
|
|
// information as is required. The enumeration below helps us to efficiently
|
|
// encode Mangled objects.
|
|
enum MangledEncoding {
|
|
/// If the Mangled object has neither a mangled name or demangled name we can
|
|
/// encode the object with one zero byte using the Empty enumeration.
|
|
Empty = 0u,
|
|
/// If the Mangled object has only a demangled name and no mangled named, we
|
|
/// can encode only the demangled name.
|
|
DemangledOnly = 1u,
|
|
/// If the mangle name can calculate the demangled name (it is the
|
|
/// mangled/demangled counterpart), then we only need to encode the mangled
|
|
/// name as the demangled name can be recomputed.
|
|
MangledOnly = 2u,
|
|
/// If we have a Mangled object with two different names that are not related
|
|
/// then we need to save both strings. This can happen if we have a name that
|
|
/// isn't a true mangled name, but we want to be able to lookup a symbol by
|
|
/// name and type in the symbol table. We do this for Objective C symbols like
|
|
/// "OBJC_CLASS_$_NSValue" where the mangled named will be set to
|
|
/// "OBJC_CLASS_$_NSValue" and the demangled name will be manually set to
|
|
/// "NSValue". If we tried to demangled the name "OBJC_CLASS_$_NSValue" it
|
|
/// would fail, but in these cases we want these unrelated names to be
|
|
/// preserved.
|
|
MangledAndDemangled = 3u
|
|
};
|
|
|
|
bool Mangled::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr,
|
|
const StringTableReader &strtab) {
|
|
m_mangled.Clear();
|
|
m_demangled.Clear();
|
|
MangledEncoding encoding = (MangledEncoding)data.GetU8(offset_ptr);
|
|
switch (encoding) {
|
|
case Empty:
|
|
return true;
|
|
|
|
case DemangledOnly:
|
|
m_demangled.SetString(strtab.Get(data.GetU32(offset_ptr)));
|
|
return true;
|
|
|
|
case MangledOnly:
|
|
m_mangled.SetString(strtab.Get(data.GetU32(offset_ptr)));
|
|
return true;
|
|
|
|
case MangledAndDemangled:
|
|
m_mangled.SetString(strtab.Get(data.GetU32(offset_ptr)));
|
|
m_demangled.SetString(strtab.Get(data.GetU32(offset_ptr)));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
/// The encoding format for the Mangled object is as follows:
|
|
///
|
|
/// uint8_t encoding;
|
|
/// char str1[]; (only if DemangledOnly, MangledOnly)
|
|
/// char str2[]; (only if MangledAndDemangled)
|
|
///
|
|
/// The strings are stored as NULL terminated UTF8 strings and str1 and str2
|
|
/// are only saved if we need them based on the encoding.
|
|
///
|
|
/// Some mangled names have a mangled name that can be demangled by the built
|
|
/// in demanglers. These kinds of mangled objects know when the mangled and
|
|
/// demangled names are the counterparts for each other. This is done because
|
|
/// demangling is very expensive and avoiding demangling the same name twice
|
|
/// saves us a lot of compute time. For these kinds of names we only need to
|
|
/// save the mangled name and have the encoding set to "MangledOnly".
|
|
///
|
|
/// If a mangled obejct has only a demangled name, then we save only that string
|
|
/// and have the encoding set to "DemangledOnly".
|
|
///
|
|
/// Some mangled objects have both mangled and demangled names, but the
|
|
/// demangled name can not be computed from the mangled name. This is often used
|
|
/// for runtime named, like Objective C runtime V2 and V3 names. Both these
|
|
/// names must be saved and the encoding is set to "MangledAndDemangled".
|
|
///
|
|
/// For a Mangled object with no names, we only need to set the encoding to
|
|
/// "Empty" and not store any string values.
|
|
void Mangled::Encode(DataEncoder &file, ConstStringTable &strtab) const {
|
|
MangledEncoding encoding = Empty;
|
|
if (m_mangled) {
|
|
encoding = MangledOnly;
|
|
if (m_demangled) {
|
|
// We have both mangled and demangled names. If the demangled name is the
|
|
// counterpart of the mangled name, then we only need to save the mangled
|
|
// named. If they are different, we need to save both.
|
|
ConstString s;
|
|
if (!(m_mangled.GetMangledCounterpart(s) && s == m_demangled))
|
|
encoding = MangledAndDemangled;
|
|
}
|
|
} else if (m_demangled) {
|
|
encoding = DemangledOnly;
|
|
}
|
|
file.AppendU8(encoding);
|
|
switch (encoding) {
|
|
case Empty:
|
|
break;
|
|
case DemangledOnly:
|
|
file.AppendU32(strtab.Add(m_demangled));
|
|
break;
|
|
case MangledOnly:
|
|
file.AppendU32(strtab.Add(m_mangled));
|
|
break;
|
|
case MangledAndDemangled:
|
|
file.AppendU32(strtab.Add(m_mangled));
|
|
file.AppendU32(strtab.Add(m_demangled));
|
|
break;
|
|
}
|
|
}
|