blob: 2522e73f3e47a68747cf7eb4fe385b68bb07ac96 [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
James Kuszmaulba0ac1a2022-08-12 16:29:30 -070034#include "symbolize.h"
Austin Schuh906616c2019-01-21 20:25:11 -080035
James Kuszmaulba0ac1a2022-08-12 16:29:30 -070036#include <glog/logging.h>
37
38#include <csignal>
Austin Schuh906616c2019-01-21 20:25:11 -080039#include <iostream>
40
Austin Schuh906616c2019-01-21 20:25:11 -080041#include "config.h"
James Kuszmaulba0ac1a2022-08-12 16:29:30 -070042#include "googletest.h"
43#include "utilities.h"
Austin Schuh906616c2019-01-21 20:25:11 -080044
45#ifdef HAVE_LIB_GFLAGS
46#include <gflags/gflags.h>
47using namespace GFLAGS_NAMESPACE;
48#endif
49
50using namespace std;
51using namespace GOOGLE_NAMESPACE;
52
James Kuszmaulba0ac1a2022-08-12 16:29:30 -070053// Avoid compile error due to "cast between pointer-to-function and
54// pointer-to-object is an extension" warnings.
55#if defined(__GNUG__)
56#pragma GCC diagnostic push
57#pragma GCC diagnostic ignored "-Wpedantic"
58#endif
59
Austin Schuh906616c2019-01-21 20:25:11 -080060#if defined(HAVE_STACKTRACE)
61
62#define always_inline
63
James Kuszmaulba0ac1a2022-08-12 16:29:30 -070064#if defined(__ELF__) || defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN)
Austin Schuh906616c2019-01-21 20:25:11 -080065// A wrapper function for Symbolize() to make the unit test simple.
66static const char *TrySymbolize(void *pc) {
67 static char symbol[4096];
68 if (Symbolize(pc, symbol, sizeof(symbol))) {
69 return symbol;
70 } else {
71 return NULL;
72 }
73}
James Kuszmaulba0ac1a2022-08-12 16:29:30 -070074#endif
Austin Schuh906616c2019-01-21 20:25:11 -080075
76# if defined(__ELF__)
77
78// This unit tests make sense only with GCC.
79// Uses lots of GCC specific features.
80#if defined(__GNUC__) && !defined(__OPENCC__)
81# if __GNUC__ >= 4
82# define TEST_WITH_MODERN_GCC
James Kuszmaulba0ac1a2022-08-12 16:29:30 -070083# if defined(__i386__) && __i386__ // always_inline isn't supported for x86_64 with GCC 4.1.0.
Austin Schuh906616c2019-01-21 20:25:11 -080084# undef always_inline
85# define always_inline __attribute__((always_inline))
86# define HAVE_ALWAYS_INLINE
87# endif // __i386__
88# else
89# endif // __GNUC__ >= 4
90# if defined(__i386__) || defined(__x86_64__)
91# define TEST_X86_32_AND_64 1
92# endif // defined(__i386__) || defined(__x86_64__)
93#endif
94
95// Make them C linkage to avoid mangled names.
96extern "C" {
97void nonstatic_func();
98void nonstatic_func() {
99 volatile int a = 0;
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700100 // NOTE: In C++20, increment of object of volatile-qualified type is
101 // deprecated.
102 a = a + 1;
Austin Schuh906616c2019-01-21 20:25:11 -0800103}
104
105static void static_func() {
106 volatile int a = 0;
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700107 // NOTE: In C++20, increment of object of volatile-qualified type is
108 // deprecated.
109 a = a + 1;
Austin Schuh906616c2019-01-21 20:25:11 -0800110}
111}
112
113TEST(Symbolize, Symbolize) {
114 // We do C-style cast since GCC 2.95.3 doesn't allow
115 // reinterpret_cast<void *>(&func).
116
117 // Compilers should give us pointers to them.
118 EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));
119
120 // The name of an internal linkage symbol is not specified; allow either a
121 // mangled or an unmangled name here.
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700122 const char *static_func_symbol =
123 TrySymbolize(reinterpret_cast<void *>(&static_func));
124
125#if !defined(_MSC_VER) || !defined(NDEBUG)
Austin Schuh906616c2019-01-21 20:25:11 -0800126 CHECK(NULL != static_func_symbol);
127 EXPECT_TRUE(strcmp("static_func", static_func_symbol) == 0 ||
128 strcmp("static_func()", static_func_symbol) == 0);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700129#endif
Austin Schuh906616c2019-01-21 20:25:11 -0800130
131 EXPECT_TRUE(NULL == TrySymbolize(NULL));
132}
133
134struct Foo {
135 static void func(int x);
136};
137
138void ATTRIBUTE_NOINLINE Foo::func(int x) {
139 volatile int a = x;
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700140 // NOTE: In C++20, increment of object of volatile-qualified type is
141 // deprecated.
142 a = a + 1;
Austin Schuh906616c2019-01-21 20:25:11 -0800143}
144
145// With a modern GCC, Symbolize() should return demangled symbol
146// names. Function parameters should be omitted.
147#ifdef TEST_WITH_MODERN_GCC
148TEST(Symbolize, SymbolizeWithDemangling) {
149 Foo::func(100);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700150#if !defined(_MSC_VER) || !defined(NDEBUG)
Austin Schuh906616c2019-01-21 20:25:11 -0800151 EXPECT_STREQ("Foo::func()", TrySymbolize((void *)(&Foo::func)));
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700152#endif
Austin Schuh906616c2019-01-21 20:25:11 -0800153}
154#endif
155
156// Tests that verify that Symbolize footprint is within some limit.
157
158// To measure the stack footprint of the Symbolize function, we create
159// a signal handler (for SIGUSR1 say) that calls the Symbolize function
160// on an alternate stack. This alternate stack is initialized to some
161// known pattern (0x55, 0x55, 0x55, ...). We then self-send this signal,
162// and after the signal handler returns, look at the alternate stack
163// buffer to see what portion has been touched.
164//
165// This trick gives us the the stack footprint of the signal handler.
166// But the signal handler, even before the call to Symbolize, consumes
167// some stack already. We however only want the stack usage of the
168// Symbolize function. To measure this accurately, we install two signal
169// handlers: one that does nothing and just returns, and another that
170// calls Symbolize. The difference between the stack consumption of these
171// two signals handlers should give us the Symbolize stack foorprint.
172
173static void *g_pc_to_symbolize;
174static char g_symbolize_buffer[4096];
175static char *g_symbolize_result;
176
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700177static void EmptySignalHandler(int /*signo*/) {}
Austin Schuh906616c2019-01-21 20:25:11 -0800178
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700179static void SymbolizeSignalHandler(int /*signo*/) {
Austin Schuh906616c2019-01-21 20:25:11 -0800180 if (Symbolize(g_pc_to_symbolize, g_symbolize_buffer,
181 sizeof(g_symbolize_buffer))) {
182 g_symbolize_result = g_symbolize_buffer;
183 } else {
184 g_symbolize_result = NULL;
185 }
186}
187
188const int kAlternateStackSize = 8096;
189const char kAlternateStackFillValue = 0x55;
190
191// These helper functions look at the alternate stack buffer, and figure
192// out what portion of this buffer has been touched - this is the stack
193// consumption of the signal handler running on this alternate stack.
194static ATTRIBUTE_NOINLINE bool StackGrowsDown(int *x) {
195 int y;
196 return &y < x;
197}
198static int GetStackConsumption(const char* alt_stack) {
199 int x;
200 if (StackGrowsDown(&x)) {
201 for (int i = 0; i < kAlternateStackSize; i++) {
202 if (alt_stack[i] != kAlternateStackFillValue) {
203 return (kAlternateStackSize - i);
204 }
205 }
206 } else {
207 for (int i = (kAlternateStackSize - 1); i >= 0; i--) {
208 if (alt_stack[i] != kAlternateStackFillValue) {
209 return i;
210 }
211 }
212 }
213 return -1;
214}
215
216#ifdef HAVE_SIGALTSTACK
217
218// Call Symbolize and figure out the stack footprint of this call.
219static const char *SymbolizeStackConsumption(void *pc, int *stack_consumed) {
220
221 g_pc_to_symbolize = pc;
222
223 // The alt-signal-stack cannot be heap allocated because there is a
224 // bug in glibc-2.2 where some signal handler setup code looks at the
225 // current stack pointer to figure out what thread is currently running.
226 // Therefore, the alternate stack must be allocated from the main stack
227 // itself.
228 char altstack[kAlternateStackSize];
229 memset(altstack, kAlternateStackFillValue, kAlternateStackSize);
230
231 // Set up the alt-signal-stack (and save the older one).
232 stack_t sigstk;
233 memset(&sigstk, 0, sizeof(stack_t));
234 stack_t old_sigstk;
235 sigstk.ss_sp = altstack;
236 sigstk.ss_size = kAlternateStackSize;
237 sigstk.ss_flags = 0;
238 CHECK_ERR(sigaltstack(&sigstk, &old_sigstk));
239
240 // Set up SIGUSR1 and SIGUSR2 signal handlers (and save the older ones).
241 struct sigaction sa;
242 memset(&sa, 0, sizeof(struct sigaction));
243 struct sigaction old_sa1, old_sa2;
244 sigemptyset(&sa.sa_mask);
245 sa.sa_flags = SA_ONSTACK;
246
247 // SIGUSR1 maps to EmptySignalHandler.
248 sa.sa_handler = EmptySignalHandler;
249 CHECK_ERR(sigaction(SIGUSR1, &sa, &old_sa1));
250
251 // SIGUSR2 maps to SymbolizeSignalHanlder.
252 sa.sa_handler = SymbolizeSignalHandler;
253 CHECK_ERR(sigaction(SIGUSR2, &sa, &old_sa2));
254
255 // Send SIGUSR1 signal and measure the stack consumption of the empty
256 // signal handler.
257 CHECK_ERR(kill(getpid(), SIGUSR1));
258 int stack_consumption1 = GetStackConsumption(altstack);
259
260 // Send SIGUSR2 signal and measure the stack consumption of the symbolize
261 // signal handler.
262 CHECK_ERR(kill(getpid(), SIGUSR2));
263 int stack_consumption2 = GetStackConsumption(altstack);
264
265 // The difference between the two stack consumption values is the
266 // stack footprint of the Symbolize function.
267 if (stack_consumption1 != -1 && stack_consumption2 != -1) {
268 *stack_consumed = stack_consumption2 - stack_consumption1;
269 } else {
270 *stack_consumed = -1;
271 }
272
273 // Log the stack consumption values.
274 LOG(INFO) << "Stack consumption of empty signal handler: "
275 << stack_consumption1;
276 LOG(INFO) << "Stack consumption of symbolize signal handler: "
277 << stack_consumption2;
278 LOG(INFO) << "Stack consumption of Symbolize: " << *stack_consumed;
279
280 // Now restore the old alt-signal-stack and signal handlers.
281 CHECK_ERR(sigaltstack(&old_sigstk, NULL));
282 CHECK_ERR(sigaction(SIGUSR1, &old_sa1, NULL));
283 CHECK_ERR(sigaction(SIGUSR2, &old_sa2, NULL));
284
285 return g_symbolize_result;
286}
287
288#ifdef __ppc64__
289// Symbolize stack consumption should be within 4kB.
290const int kStackConsumptionUpperLimit = 4096;
291#else
292// Symbolize stack consumption should be within 2kB.
293const int kStackConsumptionUpperLimit = 2048;
294#endif
295
296TEST(Symbolize, SymbolizeStackConsumption) {
297 int stack_consumed;
298 const char* symbol;
299
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700300 symbol = SymbolizeStackConsumption(reinterpret_cast<void *>(&nonstatic_func),
Austin Schuh906616c2019-01-21 20:25:11 -0800301 &stack_consumed);
302 EXPECT_STREQ("nonstatic_func", symbol);
303 EXPECT_GT(stack_consumed, 0);
304 EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
305
306 // The name of an internal linkage symbol is not specified; allow either a
307 // mangled or an unmangled name here.
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700308 symbol = SymbolizeStackConsumption(reinterpret_cast<void *>(&static_func),
Austin Schuh906616c2019-01-21 20:25:11 -0800309 &stack_consumed);
310 CHECK(NULL != symbol);
311 EXPECT_TRUE(strcmp("static_func", symbol) == 0 ||
312 strcmp("static_func()", symbol) == 0);
313 EXPECT_GT(stack_consumed, 0);
314 EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
315}
316
317#ifdef TEST_WITH_MODERN_GCC
318TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) {
319 Foo::func(100);
320 int stack_consumed;
321 const char* symbol;
322
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700323 symbol = SymbolizeStackConsumption(reinterpret_cast<void *>(&Foo::func),
324 &stack_consumed);
Austin Schuh906616c2019-01-21 20:25:11 -0800325
326 EXPECT_STREQ("Foo::func()", symbol);
327 EXPECT_GT(stack_consumed, 0);
328 EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
329}
330#endif
331
332#endif // HAVE_SIGALTSTACK
333
334// x86 specific tests. Uses some inline assembler.
335extern "C" {
336inline void* always_inline inline_func() {
337 void *pc = NULL;
338#ifdef TEST_X86_32_AND_64
339 __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
340#endif
341 return pc;
342}
343
344void* ATTRIBUTE_NOINLINE non_inline_func();
345void* ATTRIBUTE_NOINLINE non_inline_func() {
346 void *pc = NULL;
347#ifdef TEST_X86_32_AND_64
348 __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
349#endif
350 return pc;
351}
352
353static void ATTRIBUTE_NOINLINE TestWithPCInsideNonInlineFunction() {
354#if defined(TEST_X86_32_AND_64) && defined(HAVE_ATTRIBUTE_NOINLINE)
355 void *pc = non_inline_func();
356 const char *symbol = TrySymbolize(pc);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700357
358#if !defined(_MSC_VER) || !defined(NDEBUG)
Austin Schuh906616c2019-01-21 20:25:11 -0800359 CHECK(symbol != NULL);
360 CHECK_STREQ(symbol, "non_inline_func");
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700361#endif
Austin Schuh906616c2019-01-21 20:25:11 -0800362 cout << "Test case TestWithPCInsideNonInlineFunction passed." << endl;
363#endif
364}
365
366static void ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {
367#if defined(TEST_X86_32_AND_64) && defined(HAVE_ALWAYS_INLINE)
368 void *pc = inline_func(); // Must be inlined.
369 const char *symbol = TrySymbolize(pc);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700370
371#if !defined(_MSC_VER) || !defined(NDEBUG)
Austin Schuh906616c2019-01-21 20:25:11 -0800372 CHECK(symbol != NULL);
373 CHECK_STREQ(symbol, __FUNCTION__);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700374#endif
Austin Schuh906616c2019-01-21 20:25:11 -0800375 cout << "Test case TestWithPCInsideInlineFunction passed." << endl;
376#endif
377}
378}
379
380// Test with a return address.
381static void ATTRIBUTE_NOINLINE TestWithReturnAddress() {
382#if defined(HAVE_ATTRIBUTE_NOINLINE)
383 void *return_address = __builtin_return_address(0);
384 const char *symbol = TrySymbolize(return_address);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700385
386#if !defined(_MSC_VER) || !defined(NDEBUG)
Austin Schuh906616c2019-01-21 20:25:11 -0800387 CHECK(symbol != NULL);
388 CHECK_STREQ(symbol, "main");
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700389#endif
Austin Schuh906616c2019-01-21 20:25:11 -0800390 cout << "Test case TestWithReturnAddress passed." << endl;
391#endif
392}
393
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700394# elif defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN)
Austin Schuh906616c2019-01-21 20:25:11 -0800395
396#ifdef _MSC_VER
397#include <intrin.h>
398#pragma intrinsic(_ReturnAddress)
399#endif
400
401struct Foo {
402 static void func(int x);
403};
404
405__declspec(noinline) void Foo::func(int x) {
406 volatile int a = x;
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700407 // NOTE: In C++20, increment of object of volatile-qualified type is
408 // deprecated.
409 a = a + 1;
Austin Schuh906616c2019-01-21 20:25:11 -0800410}
411
412TEST(Symbolize, SymbolizeWithDemangling) {
413 Foo::func(100);
414 const char* ret = TrySymbolize((void *)(&Foo::func));
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700415
416#if defined(HAVE_DBGHELP) && !defined(NDEBUG)
Austin Schuh906616c2019-01-21 20:25:11 -0800417 EXPECT_STREQ("public: static void __cdecl Foo::func(int)", ret);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700418#endif
Austin Schuh906616c2019-01-21 20:25:11 -0800419}
420
421__declspec(noinline) void TestWithReturnAddress() {
422 void *return_address =
423#ifdef __GNUC__ // Cygwin and MinGW support
424 __builtin_return_address(0)
425#else
426 _ReturnAddress()
427#endif
428 ;
429 const char *symbol = TrySymbolize(return_address);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700430#if !defined(_MSC_VER) || !defined(NDEBUG)
Austin Schuh906616c2019-01-21 20:25:11 -0800431 CHECK(symbol != NULL);
432 CHECK_STREQ(symbol, "main");
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700433#endif
Austin Schuh906616c2019-01-21 20:25:11 -0800434 cout << "Test case TestWithReturnAddress passed." << endl;
435}
436# endif // __ELF__
437#endif // HAVE_STACKTRACE
438
439int main(int argc, char **argv) {
440 FLAGS_logtostderr = true;
441 InitGoogleLogging(argv[0]);
442 InitGoogleTest(&argc, argv);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700443#if defined(HAVE_SYMBOLIZE) && defined(HAVE_STACKTRACE)
Austin Schuh906616c2019-01-21 20:25:11 -0800444# if defined(__ELF__)
445 // We don't want to get affected by the callback interface, that may be
446 // used to install some callback function at InitGoogle() time.
447 InstallSymbolizeCallback(NULL);
448
449 TestWithPCInsideInlineFunction();
450 TestWithPCInsideNonInlineFunction();
451 TestWithReturnAddress();
452 return RUN_ALL_TESTS();
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700453# elif defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN)
Austin Schuh906616c2019-01-21 20:25:11 -0800454 TestWithReturnAddress();
455 return RUN_ALL_TESTS();
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700456# else // GLOG_OS_WINDOWS
Austin Schuh906616c2019-01-21 20:25:11 -0800457 printf("PASS (no symbolize_unittest support)\n");
458 return 0;
459# endif // __ELF__
460#else
461 printf("PASS (no symbolize support)\n");
462 return 0;
463#endif // HAVE_SYMBOLIZE
464}
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700465
466#if defined(__GNUG__)
467#pragma GCC diagnostic pop
468#endif