diff --git a/CMakeLists.txt b/CMakeLists.txt index f3dba78..c77161d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -586,10 +586,6 @@ if (BUILD_TESTING) add_test (NAME demangle COMMAND demangle_unittest) - if (HAVE___CXA_DEMANGLE) - # Demangle tests use a different (reduced) representation of symbols - set_tests_properties (demangle PROPERTIES DISABLED ON) - endif (HAVE___CXA_DEMANGLE) if (HAVE_STACKTRACE) add_executable (stacktrace_unittest diff --git a/src/demangle.cc b/src/demangle.cc index 9902c1e..8f4db36 100644 --- a/src/demangle.cc +++ b/src/demangle.cc @@ -37,6 +37,7 @@ #include "demangle.h" #include +#include #include #include @@ -1350,7 +1351,15 @@ bool Demangle(const char* mangled, char* out, size_t out_size) { return false; } - std::copy_n(unmangled.get(), std::min(n, out_size), out); + if (out_size > 0) { + assert(n > 1); + // n is the size of the allocated buffer, not the length of the string. + // Therefore, it includes the terminating zero (and possibly additional + // space). + std::size_t copy_size = std::min(n, out_size) - 1; + std::copy_n(unmangled.get(), copy_size, out); + out[copy_size] = '\0'; // Ensure terminating null if n > out_size + } return status == 0; #else State state; diff --git a/src/demangle_unittest.cc b/src/demangle_unittest.cc index a851664..2dcbd34 100644 --- a/src/demangle_unittest.cc +++ b/src/demangle_unittest.cc @@ -89,12 +89,10 @@ TEST(Demangle, CornerCases) { EXPECT_STREQ(demangled, tmp); EXPECT_TRUE(Demangle(mangled, tmp, size - 1)); EXPECT_STREQ(demangled, tmp); - EXPECT_FALSE(Demangle(mangled, tmp, size - 2)); // Not enough. - EXPECT_FALSE(Demangle(mangled, tmp, 1)); - EXPECT_FALSE(Demangle(mangled, tmp, 0)); - EXPECT_FALSE(Demangle(mangled, nullptr, 0)); // Should not cause SEGV. } +// Demangle tests use a different (reduced) representation of symbols +# if !defined(HAVE___CXA_DEMANGLE) // Test handling of functions suffixed with .clone.N, which is used by GCC // 4.5.x, and .constprop.N and .isra.N, which are used by GCC 4.6.x. These // suffixes are used to indicate functions which have been cloned during @@ -141,6 +139,19 @@ TEST(Demangle, FromFile) { EXPECT_EQ(demangled, DemangleIt(mangled.c_str())); } } +# endif // defined(HAVE___CXA_DEMANGLE) + +TEST(Demangle, SmallBuffer) { + constexpr std::size_t size = 10; + char tmp[size]; + const char* const mangled = "_Z6foobarv"; + EXPECT_FALSE(Demangle(mangled, tmp, size - 2)); // Not enough. + EXPECT_FALSE(Demangle(mangled, tmp, 1)); + EXPECT_FALSE(Demangle(mangled, tmp, 0)); + EXPECT_FALSE(Demangle(mangled, nullptr, 0)); // Should not cause SEGV. + EXPECT_FALSE(Demangle("", tmp, sizeof(tmp))); + EXPECT_TRUE(Demangle("_Z3foo", nullptr, 0)); +} #endif diff --git a/src/symbolize_unittest.cc b/src/symbolize_unittest.cc index f07484e..ac04d4e 100644 --- a/src/symbolize_unittest.cc +++ b/src/symbolize_unittest.cc @@ -34,13 +34,16 @@ #include "symbolize.h" #include +#include #include +#include +#include #include "config.h" #include "glog/logging.h" #include "googletest.h" -#include "utilities.h" #include "stacktrace.h" +#include "utilities.h" #ifdef GLOG_USE_GFLAGS # include @@ -133,6 +136,36 @@ TEST(Symbolize, Symbolize) { struct Foo { static void func(int x); + static size_t longParamFunc(std::map, + std::map> + p0, + std::map, + std::map> + p1, + std::map, + std::map> + p2, + std::map, + std::map> + p3, + std::map, + std::map> + p4, + std::map, + std::map> + p5, + std::map, + std::map> + p6, + std::map, + std::map> + p7, + std::map, + std::map> + p8, + std::map, + std::map> + p9); }; void ATTRIBUTE_NOINLINE Foo::func(int x) { @@ -142,6 +175,41 @@ void ATTRIBUTE_NOINLINE Foo::func(int x) { a = a + 1; } +size_t ATTRIBUTE_NOINLINE +Foo::longParamFunc(std::map, + std::map> + p0, + std::map, + std::map> + p1, + std::map, + std::map> + p2, + std::map, + std::map> + p3, + std::map, + std::map> + p4, + std::map, + std::map> + p5, + std::map, + std::map> + p6, + std::map, + std::map> + p7, + std::map, + std::map> + p8, + std::map, + std::map> + p9) { + return p0.size() + p1.size() + p2.size() + p3.size() + p4.size() + p5.size() + + p6.size() + p7.size() + p8.size() + p9.size(); +} + // With a modern GCC, Symbolize() should return demangled symbol // names. Function parameters should be omitted. # ifdef TEST_WITH_MODERN_GCC @@ -150,6 +218,10 @@ TEST(Symbolize, SymbolizeWithDemangling) { # if !defined(_MSC_VER) || !defined(NDEBUG) # if defined(HAVE___CXA_DEMANGLE) EXPECT_STREQ("Foo::func(int)", TrySymbolize((void*)(&Foo::func))); + // Very long functions can be truncated, but we should not crash or return + // null. Also the result should start properly. + const char* symbol = TrySymbolize((void*)(&Foo::longParamFunc)); + EXPECT_TRUE(symbol == std::strstr(symbol, "Foo::longParamFunc(")); # else EXPECT_STREQ("Foo::func()", TrySymbolize((void*)(&Foo::func))); # endif