diff options
author | bryner <bryner@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2006-08-25 21:14:45 +0000 |
---|---|---|
committer | bryner <bryner@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2006-08-25 21:14:45 +0000 |
commit | cb91a2f879250f2ef5f74321b5d08807247d41a7 (patch) | |
tree | 6eebf88acb8342b6c27d9c29df78f92cbb035c4d /src/processor | |
parent | Adding a test html file (diff) | |
download | breakpad-cb91a2f879250f2ef5f74321b5d08807247d41a7.tar.xz |
Initial import, which includes the Windows client-side dump_syms tool, and
part of the server-side dump processor.
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@4 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/processor')
-rw-r--r-- | src/processor/source_line_resolver.cc | 275 | ||||
-rw-r--r-- | src/processor/source_line_resolver.h | 88 | ||||
-rw-r--r-- | src/processor/source_line_resolver_unittest.cc | 86 | ||||
-rw-r--r-- | src/processor/testdata/module1.out | 12 | ||||
-rw-r--r-- | src/processor/testdata/module2.out | 12 | ||||
-rw-r--r-- | src/processor/testdata/module3_bad.out | 2 |
6 files changed, 475 insertions, 0 deletions
diff --git a/src/processor/source_line_resolver.cc b/src/processor/source_line_resolver.cc new file mode 100644 index 00000000..a6e87e7b --- /dev/null +++ b/src/processor/source_line_resolver.cc @@ -0,0 +1,275 @@ +// Copyright (C) 2006 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <stdio.h> +#include <map> +#include <string.h> +#include <vector> +#include <utility> +#include "source_line_resolver.h" + +using STL_NAMESPACE::map; +using STL_NAMESPACE::vector; +using STL_NAMESPACE::make_pair; +using __gnu_cxx::hash; + +_START_GOOGLE_NAMESPACE_ + +void SourceLineResolver::SourceLineInfo::Reset() { + function_name.clear(); + source_file.clear(); + source_line = 0; +} + +// MemAddrMap is a map subclass which has the following properties: +// - stores pointers to an "entry" type, which are deleted on destruction +// - suitable for address lookup via FindContainingEntry + +template<class T> +class SourceLineResolver::MemAddrMap : public map<MemAddr, T*> { + public: + ~MemAddrMap(); + + // Find the entry which "contains" a given relative address, that is, + // the entry with the highest address not greater than the given address. + // Returns NULL if there is no such entry. + T* FindContainingEntry(MemAddr address) const; + + private: + typedef map<MemAddr, T*> MapType; +}; + +template<class T> +SourceLineResolver::MemAddrMap<T>::~MemAddrMap() { + typename MapType::iterator it; + for (it = MapType::begin(); it != MapType::end(); ++it) { + delete it->second; + } +} + +template<class T> +T* SourceLineResolver::MemAddrMap<T>::FindContainingEntry( + MemAddr address) const { + typename MapType::const_iterator it = MapType::lower_bound(address); + if (it->first != address) { + if (it == MapType::begin()) { + // Nowhere to go, so no entry contains the address + return NULL; + } + --it; // back up to the entry before address + } + return it->second; +} + +struct SourceLineResolver::Line { + Line(MemAddr addr, int file_id, int source_line) + : address(addr), source_file_id(file_id), line(source_line) { } + + MemAddr address; + int source_file_id; + int line; +}; + +struct SourceLineResolver::Function { + Function(const string &function_name, MemAddr function_address) + : name(function_name), address(function_address) { } + + string name; + MemAddr address; + MemAddrMap<Line> lines; +}; + +class SourceLineResolver::Module { + public: + Module(const string &name) : name_(name) { } + + // Loads the given map file, returning true on success. + bool LoadMap(const string &map_file); + + // Looks up the given relative address, and fills the SourceLineInfo struct + // with the result. + void LookupAddress(MemAddr address, SourceLineInfo *info) const; + + private: + friend class SourceLineResolver; + typedef hash_map<int, string> FileMap; + + // Parses a file declaration + void ParseFile(char *file_line); + + // Parses a function declaration, returning a new Function object. + Function* ParseFunction(char *function_line); + + // Parses a line declaration, returning a new Line object. + Line* ParseLine(char *line_line); + + string name_; + FileMap files_; + MemAddrMap<Function> functions_; +}; + +SourceLineResolver::SourceLineResolver() : modules_(new ModuleMap) { +} + +SourceLineResolver::~SourceLineResolver() { + ModuleMap::iterator it; + for (it = modules_->begin(); it != modules_->end(); ++it) { + delete it->second; + } + delete modules_; +} + +bool SourceLineResolver::LoadModule(const string &module_name, + const string &map_file) { + // Make sure we don't already have a module with the given name. + if (modules_->find(module_name) != modules_->end()) { + return false; + } + + Module *module = new Module(module_name); + if (!module->LoadMap(map_file)) { + delete module; + return false; + } + + modules_->insert(make_pair(module_name, module)); + return true; +} + +void SourceLineResolver::LookupAddress(MemAddr address, + const string &module_name, + SourceLineInfo *info) const { + info->Reset(); + ModuleMap::const_iterator it = modules_->find(module_name); + if (it != modules_->end()) { + it->second->LookupAddress(address, info); + } +} + +bool SourceLineResolver::Module::LoadMap(const string &map_file) { + FILE *f = fopen(map_file.c_str(), "r"); + if (!f) { + return false; + } + + char buffer[1024]; + Function *cur_func = NULL; + + while (fgets(buffer, sizeof(buffer), f)) { + if (strncmp(buffer, "FILE ", 5) == 0) { + ParseFile(buffer); + } else if (strncmp(buffer, "FUNC ", 5) == 0) { + cur_func = ParseFunction(buffer); + if (!cur_func) { + return false; + } + functions_.insert(make_pair(cur_func->address, cur_func)); + } else { + if (!cur_func) { + return false; + } + Line *line = ParseLine(buffer); + if (!line) { + return false; + } + cur_func->lines.insert(make_pair(line->address, line)); + } + } + + fclose(f); + return true; +} + +void SourceLineResolver::Module::LookupAddress(MemAddr address, + SourceLineInfo *info) const { + Function *func = functions_.FindContainingEntry(address); + if (!func) { + return; + } + + info->function_name = func->name; + Line *line = func->lines.FindContainingEntry(address); + if (!line) { + return; + } + + FileMap::const_iterator it = files_.find(line->source_file_id); + if (it != files_.end()) { + info->source_file = files_.find(line->source_file_id)->second; + } + info->source_line = line->line; +} + +void SourceLineResolver::Module::ParseFile(char *file_line) { + // FILE <id> <filename> + file_line += 5; // skip prefix + char *id = strtok(file_line, " "); + if (!id) { + return; + } + + int index = atoi(id); + if (index < 0) { + return; + } + + char *filename = strtok(NULL, "\r\n"); + if (filename) { + files_.insert(make_pair(index, string(filename))); + } +} + +SourceLineResolver::Function* SourceLineResolver::Module::ParseFunction( + char *function_line) { + // FUNC <address> <name> + function_line += 5; // skip prefix + char *addr = strtok(function_line, " "); + if (!addr) { + return NULL; + } + + char *name = strtok(NULL, "\r\n"); + if (!name) { + return NULL; + } + + return new Function(name, strtoull(addr, NULL, 16)); +} + +SourceLineResolver::Line* SourceLineResolver::Module::ParseLine( + char *line_line) { + // <address> <line number> <source file id> + char *addr = strtok(line_line, " "); + if (!addr) { + return NULL; + } + + char *line_num_str = strtok(NULL, "\r\n"); + if (!line_num_str) { + return NULL; + } + + int line_number, source_file; + if (sscanf(line_num_str, "%d %d", &line_number, &source_file) != 2) { + return NULL; + } + + return new Line(strtoull(addr, NULL, 16), source_file, line_number); +} + +size_t SourceLineResolver::HashString::operator()(const string &s) const { + return hash<const char*>()(s.c_str()); +} + +_END_GOOGLE_NAMESPACE_ diff --git a/src/processor/source_line_resolver.h b/src/processor/source_line_resolver.h new file mode 100644 index 00000000..44325872 --- /dev/null +++ b/src/processor/source_line_resolver.h @@ -0,0 +1,88 @@ +// Copyright (C) 2006 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// SourceLineResolver returns function/file/line info for a memory address. +// It uses address map files produced by a compatible writer, e.g. +// PDBSourceLineWriter. + +#ifndef _SOURCE_LINE_RESOLVER_H__ +#define _SOURCE_LINE_RESOLVER_H__ + +#include "config.h" +#include <string> +#include <ext/hash_map> + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::string; +using __gnu_cxx::hash_map; + +class SourceLineResolver { + public: + typedef unsigned long long MemAddr; + + // A struct that gives source file information for a memory address. + struct SourceLineInfo { + // Resets all fields to their default empty values + void Reset(); + + // The function name, for example Foo::Foo() + string function_name; + + // The source file, for example C:\foo\bar.cc + string source_file; + + // The line number within the source file (1-based) + int source_line; + }; + + SourceLineResolver(); + ~SourceLineResolver(); + + // Adds a module to this resolver, returning true on success. + // + // module_name may be an arbitrary string. Typically, it will be the + // filename of the module, optionally with version identifiers. + // + // map_file should contain line/address mappings for this module. + bool LoadModule(const string &module_name, const string &map_file); + + // Determines the source line for the given address, and fills info + // with the result. module_name must match a module name that was + // passed to LoadModule(). The address should be module-relative. + void LookupAddress(MemAddr address, const string &module_name, + SourceLineInfo *info) const; + + private: + template<class T> class MemAddrMap; + struct Line; + struct Function; + struct File; + struct HashString { + size_t operator()(const string &s) const; + }; + class Module; + + // All of the modules we've loaded + typedef hash_map<string, Module*, HashString> ModuleMap; + ModuleMap *modules_; + + // Disallow unwanted copy ctor and assignment operator + SourceLineResolver(const SourceLineResolver&); + void operator=(const SourceLineResolver&); +}; + +_END_GOOGLE_NAMESPACE_ + +#endif // _SOLURCE_LINE_RESOLVER_H__ diff --git a/src/processor/source_line_resolver_unittest.cc b/src/processor/source_line_resolver_unittest.cc new file mode 100644 index 00000000..99f499df --- /dev/null +++ b/src/processor/source_line_resolver_unittest.cc @@ -0,0 +1,86 @@ +// Copyright (C) 2006 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <stdio.h> +#include <string> +#include "source_line_resolver.h" + +using STL_NAMESPACE::string; +using GOOGLE_NAMESPACE::SourceLineResolver; + +#define ASSERT_TRUE(cond) \ + if (!(cond)) { \ + fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \ + return false; \ + } + +#define ASSERT_FALSE(cond) ASSERT_TRUE(!(cond)) + +#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2)) + +static bool VerifyEmpty(const SourceLineResolver::SourceLineInfo &info) { + ASSERT_TRUE(info.function_name.empty()); + ASSERT_TRUE(info.source_file.empty()); + ASSERT_EQ(info.source_line, 0); + return true; +} + +static bool RunTests() { + string testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata"; + + SourceLineResolver resolver; + ASSERT_TRUE(resolver.LoadModule("module1", testdata_dir + "/module1.out")); + ASSERT_TRUE(resolver.LoadModule("module2", testdata_dir + "/module2.out")); + + SourceLineResolver::SourceLineInfo info; + resolver.LookupAddress(0x1000, "module1", &info); + ASSERT_EQ(info.function_name, "Function1_1"); + ASSERT_EQ(info.source_file, "file1_1.cc"); + ASSERT_EQ(info.source_line, 44); + + info.Reset(); + ASSERT_TRUE(VerifyEmpty(info)); + + resolver.LookupAddress(0x800, "module1", &info); + ASSERT_TRUE(VerifyEmpty(info)); + + resolver.LookupAddress(0x1280, "module1", &info); + ASSERT_EQ(info.function_name, "Function1_3"); + ASSERT_TRUE(info.source_file.empty()); + ASSERT_EQ(info.source_line, 0); + + resolver.LookupAddress(0x1380, "module1", &info); + ASSERT_EQ(info.function_name, "Function1_4"); + ASSERT_TRUE(info.source_file.empty()); + ASSERT_EQ(info.source_line, 0); + + resolver.LookupAddress(0x2180, "module2", &info); + ASSERT_EQ(info.function_name, "Function2_2"); + ASSERT_EQ(info.source_file, "file2_2.cc"); + ASSERT_EQ(info.source_line, 21); + + ASSERT_FALSE(resolver.LoadModule("module3", + testdata_dir + "/module3_bad.out")); + ASSERT_FALSE(resolver.LoadModule("module4", + testdata_dir + "/invalid-filename")); + return true; +} + +int main(int argc, char **argv) { + if (!RunTests()) { + return 1; + } + return 0; +} diff --git a/src/processor/testdata/module1.out b/src/processor/testdata/module1.out new file mode 100644 index 00000000..75d55e38 --- /dev/null +++ b/src/processor/testdata/module1.out @@ -0,0 +1,12 @@ +FILE 1 file1_1.cc +FILE 2 file1_2.cc +FILE 3 file1_3.cc +FUNC 1000 Function1_1 +1000 44 1 +1004 45 1 +1008 46 1 +FUNC 1100 Function1_2 +1100 65 2 +1104 66 2 +FUNC 1200 Function1_3 +FUNC 1300 Function1_4 diff --git a/src/processor/testdata/module2.out b/src/processor/testdata/module2.out new file mode 100644 index 00000000..762f91e2 --- /dev/null +++ b/src/processor/testdata/module2.out @@ -0,0 +1,12 @@ +FILE 1 file2_1.cc +FILE 2 file2_2.cc +FILE 3 file2_3.cc +FUNC 2000 Function2_1 +1000 54 1 +1004 55 1 +1008 56 1 +FUNC 2170 Function2_2 +2170 10 2 +2176 12 2 +217a 13 2 +2180 21 2 diff --git a/src/processor/testdata/module3_bad.out b/src/processor/testdata/module3_bad.out new file mode 100644 index 00000000..1b3f8e62 --- /dev/null +++ b/src/processor/testdata/module3_bad.out @@ -0,0 +1,2 @@ +FILE 1 file1.cc +FUNC 1000 |