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 @@ ...@@ -19,46 +19,113 @@
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include <folly/detail/Demangle.h>
#include <folly/lang/CString.h> #include <folly/lang/CString.h>
#include <folly/portability/Config.h> #include <folly/portability/Config.h>
#if FOLLY_DETAIL_HAVE_DEMANGLE_H #if __has_include(<cxxabi.h>)
#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 #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 { 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) { fbstring demangle(const char* name) {
if (!name) { if (!name) {
return fbstring(); return fbstring();
} }
#ifdef FOLLY_DEMANGLE_MAX_SYMBOL_SIZE
if (demangle_max_symbol_size) {
// GCC's __cxa_demangle() uses on-stack data structures for the // GCC's __cxa_demangle() uses on-stack data structures for the
// parser state which are linear in the number of components of the // parser state which are linear in the number of components of the
// symbol. For extremely long symbols, this can cause a stack // symbol. For extremely long symbols, this can cause a stack
// overflow. We set an arbitrary symbol length limit above which we // overflow. We set an arbitrary symbol length limit above which we
// just return the mangled name. // just return the mangled name.
size_t mangledLen = strlen(name); size_t mangledLen = strlen(name);
if (mangledLen > FOLLY_DEMANGLE_MAX_SYMBOL_SIZE) { if (mangledLen > demangle_max_symbol_size) {
return fbstring(name, mangledLen); return fbstring(name, mangledLen);
} }
#endif }
if (cxxabi_demangle) {
int status; int status;
size_t len = 0; size_t len = 0;
// malloc() memory for the demangled type name // 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) { if (status != 0) {
return name; return name;
} }
// len is the length of the buffer (including NUL terminator and maybe // len is the length of the buffer (including NUL terminator and maybe
// other junk) // other junk)
return fbstring(demangled, strlen(demangled), len, AcquireMallocatedString()); return fbstring(
demangled, strlen(demangled), len, AcquireMallocatedString());
} else {
return fbstring(name);
}
} }
namespace { namespace {
...@@ -81,9 +148,9 @@ void demangleCallback(const char* str, size_t size, void* p) { ...@@ -81,9 +148,9 @@ void demangleCallback(const char* str, size_t size, void* p) {
} // namespace } // namespace
size_t demangle(const char* name, char* out, size_t outSize) { 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); size_t mangledLen = strlen(name);
if (mangledLen > FOLLY_DEMANGLE_MAX_SYMBOL_SIZE) { if (mangledLen > demangle_max_symbol_size) {
if (outSize) { if (outSize) {
size_t n = std::min(mangledLen, outSize - 1); size_t n = std::min(mangledLen, outSize - 1);
memcpy(out, name, n); memcpy(out, name, n);
...@@ -91,16 +158,17 @@ size_t demangle(const char* name, char* out, size_t outSize) { ...@@ -91,16 +158,17 @@ size_t demangle(const char* name, char* out, size_t outSize) {
} }
return mangledLen; return mangledLen;
} }
#endif }
if (reinterpret_cast<void*>(liberty_demangle)) {
DemangleBuf dbuf; DemangleBuf dbuf;
dbuf.dest = out; dbuf.dest = out;
dbuf.remaining = outSize ? outSize - 1 : 0; // leave room for null term dbuf.remaining = outSize ? outSize - 1 : 0; // leave room for null term
dbuf.total = 0; dbuf.total = 0;
// Unlike most library functions, this returns 1 on success and 0 on failure // Unlike most library functions, this returns 1 on success and 0 on failure
int status = int status = liberty_demangle(
detail::cplus_demangle_v3_callback_wrapper(name, demangleCallback, &dbuf); name, liberty_demangle_options, demangleCallback, &dbuf);
if (status == 0) { // failed, return original if (status == 0) { // failed, return original
return folly::strlcpy(out, name, outSize); return folly::strlcpy(out, name, outSize);
} }
...@@ -108,18 +176,9 @@ size_t demangle(const char* name, char* out, size_t outSize) { ...@@ -108,18 +176,9 @@ size_t demangle(const char* name, char* out, size_t outSize) {
*dbuf.dest = '\0'; *dbuf.dest = '\0';
} }
return dbuf.total; return dbuf.total;
} } else {
#else
fbstring demangle(const char* name) {
return name;
}
size_t demangle(const char* name, char* out, size_t outSize) {
return folly::strlcpy(out, name, outSize); return folly::strlcpy(out, name, outSize);
}
} }
#endif
} // namespace folly } // namespace folly
...@@ -17,9 +17,20 @@ ...@@ -17,9 +17,20 @@
#pragma once #pragma once
#include <folly/FBString.h> #include <folly/FBString.h>
#include <folly/portability/Config.h>
namespace folly { 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. * 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 @@ ...@@ -16,73 +16,62 @@
#include <folly/Demangle.h> #include <folly/Demangle.h>
#include <folly/detail/Demangle.h> #include <folly/lang/Pretty.h>
#include <folly/lang/CString.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
using folly::demangle; 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 { namespace folly_test {
struct ThisIsAVeryLongStructureName {}; struct ThisIsAVeryLongStructureName {};
} // namespace folly_test } // namespace folly_test
#if FOLLY_DETAIL_HAVE_DEMANGLE_H class DemangleTest : public testing::Test {};
TEST(Demangle, demangle) {
char expected[] = "folly_test::ThisIsAVeryLongStructureName";
EXPECT_STREQ(
expected,
demangle(typeid(folly_test::ThisIsAVeryLongStructureName)).c_str());
{ TEST_F(DemangleTest, demangle_return_string) {
char buf[sizeof(expected)]; using type = folly_test::ThisIsAVeryLongStructureName;
EXPECT_EQ( auto const raw = typeid(type).name();
sizeof(expected) - 1, auto const expected = demangle_build_has_cxxabi ? pretty_name<type>() : raw;
demangle( EXPECT_EQ(expected, demangle(typeid(type)));
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);
}
} }
#if defined(FOLLY_DEMANGLE_MAX_SYMBOL_SIZE) TEST_F(DemangleTest, demangle_to_buffer) {
namespace { using type = folly_test::ThisIsAVeryLongStructureName;
auto const raw = typeid(type).name();
template <int I, class T1, class T2> auto const expected = demangle_build_has_liberty ? pretty_name<type>() : raw;
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(Demangle, LongSymbolFallback) { {
// The symbol must be at least FOLLY_DEMANGLE_MAX_SYMBOL_SIZE long. std::vector<char> buf;
using Symbol = LongSymbol<FOLLY_DEMANGLE_MAX_SYMBOL_SIZE>::type; buf.resize(1 + strlen(expected));
auto name = typeid(Symbol).name(); EXPECT_EQ(strlen(expected), demangle(typeid(type), buf.data(), buf.size()));
EXPECT_EQ(std::string(expected), buf.data());
EXPECT_STREQ(name, demangle(name).c_str()); }
char buf[16]; {
char expected[16]; constexpr size_t size = 10;
folly::demangle(name, buf, 16); std::vector<char> buf;
folly::strlcpy(expected, name, 16); buf.resize(1 + size);
EXPECT_STREQ(expected, buf); 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