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:
jimblandy
2010-04-05 19:40:17 +00:00
parent 9e6b619ad0
commit 87855248f1
29 changed files with 38 additions and 36 deletions

View File

@@ -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

View File

@@ -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__

View File

@@ -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

View File

@@ -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 {

View File

@@ -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 &reg) {
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 &reg) {
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

View File

@@ -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 &section)
: 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 &reg);
// 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 &reg);
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> &register_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> &register_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

View File

@@ -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 &section)
: Reporter(file, section) { }
MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg));
MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string &reg));
MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string &reg));
};
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));
}

View File

@@ -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 &section_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

View File

@@ -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 &section_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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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__

View File

@@ -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

View File

@@ -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__

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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__

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1 +0,0 @@
SO 10 11 0x12 file2-1.c

View File

@@ -1,2 +0,0 @@
SO 10 11 0x12 file3-1.c
FUN 20 21 0x22 fun3_1

View File

@@ -1 +0,0 @@
SO 10 11 0x12 build-directory/

View File

@@ -1,2 +0,0 @@
SO 10 11 0x12 file5-1.c
SO 20 21 0x22 file5-2.c

View File

@@ -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