symbolize_unittest.cc 11.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
// 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.
//
's avatar
committed
30 31 32 33
// Author: Satoru Takabayashi
//
// Unit tests for functions in symbolize.cc.

34 35
#include "utilities.h"

's avatar
committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
#include <signal.h>
#include <iostream>

#include "glog/logging.h"
#include "symbolize.h"
#include "googletest.h"
#include "config.h"

using namespace std;
using namespace GOOGLE_NAMESPACE;

#if defined(HAVE_STACKTRACE) && defined(__ELF__)

#define always_inline

// 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

// 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;
  }
}

// Make them C linkage to avoid mangled names.
extern "C" {
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)));
  EXPECT_STREQ("static_func", TrySymbolize((void *)(&static_func)));

  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.
158
static ATTRIBUTE_NOINLINE bool StackGrowsDown(int *x) {
's avatar
committed
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
  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).
196 197
  stack_t sigstk;
  memset(&sigstk, 0, sizeof(stack_t));
's avatar
committed
198 199 200 201 202 203 204
  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).
205 206
  struct sigaction sa;
  memset(&sa, 0, sizeof(struct sigaction));
's avatar
committed
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
  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;
}

// Symbolize stack consumption should be within 2kB.
const int kStackConsumptionUpperLimit = 2048;

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);

  symbol = SymbolizeStackConsumption((void *)(&static_func),
                                     &stack_consumed);
  EXPECT_STREQ("static_func", symbol);
  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() {
  register 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() {
  register void *pc = NULL;
#ifdef TEST_X86_32_AND_64
  __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
#endif
  return pc;
}

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);
311
  CHECK_STREQ(symbol, "non_inline_func");
's avatar
committed
312 313 314 315 316 317 318 319 320
  cout << "Test case TestWithPCInsideNonInlineFunction passed." << endl;
#endif
}

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);
321
  CHECK_STREQ(symbol, __FUNCTION__);
's avatar
committed
322 323 324 325 326 327 328 329 330 331 332
  cout << "Test case TestWithPCInsideInlineFunction passed." << endl;
#endif
}
}

// Test with a return address.
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);
333
  CHECK_STREQ(symbol, "main");
's avatar
committed
334 335 336 337 338 339 340
  cout << "Test case TestWithReturnAddress passed." << endl;
#endif
}

int main(int argc, char **argv) {
  FLAGS_logtostderr = true;
  InitGoogleLogging(argv[0]);
's avatar
committed
341
  InitGoogleTest(&argc, argv);
342
#ifdef HAVE_SYMBOLIZE
343 344 345
  // 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);
's avatar
committed
346 347 348 349 350

  TestWithPCInsideInlineFunction();
  TestWithPCInsideNonInlineFunction();
  TestWithReturnAddress();
  return RUN_ALL_TESTS();
351 352
#else
  return 0;
's avatar
committed
353 354 355 356 357
#endif
}

#else
int main() {
358 359 360
#ifdef HAVE_SYMBOLIZE
  printf("PASS (no symbolize_unittest support)\n");
#else
's avatar
committed
361
  printf("PASS (no symbolize support)\n");
362
#endif
's avatar
committed
363 364 365
  return 0;
}
#endif  // HAVE_STACKTRACE