Blame src/stacktrace_unittest.cc

Packit Service 9def5d
// Copyright (c) 2004, Google Inc.
Packit Service 9def5d
// All rights reserved.
Packit Service 9def5d
//
Packit Service 9def5d
// Redistribution and use in source and binary forms, with or without
Packit Service 9def5d
// modification, are permitted provided that the following conditions are
Packit Service 9def5d
// met:
Packit Service 9def5d
//
Packit Service 9def5d
//     * Redistributions of source code must retain the above copyright
Packit Service 9def5d
// notice, this list of conditions and the following disclaimer.
Packit Service 9def5d
//     * Redistributions in binary form must reproduce the above
Packit Service 9def5d
// copyright notice, this list of conditions and the following disclaimer
Packit Service 9def5d
// in the documentation and/or other materials provided with the
Packit Service 9def5d
// distribution.
Packit Service 9def5d
//     * Neither the name of Google Inc. nor the names of its
Packit Service 9def5d
// contributors may be used to endorse or promote products derived from
Packit Service 9def5d
// this software without specific prior written permission.
Packit Service 9def5d
//
Packit Service 9def5d
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit Service 9def5d
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit Service 9def5d
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
Packit Service 9def5d
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
Packit Service 9def5d
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
Packit Service 9def5d
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
Packit Service 9def5d
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit Service 9def5d
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit Service 9def5d
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit Service 9def5d
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
Packit Service 9def5d
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit Service 9def5d
Packit Service 9def5d
#include "utilities.h"
Packit Service 9def5d
Packit Service 9def5d
#include <stdio.h>
Packit Service 9def5d
#include <stdlib.h>
Packit Service 9def5d
#include "config.h"
Packit Service 9def5d
#include "base/commandlineflags.h"
Packit Service 9def5d
#include "glog/logging.h"
Packit Service 9def5d
#include "stacktrace.h"
Packit Service 9def5d
Packit Service 9def5d
#ifdef HAVE_EXECINFO_H
Packit Service 9def5d
# include <execinfo.h>
Packit Service 9def5d
#endif
Packit Service 9def5d
Packit Service 9def5d
using namespace GOOGLE_NAMESPACE;
Packit Service 9def5d
Packit Service 9def5d
#ifdef HAVE_STACKTRACE
Packit Service 9def5d
Packit Service 9def5d
// Obtain a backtrace, verify that the expected callers are present in the
Packit Service 9def5d
// backtrace, and maybe print the backtrace to stdout.
Packit Service 9def5d
Packit Service 9def5d
// The sequence of functions whose return addresses we expect to see in the
Packit Service 9def5d
// backtrace.
Packit Service 9def5d
const int BACKTRACE_STEPS = 6;
Packit Service 9def5d
Packit Service 9def5d
struct AddressRange {
Packit Service 9def5d
  const void *start, *end;
Packit Service 9def5d
};
Packit Service 9def5d
Packit Service 9def5d
// Expected function [start,end] range.
Packit Service 9def5d
AddressRange expected_range[BACKTRACE_STEPS];
Packit Service 9def5d
Packit Service 9def5d
#if __GNUC__
Packit Service 9def5d
// Using GCC extension: address of a label can be taken with '&&label'.
Packit Service 9def5d
// Start should be a label somewhere before recursive call, end somewhere
Packit Service 9def5d
// after it.
Packit Service 9def5d
#define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange)           \
Packit Service 9def5d
  do {                                                                   \
Packit Service 9def5d
    (prange)->start = &&start_label;                                     \
Packit Service 9def5d
    (prange)->end = &&end_label;                                         \
Packit Service 9def5d
    CHECK_LT((prange)->start, (prange)->end);                            \
Packit Service 9def5d
  } while (0)
Packit Service 9def5d
// This macro expands into "unmovable" code (opaque to GCC), and that
Packit Service 9def5d
// prevents GCC from moving a_label up or down in the code.
Packit Service 9def5d
// Without it, there is no code following the 'end' label, and GCC
Packit Service 9def5d
// (4.3.1, 4.4.0) thinks it safe to assign &&end an address that is before
Packit Service 9def5d
// the recursive call.
Packit Service 9def5d
#define DECLARE_ADDRESS_LABEL(a_label)                                   \
Packit Service 9def5d
  a_label: do { __asm__ __volatile__(""); } while (0)
Packit Service 9def5d
// Gcc 4.4.0 may split function into multiple chunks, and the chunk
Packit Service 9def5d
// performing recursive call may end up later in the code then the return
Packit Service 9def5d
// instruction (this actually happens with FDO).
Packit Service 9def5d
// Adjust function range from __builtin_return_address.
Packit Service 9def5d
#define ADJUST_ADDRESS_RANGE_FROM_RA(prange)                             \
Packit Service 9def5d
  do {                                                                   \
Packit Service 9def5d
    void *ra = __builtin_return_address(0);                              \
Packit Service 9def5d
    CHECK_LT((prange)->start, ra);                                       \
Packit Service 9def5d
    if (ra > (prange)->end) {                                            \
Packit Service 9def5d
      printf("Adjusting range from %p..%p to %p..%p\n",                  \
Packit Service 9def5d
             (prange)->start, (prange)->end,                             \
Packit Service 9def5d
             (prange)->start, ra);                                       \
Packit Service 9def5d
      (prange)->end = ra;                                                \
Packit Service 9def5d
    }                                                                    \
Packit Service 9def5d
  } while (0)
Packit Service 9def5d
#else
Packit Service 9def5d
// Assume the Check* functions below are not longer than 256 bytes.
Packit Service 9def5d
#define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange)           \
Packit Service 9def5d
  do {                                                                   \
Packit Service 9def5d
    (prange)->start = reinterpret_cast<const void *>(&fn);               \
Packit Service 9def5d
    (prange)->end = reinterpret_cast<const char *>(&fn) + 256;           \
Packit Service 9def5d
  } while (0)
Packit Service 9def5d
#define DECLARE_ADDRESS_LABEL(a_label) do { } while (0)
Packit Service 9def5d
#define ADJUST_ADDRESS_RANGE_FROM_RA(prange) do { } while (0)
Packit Service 9def5d
#endif  // __GNUC__
Packit Service 9def5d
Packit Service 9def5d
//-----------------------------------------------------------------------//
Packit Service 9def5d
Packit Service 9def5d
void CheckRetAddrIsInFunction(void *ret_addr, const AddressRange &range)
Packit Service 9def5d
{
Packit Service 9def5d
  CHECK_GE(ret_addr, range.start);
Packit Service 9def5d
  CHECK_LE(ret_addr, range.end);
Packit Service 9def5d
}
Packit Service 9def5d
Packit Service 9def5d
//-----------------------------------------------------------------------//
Packit Service 9def5d
Packit Service 9def5d
void ATTRIBUTE_NOINLINE CheckStackTrace(int);
Packit Service 9def5d
void ATTRIBUTE_NOINLINE CheckStackTraceLeaf(void) {
Packit Service 9def5d
  const int STACK_LEN = 10;
Packit Service 9def5d
  void *stack[STACK_LEN];
Packit Service 9def5d
  int size;
Packit Service 9def5d
Packit Service 9def5d
  ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[1]);
Packit Service 9def5d
  INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]);
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(start);
Packit Service 9def5d
  size = GetStackTrace(stack, STACK_LEN, 0);
Packit Service 9def5d
  printf("Obtained %d stack frames.\n", size);
Packit Service 9def5d
  CHECK_GE(size, 1);
Packit Service 9def5d
  CHECK_LE(size, STACK_LEN);
Packit Service 9def5d
Packit Service 9def5d
  if (1) {
Packit Service 9def5d
#ifdef HAVE_EXECINFO_H
Packit Service 9def5d
    char **strings = backtrace_symbols(stack, size);
Packit Service 9def5d
    printf("Obtained %d stack frames.\n", size);
Packit Service 9def5d
    for (int i = 0; i < size; i++)
Packit Service 9def5d
      printf("%s %p\n", strings[i], stack[i]);
Packit Service 9def5d
    printf("CheckStackTrace() addr: %p\n", &CheckStackTrace);
Packit Service 9def5d
    free(strings);
Packit Service 9def5d
#endif
Packit Service 9def5d
  }
Packit Service 9def5d
  for (int i = 0; i < BACKTRACE_STEPS; i++) {
Packit Service 9def5d
    printf("Backtrace %d: expected: %p..%p  actual: %p ... ",
Packit Service 9def5d
           i, expected_range[i].start, expected_range[i].end, stack[i]);
Packit Service 9def5d
    fflush(stdout);
Packit Service 9def5d
    CheckRetAddrIsInFunction(stack[i], expected_range[i]);
Packit Service 9def5d
    printf("OK\n");
Packit Service 9def5d
  }
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(end);
Packit Service 9def5d
}
Packit Service 9def5d
Packit Service 9def5d
//-----------------------------------------------------------------------//
Packit Service 9def5d
Packit Service 9def5d
/* Dummy functions to make the backtrace more interesting. */
Packit Service 9def5d
void ATTRIBUTE_NOINLINE CheckStackTrace4(int i) {
Packit Service 9def5d
  ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[2]);
Packit Service 9def5d
  INIT_ADDRESS_RANGE(CheckStackTrace4, start, end, &expected_range[1]);
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(start);
Packit Service 9def5d
  for (int j = i; j >= 0; j--)
Packit Service 9def5d
    CheckStackTraceLeaf();
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(end);
Packit Service 9def5d
}
Packit Service 9def5d
void ATTRIBUTE_NOINLINE CheckStackTrace3(int i) {
Packit Service 9def5d
  ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[3]);
Packit Service 9def5d
  INIT_ADDRESS_RANGE(CheckStackTrace3, start, end, &expected_range[2]);
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(start);
Packit Service 9def5d
  for (int j = i; j >= 0; j--)
Packit Service 9def5d
    CheckStackTrace4(j);
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(end);
Packit Service 9def5d
}
Packit Service 9def5d
void ATTRIBUTE_NOINLINE CheckStackTrace2(int i) {
Packit Service 9def5d
  ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[4]);
Packit Service 9def5d
  INIT_ADDRESS_RANGE(CheckStackTrace2, start, end, &expected_range[3]);
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(start);
Packit Service 9def5d
  for (int j = i; j >= 0; j--)
Packit Service 9def5d
    CheckStackTrace3(j);
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(end);
Packit Service 9def5d
}
Packit Service 9def5d
void ATTRIBUTE_NOINLINE CheckStackTrace1(int i) {
Packit Service 9def5d
  ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[5]);
Packit Service 9def5d
  INIT_ADDRESS_RANGE(CheckStackTrace1, start, end, &expected_range[4]);
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(start);
Packit Service 9def5d
  for (int j = i; j >= 0; j--)
Packit Service 9def5d
    CheckStackTrace2(j);
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(end);
Packit Service 9def5d
}
Packit Service 9def5d
void ATTRIBUTE_NOINLINE CheckStackTrace(int i) {
Packit Service 9def5d
  INIT_ADDRESS_RANGE(CheckStackTrace, start, end, &expected_range[5]);
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(start);
Packit Service 9def5d
  for (int j = i; j >= 0; j--)
Packit Service 9def5d
    CheckStackTrace1(j);
Packit Service 9def5d
  DECLARE_ADDRESS_LABEL(end);
Packit Service 9def5d
}
Packit Service 9def5d
Packit Service 9def5d
//-----------------------------------------------------------------------//
Packit Service 9def5d
Packit Service 9def5d
int main(int, char ** argv) {
Packit Service 9def5d
  FLAGS_logtostderr = true;
Packit Service 9def5d
  InitGoogleLogging(argv[0]);
Packit Service 9def5d
Packit Service 9def5d
  CheckStackTrace(0);
Packit Service 9def5d
Packit Service 9def5d
  printf("PASS\n");
Packit Service 9def5d
  return 0;
Packit Service 9def5d
}
Packit Service 9def5d
Packit Service 9def5d
#else
Packit Service 9def5d
int main() {
Packit Service 9def5d
  printf("PASS (no stacktrace support)\n");
Packit Service 9def5d
  return 0;
Packit Service 9def5d
}
Packit Service 9def5d
#endif  // HAVE_STACKTRACE