mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2026-02-19 08:42:59 +00:00
Breakpad symbol dumper: Move Linux dumping classes into src/common.
The Linux symbol dumper's classes are reasonably portable, and should be usable for the Mac dumper as well. Move them to src/common, along with their unit tests. Update #include directives and Makefile. a=jimblandy, r=nealsid git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@567 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
@@ -1,178 +0,0 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// dump_stabs.cc --- implement the DumpStabsHandler class.
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cxxabi.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "common/linux/dump_stabs.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
|
||||
// Demangle using abi call.
|
||||
// Older GCC may not support it.
|
||||
static string Demangle(const string &mangled) {
|
||||
int status = 0;
|
||||
char *demangled = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
|
||||
if (status == 0 && demangled != NULL) {
|
||||
string str(demangled);
|
||||
free(demangled);
|
||||
return str;
|
||||
}
|
||||
return string(mangled);
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::StartCompilationUnit(const char *name, uint64_t address,
|
||||
const char *build_directory) {
|
||||
assert(!in_compilation_unit_);
|
||||
in_compilation_unit_ = true;
|
||||
current_source_file_name_ = name;
|
||||
current_source_file_ = module_->FindFile(name);
|
||||
comp_unit_base_address_ = address;
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::EndCompilationUnit(uint64_t address) {
|
||||
assert(in_compilation_unit_);
|
||||
in_compilation_unit_ = false;
|
||||
comp_unit_base_address_ = 0;
|
||||
current_source_file_ = NULL;
|
||||
current_source_file_name_ = NULL;
|
||||
if (address)
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::StartFunction(const string &name,
|
||||
uint64_t address) {
|
||||
assert(!current_function_);
|
||||
Module::Function *f = new Module::Function;
|
||||
f->name = Demangle(name);
|
||||
f->address = address;
|
||||
f->size = 0; // We compute this in DumpStabsHandler::Finalize().
|
||||
f->parameter_size = 0; // We don't provide this information.
|
||||
current_function_ = f;
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::EndFunction(uint64_t address) {
|
||||
assert(current_function_);
|
||||
// Functions in this compilation unit should have address bigger
|
||||
// than the compilation unit's starting address. There may be a lot
|
||||
// of duplicated entries for functions in the STABS data; only one
|
||||
// entry can meet this requirement.
|
||||
//
|
||||
// (I don't really understand the above comment; just bringing it
|
||||
// along from the previous code, and leaving the behaivor unchanged.
|
||||
// If you know the whole story, please patch this comment. --jimb)
|
||||
if (current_function_->address >= comp_unit_base_address_)
|
||||
functions_.push_back(current_function_);
|
||||
else
|
||||
delete current_function_;
|
||||
current_function_ = NULL;
|
||||
if (address)
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::Line(uint64_t address, const char *name, int number) {
|
||||
assert(current_function_);
|
||||
assert(current_source_file_);
|
||||
if (name != current_source_file_name_) {
|
||||
current_source_file_ = module_->FindFile(name);
|
||||
current_source_file_name_ = name;
|
||||
}
|
||||
Module::Line line;
|
||||
line.address = address;
|
||||
line.size = 0; // We compute this in DumpStabsHandler::Finalize().
|
||||
line.file = current_source_file_;
|
||||
line.number = number;
|
||||
current_function_->lines.push_back(line);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpStabsHandler::Warning(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void DumpStabsHandler::Finalize() {
|
||||
// Sort our boundary list, so we can search it quickly.
|
||||
sort(boundaries_.begin(), boundaries_.end());
|
||||
// Sort all functions by address, just for neatness.
|
||||
sort(functions_.begin(), functions_.end(),
|
||||
Module::Function::CompareByAddress);
|
||||
for (vector<Module::Function *>::iterator func_it = functions_.begin();
|
||||
func_it != functions_.end();
|
||||
func_it++) {
|
||||
Module::Function *f = *func_it;
|
||||
// Compute the function f's size.
|
||||
vector<Module::Address>::iterator boundary
|
||||
= std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address);
|
||||
if (boundary != boundaries_.end())
|
||||
f->size = *boundary - f->address;
|
||||
else
|
||||
// If this is the last function in the module, and the STABS
|
||||
// reader was unable to give us its ending address, then assign
|
||||
// it a bogus, very large value. This will happen at most once
|
||||
// per module: since we've added all functions' addresses to the
|
||||
// boundary table, only one can be the last.
|
||||
f->size = kFallbackSize;
|
||||
|
||||
// Compute sizes for each of the function f's lines --- if it has any.
|
||||
if (!f->lines.empty()) {
|
||||
stable_sort(f->lines.begin(), f->lines.end(),
|
||||
Module::Line::CompareByAddress);
|
||||
vector<Module::Line>::iterator last_line = f->lines.end() - 1;
|
||||
for (vector<Module::Line>::iterator line_it = f->lines.begin();
|
||||
line_it != last_line; line_it++)
|
||||
line_it[0].size = line_it[1].address - line_it[0].address;
|
||||
// Compute the size of the last line from f's end address.
|
||||
last_line->size = (f->address + f->size) - last_line->address;
|
||||
}
|
||||
}
|
||||
// Now that everything has a size, add our functions to the module, and
|
||||
// dispose of our private list.
|
||||
module_->AddFunctions(functions_.begin(), functions_.end());
|
||||
functions_.clear();
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
@@ -1,139 +0,0 @@
|
||||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// dump_stabs.h: Define the DumpStabsHandler class, which receives
|
||||
// STABS debugging information from a parser and adds it to a Breakpad
|
||||
// symbol file.
|
||||
|
||||
#ifndef COMMON_LINUX_DUMP_STABS_H__
|
||||
#define COMMON_LINUX_DUMP_STABS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/linux/module.h"
|
||||
#include "common/linux/stabs_reader.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
// A DumpStabsHandler is a handler that receives parsed STABS
|
||||
// debugging information from a StabsReader, and uses that to populate
|
||||
// a Module. (All classes are in the google_breakpad namespace.) A
|
||||
// Module represents the contents of a Breakpad symbol file, and knows
|
||||
// how to write itself out as such. A DumpStabsHandler thus acts as
|
||||
// the bridge between STABS and Breakpad data.
|
||||
class DumpStabsHandler: public google_breakpad::StabsHandler {
|
||||
public:
|
||||
// Receive parsed debugging information from a StabsReader, and
|
||||
// store it all in MODULE.
|
||||
DumpStabsHandler(Module *module) :
|
||||
module_(module),
|
||||
in_compilation_unit_(false),
|
||||
comp_unit_base_address_(0),
|
||||
current_function_(NULL),
|
||||
current_source_file_(NULL),
|
||||
current_source_file_name_(NULL) { }
|
||||
|
||||
// The standard StabsHandler virtual member functions.
|
||||
bool StartCompilationUnit(const char *name, uint64_t address,
|
||||
const char *build_directory);
|
||||
bool EndCompilationUnit(uint64_t address);
|
||||
bool StartFunction(const string &name, uint64_t address);
|
||||
bool EndFunction(uint64_t address);
|
||||
bool Line(uint64_t address, const char *name, int number);
|
||||
void Warning(const char *format, ...);
|
||||
|
||||
// Do any final processing necessary to make module_ contain all the
|
||||
// data provided by the STABS reader.
|
||||
//
|
||||
// Because STABS does not provide reliable size information for
|
||||
// functions and lines, we need to make a pass over the data after
|
||||
// processing all the STABS to compute those sizes. We take care of
|
||||
// that here.
|
||||
void Finalize();
|
||||
|
||||
private:
|
||||
|
||||
// An arbitrary, but very large, size to use for functions whose
|
||||
// size we can't compute properly.
|
||||
static const uint64_t kFallbackSize = 0x10000000;
|
||||
|
||||
// The module we're contributing debugging info to.
|
||||
Module *module_;
|
||||
|
||||
// The functions we've generated so far. We don't add these to
|
||||
// module_ as we parse them. Instead, we wait until we've computed
|
||||
// their ending address, and their lines' ending addresses.
|
||||
//
|
||||
// We could just stick them in module_ from the outset, but if
|
||||
// module_ already contains data gathered from other debugging
|
||||
// formats, that would complicate the size computation.
|
||||
vector<Module::Function *> functions_;
|
||||
|
||||
// Boundary addresses. STABS doesn't necessarily supply sizes for
|
||||
// functions and lines, so we need to compute them ourselves by
|
||||
// finding the next object.
|
||||
vector<Module::Address> boundaries_;
|
||||
|
||||
// True if we are currently within a compilation unit: we have gotten a
|
||||
// StartCompilationUnit call, but no matching EndCompilationUnit call
|
||||
// yet. We use this for sanity checks.
|
||||
bool in_compilation_unit_;
|
||||
|
||||
// The base address of the current compilation unit. We use this to
|
||||
// recognize functions we should omit from the symbol file. (If you
|
||||
// know the details of why we omit these, please patch this
|
||||
// comment.)
|
||||
Module::Address comp_unit_base_address_;
|
||||
|
||||
// The function we're currently contributing lines to.
|
||||
Module::Function *current_function_;
|
||||
|
||||
// The last Module::File we got a line number in.
|
||||
Module::File *current_source_file_;
|
||||
|
||||
// The pointer in the .stabstr section of the name that
|
||||
// current_source_file_ is built from. This allows us to quickly
|
||||
// recognize when the current line is in the same file as the
|
||||
// previous one (which it usually is).
|
||||
const char *current_source_file_name_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_DUMP_STABS_H__
|
||||
@@ -1,193 +0,0 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// dump_stabs_unittest.cc: Unit tests for DumpStabsHandler.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/dump_stabs.h"
|
||||
|
||||
using google_breakpad::DumpStabsHandler;
|
||||
using google_breakpad::Module;
|
||||
using std::vector;
|
||||
|
||||
TEST(DumpStabsHandler, SimpleCU) {
|
||||
Module m("name", "os", "arch", "id");
|
||||
DumpStabsHandler h(&m);
|
||||
|
||||
// Feed in a simple compilation unit that defines a function with
|
||||
// one line.
|
||||
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0x9f4d1271e50db93bLL,
|
||||
"build-directory"));
|
||||
EXPECT_TRUE(h.StartFunction("function", 0xfde4abbed390c394LL));
|
||||
EXPECT_TRUE(h.Line(0xfde4abbed390c394LL, "source-file-name", 174823314));
|
||||
EXPECT_TRUE(h.EndFunction(0xfde4abbed390c3a4LL));
|
||||
EXPECT_TRUE(h.EndCompilationUnit(0xfee4abbed390c3a4LL));
|
||||
h.Finalize();
|
||||
|
||||
// Now check to see what has been added to the Module.
|
||||
Module::File *file = m.FindExistingFile("source-file-name");
|
||||
ASSERT_TRUE(file != NULL);
|
||||
|
||||
vector<Module::Function *> functions;
|
||||
m.GetFunctions(&functions, functions.end());
|
||||
ASSERT_EQ((size_t) 1, functions.size());
|
||||
Module::Function *function = functions[0];
|
||||
EXPECT_STREQ("function", function->name.c_str());
|
||||
EXPECT_EQ(0xfde4abbed390c394LL, function->address);
|
||||
EXPECT_EQ(0x10U, function->size);
|
||||
EXPECT_EQ(0U, function->parameter_size);
|
||||
ASSERT_EQ((size_t) 1, function->lines.size());
|
||||
Module::Line *line = &function->lines[0];
|
||||
EXPECT_EQ(0xfde4abbed390c394LL, line->address);
|
||||
EXPECT_EQ(0x10U, line->size); // derived from EndFunction
|
||||
EXPECT_TRUE(line->file == file);
|
||||
EXPECT_EQ(174823314, line->number);
|
||||
}
|
||||
|
||||
TEST(InferSizes, LineSize) {
|
||||
Module m("name", "os", "arch", "id");
|
||||
DumpStabsHandler h(&m);
|
||||
|
||||
// Feed in a simple compilation unit that defines a function with
|
||||
// one line.
|
||||
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xb4513962eff94e92LL,
|
||||
"build-directory"));
|
||||
EXPECT_TRUE(h.StartFunction("function", 0xb4513962eff94e92LL));
|
||||
EXPECT_TRUE(h.Line(0xb4513962eff94e92LL, "source-file-name-1", 77396614));
|
||||
EXPECT_TRUE(h.Line(0xb4513963eff94e92LL, "source-file-name-2", 87660088));
|
||||
EXPECT_TRUE(h.EndFunction(0)); // unknown function end address
|
||||
EXPECT_TRUE(h.EndCompilationUnit(0)); // unknown CU end address
|
||||
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit-2", 0xb4523963eff94e92LL,
|
||||
"build-directory-2")); // next boundary
|
||||
EXPECT_TRUE(h.EndCompilationUnit(0));
|
||||
h.Finalize();
|
||||
|
||||
// Now check to see what has been added to the Module.
|
||||
Module::File *file1 = m.FindExistingFile("source-file-name-1");
|
||||
ASSERT_TRUE(file1 != NULL);
|
||||
Module::File *file2 = m.FindExistingFile("source-file-name-2");
|
||||
ASSERT_TRUE(file2 != NULL);
|
||||
|
||||
vector<Module::Function *> functions;
|
||||
m.GetFunctions(&functions, functions.end());
|
||||
ASSERT_EQ((size_t) 1, functions.size());
|
||||
|
||||
Module::Function *function = functions[0];
|
||||
EXPECT_STREQ("function", function->name.c_str());
|
||||
EXPECT_EQ(0xb4513962eff94e92LL, function->address);
|
||||
EXPECT_EQ(0x1000100000000ULL, function->size); // inferred from CU end
|
||||
EXPECT_EQ(0U, function->parameter_size);
|
||||
ASSERT_EQ((size_t) 2, function->lines.size());
|
||||
|
||||
Module::Line *line1 = &function->lines[0];
|
||||
EXPECT_EQ(0xb4513962eff94e92LL, line1->address);
|
||||
EXPECT_EQ(0x100000000ULL, line1->size); // derived from EndFunction
|
||||
EXPECT_TRUE(line1->file == file1);
|
||||
EXPECT_EQ(77396614, line1->number);
|
||||
|
||||
Module::Line *line2 = &function->lines[1];
|
||||
EXPECT_EQ(0xb4513963eff94e92LL, line2->address);
|
||||
EXPECT_EQ(0x1000000000000ULL, line2->size); // derived from EndFunction
|
||||
EXPECT_TRUE(line2->file == file2);
|
||||
EXPECT_EQ(87660088, line2->number);
|
||||
}
|
||||
|
||||
TEST(FunctionNames, Mangled) {
|
||||
Module m("name", "os", "arch", "id");
|
||||
DumpStabsHandler h(&m);
|
||||
|
||||
// Compilation unit with one function, mangled name.
|
||||
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xf2cfda63cef7f46cLL,
|
||||
"build-directory"));
|
||||
EXPECT_TRUE(h.StartFunction("_ZNSt6vectorIySaIyEE9push_backERKy",
|
||||
0xf2cfda63cef7f46dLL));
|
||||
EXPECT_TRUE(h.EndFunction(0));
|
||||
EXPECT_TRUE(h.EndCompilationUnit(0));
|
||||
|
||||
h.Finalize();
|
||||
|
||||
// Now check to see what has been added to the Module.
|
||||
Module::File *file = m.FindExistingFile("compilation-unit");
|
||||
ASSERT_TRUE(file != NULL);
|
||||
|
||||
vector<Module::Function *> functions;
|
||||
m.GetFunctions(&functions, functions.end());
|
||||
ASSERT_EQ(1U, functions.size());
|
||||
|
||||
Module::Function *function = functions[0];
|
||||
// This is GCC-specific, but we shouldn't be seeing STABS data anywhere
|
||||
// but Linux.
|
||||
EXPECT_STREQ("std::vector<unsigned long long, "
|
||||
"std::allocator<unsigned long long> >::"
|
||||
"push_back(unsigned long long const&)",
|
||||
function->name.c_str());
|
||||
EXPECT_EQ(0xf2cfda63cef7f46dLL, function->address);
|
||||
EXPECT_LT(0U, function->size); // should have used dummy size
|
||||
EXPECT_EQ(0U, function->parameter_size);
|
||||
ASSERT_EQ(0U, function->lines.size());
|
||||
}
|
||||
|
||||
// The GNU toolchain can omit functions that are not used; however,
|
||||
// when it does so, it doesn't clean up the debugging information that
|
||||
// refers to them. In STABS, this results in compilation units whose
|
||||
// SO addresses are zero.
|
||||
TEST(Omitted, Function) {
|
||||
Module m("name", "os", "arch", "id");
|
||||
DumpStabsHandler h(&m);
|
||||
|
||||
// The StartCompilationUnit and EndCompilationUnit calls may both have an
|
||||
// address of zero if the compilation unit has had sections removed.
|
||||
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0, "build-directory"));
|
||||
EXPECT_TRUE(h.StartFunction("function", 0x2a133596));
|
||||
EXPECT_TRUE(h.EndFunction(0));
|
||||
EXPECT_TRUE(h.EndCompilationUnit(0));
|
||||
}
|
||||
|
||||
// TODO --- if we actually cared about STABS. Even without these we've
|
||||
// got full coverage of non-failure source lines in dump_stabs.cc.
|
||||
|
||||
// Line size from next line
|
||||
// Line size from function end
|
||||
// Line size from next function start
|
||||
// line size from cu end
|
||||
// line size from next cu start
|
||||
// fallback size is something plausible
|
||||
|
||||
// function size from function end
|
||||
// function size from next function start
|
||||
// function size from cu end
|
||||
// function size from next cu start
|
||||
// fallback size is something plausible
|
||||
|
||||
// omitting functions outside the compilation unit's address range
|
||||
// zero-line, one-line, many-line functions
|
||||
@@ -48,14 +48,14 @@
|
||||
|
||||
#include "common/dwarf/bytereader-inl.h"
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
#include "common/linux/dump_stabs.h"
|
||||
#include "common/dump_stabs.h"
|
||||
#include "common/linux/dump_symbols.h"
|
||||
#include "common/linux/dwarf_cfi_to_module.h"
|
||||
#include "common/linux/dwarf_cu_to_module.h"
|
||||
#include "common/linux/dwarf_line_to_module.h"
|
||||
#include "common/dwarf_cfi_to_module.h"
|
||||
#include "common/dwarf_cu_to_module.h"
|
||||
#include "common/dwarf_line_to_module.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/module.h"
|
||||
#include "common/linux/stabs_reader.h"
|
||||
#include "common/module.h"
|
||||
#include "common/stabs_reader.h"
|
||||
|
||||
// This namespace contains helper functions.
|
||||
namespace {
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010, 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// Implementation of google_breakpad::DwarfCFIToModule.
|
||||
// See dwarf_cfi_to_module.h for details.
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "common/linux/dwarf_cfi_to_module.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::ostringstream;
|
||||
|
||||
bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
|
||||
uint8 version, const string &augmentation,
|
||||
unsigned return_address) {
|
||||
assert(!entry_);
|
||||
|
||||
// If dwarf2reader::CallFrameInfo can handle this version and
|
||||
// augmentation, then we should be okay with that, so there's no
|
||||
// need to check them here.
|
||||
|
||||
// Get ready to collect entries.
|
||||
entry_ = new Module::StackFrameEntry;
|
||||
entry_->address = address;
|
||||
entry_->size = length;
|
||||
entry_offset_ = offset;
|
||||
return_address_ = return_address;
|
||||
|
||||
// Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI
|
||||
// may not establish any rule for .ra if the return address column
|
||||
// is an ordinary register, and that register holds the return
|
||||
// address on entry to the function. So establish an initial .ra
|
||||
// rule citing the return address register.
|
||||
if (return_address_ < register_names_.size())
|
||||
entry_->initial_rules[".ra"] = register_names_[return_address_];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string DwarfCFIToModule::RegisterName(int i) {
|
||||
assert(entry_);
|
||||
if (i < 0) {
|
||||
assert(i == kCFARegister);
|
||||
return ".cfa";
|
||||
}
|
||||
unsigned reg = i;
|
||||
if (reg == return_address_)
|
||||
return ".ra";
|
||||
|
||||
if (0 <= reg && reg < register_names_.size())
|
||||
return register_names_[reg];
|
||||
|
||||
reporter_->UnnamedRegister(entry_offset_, reg);
|
||||
char buf[30];
|
||||
sprintf(buf, "unnamed_register%u", reg);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void DwarfCFIToModule::Record(Module::Address address, int reg,
|
||||
const string &rule) {
|
||||
assert(entry_);
|
||||
// Is this one of this entry's initial rules?
|
||||
if (address == entry_->address)
|
||||
entry_->initial_rules[RegisterName(reg)] = rule;
|
||||
// File it under the appropriate address.
|
||||
else
|
||||
entry_->rule_changes[address][RegisterName(reg)] = rule;
|
||||
}
|
||||
|
||||
bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) {
|
||||
reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg));
|
||||
// Treat this as a non-fatal error.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) {
|
||||
ostringstream s;
|
||||
s << RegisterName(reg);
|
||||
Record(address, reg, s.str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DwarfCFIToModule::OffsetRule(uint64 address, int reg,
|
||||
int base_register, long offset) {
|
||||
ostringstream s;
|
||||
s << RegisterName(base_register) << " " << offset << " + ^";
|
||||
Record(address, reg, s.str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg,
|
||||
int base_register, long offset) {
|
||||
ostringstream s;
|
||||
s << RegisterName(base_register) << " " << offset << " +";
|
||||
Record(address, reg, s.str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DwarfCFIToModule::RegisterRule(uint64 address, int reg,
|
||||
int base_register) {
|
||||
ostringstream s;
|
||||
s << RegisterName(base_register);
|
||||
Record(address, reg, s.str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg,
|
||||
const string &expression) {
|
||||
reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg));
|
||||
// Treat this as a non-fatal error.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg,
|
||||
const string &expression) {
|
||||
reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg));
|
||||
// Treat this as a non-fatal error.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DwarfCFIToModule::End() {
|
||||
module_->AddStackFrameEntry(entry_);
|
||||
entry_ = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DwarfCFIToModule::Reporter::UnnamedRegister(size_t offset, int reg) {
|
||||
fprintf(stderr, "%s, section '%s': "
|
||||
"the call frame entry at offset 0x%zx refers to register %d,"
|
||||
" whose name we don't know\n",
|
||||
file_.c_str(), section_.c_str(), offset, reg);
|
||||
}
|
||||
|
||||
void DwarfCFIToModule::Reporter::UndefinedNotSupported(size_t offset,
|
||||
const string ®) {
|
||||
fprintf(stderr, "%s, section '%s': "
|
||||
"the call frame entry at offset 0x%zx sets the rule for "
|
||||
"register '%s' to 'undefined', but the Breakpad symbol file format"
|
||||
" cannot express this\n",
|
||||
file_.c_str(), section_.c_str(), offset, reg.c_str());
|
||||
}
|
||||
|
||||
void DwarfCFIToModule::Reporter::ExpressionsNotSupported(size_t offset,
|
||||
const string ®) {
|
||||
fprintf(stderr, "%s, section '%s': "
|
||||
"the call frame entry at offset 0x%zx uses a DWARF expression to"
|
||||
" describe how to recover register '%s', "
|
||||
" but this translator cannot yet translate DWARF expressions to"
|
||||
" Breakpad postfix expressions\n",
|
||||
file_.c_str(), section_.c_str(), offset, reg.c_str());
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
@@ -1,154 +0,0 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010, 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// dwarf_cfi_to_module.h: Define the DwarfCFIToModule class, which
|
||||
// accepts parsed DWARF call frame info and adds it to a
|
||||
// google_breakpad::Module object, which can write that information to
|
||||
// a Breakpad symbol file.
|
||||
|
||||
#ifndef COMMON_LINUX_DWARF_CFI_TO_MODULE_H
|
||||
#define COMMON_LINUX_DWARF_CFI_TO_MODULE_H
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/linux/module.h"
|
||||
#include "common/dwarf/dwarf2reader.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using dwarf2reader::CallFrameInfo;
|
||||
using google_breakpad::Module;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
// A class that accepts parsed call frame information from the DWARF
|
||||
// CFI parser and populates a google_breakpad::Module object with the
|
||||
// contents.
|
||||
class DwarfCFIToModule: public CallFrameInfo::Handler {
|
||||
public:
|
||||
|
||||
// DwarfCFIToModule uses an instance of this class to report errors
|
||||
// detected while converting DWARF CFI to Breakpad STACK CFI records.
|
||||
class Reporter {
|
||||
public:
|
||||
// Create a reporter that writes messages to the standard error
|
||||
// stream. FILE is the name of the file we're processing, and
|
||||
// SECTION is the name of the section within that file that we're
|
||||
// looking at (.debug_frame, .eh_frame, etc.).
|
||||
Reporter(const string &file, const string §ion)
|
||||
: file_(file), section_(section) { }
|
||||
virtual ~Reporter() { }
|
||||
|
||||
// The DWARF CFI entry at OFFSET cites register REG, but REG is not
|
||||
// covered by the vector of register names passed to the
|
||||
// DwarfCFIToModule constructor, nor does it match the return
|
||||
// address column number for this entry.
|
||||
virtual void UnnamedRegister(size_t offset, int reg);
|
||||
|
||||
// The DWARF CFI entry at OFFSET says that REG is undefined, but the
|
||||
// Breakpad symbol file format cannot express this.
|
||||
virtual void UndefinedNotSupported(size_t offset, const string ®);
|
||||
|
||||
// The DWARF CFI entry at OFFSET says that REG uses a DWARF
|
||||
// expression to find its value, but DwarfCFIToModule is not
|
||||
// capable of translating DWARF expressions to Breakpad postfix
|
||||
// expressions.
|
||||
virtual void ExpressionsNotSupported(size_t offset, const string ®);
|
||||
|
||||
protected:
|
||||
string file_, section_;
|
||||
};
|
||||
|
||||
// Create a handler for the dwarf2reader::CallFrameInfo parser that
|
||||
// records the stack unwinding information it receives in MODULE.
|
||||
//
|
||||
// Use REGISTER_NAMES[I] as the name of register number I; *this
|
||||
// keeps a reference to the vector, so the vector should remain
|
||||
// alive for as long as the DwarfCFIToModule does.
|
||||
//
|
||||
// Use REPORTER for reporting problems encountered in the conversion
|
||||
// process.
|
||||
DwarfCFIToModule(Module *module, const vector<string> ®ister_names,
|
||||
Reporter *reporter)
|
||||
: module_(module), register_names_(register_names), reporter_(reporter),
|
||||
entry_(NULL), return_address_(-1) { }
|
||||
virtual ~DwarfCFIToModule() { delete entry_; }
|
||||
|
||||
virtual bool Entry(size_t offset, uint64 address, uint64 length,
|
||||
uint8 version, const string &augmentation,
|
||||
unsigned return_address);
|
||||
virtual bool UndefinedRule(uint64 address, int reg);
|
||||
virtual bool SameValueRule(uint64 address, int reg);
|
||||
virtual bool OffsetRule(uint64 address, int reg,
|
||||
int base_register, long offset);
|
||||
virtual bool ValOffsetRule(uint64 address, int reg,
|
||||
int base_register, long offset);
|
||||
virtual bool RegisterRule(uint64 address, int reg, int base_register);
|
||||
virtual bool ExpressionRule(uint64 address, int reg,
|
||||
const string &expression);
|
||||
virtual bool ValExpressionRule(uint64 address, int reg,
|
||||
const string &expression);
|
||||
virtual bool End();
|
||||
|
||||
private:
|
||||
// Return the name to use for register REG.
|
||||
string RegisterName(int i);
|
||||
|
||||
// Record RULE for register REG at ADDRESS.
|
||||
void Record(Module::Address address, int reg, const string &rule);
|
||||
|
||||
// The module to which we should add entries.
|
||||
Module *module_;
|
||||
|
||||
// Map from register numbers to register names.
|
||||
const vector<string> ®ister_names_;
|
||||
|
||||
// The reporter to use to report problems.
|
||||
Reporter *reporter_;
|
||||
|
||||
// The current entry we're constructing.
|
||||
Module::StackFrameEntry *entry_;
|
||||
|
||||
// The section offset of the current frame description entry, for
|
||||
// use in error messages.
|
||||
size_t entry_offset_;
|
||||
|
||||
// The return address column for that entry.
|
||||
unsigned return_address_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H
|
||||
@@ -1,260 +0,0 @@
|
||||
// Copyright (c) 2010, 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule.
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/dwarf_cfi_to_module.h"
|
||||
|
||||
using google_breakpad::Module;
|
||||
using google_breakpad::DwarfCFIToModule;
|
||||
using testing::ContainerEq;
|
||||
using testing::Test;
|
||||
using testing::_;
|
||||
|
||||
struct MockCFIReporter: public DwarfCFIToModule::Reporter {
|
||||
MockCFIReporter(const string &file, const string §ion)
|
||||
: Reporter(file, section) { }
|
||||
MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg));
|
||||
MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string ®));
|
||||
MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string ®));
|
||||
};
|
||||
|
||||
struct DwarfCFIToModuleFixture {
|
||||
DwarfCFIToModuleFixture()
|
||||
: module("module name", "module os", "module arch", "module id"),
|
||||
reporter("reporter file", "reporter section"),
|
||||
handler(&module, register_names, &reporter) {
|
||||
register_names.push_back("reg0");
|
||||
register_names.push_back("reg1");
|
||||
register_names.push_back("reg2");
|
||||
register_names.push_back("reg3");
|
||||
register_names.push_back("reg4");
|
||||
register_names.push_back("reg5");
|
||||
register_names.push_back("reg6");
|
||||
register_names.push_back("reg7");
|
||||
register_names.push_back("sp");
|
||||
register_names.push_back("pc");
|
||||
|
||||
EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0);
|
||||
EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0);
|
||||
EXPECT_CALL(reporter, ExpressionsNotSupported(_, _)).Times(0);
|
||||
}
|
||||
|
||||
Module module;
|
||||
vector<string> register_names;
|
||||
MockCFIReporter reporter;
|
||||
DwarfCFIToModule handler;
|
||||
vector<Module::StackFrameEntry *> entries;
|
||||
};
|
||||
|
||||
class Entry: public DwarfCFIToModuleFixture, public Test { };
|
||||
|
||||
TEST_F(Entry, Accept) {
|
||||
ASSERT_TRUE(handler.Entry(0x3b8961b8, 0xa21069698096fc98ULL,
|
||||
0xb440ce248169c8d6ULL, 3, "", 0xea93c106));
|
||||
ASSERT_TRUE(handler.End());
|
||||
module.GetStackFrameEntries(&entries);
|
||||
EXPECT_EQ(1U, entries.size());
|
||||
EXPECT_EQ(0xa21069698096fc98ULL, entries[0]->address);
|
||||
EXPECT_EQ(0xb440ce248169c8d6ULL, entries[0]->size);
|
||||
EXPECT_EQ(0U, entries[0]->initial_rules.size());
|
||||
EXPECT_EQ(0U, entries[0]->rule_changes.size());
|
||||
}
|
||||
|
||||
TEST_F(Entry, AcceptOldVersion) {
|
||||
ASSERT_TRUE(handler.Entry(0xeb60e0fc, 0x75b8806bb09eab78ULL,
|
||||
0xc771f44958d40bbcULL, 1, "", 0x093c945e));
|
||||
ASSERT_TRUE(handler.End());
|
||||
module.GetStackFrameEntries(&entries);
|
||||
EXPECT_EQ(1U, entries.size());
|
||||
EXPECT_EQ(0x75b8806bb09eab78ULL, entries[0]->address);
|
||||
EXPECT_EQ(0xc771f44958d40bbcULL, entries[0]->size);
|
||||
EXPECT_EQ(0U, entries[0]->initial_rules.size());
|
||||
EXPECT_EQ(0U, entries[0]->rule_changes.size());
|
||||
}
|
||||
|
||||
struct RuleFixture: public DwarfCFIToModuleFixture {
|
||||
RuleFixture() : DwarfCFIToModuleFixture() {
|
||||
entry_address = 0x89327ebf86b47492ULL;
|
||||
entry_size = 0x2f8cd573072fe02aULL;
|
||||
return_reg = 0x7886a346;
|
||||
}
|
||||
void StartEntry() {
|
||||
ASSERT_TRUE(handler.Entry(0x4445c05c, entry_address, entry_size,
|
||||
3, "", return_reg));
|
||||
}
|
||||
void CheckEntry() {
|
||||
module.GetStackFrameEntries(&entries);
|
||||
EXPECT_EQ(1U, entries.size());
|
||||
EXPECT_EQ(entry_address, entries[0]->address);
|
||||
EXPECT_EQ(entry_size, entries[0]->size);
|
||||
}
|
||||
uint64 entry_address, entry_size;
|
||||
unsigned return_reg;
|
||||
};
|
||||
|
||||
class Rule: public RuleFixture, public Test { };
|
||||
|
||||
TEST_F(Rule, UndefinedRule) {
|
||||
EXPECT_CALL(reporter, UndefinedNotSupported(_, "reg7"));
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.UndefinedRule(entry_address, 7));
|
||||
ASSERT_TRUE(handler.End());
|
||||
CheckEntry();
|
||||
EXPECT_EQ(0U, entries[0]->initial_rules.size());
|
||||
EXPECT_EQ(0U, entries[0]->rule_changes.size());
|
||||
}
|
||||
|
||||
TEST_F(Rule, SameValueRule) {
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.SameValueRule(entry_address, 6));
|
||||
ASSERT_TRUE(handler.End());
|
||||
CheckEntry();
|
||||
Module::RuleMap expected_initial;
|
||||
expected_initial["reg6"] = "reg6";
|
||||
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
|
||||
EXPECT_EQ(0U, entries[0]->rule_changes.size());
|
||||
}
|
||||
|
||||
TEST_F(Rule, OffsetRule) {
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.OffsetRule(entry_address + 1, return_reg,
|
||||
DwarfCFIToModule::kCFARegister,
|
||||
16927065));
|
||||
ASSERT_TRUE(handler.End());
|
||||
CheckEntry();
|
||||
EXPECT_EQ(0U, entries[0]->initial_rules.size());
|
||||
Module::RuleChangeMap expected_changes;
|
||||
expected_changes[entry_address + 1][".ra"] = ".cfa 16927065 + ^";
|
||||
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
|
||||
}
|
||||
|
||||
TEST_F(Rule, OffsetRuleNegative) {
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.OffsetRule(entry_address + 1,
|
||||
DwarfCFIToModule::kCFARegister, 4, -34530721));
|
||||
ASSERT_TRUE(handler.End());
|
||||
CheckEntry();
|
||||
EXPECT_EQ(0U, entries[0]->initial_rules.size());
|
||||
Module::RuleChangeMap expected_changes;
|
||||
expected_changes[entry_address + 1][".cfa"] = "reg4 -34530721 + ^";
|
||||
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
|
||||
}
|
||||
|
||||
TEST_F(Rule, ValOffsetRule) {
|
||||
// Use an unnamed register number, to exercise that branch of RegisterName.
|
||||
EXPECT_CALL(reporter, UnnamedRegister(_, 10));
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7,
|
||||
DwarfCFIToModule::kCFARegister,
|
||||
10, 61812979));
|
||||
ASSERT_TRUE(handler.End());
|
||||
CheckEntry();
|
||||
EXPECT_EQ(0U, entries[0]->initial_rules.size());
|
||||
Module::RuleChangeMap expected_changes;
|
||||
expected_changes[entry_address + 0x5ab7][".cfa"] =
|
||||
"unnamed_register10 61812979 +";
|
||||
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
|
||||
}
|
||||
|
||||
TEST_F(Rule, RegisterRule) {
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 3));
|
||||
ASSERT_TRUE(handler.End());
|
||||
CheckEntry();
|
||||
Module::RuleMap expected_initial;
|
||||
expected_initial[".ra"] = "reg3";
|
||||
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
|
||||
EXPECT_EQ(0U, entries[0]->rule_changes.size());
|
||||
}
|
||||
|
||||
TEST_F(Rule, ExpressionRule) {
|
||||
EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg2"));
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.ExpressionRule(entry_address + 0xf326, 2,
|
||||
"it takes two to tango"));
|
||||
ASSERT_TRUE(handler.End());
|
||||
CheckEntry();
|
||||
EXPECT_EQ(0U, entries[0]->initial_rules.size());
|
||||
EXPECT_EQ(0U, entries[0]->rule_changes.size());
|
||||
}
|
||||
|
||||
TEST_F(Rule, ValExpressionRule) {
|
||||
EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg0"));
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.ValExpressionRule(entry_address + 0x6367, 0,
|
||||
"bit off more than he could chew"));
|
||||
ASSERT_TRUE(handler.End());
|
||||
CheckEntry();
|
||||
EXPECT_EQ(0U, entries[0]->initial_rules.size());
|
||||
EXPECT_EQ(0U, entries[0]->rule_changes.size());
|
||||
}
|
||||
|
||||
TEST_F(Rule, DefaultReturnAddressRule) {
|
||||
return_reg = 2;
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.RegisterRule(entry_address, 0, 1));
|
||||
ASSERT_TRUE(handler.End());
|
||||
CheckEntry();
|
||||
Module::RuleMap expected_initial;
|
||||
expected_initial[".ra"] = "reg2";
|
||||
expected_initial["reg0"] = "reg1";
|
||||
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
|
||||
EXPECT_EQ(0U, entries[0]->rule_changes.size());
|
||||
}
|
||||
|
||||
TEST_F(Rule, DefaultReturnAddressRuleOverride) {
|
||||
return_reg = 2;
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 1));
|
||||
ASSERT_TRUE(handler.End());
|
||||
CheckEntry();
|
||||
Module::RuleMap expected_initial;
|
||||
expected_initial[".ra"] = "reg1";
|
||||
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
|
||||
EXPECT_EQ(0U, entries[0]->rule_changes.size());
|
||||
}
|
||||
|
||||
TEST_F(Rule, DefaultReturnAddressRuleLater) {
|
||||
return_reg = 2;
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.RegisterRule(entry_address + 1, return_reg, 1));
|
||||
ASSERT_TRUE(handler.End());
|
||||
CheckEntry();
|
||||
Module::RuleMap expected_initial;
|
||||
expected_initial[".ra"] = "reg2";
|
||||
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
|
||||
Module::RuleChangeMap expected_changes;
|
||||
expected_changes[entry_address + 1][".ra"] = "reg1";
|
||||
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
|
||||
}
|
||||
|
||||
@@ -1,881 +0,0 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// Implement the DwarfCUToModule class; see dwarf_cu_to_module.h.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "common/linux/dwarf_cu_to_module.h"
|
||||
#include "common/linux/dwarf_line_to_module.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::map;
|
||||
using std::vector;
|
||||
|
||||
// Data provided by a DWARF specification DIE.
|
||||
//
|
||||
// In DWARF, the DIE for a definition may contain a DW_AT_specification
|
||||
// attribute giving the offset of the corresponding declaration DIE, and
|
||||
// the definition DIE may omit information given in the declaration. For
|
||||
// example, it's common for a function's address range to appear only in
|
||||
// its definition DIE, but its name to appear only in its declaration
|
||||
// DIE.
|
||||
//
|
||||
// The dumper needs to be able to follow DW_AT_specification links to
|
||||
// bring all this information together in a FUNC record. Conveniently,
|
||||
// DIEs that are the target of such links have a DW_AT_declaration flag
|
||||
// set, so we can identify them when we first see them, and record their
|
||||
// contents for later reference.
|
||||
//
|
||||
// A Specification holds information gathered from a declaration DIE that
|
||||
// we may need if we find a DW_AT_specification link pointing to it.
|
||||
struct DwarfCUToModule::Specification {
|
||||
// The name of the enclosing scope, or the empty string if there is none.
|
||||
string enclosing_name;
|
||||
|
||||
// The name for the specification DIE itself, without any enclosing
|
||||
// name components.
|
||||
string unqualified_name;
|
||||
};
|
||||
|
||||
// An abstract origin -- base definition of an inline function.
|
||||
struct AbstractOrigin {
|
||||
AbstractOrigin() : name() {}
|
||||
AbstractOrigin(const string& name) : name(name) {}
|
||||
|
||||
string name;
|
||||
};
|
||||
|
||||
typedef map<uint64, AbstractOrigin> AbstractOriginByOffset;
|
||||
|
||||
// Data global to the DWARF-bearing file that is private to the
|
||||
// DWARF-to-Module process.
|
||||
struct DwarfCUToModule::FilePrivate {
|
||||
// A map from offsets of DIEs within the .debug_info section to
|
||||
// Specifications describing those DIEs. Specification references can
|
||||
// cross compilation unit boundaries.
|
||||
SpecificationByOffset specifications;
|
||||
|
||||
AbstractOriginByOffset origins;
|
||||
};
|
||||
|
||||
DwarfCUToModule::FileContext::FileContext(const string &filename_arg,
|
||||
Module *module_arg)
|
||||
: filename(filename_arg), module(module_arg) {
|
||||
file_private = new FilePrivate();
|
||||
}
|
||||
|
||||
DwarfCUToModule::FileContext::~FileContext() {
|
||||
delete file_private;
|
||||
}
|
||||
|
||||
// Information global to the particular compilation unit we're
|
||||
// parsing. This is for data shared across the CU's entire DIE tree,
|
||||
// and parameters from the code invoking the CU parser.
|
||||
struct DwarfCUToModule::CUContext {
|
||||
CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg)
|
||||
: file_context(file_context_arg),
|
||||
reporter(reporter_arg),
|
||||
language(Language::CPlusPlus) { }
|
||||
~CUContext() {
|
||||
for (vector<Module::Function *>::iterator it = functions.begin();
|
||||
it != functions.end(); it++)
|
||||
delete *it;
|
||||
};
|
||||
|
||||
// The DWARF-bearing file into which this CU was incorporated.
|
||||
FileContext *file_context;
|
||||
|
||||
// For printing error messages.
|
||||
WarningReporter *reporter;
|
||||
|
||||
// The source language of this compilation unit.
|
||||
const Language *language;
|
||||
|
||||
// The functions defined in this compilation unit. We accumulate
|
||||
// them here during parsing. Then, in DwarfCUToModule::Finish, we
|
||||
// assign them lines and add them to file_context->module.
|
||||
//
|
||||
// Destroying this destroys all the functions this vector points to.
|
||||
vector<Module::Function *> functions;
|
||||
};
|
||||
|
||||
// Information about the context of a particular DIE. This is for
|
||||
// information that changes as we descend the tree towards the leaves:
|
||||
// the containing classes/namespaces, etc.
|
||||
struct DwarfCUToModule::DIEContext {
|
||||
// The fully-qualified name of the context. For example, for a
|
||||
// tree like:
|
||||
//
|
||||
// DW_TAG_namespace Foo
|
||||
// DW_TAG_class Bar
|
||||
// DW_TAG_subprogram Baz
|
||||
//
|
||||
// in a C++ compilation unit, the DIEContext's name for the
|
||||
// DW_TAG_subprogram DIE would be "Foo::Bar". The DIEContext's
|
||||
// name for the DW_TAG_namespace DIE would be "".
|
||||
string name;
|
||||
};
|
||||
|
||||
// An abstract base class for all the dumper's DIE handlers.
|
||||
class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
|
||||
public:
|
||||
// Create a handler for the DIE at OFFSET whose compilation unit is
|
||||
// described by CU_CONTEXT, and whose immediate context is described
|
||||
// by PARENT_CONTEXT.
|
||||
GenericDIEHandler(CUContext *cu_context, DIEContext *parent_context,
|
||||
uint64 offset)
|
||||
: cu_context_(cu_context),
|
||||
parent_context_(parent_context),
|
||||
offset_(offset),
|
||||
declaration_(false),
|
||||
specification_(NULL) { }
|
||||
|
||||
// Derived classes' ProcessAttributeUnsigned can defer to this to
|
||||
// handle DW_AT_declaration, or simply not override it.
|
||||
void ProcessAttributeUnsigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data);
|
||||
|
||||
// Derived classes' ProcessAttributeReference can defer to this to
|
||||
// handle DW_AT_specification, or simply not override it.
|
||||
void ProcessAttributeReference(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data);
|
||||
|
||||
// Derived classes' ProcessAttributeReference can defer to this to
|
||||
// handle DW_AT_specification, or simply not override it.
|
||||
void ProcessAttributeString(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string &data);
|
||||
|
||||
protected:
|
||||
// Compute and return the fully-qualified name of the DIE. If this
|
||||
// DIE is a declaration DIE, to be cited by other DIEs'
|
||||
// DW_AT_specification attributes, record its enclosing name and
|
||||
// unqualified name in the specification table.
|
||||
//
|
||||
// Use this from EndAttributes member functions, not ProcessAttribute*
|
||||
// functions; only the former can be sure that all the DIE's attributes
|
||||
// have been seen.
|
||||
string ComputeQualifiedName();
|
||||
|
||||
CUContext *cu_context_;
|
||||
DIEContext *parent_context_;
|
||||
uint64 offset_;
|
||||
|
||||
// If this DIE has a DW_AT_declaration attribute, this is its value.
|
||||
// It is false on DIEs with no DW_AT_declaration attribute.
|
||||
bool declaration_;
|
||||
|
||||
// If this DIE has a DW_AT_specification attribute, this is the
|
||||
// Specification structure for the DIE the attribute refers to.
|
||||
// Otherwise, this is NULL.
|
||||
Specification *specification_;
|
||||
|
||||
// The value of the DW_AT_name attribute, or the empty string if the
|
||||
// DIE has no such attribute.
|
||||
string name_attribute_;
|
||||
};
|
||||
|
||||
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeUnsigned(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_declaration: declaration_ = (data != 0); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_specification: {
|
||||
// Find the Specification to which this attribute refers, and
|
||||
// set specification_ appropriately. We could do more processing
|
||||
// here, but it's better to leave the real work to our
|
||||
// EndAttribute member function, at which point we know we have
|
||||
// seen all the DIE's attributes.
|
||||
FileContext *file_context = cu_context_->file_context;
|
||||
SpecificationByOffset *specifications
|
||||
= &file_context->file_private->specifications;
|
||||
SpecificationByOffset::iterator spec = specifications->find(data);
|
||||
if (spec != specifications->end()) {
|
||||
specification_ = &spec->second;
|
||||
} else {
|
||||
// Technically, there's no reason a DW_AT_specification
|
||||
// couldn't be a forward reference, but supporting that would
|
||||
// be a lot of work (changing to a two-pass structure), and I
|
||||
// don't think any producers we care about ever emit such
|
||||
// things.
|
||||
cu_context_->reporter->UnknownSpecification(offset_, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string &data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_name: name_attribute_ = data; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
|
||||
// Find our unqualified name. If the DIE has its own DW_AT_name
|
||||
// attribute, then use that; otherwise, check our specification.
|
||||
const string *unqualified_name;
|
||||
if (name_attribute_.empty() && specification_)
|
||||
unqualified_name = &specification_->unqualified_name;
|
||||
else
|
||||
unqualified_name = &name_attribute_;
|
||||
|
||||
// Find the name of our enclosing context. If we have a
|
||||
// specification, it's the specification's enclosing context that
|
||||
// counts; otherwise, use this DIE's context.
|
||||
const string *enclosing_name;
|
||||
if (specification_)
|
||||
enclosing_name = &specification_->enclosing_name;
|
||||
else
|
||||
enclosing_name = &parent_context_->name;
|
||||
|
||||
// If this DIE was marked as a declaration, record its names in the
|
||||
// specification table.
|
||||
if (declaration_) {
|
||||
FileContext *file_context = cu_context_->file_context;
|
||||
Specification spec;
|
||||
spec.enclosing_name = *enclosing_name;
|
||||
spec.unqualified_name = *unqualified_name;
|
||||
file_context->file_private->specifications[offset_] = spec;
|
||||
}
|
||||
|
||||
// Combine the enclosing name and unqualified name to produce our
|
||||
// own fully-qualified name.
|
||||
return cu_context_->language->MakeQualifiedName(*enclosing_name,
|
||||
*unqualified_name);
|
||||
}
|
||||
|
||||
// A handler class for DW_TAG_subprogram DIEs.
|
||||
class DwarfCUToModule::FuncHandler: public GenericDIEHandler {
|
||||
public:
|
||||
FuncHandler(CUContext *cu_context, DIEContext *parent_context,
|
||||
uint64 offset)
|
||||
: GenericDIEHandler(cu_context, parent_context, offset),
|
||||
low_pc_(0), high_pc_(0), abstract_origin_(NULL), inline_(false) { }
|
||||
void ProcessAttributeUnsigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data);
|
||||
void ProcessAttributeSigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
int64 data);
|
||||
void ProcessAttributeReference(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data);
|
||||
|
||||
bool EndAttributes();
|
||||
void Finish();
|
||||
|
||||
private:
|
||||
// The fully-qualified name, as derived from name_attribute_,
|
||||
// specification_, parent_context_. Computed in EndAttributes.
|
||||
string name_;
|
||||
uint64 low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc
|
||||
const AbstractOrigin* abstract_origin_;
|
||||
bool inline_;
|
||||
};
|
||||
|
||||
void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) {
|
||||
switch (attr) {
|
||||
// If this attribute is present at all --- even if its value is
|
||||
// DW_INL_not_inlined --- then GCC may cite it as someone else's
|
||||
// DW_AT_abstract_origin attribute.
|
||||
case dwarf2reader::DW_AT_inline: inline_ = true; break;
|
||||
|
||||
case dwarf2reader::DW_AT_low_pc: low_pc_ = data; break;
|
||||
case dwarf2reader::DW_AT_high_pc: high_pc_ = data; break;
|
||||
default:
|
||||
GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::FuncHandler::ProcessAttributeSigned(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
int64 data) {
|
||||
switch (attr) {
|
||||
// If this attribute is present at all --- even if its value is
|
||||
// DW_INL_not_inlined --- then GCC may cite it as someone else's
|
||||
// DW_AT_abstract_origin attribute.
|
||||
case dwarf2reader::DW_AT_inline: inline_ = true; break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::FuncHandler::ProcessAttributeReference(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) {
|
||||
switch(attr) {
|
||||
case dwarf2reader::DW_AT_abstract_origin: {
|
||||
const AbstractOriginByOffset& origins =
|
||||
cu_context_->file_context->file_private->origins;
|
||||
AbstractOriginByOffset::const_iterator origin = origins.find(data);
|
||||
if (origin != origins.end()) {
|
||||
abstract_origin_ = &(origin->second);
|
||||
} else {
|
||||
cu_context_->reporter->UnknownAbstractOrigin(offset_, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
GenericDIEHandler::ProcessAttributeReference(attr, form, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool DwarfCUToModule::FuncHandler::EndAttributes() {
|
||||
// Compute our name, and record a specification, if appropriate.
|
||||
name_ = ComputeQualifiedName();
|
||||
if (name_.empty() && abstract_origin_) {
|
||||
name_ = abstract_origin_->name;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DwarfCUToModule::FuncHandler::Finish() {
|
||||
// Did we collect the information we need? Not all DWARF function
|
||||
// entries have low and high addresses (for example, inlined
|
||||
// functions that were never used), but all the ones we're
|
||||
// interested in cover a non-empty range of bytes.
|
||||
if (low_pc_ < high_pc_) {
|
||||
// Create a Module::Function based on the data we've gathered, and
|
||||
// add it to the functions_ list.
|
||||
Module::Function *func = new Module::Function;
|
||||
func->name = name_;
|
||||
func->address = low_pc_;
|
||||
func->size = high_pc_ - low_pc_;
|
||||
func->parameter_size = 0;
|
||||
cu_context_->functions.push_back(func);
|
||||
} else if (inline_) {
|
||||
AbstractOrigin origin(name_);
|
||||
cu_context_->file_context->file_private->origins[offset_] = origin;
|
||||
}
|
||||
}
|
||||
|
||||
// A handler for DIEs that contain functions and contribute a
|
||||
// component to their names: namespaces, classes, etc.
|
||||
class DwarfCUToModule::NamedScopeHandler: public GenericDIEHandler {
|
||||
public:
|
||||
NamedScopeHandler(CUContext *cu_context, DIEContext *parent_context,
|
||||
uint64 offset)
|
||||
: GenericDIEHandler(cu_context, parent_context, offset) { }
|
||||
bool EndAttributes();
|
||||
DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
|
||||
const AttributeList &attrs);
|
||||
|
||||
private:
|
||||
DIEContext child_context_; // A context for our children.
|
||||
};
|
||||
|
||||
bool DwarfCUToModule::NamedScopeHandler::EndAttributes() {
|
||||
child_context_.name = ComputeQualifiedName();
|
||||
return true;
|
||||
}
|
||||
|
||||
dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler(
|
||||
uint64 offset,
|
||||
enum DwarfTag tag,
|
||||
const AttributeList &attrs) {
|
||||
switch (tag) {
|
||||
case dwarf2reader::DW_TAG_subprogram:
|
||||
return new FuncHandler(cu_context_, &child_context_, offset);
|
||||
case dwarf2reader::DW_TAG_namespace:
|
||||
case dwarf2reader::DW_TAG_class_type:
|
||||
case dwarf2reader::DW_TAG_structure_type:
|
||||
case dwarf2reader::DW_TAG_union_type:
|
||||
return new NamedScopeHandler(cu_context_, &child_context_, offset);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
void DwarfCUToModule::WarningReporter::CUHeading() {
|
||||
if (printed_cu_header_)
|
||||
return;
|
||||
fprintf(stderr, "%s: in compilation unit '%s' (offset 0x%llx):\n",
|
||||
filename_.c_str(), cu_name_.c_str(), cu_offset_);
|
||||
printed_cu_header_ = true;
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UnknownSpecification(uint64 offset,
|
||||
uint64 target) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_specification"
|
||||
" attribute referring to the die at offset 0x%llx, which either"
|
||||
" was not marked as a declaration, or comes later in the file\n",
|
||||
filename_.c_str(), offset, target);
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UnknownAbstractOrigin(uint64 offset,
|
||||
uint64 target) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_abstract_origin"
|
||||
" attribute referring to the die at offset 0x%llx, which either"
|
||||
" was not marked as an inline, or comes later in the file\n",
|
||||
filename_.c_str(), offset, target);
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::MissingSection(const string &name) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: warning: couldn't find DWARF '%s' section\n",
|
||||
filename_.c_str(), name.c_str());
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::BadLineInfoOffset(uint64 offset) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: warning: line number data offset beyond end"
|
||||
" of '.debug_line' section\n",
|
||||
filename_.c_str());
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UncoveredHeading() {
|
||||
if (printed_unpaired_header_)
|
||||
return;
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: warning: skipping unpaired lines/functions:\n",
|
||||
filename_.c_str());
|
||||
printed_unpaired_header_ = true;
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UncoveredFunction(
|
||||
const Module::Function &function) {
|
||||
if (!uncovered_warnings_enabled_)
|
||||
return;
|
||||
UncoveredHeading();
|
||||
fprintf(stderr, " function%s: %s\n",
|
||||
function.size == 0 ? " (zero-length)" : "",
|
||||
function.name.c_str());
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UncoveredLine(const Module::Line &line) {
|
||||
if (!uncovered_warnings_enabled_)
|
||||
return;
|
||||
UncoveredHeading();
|
||||
fprintf(stderr, " line%s: %s:%d at 0x%llx\n",
|
||||
(line.size == 0 ? " (zero-length)" : ""),
|
||||
line.file->name.c_str(), line.number, line.address);
|
||||
}
|
||||
|
||||
DwarfCUToModule::DwarfCUToModule(FileContext *file_context,
|
||||
LineToModuleFunctor *line_reader,
|
||||
WarningReporter *reporter)
|
||||
: line_reader_(line_reader), has_source_line_info_(false) {
|
||||
cu_context_ = new CUContext(file_context, reporter);
|
||||
child_context_ = new DIEContext();
|
||||
}
|
||||
|
||||
DwarfCUToModule::~DwarfCUToModule() {
|
||||
delete cu_context_;
|
||||
delete child_context_;
|
||||
}
|
||||
|
||||
void DwarfCUToModule::ProcessAttributeSigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
int64 data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_language: // source language of this CU
|
||||
SetLanguage(static_cast<DwarfLanguage>(data));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_stmt_list: // Line number information.
|
||||
has_source_line_info_ = true;
|
||||
source_line_offset_ = data;
|
||||
break;
|
||||
case dwarf2reader::DW_AT_language: // source language of this CU
|
||||
SetLanguage(static_cast<DwarfLanguage>(data));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string &data) {
|
||||
if (attr == dwarf2reader::DW_AT_name)
|
||||
cu_context_->reporter->SetCUName(data);
|
||||
}
|
||||
|
||||
bool DwarfCUToModule::EndAttributes() {
|
||||
return true;
|
||||
}
|
||||
|
||||
dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler(
|
||||
uint64 offset,
|
||||
enum DwarfTag tag,
|
||||
const AttributeList &attrs) {
|
||||
switch (tag) {
|
||||
case dwarf2reader::DW_TAG_subprogram:
|
||||
return new FuncHandler(cu_context_, child_context_, offset);
|
||||
case dwarf2reader::DW_TAG_namespace:
|
||||
case dwarf2reader::DW_TAG_class_type:
|
||||
case dwarf2reader::DW_TAG_structure_type:
|
||||
case dwarf2reader::DW_TAG_union_type:
|
||||
return new NamedScopeHandler(cu_context_, child_context_, offset);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
|
||||
switch (language) {
|
||||
case dwarf2reader::DW_LANG_Java:
|
||||
cu_context_->language = Language::Java;
|
||||
break;
|
||||
|
||||
// DWARF has no generic language code for assembly language; this is
|
||||
// what the GNU toolchain uses.
|
||||
case dwarf2reader::DW_LANG_Mips_Assembler:
|
||||
cu_context_->language = Language::Assembler;
|
||||
break;
|
||||
|
||||
// C++ covers so many cases that it probably has some way to cope
|
||||
// with whatever the other languages throw at us. So make it the
|
||||
// default.
|
||||
//
|
||||
// Objective C and Objective C++ seem to create entries for
|
||||
// methods whose DW_AT_name values are already fully-qualified:
|
||||
// "-[Classname method:]". These appear at the top level.
|
||||
//
|
||||
// DWARF data for C should never include namespaces or functions
|
||||
// nested in struct types, but if it ever does, then C++'s
|
||||
// notation is probably not a bad choice for that.
|
||||
default:
|
||||
case dwarf2reader::DW_LANG_ObjC:
|
||||
case dwarf2reader::DW_LANG_ObjC_plus_plus:
|
||||
case dwarf2reader::DW_LANG_C:
|
||||
case dwarf2reader::DW_LANG_C89:
|
||||
case dwarf2reader::DW_LANG_C99:
|
||||
case dwarf2reader::DW_LANG_C_plus_plus:
|
||||
cu_context_->language = Language::CPlusPlus;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::ReadSourceLines(uint64 offset) {
|
||||
const dwarf2reader::SectionMap §ion_map
|
||||
= cu_context_->file_context->section_map;
|
||||
dwarf2reader::SectionMap::const_iterator map_entry
|
||||
= section_map.find(".debug_line");
|
||||
if (map_entry == section_map.end()) {
|
||||
cu_context_->reporter->MissingSection(".debug_line");
|
||||
return;
|
||||
}
|
||||
const char *section_start = map_entry->second.first;
|
||||
uint64 section_length = map_entry->second.second;
|
||||
if (offset >= section_length) {
|
||||
cu_context_->reporter->BadLineInfoOffset(offset);
|
||||
return;
|
||||
}
|
||||
(*line_reader_)(section_start + offset, section_length - offset,
|
||||
cu_context_->file_context->module, &lines_);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Return true if ADDRESS falls within the range of ITEM.
|
||||
template <class T>
|
||||
inline bool within(const T &item, Module::Address address) {
|
||||
// Because Module::Address is unsigned, and unsigned arithmetic
|
||||
// wraps around, this will be false if ADDRESS falls before the
|
||||
// start of ITEM, or if it falls after ITEM's end.
|
||||
return address - item.address < item.size;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::AssignLinesToFunctions() {
|
||||
vector<Module::Function *> *functions = &cu_context_->functions;
|
||||
WarningReporter *reporter = cu_context_->reporter;
|
||||
|
||||
// This would be simpler if we assumed that source line entries
|
||||
// don't cross function boundaries. However, there's no real reason
|
||||
// to assume that (say) a series of function definitions on the same
|
||||
// line wouldn't get coalesced into one line number entry. The
|
||||
// DWARF spec certainly makes no such promises.
|
||||
//
|
||||
// So treat the functions and lines as peers, and take the trouble
|
||||
// to compute their ranges' intersections precisely. In any case,
|
||||
// the hair here is a constant factor for performance; the
|
||||
// complexity from here on out is linear.
|
||||
|
||||
// Put both our functions and lines in order by address.
|
||||
sort(functions->begin(), functions->end(),
|
||||
Module::Function::CompareByAddress);
|
||||
sort(lines_.begin(), lines_.end(), Module::Line::CompareByAddress);
|
||||
|
||||
// The last line that we used any piece of. We use this only for
|
||||
// generating warnings.
|
||||
const Module::Line *last_line_used = NULL;
|
||||
|
||||
// The last function and line we warned about --- so we can avoid
|
||||
// doing so more than once.
|
||||
const Module::Function *last_function_cited = NULL;
|
||||
const Module::Line *last_line_cited = NULL;
|
||||
|
||||
// Make a single pass through both vectors from lower to higher
|
||||
// addresses, populating each Function's lines vector with lines
|
||||
// from our lines_ vector that fall within the function's address
|
||||
// range.
|
||||
vector<Module::Function *>::iterator func_it = functions->begin();
|
||||
vector<Module::Line>::const_iterator line_it = lines_.begin();
|
||||
|
||||
Module::Address current;
|
||||
|
||||
// Pointers to the referents of func_it and line_it, or NULL if the
|
||||
// iterator is at the end of the sequence.
|
||||
Module::Function *func;
|
||||
const Module::Line *line;
|
||||
|
||||
// Start current at the beginning of the first line or function,
|
||||
// whichever is earlier.
|
||||
if (func_it != functions->end() && line_it != lines_.end()) {
|
||||
func = *func_it;
|
||||
line = &*line_it;
|
||||
current = std::min(func->address, line->address);
|
||||
} else if (line_it != lines_.end()) {
|
||||
func = NULL;
|
||||
line = &*line_it;
|
||||
current = line->address;
|
||||
} else if (func_it != functions->end()) {
|
||||
func = *func_it;
|
||||
line = NULL;
|
||||
current = (*func_it)->address;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
while (func || line) {
|
||||
// This loop has two invariants that hold at the top.
|
||||
//
|
||||
// First, at least one of the iterators is not at the end of its
|
||||
// sequence, and those that are not refer to the earliest
|
||||
// function or line that contains or starts after CURRENT.
|
||||
//
|
||||
// Note that every byte is in one of four states: it is covered
|
||||
// or not covered by a function, and, independently, it is
|
||||
// covered or not covered by a line.
|
||||
//
|
||||
// The second invariant is that CURRENT refers to a byte whose
|
||||
// state is different from its predecessor, or it refers to the
|
||||
// first byte in the address space. In other words, CURRENT is
|
||||
// always the address of a transition.
|
||||
//
|
||||
// Note that, although each iteration advances CURRENT from one
|
||||
// transition address to the next in each iteration, it might
|
||||
// not advance the iterators. Suppose we have a function that
|
||||
// starts with a line, has a gap, and then a second line, and
|
||||
// suppose that we enter an iteration with CURRENT at the end of
|
||||
// the first line. The next transition address is the start of
|
||||
// the second line, after the gap, so the iteration should
|
||||
// advance CURRENT to that point. At the head of that iteration,
|
||||
// the invariants require that the line iterator be pointing at
|
||||
// the second line. But this is also true at the head of the
|
||||
// next. And clearly, the iteration must not change the function
|
||||
// iterator. So neither iterator moves.
|
||||
|
||||
// Assert the first invariant (see above).
|
||||
assert(!func || current < func->address || within(*func, current));
|
||||
assert(!line || current < line->address || within(*line, current));
|
||||
|
||||
// The next transition after CURRENT.
|
||||
Module::Address next_transition;
|
||||
|
||||
// Figure out which state we're in, add lines or warn, and compute
|
||||
// the next transition address.
|
||||
if (func && current >= func->address) {
|
||||
if (line && current >= line->address) {
|
||||
// Covered by both a line and a function.
|
||||
Module::Address func_left = func->size - (current - func->address);
|
||||
Module::Address line_left = line->size - (current - line->address);
|
||||
// This may overflow, but things work out.
|
||||
next_transition = current + std::min(func_left, line_left);
|
||||
Module::Line l = *line;
|
||||
l.address = current;
|
||||
l.size = next_transition - current;
|
||||
func->lines.push_back(l);
|
||||
last_line_used = line;
|
||||
} else {
|
||||
// Covered by a function, but no line.
|
||||
if (func != last_function_cited) {
|
||||
reporter->UncoveredFunction(*func);
|
||||
last_function_cited = func;
|
||||
}
|
||||
if (line && within(*func, line->address))
|
||||
next_transition = line->address;
|
||||
else
|
||||
// If this overflows, we'll catch it below.
|
||||
next_transition = func->address + func->size;
|
||||
}
|
||||
} else {
|
||||
if (line && current >= line->address) {
|
||||
// Covered by a line, but no function.
|
||||
//
|
||||
// If GCC emits padding after one function to align the start
|
||||
// of the next, then it will attribute the padding
|
||||
// instructions to the last source line of function (to reduce
|
||||
// the size of the line number info), but omit it from the
|
||||
// DW_AT_{low,high}_pc range given in .debug_info (since it
|
||||
// costs nothing to be precise there). If we did use at least
|
||||
// some of the line we're about to skip, and it ends at the
|
||||
// start of the next function, then assume this is what
|
||||
// happened, and don't warn.
|
||||
if (line != last_line_cited
|
||||
&& !(func
|
||||
&& line == last_line_used
|
||||
&& func->address - line->address == line->size)) {
|
||||
reporter->UncoveredLine(*line);
|
||||
last_line_cited = line;
|
||||
}
|
||||
if (func && within(*line, func->address))
|
||||
next_transition = func->address;
|
||||
else
|
||||
// If this overflows, we'll catch it below.
|
||||
next_transition = line->address + line->size;
|
||||
} else {
|
||||
// Covered by neither a function nor a line. By the invariant,
|
||||
// both func and line begin after CURRENT. The next transition
|
||||
// is the start of the next function or next line, whichever
|
||||
// is earliest.
|
||||
assert (func || line);
|
||||
if (func && line)
|
||||
next_transition = std::min(func->address, line->address);
|
||||
else if (func)
|
||||
next_transition = func->address;
|
||||
else
|
||||
next_transition = line->address;
|
||||
}
|
||||
}
|
||||
|
||||
// If a function or line abuts the end of the address space, then
|
||||
// next_transition may end up being zero, in which case we've completed
|
||||
// our pass. Handle that here, instead of trying to deal with it in
|
||||
// each place we compute next_transition.
|
||||
if (!next_transition)
|
||||
break;
|
||||
|
||||
// Advance iterators as needed. If lines overlap or functions overlap,
|
||||
// then we could go around more than once. We don't worry too much
|
||||
// about what result we produce in that case, just as long as we don't
|
||||
// hang or crash.
|
||||
while (func_it != functions->end()
|
||||
&& current >= (*func_it)->address
|
||||
&& !within(**func_it, next_transition))
|
||||
func_it++;
|
||||
func = (func_it != functions->end()) ? *func_it : NULL;
|
||||
while (line_it != lines_.end()
|
||||
&& current >= line_it->address
|
||||
&& !within(*line_it, next_transition))
|
||||
line_it++;
|
||||
line = (line_it != lines_.end()) ? &*line_it : NULL;
|
||||
|
||||
// We must make progress.
|
||||
assert(next_transition > current);
|
||||
current = next_transition;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::Finish() {
|
||||
// Assembly language files have no function data, and that gives us
|
||||
// no place to store our line numbers (even though the GNU toolchain
|
||||
// will happily produce source line info for assembly language
|
||||
// files). To avoid spurious warnings about lines we can't assign
|
||||
// to functions, skip CUs in languages that lack functions.
|
||||
if (!cu_context_->language->HasFunctions())
|
||||
return;
|
||||
|
||||
// Read source line info, if we have any.
|
||||
if (has_source_line_info_)
|
||||
ReadSourceLines(source_line_offset_);
|
||||
|
||||
vector<Module::Function *> *functions = &cu_context_->functions;
|
||||
|
||||
// Dole out lines to the appropriate functions.
|
||||
AssignLinesToFunctions();
|
||||
|
||||
// Add our functions, which now have source lines assigned to them,
|
||||
// to module_.
|
||||
cu_context_->file_context->module->AddFunctions(functions->begin(),
|
||||
functions->end());
|
||||
|
||||
// Ownership of the function objects has shifted from cu_context to
|
||||
// the Module.
|
||||
functions->clear();
|
||||
}
|
||||
|
||||
bool DwarfCUToModule::StartCompilationUnit(uint64 offset,
|
||||
uint8 address_size,
|
||||
uint8 offset_size,
|
||||
uint64 cu_length,
|
||||
uint8 dwarf_version) {
|
||||
return dwarf_version >= 2;
|
||||
}
|
||||
|
||||
bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag,
|
||||
const AttributeList& attrs) {
|
||||
// We don't deal with partial compilation units (the only other tag
|
||||
// likely to be used for root DIE).
|
||||
return tag == dwarf2reader::DW_TAG_compile_unit;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
@@ -1,272 +0,0 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// Add DWARF debugging information to a Breakpad symbol file. This
|
||||
// file defines the DwarfCUToModule class, which accepts parsed DWARF
|
||||
// data and populates a google_breakpad::Module with the results; the
|
||||
// Module can then write its contents as a Breakpad symbol file.
|
||||
|
||||
#ifndef COMMON_LINUX_DWARF_CU_TO_MODULE_H__
|
||||
#define COMMON_LINUX_DWARF_CU_TO_MODULE_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
#include "common/linux/language.h"
|
||||
#include "common/linux/module.h"
|
||||
#include "common/dwarf/bytereader.h"
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
#include "common/dwarf/dwarf2reader.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using dwarf2reader::AttributeList;
|
||||
using dwarf2reader::DwarfAttribute;
|
||||
using dwarf2reader::DwarfForm;
|
||||
using dwarf2reader::DwarfLanguage;
|
||||
using dwarf2reader::DwarfTag;
|
||||
|
||||
// Populate a google_breakpad::Module with DWARF debugging information.
|
||||
//
|
||||
// An instance of this class can be provided as a handler to a
|
||||
// dwarf2reader::CompilationUnit DWARF parser. The handler uses the
|
||||
// results of parsing to populate a google_breakpad::Module with
|
||||
// source file, function, and source line information.
|
||||
class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
||||
struct FilePrivate;
|
||||
public:
|
||||
|
||||
// Information global to the DWARF-bearing file we are processing,
|
||||
// for use by DwarfCUToModule. Each DwarfCUToModule instance deals
|
||||
// with a single compilation unit within the file, but information
|
||||
// global to the whole file is held here. The client is responsible
|
||||
// for filling it in appropriately (except for the 'file_private'
|
||||
// field, which the constructor and destructor take care of), and
|
||||
// then providing it to the DwarfCUToModule instance for each
|
||||
// compilation unit we process in that file.
|
||||
struct FileContext {
|
||||
FileContext(const string &filename_arg, Module *module_arg);
|
||||
~FileContext();
|
||||
|
||||
// The name of this file, for use in error messages.
|
||||
string filename;
|
||||
|
||||
// A map of this file's sections, used for finding other DWARF
|
||||
// sections that the .debug_info section may refer to.
|
||||
dwarf2reader::SectionMap section_map;
|
||||
|
||||
// The Module to which we're contributing definitions.
|
||||
Module *module;
|
||||
|
||||
// Inter-compilation unit data used internally by the handlers.
|
||||
FilePrivate *file_private;
|
||||
};
|
||||
|
||||
// An abstract base class for functors that handle DWARF line data
|
||||
// for DwarfCUToModule. DwarfCUToModule could certainly just use
|
||||
// dwarf2reader::LineInfo itself directly, but decoupling things
|
||||
// this way makes unit testing a little easier.
|
||||
class LineToModuleFunctor {
|
||||
public:
|
||||
LineToModuleFunctor() { }
|
||||
virtual ~LineToModuleFunctor() { }
|
||||
|
||||
// Populate MODULE and LINES with source file names and code/line
|
||||
// mappings, given a pointer to some DWARF line number data
|
||||
// PROGRAM, and an overestimate of its size. Add no zero-length
|
||||
// lines to LINES.
|
||||
virtual void operator()(const char *program, uint64 length,
|
||||
Module *module, vector<Module::Line> *lines) = 0;
|
||||
};
|
||||
|
||||
// The interface DwarfCUToModule uses to report warnings. The member
|
||||
// function definitions for this class write messages to stderr, but
|
||||
// you can override them if you'd like to detect or report these
|
||||
// conditions yourself.
|
||||
class WarningReporter {
|
||||
public:
|
||||
// Warn about problems in the DWARF file FILENAME, in the
|
||||
// compilation unit at OFFSET.
|
||||
WarningReporter(const string &filename, uint64 cu_offset)
|
||||
: filename_(filename), cu_offset_(cu_offset), printed_cu_header_(false),
|
||||
printed_unpaired_header_(false),
|
||||
uncovered_warnings_enabled_(false) { }
|
||||
virtual ~WarningReporter() { }
|
||||
|
||||
// Set the name of the compilation unit we're processing to NAME.
|
||||
virtual void SetCUName(const string &name) { cu_name_ = name; }
|
||||
|
||||
// Accessor and setter for uncovered_warnings_enabled_.
|
||||
// UncoveredFunction and UncoveredLine only report a problem if that is
|
||||
// true. By default, these warnings are disabled, because those
|
||||
// conditions occur occasionally in healthy code.
|
||||
virtual bool uncovered_warnings_enabled() const {
|
||||
return uncovered_warnings_enabled_;
|
||||
}
|
||||
virtual void set_uncovered_warnings_enabled(bool value) {
|
||||
uncovered_warnings_enabled_ = value;
|
||||
}
|
||||
|
||||
// A DW_AT_specification in the DIE at OFFSET refers to a DIE we
|
||||
// haven't processed yet, or that wasn't marked as a declaration,
|
||||
// at TARGET.
|
||||
virtual void UnknownSpecification(uint64 offset, uint64 target);
|
||||
|
||||
// A DW_AT_abstract_origin in the DIE at OFFSET refers to a DIE we
|
||||
// haven't processed yet, or that wasn't marked as inline, at TARGET.
|
||||
virtual void UnknownAbstractOrigin(uint64 offset, uint64 target);
|
||||
|
||||
// We were unable to find the DWARF section named SECTION_NAME.
|
||||
virtual void MissingSection(const string §ion_name);
|
||||
|
||||
// The CU's DW_AT_stmt_list offset OFFSET is bogus.
|
||||
virtual void BadLineInfoOffset(uint64 offset);
|
||||
|
||||
// FUNCTION includes code covered by no line number data.
|
||||
virtual void UncoveredFunction(const Module::Function &function);
|
||||
|
||||
// Line number NUMBER in LINE_FILE, of length LENGTH, includes code
|
||||
// covered by no function.
|
||||
virtual void UncoveredLine(const Module::Line &line);
|
||||
|
||||
protected:
|
||||
string filename_;
|
||||
uint64 cu_offset_;
|
||||
string cu_name_;
|
||||
bool printed_cu_header_;
|
||||
bool printed_unpaired_header_;
|
||||
bool uncovered_warnings_enabled_;
|
||||
|
||||
private:
|
||||
// Print a per-CU heading, once.
|
||||
void CUHeading();
|
||||
// Print an unpaired function/line heading, once.
|
||||
void UncoveredHeading();
|
||||
};
|
||||
|
||||
// Create a DWARF debugging info handler for a compilation unit
|
||||
// within FILE_CONTEXT. This uses information received from the
|
||||
// dwarf2reader::CompilationUnit DWARF parser to populate
|
||||
// FILE_CONTEXT->module. Use LINE_READER to handle the compilation
|
||||
// unit's line number data. Use REPORTER to report problems with the
|
||||
// data we find.
|
||||
DwarfCUToModule(FileContext *file_context,
|
||||
LineToModuleFunctor *line_reader,
|
||||
WarningReporter *reporter);
|
||||
~DwarfCUToModule();
|
||||
|
||||
void ProcessAttributeSigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
int64 data);
|
||||
void ProcessAttributeUnsigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data);
|
||||
void ProcessAttributeString(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string &data);
|
||||
bool EndAttributes();
|
||||
DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
|
||||
const AttributeList &attrs);
|
||||
|
||||
// Assign all our source Lines to the Functions that cover their
|
||||
// addresses, and then add them to module_.
|
||||
void Finish();
|
||||
|
||||
bool StartCompilationUnit(uint64 offset, uint8 address_size,
|
||||
uint8 offset_size, uint64 cu_length,
|
||||
uint8 dwarf_version);
|
||||
bool StartRootDIE(uint64 offset, enum DwarfTag tag,
|
||||
const AttributeList& attrs);
|
||||
|
||||
private:
|
||||
|
||||
// Used internally by the handler. Full definitions are in
|
||||
// dwarf_cu_to_module.cc.
|
||||
struct FilePrivate;
|
||||
struct Specification;
|
||||
struct CUContext;
|
||||
struct DIEContext;
|
||||
class GenericDIEHandler;
|
||||
class FuncHandler;
|
||||
class NamedScopeHandler;
|
||||
|
||||
// A map from section offsets to specifications.
|
||||
typedef map<uint64, Specification> SpecificationByOffset;
|
||||
|
||||
// Set this compilation unit's source language to LANGUAGE.
|
||||
void SetLanguage(DwarfLanguage language);
|
||||
|
||||
// Read source line information at OFFSET in the .debug_line
|
||||
// section. Record source files in module_, but record source lines
|
||||
// in lines_; we apportion them to functions in
|
||||
// AssignLinesToFunctions.
|
||||
void ReadSourceLines(uint64 offset);
|
||||
|
||||
// Assign the lines in lines_ to the individual line lists of the
|
||||
// functions in functions_. (DWARF line information maps an entire
|
||||
// compilation unit at a time, and gives no indication of which
|
||||
// lines belong to which functions, beyond their addresses.)
|
||||
void AssignLinesToFunctions();
|
||||
|
||||
// The only reason cu_context_ and child_context_ are pointers is
|
||||
// that we want to keep their definitions private to
|
||||
// dwarf_cu_to_module.cc, instead of listing them all here. They are
|
||||
// owned by this DwarfCUToModule: the constructor sets them, and the
|
||||
// destructor deletes them.
|
||||
|
||||
// The functor to use to handle line number data.
|
||||
LineToModuleFunctor *line_reader_;
|
||||
|
||||
// This compilation unit's context.
|
||||
CUContext *cu_context_;
|
||||
|
||||
// A context for our children.
|
||||
DIEContext *child_context_;
|
||||
|
||||
// True if this compilation unit has source line information.
|
||||
bool has_source_line_info_;
|
||||
|
||||
// The offset of this compilation unit's line number information in
|
||||
// the .debug_line section.
|
||||
uint64 source_line_offset_;
|
||||
|
||||
// The line numbers we have seen thus far. We accumulate these here
|
||||
// during parsing. Then, in Finish, we call AssignLinesToFunctions
|
||||
// to dole them out to the appropriate functions.
|
||||
vector<Module::Line> lines_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_DWARF_CU_TO_MODULE_H__
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,132 +0,0 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// dwarf_line_to_module.cc: Implementation of DwarfLineToModule class.
|
||||
// See dwarf_line_to_module.h for details.
|
||||
|
||||
#include "common/linux/dwarf_line_to_module.h"
|
||||
|
||||
// Trying to support Windows paths in a reasonable way adds a lot of
|
||||
// variations to test; it would be better to just put off dealing with
|
||||
// it until we actually have to deal with DWARF on Windows.
|
||||
|
||||
// Return true if PATH is an absolute path, false if it is relative.
|
||||
static bool PathIsAbsolute(const string &path) {
|
||||
return (path.size() >= 1 && path[0] == '/');
|
||||
}
|
||||
|
||||
// If PATH is an absolute path, return PATH. If PATH is a relative path,
|
||||
// treat it as relative to BASE and return the combined path.
|
||||
static string ExpandPath(const string &path, const string &base) {
|
||||
if (PathIsAbsolute(path))
|
||||
return path;
|
||||
return base + "/" + path;
|
||||
}
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) {
|
||||
// Directory number zero is reserved to mean the compilation
|
||||
// directory. Silently ignore attempts to redefine it.
|
||||
if (dir_num != 0)
|
||||
directories_[dir_num] = name;
|
||||
}
|
||||
|
||||
void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
|
||||
uint32 dir_num, uint64 mod_time,
|
||||
uint64 length) {
|
||||
if (file_num == -1)
|
||||
file_num = ++highest_file_number_;
|
||||
else if (file_num > highest_file_number_)
|
||||
highest_file_number_ = file_num;
|
||||
|
||||
std::string full_name;
|
||||
if (dir_num != 0) {
|
||||
DirectoryTable::const_iterator directory_it = directories_.find(dir_num);
|
||||
if (directory_it != directories_.end()) {
|
||||
full_name = ExpandPath(name, directory_it->second);
|
||||
} else {
|
||||
if (!warned_bad_directory_number_) {
|
||||
fprintf(stderr, "warning: DWARF line number data refers to undefined"
|
||||
" directory numbers\n");
|
||||
warned_bad_directory_number_ = true;
|
||||
}
|
||||
full_name = name; // just treat name as relative
|
||||
}
|
||||
} else {
|
||||
// Directory number zero is the compilation directory; we just report
|
||||
// relative paths in that case.
|
||||
full_name = name;
|
||||
}
|
||||
|
||||
// Find a Module::File object of the given name, and add it to the
|
||||
// file table.
|
||||
files_[file_num] = module_->FindFile(full_name);
|
||||
}
|
||||
|
||||
void DwarfLineToModule::AddLine(uint64 address, uint64 length,
|
||||
uint32 file_num, uint32 line_num,
|
||||
uint32 column_num) {
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
// Clip lines not to extend beyond the end of the address space.
|
||||
if (address + length < address)
|
||||
length = -address;
|
||||
|
||||
// Should we omit this line? (See the comments for omitted_line_end_.)
|
||||
if (address == 0 || address == omitted_line_end_) {
|
||||
omitted_line_end_ = address + length;
|
||||
return;
|
||||
} else {
|
||||
omitted_line_end_ = 0;
|
||||
}
|
||||
|
||||
// Find the source file being referred to.
|
||||
Module::File *file = files_[file_num];
|
||||
if (!file) {
|
||||
if (!warned_bad_file_number_) {
|
||||
fprintf(stderr, "warning: DWARF line number data refers to "
|
||||
"undefined file numbers\n");
|
||||
warned_bad_file_number_ = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
Module::Line line;
|
||||
line.address = address;
|
||||
// We set the size when we get the next line or the EndSequence call.
|
||||
line.size = length;
|
||||
line.file = file;
|
||||
line.number = line_num;
|
||||
lines_->push_back(line);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
@@ -1,179 +0,0 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// The DwarfLineToModule class accepts line number information from a
|
||||
// DWARF parser and adds it to a google_breakpad::Module. The Module
|
||||
// can write that data out as a Breakpad symbol file.
|
||||
|
||||
#ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H
|
||||
#define COMMON_LINUX_DWARF_LINE_TO_MODULE_H
|
||||
|
||||
#include "common/linux/module.h"
|
||||
#include "common/dwarf/dwarf2reader.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// A class for producing a vector of google_breakpad::Module::Line
|
||||
// instances from parsed DWARF line number data.
|
||||
//
|
||||
// An instance of this class can be provided as a handler to a
|
||||
// dwarf2reader::LineInfo DWARF line number information parser. The
|
||||
// handler accepts source location information from the parser and
|
||||
// uses it to produce a vector of google_breakpad::Module::Line
|
||||
// objects, referring to google_breakpad::Module::File objects added
|
||||
// to a particular google_breakpad::Module.
|
||||
//
|
||||
// GNU toolchain omitted sections support:
|
||||
// ======================================
|
||||
//
|
||||
// Given the right options, the GNU toolchain will omit unreferenced
|
||||
// functions from the final executable. Unfortunately, when it does so, it
|
||||
// does not remove the associated portions of the DWARF line number
|
||||
// program; instead, it gives the DW_LNE_set_address instructions referring
|
||||
// to the now-deleted code addresses of zero. Given this input, the DWARF
|
||||
// line parser will call AddLine with a series of lines starting at address
|
||||
// zero. For example, here is the output from 'readelf -wl' for a program
|
||||
// with four functions, the first three of which have been omitted:
|
||||
//
|
||||
// Line Number Statements:
|
||||
// Extended opcode 2: set Address to 0x0
|
||||
// Advance Line by 14 to 15
|
||||
// Copy
|
||||
// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16
|
||||
// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18
|
||||
// Advance PC by 2 to 0xd
|
||||
// Extended opcode 1: End of Sequence
|
||||
//
|
||||
// Extended opcode 2: set Address to 0x0
|
||||
// Advance Line by 14 to 15
|
||||
// Copy
|
||||
// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16
|
||||
// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18
|
||||
// Advance PC by 2 to 0xd
|
||||
// Extended opcode 1: End of Sequence
|
||||
//
|
||||
// Extended opcode 2: set Address to 0x0
|
||||
// Advance Line by 19 to 20
|
||||
// Copy
|
||||
// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 21
|
||||
// Special opcode 76: advance Address by 5 to 0x8 and Line by 1 to 22
|
||||
// Advance PC by 2 to 0xa
|
||||
// Extended opcode 1: End of Sequence
|
||||
//
|
||||
// Extended opcode 2: set Address to 0x80483a4
|
||||
// Advance Line by 23 to 24
|
||||
// Copy
|
||||
// Special opcode 202: advance Address by 14 to 0x80483b2 and Line by 1 to 25
|
||||
// Special opcode 76: advance Address by 5 to 0x80483b7 and Line by 1 to 26
|
||||
// Advance PC by 6 to 0x80483bd
|
||||
// Extended opcode 1: End of Sequence
|
||||
//
|
||||
// Instead of collecting runs of lines describing code that is not there,
|
||||
// we try to recognize and drop them. Since the linker doesn't explicitly
|
||||
// distinguish references to dropped sections from genuine references to
|
||||
// code at address zero, we must use a heuristic. We have chosen:
|
||||
//
|
||||
// - If a line starts at address zero, omit it. (On the platforms
|
||||
// breakpad targets, it is extremely unlikely that there will be code
|
||||
// at address zero.)
|
||||
//
|
||||
// - If a line starts immediately after an omitted line, omit it too.
|
||||
class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
|
||||
public:
|
||||
// As the DWARF line info parser passes us line records, add source
|
||||
// files to MODULE, and add all lines to the end of LINES. LINES
|
||||
// need not be empty. If the parser hands us a zero-length line, we
|
||||
// omit it. If the parser hands us a line that extends beyond the
|
||||
// end of the address space, we clip it. It's up to our client to
|
||||
// sort out which lines belong to which functions; we don't add them
|
||||
// to any particular function in MODULE ourselves.
|
||||
DwarfLineToModule(Module *module, vector<Module::Line> *lines)
|
||||
: module_(module),
|
||||
lines_(lines),
|
||||
highest_file_number_(-1),
|
||||
omitted_line_end_(0),
|
||||
warned_bad_file_number_(false),
|
||||
warned_bad_directory_number_(false) { }
|
||||
|
||||
~DwarfLineToModule() { }
|
||||
|
||||
void DefineDir(const std::string &name, uint32 dir_num);
|
||||
void DefineFile(const std::string &name, int32 file_num,
|
||||
uint32 dir_num, uint64 mod_time,
|
||||
uint64 length);
|
||||
void AddLine(uint64 address, uint64 length,
|
||||
uint32 file_num, uint32 line_num, uint32 column_num);
|
||||
|
||||
private:
|
||||
|
||||
typedef std::map<uint32, std::string> DirectoryTable;
|
||||
typedef std::map<uint32, Module::File *> FileTable;
|
||||
|
||||
// The module we're contributing debugging info to. Owned by our
|
||||
// client.
|
||||
Module *module_;
|
||||
|
||||
// The vector of lines we're accumulating. Owned by our client.
|
||||
//
|
||||
// In a Module, as in a breakpad symbol file, lines belong to
|
||||
// specific functions, but DWARF simply assigns lines to addresses;
|
||||
// one must infer the line/function relationship using the
|
||||
// functions' beginning and ending addresses. So we can't add these
|
||||
// to the appropriate function from module_ until we've read the
|
||||
// function info as well. Instead, we accumulate lines here, and let
|
||||
// whoever constructed this sort it all out.
|
||||
vector<Module::Line> *lines_;
|
||||
|
||||
// A table mapping directory numbers to paths.
|
||||
DirectoryTable directories_;
|
||||
|
||||
// A table mapping file numbers to Module::File pointers.
|
||||
FileTable files_;
|
||||
|
||||
// The highest file number we've seen so far, or -1 if we've seen
|
||||
// none. Used for dynamically defined file numbers.
|
||||
int32 highest_file_number_;
|
||||
|
||||
// This is the ending address of the last line we omitted, or zero if we
|
||||
// didn't omit the previous line. It is zero before we have received any
|
||||
// AddLine calls.
|
||||
uint64 omitted_line_end_;
|
||||
|
||||
// True if we've warned about:
|
||||
bool warned_bad_file_number_; // bad file numbers
|
||||
bool warned_bad_directory_number_; // bad directory numbers
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_DWARF_LINE_TO_MODULE_H
|
||||
@@ -1,339 +0,0 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// dwarf_line_to_module.cc: Unit tests for google_breakpad::DwarfLineToModule.
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/dwarf_line_to_module.h"
|
||||
|
||||
using google_breakpad::DwarfLineToModule;
|
||||
using google_breakpad::Module;
|
||||
using google_breakpad::Module;
|
||||
|
||||
TEST(Simple, One) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("file1", 0x30bf0f27, 0, 0, 0);
|
||||
h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27,
|
||||
0x4c090cbf, 0x1cf9fe0d);
|
||||
|
||||
vector<Module::File *> files;
|
||||
m.GetFiles(&files);
|
||||
EXPECT_EQ(1U, files.size());
|
||||
EXPECT_STREQ("file1", files[0]->name.c_str());
|
||||
|
||||
EXPECT_EQ(1U, lines.size());
|
||||
EXPECT_EQ(0x6fd126fbf74f2680ULL, lines[0].address);
|
||||
EXPECT_EQ(0x63c9a14cf556712bULL, lines[0].size);
|
||||
EXPECT_TRUE(lines[0].file == files[0]);
|
||||
EXPECT_EQ(0x4c090cbf, lines[0].number);
|
||||
}
|
||||
|
||||
TEST(Simple, Many) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory1", 0x838299ab);
|
||||
h.DefineDir("directory2", 0xf85de023);
|
||||
h.DefineFile("file1", 0x2b80377a, 0x838299ab, 0, 0);
|
||||
h.DefineFile("file1", 0x63beb4a4, 0xf85de023, 0, 0);
|
||||
h.DefineFile("file2", 0x1d161d56, 0x838299ab, 0, 0);
|
||||
h.DefineFile("file2", 0x1e7a667c, 0xf85de023, 0, 0);
|
||||
h.AddLine(0x69900c5d553b7274ULL, 0x90fded183f0d0d3cULL, 0x2b80377a,
|
||||
0x15b0f0a9U, 0x3ff5abd6U);
|
||||
h.AddLine(0x45811219a39b7101ULL, 0x25a5e6a924afc41fULL, 0x63beb4a4,
|
||||
0x4d259ce9U, 0x41c5ee32U);
|
||||
h.AddLine(0xfa90514c1dc9704bULL, 0x0063efeabc02f313ULL, 0x1d161d56,
|
||||
0x1ee9fa4fU, 0xbf70e46aU);
|
||||
h.AddLine(0x556b55fb6a647b10ULL, 0x3f3089ca2bfd80f5ULL, 0x1e7a667c,
|
||||
0x77fc280eU, 0x2c4a728cU);
|
||||
h.DefineFile("file3", -1, 0, 0, 0);
|
||||
h.AddLine(0xe2d72a37f8d9403aULL, 0x034dfab5b0d4d236ULL, 0x63beb4a5,
|
||||
0x75047044U, 0xb6a0016cU);
|
||||
|
||||
vector<Module::File *> files;
|
||||
m.GetFiles(&files);
|
||||
ASSERT_EQ(5U, files.size());
|
||||
EXPECT_STREQ("directory1/file1", files[0]->name.c_str());
|
||||
EXPECT_STREQ("directory1/file2", files[1]->name.c_str());
|
||||
EXPECT_STREQ("directory2/file1", files[2]->name.c_str());
|
||||
EXPECT_STREQ("directory2/file2", files[3]->name.c_str());
|
||||
EXPECT_STREQ("file3", files[4]->name.c_str());
|
||||
|
||||
ASSERT_EQ(5U, lines.size());
|
||||
|
||||
EXPECT_EQ(0x69900c5d553b7274ULL, lines[0].address);
|
||||
EXPECT_EQ(0x90fded183f0d0d3cULL, lines[0].size);
|
||||
EXPECT_TRUE(lines[0].file == files[0]);
|
||||
EXPECT_EQ(0x15b0f0a9, lines[0].number);
|
||||
|
||||
EXPECT_EQ(0x45811219a39b7101ULL, lines[1].address);
|
||||
EXPECT_EQ(0x25a5e6a924afc41fULL, lines[1].size);
|
||||
EXPECT_TRUE(lines[1].file == files[2]);
|
||||
EXPECT_EQ(0x4d259ce9, lines[1].number);
|
||||
|
||||
EXPECT_EQ(0xfa90514c1dc9704bULL, lines[2].address);
|
||||
EXPECT_EQ(0x0063efeabc02f313ULL, lines[2].size);
|
||||
EXPECT_TRUE(lines[2].file == files[1]);
|
||||
EXPECT_EQ(0x1ee9fa4f, lines[2].number);
|
||||
|
||||
EXPECT_EQ(0x556b55fb6a647b10ULL, lines[3].address);
|
||||
EXPECT_EQ(0x3f3089ca2bfd80f5ULL, lines[3].size);
|
||||
EXPECT_TRUE(lines[3].file == files[3]);
|
||||
EXPECT_EQ(0x77fc280e, lines[3].number);
|
||||
|
||||
EXPECT_EQ(0xe2d72a37f8d9403aULL, lines[4].address);
|
||||
EXPECT_EQ(0x034dfab5b0d4d236ULL, lines[4].size);
|
||||
EXPECT_TRUE(lines[4].file == files[4]);
|
||||
EXPECT_EQ(0x75047044, lines[4].number);
|
||||
}
|
||||
|
||||
TEST(Filenames, Absolute) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory1", 1);
|
||||
h.DefineFile("/absolute", 1, 1, 0, 0);
|
||||
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
vector<Module::File *> files;
|
||||
m.GetFiles(&files);
|
||||
ASSERT_EQ(1U, files.size());
|
||||
EXPECT_STREQ("/absolute", files[0]->name.c_str());
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_TRUE(lines[0].file == files[0]);
|
||||
}
|
||||
|
||||
TEST(Filenames, Relative) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory1", 1);
|
||||
h.DefineFile("relative", 1, 1, 0, 0);
|
||||
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
vector<Module::File *> files;
|
||||
m.GetFiles(&files);
|
||||
ASSERT_EQ(1U, files.size());
|
||||
EXPECT_STREQ("directory1/relative", files[0]->name.c_str());
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_TRUE(lines[0].file == files[0]);
|
||||
}
|
||||
|
||||
TEST(Filenames, StrangeFile) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory1", 1);
|
||||
h.DefineFile("", 1, 1, 0, 0);
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_STREQ("directory1/", lines[0].file->name.c_str());
|
||||
}
|
||||
|
||||
TEST(Filenames, StrangeDirectory) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("", 1);
|
||||
h.DefineFile("file1", 1, 1, 0, 0);
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_STREQ("/file1", lines[0].file->name.c_str());
|
||||
}
|
||||
|
||||
TEST(Filenames, StrangeDirectoryAndFile) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("", 1);
|
||||
h.DefineFile("", 1, 1, 0, 0);
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_STREQ("/", lines[0].file->name.c_str());
|
||||
}
|
||||
|
||||
// We should silently ignore attempts to define directory number zero,
|
||||
// since that is always the compilation directory.
|
||||
TEST(Errors, DirectoryZero) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory0", 0); // should be ignored
|
||||
h.DefineFile("relative", 1, 0, 0, 0);
|
||||
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_STREQ("relative", lines[0].file->name.c_str());
|
||||
}
|
||||
|
||||
// We should refuse to add lines with bogus file numbers. We should
|
||||
// produce only one warning, however.
|
||||
TEST(Errors, BadFileNumber) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("relative", 1, 0, 0, 0);
|
||||
h.AddLine(1, 1, 2, 0, 0); // bad file number
|
||||
h.AddLine(2, 1, 2, 0, 0); // bad file number (no duplicate warning)
|
||||
|
||||
EXPECT_EQ(0U, lines.size());
|
||||
}
|
||||
|
||||
// We should treat files with bogus directory numbers as relative to
|
||||
// the compilation unit.
|
||||
TEST(Errors, BadDirectoryNumber) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory1", 1);
|
||||
h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number
|
||||
h.DefineFile("baddirnumber2", 2, 2, 0, 0); // bad dir number (no warning)
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_STREQ("baddirnumber1", lines[0].file->name.c_str());
|
||||
}
|
||||
|
||||
// We promise not to report empty lines.
|
||||
TEST(Errors, EmptyLine) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(1, 0, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(0U, lines.size());
|
||||
}
|
||||
|
||||
// We are supposed to clip lines that extend beyond the end of the
|
||||
// address space.
|
||||
TEST(Errors, BigLine) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_EQ(1U, lines[0].size);
|
||||
}
|
||||
|
||||
// The 'Omitted' tests verify that we correctly omit line information
|
||||
// for code in sections that the linker has dropped. See "GNU
|
||||
// toolchain omitted sections support" at the top of the
|
||||
// DwarfLineToModule class.
|
||||
|
||||
TEST(Omitted, DroppedThenGood) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(0, 10, 1, 83816211, 0); // should be omitted
|
||||
h.AddLine(20, 10, 1, 13059195, 0); // should be recorded
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_EQ(13059195, lines[0].number);
|
||||
}
|
||||
|
||||
TEST(Omitted, GoodThenDropped) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded
|
||||
h.AddLine(0, 10, 1, 44793413, 0); // should be omitted
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_EQ(41454594, lines[0].number);
|
||||
}
|
||||
|
||||
TEST(Omitted, Mix1) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded
|
||||
h.AddLine(0xdfb5a72d, 10, 1, 39847385, 0); // should be recorded
|
||||
h.AddLine(0, 0x78, 1, 23053829, 0); // should be omitted
|
||||
h.AddLine(0x78, 0x6a, 1, 65317783, 0); // should be omitted
|
||||
h.AddLine(0x78 + 0x6a, 0x2a, 1, 77601423, 0); // should be omitted
|
||||
h.AddLine(0x9fe0cea5, 10, 1, 91806582, 0); // should be recorded
|
||||
h.AddLine(0x7e41a109, 10, 1, 56169221, 0); // should be recorded
|
||||
|
||||
ASSERT_EQ(4U, lines.size());
|
||||
EXPECT_EQ(58932642, lines[0].number);
|
||||
EXPECT_EQ(39847385, lines[1].number);
|
||||
EXPECT_EQ(91806582, lines[2].number);
|
||||
EXPECT_EQ(56169221, lines[3].number);
|
||||
}
|
||||
|
||||
TEST(Omitted, Mix2) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted
|
||||
h.AddLine(0xf2, 0xb9, 1, 78958222, 0); // should be omitted
|
||||
h.AddLine(0xf2 + 0xb9, 0xf7, 1, 64861892, 0); // should be omitted
|
||||
h.AddLine(0x4e4d271e, 9, 1, 67355743, 0); // should be recorded
|
||||
h.AddLine(0xdfb5a72d, 30, 1, 23365776, 0); // should be recorded
|
||||
h.AddLine(0, 0x64, 1, 76196762, 0); // should be omitted
|
||||
h.AddLine(0x64, 0x33, 1, 71066611, 0); // should be omitted
|
||||
h.AddLine(0x64 + 0x33, 0xe3, 1, 61749337, 0); // should be omitted
|
||||
|
||||
ASSERT_EQ(2U, lines.size());
|
||||
EXPECT_EQ(67355743, lines[0].number);
|
||||
EXPECT_EQ(23365776, lines[1].number);
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// language.cc: Subclasses and singletons for google_breakpad::Language.
|
||||
// See language.h for details.
|
||||
|
||||
#include "common/linux/language.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// C++ language-specific operations.
|
||||
class CPPLanguage: public Language {
|
||||
public:
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
if (parent_name.empty())
|
||||
return name;
|
||||
else
|
||||
return parent_name + "::" + name;
|
||||
}
|
||||
};
|
||||
|
||||
const CPPLanguage CPPLanguageSingleton;
|
||||
|
||||
// Java language-specific operations.
|
||||
class JavaLanguage: public Language {
|
||||
public:
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
if (parent_name.empty())
|
||||
return name;
|
||||
else
|
||||
return parent_name + "." + name;
|
||||
}
|
||||
};
|
||||
|
||||
JavaLanguage JavaLanguageSingleton;
|
||||
|
||||
// Assembler language-specific operations.
|
||||
class AssemblerLanguage: public Language {
|
||||
bool HasFunctions() const { return false; }
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
AssemblerLanguage AssemblerLanguageSingleton;
|
||||
|
||||
const Language * const Language::CPlusPlus = &CPPLanguageSingleton;
|
||||
const Language * const Language::Java = &JavaLanguageSingleton;
|
||||
const Language * const Language::Assembler = &AssemblerLanguageSingleton;
|
||||
|
||||
} // namespace google_breakpad
|
||||
@@ -1,84 +0,0 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// language.h: Define google_breakpad::Language. Instances of
|
||||
// subclasses of this class provide language-appropriate operations
|
||||
// for the Breakpad symbol dumper.
|
||||
|
||||
#ifndef COMMON_LINUX_LANGUAGE_H__
|
||||
#define COMMON_LINUX_LANGUAGE_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
|
||||
// An abstract base class for language-specific operations. We choose
|
||||
// an instance of a subclass of this when we find the CU's language.
|
||||
// This class's definitions are appropriate for CUs with no specified
|
||||
// language.
|
||||
class Language {
|
||||
public:
|
||||
// Return true if this language has functions to which we can assign
|
||||
// line numbers. (Debugging info for assembly language, for example,
|
||||
// can have source location information, but does not have functions
|
||||
// recorded using DW_TAG_subprogram DIEs.)
|
||||
virtual bool HasFunctions() const { return true; }
|
||||
|
||||
// Construct a fully-qualified, language-appropriate form of NAME,
|
||||
// given that PARENT_NAME is the name of the construct enclosing
|
||||
// NAME. If PARENT_NAME is the empty string, then NAME is a
|
||||
// top-level name.
|
||||
//
|
||||
// This API sort of assumes that a fully-qualified name is always
|
||||
// some simple textual composition of the unqualified name and its
|
||||
// parent's name, and that we don't need to know anything else about
|
||||
// the parent or the child (say, their DIEs' tags) to do the job.
|
||||
// This is true for the languages we support at the moment, and
|
||||
// keeps things concrete. Perhaps a more refined operation would
|
||||
// take into account the parent and child DIE types, allow languages
|
||||
// to use their own data type for complex parent names, etc. But if
|
||||
// C++ doesn't need all that, who would?
|
||||
virtual string MakeQualifiedName (const string &parent_name,
|
||||
const string &name) const = 0;
|
||||
|
||||
// Instances for specific languages.
|
||||
static const Language * const CPlusPlus,
|
||||
* const Java,
|
||||
* const Assembler;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_LANGUAGE_H__
|
||||
@@ -1,235 +0,0 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// module.cc: Implement google_breakpad::Module. See module.h.
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/linux/module.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
Module::Module(const string &name, const string &os,
|
||||
const string &architecture, const string &id) :
|
||||
name_(name),
|
||||
os_(os),
|
||||
architecture_(architecture),
|
||||
id_(id),
|
||||
load_address_(0) { }
|
||||
|
||||
Module::~Module() {
|
||||
for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++)
|
||||
delete it->second;
|
||||
for (vector<Function *>::iterator it = functions_.begin();
|
||||
it != functions_.end(); it++)
|
||||
delete *it;
|
||||
for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin();
|
||||
it != stack_frame_entries_.end(); it++)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
void Module::SetLoadAddress(Address address) {
|
||||
load_address_ = address;
|
||||
}
|
||||
|
||||
void Module::AddFunction(Function *function) {
|
||||
functions_.push_back(function);
|
||||
}
|
||||
|
||||
void Module::AddFunctions(vector<Function *>::iterator begin,
|
||||
vector<Function *>::iterator end) {
|
||||
functions_.insert(functions_.end(), begin, end);
|
||||
}
|
||||
|
||||
void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) {
|
||||
stack_frame_entries_.push_back(stack_frame_entry);
|
||||
}
|
||||
|
||||
void Module::GetFunctions(vector<Function *> *vec,
|
||||
vector<Function *>::iterator i) {
|
||||
vec->insert(i, functions_.begin(), functions_.end());
|
||||
}
|
||||
|
||||
Module::File *Module::FindFile(const string &name) {
|
||||
// A tricky bit here. The key of each map entry needs to be a
|
||||
// pointer to the entry's File's name string. This means that we
|
||||
// can't do the initial lookup with any operation that would create
|
||||
// an empty entry for us if the name isn't found (like, say,
|
||||
// operator[] or insert do), because such a created entry's key will
|
||||
// be a pointer the string passed as our argument. Since the key of
|
||||
// a map's value type is const, we can't fix it up once we've
|
||||
// created our file. lower_bound does the lookup without doing an
|
||||
// insertion, and returns a good hint iterator to pass to insert.
|
||||
// Our "destiny" is where we belong, whether we're there or not now.
|
||||
FileByNameMap::iterator destiny = files_.lower_bound(&name);
|
||||
if (destiny == files_.end()
|
||||
|| *destiny->first != name) { // Repeated string comparison, boo hoo.
|
||||
File *file = new File;
|
||||
file->name = name;
|
||||
file->source_id = -1;
|
||||
destiny = files_.insert(destiny,
|
||||
FileByNameMap::value_type(&file->name, file));
|
||||
}
|
||||
return destiny->second;
|
||||
}
|
||||
|
||||
Module::File *Module::FindFile(const char *name) {
|
||||
string name_string = name;
|
||||
return FindFile(name_string);
|
||||
}
|
||||
|
||||
Module::File *Module::FindExistingFile(const string &name) {
|
||||
FileByNameMap::iterator it = files_.find(&name);
|
||||
return (it == files_.end()) ? NULL : it->second;
|
||||
}
|
||||
|
||||
void Module::GetFiles(vector<File *> *vec) {
|
||||
vec->clear();
|
||||
for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++)
|
||||
vec->push_back(it->second);
|
||||
}
|
||||
|
||||
void Module::GetStackFrameEntries(vector<StackFrameEntry *> *vec) {
|
||||
*vec = stack_frame_entries_;
|
||||
}
|
||||
|
||||
void Module::AssignSourceIds() {
|
||||
// First, give every source file an id of -1.
|
||||
for (FileByNameMap::iterator file_it = files_.begin();
|
||||
file_it != files_.end(); file_it++)
|
||||
file_it->second->source_id = -1;
|
||||
|
||||
// Next, mark all files actually cited by our functions' line number
|
||||
// info, by setting each one's source id to zero.
|
||||
for (vector<Function *>::const_iterator func_it = functions_.begin();
|
||||
func_it != functions_.end(); func_it++) {
|
||||
Function *func = *func_it;
|
||||
for (vector<Line>::iterator line_it = func->lines.begin();
|
||||
line_it != func->lines.end(); line_it++)
|
||||
line_it->file->source_id = 0;
|
||||
}
|
||||
|
||||
// Finally, assign source ids to those files that have been marked.
|
||||
// We could have just assigned source id numbers while traversing
|
||||
// the line numbers, but doing it this way numbers the files in
|
||||
// lexicographical order by name, which is neat.
|
||||
int next_source_id = 0;
|
||||
for (FileByNameMap::iterator file_it = files_.begin();
|
||||
file_it != files_.end(); file_it++)
|
||||
if (! file_it->second->source_id)
|
||||
file_it->second->source_id = next_source_id++;
|
||||
}
|
||||
|
||||
bool Module::ReportError() {
|
||||
fprintf(stderr, "error writing symbol file: %s\n",
|
||||
strerror (errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Module::WriteRuleMap(const RuleMap &rule_map, FILE *stream) {
|
||||
for (RuleMap::const_iterator it = rule_map.begin();
|
||||
it != rule_map.end(); it++) {
|
||||
if (it != rule_map.begin() &&
|
||||
0 > putc(' ', stream))
|
||||
return false;
|
||||
if (0 > fprintf(stream, "%s: %s", it->first.c_str(), it->second.c_str()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Module::Write(FILE *stream) {
|
||||
if (0 > fprintf(stream, "MODULE %s %s %s %s\n",
|
||||
os_.c_str(), architecture_.c_str(), id_.c_str(),
|
||||
name_.c_str()))
|
||||
return ReportError();
|
||||
|
||||
AssignSourceIds();
|
||||
|
||||
// Write out files.
|
||||
for (FileByNameMap::iterator file_it = files_.begin();
|
||||
file_it != files_.end(); file_it++) {
|
||||
File *file = file_it->second;
|
||||
if (file->source_id >= 0) {
|
||||
if (0 > fprintf(stream, "FILE %d %s\n",
|
||||
file->source_id, file->name.c_str()))
|
||||
return ReportError();
|
||||
}
|
||||
}
|
||||
|
||||
// Write out functions and their lines.
|
||||
for (vector<Function *>::const_iterator func_it = functions_.begin();
|
||||
func_it != functions_.end(); func_it++) {
|
||||
Function *func = *func_it;
|
||||
if (0 > fprintf(stream, "FUNC %llx %llx %llx %s\n",
|
||||
(unsigned long long) (func->address - load_address_),
|
||||
(unsigned long long) func->size,
|
||||
(unsigned long long) func->parameter_size,
|
||||
func->name.c_str()))
|
||||
return ReportError();
|
||||
for (vector<Line>::iterator line_it = func->lines.begin();
|
||||
line_it != func->lines.end(); line_it++)
|
||||
if (0 > fprintf(stream, "%llx %llx %d %d\n",
|
||||
(unsigned long long) (line_it->address - load_address_),
|
||||
(unsigned long long) line_it->size,
|
||||
line_it->number,
|
||||
line_it->file->source_id))
|
||||
return ReportError();
|
||||
}
|
||||
|
||||
// Write out 'STACK CFI INIT' and 'STACK CFI' records.
|
||||
vector<StackFrameEntry *>::const_iterator frame_it;
|
||||
for (frame_it = stack_frame_entries_.begin();
|
||||
frame_it != stack_frame_entries_.end(); frame_it++) {
|
||||
StackFrameEntry *entry = *frame_it;
|
||||
if (0 > fprintf(stream, "STACK CFI INIT %llx %llx ",
|
||||
(unsigned long long) entry->address - load_address_,
|
||||
(unsigned long long) entry->size)
|
||||
|| !WriteRuleMap(entry->initial_rules, stream)
|
||||
|| 0 > putc('\n', stream))
|
||||
return ReportError();
|
||||
|
||||
// Write out this entry's delta rules as 'STACK CFI' records.
|
||||
for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin();
|
||||
delta_it != entry->rule_changes.end(); delta_it++) {
|
||||
if (0 > fprintf(stream, "STACK CFI %llx ",
|
||||
(unsigned long long) delta_it->first - load_address_)
|
||||
|| !WriteRuleMap(delta_it->second, stream)
|
||||
|| 0 > putc('\n', stream))
|
||||
return ReportError();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
@@ -1,268 +0,0 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// module.h: Define google_breakpad::Module. A Module holds debugging
|
||||
// information, and can write that information out as a Breakpad
|
||||
// symbol file.
|
||||
|
||||
#ifndef COMMON_LINUX_MODULE_H__
|
||||
#define COMMON_LINUX_MODULE_H__
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::map;
|
||||
|
||||
// A Module represents the contents of a module, and supports methods
|
||||
// for adding information produced by parsing STABS or DWARF data
|
||||
// --- possibly both from the same file --- and then writing out the
|
||||
// unified contents as a Breakpad-format symbol file.
|
||||
class Module {
|
||||
public:
|
||||
// The type of addresses and sizes in a symbol table.
|
||||
typedef u_int64_t Address;
|
||||
struct File;
|
||||
struct Function;
|
||||
struct Line;
|
||||
|
||||
// Addresses appearing in File, Function, and Line structures are
|
||||
// absolute, not relative to the the module's load address. That
|
||||
// is, if the module were loaded at its nominal load address, the
|
||||
// addresses would be correct.
|
||||
|
||||
// A source file.
|
||||
struct File {
|
||||
// The name of the source file.
|
||||
string name;
|
||||
|
||||
// The file's source id. The Write member function clears this
|
||||
// field and assigns source ids a fresh, so any value placed here
|
||||
// before calling Write will be lost.
|
||||
int source_id;
|
||||
};
|
||||
|
||||
// A function.
|
||||
struct Function {
|
||||
// For sorting by address. (Not style-guide compliant, but it's
|
||||
// stupid not to put this in the struct.)
|
||||
static bool CompareByAddress(const Function *x, const Function *y) {
|
||||
return x->address < y->address;
|
||||
}
|
||||
|
||||
// The function's name.
|
||||
string name;
|
||||
|
||||
// The start address and length of the function's code.
|
||||
Address address, size;
|
||||
|
||||
// The function's parameter size.
|
||||
Address parameter_size;
|
||||
|
||||
// Source lines belonging to this function, sorted by increasing
|
||||
// address.
|
||||
vector<Line> lines;
|
||||
};
|
||||
|
||||
// A source line.
|
||||
struct Line {
|
||||
// For sorting by address. (Not style-guide compliant, but it's
|
||||
// stupid not to put this in the struct.)
|
||||
static bool CompareByAddress(const Module::Line &x, const Module::Line &y) {
|
||||
return x.address < y.address;
|
||||
}
|
||||
|
||||
Address address, size; // The address and size of the line's code.
|
||||
File *file; // The source file.
|
||||
int number; // The source line number.
|
||||
};
|
||||
|
||||
// A map from register names to postfix expressions that recover
|
||||
// their their values. This can represent a complete set of rules to
|
||||
// follow at some address, or a set of changes to be applied to an
|
||||
// extant set of rules.
|
||||
typedef map<string, string> RuleMap;
|
||||
|
||||
// A map from addresses to RuleMaps, representing changes that take
|
||||
// effect at given addresses.
|
||||
typedef map<Address, RuleMap> RuleChangeMap;
|
||||
|
||||
// A range of 'STACK CFI' stack walking information. An instance of
|
||||
// this structure corresponds to a 'STACK CFI INIT' record and the
|
||||
// subsequent 'STACK CFI' records that fall within its range.
|
||||
struct StackFrameEntry {
|
||||
// The starting address and number of bytes of machine code this
|
||||
// entry covers.
|
||||
Address address, size;
|
||||
|
||||
// The initial register recovery rules, in force at the starting
|
||||
// address.
|
||||
RuleMap initial_rules;
|
||||
|
||||
// A map from addresses to rule changes. To find the rules in
|
||||
// force at a given address, start with initial_rules, and then
|
||||
// apply the changes given in this map for all addresses up to and
|
||||
// including the address you're interested in.
|
||||
RuleChangeMap rule_changes;
|
||||
};
|
||||
|
||||
// Create a new module with the given name, operating system,
|
||||
// architecture, and ID string.
|
||||
Module(const string &name, const string &os, const string &architecture,
|
||||
const string &id);
|
||||
~Module();
|
||||
|
||||
// Set the module's load address to LOAD_ADDRESS; addresses given
|
||||
// for functions and lines will be written to the Breakpad symbol
|
||||
// file as offsets from this address. Construction initializes this
|
||||
// module's load address to zero: addresses written to the symbol
|
||||
// file will be the same as they appear in the File and Line
|
||||
// structures.
|
||||
void SetLoadAddress(Address load_address);
|
||||
|
||||
// Add FUNCTION to the module.
|
||||
// This module owns all Function objects added with this function:
|
||||
// destroying the module destroys them as well.
|
||||
void AddFunction(Function *function);
|
||||
|
||||
// Add all the functions in [BEGIN,END) to the module.
|
||||
// This module owns all Function objects added with this function:
|
||||
// destroying the module destroys them as well.
|
||||
void AddFunctions(vector<Function *>::iterator begin,
|
||||
vector<Function *>::iterator end);
|
||||
|
||||
// Add STACK_FRAME_ENTRY to the module.
|
||||
//
|
||||
// This module owns all StackFrameEntry objects added with this
|
||||
// function: destroying the module destroys them as well.
|
||||
void AddStackFrameEntry(StackFrameEntry *stack_frame_entry);
|
||||
|
||||
// If this module has a file named NAME, return a pointer to it. If
|
||||
// it has none, then create one and return a pointer to the new
|
||||
// file. This module owns all File objects created using these
|
||||
// functions; destroying the module destroys them as well.
|
||||
File *FindFile(const string &name);
|
||||
File *FindFile(const char *name);
|
||||
|
||||
// If this module has a file named NAME, return a pointer to it.
|
||||
// Otherwise, return NULL.
|
||||
File *FindExistingFile(const string &name);
|
||||
|
||||
// Insert pointers to the functions added to this module at I in
|
||||
// VEC. The pointed-to Functions are still owned by this module.
|
||||
// (Since this is effectively a copy of the function list, this is
|
||||
// mostly useful for testing; other uses should probably get a more
|
||||
// appropriate interface.)
|
||||
void GetFunctions(vector<Function *> *vec, vector<Function *>::iterator i);
|
||||
|
||||
// Clear VEC and fill it with pointers to the Files added to this
|
||||
// module, sorted by name. The pointed-to Files are still owned by
|
||||
// this module. (Since this is effectively a copy of the file list,
|
||||
// this is mostly useful for testing; other uses should probably get
|
||||
// a more appropriate interface.)
|
||||
void GetFiles(vector<File *> *vec);
|
||||
|
||||
// Clear VEC and fill it with pointers to the StackFrameEntry
|
||||
// objects that have been added to this module. (Since this is
|
||||
// effectively a copy of the stack frame entry list, this is mostly
|
||||
// useful for testing; other uses should probably get
|
||||
// a more appropriate interface.)
|
||||
void GetStackFrameEntries(vector<StackFrameEntry *> *vec);
|
||||
|
||||
// Find those files in this module that are actually referred to by
|
||||
// functions' line number data, and assign them source id numbers.
|
||||
// Set the source id numbers for all other files --- unused by the
|
||||
// source line data --- to -1. We do this before writing out the
|
||||
// symbol file, at which point we omit any unused files.
|
||||
void AssignSourceIds();
|
||||
|
||||
// Call AssignSourceIds, and write this module to STREAM in the
|
||||
// breakpad symbol format. Return true if all goes well, or false if
|
||||
// an error occurs. This method writes out:
|
||||
// - a header based on the values given to the constructor,
|
||||
// - the source files added via FindFile, and finally
|
||||
// - the functions added via AddFunctions, each with its lines.
|
||||
// Addresses in the output are all relative to the load address
|
||||
// established by SetLoadAddress.
|
||||
bool Write(FILE *stream);
|
||||
|
||||
private:
|
||||
|
||||
// Report an error that has occurred writing the symbol file, using
|
||||
// errno to find the appropriate cause. Return false.
|
||||
static bool ReportError();
|
||||
|
||||
// Write RULE_MAP to STREAM, in the form appropriate for 'STACK CFI'
|
||||
// records, without a final newline. Return true if all goes well;
|
||||
// if an error occurs, return false, and leave errno set.
|
||||
static bool WriteRuleMap(const RuleMap &rule_map, FILE *stream);
|
||||
|
||||
// Module header entries.
|
||||
string name_, os_, architecture_, id_;
|
||||
|
||||
// The module's nominal load address. Addresses for functions and
|
||||
// lines are absolute, assuming the module is loaded at this
|
||||
// address.
|
||||
Address load_address_;
|
||||
|
||||
// Relation for maps whose keys are strings shared with some other
|
||||
// structure.
|
||||
struct CompareStringPtrs {
|
||||
bool operator()(const string *x, const string *y) { return *x < *y; };
|
||||
};
|
||||
|
||||
// A map from filenames to File structures. The map's keys are
|
||||
// pointers to the Files' names.
|
||||
typedef map<const string *, File *, CompareStringPtrs> FileByNameMap;
|
||||
|
||||
// The module owns all the files and functions that have been added
|
||||
// to it; destroying the module frees the Files and Functions these
|
||||
// point to.
|
||||
FileByNameMap files_; // This module's source files.
|
||||
vector<Function *> functions_; // This module's functions.
|
||||
|
||||
// The module owns all the call frame info entries that have been
|
||||
// added to it.
|
||||
vector<StackFrameEntry *> stack_frame_entries_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_MODULE_H__
|
||||
@@ -1,396 +0,0 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// module_unittest.cc: Unit tests for google_breakpad::Module.
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/module.h"
|
||||
|
||||
using google_breakpad::Module;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using testing::ContainerEq;
|
||||
|
||||
// Return a FILE * referring to a temporary file that will be deleted
|
||||
// automatically when the stream is closed or the program exits.
|
||||
FILE *checked_tmpfile() {
|
||||
FILE *f = tmpfile();
|
||||
if (!f) {
|
||||
fprintf(stderr, "error creating temporary file: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
// Read from STREAM until end of file, and return the contents as a
|
||||
// string.
|
||||
string checked_read(FILE *stream) {
|
||||
string contents;
|
||||
int c;
|
||||
while ((c = getc(stream)) != EOF)
|
||||
contents.push_back(c);
|
||||
if (ferror(stream)) {
|
||||
fprintf(stderr, "error reading temporary file contents: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
// Apply 'fflush' to STREAM, and check for errors.
|
||||
void checked_fflush(FILE *stream) {
|
||||
if (fflush(stream) == EOF) {
|
||||
fprintf(stderr, "error flushing temporary file stream: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply 'fclose' to STREAM, and check for errors.
|
||||
void checked_fclose(FILE *stream) {
|
||||
if (fclose(stream) == EOF) {
|
||||
fprintf(stderr, "error closing temporary file stream: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define MODULE_NAME "name with spaces"
|
||||
#define MODULE_OS "os-name"
|
||||
#define MODULE_ARCH "architecture"
|
||||
#define MODULE_ID "id-string"
|
||||
|
||||
TEST(Write, Header) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Write, OneLineFunc) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
Module::File *file = m.FindFile("file_name.cc");
|
||||
Module::Function *function = new(Module::Function);
|
||||
function->name = "function_name";
|
||||
function->address = 0xe165bf8023b9d9abLL;
|
||||
function->size = 0x1e4bb0eb1cbf5b09LL;
|
||||
function->parameter_size = 0x772beee89114358aLL;
|
||||
Module::Line line = { 0xe165bf8023b9d9abLL, 0x1e4bb0eb1cbf5b09LL,
|
||||
file, 67519080 };
|
||||
function->lines.push_back(line);
|
||||
m.AddFunction(function);
|
||||
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"FILE 0 file_name.cc\n"
|
||||
"FUNC e165bf8023b9d9ab 1e4bb0eb1cbf5b09 772beee89114358a"
|
||||
" function_name\n"
|
||||
"e165bf8023b9d9ab 1e4bb0eb1cbf5b09 67519080 0\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Write, RelativeLoadAddress) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
m.SetLoadAddress(0x2ab698b0b6407073LL);
|
||||
|
||||
// Some source files. We will expect to see them in lexicographic order.
|
||||
Module::File *file1 = m.FindFile("filename-b.cc");
|
||||
Module::File *file2 = m.FindFile("filename-a.cc");
|
||||
|
||||
// A function.
|
||||
Module::Function *function = new(Module::Function);
|
||||
function->name = "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)";
|
||||
function->address = 0xbec774ea5dd935f3LL;
|
||||
function->size = 0x2922088f98d3f6fcLL;
|
||||
function->parameter_size = 0xe5e9aa008bd5f0d0LL;
|
||||
|
||||
// Some source lines. The module should not sort these.
|
||||
Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL,
|
||||
file1, 41676901 };
|
||||
Module::Line line2 = { 0xdaf35bc123885c04LL, 0xcf621b8d324d0ebLL,
|
||||
file2, 67519080 };
|
||||
function->lines.push_back(line2);
|
||||
function->lines.push_back(line1);
|
||||
|
||||
m.AddFunction(function);
|
||||
|
||||
// Some stack information.
|
||||
Module::StackFrameEntry *entry = new Module::StackFrameEntry();
|
||||
entry->address = 0x30f9e5c83323973dULL;
|
||||
entry->size = 0x49fc9ca7c7c13dc2ULL;
|
||||
entry->initial_rules[".cfa"] = "he was a handsome man";
|
||||
entry->initial_rules["and"] = "what i want to know is";
|
||||
entry->rule_changes[0x30f9e5c83323973eULL]["how"] =
|
||||
"do you like your blueeyed boy";
|
||||
entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death";
|
||||
m.AddStackFrameEntry(entry);
|
||||
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"FILE 0 filename-a.cc\n"
|
||||
"FILE 1 filename-b.cc\n"
|
||||
"FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0"
|
||||
" A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n"
|
||||
"b03cc3106d47eb91 cf621b8d324d0eb 67519080 0\n"
|
||||
"9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n"
|
||||
"STACK CFI INIT 6434d177ce326ca 49fc9ca7c7c13dc2"
|
||||
" .cfa: he was a handsome man"
|
||||
" and: what i want to know is\n"
|
||||
"STACK CFI 6434d177ce326cb"
|
||||
" Mister: Death"
|
||||
" how: do you like your blueeyed boy\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Write, OmitUnusedFiles) {
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
// Create some source files.
|
||||
Module::File *file1 = m.FindFile("filename1");
|
||||
m.FindFile("filename2"); // not used by any line
|
||||
Module::File *file3 = m.FindFile("filename3");
|
||||
|
||||
// Create a function.
|
||||
Module::Function *function = new(Module::Function);
|
||||
function->name = "function_name";
|
||||
function->address = 0x9b926d464f0b9384LL;
|
||||
function->size = 0x4f524a4ba795e6a6LL;
|
||||
function->parameter_size = 0xbbe8133a6641c9b7LL;
|
||||
|
||||
// Source files that refer to some files, but not others.
|
||||
Module::Line line1 = { 0x595fa44ebacc1086LL, 0x1e1e0191b066c5b3LL,
|
||||
file1, 137850127 };
|
||||
Module::Line line2 = { 0x401ce8c8a12d25e3LL, 0x895751c41b8d2ce2LL,
|
||||
file3, 28113549 };
|
||||
function->lines.push_back(line1);
|
||||
function->lines.push_back(line2);
|
||||
m.AddFunction(function);
|
||||
|
||||
m.AssignSourceIds();
|
||||
|
||||
vector<Module::File *> vec;
|
||||
m.GetFiles(&vec);
|
||||
EXPECT_EQ((size_t) 3, vec.size());
|
||||
EXPECT_STREQ("filename1", vec[0]->name.c_str());
|
||||
EXPECT_NE(-1, vec[0]->source_id);
|
||||
// Expect filename2 not to be used.
|
||||
EXPECT_STREQ("filename2", vec[1]->name.c_str());
|
||||
EXPECT_EQ(-1, vec[1]->source_id);
|
||||
EXPECT_STREQ("filename3", vec[2]->name.c_str());
|
||||
EXPECT_NE(-1, vec[2]->source_id);
|
||||
|
||||
FILE *f = checked_tmpfile();
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"FILE 0 filename1\n"
|
||||
"FILE 1 filename3\n"
|
||||
"FUNC 9b926d464f0b9384 4f524a4ba795e6a6 bbe8133a6641c9b7"
|
||||
" function_name\n"
|
||||
"595fa44ebacc1086 1e1e0191b066c5b3 137850127 0\n"
|
||||
"401ce8c8a12d25e3 895751c41b8d2ce2 28113549 1\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Construct, AddFunctions) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
// Two functions.
|
||||
Module::Function *function1 = new(Module::Function);
|
||||
function1->name = "_without_form";
|
||||
function1->address = 0xd35024aa7ca7da5cLL;
|
||||
function1->size = 0x200b26e605f99071LL;
|
||||
function1->parameter_size = 0xf14ac4fed48c4a99LL;
|
||||
|
||||
Module::Function *function2 = new(Module::Function);
|
||||
function2->name = "_and_void";
|
||||
function2->address = 0x2987743d0b35b13fLL;
|
||||
function2->size = 0xb369db048deb3010LL;
|
||||
function2->parameter_size = 0x938e556cb5a79988LL;
|
||||
|
||||
// Put them in a vector.
|
||||
vector<Module::Function *> vec;
|
||||
vec.push_back(function1);
|
||||
vec.push_back(function2);
|
||||
|
||||
m.AddFunctions(vec.begin(), vec.end());
|
||||
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"FUNC d35024aa7ca7da5c 200b26e605f99071 f14ac4fed48c4a99"
|
||||
" _without_form\n"
|
||||
"FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988"
|
||||
" _and_void\n",
|
||||
contents.c_str());
|
||||
|
||||
// Check that m.GetFunctions returns the functions we expect.
|
||||
vec.clear();
|
||||
m.GetFunctions(&vec, vec.end());
|
||||
EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function1));
|
||||
EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function2));
|
||||
EXPECT_EQ((size_t) 2, vec.size());
|
||||
}
|
||||
|
||||
TEST(Construct, AddFrames) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
// First STACK CFI entry, with no initial rules or deltas.
|
||||
Module::StackFrameEntry *entry1 = new Module::StackFrameEntry();
|
||||
entry1->address = 0xddb5f41285aa7757ULL;
|
||||
entry1->size = 0x1486493370dc5073ULL;
|
||||
m.AddStackFrameEntry(entry1);
|
||||
|
||||
// Second STACK CFI entry, with initial rules but no deltas.
|
||||
Module::StackFrameEntry *entry2 = new Module::StackFrameEntry();
|
||||
entry2->address = 0x8064f3af5e067e38ULL;
|
||||
entry2->size = 0x0de2a5ee55509407ULL;
|
||||
entry2->initial_rules[".cfa"] = "I think that I shall never see";
|
||||
entry2->initial_rules["stromboli"] = "a poem lovely as a tree";
|
||||
entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest";
|
||||
m.AddStackFrameEntry(entry2);
|
||||
|
||||
// Third STACK CFI entry, with initial rules and deltas.
|
||||
Module::StackFrameEntry *entry3 = new Module::StackFrameEntry();
|
||||
entry3->address = 0x5e8d0db0a7075c6cULL;
|
||||
entry3->size = 0x1c7edb12a7aea229ULL;
|
||||
entry3->initial_rules[".cfa"] = "Whose woods are these";
|
||||
entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] =
|
||||
"the village though";
|
||||
entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
|
||||
"he will not see me stopping here";
|
||||
entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] =
|
||||
"his house is in";
|
||||
entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] =
|
||||
"I think I know";
|
||||
m.AddStackFrameEntry(entry3);
|
||||
|
||||
// Check that Write writes STACK CFI records properly.
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n"
|
||||
"STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407"
|
||||
" .cfa: I think that I shall never see"
|
||||
" cannoli: a tree whose hungry mouth is prest"
|
||||
" stromboli: a poem lovely as a tree\n"
|
||||
"STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229"
|
||||
" .cfa: Whose woods are these\n"
|
||||
"STACK CFI 36682fad3763ffff"
|
||||
" .cfa: I think I know"
|
||||
" stromboli: his house is in\n"
|
||||
"STACK CFI 47ceb0f63c269d7f"
|
||||
" calzone: the village though"
|
||||
" cannoli: he will not see me stopping here\n",
|
||||
contents.c_str());
|
||||
|
||||
// Check that GetStackFrameEntries works.
|
||||
vector<Module::StackFrameEntry *> entries;
|
||||
m.GetStackFrameEntries(&entries);
|
||||
ASSERT_EQ(3U, entries.size());
|
||||
// Check first entry.
|
||||
EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address);
|
||||
EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size);
|
||||
ASSERT_EQ(0U, entries[0]->initial_rules.size());
|
||||
ASSERT_EQ(0U, entries[0]->rule_changes.size());
|
||||
// Check second entry.
|
||||
EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address);
|
||||
EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size);
|
||||
ASSERT_EQ(3U, entries[1]->initial_rules.size());
|
||||
Module::RuleMap entry2_initial;
|
||||
entry2_initial[".cfa"] = "I think that I shall never see";
|
||||
entry2_initial["stromboli"] = "a poem lovely as a tree";
|
||||
entry2_initial["cannoli"] = "a tree whose hungry mouth is prest";
|
||||
EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial));
|
||||
ASSERT_EQ(0U, entries[1]->rule_changes.size());
|
||||
// Check third entry.
|
||||
EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[2]->address);
|
||||
EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size);
|
||||
Module::RuleMap entry3_initial;
|
||||
entry3_initial[".cfa"] = "Whose woods are these";
|
||||
EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial));
|
||||
Module::RuleChangeMap entry3_changes;
|
||||
entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know";
|
||||
entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in";
|
||||
entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though";
|
||||
entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
|
||||
"he will not see me stopping here";
|
||||
EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes));
|
||||
}
|
||||
|
||||
TEST(Construct, UniqueFiles) {
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
Module::File *file1 = m.FindFile("foo");
|
||||
Module::File *file2 = m.FindFile(string("bar"));
|
||||
Module::File *file3 = m.FindFile(string("foo"));
|
||||
Module::File *file4 = m.FindFile("bar");
|
||||
EXPECT_NE(file1, file2);
|
||||
EXPECT_EQ(file1, file3);
|
||||
EXPECT_EQ(file2, file4);
|
||||
EXPECT_EQ(file1, m.FindExistingFile("foo"));
|
||||
EXPECT_TRUE(m.FindExistingFile("baz") == NULL);
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// This file implements the google_breakpad::StabsReader class.
|
||||
// See stabs_reader.h.
|
||||
|
||||
#include <a.out.h>
|
||||
#include <stab.h>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
#include "common/linux/stabs_reader.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
StabsReader::StabsReader(const uint8_t *stab, size_t stab_size,
|
||||
const uint8_t *stabstr, size_t stabstr_size,
|
||||
StabsHandler *handler) :
|
||||
stabstr_(stabstr),
|
||||
stabstr_size_(stabstr_size),
|
||||
handler_(handler),
|
||||
string_offset_(0),
|
||||
next_cu_string_offset_(0),
|
||||
symbol_(NULL),
|
||||
current_source_file_(NULL) {
|
||||
symbols_ = reinterpret_cast<const struct nlist *>(stab);
|
||||
symbols_end_ = symbols_ + (stab_size / sizeof (*symbols_));
|
||||
}
|
||||
|
||||
const char *StabsReader::SymbolString() {
|
||||
ptrdiff_t offset = string_offset_ + symbol_->n_un.n_strx;
|
||||
if (offset < 0 || (size_t) offset >= stabstr_size_) {
|
||||
handler_->Warning("symbol %d: name offset outside the string section\n",
|
||||
symbol_ - symbols_);
|
||||
// Return our null string, to keep our promise about all names being
|
||||
// taken from the string section.
|
||||
offset = 0;
|
||||
}
|
||||
return reinterpret_cast<const char *>(stabstr_ + offset);
|
||||
}
|
||||
|
||||
bool StabsReader::Process() {
|
||||
symbol_ = symbols_;
|
||||
while (symbol_ < symbols_end_) {
|
||||
if (symbol_->n_type == N_SO) {
|
||||
if (! ProcessCompilationUnit())
|
||||
return false;
|
||||
} else if (symbol_->n_type == N_UNDF) {
|
||||
// At the head of each compilation unit's entries there is an
|
||||
// N_UNDF stab giving the number of symbols in the compilation
|
||||
// unit, and the number of bytes that compilation unit's strings
|
||||
// take up in the .stabstr section. Each CU's strings are
|
||||
// separate; the n_strx values are offsets within the current
|
||||
// CU's portion of the .stabstr section.
|
||||
//
|
||||
// As an optimization, the GNU linker combines all the
|
||||
// compilation units into one, with a single N_UNDF at the
|
||||
// beginning. However, other linkers, like Gold, do not perform
|
||||
// this optimization.
|
||||
string_offset_ = next_cu_string_offset_;
|
||||
next_cu_string_offset_ = SymbolValue();
|
||||
symbol_++;
|
||||
} else
|
||||
symbol_++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StabsReader::ProcessCompilationUnit() {
|
||||
assert(symbol_ < symbols_end_ && symbol_->n_type == N_SO);
|
||||
|
||||
// There may be an N_SO entry whose name ends with a slash,
|
||||
// indicating the directory in which the compilation occurred.
|
||||
// The build directory defaults to NULL.
|
||||
const char *build_directory = NULL;
|
||||
{
|
||||
const char *name = SymbolString();
|
||||
if (name[0] && name[strlen(name) - 1] == '/') {
|
||||
build_directory = name;
|
||||
symbol_++;
|
||||
}
|
||||
}
|
||||
|
||||
// We expect to see an N_SO entry with a filename next, indicating
|
||||
// the start of the compilation unit.
|
||||
{
|
||||
if (symbol_ >= symbols_end_ || symbol_->n_type != N_SO)
|
||||
return true;
|
||||
const char *name = SymbolString();
|
||||
if (name[0] == '\0') {
|
||||
// This seems to be a stray end-of-compilation-unit marker;
|
||||
// consume it, but don't report the end, since we didn't see a
|
||||
// beginning.
|
||||
symbol_++;
|
||||
return true;
|
||||
}
|
||||
current_source_file_ = name;
|
||||
}
|
||||
|
||||
if (! handler_->StartCompilationUnit(current_source_file_,
|
||||
SymbolValue(),
|
||||
build_directory))
|
||||
return false;
|
||||
|
||||
symbol_++;
|
||||
|
||||
// The STABS documentation says that some compilers may emit
|
||||
// additional N_SO entries with names immediately following the
|
||||
// first, and that they should be ignored. However, the original
|
||||
// Breakpad STABS reader doesn't ignore them, so we won't either.
|
||||
|
||||
// Process the body of the compilation unit, up to the next N_SO.
|
||||
while (symbol_ < symbols_end_ && symbol_->n_type != N_SO) {
|
||||
if (symbol_->n_type == N_FUN) {
|
||||
if (! ProcessFunction())
|
||||
return false;
|
||||
} else
|
||||
// Ignore anything else.
|
||||
symbol_++;
|
||||
}
|
||||
|
||||
// An N_SO with an empty name indicates the end of the compilation
|
||||
// unit. Default to zero.
|
||||
uint64_t ending_address = 0;
|
||||
if (symbol_ < symbols_end_) {
|
||||
assert(symbol_->n_type == N_SO);
|
||||
const char *name = SymbolString();
|
||||
if (name[0] == '\0') {
|
||||
ending_address = SymbolValue();
|
||||
symbol_++;
|
||||
}
|
||||
}
|
||||
|
||||
if (! handler_->EndCompilationUnit(ending_address))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StabsReader::ProcessFunction() {
|
||||
assert(symbol_ < symbols_end_ && symbol_->n_type == N_FUN);
|
||||
|
||||
uint64_t function_address = SymbolValue();
|
||||
// The STABS string for an N_FUN entry is the name of the function,
|
||||
// followed by a colon, followed by type information for the
|
||||
// function. We want to pass the name alone to StartFunction.
|
||||
const char *stab_string = SymbolString();
|
||||
const char *name_end = strchr(stab_string, ':');
|
||||
if (! name_end)
|
||||
name_end = stab_string + strlen(stab_string);
|
||||
std::string name(stab_string, name_end - stab_string);
|
||||
if (! handler_->StartFunction(name, function_address))
|
||||
return false;
|
||||
symbol_++;
|
||||
|
||||
while (symbol_ < symbols_end_) {
|
||||
if (symbol_->n_type == N_SO || symbol_->n_type == N_FUN)
|
||||
break;
|
||||
else if (symbol_->n_type == N_SLINE) {
|
||||
// The value of an N_SLINE entry is the offset of the line from
|
||||
// the function's start address.
|
||||
uint64_t line_address = function_address + SymbolValue();
|
||||
// The n_desc of a N_SLINE entry is the line number. It's a
|
||||
// signed 16-bit field; line numbers from 32768 to 65535 are
|
||||
// stored as n-65536.
|
||||
uint16_t line_number = symbol_->n_desc;
|
||||
if (! handler_->Line(line_address, current_source_file_, line_number))
|
||||
return false;
|
||||
symbol_++;
|
||||
} else if (symbol_->n_type == N_SOL) {
|
||||
current_source_file_ = SymbolString();
|
||||
symbol_++;
|
||||
} else
|
||||
// Ignore anything else.
|
||||
symbol_++;
|
||||
}
|
||||
|
||||
// If there is a subsequent N_SO or N_FUN entry, its address is our
|
||||
// end address.
|
||||
uint64_t ending_address = 0;
|
||||
if (symbol_ < symbols_end_) {
|
||||
assert(symbol_->n_type == N_SO || symbol_->n_type == N_FUN);
|
||||
ending_address = SymbolValue();
|
||||
// Note: we do not increment symbol_ here, since we haven't consumed it.
|
||||
}
|
||||
|
||||
if (! handler_->EndFunction(ending_address))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
@@ -1,206 +0,0 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// stabs_reader.h: Define StabsReader, a parser for STABS debugging
|
||||
// information. A description of the STABS debugging format can be
|
||||
// found at:
|
||||
//
|
||||
// http://sourceware.org/gdb/current/onlinedocs/stabs_toc.html
|
||||
//
|
||||
// The comments here assume you understand the format.
|
||||
//
|
||||
// This parser assumes that the system's <a.out.h> and <stab.h>
|
||||
// headers accurately describe the layout of the STABS data; this code
|
||||
// will not parse STABS data for a system with a different address
|
||||
// size or endianness.
|
||||
|
||||
#ifndef COMMON_LINUX_STABS_READER_H__
|
||||
#define COMMON_LINUX_STABS_READER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstddef>
|
||||
#include <a.out.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class StabsHandler;
|
||||
|
||||
class StabsReader {
|
||||
public:
|
||||
// Create a reader for the STABS debug information whose .stab
|
||||
// section is the STAB_SIZE bytes at STAB, and whose .stabstr
|
||||
// section is the STABSTR_SIZE bytes at STABSTR. The reader will
|
||||
// call the member functions of HANDLER to report the information it
|
||||
// finds, when the reader's 'Process' member function is called.
|
||||
//
|
||||
// Note that, in ELF, the .stabstr section should be found using the
|
||||
// 'sh_link' field of the .stab section header, not by name.
|
||||
StabsReader(const uint8_t *stab, size_t stab_size,
|
||||
const uint8_t *stabstr, size_t stabstr_size,
|
||||
StabsHandler *handler);
|
||||
|
||||
// Process the STABS data, calling the handler's member functions to
|
||||
// report what we find. While the handler functions return true,
|
||||
// continue to process until we reach the end of the section. If we
|
||||
// processed the entire section and all handlers returned true,
|
||||
// return true. If any handler returned false, return false.
|
||||
bool Process();
|
||||
|
||||
private:
|
||||
// Return the name of the current symbol.
|
||||
const char *SymbolString();
|
||||
|
||||
// Return the value of the current symbol.
|
||||
const uint64_t SymbolValue() {
|
||||
return symbol_->n_value;
|
||||
}
|
||||
|
||||
// Process a compilation unit starting at symbol_. Return true
|
||||
// to continue processing, or false to abort.
|
||||
bool ProcessCompilationUnit();
|
||||
|
||||
// Process a function in current_source_file_ starting at symbol_.
|
||||
// Return true to continue processing, or false to abort.
|
||||
bool ProcessFunction();
|
||||
|
||||
// The debugging information we're reading.
|
||||
const struct nlist *symbols_, *symbols_end_;
|
||||
const uint8_t *stabstr_;
|
||||
size_t stabstr_size_;
|
||||
|
||||
StabsHandler *handler_;
|
||||
|
||||
// The offset of the current compilation unit's strings within stabstr_.
|
||||
size_t string_offset_;
|
||||
|
||||
// The value string_offset_ should have for the next compilation unit,
|
||||
// as established by N_UNDF entries.
|
||||
size_t next_cu_string_offset_;
|
||||
|
||||
// The current symbol we're processing.
|
||||
const struct nlist *symbol_;
|
||||
|
||||
// The current source file name.
|
||||
const char *current_source_file_;
|
||||
};
|
||||
|
||||
// Consumer-provided callback structure for the STABS reader. Clients
|
||||
// of the STABS reader provide an instance of this structure. The
|
||||
// reader then invokes the member functions of that instance to report
|
||||
// the information it finds.
|
||||
//
|
||||
// The default definitions of the member functions do nothing, and return
|
||||
// true so processing will continue.
|
||||
class StabsHandler {
|
||||
public:
|
||||
StabsHandler() { }
|
||||
virtual ~StabsHandler() { }
|
||||
|
||||
// Some general notes about the handler callback functions:
|
||||
|
||||
// Processing proceeds until the end of the .stabs section, or until
|
||||
// one of these functions returns false.
|
||||
|
||||
// The addresses given are as reported in the STABS info, without
|
||||
// regard for whether the module may be loaded at different
|
||||
// addresses at different times (a shared library, say). When
|
||||
// processing STABS from an ELF shared library, the addresses given
|
||||
// all assume the library is loaded at its nominal load address.
|
||||
// They are *not* offsets from the nominal load address. If you
|
||||
// want offsets, you must subtract off the library's nominal load
|
||||
// address.
|
||||
|
||||
// The arguments to these functions named FILENAME are all
|
||||
// references to strings stored in the .stabstr section. Because
|
||||
// both the Linux and Solaris linkers factor out duplicate strings
|
||||
// from the .stabstr section, the consumer can assume that if two
|
||||
// FILENAME values are different addresses, they represent different
|
||||
// file names.
|
||||
//
|
||||
// Thus, it's safe to use (say) std::map<char *, ...>, which does
|
||||
// string address comparisons, not string content comparisons.
|
||||
// Since all the strings are in same array of characters --- the
|
||||
// .stabstr section --- comparing their addresses produces
|
||||
// predictable, if not lexicographically meaningful, results.
|
||||
|
||||
// Begin processing a compilation unit whose main source file is
|
||||
// named FILENAME, and whose base address is ADDRESS. If
|
||||
// BUILD_DIRECTORY is non-NULL, it is the name of the build
|
||||
// directory in which the compilation occurred.
|
||||
virtual bool StartCompilationUnit(const char *filename, uint64_t address,
|
||||
const char *build_directory) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finish processing the compilation unit. If ADDRESS is non-zero,
|
||||
// it is the ending address of the compilation unit. If ADDRESS is
|
||||
// zero, then the compilation unit's ending address is not
|
||||
// available, and the consumer must infer it by other means.
|
||||
virtual bool EndCompilationUnit(uint64_t address) { return true; }
|
||||
|
||||
// Begin processing a function named NAME, whose starting address is
|
||||
// ADDRESS. This function belongs to the compilation unit that was
|
||||
// most recently started but not ended.
|
||||
//
|
||||
// Note that, unlike filenames, NAME is not a pointer into the
|
||||
// .stabstr section; this is because the name as it appears in the
|
||||
// STABS data is followed by type information. The value passed to
|
||||
// StartFunction is the function name alone.
|
||||
//
|
||||
// In languages that use name mangling, like C++, NAME is mangled.
|
||||
virtual bool StartFunction(const std::string &name, uint64_t address) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finish processing the function. If ADDRESS is non-zero, it is
|
||||
// the ending address for the function. If ADDRESS is zero, then
|
||||
// the function's ending address is not available, and the consumer
|
||||
// must infer it by other means.
|
||||
virtual bool EndFunction(uint64_t address) { return true; }
|
||||
|
||||
// Report that the code at ADDRESS is attributable to line NUMBER of
|
||||
// the source file named FILENAME. The caller must infer the ending
|
||||
// address of the line.
|
||||
virtual bool Line(uint64_t address, const char *filename, int number) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Report a warning. FORMAT is a printf-like format string,
|
||||
// specifying how to format the subsequent arguments.
|
||||
virtual void Warning(const char *format, ...) = 0;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_STABS_READER_H__
|
||||
@@ -1,685 +0,0 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// stabs_reader_unittest.cc: Unit tests for google_breakpad::StabsReader.
|
||||
|
||||
#include <a.out.h>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstdarg>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <stab.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/stabs_reader.h"
|
||||
|
||||
using std::istream;
|
||||
using std::istringstream;
|
||||
using std::map;
|
||||
using std::ostream;
|
||||
using std::ostringstream;
|
||||
using std::string;
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Eq;
|
||||
using ::testing::InSequence;
|
||||
using ::testing::Return;
|
||||
using ::testing::Sequence;
|
||||
using ::testing::StrEq;
|
||||
|
||||
using google_breakpad::StabsHandler;
|
||||
using google_breakpad::StabsReader;
|
||||
|
||||
namespace {
|
||||
|
||||
// Mock stabs file parser
|
||||
//
|
||||
// In order to test StabsReader, we parse a human-readable input file
|
||||
// describing STABS entries into in-memory .stab and .stabstr
|
||||
// sections, and then pass those to StabsReader to look at. The
|
||||
// human-readable file is called a "mock stabs file".
|
||||
//
|
||||
// Except for compilation unit boundary lines (described below), each
|
||||
// line of a mock stabs file should have the following form:
|
||||
//
|
||||
// TYPE OTHER DESC VALUE NAME
|
||||
//
|
||||
// where all data is Latin-1 bytes and fields are separated by single
|
||||
// space characters, except for NAME, which may contain spaces and
|
||||
// continues to the end of the line. The fields have the following
|
||||
// meanings:
|
||||
//
|
||||
// - TYPE: the name of the stabs symbol type; like SO or FUN. These are
|
||||
// the names from /usr/include/bits/stab.def, without the leading N_.
|
||||
//
|
||||
// - OTHER, DESC, VALUE: numeric values for the n_other, n_desc, and
|
||||
// n_value fields of the stab. These can be decimal or hex,
|
||||
// using C++ notation (10, 0x10)
|
||||
//
|
||||
// - NAME: textual data for the entry. STABS packs all kinds of
|
||||
// interesting data into entries' NAME fields, so calling it a NAME
|
||||
// is misleading, but that's how it is. For SO, this may be a
|
||||
// filename; for FUN, this is the function name, plus type data; and
|
||||
// so on.
|
||||
//
|
||||
// A compilation unit boundary line has the form:
|
||||
//
|
||||
// cu-boundary FILENAME
|
||||
|
||||
// I don't know if the whole parser/handler pattern is really worth
|
||||
// the bureaucracy in this case. But just writing it out as
|
||||
// old-fashioned functions wasn't astonishingly clear either, so it
|
||||
// seemed worth a try.
|
||||
|
||||
// A handler class for mock stabs data.
|
||||
class MockStabsHandler {
|
||||
public:
|
||||
MockStabsHandler() { }
|
||||
virtual ~MockStabsHandler() { }
|
||||
// The mock stabs parser calls this member function for each entry
|
||||
// it parses, passing it the contents of the entry. If this function
|
||||
// returns true, the parser continues; if it returns false, the parser
|
||||
// stops, and its Process member function returns false.
|
||||
virtual bool Entry(enum __stab_debug_code type, char other, short desc,
|
||||
unsigned long value, const string &name) { return true; }
|
||||
// Report a compilation unit boundary whose filename is FILENAME. As
|
||||
// for the Entry function, this should return true to continue
|
||||
// parsing, or false to stop processing.
|
||||
virtual bool CUBoundary(const string &filename) { return true; }
|
||||
|
||||
// Report an error in parsing the mock stabs data. If this returns true,
|
||||
// the parser continues; if it returns false, the parser stops and
|
||||
// its Process member function returns false.
|
||||
virtual bool Error(const char *format, ...) = 0;
|
||||
};
|
||||
|
||||
// A class for parsing mock stabs files.
|
||||
class MockStabsParser {
|
||||
public:
|
||||
// Create a parser reading input from STREAM and passing data to HANDLER.
|
||||
// Use FILENAME when reporting errors.
|
||||
MockStabsParser(const string &filename, istream *stream,
|
||||
MockStabsHandler *handler);
|
||||
// Parse data from the STREAM, invoking HANDLER->Entry for each
|
||||
// entry we get. Return true if we parsed all the data succesfully,
|
||||
// or false if we stopped early because Entry returned false, or if
|
||||
// there were any errors during parsing.
|
||||
bool Process();
|
||||
private:
|
||||
// A type for maps from stab type names ("SO", "SLINE", etc.) to
|
||||
// n_type values.
|
||||
typedef map<string, unsigned char> StabTypeNameTable;
|
||||
|
||||
// Initialize the table mapping STAB type names to n_type values.
|
||||
void InitializeTypeNames();
|
||||
|
||||
// Parse LINE, one line of input from a mock stabs file, and pass
|
||||
// its contents to handler_->Entry and return the boolean value that
|
||||
// returns. If we encounter an error parsing the line, report it
|
||||
// using handler->Error.
|
||||
bool ParseLine(const string &line);
|
||||
|
||||
const string &filename_;
|
||||
istream *stream_;
|
||||
MockStabsHandler *handler_;
|
||||
int line_number_;
|
||||
StabTypeNameTable type_names_;
|
||||
};
|
||||
|
||||
MockStabsParser::MockStabsParser(const string &filename, istream *stream,
|
||||
MockStabsHandler *handler):
|
||||
filename_(filename), stream_(stream), handler_(handler),
|
||||
line_number_(0) {
|
||||
InitializeTypeNames();
|
||||
}
|
||||
|
||||
bool MockStabsParser::Process() {
|
||||
// Iterate once per line, including a line at EOF without a
|
||||
// terminating newline.
|
||||
for(;;) {
|
||||
string line;
|
||||
getline(*stream_, line, '\n');
|
||||
if (line.empty() && stream_->eof())
|
||||
break;
|
||||
line_number_++;
|
||||
if (! ParseLine(line))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MockStabsParser::InitializeTypeNames() {
|
||||
// On GLIBC-based systems, <bits/stab.def> is a file containing a
|
||||
// call to an unspecified macro __define_stab for each stab type.
|
||||
// <stab.h> uses it to define the __stab_debug_code enum type. We
|
||||
// use it here to initialize our mapping from type names to enum
|
||||
// values.
|
||||
//
|
||||
// This isn't portable to non-GLIBC systems. Feel free to just
|
||||
// hard-code the values if this becomes a problem.
|
||||
# define __define_stab(name, code, str) type_names_[string(str)] = code;
|
||||
# include <bits/stab.def>
|
||||
# undef __define_stab
|
||||
}
|
||||
|
||||
bool MockStabsParser::ParseLine(const string &line) {
|
||||
istringstream linestream(line);
|
||||
// Allow "0x" prefix for hex, and so on.
|
||||
linestream.unsetf(istringstream::basefield);
|
||||
// Parse and validate the stabs type.
|
||||
string typeName;
|
||||
linestream >> typeName;
|
||||
if (typeName == "cu-boundary") {
|
||||
if (linestream.peek() == ' ')
|
||||
linestream.get();
|
||||
string filename;
|
||||
getline(linestream, filename, '\n');
|
||||
return handler_->CUBoundary(filename);
|
||||
} else {
|
||||
StabTypeNameTable::const_iterator typeIt = type_names_.find(typeName);
|
||||
if (typeIt == type_names_.end())
|
||||
return handler_->Error("%s:%d: unrecognized stab type: %s\n",
|
||||
filename_.c_str(), line_number_, typeName.c_str());
|
||||
// These are int, not char and unsigned char, to ensure they're parsed
|
||||
// as decimal numbers, not characters.
|
||||
int otherInt, descInt;
|
||||
unsigned long value;
|
||||
linestream >> otherInt >> descInt >> value;
|
||||
if (linestream.fail())
|
||||
return handler_->Error("%s:%d: malformed mock stabs input line\n",
|
||||
filename_.c_str(), line_number_);
|
||||
if (linestream.peek() == ' ')
|
||||
linestream.get();
|
||||
string name;
|
||||
getline(linestream, name, '\n');
|
||||
return handler_->Entry(static_cast<__stab_debug_code>(typeIt->second),
|
||||
otherInt, descInt, value, name);
|
||||
}
|
||||
}
|
||||
|
||||
// A class for constructing .stab sections.
|
||||
//
|
||||
// A .stab section is an array of struct nlist entries. These
|
||||
// entries' n_un.n_strx fields are indices into an accompanying
|
||||
// .stabstr section.
|
||||
class StabSection {
|
||||
public:
|
||||
StabSection(): used_(0), size_(1) {
|
||||
entries_ = (struct nlist *) malloc(sizeof(*entries_) * size_);
|
||||
}
|
||||
~StabSection() { free(entries_); }
|
||||
|
||||
// Append a new 'struct nlist' entry to the end of the section, and
|
||||
// return a pointer to it. This pointer is valid until the next
|
||||
// call to Append. The caller should initialize the returned entry
|
||||
// as needed.
|
||||
struct nlist *Append();
|
||||
// Set the first entry's n_desc field to COUNT, and set its n_value field
|
||||
// to STRING_SIZE.
|
||||
void SetHeader(short count, unsigned long string_size);
|
||||
// Set SECTION to the contents of a .stab section holding the
|
||||
// accumulated list of entries added with Append.
|
||||
void GetSection(string *section);
|
||||
// Clear the array, and prepare this StabSection to accumulate a fresh
|
||||
// set of entries.
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
// The array of stabs entries,
|
||||
struct nlist *entries_;
|
||||
// The number of elements of entries_ that are used, and the allocated size
|
||||
// of the array.
|
||||
size_t used_, size_;
|
||||
};
|
||||
|
||||
struct nlist *StabSection::Append() {
|
||||
if (used_ == size_) {
|
||||
size_ *= 2;
|
||||
entries_ = (struct nlist *) realloc(entries_, sizeof(*entries_) * size_);
|
||||
}
|
||||
assert(used_ < size_);
|
||||
return &entries_[used_++];
|
||||
}
|
||||
|
||||
void StabSection::SetHeader(short count, unsigned long string_size) {
|
||||
assert(used_ >= 1);
|
||||
entries_[0].n_desc = count;
|
||||
entries_[0].n_value = string_size;
|
||||
}
|
||||
|
||||
void StabSection::GetSection(string *section) {
|
||||
section->assign(reinterpret_cast<char *>(entries_),
|
||||
sizeof(*entries_) * used_);
|
||||
}
|
||||
|
||||
void StabSection::Clear() {
|
||||
used_ = 0;
|
||||
size_ = 1;
|
||||
entries_ = (struct nlist *) realloc(entries_, sizeof(*entries_) * size_);
|
||||
}
|
||||
|
||||
// A class for building .stabstr sections.
|
||||
//
|
||||
// A .stabstr section is an array of characters containing a bunch of
|
||||
// null-terminated strings. A string is identified by the index of
|
||||
// its initial character in the array. The array always starts with a
|
||||
// null byte, so that an index of zero refers to the empty string.
|
||||
//
|
||||
// This implementation also ensures that if two strings are equal, we
|
||||
// assign them the same indices; most linkers do this, and some
|
||||
// clients may rely upon it. (Note that this is not quite the same as
|
||||
// ensuring that a string only appears once in the section; you could
|
||||
// share space when one string is a suffix of another, but we don't.)
|
||||
class StabstrSection {
|
||||
public:
|
||||
StabstrSection(): next_byte_(1) { string_indices_[""] = 0; }
|
||||
// Ensure STR is present in the string section, and return its index.
|
||||
size_t Insert(const string &str);
|
||||
// Set SECTION to the contents of a .stabstr section in which the
|
||||
// strings passed to Insert appear at the indices we promised.
|
||||
void GetSection(string *section);
|
||||
// Clear the contents of this StabstrSection, and prepare it to
|
||||
// accumulate a new set of strings.
|
||||
void Clear();
|
||||
private:
|
||||
// Maps from strings to .stabstr indices and back.
|
||||
typedef map<string, size_t> StringToIndex;
|
||||
typedef map<size_t, const string *> IndexToString;
|
||||
|
||||
// A map from strings to the indices we've assigned them.
|
||||
StringToIndex string_indices_;
|
||||
|
||||
// The next unused byte in the section. The next string we add
|
||||
// will get this index.
|
||||
size_t next_byte_;
|
||||
};
|
||||
|
||||
size_t StabstrSection::Insert(const string &str) {
|
||||
StringToIndex::iterator it = string_indices_.find(str);
|
||||
size_t index;
|
||||
if (it != string_indices_.end()) {
|
||||
index = it->second;
|
||||
} else {
|
||||
// This is the first time we've seen STR; add it to the table.
|
||||
string_indices_[str] = next_byte_;
|
||||
index = next_byte_;
|
||||
next_byte_ += str.size() + 1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
void StabstrSection::GetSection(string *section) {
|
||||
// First we have to invert the map.
|
||||
IndexToString byIndex;
|
||||
for (StringToIndex::const_iterator it = string_indices_.begin();
|
||||
it != string_indices_.end(); it++)
|
||||
byIndex[it->second] = &it->first;
|
||||
// Now we build the .stabstr section.
|
||||
section->clear();
|
||||
for (IndexToString::const_iterator it = byIndex.begin();
|
||||
it != byIndex.end(); it++) {
|
||||
// Make sure we're actually assigning it the index we claim to be.
|
||||
assert(it->first == section->size());
|
||||
*section += *(it->second);
|
||||
*section += '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void StabstrSection::Clear() {
|
||||
string_indices_.clear();
|
||||
string_indices_[""] = 0;
|
||||
next_byte_ = 1;
|
||||
}
|
||||
|
||||
// A mock stabs parser handler class that builds .stab and .stabstr
|
||||
// sections.
|
||||
class StabsSectionsBuilder: public MockStabsHandler {
|
||||
public:
|
||||
// Construct a handler that will receive data from a MockStabsParser
|
||||
// and construct .stab and .stabstr sections. FILENAME should be
|
||||
// the name of the mock stabs input file; we use it in error
|
||||
// messages.
|
||||
StabsSectionsBuilder(const string &filename)
|
||||
: filename_(filename), error_count_(0), has_header_(false),
|
||||
entry_count_(0) { }
|
||||
|
||||
// Overridden virtual member functions.
|
||||
bool Entry(enum __stab_debug_code type, char other, short desc,
|
||||
unsigned long value, const string &name);
|
||||
bool CUBoundary(const string &filename);
|
||||
bool Error(const char *format, ...);
|
||||
|
||||
// Set SECTION to the contents of a .stab or .stabstr section
|
||||
// reflecting the entries that have been passed to us via Entry.
|
||||
void GetStab(string *section);
|
||||
void GetStabstr(string *section);
|
||||
|
||||
private:
|
||||
// Finish a compilation unit. If there are any entries accumulated in
|
||||
// stab_ and stabstr_, add them as a new compilation unit to
|
||||
// finished_cu_stabs_ and finished_cu_stabstr_, and then clear stab_ and
|
||||
// stabstr_.
|
||||
void FinishCU();
|
||||
|
||||
const string &filename_; // input filename, for error messages
|
||||
int error_count_; // number of errors we've seen so far
|
||||
|
||||
// The following members accumulate the contents of a single compilation
|
||||
// unit, possibly headed by an N_UNDF stab.
|
||||
bool has_header_; // true if we have an N_UNDF header
|
||||
int entry_count_; // the number of entries we've seen
|
||||
StabSection stab_; // 'struct nlist' entries
|
||||
StabstrSection stabstr_; // and the strings they love
|
||||
|
||||
// Accumulated .stab and .stabstr content for all compilation units.
|
||||
string finished_cu_stab_, finished_cu_stabstr_;
|
||||
};
|
||||
|
||||
bool StabsSectionsBuilder::Entry(enum __stab_debug_code type, char other,
|
||||
short desc, unsigned long value,
|
||||
const string &name) {
|
||||
struct nlist *entry = stab_.Append();
|
||||
entry->n_type = type;
|
||||
entry->n_other = other;
|
||||
entry->n_desc = desc;
|
||||
entry->n_value = value;
|
||||
entry->n_un.n_strx = stabstr_.Insert(name);
|
||||
entry_count_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StabsSectionsBuilder::CUBoundary(const string &filename) {
|
||||
FinishCU();
|
||||
// Add a header for the compilation unit.
|
||||
assert(!has_header_);
|
||||
assert(entry_count_ == 0);
|
||||
struct nlist *entry = stab_.Append();
|
||||
entry->n_type = N_UNDF;
|
||||
entry->n_other = 0;
|
||||
entry->n_desc = 0; // will be set to number of entries
|
||||
entry->n_value = 0; // will be set to size of .stabstr data
|
||||
entry->n_un.n_strx = stabstr_.Insert(filename);
|
||||
has_header_ = true;
|
||||
// The N_UNDF header isn't included in the symbol count, so we
|
||||
// shouldn't bump entry_count_ here.
|
||||
return true;
|
||||
}
|
||||
|
||||
void StabsSectionsBuilder::FinishCU() {
|
||||
if (entry_count_ > 0) {
|
||||
// Get the strings first, so we can record their size in the header.
|
||||
string stabstr;
|
||||
stabstr_.GetSection(&stabstr);
|
||||
finished_cu_stabstr_ += stabstr;
|
||||
|
||||
// Initialize our header, if we have one, and extract the .stab data.
|
||||
if (has_header_)
|
||||
stab_.SetHeader(entry_count_, stabstr.size());
|
||||
string stab;
|
||||
stab_.GetSection(&stab);
|
||||
finished_cu_stab_ += stab;
|
||||
}
|
||||
|
||||
stab_.Clear();
|
||||
stabstr_.Clear();
|
||||
has_header_ = false;
|
||||
entry_count_ = 0;
|
||||
}
|
||||
|
||||
bool StabsSectionsBuilder::Error(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
error_count_++;
|
||||
if (error_count_ >= 20) {
|
||||
fprintf(stderr,
|
||||
"%s: lots of errors; is this really a mock stabs file?\n",
|
||||
filename_.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void StabsSectionsBuilder::GetStab(string *section) {
|
||||
FinishCU();
|
||||
*section = finished_cu_stab_;
|
||||
}
|
||||
|
||||
void StabsSectionsBuilder::GetStabstr(string *section) {
|
||||
FinishCU();
|
||||
*section = finished_cu_stabstr_;
|
||||
}
|
||||
|
||||
class MockStabsReaderHandler: public StabsHandler {
|
||||
public:
|
||||
MOCK_METHOD3(StartCompilationUnit,
|
||||
bool(const char *, uint64_t, const char *));
|
||||
MOCK_METHOD1(EndCompilationUnit, bool(uint64_t));
|
||||
MOCK_METHOD2(StartFunction, bool(const std::string &, uint64_t));
|
||||
MOCK_METHOD1(EndFunction, bool(uint64_t));
|
||||
MOCK_METHOD3(Line, bool(uint64_t, const char *, int));
|
||||
void Warning(const char *format, ...) { MockWarning(format); }
|
||||
MOCK_METHOD1(MockWarning, void(const char *));
|
||||
};
|
||||
|
||||
// Create a StabsReader to parse the mock stabs data in INPUT_FILE,
|
||||
// passing the parsed information to HANDLER. If all goes well, return
|
||||
// the result of calling the reader's Process member function.
|
||||
// Otherwise, return false. INPUT_FILE should be relative to the top
|
||||
// of the source tree.
|
||||
static bool ApplyHandlerToMockStabsData(StabsHandler *handler,
|
||||
const string &input_file) {
|
||||
string full_input_file
|
||||
= string(getenv("srcdir") ? getenv("srcdir") : ".") + "/" + input_file;
|
||||
|
||||
// Open the input file.
|
||||
std::ifstream stream(full_input_file.c_str());
|
||||
if (stream.fail()) {
|
||||
fprintf(stderr, "error opening mock stabs input file %s: %s\n",
|
||||
full_input_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the mock stabs data, and produce stabs sections to use as
|
||||
// test input to the reader.
|
||||
StabsSectionsBuilder builder(full_input_file);
|
||||
MockStabsParser mock_parser(full_input_file, &stream, &builder);
|
||||
if (!mock_parser.Process())
|
||||
return false;
|
||||
string stab, stabstr;
|
||||
builder.GetStab(&stab);
|
||||
builder.GetStabstr(&stabstr);
|
||||
|
||||
// Run the parser on the test input, passing whatever we find to HANDLER.
|
||||
StabsReader reader(
|
||||
reinterpret_cast<const uint8_t *>(stab.data()), stab.size(),
|
||||
reinterpret_cast<const uint8_t *>(stabstr.data()), stabstr.size(),
|
||||
handler);
|
||||
return reader.Process();
|
||||
}
|
||||
|
||||
TEST(StabsReader, MockStabsInput) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file1.c"),
|
||||
0x42, StrEq("builddir1/")))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, StartFunction(StrEq("fun1"), 0x62))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, Line(0xe4, StrEq("file1.c"), 91))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, Line(0x164, StrEq("header.h"), 111))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndFunction(0x112))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, StartFunction(StrEq("fun2"), 0x112))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, Line(0x234, StrEq("header.h"), 131))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, Line(0x254, StrEq("file1.c"), 151))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndFunction(0x152))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndCompilationUnit(0x152))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file3.c"),
|
||||
0x182, NULL))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndCompilationUnit(0x192))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(ApplyHandlerToMockStabsData(
|
||||
&mock_handler,
|
||||
"common/linux/testdata/stabs_reader_unittest.input1"));
|
||||
}
|
||||
|
||||
TEST(StabsReader, AbruptCU) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(mock_handler,
|
||||
StartCompilationUnit(StrEq("file2-1.c"), 0x12, NULL))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndCompilationUnit(NULL))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(ApplyHandlerToMockStabsData(
|
||||
&mock_handler,
|
||||
"common/linux/testdata/stabs_reader_unittest.input2"));
|
||||
}
|
||||
|
||||
TEST(StabsReader, AbruptFunction) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(mock_handler,
|
||||
StartCompilationUnit(StrEq("file3-1.c"), 0x12, NULL))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, StartFunction(StrEq("fun3_1"), 0x22))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndFunction(NULL))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndCompilationUnit(NULL))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(ApplyHandlerToMockStabsData(
|
||||
&mock_handler,
|
||||
"common/linux/testdata/stabs_reader_unittest.input3"));
|
||||
}
|
||||
|
||||
TEST(StabsReader, NoCU) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
EXPECT_CALL(mock_handler, StartCompilationUnit(_, _, _))
|
||||
.Times(0);
|
||||
EXPECT_CALL(mock_handler, StartFunction(_, _))
|
||||
.Times(0);
|
||||
|
||||
ASSERT_TRUE(ApplyHandlerToMockStabsData(
|
||||
&mock_handler,
|
||||
"common/linux/testdata/stabs_reader_unittest.input4"));
|
||||
|
||||
}
|
||||
|
||||
TEST(StabsReader, NoCUEnd) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(mock_handler,
|
||||
StartCompilationUnit(StrEq("file5-1.c"), 0x12, NULL))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndCompilationUnit(NULL))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler,
|
||||
StartCompilationUnit(StrEq("file5-2.c"), 0x22, NULL))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndCompilationUnit(NULL))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(ApplyHandlerToMockStabsData(
|
||||
&mock_handler,
|
||||
"common/linux/testdata/stabs_reader_unittest.input5"));
|
||||
|
||||
}
|
||||
|
||||
TEST(StabsReader, MultipleCUs) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
{
|
||||
InSequence s;
|
||||
EXPECT_CALL(mock_handler,
|
||||
StartCompilationUnit(StrEq("antimony"), 0x12, NULL))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, StartFunction(Eq("arsenic"), 0x22))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndFunction(0x32))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndCompilationUnit(0x32))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler,
|
||||
StartCompilationUnit(StrEq("aluminum"), 0x42, NULL))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, StartFunction(Eq("selenium"), 0x52))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndFunction(0x62))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndCompilationUnit(0x62))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(ApplyHandlerToMockStabsData(
|
||||
&mock_handler,
|
||||
"common/linux/testdata/stabs_reader_unittest.input6"));
|
||||
}
|
||||
|
||||
// name duplication
|
||||
|
||||
} // anonymous namespace
|
||||
676
src/common/linux/testdata/func-line-pairing.h
vendored
676
src/common/linux/testdata/func-line-pairing.h
vendored
@@ -1,676 +0,0 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Test data for pairing functions and lines.
|
||||
//
|
||||
// For a pair of functions that are adjacent (10,20),(20,25) and a
|
||||
// pair that are not (10,15),(20,25), we include a test case for every
|
||||
// possible arrangement of two lines relative to those functions. We
|
||||
// include cases only for non-empty ranges, since empty functions and
|
||||
// lines are dropped before we do any pairing.
|
||||
//
|
||||
// Each test case is represented by a macro call of the form:
|
||||
//
|
||||
// PAIRING(func1_start, func1_end, func2_start, func2_end,
|
||||
// line1_start, line1_end, line2_start, line2_end,
|
||||
// func1_num_lines, func2_num_lines,
|
||||
// func1_line1_start, func1_line1_end,
|
||||
// func1_line2_start, func1_line2_end,
|
||||
// func2_line1_start, func2_line1_end,
|
||||
// func2_line2_start, func2_line2_end,
|
||||
// uncovered_funcs, uncovered_lines)
|
||||
//
|
||||
// where:
|
||||
// - funcN_{start,end} is the range of the N'th function
|
||||
// - lineN_{start,end} is the range of the N'th function
|
||||
// - funcN_num_lines is the number of source lines that should be
|
||||
// paired with the N'th function
|
||||
// - funcN_lineM_{start,end} is the range of the M'th line
|
||||
// paired with the N'th function, where 0,0 indicates that
|
||||
// there should be no such line paired
|
||||
// - uncovered_funcs is the number of functions with area that is
|
||||
// uncovered by any line, and
|
||||
// - uncovered_lines is the reverse.
|
||||
|
||||
// func1 func2 line1 line2 num pairing1 pairing2 uncovered
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #0
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #1
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #2
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #3
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #4
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #5
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #6
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #7
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #8
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #9
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #10
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #11
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #12
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #13
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #14
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #15
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #16
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #17
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #18
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #19
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #20
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #21
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #22
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #23
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #24
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #25
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #26
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #27
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #28
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #29
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #30
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #31
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #32
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #33
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #34
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #35
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #36
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #37
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #38
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #39
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #40
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #41
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #42
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #43
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #44
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #45
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #46
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #47
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #48
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #49
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #50
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 1) // #51
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 1) // #52
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #53
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 2) // #54
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #55
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 1) // #56
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 1) // #57
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #58
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 2) // #59
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #60
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #61
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #62
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #63
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #64
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #65
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #66
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #67
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #68
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #69
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #70
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 1) // #71
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #72
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 2) // #73
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #74
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #75
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 1) // #76
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #77
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 2) // #78
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 1) // #79
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #80
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 2) // #81
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #82
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #83
|
||||
PAIRING(10, 20, 20, 25, 6, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #84
|
||||
PAIRING(10, 20, 20, 25, 6, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #85
|
||||
PAIRING(10, 20, 20, 25, 6, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #86
|
||||
PAIRING(10, 20, 20, 25, 6, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #87
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #88
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 0) // #89
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 0) // #90
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 0) // #91
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #92
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #93
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 0) // #94
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 0) // #95
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 0) // #96
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #97
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #98
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #99
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #100
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #101
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #102
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #103
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #104
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #105
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 0) // #106
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 0) // #107
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #108
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 0) // #109
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 0) // #110
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #111
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #112
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #113
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 0) // #114
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 0) // #115
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #116
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 0) // #117
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 0) // #118
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #119
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #120
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #121
|
||||
PAIRING(10, 20, 20, 25, 10, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #122
|
||||
PAIRING(10, 20, 20, 25, 10, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #123
|
||||
PAIRING(10, 20, 20, 25, 10, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #124
|
||||
PAIRING(10, 20, 20, 25, 10, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #125
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #126
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 20, 0, 0, 0, 0, 2, 0) // #127
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 20, 20, 21, 0, 0, 2, 0) // #128
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 0) // #129
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 1) // #130
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #131
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 20, 0, 0, 0, 0, 2, 0) // #132
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 20, 20, 21, 0, 0, 2, 0) // #133
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 0) // #134
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 1) // #135
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #136
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #137
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #138
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #139
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #140
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #141
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #142
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #143
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 20, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 0) // #144
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 20, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 0) // #145
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 20, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #146
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 21, 22, 1, 1, 11, 20, 0, 0, 21, 22, 0, 0, 2, 0) // #147
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 21, 25, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 0) // #148
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 21, 26, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 1) // #149
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 25, 26, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #150
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 26, 27, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #151
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 21, 22, 1, 2, 11, 20, 0, 0, 20, 21, 21, 22, 2, 0) // #152
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 21, 25, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 0) // #153
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 21, 26, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 1) // #154
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 22, 23, 1, 2, 11, 20, 0, 0, 20, 21, 22, 23, 2, 0) // #155
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 22, 25, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 0) // #156
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 22, 26, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 1) // #157
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 25, 26, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #158
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 26, 27, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #159
|
||||
PAIRING(10, 20, 20, 25, 11, 25, 25, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #160
|
||||
PAIRING(10, 20, 20, 25, 11, 25, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #161
|
||||
PAIRING(10, 20, 20, 25, 11, 26, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #162
|
||||
PAIRING(10, 20, 20, 25, 11, 26, 27, 28, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #163
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #164
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #165
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #166
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #167
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #168
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #169
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #170
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #171
|
||||
PAIRING(10, 20, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #172
|
||||
PAIRING(10, 20, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #173
|
||||
PAIRING(10, 20, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #174
|
||||
PAIRING(10, 20, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #175
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #176
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #177
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #178
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #179
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #180
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #181
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #182
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #183
|
||||
PAIRING(10, 20, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #184
|
||||
PAIRING(10, 20, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #185
|
||||
PAIRING(10, 20, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #186
|
||||
PAIRING(10, 20, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #187
|
||||
PAIRING(10, 20, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #188
|
||||
PAIRING(10, 20, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #189
|
||||
PAIRING(10, 20, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #190
|
||||
PAIRING(10, 20, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #191
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #192
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #193
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #194
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #195
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #196
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #197
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #198
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #199
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #200
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #201
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #202
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #203
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #204
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #205
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #206
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #207
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #208
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #209
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #210
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #211
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #212
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #213
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #214
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #215
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #216
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #217
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #218
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #219
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #220
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #221
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #222
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #223
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #224
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #225
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #226
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #227
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #228
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #229
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #230
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #231
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #232
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #233
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #234
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #235
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #236
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #237
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #238
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #239
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #240
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #241
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #242
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #243
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #244
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #245
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #246
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #247
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #248
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #249
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #250
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #251
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #252
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #253
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #254
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #255
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #256
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #257
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #258
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #259
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #260
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #261
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #262
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #263
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #264
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #265
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #266
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #267
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #268
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #269
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #270
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #271
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #272
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #273
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #274
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #275
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 2) // #276
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #277
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 2) // #278
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #279
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #280
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #281
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #282
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 2) // #283
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #284
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 2) // #285
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #286
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #287
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #288
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #289
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #290
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #291
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #292
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #293
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #294
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #295
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #296
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #297
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #298
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #299
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #300
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #301
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #302
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #303
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #304
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #305
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #306
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #307
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #308
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #309
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #310
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #311
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #312
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #313
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #314
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #315
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #316
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #317
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #318
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #319
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #320
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #321
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #322
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #323
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #324
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #325
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #326
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #327
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #328
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #329
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #330
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #331
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #332
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #333
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #334
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #335
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #336
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #337
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #338
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #339
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #340
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #341
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #342
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #343
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #344
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #345
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #346
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #347
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #348
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #349
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #350
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #351
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #352
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #353
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #354
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #355
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #356
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #357
|
||||
PAIRING(10, 15, 20, 25, 6, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #358
|
||||
PAIRING(10, 15, 20, 25, 6, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #359
|
||||
PAIRING(10, 15, 20, 25, 6, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #360
|
||||
PAIRING(10, 15, 20, 25, 6, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #361
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #362
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #363
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #364
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #365
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 1) // #366
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #367
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #368
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #369
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #370
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #371
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #372
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 1) // #373
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #374
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #375
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #376
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #377
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #378
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #379
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #380
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #381
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #382
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #383
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #384
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #385
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #386
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #387
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #388
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #389
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #390
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #391
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #392
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #393
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #394
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #395
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #396
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #397
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #398
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #399
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #400
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #401
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #402
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #403
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #404
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #405
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #406
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #407
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #408
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #409
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #410
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #411
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #412
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #413
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #414
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #415
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #416
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #417
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #418
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #419
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #420
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #421
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #422
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #423
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #424
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #425
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #426
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #427
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #428
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #429
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #430
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #431
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #432
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #433
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #434
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #435
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #436
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #437
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #438
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #439
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #440
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #441
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #442
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #443
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #444
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #445
|
||||
PAIRING(10, 15, 20, 25, 10, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #446
|
||||
PAIRING(10, 15, 20, 25, 10, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #447
|
||||
PAIRING(10, 15, 20, 25, 10, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #448
|
||||
PAIRING(10, 15, 20, 25, 10, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #449
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #450
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 15, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #451
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 16, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 1) // #452
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #453
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 15, 20, 21, 0, 0, 2, 1) // #454
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #455
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #456
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #457
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 15, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #458
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 16, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 1) // #459
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #460
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 15, 20, 21, 0, 0, 2, 1) // #461
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #462
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #463
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 15, 16, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #464
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 15, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #465
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 15, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #466
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 15, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #467
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 15, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #468
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 16, 17, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #469
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 16, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #470
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 16, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #471
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 16, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #472
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 16, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #473
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #474
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #475
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #476
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #477
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #478
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #479
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #480
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #481
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 15, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #482
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 15, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #483
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 15, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #484
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 15, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #485
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 15, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #486
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #487
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #488
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #489
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #490
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #491
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #492
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #493
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #494
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #495
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #496
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #497
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #498
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #499
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #500
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #501
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #502
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #503
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #504
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 17, 18, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #505
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 17, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #506
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 17, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #507
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 17, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #508
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 17, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #509
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #510
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #511
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #512
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 1) // #513
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #514
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 2) // #515
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #516
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #517
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #518
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #519
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #520
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #521
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #522
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #523
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #524
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #525
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 21, 22, 1, 2, 11, 15, 0, 0, 20, 21, 21, 22, 2, 1) // #526
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 21, 25, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 1) // #527
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 21, 26, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 2) // #528
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 22, 23, 1, 2, 11, 15, 0, 0, 20, 21, 22, 23, 2, 1) // #529
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 22, 25, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 1) // #530
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 22, 26, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 2) // #531
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 25, 26, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #532
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 26, 27, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #533
|
||||
PAIRING(10, 15, 20, 25, 11, 25, 25, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #534
|
||||
PAIRING(10, 15, 20, 25, 11, 25, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #535
|
||||
PAIRING(10, 15, 20, 25, 11, 26, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #536
|
||||
PAIRING(10, 15, 20, 25, 11, 26, 27, 28, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #537
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #538
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #539
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #540
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #541
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #542
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #543
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #544
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #545
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #546
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #547
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #548
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #549
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #550
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #551
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #552
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #553
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #554
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #555
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #556
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #557
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #558
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #559
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #560
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #561
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #562
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #563
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #564
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #565
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #566
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #567
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #568
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #569
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #570
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #571
|
||||
PAIRING(10, 15, 20, 25, 15, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #572
|
||||
PAIRING(10, 15, 20, 25, 15, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #573
|
||||
PAIRING(10, 15, 20, 25, 15, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #574
|
||||
PAIRING(10, 15, 20, 25, 15, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #575
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #576
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #577
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #578
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #579
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #580
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #581
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 18, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #582
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 18, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #583
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 18, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #584
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 18, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #585
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #586
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #587
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #588
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #589
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #590
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #591
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #592
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #593
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #594
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #595
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #596
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #597
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #598
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #599
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #600
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #601
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #602
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #603
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #604
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #605
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #606
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #607
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #608
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #609
|
||||
PAIRING(10, 15, 20, 25, 16, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #610
|
||||
PAIRING(10, 15, 20, 25, 16, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #611
|
||||
PAIRING(10, 15, 20, 25, 16, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #612
|
||||
PAIRING(10, 15, 20, 25, 16, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #613
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #614
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #615
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #616
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #617
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #618
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #619
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #620
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #621
|
||||
PAIRING(10, 15, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #622
|
||||
PAIRING(10, 15, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #623
|
||||
PAIRING(10, 15, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #624
|
||||
PAIRING(10, 15, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #625
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #626
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #627
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #628
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #629
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #630
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #631
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #632
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #633
|
||||
PAIRING(10, 15, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #634
|
||||
PAIRING(10, 15, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #635
|
||||
PAIRING(10, 15, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #636
|
||||
PAIRING(10, 15, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #637
|
||||
PAIRING(10, 15, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #638
|
||||
PAIRING(10, 15, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #639
|
||||
PAIRING(10, 15, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #640
|
||||
PAIRING(10, 15, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #641
|
||||
@@ -1,19 +0,0 @@
|
||||
SO 10 11 0x02 builddir/
|
||||
FUN 20 21 0x12 not the SO with source file name we expected
|
||||
SO 30 31 0x22
|
||||
SO 40 41 0x32 builddir1/
|
||||
SO 50 51 0x42 file1.c
|
||||
LSYM 60 61 0x52 not the FUN we're looking for
|
||||
FUN 70 71 0x62 fun1
|
||||
BINCL 80 81 0x72 something to ignore in a FUN body
|
||||
SLINE 90 91 0x82
|
||||
SOL 100 101 0x92 header.h
|
||||
SLINE 110 111 0x102
|
||||
FUN 120 121 0x112 fun2:some stabs type info here, to trim from the name
|
||||
SLINE 130 131 0x122
|
||||
SOL 140 141 0x132 file1.c
|
||||
SLINE 150 151 0x142
|
||||
SO 160 161 0x152
|
||||
LSYM 170 171 0x162
|
||||
SO 180 181 0x182 file3.c
|
||||
SO 190 191 0x192
|
||||
@@ -1 +0,0 @@
|
||||
SO 10 11 0x12 file2-1.c
|
||||
@@ -1,2 +0,0 @@
|
||||
SO 10 11 0x12 file3-1.c
|
||||
FUN 20 21 0x22 fun3_1
|
||||
@@ -1 +0,0 @@
|
||||
SO 10 11 0x12 build-directory/
|
||||
@@ -1,2 +0,0 @@
|
||||
SO 10 11 0x12 file5-1.c
|
||||
SO 20 21 0x22 file5-2.c
|
||||
@@ -1,8 +0,0 @@
|
||||
cu-boundary antimony
|
||||
SO 10 11 0x12 antimony
|
||||
FUN 20 21 0x22 arsenic
|
||||
SO 30 31 0x32
|
||||
cu-boundary aluminum
|
||||
SO 40 41 0x42 aluminum
|
||||
FUN 50 51 0x52 selenium
|
||||
SO 60 61 0x62
|
||||
Reference in New Issue
Block a user