218 lines
8.3 KiB
C++
218 lines
8.3 KiB
C++
|
// Copyright (c) 2004, 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.
|
||
|
|
||
|
#include "utilities.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include "config.h"
|
||
|
#include "base/commandlineflags.h"
|
||
|
#include "glog/logging.h"
|
||
|
#include "stacktrace.h"
|
||
|
|
||
|
#ifdef HAVE_EXECINFO_H
|
||
|
# include <execinfo.h>
|
||
|
#endif
|
||
|
|
||
|
using namespace GOOGLE_NAMESPACE;
|
||
|
|
||
|
#ifdef HAVE_STACKTRACE
|
||
|
|
||
|
// Obtain a backtrace, verify that the expected callers are present in the
|
||
|
// backtrace, and maybe print the backtrace to stdout.
|
||
|
|
||
|
// The sequence of functions whose return addresses we expect to see in the
|
||
|
// backtrace.
|
||
|
const int BACKTRACE_STEPS = 6;
|
||
|
|
||
|
struct AddressRange {
|
||
|
const void *start, *end;
|
||
|
};
|
||
|
|
||
|
// Expected function [start,end] range.
|
||
|
AddressRange expected_range[BACKTRACE_STEPS];
|
||
|
|
||
|
#if __GNUC__
|
||
|
// Using GCC extension: address of a label can be taken with '&&label'.
|
||
|
// Start should be a label somewhere before recursive call, end somewhere
|
||
|
// after it.
|
||
|
#define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \
|
||
|
do { \
|
||
|
(prange)->start = &&start_label; \
|
||
|
(prange)->end = &&end_label; \
|
||
|
CHECK_LT((prange)->start, (prange)->end); \
|
||
|
} while (0)
|
||
|
// This macro expands into "unmovable" code (opaque to GCC), and that
|
||
|
// prevents GCC from moving a_label up or down in the code.
|
||
|
// Without it, there is no code following the 'end' label, and GCC
|
||
|
// (4.3.1, 4.4.0) thinks it safe to assign &&end an address that is before
|
||
|
// the recursive call.
|
||
|
#define DECLARE_ADDRESS_LABEL(a_label) \
|
||
|
a_label: do { __asm__ __volatile__(""); } while (0)
|
||
|
// Gcc 4.4.0 may split function into multiple chunks, and the chunk
|
||
|
// performing recursive call may end up later in the code then the return
|
||
|
// instruction (this actually happens with FDO).
|
||
|
// Adjust function range from __builtin_return_address.
|
||
|
#define ADJUST_ADDRESS_RANGE_FROM_RA(prange) \
|
||
|
do { \
|
||
|
void *ra = __builtin_return_address(0); \
|
||
|
CHECK_LT((prange)->start, ra); \
|
||
|
if (ra > (prange)->end) { \
|
||
|
printf("Adjusting range from %p..%p to %p..%p\n", \
|
||
|
(prange)->start, (prange)->end, \
|
||
|
(prange)->start, ra); \
|
||
|
(prange)->end = ra; \
|
||
|
} \
|
||
|
} while (0)
|
||
|
#else
|
||
|
// Assume the Check* functions below are not longer than 256 bytes.
|
||
|
#define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \
|
||
|
do { \
|
||
|
(prange)->start = reinterpret_cast<const void *>(&fn); \
|
||
|
(prange)->end = reinterpret_cast<const char *>(&fn) + 256; \
|
||
|
} while (0)
|
||
|
#define DECLARE_ADDRESS_LABEL(a_label) do { } while (0)
|
||
|
#define ADJUST_ADDRESS_RANGE_FROM_RA(prange) do { } while (0)
|
||
|
#endif // __GNUC__
|
||
|
|
||
|
//-----------------------------------------------------------------------//
|
||
|
|
||
|
static void CheckRetAddrIsInFunction(void *ret_addr, const AddressRange &range)
|
||
|
{
|
||
|
CHECK_GE(ret_addr, range.start);
|
||
|
CHECK_LE(ret_addr, range.end);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------//
|
||
|
|
||
|
void ATTRIBUTE_NOINLINE CheckStackTrace(int);
|
||
|
static void ATTRIBUTE_NOINLINE CheckStackTraceLeaf(void) {
|
||
|
const int STACK_LEN = 10;
|
||
|
void *stack[STACK_LEN];
|
||
|
int size;
|
||
|
|
||
|
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[1]);
|
||
|
INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]);
|
||
|
DECLARE_ADDRESS_LABEL(start);
|
||
|
size = GetStackTrace(stack, STACK_LEN, 0);
|
||
|
printf("Obtained %d stack frames.\n", size);
|
||
|
CHECK_GE(size, 1);
|
||
|
CHECK_LE(size, STACK_LEN);
|
||
|
|
||
|
if (1) {
|
||
|
#ifdef HAVE_EXECINFO_H
|
||
|
char **strings = backtrace_symbols(stack, size);
|
||
|
printf("Obtained %d stack frames.\n", size);
|
||
|
for (int i = 0; i < size; i++)
|
||
|
printf("%s %p\n", strings[i], stack[i]);
|
||
|
printf("CheckStackTrace() addr: %p\n", &CheckStackTrace);
|
||
|
free(strings);
|
||
|
#endif
|
||
|
}
|
||
|
for (int i = 0; i < BACKTRACE_STEPS; i++) {
|
||
|
printf("Backtrace %d: expected: %p..%p actual: %p ... ",
|
||
|
i, expected_range[i].start, expected_range[i].end, stack[i]);
|
||
|
fflush(stdout);
|
||
|
CheckRetAddrIsInFunction(stack[i], expected_range[i]);
|
||
|
printf("OK\n");
|
||
|
}
|
||
|
DECLARE_ADDRESS_LABEL(end);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------//
|
||
|
|
||
|
/* Dummy functions to make the backtrace more interesting. */
|
||
|
static void ATTRIBUTE_NOINLINE CheckStackTrace4(int i) {
|
||
|
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[2]);
|
||
|
INIT_ADDRESS_RANGE(CheckStackTrace4, start, end, &expected_range[1]);
|
||
|
DECLARE_ADDRESS_LABEL(start);
|
||
|
for (int j = i; j >= 0; j--)
|
||
|
CheckStackTraceLeaf();
|
||
|
DECLARE_ADDRESS_LABEL(end);
|
||
|
}
|
||
|
static void ATTRIBUTE_NOINLINE CheckStackTrace3(int i) {
|
||
|
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[3]);
|
||
|
INIT_ADDRESS_RANGE(CheckStackTrace3, start, end, &expected_range[2]);
|
||
|
DECLARE_ADDRESS_LABEL(start);
|
||
|
for (int j = i; j >= 0; j--)
|
||
|
CheckStackTrace4(j);
|
||
|
DECLARE_ADDRESS_LABEL(end);
|
||
|
}
|
||
|
static void ATTRIBUTE_NOINLINE CheckStackTrace2(int i) {
|
||
|
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[4]);
|
||
|
INIT_ADDRESS_RANGE(CheckStackTrace2, start, end, &expected_range[3]);
|
||
|
DECLARE_ADDRESS_LABEL(start);
|
||
|
for (int j = i; j >= 0; j--)
|
||
|
CheckStackTrace3(j);
|
||
|
DECLARE_ADDRESS_LABEL(end);
|
||
|
}
|
||
|
static void ATTRIBUTE_NOINLINE CheckStackTrace1(int i) {
|
||
|
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[5]);
|
||
|
INIT_ADDRESS_RANGE(CheckStackTrace1, start, end, &expected_range[4]);
|
||
|
DECLARE_ADDRESS_LABEL(start);
|
||
|
for (int j = i; j >= 0; j--)
|
||
|
CheckStackTrace2(j);
|
||
|
DECLARE_ADDRESS_LABEL(end);
|
||
|
}
|
||
|
#ifndef __GNUC__
|
||
|
// On non-GNU environment, we use the address of `CheckStackTrace` to
|
||
|
// guess the address range of this function. This guess is wrong for
|
||
|
// non-static function on Windows. This is probably because
|
||
|
// `&CheckStackTrace` returns the address of a trampoline like PLT,
|
||
|
// not the actual address of `CheckStackTrace`.
|
||
|
// See https://github.com/google/glog/issues/421 for the detail.
|
||
|
static
|
||
|
#endif
|
||
|
void ATTRIBUTE_NOINLINE CheckStackTrace(int i) {
|
||
|
INIT_ADDRESS_RANGE(CheckStackTrace, start, end, &expected_range[5]);
|
||
|
DECLARE_ADDRESS_LABEL(start);
|
||
|
for (int j = i; j >= 0; j--)
|
||
|
CheckStackTrace1(j);
|
||
|
DECLARE_ADDRESS_LABEL(end);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------//
|
||
|
|
||
|
int main(int, char ** argv) {
|
||
|
FLAGS_logtostderr = true;
|
||
|
InitGoogleLogging(argv[0]);
|
||
|
|
||
|
CheckStackTrace(0);
|
||
|
|
||
|
printf("PASS\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
int main() {
|
||
|
printf("PASS (no stacktrace support)\n");
|
||
|
return 0;
|
||
|
}
|
||
|
#endif // HAVE_STACKTRACE
|