mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-12-23 07:05:37 +00:00
Breakpad Linux dumper: Add unit tests for google_breakpad::Module.
Adjust Module's interface a bit to facilitate testing: - Make AssignSourceIds something a client can call --- it's perfectly well-defined, so this is an okay change. - Add GetFunctions, GetFiles and FindExistingfile member functions, which the test harness will use to get results to examine. a=jimblandy, r=nealsid git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@466 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
6ed5383245
commit
52cb2c6f42
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/linux/module.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
@ -62,6 +63,11 @@ void Module::AddFunctions(vector<Function *>::iterator begin,
|
|||
functions_.insert(functions_.end(), begin, end);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -90,6 +96,17 @@ Module::File *Module::FindFile(const char *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::AssignSourceIds() {
|
||||
// First, give every source file an id of -1.
|
||||
for (FileByNameMap::iterator file_it = files_.begin();
|
||||
|
@ -129,8 +146,9 @@ bool Module::Write(FILE *stream) {
|
|||
name_.c_str()))
|
||||
return ReportError();
|
||||
|
||||
// Write out files.
|
||||
AssignSourceIds();
|
||||
|
||||
// Write out files.
|
||||
for (FileByNameMap::iterator file_it = files_.begin();
|
||||
file_it != files_.end(); file_it++) {
|
||||
File *file = file_it->second;
|
||||
|
|
|
@ -140,9 +140,32 @@ class Module {
|
|||
File *FindFile(const string &name);
|
||||
File *FindFile(const char *name);
|
||||
|
||||
// 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:
|
||||
// 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. (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. (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 GetFiles(vector<File *> *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.
|
||||
|
@ -152,13 +175,6 @@ class Module {
|
|||
|
||||
private:
|
||||
|
||||
// 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();
|
||||
|
||||
// Report an error that has occurred writing the symbol file, using
|
||||
// errno to find the appropriate cause. Return false.
|
||||
static bool ReportError();
|
||||
|
|
286
src/common/linux/module_unittest.cc
Normal file
286
src/common/linux/module_unittest.cc
Normal file
|
@ -0,0 +1,286 @@
|
|||
// Copyright (c) 2009, 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.
|
||||
|
||||
// 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 std::string;
|
||||
using std::vector;
|
||||
using google_breakpad::Module;
|
||||
|
||||
// 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);
|
||||
|
||||
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",
|
||||
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, 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);
|
||||
}
|
|
@ -52,7 +52,8 @@ SRC = ../../..
|
|||
# dependencies using $(CXX). Value accumulated throughout the file.
|
||||
CPP_EXECUTABLES =
|
||||
|
||||
# Source files whose coverage we are interested in. Value accumulated
|
||||
# Add the names of source files whose coverage you'd like 'make
|
||||
# coverage' to report on to this variable. Value accumulated
|
||||
# throughout the file.
|
||||
COVERAGE_SOURCES =
|
||||
|
||||
|
@ -79,6 +80,7 @@ dump_stabs.o: dump_stabs.cc
|
|||
dump_symbols.o: dump_symbols.cc
|
||||
file_id.o: file_id.cc
|
||||
module.o: module.cc
|
||||
COVERAGE_SOURCES += module.cc
|
||||
stabs_reader.o: stabs_reader.cc
|
||||
COVERAGE_SOURCES += stabs_reader.cc
|
||||
|
||||
|
@ -132,6 +134,22 @@ clean::
|
|||
rm -f file_id_unittest
|
||||
|
||||
|
||||
### Unit tests for google_breakpad::Module.
|
||||
check: check-module_unittest
|
||||
check-module_unittest: module_unittest
|
||||
module_unittest: \
|
||||
gtest-all.o \
|
||||
gtest_main.o \
|
||||
module.o \
|
||||
module_unittest.o \
|
||||
$(empty)
|
||||
CPP_EXECUTABLES += module_unittest
|
||||
module_unittest.o: module_unittest.cc
|
||||
module_unittest.o: override CPPFLAGS += $(GTEST_CPPFLAGS) $(GMOCK_CPPFLAGS)
|
||||
clean::
|
||||
rm -f module_unittest
|
||||
|
||||
|
||||
### Generic compilation rules.
|
||||
|
||||
# Link C++ executables using the C++ compiler; see CPP_EXECUTABLES above.
|
||||
|
|
Loading…
Reference in a new issue