PageRenderTime 44ms CodeModel.GetById 11ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/third_party/glog/src/symbolize_unittest.cc

http://github.com/tomahawk-player/tomahawk
C++ | 365 lines | 230 code | 53 blank | 82 comment | 29 complexity | cb15f2dad31c6347b424f43ce10d27d5 MD5 | raw file
  1// Copyright (c) 2006, Google Inc.
  2// All rights reserved.
  3//
  4// Redistribution and use in source and binary forms, with or without
  5// modification, are permitted provided that the following conditions are
  6// met:
  7//
  8//     * Redistributions of source code must retain the above copyright
  9// notice, this list of conditions and the following disclaimer.
 10//     * Redistributions in binary form must reproduce the above
 11// copyright notice, this list of conditions and the following disclaimer
 12// in the documentation and/or other materials provided with the
 13// distribution.
 14//     * Neither the name of Google Inc. nor the names of its
 15// contributors may be used to endorse or promote products derived from
 16// this software without specific prior written permission.
 17//
 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 29//
 30// Author: Satoru Takabayashi
 31//
 32// Unit tests for functions in symbolize.cc.
 33
 34#include "utilities.h"
 35
 36#include <signal.h>
 37#include <iostream>
 38
 39#include "glog/logging.h"
 40#include "symbolize.h"
 41#include "googletest.h"
 42#include "config.h"
 43
 44using namespace std;
 45using namespace GOOGLE_NAMESPACE;
 46
 47#if defined(HAVE_STACKTRACE) && defined(__ELF__)
 48
 49#define always_inline
 50
 51// This unit tests make sense only with GCC.
 52// Uses lots of GCC specific features.
 53#if defined(__GNUC__) && !defined(__OPENCC__)
 54#  if __GNUC__ >= 4
 55#    define TEST_WITH_MODERN_GCC
 56#    if __i386__  // always_inline isn't supported for x86_64 with GCC 4.1.0.
 57#      undef always_inline
 58#      define always_inline __attribute__((always_inline))
 59#      define HAVE_ALWAYS_INLINE
 60#    endif  // __i386__
 61#  else
 62#  endif  // __GNUC__ >= 4
 63#  if defined(__i386__) || defined(__x86_64__)
 64#    define TEST_X86_32_AND_64 1
 65#  endif  // defined(__i386__) || defined(__x86_64__)
 66#endif
 67
 68// A wrapper function for Symbolize() to make the unit test simple.
 69static const char *TrySymbolize(void *pc) {
 70  static char symbol[4096];
 71  if (Symbolize(pc, symbol, sizeof(symbol))) {
 72    return symbol;
 73  } else {
 74    return NULL;
 75  }
 76}
 77
 78// Make them C linkage to avoid mangled names.
 79extern "C" {
 80void nonstatic_func() {
 81  volatile int a = 0;
 82  ++a;
 83}
 84
 85static void static_func() {
 86  volatile int a = 0;
 87  ++a;
 88}
 89}
 90
 91TEST(Symbolize, Symbolize) {
 92  // We do C-style cast since GCC 2.95.3 doesn't allow
 93  // reinterpret_cast<void *>(&func).
 94
 95  // Compilers should give us pointers to them.
 96  EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));
 97  EXPECT_STREQ("static_func", TrySymbolize((void *)(&static_func)));
 98
 99  EXPECT_TRUE(NULL == TrySymbolize(NULL));
100}
101
102struct Foo {
103  static void func(int x);
104};
105
106void ATTRIBUTE_NOINLINE Foo::func(int x) {
107  volatile int a = x;
108  ++a;
109}
110
111// With a modern GCC, Symbolize() should return demangled symbol
112// names.  Function parameters should be omitted.
113#ifdef TEST_WITH_MODERN_GCC
114TEST(Symbolize, SymbolizeWithDemangling) {
115  Foo::func(100);
116  EXPECT_STREQ("Foo::func()", TrySymbolize((void *)(&Foo::func)));
117}
118#endif
119
120// Tests that verify that Symbolize footprint is within some limit.
121
122// To measure the stack footprint of the Symbolize function, we create
123// a signal handler (for SIGUSR1 say) that calls the Symbolize function
124// on an alternate stack. This alternate stack is initialized to some
125// known pattern (0x55, 0x55, 0x55, ...). We then self-send this signal,
126// and after the signal handler returns, look at the alternate stack
127// buffer to see what portion has been touched.
128//
129// This trick gives us the the stack footprint of the signal handler.
130// But the signal handler, even before the call to Symbolize, consumes
131// some stack already. We however only want the stack usage of the
132// Symbolize function. To measure this accurately, we install two signal
133// handlers: one that does nothing and just returns, and another that
134// calls Symbolize. The difference between the stack consumption of these
135// two signals handlers should give us the Symbolize stack foorprint.
136
137static void *g_pc_to_symbolize;
138static char g_symbolize_buffer[4096];
139static char *g_symbolize_result;
140
141static void EmptySignalHandler(int signo) {}
142
143static void SymbolizeSignalHandler(int signo) {
144  if (Symbolize(g_pc_to_symbolize, g_symbolize_buffer,
145                sizeof(g_symbolize_buffer))) {
146    g_symbolize_result = g_symbolize_buffer;
147  } else {
148    g_symbolize_result = NULL;
149  }
150}
151
152const int kAlternateStackSize = 8096;
153const char kAlternateStackFillValue = 0x55;
154
155// These helper functions look at the alternate stack buffer, and figure
156// out what portion of this buffer has been touched - this is the stack
157// consumption of the signal handler running on this alternate stack.
158static ATTRIBUTE_NOINLINE bool StackGrowsDown(int *x) {
159  int y;
160  return &y < x;
161}
162static int GetStackConsumption(const char* alt_stack) {
163  int x;
164  if (StackGrowsDown(&x)) {
165    for (int i = 0; i < kAlternateStackSize; i++) {
166      if (alt_stack[i] != kAlternateStackFillValue) {
167        return (kAlternateStackSize - i);
168      }
169    }
170  } else {
171    for (int i = (kAlternateStackSize - 1); i >= 0; i--) {
172      if (alt_stack[i] != kAlternateStackFillValue) {
173        return i;
174      }
175    }
176  }
177  return -1;
178}
179
180#ifdef HAVE_SIGALTSTACK
181
182// Call Symbolize and figure out the stack footprint of this call.
183static const char *SymbolizeStackConsumption(void *pc, int *stack_consumed) {
184
185  g_pc_to_symbolize = pc;
186
187  // The alt-signal-stack cannot be heap allocated because there is a
188  // bug in glibc-2.2 where some signal handler setup code looks at the
189  // current stack pointer to figure out what thread is currently running.
190  // Therefore, the alternate stack must be allocated from the main stack
191  // itself.
192  char altstack[kAlternateStackSize];
193  memset(altstack, kAlternateStackFillValue, kAlternateStackSize);
194
195  // Set up the alt-signal-stack (and save the older one).
196  stack_t sigstk;
197  memset(&sigstk, 0, sizeof(stack_t));
198  stack_t old_sigstk;
199  sigstk.ss_sp = altstack;
200  sigstk.ss_size = kAlternateStackSize;
201  sigstk.ss_flags = 0;
202  CHECK_ERR(sigaltstack(&sigstk, &old_sigstk));
203
204  // Set up SIGUSR1 and SIGUSR2 signal handlers (and save the older ones).
205  struct sigaction sa;
206  memset(&sa, 0, sizeof(struct sigaction));
207  struct sigaction old_sa1, old_sa2;
208  sigemptyset(&sa.sa_mask);
209  sa.sa_flags = SA_ONSTACK;
210
211  // SIGUSR1 maps to EmptySignalHandler.
212  sa.sa_handler = EmptySignalHandler;
213  CHECK_ERR(sigaction(SIGUSR1, &sa, &old_sa1));
214
215  // SIGUSR2 maps to SymbolizeSignalHanlder.
216  sa.sa_handler = SymbolizeSignalHandler;
217  CHECK_ERR(sigaction(SIGUSR2, &sa, &old_sa2));
218
219  // Send SIGUSR1 signal and measure the stack consumption of the empty
220  // signal handler.
221  CHECK_ERR(kill(getpid(), SIGUSR1));
222  int stack_consumption1 = GetStackConsumption(altstack);
223
224  // Send SIGUSR2 signal and measure the stack consumption of the symbolize
225  // signal handler.
226  CHECK_ERR(kill(getpid(), SIGUSR2));
227  int stack_consumption2 = GetStackConsumption(altstack);
228
229  // The difference between the two stack consumption values is the
230  // stack footprint of the Symbolize function.
231  if (stack_consumption1 != -1 && stack_consumption2 != -1) {
232    *stack_consumed = stack_consumption2 - stack_consumption1;
233  } else {
234    *stack_consumed = -1;
235  }
236
237  // Log the stack consumption values.
238  LOG(INFO) << "Stack consumption of empty signal handler: "
239            << stack_consumption1;
240  LOG(INFO) << "Stack consumption of symbolize signal handler: "
241            << stack_consumption2;
242  LOG(INFO) << "Stack consumption of Symbolize: " << *stack_consumed;
243
244  // Now restore the old alt-signal-stack and signal handlers.
245  CHECK_ERR(sigaltstack(&old_sigstk, NULL));
246  CHECK_ERR(sigaction(SIGUSR1, &old_sa1, NULL));
247  CHECK_ERR(sigaction(SIGUSR2, &old_sa2, NULL));
248
249  return g_symbolize_result;
250}
251
252// Symbolize stack consumption should be within 2kB.
253const int kStackConsumptionUpperLimit = 2048;
254
255TEST(Symbolize, SymbolizeStackConsumption) {
256  int stack_consumed;
257  const char* symbol;
258
259  symbol = SymbolizeStackConsumption((void *)(&nonstatic_func),
260                                     &stack_consumed);
261  EXPECT_STREQ("nonstatic_func", symbol);
262  EXPECT_GT(stack_consumed, 0);
263  EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
264
265  symbol = SymbolizeStackConsumption((void *)(&static_func),
266                                     &stack_consumed);
267  EXPECT_STREQ("static_func", symbol);
268  EXPECT_GT(stack_consumed, 0);
269  EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
270}
271
272#ifdef TEST_WITH_MODERN_GCC
273TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) {
274  Foo::func(100);
275  int stack_consumed;
276  const char* symbol;
277
278  symbol = SymbolizeStackConsumption((void *)(&Foo::func), &stack_consumed);
279
280  EXPECT_STREQ("Foo::func()", symbol);
281  EXPECT_GT(stack_consumed, 0);
282  EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
283}
284#endif
285
286#endif  // HAVE_SIGALTSTACK
287
288// x86 specific tests.  Uses some inline assembler.
289extern "C" {
290inline void* always_inline inline_func() {
291  register void *pc = NULL;
292#ifdef TEST_X86_32_AND_64
293  __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
294#endif
295  return pc;
296}
297
298void* ATTRIBUTE_NOINLINE non_inline_func() {
299  register void *pc = NULL;
300#ifdef TEST_X86_32_AND_64
301  __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
302#endif
303  return pc;
304}
305
306void ATTRIBUTE_NOINLINE TestWithPCInsideNonInlineFunction() {
307#if defined(TEST_X86_32_AND_64) && defined(HAVE_ATTRIBUTE_NOINLINE)
308  void *pc = non_inline_func();
309  const char *symbol = TrySymbolize(pc);
310  CHECK(symbol != NULL);
311  CHECK_STREQ(symbol, "non_inline_func");
312  cout << "Test case TestWithPCInsideNonInlineFunction passed." << endl;
313#endif
314}
315
316void ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {
317#if defined(TEST_X86_32_AND_64) && defined(HAVE_ALWAYS_INLINE)
318  void *pc = inline_func();  // Must be inlined.
319  const char *symbol = TrySymbolize(pc);
320  CHECK(symbol != NULL);
321  CHECK_STREQ(symbol, __FUNCTION__);
322  cout << "Test case TestWithPCInsideInlineFunction passed." << endl;
323#endif
324}
325}
326
327// Test with a return address.
328void ATTRIBUTE_NOINLINE TestWithReturnAddress() {
329#if defined(HAVE_ATTRIBUTE_NOINLINE)
330  void *return_address = __builtin_return_address(0);
331  const char *symbol = TrySymbolize(return_address);
332  CHECK(symbol != NULL);
333  CHECK_STREQ(symbol, "main");
334  cout << "Test case TestWithReturnAddress passed." << endl;
335#endif
336}
337
338int main(int argc, char **argv) {
339  FLAGS_logtostderr = true;
340  InitGoogleLogging(argv[0]);
341  InitGoogleTest(&argc, argv);
342#ifdef HAVE_SYMBOLIZE
343  // We don't want to get affected by the callback interface, that may be
344  // used to install some callback function at InitGoogle() time.
345  InstallSymbolizeCallback(NULL);
346
347  TestWithPCInsideInlineFunction();
348  TestWithPCInsideNonInlineFunction();
349  TestWithReturnAddress();
350  return RUN_ALL_TESTS();
351#else
352  return 0;
353#endif
354}
355
356#else
357int main() {
358#ifdef HAVE_SYMBOLIZE
359  printf("PASS (no symbolize_unittest support)\n");
360#else
361  printf("PASS (no symbolize support)\n");
362#endif
363  return 0;
364}
365#endif  // HAVE_STACKTRACE