Commit 2690cff7 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

revise demangle

Summary:
Revise `folly::demangle` and its test suite.

As a few items:
* Minimize preprocessor conditional compilation.
* Make the tests work for all combinations.

Differential Revision: D26551208

fbshipit-source-id: 422f6f96632c4dfc3f2e51f7b3ae8855cdb3ab30
parent 3fdabf98
......@@ -19,46 +19,113 @@
#include <algorithm>
#include <cstring>
#include <folly/detail/Demangle.h>
#include <folly/lang/CString.h>
#include <folly/portability/Config.h>
#if FOLLY_DETAIL_HAVE_DEMANGLE_H
#if __has_include(<cxxabi.h>)
#include <cxxabi.h>
#endif
// The headers <libiberty.h> (binutils) and <string.h> (glibc) both declare the
// symbol basename. Unfortunately, the declarations are different. So including
// both headers in the same translation unit fails due to the two conflicting
// declarations. Since <demangle.h> includes <libiberty.h> we must be careful.
#if __has_include(<demangle.h>)
#pragma push_macro("HAVE_DECL_BASENAME")
#define HAVE_DECL_BASENAME 1
#include <demangle.h> // @manual
#pragma pop_macro("HAVE_DECL_BASENAME")
#endif
// try to find cxxabi demangle
//
// prefer using a weak symbol
#if FOLLY_HAVE_WEAK_SYMBOLS
namespace __cxxabiv1 {
extern "C" FOLLY_ATTR_WEAK char* __cxa_demangle(
char const*, char*, size_t*, int*);
}
static auto const cxxabi_demangle = __cxxabiv1::__cxa_demangle;
#else // FOLLY_HAVE_WEAK_SYMBOLS
static constexpr auto cxxabi_demangle = static_cast<char* (*)(...)>(nullptr);
#endif // FOLLY_HAVE_WEAK_SYMBOLS
// try to find liberty demangle
//
// cannot use a weak symbol since it may be the only referenced symbol in
// liberty
//
// in contrast with cxxabi, where there are certainly other referenced symbols
#if __has_include(<demangle.h>)
static constexpr auto liberty_demangle = cplus_demangle_v3_callback;
#if defined(DMGL_NO_RECURSE_LIMIT)
static constexpr auto liberty_demangle_options_no_recurse_limit =
DMGL_NO_RECURSE_LIMIT;
#else
static constexpr auto liberty_demangle_options_no_recurse_limit = 0;
#endif
static constexpr auto liberty_demangle_options = //
DMGL_PARAMS | DMGL_ANSI | DMGL_TYPES | //
liberty_demangle_options_no_recurse_limit;
#else // __has_include(<demangle.h>)
static constexpr auto liberty_demangle = static_cast<int (*)(...)>(nullptr);
static constexpr auto liberty_demangle_options = 0;
#endif // __has_include(<demangle.h>)
// implementations
namespace folly {
#if FOLLY_DETAIL_HAVE_DEMANGLE_H
// reinterpret-cast currently evades -Waddress
bool const demangle_build_has_cxxabi = cxxabi_demangle;
bool const demangle_build_has_liberty =
reinterpret_cast<void*>(liberty_demangle);
fbstring demangle(const char* name) {
if (!name) {
return fbstring();
}
#ifdef FOLLY_DEMANGLE_MAX_SYMBOL_SIZE
if (demangle_max_symbol_size) {
// GCC's __cxa_demangle() uses on-stack data structures for the
// parser state which are linear in the number of components of the
// symbol. For extremely long symbols, this can cause a stack
// overflow. We set an arbitrary symbol length limit above which we
// just return the mangled name.
size_t mangledLen = strlen(name);
if (mangledLen > FOLLY_DEMANGLE_MAX_SYMBOL_SIZE) {
if (mangledLen > demangle_max_symbol_size) {
return fbstring(name, mangledLen);
}
#endif
}
if (cxxabi_demangle) {
int status;
size_t len = 0;
// malloc() memory for the demangled type name
char* demangled = abi::__cxa_demangle(name, nullptr, &len, &status);
char* demangled = cxxabi_demangle(name, nullptr, &len, &status);
if (status != 0) {
return name;
}
// len is the length of the buffer (including NUL terminator and maybe
// other junk)
return fbstring(demangled, strlen(demangled), len, AcquireMallocatedString());
return fbstring(
demangled, strlen(demangled), len, AcquireMallocatedString());
} else {
return fbstring(name);
}
}
namespace {
......@@ -81,9 +148,9 @@ void demangleCallback(const char* str, size_t size, void* p) {
} // namespace
size_t demangle(const char* name, char* out, size_t outSize) {
#ifdef FOLLY_DEMANGLE_MAX_SYMBOL_SIZE
if (demangle_max_symbol_size) {
size_t mangledLen = strlen(name);
if (mangledLen > FOLLY_DEMANGLE_MAX_SYMBOL_SIZE) {
if (mangledLen > demangle_max_symbol_size) {
if (outSize) {
size_t n = std::min(mangledLen, outSize - 1);
memcpy(out, name, n);
......@@ -91,16 +158,17 @@ size_t demangle(const char* name, char* out, size_t outSize) {
}
return mangledLen;
}
#endif
}
if (reinterpret_cast<void*>(liberty_demangle)) {
DemangleBuf dbuf;
dbuf.dest = out;
dbuf.remaining = outSize ? outSize - 1 : 0; // leave room for null term
dbuf.total = 0;
// Unlike most library functions, this returns 1 on success and 0 on failure
int status =
detail::cplus_demangle_v3_callback_wrapper(name, demangleCallback, &dbuf);
int status = liberty_demangle(
name, liberty_demangle_options, demangleCallback, &dbuf);
if (status == 0) { // failed, return original
return folly::strlcpy(out, name, outSize);
}
......@@ -108,18 +176,9 @@ size_t demangle(const char* name, char* out, size_t outSize) {
*dbuf.dest = '\0';
}
return dbuf.total;
}
#else
fbstring demangle(const char* name) {
return name;
}
size_t demangle(const char* name, char* out, size_t outSize) {
} else {
return folly::strlcpy(out, name, outSize);
}
}
#endif
} // namespace folly
......@@ -17,9 +17,20 @@
#pragma once
#include <folly/FBString.h>
#include <folly/portability/Config.h>
namespace folly {
FOLLY_INLINE_VARIABLE constexpr size_t demangle_max_symbol_size =
#if defined(FOLLY_DEMANGLE_MAX_SYMBOL_SIZE)
FOLLY_DEMANGLE_MAX_SYMBOL_SIZE;
#else
0;
#endif
extern bool const demangle_build_has_cxxabi;
extern bool const demangle_build_has_liberty;
/**
* Return the demangled (prettyfied) version of a C++ type.
*
......
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/detail/Demangle.h>
// Do not include <libiberty.h> (binutils) and <string.h> (glibc) in the same
// translation unit since they contain conflicting declarations for the symbol
// `basename`.
//
// So we extract the inclusion of `<demangle.h>` which includes `<libiberty.h>`
// to here, isolating it.
#if FOLLY_DETAIL_HAVE_DEMANGLE_H
// Work around an issue with conflicting `basename` definitions in glibc and
// binutils headers.
#define HAVE_DECL_BASENAME 1
#include <demangle.h> // @manual
#undef HAVE_DECL_BASENAME
#endif
#ifndef DMGL_NO_RECURSE_LIMIT
#define DMGL_NO_RECURSE_LIMIT 0
#endif
namespace folly {
namespace detail {
int cplus_demangle_v3_callback_wrapper(
char const* const mangled,
void (*const cbref)(char const*, std::size_t, void*),
void* const opaque) {
#if FOLLY_DETAIL_HAVE_DEMANGLE_H
auto const options =
DMGL_PARAMS | DMGL_ANSI | DMGL_TYPES | DMGL_NO_RECURSE_LIMIT;
return cplus_demangle_v3_callback(mangled, options, cbref, opaque);
#else
(void)mangled;
(void)cbref;
(void)opaque;
return 0;
#endif
}
char* cplus_demangle_v3_wrapper(const char* mangled) {
#if FOLLY_DETAIL_HAVE_DEMANGLE_H
auto const options =
DMGL_PARAMS | DMGL_ANSI | DMGL_TYPES | DMGL_NO_RECURSE_LIMIT;
return cplus_demangle_v3(mangled, options);
#else
(void)mangled;
return nullptr;
#endif
}
} // namespace detail
} // namespace folly
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <cstddef>
#if __has_include(<demangle.h>)
#define FOLLY_DETAIL_HAVE_DEMANGLE_H 1
#else
#define FOLLY_DETAIL_HAVE_DEMANGLE_H 0
#endif
namespace folly {
namespace detail {
extern int cplus_demangle_v3_callback_wrapper(
char const* mangled,
void (*cbref)(char const*, std::size_t, void*),
void* opaque);
extern char* cplus_demangle_v3_wrapper(const char* mangled);
} // namespace detail
} // namespace folly
......@@ -16,73 +16,62 @@
#include <folly/Demangle.h>
#include <folly/detail/Demangle.h>
#include <folly/lang/CString.h>
#include <folly/lang/Pretty.h>
#include <folly/portability/GTest.h>
using folly::demangle;
using folly::demangle_build_has_cxxabi;
using folly::demangle_build_has_liberty;
using folly::demangle_max_symbol_size;
using folly::pretty_name;
namespace folly_test {
struct ThisIsAVeryLongStructureName {};
} // namespace folly_test
#if FOLLY_DETAIL_HAVE_DEMANGLE_H
TEST(Demangle, demangle) {
char expected[] = "folly_test::ThisIsAVeryLongStructureName";
EXPECT_STREQ(
expected,
demangle(typeid(folly_test::ThisIsAVeryLongStructureName)).c_str());
class DemangleTest : public testing::Test {};
{
char buf[sizeof(expected)];
EXPECT_EQ(
sizeof(expected) - 1,
demangle(
typeid(folly_test::ThisIsAVeryLongStructureName),
buf,
sizeof(buf)));
EXPECT_STREQ(expected, buf);
EXPECT_EQ(
sizeof(expected) - 1,
demangle(typeid(folly_test::ThisIsAVeryLongStructureName), buf, 11));
EXPECT_STREQ("folly_test", buf);
}
TEST_F(DemangleTest, demangle_return_string) {
using type = folly_test::ThisIsAVeryLongStructureName;
auto const raw = typeid(type).name();
auto const expected = demangle_build_has_cxxabi ? pretty_name<type>() : raw;
EXPECT_EQ(expected, demangle(typeid(type)));
}
#if defined(FOLLY_DEMANGLE_MAX_SYMBOL_SIZE)
namespace {
template <int I, class T1, class T2>
struct Node {};
template <int N, int I = 1>
struct LongSymbol {
using arg1 = typename LongSymbol<N / 2, 2 * I>::type;
using arg2 = typename LongSymbol<N / 2, 2 * I + 1>::type;
using type = Node<I, arg1, arg2>;
};
template <int I>
struct LongSymbol<0, I> {
using type = void;
};
} // namespace
TEST_F(DemangleTest, demangle_to_buffer) {
using type = folly_test::ThisIsAVeryLongStructureName;
auto const raw = typeid(type).name();
auto const expected = demangle_build_has_liberty ? pretty_name<type>() : raw;
TEST(Demangle, LongSymbolFallback) {
// The symbol must be at least FOLLY_DEMANGLE_MAX_SYMBOL_SIZE long.
using Symbol = LongSymbol<FOLLY_DEMANGLE_MAX_SYMBOL_SIZE>::type;
auto name = typeid(Symbol).name();
EXPECT_STREQ(name, demangle(name).c_str());
{
std::vector<char> buf;
buf.resize(1 + strlen(expected));
EXPECT_EQ(strlen(expected), demangle(typeid(type), buf.data(), buf.size()));
EXPECT_EQ(std::string(expected), buf.data());
}
char buf[16];
char expected[16];
folly::demangle(name, buf, 16);
folly::strlcpy(expected, name, 16);
EXPECT_STREQ(expected, buf);
{
constexpr size_t size = 10;
std::vector<char> buf;
buf.resize(1 + size);
EXPECT_EQ(strlen(expected), demangle(typeid(type), buf.data(), buf.size()));
EXPECT_EQ(std::string(expected).substr(0, size), buf.data());
}
}
#endif // defined(FOLLY_DEMANGLE_MAX_SYMBOL_SIZE)
#endif // FOLLY_DETAIL_HAVE_DEMANGLE_H
TEST_F(DemangleTest, demangle_long_symbol) {
// pretty and demangled names are the same for int but not for size_t
// mangling strlen multiplier can be assumed to be at least 4
using type = std::make_integer_sequence<int, demangle_max_symbol_size / 4>;
auto raw = typeid(type).name();
auto choice = demangle_max_symbol_size ? raw : pretty_name<type>();
EXPECT_EQ(std::string(choice), demangle(raw).toStdString());
auto const expected = demangle_build_has_liberty ? choice : raw;
constexpr size_t size = 15;
std::vector<char> buf;
buf.resize(1 + size);
EXPECT_EQ(strlen(expected), demangle(raw, buf.data(), buf.size()));
EXPECT_EQ(std::string(expected).substr(0, size), buf.data());
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment