blob: 9a17e7b2aa2d6831032402c4fa29e027db5f3a1a [file] [log] [blame]
Austin Schuh906616c2019-01-21 20:25:11 -08001// 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
44#ifdef HAVE_LIB_GFLAGS
45#include <gflags/gflags.h>
46using namespace GFLAGS_NAMESPACE;
47#endif
48
49using namespace std;
50using namespace GOOGLE_NAMESPACE;
51
52#if defined(HAVE_STACKTRACE)
53
54#define always_inline
55
56// A wrapper function for Symbolize() to make the unit test simple.
57static const char *TrySymbolize(void *pc) {
58 static char symbol[4096];
59 if (Symbolize(pc, symbol, sizeof(symbol))) {
60 return symbol;
61 } else {
62 return NULL;
63 }
64}
65
66# if defined(__ELF__)
67
68// This unit tests make sense only with GCC.
69// Uses lots of GCC specific features.
70#if defined(__GNUC__) && !defined(__OPENCC__)
71# if __GNUC__ >= 4
72# define TEST_WITH_MODERN_GCC
73# if __i386__ // always_inline isn't supported for x86_64 with GCC 4.1.0.
74# undef always_inline
75# define always_inline __attribute__((always_inline))
76# define HAVE_ALWAYS_INLINE
77# endif // __i386__
78# else
79# endif // __GNUC__ >= 4
80# if defined(__i386__) || defined(__x86_64__)
81# define TEST_X86_32_AND_64 1
82# endif // defined(__i386__) || defined(__x86_64__)
83#endif
84
85// Make them C linkage to avoid mangled names.
86extern "C" {
87void nonstatic_func();
88void nonstatic_func() {
89 volatile int a = 0;
90 ++a;
91}
92
93static void static_func() {
94 volatile int a = 0;
95 ++a;
96}
97}
98
99TEST(Symbolize, Symbolize) {
100 // We do C-style cast since GCC 2.95.3 doesn't allow
101 // reinterpret_cast<void *>(&func).
102
103 // Compilers should give us pointers to them.
104 EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));
105
106 // The name of an internal linkage symbol is not specified; allow either a
107 // mangled or an unmangled name here.
108 const char *static_func_symbol = TrySymbolize((void *)(&static_func));
109 CHECK(NULL != static_func_symbol);
110 EXPECT_TRUE(strcmp("static_func", static_func_symbol) == 0 ||
111 strcmp("static_func()", static_func_symbol) == 0);
112
113 EXPECT_TRUE(NULL == TrySymbolize(NULL));
114}
115
116struct Foo {
117 static void func(int x);
118};
119
120void ATTRIBUTE_NOINLINE Foo::func(int x) {
121 volatile int a = x;
122 ++a;
123}
124
125// With a modern GCC, Symbolize() should return demangled symbol
126// names. Function parameters should be omitted.
127#ifdef TEST_WITH_MODERN_GCC
128TEST(Symbolize, SymbolizeWithDemangling) {
129 Foo::func(100);
130 EXPECT_STREQ("Foo::func()", TrySymbolize((void *)(&Foo::func)));
131}
132#endif
133
134// Tests that verify that Symbolize footprint is within some limit.
135
136// To measure the stack footprint of the Symbolize function, we create
137// a signal handler (for SIGUSR1 say) that calls the Symbolize function
138// on an alternate stack. This alternate stack is initialized to some
139// known pattern (0x55, 0x55, 0x55, ...). We then self-send this signal,
140// and after the signal handler returns, look at the alternate stack
141// buffer to see what portion has been touched.
142//
143// This trick gives us the the stack footprint of the signal handler.
144// But the signal handler, even before the call to Symbolize, consumes
145// some stack already. We however only want the stack usage of the
146// Symbolize function. To measure this accurately, we install two signal
147// handlers: one that does nothing and just returns, and another that
148// calls Symbolize. The difference between the stack consumption of these
149// two signals handlers should give us the Symbolize stack foorprint.
150
151static void *g_pc_to_symbolize;
152static char g_symbolize_buffer[4096];
153static char *g_symbolize_result;
154
155static void EmptySignalHandler(int signo) {}
156
157static void SymbolizeSignalHandler(int signo) {
158 if (Symbolize(g_pc_to_symbolize, g_symbolize_buffer,
159 sizeof(g_symbolize_buffer))) {
160 g_symbolize_result = g_symbolize_buffer;
161 } else {
162 g_symbolize_result = NULL;
163 }
164}
165
166const int kAlternateStackSize = 8096;
167const char kAlternateStackFillValue = 0x55;
168
169// These helper functions look at the alternate stack buffer, and figure
170// out what portion of this buffer has been touched - this is the stack
171// consumption of the signal handler running on this alternate stack.
172static ATTRIBUTE_NOINLINE bool StackGrowsDown(int *x) {
173 int y;
174 return &y < x;
175}
176static int GetStackConsumption(const char* alt_stack) {
177 int x;
178 if (StackGrowsDown(&x)) {
179 for (int i = 0; i < kAlternateStackSize; i++) {
180 if (alt_stack[i] != kAlternateStackFillValue) {
181 return (kAlternateStackSize - i);
182 }
183 }
184 } else {
185 for (int i = (kAlternateStackSize - 1); i >= 0; i--) {
186 if (alt_stack[i] != kAlternateStackFillValue) {
187 return i;
188 }
189 }
190 }
191 return -1;
192}
193
194#ifdef HAVE_SIGALTSTACK
195
196// Call Symbolize and figure out the stack footprint of this call.
197static const char *SymbolizeStackConsumption(void *pc, int *stack_consumed) {
198
199 g_pc_to_symbolize = pc;
200
201 // The alt-signal-stack cannot be heap allocated because there is a
202 // bug in glibc-2.2 where some signal handler setup code looks at the
203 // current stack pointer to figure out what thread is currently running.
204 // Therefore, the alternate stack must be allocated from the main stack
205 // itself.
206 char altstack[kAlternateStackSize];
207 memset(altstack, kAlternateStackFillValue, kAlternateStackSize);
208
209 // Set up the alt-signal-stack (and save the older one).
210 stack_t sigstk;
211 memset(&sigstk, 0, sizeof(stack_t));
212 stack_t old_sigstk;
213 sigstk.ss_sp = altstack;
214 sigstk.ss_size = kAlternateStackSize;
215 sigstk.ss_flags = 0;
216 CHECK_ERR(sigaltstack(&sigstk, &old_sigstk));
217
218 // Set up SIGUSR1 and SIGUSR2 signal handlers (and save the older ones).
219 struct sigaction sa;
220 memset(&sa, 0, sizeof(struct sigaction));
221 struct sigaction old_sa1, old_sa2;
222 sigemptyset(&sa.sa_mask);
223 sa.sa_flags = SA_ONSTACK;
224
225 // SIGUSR1 maps to EmptySignalHandler.
226 sa.sa_handler = EmptySignalHandler;
227 CHECK_ERR(sigaction(SIGUSR1, &sa, &old_sa1));
228
229 // SIGUSR2 maps to SymbolizeSignalHanlder.
230 sa.sa_handler = SymbolizeSignalHandler;
231 CHECK_ERR(sigaction(SIGUSR2, &sa, &old_sa2));
232
233 // Send SIGUSR1 signal and measure the stack consumption of the empty
234 // signal handler.
235 CHECK_ERR(kill(getpid(), SIGUSR1));
236 int stack_consumption1 = GetStackConsumption(altstack);
237
238 // Send SIGUSR2 signal and measure the stack consumption of the symbolize
239 // signal handler.
240 CHECK_ERR(kill(getpid(), SIGUSR2));
241 int stack_consumption2 = GetStackConsumption(altstack);
242
243 // The difference between the two stack consumption values is the
244 // stack footprint of the Symbolize function.
245 if (stack_consumption1 != -1 && stack_consumption2 != -1) {
246 *stack_consumed = stack_consumption2 - stack_consumption1;
247 } else {
248 *stack_consumed = -1;
249 }
250
251 // Log the stack consumption values.
252 LOG(INFO) << "Stack consumption of empty signal handler: "
253 << stack_consumption1;
254 LOG(INFO) << "Stack consumption of symbolize signal handler: "
255 << stack_consumption2;
256 LOG(INFO) << "Stack consumption of Symbolize: " << *stack_consumed;
257
258 // Now restore the old alt-signal-stack and signal handlers.
259 CHECK_ERR(sigaltstack(&old_sigstk, NULL));
260 CHECK_ERR(sigaction(SIGUSR1, &old_sa1, NULL));
261 CHECK_ERR(sigaction(SIGUSR2, &old_sa2, NULL));
262
263 return g_symbolize_result;
264}
265
266#ifdef __ppc64__
267// Symbolize stack consumption should be within 4kB.
268const int kStackConsumptionUpperLimit = 4096;
269#else
270// Symbolize stack consumption should be within 2kB.
271const int kStackConsumptionUpperLimit = 2048;
272#endif
273
274TEST(Symbolize, SymbolizeStackConsumption) {
275 int stack_consumed;
276 const char* symbol;
277
278 symbol = SymbolizeStackConsumption((void *)(&nonstatic_func),
279 &stack_consumed);
280 EXPECT_STREQ("nonstatic_func", symbol);
281 EXPECT_GT(stack_consumed, 0);
282 EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
283
284 // The name of an internal linkage symbol is not specified; allow either a
285 // mangled or an unmangled name here.
286 symbol = SymbolizeStackConsumption((void *)(&static_func),
287 &stack_consumed);
288 CHECK(NULL != symbol);
289 EXPECT_TRUE(strcmp("static_func", symbol) == 0 ||
290 strcmp("static_func()", symbol) == 0);
291 EXPECT_GT(stack_consumed, 0);
292 EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
293}
294
295#ifdef TEST_WITH_MODERN_GCC
296TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) {
297 Foo::func(100);
298 int stack_consumed;
299 const char* symbol;
300
301 symbol = SymbolizeStackConsumption((void *)(&Foo::func), &stack_consumed);
302
303 EXPECT_STREQ("Foo::func()", symbol);
304 EXPECT_GT(stack_consumed, 0);
305 EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
306}
307#endif
308
309#endif // HAVE_SIGALTSTACK
310
311// x86 specific tests. Uses some inline assembler.
312extern "C" {
313inline void* always_inline inline_func() {
314 void *pc = NULL;
315#ifdef TEST_X86_32_AND_64
316 __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
317#endif
318 return pc;
319}
320
321void* ATTRIBUTE_NOINLINE non_inline_func();
322void* ATTRIBUTE_NOINLINE non_inline_func() {
323 void *pc = NULL;
324#ifdef TEST_X86_32_AND_64
325 __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
326#endif
327 return pc;
328}
329
330static void ATTRIBUTE_NOINLINE TestWithPCInsideNonInlineFunction() {
331#if defined(TEST_X86_32_AND_64) && defined(HAVE_ATTRIBUTE_NOINLINE)
332 void *pc = non_inline_func();
333 const char *symbol = TrySymbolize(pc);
334 CHECK(symbol != NULL);
335 CHECK_STREQ(symbol, "non_inline_func");
336 cout << "Test case TestWithPCInsideNonInlineFunction passed." << endl;
337#endif
338}
339
340static void ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {
341#if defined(TEST_X86_32_AND_64) && defined(HAVE_ALWAYS_INLINE)
342 void *pc = inline_func(); // Must be inlined.
343 const char *symbol = TrySymbolize(pc);
344 CHECK(symbol != NULL);
345 CHECK_STREQ(symbol, __FUNCTION__);
346 cout << "Test case TestWithPCInsideInlineFunction passed." << endl;
347#endif
348}
349}
350
351// Test with a return address.
352static void ATTRIBUTE_NOINLINE TestWithReturnAddress() {
353#if defined(HAVE_ATTRIBUTE_NOINLINE)
354 void *return_address = __builtin_return_address(0);
355 const char *symbol = TrySymbolize(return_address);
356 CHECK(symbol != NULL);
357 CHECK_STREQ(symbol, "main");
358 cout << "Test case TestWithReturnAddress passed." << endl;
359#endif
360}
361
362# elif defined(OS_WINDOWS) || defined(OS_CYGWIN)
363
364#ifdef _MSC_VER
365#include <intrin.h>
366#pragma intrinsic(_ReturnAddress)
367#endif
368
369struct Foo {
370 static void func(int x);
371};
372
373__declspec(noinline) void Foo::func(int x) {
374 volatile int a = x;
375 ++a;
376}
377
378TEST(Symbolize, SymbolizeWithDemangling) {
379 Foo::func(100);
380 const char* ret = TrySymbolize((void *)(&Foo::func));
381 EXPECT_STREQ("public: static void __cdecl Foo::func(int)", ret);
382}
383
384__declspec(noinline) void TestWithReturnAddress() {
385 void *return_address =
386#ifdef __GNUC__ // Cygwin and MinGW support
387 __builtin_return_address(0)
388#else
389 _ReturnAddress()
390#endif
391 ;
392 const char *symbol = TrySymbolize(return_address);
393 CHECK(symbol != NULL);
394 CHECK_STREQ(symbol, "main");
395 cout << "Test case TestWithReturnAddress passed." << endl;
396}
397# endif // __ELF__
398#endif // HAVE_STACKTRACE
399
400int main(int argc, char **argv) {
401 FLAGS_logtostderr = true;
402 InitGoogleLogging(argv[0]);
403 InitGoogleTest(&argc, argv);
404#if defined(HAVE_SYMBOLIZE)
405# if defined(__ELF__)
406 // We don't want to get affected by the callback interface, that may be
407 // used to install some callback function at InitGoogle() time.
408 InstallSymbolizeCallback(NULL);
409
410 TestWithPCInsideInlineFunction();
411 TestWithPCInsideNonInlineFunction();
412 TestWithReturnAddress();
413 return RUN_ALL_TESTS();
414# elif defined(OS_WINDOWS) || defined(OS_CYGWIN)
415 TestWithReturnAddress();
416 return RUN_ALL_TESTS();
417# else // OS_WINDOWS
418 printf("PASS (no symbolize_unittest support)\n");
419 return 0;
420# endif // __ELF__
421#else
422 printf("PASS (no symbolize support)\n");
423 return 0;
424#endif // HAVE_SYMBOLIZE
425}