425 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			425 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (c) 2006, Google Inc.
 | |
| // All rights reserved.
 | |
| //
 | |
| // Redistribution and use in source and binary forms, with or without
 | |
| // modification, are permitted provided that the following conditions are
 | |
| // met:
 | |
| //
 | |
| //     * Redistributions of source code must retain the above copyright
 | |
| // notice, this list of conditions and the following disclaimer.
 | |
| //     * Redistributions in binary form must reproduce the above
 | |
| // copyright notice, this list of conditions and the following disclaimer
 | |
| // in the documentation and/or other materials provided with the
 | |
| // distribution.
 | |
| //     * Neither the name of Google Inc. nor the names of its
 | |
| // contributors may be used to endorse or promote products derived from
 | |
| // this software without specific prior written permission.
 | |
| //
 | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
| //
 | |
| // Author: Satoru Takabayashi
 | |
| //
 | |
| // Unit tests for functions in symbolize.cc.
 | |
| 
 | |
| #include "utilities.h"
 | |
| 
 | |
| #include <signal.h>
 | |
| #include <iostream>
 | |
| 
 | |
| #include "glog/logging.h"
 | |
| #include "symbolize.h"
 | |
| #include "googletest.h"
 | |
| #include "config.h"
 | |
| 
 | |
| #ifdef HAVE_LIB_GFLAGS
 | |
| #include <gflags/gflags.h>
 | |
| using namespace GFLAGS_NAMESPACE;
 | |
| #endif
 | |
| 
 | |
| using namespace std;
 | |
| using namespace GOOGLE_NAMESPACE;
 | |
| 
 | |
| #if defined(HAVE_STACKTRACE)
 | |
| 
 | |
| #define always_inline
 | |
| 
 | |
| // A wrapper function for Symbolize() to make the unit test simple.
 | |
| static const char *TrySymbolize(void *pc) {
 | |
|   static char symbol[4096];
 | |
|   if (Symbolize(pc, symbol, sizeof(symbol))) {
 | |
|     return symbol;
 | |
|   } else {
 | |
|     return NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| # if defined(__ELF__)
 | |
| 
 | |
| // This unit tests make sense only with GCC.
 | |
| // Uses lots of GCC specific features.
 | |
| #if defined(__GNUC__) && !defined(__OPENCC__)
 | |
| #  if __GNUC__ >= 4
 | |
| #    define TEST_WITH_MODERN_GCC
 | |
| #    if __i386__  // always_inline isn't supported for x86_64 with GCC 4.1.0.
 | |
| #      undef always_inline
 | |
| #      define always_inline __attribute__((always_inline))
 | |
| #      define HAVE_ALWAYS_INLINE
 | |
| #    endif  // __i386__
 | |
| #  else
 | |
| #  endif  // __GNUC__ >= 4
 | |
| #  if defined(__i386__) || defined(__x86_64__)
 | |
| #    define TEST_X86_32_AND_64 1
 | |
| #  endif  // defined(__i386__) || defined(__x86_64__)
 | |
| #endif
 | |
| 
 | |
| // Make them C linkage to avoid mangled names.
 | |
| extern "C" {
 | |
| void nonstatic_func();
 | |
| void nonstatic_func() {
 | |
|   volatile int a = 0;
 | |
|   ++a;
 | |
| }
 | |
| 
 | |
| static void static_func() {
 | |
|   volatile int a = 0;
 | |
|   ++a;
 | |
| }
 | |
| }
 | |
| 
 | |
| TEST(Symbolize, Symbolize) {
 | |
|   // We do C-style cast since GCC 2.95.3 doesn't allow
 | |
|   // reinterpret_cast<void *>(&func).
 | |
| 
 | |
|   // Compilers should give us pointers to them.
 | |
|   EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));
 | |
| 
 | |
|   // The name of an internal linkage symbol is not specified; allow either a
 | |
|   // mangled or an unmangled name here.
 | |
|   const char *static_func_symbol = TrySymbolize((void *)(&static_func));
 | |
|   CHECK(NULL != static_func_symbol);
 | |
|   EXPECT_TRUE(strcmp("static_func", static_func_symbol) == 0 ||
 | |
|               strcmp("static_func()", static_func_symbol) == 0);
 | |
| 
 | |
|   EXPECT_TRUE(NULL == TrySymbolize(NULL));
 | |
| }
 | |
| 
 | |
| struct Foo {
 | |
|   static void func(int x);
 | |
| };
 | |
| 
 | |
| void ATTRIBUTE_NOINLINE Foo::func(int x) {
 | |
|   volatile int a = x;
 | |
|   ++a;
 | |
| }
 | |
| 
 | |
| // With a modern GCC, Symbolize() should return demangled symbol
 | |
| // names.  Function parameters should be omitted.
 | |
| #ifdef TEST_WITH_MODERN_GCC
 | |
| TEST(Symbolize, SymbolizeWithDemangling) {
 | |
|   Foo::func(100);
 | |
|   EXPECT_STREQ("Foo::func()", TrySymbolize((void *)(&Foo::func)));
 | |
| }
 | |
| #endif
 | |
| 
 | |
| // Tests that verify that Symbolize footprint is within some limit.
 | |
| 
 | |
| // To measure the stack footprint of the Symbolize function, we create
 | |
| // a signal handler (for SIGUSR1 say) that calls the Symbolize function
 | |
| // on an alternate stack. This alternate stack is initialized to some
 | |
| // known pattern (0x55, 0x55, 0x55, ...). We then self-send this signal,
 | |
| // and after the signal handler returns, look at the alternate stack
 | |
| // buffer to see what portion has been touched.
 | |
| //
 | |
| // This trick gives us the the stack footprint of the signal handler.
 | |
| // But the signal handler, even before the call to Symbolize, consumes
 | |
| // some stack already. We however only want the stack usage of the
 | |
| // Symbolize function. To measure this accurately, we install two signal
 | |
| // handlers: one that does nothing and just returns, and another that
 | |
| // calls Symbolize. The difference between the stack consumption of these
 | |
| // two signals handlers should give us the Symbolize stack foorprint.
 | |
| 
 | |
| static void *g_pc_to_symbolize;
 | |
| static char g_symbolize_buffer[4096];
 | |
| static char *g_symbolize_result;
 | |
| 
 | |
| static void EmptySignalHandler(int signo) {}
 | |
| 
 | |
| static void SymbolizeSignalHandler(int signo) {
 | |
|   if (Symbolize(g_pc_to_symbolize, g_symbolize_buffer,
 | |
|                 sizeof(g_symbolize_buffer))) {
 | |
|     g_symbolize_result = g_symbolize_buffer;
 | |
|   } else {
 | |
|     g_symbolize_result = NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| const int kAlternateStackSize = 8096;
 | |
| const char kAlternateStackFillValue = 0x55;
 | |
| 
 | |
| // These helper functions look at the alternate stack buffer, and figure
 | |
| // out what portion of this buffer has been touched - this is the stack
 | |
| // consumption of the signal handler running on this alternate stack.
 | |
| static ATTRIBUTE_NOINLINE bool StackGrowsDown(int *x) {
 | |
|   int y;
 | |
|   return &y < x;
 | |
| }
 | |
| static int GetStackConsumption(const char* alt_stack) {
 | |
|   int x;
 | |
|   if (StackGrowsDown(&x)) {
 | |
|     for (int i = 0; i < kAlternateStackSize; i++) {
 | |
|       if (alt_stack[i] != kAlternateStackFillValue) {
 | |
|         return (kAlternateStackSize - i);
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     for (int i = (kAlternateStackSize - 1); i >= 0; i--) {
 | |
|       if (alt_stack[i] != kAlternateStackFillValue) {
 | |
|         return i;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| #ifdef HAVE_SIGALTSTACK
 | |
| 
 | |
| // Call Symbolize and figure out the stack footprint of this call.
 | |
| static const char *SymbolizeStackConsumption(void *pc, int *stack_consumed) {
 | |
| 
 | |
|   g_pc_to_symbolize = pc;
 | |
| 
 | |
|   // The alt-signal-stack cannot be heap allocated because there is a
 | |
|   // bug in glibc-2.2 where some signal handler setup code looks at the
 | |
|   // current stack pointer to figure out what thread is currently running.
 | |
|   // Therefore, the alternate stack must be allocated from the main stack
 | |
|   // itself.
 | |
|   char altstack[kAlternateStackSize];
 | |
|   memset(altstack, kAlternateStackFillValue, kAlternateStackSize);
 | |
| 
 | |
|   // Set up the alt-signal-stack (and save the older one).
 | |
|   stack_t sigstk;
 | |
|   memset(&sigstk, 0, sizeof(stack_t));
 | |
|   stack_t old_sigstk;
 | |
|   sigstk.ss_sp = altstack;
 | |
|   sigstk.ss_size = kAlternateStackSize;
 | |
|   sigstk.ss_flags = 0;
 | |
|   CHECK_ERR(sigaltstack(&sigstk, &old_sigstk));
 | |
| 
 | |
|   // Set up SIGUSR1 and SIGUSR2 signal handlers (and save the older ones).
 | |
|   struct sigaction sa;
 | |
|   memset(&sa, 0, sizeof(struct sigaction));
 | |
|   struct sigaction old_sa1, old_sa2;
 | |
|   sigemptyset(&sa.sa_mask);
 | |
|   sa.sa_flags = SA_ONSTACK;
 | |
| 
 | |
|   // SIGUSR1 maps to EmptySignalHandler.
 | |
|   sa.sa_handler = EmptySignalHandler;
 | |
|   CHECK_ERR(sigaction(SIGUSR1, &sa, &old_sa1));
 | |
| 
 | |
|   // SIGUSR2 maps to SymbolizeSignalHanlder.
 | |
|   sa.sa_handler = SymbolizeSignalHandler;
 | |
|   CHECK_ERR(sigaction(SIGUSR2, &sa, &old_sa2));
 | |
| 
 | |
|   // Send SIGUSR1 signal and measure the stack consumption of the empty
 | |
|   // signal handler.
 | |
|   CHECK_ERR(kill(getpid(), SIGUSR1));
 | |
|   int stack_consumption1 = GetStackConsumption(altstack);
 | |
| 
 | |
|   // Send SIGUSR2 signal and measure the stack consumption of the symbolize
 | |
|   // signal handler.
 | |
|   CHECK_ERR(kill(getpid(), SIGUSR2));
 | |
|   int stack_consumption2 = GetStackConsumption(altstack);
 | |
| 
 | |
|   // The difference between the two stack consumption values is the
 | |
|   // stack footprint of the Symbolize function.
 | |
|   if (stack_consumption1 != -1 && stack_consumption2 != -1) {
 | |
|     *stack_consumed = stack_consumption2 - stack_consumption1;
 | |
|   } else {
 | |
|     *stack_consumed = -1;
 | |
|   }
 | |
| 
 | |
|   // Log the stack consumption values.
 | |
|   LOG(INFO) << "Stack consumption of empty signal handler: "
 | |
|             << stack_consumption1;
 | |
|   LOG(INFO) << "Stack consumption of symbolize signal handler: "
 | |
|             << stack_consumption2;
 | |
|   LOG(INFO) << "Stack consumption of Symbolize: " << *stack_consumed;
 | |
| 
 | |
|   // Now restore the old alt-signal-stack and signal handlers.
 | |
|   CHECK_ERR(sigaltstack(&old_sigstk, NULL));
 | |
|   CHECK_ERR(sigaction(SIGUSR1, &old_sa1, NULL));
 | |
|   CHECK_ERR(sigaction(SIGUSR2, &old_sa2, NULL));
 | |
| 
 | |
|   return g_symbolize_result;
 | |
| }
 | |
| 
 | |
| #ifdef __ppc64__
 | |
| // Symbolize stack consumption should be within 4kB.
 | |
| const int kStackConsumptionUpperLimit = 4096;
 | |
| #else
 | |
| // Symbolize stack consumption should be within 2kB.
 | |
| const int kStackConsumptionUpperLimit = 2048;
 | |
| #endif
 | |
| 
 | |
| TEST(Symbolize, SymbolizeStackConsumption) {
 | |
|   int stack_consumed;
 | |
|   const char* symbol;
 | |
| 
 | |
|   symbol = SymbolizeStackConsumption((void *)(&nonstatic_func),
 | |
|                                      &stack_consumed);
 | |
|   EXPECT_STREQ("nonstatic_func", symbol);
 | |
|   EXPECT_GT(stack_consumed, 0);
 | |
|   EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
 | |
| 
 | |
|   // The name of an internal linkage symbol is not specified; allow either a
 | |
|   // mangled or an unmangled name here.
 | |
|   symbol = SymbolizeStackConsumption((void *)(&static_func),
 | |
|                                      &stack_consumed);
 | |
|   CHECK(NULL != symbol);
 | |
|   EXPECT_TRUE(strcmp("static_func", symbol) == 0 ||
 | |
|               strcmp("static_func()", symbol) == 0);
 | |
|   EXPECT_GT(stack_consumed, 0);
 | |
|   EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
 | |
| }
 | |
| 
 | |
| #ifdef TEST_WITH_MODERN_GCC
 | |
| TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) {
 | |
|   Foo::func(100);
 | |
|   int stack_consumed;
 | |
|   const char* symbol;
 | |
| 
 | |
|   symbol = SymbolizeStackConsumption((void *)(&Foo::func), &stack_consumed);
 | |
| 
 | |
|   EXPECT_STREQ("Foo::func()", symbol);
 | |
|   EXPECT_GT(stack_consumed, 0);
 | |
|   EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #endif  // HAVE_SIGALTSTACK
 | |
| 
 | |
| // x86 specific tests.  Uses some inline assembler.
 | |
| extern "C" {
 | |
| inline void* always_inline inline_func() {
 | |
|   void *pc = NULL;
 | |
| #ifdef TEST_X86_32_AND_64
 | |
|   __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
 | |
| #endif
 | |
|   return pc;
 | |
| }
 | |
| 
 | |
| void* ATTRIBUTE_NOINLINE non_inline_func();
 | |
| void* ATTRIBUTE_NOINLINE non_inline_func() {
 | |
|   void *pc = NULL;
 | |
| #ifdef TEST_X86_32_AND_64
 | |
|   __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
 | |
| #endif
 | |
|   return pc;
 | |
| }
 | |
| 
 | |
| static void ATTRIBUTE_NOINLINE TestWithPCInsideNonInlineFunction() {
 | |
| #if defined(TEST_X86_32_AND_64) && defined(HAVE_ATTRIBUTE_NOINLINE)
 | |
|   void *pc = non_inline_func();
 | |
|   const char *symbol = TrySymbolize(pc);
 | |
|   CHECK(symbol != NULL);
 | |
|   CHECK_STREQ(symbol, "non_inline_func");
 | |
|   cout << "Test case TestWithPCInsideNonInlineFunction passed." << endl;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static void ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {
 | |
| #if defined(TEST_X86_32_AND_64) && defined(HAVE_ALWAYS_INLINE)
 | |
|   void *pc = inline_func();  // Must be inlined.
 | |
|   const char *symbol = TrySymbolize(pc);
 | |
|   CHECK(symbol != NULL);
 | |
|   CHECK_STREQ(symbol, __FUNCTION__);
 | |
|   cout << "Test case TestWithPCInsideInlineFunction passed." << endl;
 | |
| #endif
 | |
| }
 | |
| }
 | |
| 
 | |
| // Test with a return address.
 | |
| static void ATTRIBUTE_NOINLINE TestWithReturnAddress() {
 | |
| #if defined(HAVE_ATTRIBUTE_NOINLINE)
 | |
|   void *return_address = __builtin_return_address(0);
 | |
|   const char *symbol = TrySymbolize(return_address);
 | |
|   CHECK(symbol != NULL);
 | |
|   CHECK_STREQ(symbol, "main");
 | |
|   cout << "Test case TestWithReturnAddress passed." << endl;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| # elif defined(OS_WINDOWS) || defined(OS_CYGWIN)
 | |
| 
 | |
| #ifdef _MSC_VER
 | |
| #include <intrin.h>
 | |
| #pragma intrinsic(_ReturnAddress)
 | |
| #endif
 | |
| 
 | |
| struct Foo {
 | |
|   static void func(int x);
 | |
| };
 | |
| 
 | |
| __declspec(noinline) void Foo::func(int x) {
 | |
|   volatile int a = x;
 | |
|   ++a;
 | |
| }
 | |
| 
 | |
| TEST(Symbolize, SymbolizeWithDemangling) {
 | |
|   Foo::func(100);
 | |
|   const char* ret = TrySymbolize((void *)(&Foo::func));
 | |
|   EXPECT_STREQ("public: static void __cdecl Foo::func(int)", ret);
 | |
| }
 | |
| 
 | |
| __declspec(noinline) void TestWithReturnAddress() {
 | |
|   void *return_address =
 | |
| #ifdef __GNUC__ // Cygwin and MinGW support
 | |
| 	  __builtin_return_address(0)
 | |
| #else
 | |
| 	  _ReturnAddress()
 | |
| #endif
 | |
| 	  ;
 | |
|   const char *symbol = TrySymbolize(return_address);
 | |
|   CHECK(symbol != NULL);
 | |
|   CHECK_STREQ(symbol, "main");
 | |
|   cout << "Test case TestWithReturnAddress passed." << endl;
 | |
| }
 | |
| # endif  // __ELF__
 | |
| #endif  // HAVE_STACKTRACE
 | |
| 
 | |
| int main(int argc, char **argv) {
 | |
|   FLAGS_logtostderr = true;
 | |
|   InitGoogleLogging(argv[0]);
 | |
|   InitGoogleTest(&argc, argv);
 | |
| #if defined(HAVE_SYMBOLIZE) && defined(HAVE_STACKTRACE)
 | |
| # if defined(__ELF__)
 | |
|   // We don't want to get affected by the callback interface, that may be
 | |
|   // used to install some callback function at InitGoogle() time.
 | |
|   InstallSymbolizeCallback(NULL);
 | |
| 
 | |
|   TestWithPCInsideInlineFunction();
 | |
|   TestWithPCInsideNonInlineFunction();
 | |
|   TestWithReturnAddress();
 | |
|   return RUN_ALL_TESTS();
 | |
| # elif defined(OS_WINDOWS) || defined(OS_CYGWIN)
 | |
|   TestWithReturnAddress();
 | |
|   return RUN_ALL_TESTS();
 | |
| # else  // OS_WINDOWS
 | |
|   printf("PASS (no symbolize_unittest support)\n");
 | |
|   return 0;
 | |
| # endif  // __ELF__
 | |
| #else
 | |
|   printf("PASS (no symbolize support)\n");
 | |
|   return 0;
 | |
| #endif  // HAVE_SYMBOLIZE
 | |
| }
 |