From: Tudor Bosman Date: Wed, 27 Nov 2013 16:56:01 +0000 (-0800) Subject: Add async-signal-safe flavor of demangle X-Git-Tag: v0.22.0~778 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=35dc92a727f253dfd2594ffd12a03ce166d8ffef Add async-signal-safe flavor of demangle Summary: To be used in the new fatal signal handler. Test Plan: test added Reviewed By: lucian@fb.com FB internal diff: D1076169 @override-unit-failures --- diff --git a/folly/String.cpp b/folly/String.cpp index aae439fc..44b6a783 100644 --- a/folly/String.cpp +++ b/folly/String.cpp @@ -25,9 +25,27 @@ #include #undef FOLLY_DEMANGLE + #if defined(__GNUG__) && __GNUG__ >= 4 # include # define FOLLY_DEMANGLE 1 + +// From libiberty +// +// TODO(tudorb): Detect this with autoconf for the open-source version. +// +// __attribute__((weak)) doesn't work, because cplus_demangle_v3_callback +// is exported by an object file in libiberty.a, and the ELF spec says +// "The link editor does not extract archive members to resolve undefined weak +// symbols" (but, interestingly enough, will resolve undefined weak symbols +// with definitions from archive members that were extracted in order to +// resolve an undefined global (strong) symbol) +extern "C" int cplus_demangle_v3_callback( + const char* mangled, + int options, // We use DMGL_PARAMS | DMGL_TYPES, aka 0x11 + void (*callback)(const char*, size_t, void*), + void* arg); + #endif namespace folly { @@ -264,6 +282,21 @@ fbstring errnoStr(int err) { return result; } +namespace { + +// glibc doesn't have strlcpy +size_t my_strlcpy(char* dest, const char* src, size_t size) { + size_t len = strlen(src); + if (size != 0) { + size_t n = std::min(len, size - 1); // always null terminate! + memcpy(dest, src, n); + dest[n] = '\0'; + } + return len; +} + +} // namespace + #ifdef FOLLY_DEMANGLE fbstring demangle(const char* name) { @@ -279,12 +312,56 @@ fbstring demangle(const char* name) { return fbstring(demangled, strlen(demangled), len, AcquireMallocatedString()); } +namespace { + +struct DemangleBuf { + char* dest; + size_t remaining; + size_t total; +}; + +void demangleCallback(const char* str, size_t size, void* p) { + DemangleBuf* buf = static_cast(p); + size_t n = std::min(buf->remaining, size); + memcpy(buf->dest, str, n); + buf->dest += n; + buf->remaining -= n; + buf->total += size; +} + +} // namespace + +size_t demangle(const char* name, char* out, size_t outSize) { + 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 = cplus_demangle_v3_callback( + name, + 0x11, // DMGL_PARAMS | DMGL_TYPES + demangleCallback, + &dbuf); + if (status == 0) { // failed, return original + return my_strlcpy(out, name, outSize); + } + if (outSize != 0) { + *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) { + return my_strlcpy(out, name, outSize); +} + #endif #undef FOLLY_DEMANGLE diff --git a/folly/String.h b/folly/String.h index d706a032..da93cd26 100644 --- a/folly/String.h +++ b/folly/String.h @@ -322,13 +322,36 @@ fbstring errnoStr(int err); * * Use for debugging -- do not rely on demangle() returning anything useful. * - * This function may allocate memory (and therefore throw). + * This function may allocate memory (and therefore throw std::bad_alloc). */ fbstring demangle(const char* name); inline fbstring demangle(const std::type_info& type) { return demangle(type.name()); } +/** + * Return the demangled (prettyfied) version of a C++ type in a user-provided + * buffer. + * + * The semantics are the same as for snprintf or strlcpy: bufSize is the size + * of the buffer, the string is always null-terminated, and the return value is + * the number of characters (not including the null terminator) that would have + * been written if the buffer was big enough. (So a return value >= bufSize + * indicates that the output was truncated) + * + * This function does not allocate memory and is async-signal-safe. + * + * Note that the underlying function for the fbstring-returning demangle is + * somewhat standard (abi::__cxa_demangle, which uses malloc), the underlying + * function for this version is less so (cplus_demangle_v3_callback from + * libiberty), so it is possible for the fbstring version to work, while this + * version returns the original, mangled name. + */ +size_t demangle(const char* name, char* buf, size_t bufSize); +inline size_t demangle(const std::type_info& type, char* buf, size_t bufSize) { + return demangle(type.name(), buf, bufSize); +} + /** * Debug string for an exception: include type and what(). */ diff --git a/folly/test/StringTest.cpp b/folly/test/StringTest.cpp index c48c4059..54547b5a 100644 --- a/folly/test/StringTest.cpp +++ b/folly/test/StringTest.cpp @@ -464,8 +464,23 @@ struct ThisIsAVeryLongStructureName { } // namespace folly_test TEST(System, demangle) { - EXPECT_EQ("folly_test::ThisIsAVeryLongStructureName", - demangle(typeid(folly_test::ThisIsAVeryLongStructureName))); + char expected[] = "folly_test::ThisIsAVeryLongStructureName"; + EXPECT_STREQ( + expected, + demangle(typeid(folly_test::ThisIsAVeryLongStructureName)).c_str()); + + { + 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); + } } namespace {