diff options
author | satorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-25 10:52:28 +0000 |
---|---|---|
committer | satorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-25 10:52:28 +0000 |
commit | b84167f708bc75d06bb175850d292778426d701c (patch) | |
tree | 396662faf225ce79b3159437f600f824282bb113 /base/third_party | |
parent | 5e9e3685204f9307b0b724a5064a31558e1455d5 (diff) | |
download | chromium_src-b84167f708bc75d06bb175850d292778426d701c.zip chromium_src-b84167f708bc75d06bb175850d292778426d701c.tar.gz chromium_src-b84167f708bc75d06bb175850d292778426d701c.tar.bz2 |
Import google-glog's Symbolize() and use it in debug_util_posix.cc.
Unlike glibc's backtrace_symbols(), google-glog's Symbolize() can
resolve symbols in binaries built with -fvisibility=hidden. This is
because Symbolize() uses both dynamic and regular symbols, while
backtrace_symbols() only uses dynamic symbols.
As shown below, the new backtrace is slightly less informative as it
does not have the binary name like out/Debug/base_unittests and the
distance information like +0x20, but it should be ok.
BUG=32762
TEST=out/Debug/base_unittests --gtest_filter='StackTrace*' --gtest_also_run_disabled_tests; and manually:
BEFORE
out/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x81d38f6]
out/Debug/base_unittests(StackTrace_DISABLED_DebugOutputToStream_Test::TestBody()+0x17) [0x806633d]
out/Debug/base_unittests(testing::Test::Run()+0x7a) [0x8242f96]
out/Debug/base_unittests(testing::internal::TestInfoImpl::Run()+0xb9) [0x824347b]
out/Debug/base_unittests(testing::TestCase::Run()+0xb7) [0x8243a7f]
out/Debug/base_unittests(testing::internal::UnitTestImpl::RunAllTests()+0x256) [0x824777e]
out/Debug/base_unittests(testing::UnitTest::Run()+0x14) [0x824680a]
out/Debug/base_unittests [0x8048cc5]
out/Debug/base_unittests(main+0x50) [0x80482a4]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0xb6b41b56]
out/Debug/base_unittests [0x80481c1]
AFTER
% out/Debug/base_unittests --gtest_filter='*DebugOutputToStream' --gtest_also_run_disabled_tests
StackTrace::StackTrace() [0x81d3910]
StackTrace_DISABLED_DebugOutputToStream_Test::TestBody() [0x806633d]
testing::Test::Run() [0x8242f66]
testing::internal::TestInfoImpl::Run() [0x824344b]
testing::TestCase::Run() [0x8243a4f]
testing::internal::UnitTestImpl::RunAllTests() [0x824774e]
testing::UnitTest::Run() [0x82467da]
TestSuite::Run() [0x8048cc5]
main [0x80482a4]
0xb6c0db56
0x80481c1
Review URL: http://codereview.chromium.org/545148
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@36997 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/third_party')
-rw-r--r-- | base/third_party/symbolize/README.chromium | 17 | ||||
-rw-r--r-- | base/third_party/symbolize/config.h | 7 | ||||
-rw-r--r-- | base/third_party/symbolize/demangle.cc | 1231 | ||||
-rw-r--r-- | base/third_party/symbolize/demangle.h | 84 | ||||
-rw-r--r-- | base/third_party/symbolize/glog/logging.h | 5 | ||||
-rw-r--r-- | base/third_party/symbolize/glog/raw_logging.h | 6 | ||||
-rw-r--r-- | base/third_party/symbolize/symbolize.cc | 681 | ||||
-rw-r--r-- | base/third_party/symbolize/symbolize.h | 116 | ||||
-rw-r--r-- | base/third_party/symbolize/utilities.h | 11 |
9 files changed, 2158 insertions, 0 deletions
diff --git a/base/third_party/symbolize/README.chromium b/base/third_party/symbolize/README.chromium new file mode 100644 index 0000000..5b3ba0e --- /dev/null +++ b/base/third_party/symbolize/README.chromium @@ -0,0 +1,17 @@ +The directory contains google-glog's symbolization library. +License can be found in symbolize.cc. + +The following files are copied AS-IS from: +http://code.google.com/p/google-glog/source/browse/#svn/trunk/src (r76) + +- demangle.cc +- demangle.h +- symbolize.cc +- symbolize.h + +The following files are minimal stubs created for use in Chromium: + +- config.h +- glog/logging.h +- glog/raw_logging.h +- utilities.h diff --git a/base/third_party/symbolize/config.h b/base/third_party/symbolize/config.h new file mode 100644 index 0000000..945f5a6 --- /dev/null +++ b/base/third_party/symbolize/config.h @@ -0,0 +1,7 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#define GOOGLE_NAMESPACE google +#define _END_GOOGLE_NAMESPACE_ } +#define _START_GOOGLE_NAMESPACE_ namespace google { diff --git a/base/third_party/symbolize/demangle.cc b/base/third_party/symbolize/demangle.cc new file mode 100644 index 0000000..46556bf --- /dev/null +++ b/base/third_party/symbolize/demangle.cc @@ -0,0 +1,1231 @@ +// Copyright (c) 2006, 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. +// +// Author: Satoru Takabayashi + +#include <stdio.h> // for NULL +#include "demangle.h" + +_START_GOOGLE_NAMESPACE_ + +typedef struct { + const char *abbrev; + const char *real_name; +} AbbrevPair; + +// List of operators from Itanium C++ ABI. +static const AbbrevPair kOperatorList[] = { + { "nw", "new" }, + { "na", "new[]" }, + { "dl", "delete" }, + { "da", "delete[]" }, + { "ps", "+" }, + { "ng", "-" }, + { "ad", "&" }, + { "de", "*" }, + { "co", "~" }, + { "pl", "+" }, + { "mi", "-" }, + { "ml", "*" }, + { "dv", "/" }, + { "rm", "%" }, + { "an", "&" }, + { "or", "|" }, + { "eo", "^" }, + { "aS", "=" }, + { "pL", "+=" }, + { "mI", "-=" }, + { "mL", "*=" }, + { "dV", "/=" }, + { "rM", "%=" }, + { "aN", "&=" }, + { "oR", "|=" }, + { "eO", "^=" }, + { "ls", "<<" }, + { "rs", ">>" }, + { "lS", "<<=" }, + { "rS", ">>=" }, + { "eq", "==" }, + { "ne", "!=" }, + { "lt", "<" }, + { "gt", ">" }, + { "le", "<=" }, + { "ge", ">=" }, + { "nt", "!" }, + { "aa", "&&" }, + { "oo", "||" }, + { "pp", "++" }, + { "mm", "--" }, + { "cm", "," }, + { "pm", "->*" }, + { "pt", "->" }, + { "cl", "()" }, + { "ix", "[]" }, + { "qu", "?" }, + { "st", "sizeof" }, + { "sz", "sizeof" }, + { NULL, NULL }, +}; + +// List of builtin types from Itanium C++ ABI. +static const AbbrevPair kBuiltinTypeList[] = { + { "v", "void" }, + { "w", "wchar_t" }, + { "b", "bool" }, + { "c", "char" }, + { "a", "signed char" }, + { "h", "unsigned char" }, + { "s", "short" }, + { "t", "unsigned short" }, + { "i", "int" }, + { "j", "unsigned int" }, + { "l", "long" }, + { "m", "unsigned long" }, + { "x", "long long" }, + { "y", "unsigned long long" }, + { "n", "__int128" }, + { "o", "unsigned __int128" }, + { "f", "float" }, + { "d", "double" }, + { "e", "long double" }, + { "g", "__float128" }, + { "z", "ellipsis" }, + { NULL, NULL } +}; + +// List of substitutions Itanium C++ ABI. +static const AbbrevPair kSubstitutionList[] = { + { "St", "" }, + { "Sa", "allocator" }, + { "Sb", "basic_string" }, + // std::basic_string<char, std::char_traits<char>,std::allocator<char> > + { "Ss", "string"}, + // std::basic_istream<char, std::char_traits<char> > + { "Si", "istream" }, + // std::basic_ostream<char, std::char_traits<char> > + { "So", "ostream" }, + // std::basic_iostream<char, std::char_traits<char> > + { "Sd", "iostream" }, + { NULL, NULL } +}; + +// State needed for demangling. +typedef struct { + const char *mangled_cur; // Cursor of mangled name. + const char *mangled_end; // End of mangled name. + char *out_cur; // Cursor of output string. + const char *out_begin; // Beginning of output string. + const char *out_end; // End of output string. + const char *prev_name; // For constructors/destructors. + int prev_name_length; // For constructors/destructors. + int nest_level; // For nested names. + int number; // Remember the previous number. + bool append; // Append flag. + bool overflowed; // True if output gets overflowed. +} State; + +// We don't use strlen() in libc since it's not guaranteed to be async +// signal safe. +static size_t StrLen(const char *str) { + size_t len = 0; + while (*str != '\0') { + ++str; + ++len; + } + return len; +} + +// Returns true if "str" has "prefix" as a prefix. +static bool StrPrefix(const char *str, const char *prefix) { + size_t i = 0; + while (str[i] != '\0' && prefix[i] != '\0' && + str[i] == prefix[i]) { + ++i; + } + return prefix[i] == '\0'; // Consumed everything in "prefix". +} + +static void InitState(State *state, const char *mangled, + char *out, int out_size) { + state->mangled_cur = mangled; + state->mangled_end = mangled + StrLen(mangled); + state->out_cur = out; + state->out_begin = out; + state->out_end = out + out_size; + state->prev_name = NULL; + state->prev_name_length = -1; + state->nest_level = -1; + state->number = -1; + state->append = true; + state->overflowed = false; +} + +// Calculates the remaining length of the mangled name. +static int RemainingLength(State *state) { + return state->mangled_end - state->mangled_cur; +} + +// Returns true and advances "mangled_cur" if we find "c" at +// "mangled_cur" position. +static bool ParseChar(State *state, const char c) { + if (RemainingLength(state) >= 1 && *state->mangled_cur == c) { + ++state->mangled_cur; + return true; + } + return false; +} + +// Returns true and advances "mangled_cur" if we find "two_chars" at +// "mangled_cur" position. +static bool ParseTwoChar(State *state, const char *two_chars) { + if (RemainingLength(state) >= 2 && + state->mangled_cur[0] == two_chars[0] && + state->mangled_cur[1] == two_chars[1]) { + state->mangled_cur += 2; + return true; + } + return false; +} + +// Returns true and advances "mangled_cur" if we find any character in +// "char_class" at "mangled_cur" position. +static bool ParseCharClass(State *state, const char *char_class) { + if (state->mangled_cur == state->mangled_end) { + return false; + } + const char *p = char_class; + for (; *p != '\0'; ++p) { + if (*state->mangled_cur == *p) { + state->mangled_cur += 1; + return true; + } + } + return false; +} + +// This function is used for handling an optional non-terminal. +static bool Optional(bool status) { + return true; +} + +// This function is used for handling <non-terminal>+ syntax. +typedef bool (*ParseFunc)(State *); +static bool OneOrMore(ParseFunc parse_func, State *state) { + if (parse_func(state)) { + while (parse_func(state)) { + } + return true; + } + return false; +} + +// Append "str" at "out_cur". If there is an overflow, "overflowed" +// is set to true for later use. The output string is ensured to +// always terminate with '\0' as long as there is no overflow. +static void Append(State *state, const char * const str, const int length) { + int i; + for (i = 0; i < length; ++i) { + if (state->out_cur + 1 < state->out_end) { // +1 for '\0' + *state->out_cur = str[i]; + ++state->out_cur; + } else { + state->overflowed = true; + break; + } + } + if (!state->overflowed) { + *state->out_cur = '\0'; // Terminate it with '\0' + } +} + +// We don't use equivalents in libc to avoid locale issues. +static bool IsLower(char c) { + return c >= 'a' && c <= 'z'; +} + +static bool IsAlpha(char c) { + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); +} + +// Append "str" with some tweaks, iff "append" state is true. +// Returns true so that it can be placed in "if" conditions. +static void MaybeAppendWithLength(State *state, const char * const str, + const int length) { + if (state->append && length > 0) { + // Append a space if the output buffer ends with '<' and "str" + // starts with '<' to avoid <<<. + if (str[0] == '<' && state->out_begin < state->out_cur && + state->out_cur[-1] == '<') { + Append(state, " ", 1); + } + // Remember the last identifier name for ctors/dtors. + if (IsAlpha(str[0]) || str[0] == '_') { + state->prev_name = state->out_cur; + state->prev_name_length = length; + } + Append(state, str, length); + } +} + +// A convenient wrapper arount MaybeAppendWithLength(). +static bool MaybeAppend(State *state, const char * const str) { + if (state->append) { + int length = StrLen(str); + MaybeAppendWithLength(state, str, length); + } + return true; +} + +// This function is used for handling nested names. +static bool EnterNestedName(State *state) { + state->nest_level = 0; + return true; +} + +// This function is used for handling nested names. +static bool LeaveNestedName(State *state, int prev_value) { + state->nest_level = prev_value; + return true; +} + +// Disable the append mode not to print function parameters, etc. +static bool DisableAppend(State *state) { + state->append = false; + return true; +} + +// Restore the append mode to the previous state. +static bool RestoreAppend(State *state, bool prev_value) { + state->append = prev_value; + return true; +} + +// Increase the nest level for nested names. +static void MaybeIncreaseNestLevel(State *state) { + if (state->nest_level > -1) { + ++state->nest_level; + } +} + +// Appends :: for nested names if necessary. +static void MaybeAppendSeparator(State *state) { + if (state->nest_level >= 1) { + MaybeAppend(state, "::"); + } +} + +// Cancel the last separator if necessary. +static void MaybeCancelLastSeparator(State *state) { + if (state->nest_level >= 1 && state->append && + state->out_begin <= state->out_cur - 2) { + state->out_cur -= 2; + *state->out_cur = '\0'; + } +} + +// Returns true if identifier pointed by "mangled_cur" is anonymous +// namespace. +static bool IdentifierIsAnonymousNamespace(State *state) { + const char anon_prefix[] = "_GLOBAL__N_"; + return (state->number > sizeof(anon_prefix) - 1 && // Should be longer. + StrPrefix(state->mangled_cur, anon_prefix)); +} + +// Forward declarations of our parsing functions. +static bool ParseMangledName(State *state); +static bool ParseEncoding(State *state); +static bool ParseName(State *state); +static bool ParseUnscopedName(State *state); +static bool ParseUnscopedTemplateName(State *state); +static bool ParseNestedName(State *state); +static bool ParsePrefix(State *state); +static bool ParseUnqualifiedName(State *state); +static bool ParseSourceName(State *state); +static bool ParseLocalSourceName(State *state); +static bool ParseNumber(State *state); +static bool ParseFloatNumber(State *state); +static bool ParseSeqId(State *state); +static bool ParseIdentifier(State *state); +static bool ParseOperatorName(State *state); +static bool ParseSpecialName(State *state); +static bool ParseCallOffset(State *state); +static bool ParseNVOffset(State *state); +static bool ParseVOffset(State *state); +static bool ParseCtorDtorName(State *state); +static bool ParseType(State *state); +static bool ParseCVQualifiers(State *state); +static bool ParseBuiltinType(State *state); +static bool ParseFunctionType(State *state); +static bool ParseBareFunctionType(State *state); +static bool ParseClassEnumType(State *state); +static bool ParseArrayType(State *state); +static bool ParsePointerToMemberType(State *state); +static bool ParseTemplateParam(State *state); +static bool ParseTemplateTemplateParam(State *state); +static bool ParseTemplateArgs(State *state); +static bool ParseTemplateArg(State *state); +static bool ParseExpression(State *state); +static bool ParseExprPrimary(State *state); +static bool ParseLocalName(State *state); +static bool ParseDiscriminator(State *state); +static bool ParseSubstitution(State *state); + +// Implementation note: the following code is a straightforward +// translation of the Itanium C++ ABI defined in BNF with a couple of +// exceptions. +// +// - Support GNU extensions not defined in the Itanium C++ ABI +// - <prefix> and <template-prefix> are combined to avoid infinite loop +// - Reorder patterns to shorten the code +// - Reorder patterns to give greedier functions precedence +// We'll mark "Less greedy than" for these cases in the code +// +// Each parsing function changes the state and returns true on +// success. Otherwise, don't change the state and returns false. To +// ensure that the state isn't changed in the latter case, we save the +// original state before we call more than one parsing functions +// consecutively with &&, and restore the state if unsuccessful. See +// ParseEncoding() as an example of this convention. We follow the +// convention throughout the code. +// +// Originally we tried to do demangling without following the full ABI +// syntax but it turned out we needed to follow the full syntax to +// parse complicated cases like nested template arguments. Note that +// implementing a full-fledged demangler isn't trivial (libiberty's +// cp-demangle.c has +4300 lines). +// +// Note that (foo) in <(foo) ...> is a modifier to be ignored. +// +// Reference: +// - Itanium C++ ABI +// <http://www.codesourcery.com/cxx-abi/abi.html#mangling> + +// <mangled-name> ::= _Z <encoding> +static bool ParseMangledName(State *state) { + if (ParseTwoChar(state, "_Z") && ParseEncoding(state)) { + // Append trailing version suffix if any. + // ex. _Z3foo@@GLIBCXX_3.4 + if (state->mangled_cur < state->mangled_end && + state->mangled_cur[0] == '@') { + MaybeAppend(state, state->mangled_cur); + state->mangled_cur = state->mangled_end; + } + return true; + } + return false; +} + +// <encoding> ::= <(function) name> <bare-function-type> +// ::= <(data) name> +// ::= <special-name> +static bool ParseEncoding(State *state) { + State copy = *state; + if (ParseName(state) && ParseBareFunctionType(state)) { + return true; + } + *state = copy; + + if (ParseName(state) || ParseSpecialName(state)) { + return true; + } + return false; +} + +// <name> ::= <nested-name> +// ::= <unscoped-template-name> <template-args> +// ::= <unscoped-name> +// ::= <local-name> +static bool ParseName(State *state) { + if (ParseNestedName(state) || ParseLocalName(state)) { + return true; + } + + State copy = *state; + if (ParseUnscopedTemplateName(state) && + ParseTemplateArgs(state)) { + return true; + } + *state = copy; + + // Less greedy than <unscoped-template-name> <template-args>. + if (ParseUnscopedName(state)) { + return true; + } + return false; +} + +// <unscoped-name> ::= <unqualified-name> +// ::= St <unqualified-name> +static bool ParseUnscopedName(State *state) { + if (ParseUnqualifiedName(state)) { + return true; + } + + State copy = *state; + if (ParseTwoChar(state, "St") && + MaybeAppend(state, "std::") && + ParseUnqualifiedName(state)) { + return true; + } + *state = copy; + return false; +} + +// <unscoped-template-name> ::= <unscoped-name> +// ::= <substitution> +static bool ParseUnscopedTemplateName(State *state) { + return ParseUnscopedName(state) || ParseSubstitution(state); +} + +// <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E +// ::= N [<CV-qualifiers>] <template-prefix> <template-args> E +static bool ParseNestedName(State *state) { + State copy = *state; + if (ParseChar(state, 'N') && + EnterNestedName(state) && + Optional(ParseCVQualifiers(state)) && + ParsePrefix(state) && + LeaveNestedName(state, copy.nest_level) && + ParseChar(state, 'E')) { + return true; + } + *state = copy; + return false; +} + +// This part is tricky. If we literally translate them to code, we'll +// end up infinite loop. Hence we merge them to avoid the case. +// +// <prefix> ::= <prefix> <unqualified-name> +// ::= <template-prefix> <template-args> +// ::= <template-param> +// ::= <substitution> +// ::= # empty +// <template-prefix> ::= <prefix> <(template) unqualified-name> +// ::= <template-param> +// ::= <substitution> +static bool ParsePrefix(State *state) { + bool has_something = false; + while (true) { + MaybeAppendSeparator(state); + if (ParseTemplateParam(state) || + ParseSubstitution(state) || + ParseUnscopedName(state)) { + has_something = true; + MaybeIncreaseNestLevel(state); + continue; + } + MaybeCancelLastSeparator(state); + if (has_something && ParseTemplateArgs(state)) { + return ParsePrefix(state); + } else { + break; + } + } + return true; +} + +// <unqualified-name> ::= <operator-name> +// ::= <ctor-dtor-name> +// ::= <source-name> +// ::= <local-source-name> +static bool ParseUnqualifiedName(State *state) { + return (ParseOperatorName(state) || + ParseCtorDtorName(state) || + ParseSourceName(state) || + ParseLocalSourceName(state)); +} + +// <source-name> ::= <positive length number> <identifier> +static bool ParseSourceName(State *state) { + State copy = *state; + if (ParseNumber(state) && ParseIdentifier(state)) { + return true; + } + *state = copy; + return false; +} + +// <local-source-name> ::= L <source-name> [<discriminator>] +// +// References: +// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 +// http://gcc.gnu.org/viewcvs?view=rev&revision=124467 +static bool ParseLocalSourceName(State *state) { + State copy = *state; + if (ParseChar(state, 'L') && ParseSourceName(state) && + Optional(ParseDiscriminator(state))) { + return true; + } + *state = copy; + return false; +} + +// <number> ::= [n] <non-negative decimal integer> +static bool ParseNumber(State *state) { + int sign = 1; + if (ParseChar(state, 'n')) { + sign = -1; + } + const char *p = state->mangled_cur; + int number = 0; + for (;p < state->mangled_end; ++p) { + if ((*p >= '0' && *p <= '9')) { + number = number * 10 + (*p - '0'); + } else { + break; + } + } + if (p != state->mangled_cur) { // Conversion succeeded. + state->mangled_cur = p; + state->number = number * sign; + return true; + } + return false; +} + +// Floating-point literals are encoded using a fixed-length lowercase +// hexadecimal string. +static bool ParseFloatNumber(State *state) { + const char *p = state->mangled_cur; + int number = 0; + for (;p < state->mangled_end; ++p) { + if ((*p >= '0' && *p <= '9')) { + number = number * 16 + (*p - '0'); + } else if (*p >= 'a' && *p <= 'f') { + number = number * 16 + (*p - 'a' + 10); + } else { + break; + } + } + if (p != state->mangled_cur) { // Conversion succeeded. + state->mangled_cur = p; + state->number = number; + return true; + } + return false; +} + +// The <seq-id> is a sequence number in base 36, +// using digits and upper case letters +static bool ParseSeqId(State *state) { + const char *p = state->mangled_cur; + int number = 0; + for (;p < state->mangled_end; ++p) { + if ((*p >= '0' && *p <= '9')) { + number = number * 36 + (*p - '0'); + } else if (*p >= 'A' && *p <= 'Z') { + number = number * 36 + (*p - 'A' + 10); + } else { + break; + } + } + if (p != state->mangled_cur) { // Conversion succeeded. + state->mangled_cur = p; + state->number = number; + return true; + } + return false; +} + +// <identifier> ::= <unqualified source code identifier> +static bool ParseIdentifier(State *state) { + if (state->number == -1 || + RemainingLength(state) < state->number) { + return false; + } + if (IdentifierIsAnonymousNamespace(state)) { + MaybeAppend(state, "(anonymous namespace)"); + } else { + MaybeAppendWithLength(state, state->mangled_cur, state->number); + } + state->mangled_cur += state->number; + state->number = -1; // Reset the number. + return true; +} + +// <operator-name> ::= nw, and other two letters cases +// ::= cv <type> # (cast) +// ::= v <digit> <source-name> # vendor extended operator +static bool ParseOperatorName(State *state) { + if (RemainingLength(state) < 2) { + return false; + } + // First check with "cv" (cast) case. + State copy = *state; + if (ParseTwoChar(state, "cv") && + MaybeAppend(state, "operator ") && + EnterNestedName(state) && + ParseType(state) && + LeaveNestedName(state, copy.nest_level)) { + return true; + } + *state = copy; + + // Then vendor extended operators. + if (ParseChar(state, 'v') && ParseCharClass(state, "0123456789") && + ParseSourceName(state)) { + return true; + } + *state = copy; + + // Other operator names should start with a lower alphabet followed + // by a lower/upper alphabet. + if (!(IsLower(state->mangled_cur[0]) && + IsAlpha(state->mangled_cur[1]))) { + return false; + } + // We may want to perform a binary search if we really need speed. + const AbbrevPair *p; + for (p = kOperatorList; p->abbrev != NULL; ++p) { + if (state->mangled_cur[0] == p->abbrev[0] && + state->mangled_cur[1] == p->abbrev[1]) { + MaybeAppend(state, "operator"); + if (IsLower(*p->real_name)) { // new, delete, etc. + MaybeAppend(state, " "); + } + MaybeAppend(state, p->real_name); + state->mangled_cur += 2; + return true; + } + } + return false; +} + +// <special-name> ::= TV <type> +// ::= TT <type> +// ::= TI <type> +// ::= TS <type> +// ::= Tc <call-offset> <call-offset> <(base) encoding> +// ::= GV <(object) name> +// ::= T <call-offset> <(base) encoding> +// G++ extensions: +// ::= TC <type> <(offset) number> _ <(base) type> +// ::= TF <type> +// ::= TJ <type> +// ::= GR <name> +// ::= GA <encoding> +// ::= Th <call-offset> <(base) encoding> +// ::= Tv <call-offset> <(base) encoding> +// +// Note: we don't care much about them since they don't appear in +// stack traces. The are special data. +static bool ParseSpecialName(State *state) { + State copy = *state; + if (ParseChar(state, 'T') && + ParseCharClass(state, "VTIS") && + ParseType(state)) { + return true; + } + *state = copy; + + if (ParseTwoChar(state, "Tc") && ParseCallOffset(state) && + ParseCallOffset(state) && ParseEncoding(state)) { + return true; + } + *state = copy; + + if (ParseTwoChar(state, "GV") && + ParseName(state)) { + return true; + } + *state = copy; + + if (ParseChar(state, 'T') && ParseCallOffset(state) && + ParseEncoding(state)) { + return true; + } + *state = copy; + + // G++ extensions + if (ParseTwoChar(state, "TC") && ParseType(state) && + ParseNumber(state) && ParseChar(state, '_') && + DisableAppend(state) && + ParseType(state)) { + RestoreAppend(state, copy.append); + return true; + } + *state = copy; + + if (ParseChar(state, 'T') && ParseCharClass(state, "FJ") && + ParseType(state)) { + return true; + } + *state = copy; + + if (ParseTwoChar(state, "GR") && ParseName(state)) { + return true; + } + *state = copy; + + if (ParseTwoChar(state, "GA") && ParseEncoding(state)) { + return true; + } + *state = copy; + + if (ParseChar(state, 'T') && ParseCharClass(state, "hv") && + ParseCallOffset(state) && ParseEncoding(state)) { + return true; + } + *state = copy; + return false; +} + +// <call-offset> ::= h <nv-offset> _ +// ::= v <v-offset> _ +static bool ParseCallOffset(State *state) { + State copy = *state; + if (ParseChar(state, 'h') && + ParseNVOffset(state) && ParseChar(state, '_')) { + return true; + } + *state = copy; + + if (ParseChar(state, 'v') && + ParseVOffset(state) && ParseChar(state, '_')) { + return true; + } + *state = copy; + + return false; +} + +// <nv-offset> ::= <(offset) number> +static bool ParseNVOffset(State *state) { + return ParseNumber(state); +} + +// <v-offset> ::= <(offset) number> _ <(virtual offset) number> +static bool ParseVOffset(State *state) { + State copy = *state; + if (ParseNumber(state) && ParseChar(state, '_') && + ParseNumber(state)) { + return true; + } + *state = copy; + return false; +} + +// <ctor-dtor-name> ::= C1 | C2 | C3 +// ::= D0 | D1 | D2 +static bool ParseCtorDtorName(State *state) { + State copy = *state; + if (ParseChar(state, 'C') && + ParseCharClass(state, "123")) { + const char * const prev_name = state->prev_name; + const int prev_name_length = state->prev_name_length; + MaybeAppendWithLength(state, prev_name, prev_name_length); + return true; + } + *state = copy; + + if (ParseChar(state, 'D') && + ParseCharClass(state, "012")) { + const char * const prev_name = state->prev_name; + const int prev_name_length = state->prev_name_length; + MaybeAppend(state, "~"); + MaybeAppendWithLength(state, prev_name, prev_name_length); + return true; + } + *state = copy; + return false; +} + +// <type> ::= <CV-qualifiers> <type> +// ::= P <type> +// ::= R <type> +// ::= C <type> +// ::= G <type> +// ::= U <source-name> <type> +// ::= <builtin-type> +// ::= <function-type> +// ::= <class-enum-type> +// ::= <array-type> +// ::= <pointer-to-member-type> +// ::= <template-template-param> <template-args> +// ::= <template-param> +// ::= <substitution> +static bool ParseType(State *state) { + // We should check CV-qualifers, and PRGC things first. + State copy = *state; + if (ParseCVQualifiers(state) && ParseType(state)) { + return true; + } + *state = copy; + + if (ParseCharClass(state, "PRCG") && ParseType(state)) { + return true; + } + *state = copy; + + if (ParseChar(state, 'U') && ParseSourceName(state) && + ParseType(state)) { + return true; + } + *state = copy; + + if (ParseBuiltinType(state) || + ParseFunctionType(state) || + ParseClassEnumType(state) || + ParseArrayType(state) || + ParsePointerToMemberType(state) || + ParseSubstitution(state)) { + return true; + } + + if (ParseTemplateTemplateParam(state) && + ParseTemplateArgs(state)) { + return true; + } + *state = copy; + + // Less greedy than <template-template-param> <template-args>. + if (ParseTemplateParam(state)) { + return true; + } + + return false; +} + +// <CV-qualifiers> ::= [r] [V] [K] +// We don't allow empty <CV-qualifiers> to avoid infinite loop in +// ParseType(). +static bool ParseCVQualifiers(State *state) { + int num_cv_qualifiers = 0; + num_cv_qualifiers += ParseChar(state, 'r'); + num_cv_qualifiers += ParseChar(state, 'V'); + num_cv_qualifiers += ParseChar(state, 'K'); + return num_cv_qualifiers > 0; +} + +// <builtin-type> ::= v, etc. +// ::= u <source-name> +static bool ParseBuiltinType(State *state) { + const AbbrevPair *p; + for (p = kBuiltinTypeList; p->abbrev != NULL; ++p) { + if (state->mangled_cur[0] == p->abbrev[0]) { + MaybeAppend(state, p->real_name); + ++state->mangled_cur; + return true; + } + } + + State copy = *state; + if (ParseChar(state, 'u') && ParseSourceName(state)) { + return true; + } + *state = copy; + return false; +} + +// <function-type> ::= F [Y] <bare-function-type> E +static bool ParseFunctionType(State *state) { + State copy = *state; + if (ParseChar(state, 'F') && Optional(ParseChar(state, 'Y')) && + ParseBareFunctionType(state) && ParseChar(state, 'E')) { + return true; + } + *state = copy; + return false; +} + +// <bare-function-type> ::= <(signature) type>+ +static bool ParseBareFunctionType(State *state) { + State copy = *state; + DisableAppend(state); + if (OneOrMore(ParseType, state)) { + RestoreAppend(state, copy.append); + MaybeAppend(state, "()"); + return true; + } + *state = copy; + return false; +} + +// <class-enum-type> ::= <name> +static bool ParseClassEnumType(State *state) { + return ParseName(state); +} + +// <array-type> ::= A <(positive dimension) number> _ <(element) type> +// ::= A [<(dimension) expression>] _ <(element) type> +static bool ParseArrayType(State *state) { + State copy = *state; + if (ParseChar(state, 'A') && ParseNumber(state) && + ParseChar(state, '_') && ParseType(state)) { + return true; + } + *state = copy; + + if (ParseChar(state, 'A') && Optional(ParseExpression(state)) && + ParseChar(state, '_') && ParseType(state)) { + return true; + } + *state = copy; + return false; +} + +// <pointer-to-member-type> ::= M <(class) type> <(member) type> +static bool ParsePointerToMemberType(State *state) { + State copy = *state; + if (ParseChar(state, 'M') && ParseType(state) && + ParseType(state)) { + return true; + } + *state = copy; + return false; +} + +// <template-param> ::= T_ +// ::= T <parameter-2 non-negative number> _ +static bool ParseTemplateParam(State *state) { + if (ParseTwoChar(state, "T_")) { + MaybeAppend(state, "?"); // We don't support template substitutions. + return true; + } + + State copy = *state; + if (ParseChar(state, 'T') && ParseNumber(state) && + ParseChar(state, '_')) { + MaybeAppend(state, "?"); // We don't support template substitutions. + return true; + } + *state = copy; + return false; +} + + +// <template-template-param> ::= <template-param> +// ::= <substitution> +static bool ParseTemplateTemplateParam(State *state) { + return (ParseTemplateParam(state) || + ParseSubstitution(state)); +} + +// <template-args> ::= I <template-arg>+ E +static bool ParseTemplateArgs(State *state) { + State copy = *state; + DisableAppend(state); + if (ParseChar(state, 'I') && + OneOrMore(ParseTemplateArg, state) && + ParseChar(state, 'E')) { + RestoreAppend(state, copy.append); + MaybeAppend(state, "<>"); + return true; + } + *state = copy; + return false; +} + +// <template-arg> ::= <type> +// ::= <expr-primary> +// ::= X <expression> E +static bool ParseTemplateArg(State *state) { + if (ParseType(state) || + ParseExprPrimary(state)) { + return true; + } + + State copy = *state; + if (ParseChar(state, 'X') && ParseExpression(state) && + ParseChar(state, 'E')) { + return true; + } + *state = copy; + return false; +} + +// <expression> ::= <template-param> +// ::= <expr-primary> +// ::= <unary operator-name> <expression> +// ::= <binary operator-name> <expression> <expression> +// ::= <trinary operator-name> <expression> <expression> +// <expression> +// ::= st <type> +// ::= sr <type> <unqualified-name> <template-args> +// ::= sr <type> <unqualified-name> +static bool ParseExpression(State *state) { + if (ParseTemplateParam(state) || ParseExprPrimary(state)) { + return true; + } + + State copy = *state; + if (ParseOperatorName(state) && + ParseExpression(state) && + ParseExpression(state) && + ParseExpression(state)) { + return true; + } + *state = copy; + + if (ParseOperatorName(state) && + ParseExpression(state) && + ParseExpression(state)) { + return true; + } + *state = copy; + + if (ParseOperatorName(state) && + ParseExpression(state)) { + return true; + } + *state = copy; + + if (ParseTwoChar(state, "st") && ParseType(state)) { + return true; + } + *state = copy; + + if (ParseTwoChar(state, "sr") && ParseType(state) && + ParseUnqualifiedName(state) && + ParseTemplateArgs(state)) { + return true; + } + *state = copy; + + if (ParseTwoChar(state, "sr") && ParseType(state) && + ParseUnqualifiedName(state)) { + return true; + } + *state = copy; + return false; +} + +// <expr-primary> ::= L <type> <(value) number> E +// ::= L <type> <(value) float> E +// ::= L <mangled-name> E +// // A bug in g++'s C++ ABI version 2 (-fabi-version=2). +// ::= LZ <encoding> E +static bool ParseExprPrimary(State *state) { + State copy = *state; + if (ParseChar(state, 'L') && ParseType(state) && + ParseNumber(state) && + ParseChar(state, 'E')) { + return true; + } + *state = copy; + + if (ParseChar(state, 'L') && ParseType(state) && + ParseFloatNumber(state) && + ParseChar(state, 'E')) { + return true; + } + *state = copy; + + if (ParseChar(state, 'L') && ParseMangledName(state) && + ParseChar(state, 'E')) { + return true; + } + *state = copy; + + if (ParseTwoChar(state, "LZ") && ParseEncoding(state) && + ParseChar(state, 'E')) { + return true; + } + *state = copy; + + return false; +} + +// <local-name> := Z <(function) encoding> E <(entity) name> +// [<discriminator>] +// := Z <(function) encoding> E s [<discriminator>] +static bool ParseLocalName(State *state) { + State copy = *state; + if (ParseChar(state, 'Z') && ParseEncoding(state) && + ParseChar(state, 'E') && MaybeAppend(state, "::") && + ParseName(state) && Optional(ParseDiscriminator(state))) { + return true; + } + *state = copy; + + if (ParseChar(state, 'Z') && ParseEncoding(state) && + ParseTwoChar(state, "Es") && Optional(ParseDiscriminator(state))) { + return true; + } + *state = copy; + return false; +} + +// <discriminator> := _ <(non-negative) number> +static bool ParseDiscriminator(State *state) { + State copy = *state; + if (ParseChar(state, '_') && ParseNumber(state)) { + return true; + } + *state = copy; + return false; +} + +// <substitution> ::= S_ +// ::= S <seq-id> _ +// ::= St, etc. +static bool ParseSubstitution(State *state) { + if (ParseTwoChar(state, "S_")) { + MaybeAppend(state, "?"); // We don't support substitutions. + return true; + } + + State copy = *state; + if (ParseChar(state, 'S') && ParseSeqId(state) && + ParseChar(state, '_')) { + MaybeAppend(state, "?"); // We don't support substitutions. + return true; + } + *state = copy; + + // Expand abbreviations like "St" => "std". + if (ParseChar(state, 'S')) { + const AbbrevPair *p; + for (p = kSubstitutionList; p->abbrev != NULL; ++p) { + if (state->mangled_cur[0] == p->abbrev[1]) { + MaybeAppend(state, "std"); + if (p->real_name[0] != '\0') { + MaybeAppend(state, "::"); + MaybeAppend(state, p->real_name); + } + state->mangled_cur += 1; + return true; + } + } + } + *state = copy; + return false; +} + +// The demangler entry point. +bool Demangle(const char *mangled, char *out, int out_size) { + State state; + InitState(&state, mangled, out, out_size); + return (ParseMangledName(&state) && + state.overflowed == false && + RemainingLength(&state) == 0); +} + +_END_GOOGLE_NAMESPACE_ diff --git a/base/third_party/symbolize/demangle.h b/base/third_party/symbolize/demangle.h new file mode 100644 index 0000000..9c75915 --- /dev/null +++ b/base/third_party/symbolize/demangle.h @@ -0,0 +1,84 @@ +// Copyright (c) 2006, 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. +// +// Author: Satoru Takabayashi +// +// An async-signal-safe and thread-safe demangler for Itanium C++ ABI +// (aka G++ V3 ABI). + +// The demangler is implemented to be used in async signal handlers to +// symbolize stack traces. We cannot use libstdc++'s +// abi::__cxa_demangle() in such signal handlers since it's not async +// signal safe (it uses malloc() internally). +// +// Note that this demangler doesn't support full demangling. More +// specifically, it doesn't print types of function parameters and +// types of template arguments. It just skips them. However, it's +// still very useful to extract basic information such as class, +// function, constructor, destructor, and operator names. +// +// See the implementation note in demangle.cc if you are interested. +// +// Example: +// +// | Mangled Name | The Demangler | abi::__cxa_demangle() +// |---------------|---------------|----------------------- +// | _Z1fv | f() | f() +// | _Z1fi | f() | f(int) +// | _Z3foo3bar | foo() | foo(bar) +// | _Z1fIiEvi | f<>() | void f<int>(int) +// | _ZN1N1fE | N::f | N::f +// | _ZN3Foo3BarEv | Foo::Bar() | Foo::Bar() +// | _Zrm1XS_" | operator%() | operator%(X, X) +// | _ZN3FooC1Ev | Foo::Foo() | Foo::Foo() +// | _Z1fSs | f() | f(std::basic_string<char, +// | | | std::char_traits<char>, +// | | | std::allocator<char> >) +// +// See the unit test for more examples. +// +// Note: we might want to write demanglers for ABIs other than Itanium +// C++ ABI in the future. +// + +#ifndef BASE_DEMANGLE_H_ +#define BASE_DEMANGLE_H_ + +#include "config.h" + +_START_GOOGLE_NAMESPACE_ + +// Demangle "mangled". On success, return true and write the +// demangled symbol name to "out". Otherwise, return false. +// "out" is modified even if demangling is unsuccessful. +bool Demangle(const char *mangled, char *out, int out_size); + +_END_GOOGLE_NAMESPACE_ + +#endif // BASE_DEMANGLE_H_ diff --git a/base/third_party/symbolize/glog/logging.h b/base/third_party/symbolize/glog/logging.h new file mode 100644 index 0000000..a42c306 --- /dev/null +++ b/base/third_party/symbolize/glog/logging.h @@ -0,0 +1,5 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Empty. diff --git a/base/third_party/symbolize/glog/raw_logging.h b/base/third_party/symbolize/glog/raw_logging.h new file mode 100644 index 0000000..f5515c4 --- /dev/null +++ b/base/third_party/symbolize/glog/raw_logging.h @@ -0,0 +1,6 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#define WARNING 1; +#define RAW_LOG(severity, ...); // Do nothing. diff --git a/base/third_party/symbolize/symbolize.cc b/base/third_party/symbolize/symbolize.cc new file mode 100644 index 0000000..3465de6 --- /dev/null +++ b/base/third_party/symbolize/symbolize.cc @@ -0,0 +1,681 @@ +// Copyright (c) 2006, 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. +// +// Author: Satoru Takabayashi +// Stack-footprint reduction work done by Raksit Ashok +// +// Implementation note: +// +// We don't use heaps but only use stacks. We want to reduce the +// stack consumption so that the symbolizer can run on small stacks. +// +// Here are some numbers collected with GCC 4.1.0 on x86: +// - sizeof(Elf32_Sym) = 16 +// - sizeof(Elf32_Shdr) = 40 +// - sizeof(Elf64_Sym) = 24 +// - sizeof(Elf64_Shdr) = 64 +// +// This implementation is intended to be async-signal-safe but uses +// some functions which are not guaranteed to be so, such as memchr() +// and memmove(). We assume they are async-signal-safe. +// + +#include "utilities.h" + +#if defined(HAVE_SYMBOLIZE) + +#include <limits> + +#include "symbolize.h" +#include "demangle.h" + +_START_GOOGLE_NAMESPACE_ + +// We don't use assert() since it's not guaranteed to be +// async-signal-safe. Instead we define a minimal assertion +// macro. So far, we don't need pretty printing for __FILE__, etc. + +// A wrapper for abort() to make it callable in ? :. +static int AssertFail() { + abort(); + return 0; // Should not reach. +} + +#define SAFE_ASSERT(expr) ((expr) ? 0 : AssertFail()) + +static SymbolizeCallback g_symbolize_callback = NULL; +void InstallSymbolizeCallback(SymbolizeCallback callback) { + g_symbolize_callback = callback; +} + +// This function wraps the Demangle function to provide an interface +// where the input symbol is demangled in-place. +// To keep stack consumption low, we would like this function to not +// get inlined. +static ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size) { + char demangled[256]; // Big enough for sane demangled symbols. + if (Demangle(out, demangled, sizeof(demangled))) { + // Demangling succeeded. Copy to out if the space allows. + int len = strlen(demangled); + if (len + 1 <= out_size) { // +1 for '\0'. + SAFE_ASSERT(len < sizeof(demangled)); + memmove(out, demangled, len + 1); + } + } +} + +_END_GOOGLE_NAMESPACE_ + +#if defined(__ELF__) + +#include <dlfcn.h> +#include <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <link.h> // For ElfW() macro. +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "symbolize.h" +#include "config.h" +#include "glog/raw_logging.h" + +// Re-runs fn until it doesn't cause EINTR. +#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR) + +_START_GOOGLE_NAMESPACE_ + +// Read up to "count" bytes from file descriptor "fd" into the buffer +// starting at "buf" while handling short reads and EINTR. On +// success, return the number of bytes read. Otherwise, return -1. +static ssize_t ReadPersistent(const int fd, void *buf, const size_t count) { + SAFE_ASSERT(fd >= 0); + SAFE_ASSERT(count >= 0 && count <= std::numeric_limits<ssize_t>::max()); + char *buf0 = reinterpret_cast<char *>(buf); + ssize_t num_bytes = 0; + while (num_bytes < count) { + ssize_t len; + NO_INTR(len = read(fd, buf0 + num_bytes, count - num_bytes)); + if (len < 0) { // There was an error other than EINTR. + return -1; + } + if (len == 0) { // Reached EOF. + break; + } + num_bytes += len; + } + SAFE_ASSERT(num_bytes <= count); + return num_bytes; +} + +// Read up to "count" bytes from "offset" in the file pointed by file +// descriptor "fd" into the buffer starting at "buf". On success, +// return the number of bytes read. Otherwise, return -1. +static ssize_t ReadFromOffset(const int fd, void *buf, + const size_t count, const off_t offset) { + off_t off = lseek(fd, offset, SEEK_SET); + if (off == (off_t)-1) { + return -1; + } + return ReadPersistent(fd, buf, count); +} + +// Try reading exactly "count" bytes from "offset" bytes in a file +// pointed by "fd" into the buffer starting at "buf" while handling +// short reads and EINTR. On success, return true. Otherwise, return +// false. +static bool ReadFromOffsetExact(const int fd, void *buf, + const size_t count, const off_t offset) { + ssize_t len = ReadFromOffset(fd, buf, count, offset); + return len == count; +} + +// Returns elf_header.e_type if the file pointed by fd is an ELF binary. +static int FileGetElfType(const int fd) { + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return -1; + } + if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) { + return -1; + } + return elf_header.e_type; +} + +// Read the section headers in the given ELF binary, and if a section +// of the specified type is found, set the output to this section header +// and return true. Otherwise, return false. +// To keep stack consumption low, we would like this function to not get +// inlined. +static ATTRIBUTE_NOINLINE bool +GetSectionHeaderByType(const int fd, ElfW(Half) sh_num, const off_t sh_offset, + ElfW(Word) type, ElfW(Shdr) *out) { + // Read at most 16 section headers at a time to save read calls. + ElfW(Shdr) buf[16]; + for (int i = 0; i < sh_num;) { + const ssize_t num_bytes_left = (sh_num - i) * sizeof(buf[0]); + const ssize_t num_bytes_to_read = + (sizeof(buf) > num_bytes_left) ? num_bytes_left : sizeof(buf); + const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read, + sh_offset + i * sizeof(buf[0])); + SAFE_ASSERT(len % sizeof(buf[0]) == 0); + const ssize_t num_headers_in_buf = len / sizeof(buf[0]); + SAFE_ASSERT(num_headers_in_buf <= sizeof(buf) / sizeof(buf[0])); + for (int j = 0; j < num_headers_in_buf; ++j) { + if (buf[j].sh_type == type) { + *out = buf[j]; + return true; + } + } + i += num_headers_in_buf; + } + return false; +} + +// There is no particular reason to limit section name to 63 characters, +// but there has (as yet) been no need for anything longer either. +const int kMaxSectionNameLen = 64; + +// name_len should include terminating '\0'. +bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, + ElfW(Shdr) *out) { + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return false; + } + + ElfW(Shdr) shstrtab; + off_t shstrtab_offset = (elf_header.e_shoff + + elf_header.e_shentsize * elf_header.e_shstrndx); + if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) { + return false; + } + + for (int i = 0; i < elf_header.e_shnum; ++i) { + off_t section_header_offset = (elf_header.e_shoff + + elf_header.e_shentsize * i); + if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) { + return false; + } + char header_name[kMaxSectionNameLen]; + if (sizeof(header_name) < name_len) { + RAW_LOG(WARNING, "Section name '%s' is too long (%"PRIuS"); " + "section will not be found (even if present).", name, name_len); + // No point in even trying. + return false; + } + off_t name_offset = shstrtab.sh_offset + out->sh_name; + ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset); + if (n_read == -1) { + return false; + } else if (n_read != name_len) { + // Short read -- name could be at end of file. + continue; + } + if (memcmp(header_name, name, name_len) == 0) { + return true; + } + } + return false; +} + +// Read a symbol table and look for the symbol containing the +// pc. Iterate over symbols in a symbol table and look for the symbol +// containing "pc". On success, return true and write the symbol name +// to out. Otherwise, return false. +// To keep stack consumption low, we would like this function to not get +// inlined. +static ATTRIBUTE_NOINLINE bool +FindSymbol(uint64_t pc, const int fd, char *out, int out_size, + uint64_t symbol_offset, const ElfW(Shdr) *strtab, + const ElfW(Shdr) *symtab) { + if (symtab == NULL) { + return false; + } + const int num_symbols = symtab->sh_size / symtab->sh_entsize; + for (int i = 0; i < num_symbols;) { + off_t offset = symtab->sh_offset + i * symtab->sh_entsize; + + // If we are reading Elf64_Sym's, we want to limit this array to + // 32 elements (to keep stack consumption low), otherwise we can + // have a 64 element Elf32_Sym array. +#if __WORDSIZE == 64 +#define NUM_SYMBOLS 32 +#else +#define NUM_SYMBOLS 64 +#endif + + // Read at most NUM_SYMBOLS symbols at once to save read() calls. + ElfW(Sym) buf[NUM_SYMBOLS]; + const ssize_t len = ReadFromOffset(fd, &buf, sizeof(buf), offset); + SAFE_ASSERT(len % sizeof(buf[0]) == 0); + const ssize_t num_symbols_in_buf = len / sizeof(buf[0]); + SAFE_ASSERT(num_symbols_in_buf <= sizeof(buf)/sizeof(buf[0])); + for (int j = 0; j < num_symbols_in_buf; ++j) { + const ElfW(Sym)& symbol = buf[j]; + uint64_t start_address = symbol.st_value; + start_address += symbol_offset; + uint64_t end_address = start_address + symbol.st_size; + if (symbol.st_value != 0 && // Skip null value symbols. + symbol.st_shndx != 0 && // Skip undefined symbols. + start_address <= pc && pc < end_address) { + ssize_t len1 = ReadFromOffset(fd, out, out_size, + strtab->sh_offset + symbol.st_name); + if (len1 <= 0 || memchr(out, '\0', out_size) == NULL) { + return false; + } + return true; // Obtained the symbol name. + } + } + i += num_symbols_in_buf; + } + return false; +} + +// Get the symbol name of "pc" from the file pointed by "fd". Process +// both regular and dynamic symbol tables if necessary. On success, +// write the symbol name to "out" and return true. Otherwise, return +// false. +static bool GetSymbolFromObjectFile(const int fd, uint64_t pc, + char *out, int out_size, + uint64_t map_start_address) { + // Read the ELF header. + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return false; + } + + uint64_t symbol_offset = 0; + if (elf_header.e_type == ET_DYN) { // DSO needs offset adjustment. + symbol_offset = map_start_address; + } + + ElfW(Shdr) symtab, strtab; + + // Consult a regular symbol table first. + if (!GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff, + SHT_SYMTAB, &symtab)) { + return false; + } + if (!ReadFromOffsetExact(fd, &strtab, sizeof(strtab), elf_header.e_shoff + + symtab.sh_link * sizeof(symtab))) { + return false; + } + if (FindSymbol(pc, fd, out, out_size, symbol_offset, + &strtab, &symtab)) { + return true; // Found the symbol in a regular symbol table. + } + + // If the symbol is not found, then consult a dynamic symbol table. + if (!GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff, + SHT_DYNSYM, &symtab)) { + return false; + } + if (!ReadFromOffsetExact(fd, &strtab, sizeof(strtab), elf_header.e_shoff + + symtab.sh_link * sizeof(symtab))) { + return false; + } + if (FindSymbol(pc, fd, out, out_size, symbol_offset, + &strtab, &symtab)) { + return true; // Found the symbol in a dynamic symbol table. + } + + return false; +} + +namespace { +// Thin wrapper around a file descriptor so that the file descriptor +// gets closed for sure. +struct FileDescriptor { + const int fd_; + explicit FileDescriptor(int fd) : fd_(fd) {} + ~FileDescriptor() { + if (fd_ >= 0) { + NO_INTR(close(fd_)); + } + } + int get() { return fd_; } + + private: + explicit FileDescriptor(const FileDescriptor&); + void operator=(const FileDescriptor&); +}; + +// Helper class for reading lines from file. +// +// Note: we don't use ProcMapsIterator since the object is big (it has +// a 5k array member) and uses async-unsafe functions such as sscanf() +// and snprintf(). +class LineReader { + public: + explicit LineReader(int fd, char *buf, int buf_len) : fd_(fd), + buf_(buf), buf_len_(buf_len), bol_(buf), eol_(buf), eod_(buf) { + } + + // Read '\n'-terminated line from file. On success, modify "bol" + // and "eol", then return true. Otherwise, return false. + // + // Note: if the last line doesn't end with '\n', the line will be + // dropped. It's an intentional behavior to make the code simple. + bool ReadLine(const char **bol, const char **eol) { + if (BufferIsEmpty()) { // First time. + const ssize_t num_bytes = ReadPersistent(fd_, buf_, buf_len_); + if (num_bytes <= 0) { // EOF or error. + return false; + } + eod_ = buf_ + num_bytes; + bol_ = buf_; + } else { + bol_ = eol_ + 1; // Advance to the next line in the buffer. + SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_". + if (!HasCompleteLine()) { + const int incomplete_line_length = eod_ - bol_; + // Move the trailing incomplete line to the beginning. + memmove(buf_, bol_, incomplete_line_length); + // Read text from file and append it. + char * const append_pos = buf_ + incomplete_line_length; + const int capacity_left = buf_len_ - incomplete_line_length; + const ssize_t num_bytes = ReadPersistent(fd_, append_pos, + capacity_left); + if (num_bytes <= 0) { // EOF or error. + return false; + } + eod_ = append_pos + num_bytes; + bol_ = buf_; + } + } + eol_ = FindLineFeed(); + if (eol_ == NULL) { // '\n' not found. Malformed line. + return false; + } + *eol_ = '\0'; // Replace '\n' with '\0'. + + *bol = bol_; + *eol = eol_; + return true; + } + + // Beginning of line. + const char *bol() { + return bol_; + } + + // End of line. + const char *eol() { + return eol_; + } + + private: + explicit LineReader(const LineReader&); + void operator=(const LineReader&); + + char *FindLineFeed() { + return reinterpret_cast<char *>(memchr(bol_, '\n', eod_ - bol_)); + } + + bool BufferIsEmpty() { + return buf_ == eod_; + } + + bool HasCompleteLine() { + return !BufferIsEmpty() && FindLineFeed() != NULL; + } + + const int fd_; + char * const buf_; + const int buf_len_; + char *bol_; + char *eol_; + const char *eod_; // End of data in "buf_". +}; +} // namespace + +// Place the hex number read from "start" into "*hex". The pointer to +// the first non-hex character or "end" is returned. +static char *GetHex(const char *start, const char *end, uint64_t *hex) { + *hex = 0; + const char *p; + for (p = start; p < end; ++p) { + int ch = *p; + if ((ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) { + *hex = (*hex << 4) | (ch < 'A' ? ch - '0' : (ch & 0xF) + 9); + } else { // Encountered the first non-hex character. + break; + } + } + SAFE_ASSERT(p <= end); + return const_cast<char *>(p); +} + +// Search for the object file (from /proc/self/maps) that contains +// the specified pc. If found, open this file and return the file handle, +// and also set start_address to the start address of where this object +// file is mapped to in memory. Otherwise, return -1. +static ATTRIBUTE_NOINLINE int +OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, + uint64_t &start_address) { + int object_fd; + + // Open /proc/self/maps. + int maps_fd; + NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY)); + FileDescriptor wrapped_maps_fd(maps_fd); + if (wrapped_maps_fd.get() < 0) { + return -1; + } + + // Iterate over maps and look for the map containing the pc. Then + // look into the symbol tables inside. + char buf[1024]; // Big enough for line of sane /proc/self/maps + LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf)); + while (true) { + const char *cursor; + const char *eol; + if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line. + return -1; + } + + // Start parsing line in /proc/self/maps. Here is an example: + // + // 08048000-0804c000 r-xp 00000000 08:01 2142121 /bin/cat + // + // We want start address (08048000), end address (0804c000), flags + // (r-xp) and file name (/bin/cat). + + // Read start address. + cursor = GetHex(cursor, eol, &start_address); + if (cursor == eol || *cursor != '-') { + return -1; // Malformed line. + } + ++cursor; // Skip '-'. + + // Read end address. + uint64_t end_address; + cursor = GetHex(cursor, eol, &end_address); + if (cursor == eol || *cursor != ' ') { + return -1; // Malformed line. + } + ++cursor; // Skip ' '. + + // Check start and end addresses. + if (!(start_address <= pc && pc < end_address)) { + continue; // We skip this map. PC isn't in this map. + } + + // Read flags. Skip flags until we encounter a space or eol. + const char * const flags_start = cursor; + while (cursor < eol && *cursor != ' ') { + ++cursor; + } + // We expect at least four letters for flags (ex. "r-xp"). + if (cursor == eol || cursor < flags_start + 4) { + return -1; // Malformed line. + } + + // Check flags. We are only interested in "r-x" maps. + if (memcmp(flags_start, "r-x", 3) != 0) { // Not a "r-x" map. + continue; // We skip this map. + } + ++cursor; // Skip ' '. + + // Skip to file name. "cursor" now points to file offset. We need to + // skip at least three spaces for file offset, dev, and inode. + int num_spaces = 0; + while (cursor < eol) { + if (*cursor == ' ') { + ++num_spaces; + } else if (num_spaces >= 3) { + // The first non-space character after skipping three spaces + // is the beginning of the file name. + break; + } + ++cursor; + } + if (cursor == eol) { + return -1; // Malformed line. + } + + // Finally, "cursor" now points to file name of our interest. + NO_INTR(object_fd = open(cursor, O_RDONLY)); + if (object_fd < 0) { + return -1; + } + return object_fd; + } +} + +// The implementation of our symbolization routine. If it +// successfully finds the symbol containing "pc" and obtains the +// symbol name, returns true and write the symbol name to "out". +// Otherwise, returns false. If Callback function is installed via +// InstallSymbolizeCallback(), the function is also called in this function, +// and "out" is used as its output. +// To keep stack consumption low, we would like this function to not +// get inlined. +static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, + int out_size) { + uint64_t pc0 = reinterpret_cast<uintptr_t>(pc); + uint64_t start_address = 0; + + int object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0, + start_address); + if (object_fd == -1) { + return false; + } + FileDescriptor wrapped_object_fd(object_fd); + int elf_type = FileGetElfType(wrapped_object_fd.get()); + if (elf_type == -1) { + return false; + } + if (g_symbolize_callback) { + // Run the call back if it's installed. + // Note: relocation (and much of the rest of this code) will be + // wrong for prelinked shared libraries and PIE executables. + uint64 relocation = (elf_type == ET_DYN) ? start_address : 0; + int num_bytes_written = g_symbolize_callback(wrapped_object_fd.get(), + pc, out, out_size, + relocation); + if (num_bytes_written > 0) { + out += num_bytes_written; + out_size -= num_bytes_written; + } + } + if (!GetSymbolFromObjectFile(wrapped_object_fd.get(), pc0, + out, out_size, start_address)) { + return false; + } + + // Symbolization succeeded. Now we try to demangle the symbol. + DemangleInplace(out, out_size); + return true; +} + +_END_GOOGLE_NAMESPACE_ + +#elif defined(OS_MACOSX) && defined(HAVE_DLADDR) + +#include <dlfcn.h> +#include <string.h> + +_START_GOOGLE_NAMESPACE_ + +static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, + int out_size) { + Dl_info info; + if (dladdr(pc, &info)) { + if (strlen(info.dli_sname) < out_size) { + strcpy(out, info.dli_sname); + // Symbolization succeeded. Now we try to demangle the symbol. + DemangleInplace(out, out_size); + return true; + } + } + return false; +} + +_END_GOOGLE_NAMESPACE_ + +#else +# error BUG: HAVE_SYMBOLIZE was wrongly set +#endif + +_START_GOOGLE_NAMESPACE_ + +bool Symbolize(void *pc, char *out, int out_size) { + SAFE_ASSERT(out_size >= 0); + return SymbolizeAndDemangle(pc, out, out_size); +} + +_END_GOOGLE_NAMESPACE_ + +#else /* HAVE_SYMBOLIZE */ + +#include <assert.h> + +#include "config.h" + +_START_GOOGLE_NAMESPACE_ + +// TODO: Support other environments. +bool Symbolize(void *pc, char *out, int out_size) { + assert(0); + return false; +} + +_END_GOOGLE_NAMESPACE_ + +#endif diff --git a/base/third_party/symbolize/symbolize.h b/base/third_party/symbolize/symbolize.h new file mode 100644 index 0000000..1ebe4dd --- /dev/null +++ b/base/third_party/symbolize/symbolize.h @@ -0,0 +1,116 @@ +// Copyright (c) 2006, 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. +// +// Author: Satoru Takabayashi +// +// This library provides Symbolize() function that symbolizes program +// counters to their corresponding symbol names on linux platforms. +// This library has a minimal implementation of an ELF symbol table +// reader (i.e. it doesn't depend on libelf, etc.). +// +// The algorithm used in Symbolize() is as follows. +// +// 1. Go through a list of maps in /proc/self/maps and find the map +// containing the program counter. +// +// 2. Open the mapped file and find a regular symbol table inside. +// Iterate over symbols in the symbol table and look for the symbol +// containing the program counter. If such a symbol is found, +// obtain the symbol name, and demangle the symbol if possible. +// If the symbol isn't found in the regular symbol table (binary is +// stripped), try the same thing with a dynamic symbol table. +// +// Note that Symbolize() is originally implemented to be used in +// FailureSignalHandler() in base/google.cc. Hence it doesn't use +// malloc() and other unsafe operations. It should be both +// thread-safe and async-signal-safe. + +#ifndef BASE_SYMBOLIZE_H_ +#define BASE_SYMBOLIZE_H_ + +#include "utilities.h" +#include "config.h" +#include "glog/logging.h" + +#ifdef HAVE_SYMBOLIZE + +#if defined(__ELF__) // defined by gcc on Linux +#include <elf.h> +#include <link.h> // For ElfW() macro. + +// If there is no ElfW macro, let's define it by ourself. +#ifndef ElfW +# if SIZEOF_VOID_P == 4 +# define ElfW(type) Elf32_##type +# elif SIZEOF_VOID_P == 8 +# define ElfW(type) Elf64_##type +# else +# error "Unknown sizeof(void *)" +# endif +#endif + +_START_GOOGLE_NAMESPACE_ + +// Gets the section header for the given name, if it exists. Returns true on +// success. Otherwise, returns false. +bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, + ElfW(Shdr) *out); + +_END_GOOGLE_NAMESPACE_ + +#endif /* __ELF__ */ + +_START_GOOGLE_NAMESPACE_ + +// Installs a callback function, which will be called right before a symbol name +// is printed. The callback is intended to be used for showing a file name and a +// line number preceding a symbol name. +// "fd" is a file descriptor of the object file containing the program +// counter "pc". The callback function should write output to "out" +// and return the size of the output written. On error, the callback +// function should return -1. +typedef int (*SymbolizeCallback)(int fd, void *pc, char *out, size_t out_size, + uint64 relocation); +void InstallSymbolizeCallback(SymbolizeCallback callback); + +_END_GOOGLE_NAMESPACE_ + +#endif + +_START_GOOGLE_NAMESPACE_ + +// Symbolizes a program counter. On success, returns true and write the +// symbol name to "out". The symbol name is demangled if possible +// (supports symbols generated by GCC 3.x or newer). Otherwise, +// returns false. +bool Symbolize(void *pc, char *out, int out_size); + +_END_GOOGLE_NAMESPACE_ + +#endif // BASE_SYMBOLIZE_H_ diff --git a/base/third_party/symbolize/utilities.h b/base/third_party/symbolize/utilities.h new file mode 100644 index 0000000..0bed526 --- /dev/null +++ b/base/third_party/symbolize/utilities.h @@ -0,0 +1,11 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +typedef uint64_t uint64; +#define HAVE_SYMBOLIZE 1 +#define ATTRIBUTE_NOINLINE __attribute__ ((noinline)) |