mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2025-12-24 00:04:58 +01:00
Refactor source line resolver, add interface in supplier and resolver.
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@711 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
d35f113d02
commit
5b117cf53a
24 changed files with 1021 additions and 418 deletions
|
|
@ -26,6 +26,12 @@
|
|||
// 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.
|
||||
//
|
||||
// basic_source_line_resolver.cc: BasicSourceLineResolver implementation.
|
||||
//
|
||||
// See basic_source_line_resolver.h and basic_source_line_resolver_types.h
|
||||
// for documentation.
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
|
@ -37,17 +43,10 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "processor/address_map-inl.h"
|
||||
#include "processor/contained_range_map-inl.h"
|
||||
#include "processor/range_map-inl.h"
|
||||
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/windows_frame_info.h"
|
||||
#include "processor/basic_source_line_resolver_types.h"
|
||||
#include "processor/module_factory.h"
|
||||
|
||||
#include "processor/tokenize.h"
|
||||
|
||||
using std::map;
|
||||
|
|
@ -58,299 +57,33 @@ namespace google_breakpad {
|
|||
|
||||
static const char *kWhitespace = " \r\n";
|
||||
|
||||
struct BasicSourceLineResolver::Line {
|
||||
Line(MemAddr addr, MemAddr code_size, int file_id, int source_line)
|
||||
: address(addr)
|
||||
, size(code_size)
|
||||
, source_file_id(file_id)
|
||||
, line(source_line) { }
|
||||
BasicSourceLineResolver::BasicSourceLineResolver() :
|
||||
SourceLineResolverBase(new BasicModuleFactory) { }
|
||||
|
||||
MemAddr address;
|
||||
MemAddr size;
|
||||
int source_file_id;
|
||||
int line;
|
||||
};
|
||||
|
||||
struct BasicSourceLineResolver::Function {
|
||||
Function(const string &function_name,
|
||||
MemAddr function_address,
|
||||
MemAddr code_size,
|
||||
int set_parameter_size)
|
||||
: name(function_name), address(function_address), size(code_size),
|
||||
parameter_size(set_parameter_size) { }
|
||||
|
||||
string name;
|
||||
MemAddr address;
|
||||
MemAddr size;
|
||||
|
||||
// The size of parameters passed to this function on the stack.
|
||||
int parameter_size;
|
||||
|
||||
RangeMap< MemAddr, linked_ptr<Line> > lines;
|
||||
};
|
||||
|
||||
struct BasicSourceLineResolver::PublicSymbol {
|
||||
PublicSymbol(const string& set_name,
|
||||
MemAddr set_address,
|
||||
int set_parameter_size)
|
||||
: name(set_name),
|
||||
address(set_address),
|
||||
parameter_size(set_parameter_size) {}
|
||||
|
||||
string name;
|
||||
MemAddr address;
|
||||
|
||||
// If the public symbol is used as a function entry point, parameter_size
|
||||
// is set to the size of the parameters passed to the funciton on the
|
||||
// stack, if known.
|
||||
int parameter_size;
|
||||
};
|
||||
|
||||
class BasicSourceLineResolver::Module {
|
||||
public:
|
||||
Module(const string &name) : name_(name) { }
|
||||
|
||||
// Loads the given map file, returning true on success. Reads the
|
||||
// map file into memory and calls LoadMapFromBuffer
|
||||
bool LoadMap(const string &map_file);
|
||||
|
||||
// Loads a map from the given buffer, returning true on success
|
||||
bool LoadMapFromBuffer(const string &map_buffer);
|
||||
|
||||
// Looks up the given relative address, and fills the StackFrame struct
|
||||
// with the result.
|
||||
void LookupAddress(StackFrame *frame) const;
|
||||
|
||||
// If Windows stack walking information is available covering ADDRESS,
|
||||
// return a WindowsFrameInfo structure describing it. If the information
|
||||
// is not available, returns NULL. A NULL return value does not indicate
|
||||
// an error. The caller takes ownership of any returned WindowsFrameInfo
|
||||
// object.
|
||||
WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const;
|
||||
|
||||
// If CFI stack walking information is available covering ADDRESS,
|
||||
// return a CFIFrameInfo structure describing it. If the information
|
||||
// is not available, return NULL. The caller takes ownership of any
|
||||
// returned CFIFrameInfo object.
|
||||
CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const;
|
||||
|
||||
private:
|
||||
friend class BasicSourceLineResolver;
|
||||
typedef map<int, string> FileMap;
|
||||
|
||||
// Parses a file declaration
|
||||
bool 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);
|
||||
|
||||
// Parses a PUBLIC symbol declaration, storing it in public_symbols_.
|
||||
// Returns false if an error occurs.
|
||||
bool ParsePublicSymbol(char *public_line);
|
||||
|
||||
// Parses a STACK WIN or STACK CFI frame info declaration, storing
|
||||
// it in the appropriate table.
|
||||
bool ParseStackInfo(char *stack_info_line);
|
||||
|
||||
// Parses a STACK CFI record, storing it in cfi_frame_info_.
|
||||
bool ParseCFIFrameInfo(char *stack_info_line);
|
||||
|
||||
// Parse RULE_SET, a series of rules of the sort appearing in STACK
|
||||
// CFI records, and store the given rules in FRAME_INFO.
|
||||
bool ParseCFIRuleSet(const string &rule_set, CFIFrameInfo *frame_info) const;
|
||||
|
||||
string name_;
|
||||
FileMap files_;
|
||||
RangeMap< MemAddr, linked_ptr<Function> > functions_;
|
||||
AddressMap< MemAddr, linked_ptr<PublicSymbol> > public_symbols_;
|
||||
|
||||
// Each element in the array is a ContainedRangeMap for a type
|
||||
// listed in WindowsFrameInfoTypes. These are split by type because
|
||||
// there may be overlaps between maps of different types, but some
|
||||
// information is only available as certain types.
|
||||
ContainedRangeMap< MemAddr, linked_ptr<WindowsFrameInfo> >
|
||||
windows_frame_info_[WindowsFrameInfo::STACK_INFO_LAST];
|
||||
|
||||
// DWARF CFI stack walking data. The Module stores the initial rule sets
|
||||
// and rule deltas as strings, just as they appear in the symbol file:
|
||||
// although the file may contain hundreds of thousands of STACK CFI
|
||||
// records, walking a stack will only ever use a few of them, so it's
|
||||
// best to delay parsing a record until it's actually needed.
|
||||
|
||||
// STACK CFI INIT records: for each range, an initial set of register
|
||||
// recovery rules. The RangeMap's itself gives the starting and ending
|
||||
// addresses.
|
||||
RangeMap<MemAddr, string> cfi_initial_rules_;
|
||||
|
||||
// STACK CFI records: at a given address, the changes to the register
|
||||
// recovery rules that take effect at that address. The map key is the
|
||||
// starting address; the ending address is the key of the next entry in
|
||||
// this map, or the end of the range as given by the cfi_initial_rules_
|
||||
// entry (which FindCFIFrameInfo looks up first).
|
||||
map<MemAddr, string> cfi_delta_rules_;
|
||||
};
|
||||
|
||||
BasicSourceLineResolver::BasicSourceLineResolver() : modules_(new ModuleMap) {
|
||||
void BasicSourceLineResolver::DeleteDataAfterLoad(char *symbol_data) {
|
||||
// Always delete allocated memory after loading symbol.
|
||||
delete symbol_data;
|
||||
}
|
||||
|
||||
BasicSourceLineResolver::~BasicSourceLineResolver() {
|
||||
ModuleMap::iterator it;
|
||||
for (it = modules_->begin(); it != modules_->end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
delete modules_;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::LoadModule(const CodeModule *module,
|
||||
const string &map_file) {
|
||||
if (module == NULL)
|
||||
return false;
|
||||
|
||||
// Make sure we don't already have a module with the given name.
|
||||
if (modules_->find(module->code_file()) != modules_->end()) {
|
||||
BPLOG(INFO) << "Symbols for module " << module->code_file()
|
||||
<< " already loaded";
|
||||
return false;
|
||||
}
|
||||
|
||||
BPLOG(INFO) << "Loading symbols for module " << module->code_file()
|
||||
<< " from " << map_file;
|
||||
|
||||
Module *basic_module = new Module(module->code_file());
|
||||
if (!basic_module->LoadMap(map_file)) {
|
||||
delete basic_module;
|
||||
return false;
|
||||
}
|
||||
|
||||
modules_->insert(make_pair(module->code_file(), basic_module));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::LoadModuleUsingMapBuffer(
|
||||
const CodeModule *module,
|
||||
const string &map_buffer) {
|
||||
if (!module)
|
||||
return false;
|
||||
|
||||
// Make sure we don't already have a module with the given name.
|
||||
if (modules_->find(module->code_file()) != modules_->end()) {
|
||||
BPLOG(INFO) << "Symbols for module " << module->code_file()
|
||||
<< " already loaded";
|
||||
return false;
|
||||
}
|
||||
|
||||
BPLOG(INFO) << "Loading symbols for module " << module->code_file()
|
||||
<< " from buffer";
|
||||
|
||||
Module *basic_module = new Module(module->code_file());
|
||||
if (!basic_module->LoadMapFromBuffer(map_buffer)) {
|
||||
delete basic_module;
|
||||
return false;
|
||||
}
|
||||
|
||||
modules_->insert(make_pair(module->code_file(), basic_module));
|
||||
return true;
|
||||
}
|
||||
|
||||
void BasicSourceLineResolver::UnloadModule(const CodeModule *module)
|
||||
{
|
||||
if (!module)
|
||||
return;
|
||||
|
||||
ModuleMap::iterator iter = modules_->find(module->code_file());
|
||||
if (iter != modules_->end()) {
|
||||
Module *basic_module = iter->second;
|
||||
modules_->erase(iter);
|
||||
delete basic_module;
|
||||
}
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::HasModule(const CodeModule *module) {
|
||||
if (!module)
|
||||
return false;
|
||||
return modules_->find(module->code_file()) != modules_->end();
|
||||
}
|
||||
|
||||
void BasicSourceLineResolver::FillSourceLineInfo(StackFrame *frame) {
|
||||
if (frame->module) {
|
||||
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
|
||||
if (it != modules_->end()) {
|
||||
it->second->LookupAddress(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WindowsFrameInfo *BasicSourceLineResolver::FindWindowsFrameInfo(
|
||||
const StackFrame *frame) {
|
||||
if (frame->module) {
|
||||
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
|
||||
if (it != modules_->end()) {
|
||||
return it->second->FindWindowsFrameInfo(frame);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFIFrameInfo *BasicSourceLineResolver::FindCFIFrameInfo(
|
||||
const StackFrame *frame) {
|
||||
if (frame->module) {
|
||||
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
|
||||
if (it != modules_->end()) {
|
||||
return it->second->FindCFIFrameInfo(frame);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
class AutoFileCloser {
|
||||
public:
|
||||
AutoFileCloser(FILE *file) : file_(file) {}
|
||||
~AutoFileCloser() {
|
||||
if (file_)
|
||||
fclose(file_);
|
||||
}
|
||||
|
||||
private:
|
||||
FILE *file_;
|
||||
};
|
||||
|
||||
bool BasicSourceLineResolver::Module::LoadMapFromBuffer(
|
||||
const string &map_buffer) {
|
||||
bool BasicSourceLineResolver::Module::LoadMapFromMemory(char *memory_buffer) {
|
||||
linked_ptr<Function> cur_func;
|
||||
int line_number = 0;
|
||||
const char *map_buffer_c_str = map_buffer.c_str();
|
||||
char *save_ptr;
|
||||
|
||||
// set up our input buffer as a c-style string so we
|
||||
// can we use strtok()
|
||||
// have to copy because modifying the result of string::c_str is not
|
||||
// permitted
|
||||
size_t map_buffer_length = strlen(map_buffer_c_str);
|
||||
size_t map_buffer_length = strlen(memory_buffer);
|
||||
|
||||
// If the length is 0, we can still pretend we have a symbol file. This is
|
||||
// for scenarios that want to test symbol lookup, but don't necessarily care if
|
||||
// certain modules do not have any information, like system libraries.
|
||||
// for scenarios that want to test symbol lookup, but don't necessarily care
|
||||
// if certain modules do not have any information, like system libraries.
|
||||
if (map_buffer_length == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
scoped_array<char> map_buffer_chars(new char[map_buffer_length]);
|
||||
if (map_buffer_chars == NULL) {
|
||||
BPLOG(ERROR) << "Memory allocation of " << map_buffer_length <<
|
||||
" bytes failed";
|
||||
return false;
|
||||
if (memory_buffer[map_buffer_length - 1] == '\n') {
|
||||
memory_buffer[map_buffer_length - 1] = '\0';
|
||||
}
|
||||
|
||||
strncpy(map_buffer_chars.get(), map_buffer_c_str, map_buffer_length);
|
||||
|
||||
if (map_buffer_chars[map_buffer_length - 1] == '\n') {
|
||||
map_buffer_chars[map_buffer_length - 1] = '\0';
|
||||
}
|
||||
char *buffer;
|
||||
buffer = strtok_r(map_buffer_chars.get(), "\r\n", &save_ptr);
|
||||
buffer = strtok_r(memory_buffer, "\r\n", &save_ptr);
|
||||
|
||||
while (buffer != NULL) {
|
||||
++line_number;
|
||||
|
|
@ -413,68 +146,11 @@ bool BasicSourceLineResolver::Module::LoadMapFromBuffer(
|
|||
cur_func->lines.StoreRange(line->address, line->size,
|
||||
linked_ptr<Line>(line));
|
||||
}
|
||||
|
||||
buffer = strtok_r(NULL, "\r\n", &save_ptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::LoadMap(const string &map_file) {
|
||||
struct stat buf;
|
||||
int error_code = stat(map_file.c_str(), &buf);
|
||||
if (error_code == -1) {
|
||||
string error_string;
|
||||
int error_code = ErrnoString(&error_string);
|
||||
BPLOG(ERROR) << "Could not open " << map_file <<
|
||||
", error " << error_code << ": " << error_string;
|
||||
return false;
|
||||
}
|
||||
|
||||
off_t file_size = buf.st_size;
|
||||
|
||||
// Allocate memory for file contents, plus a null terminator
|
||||
// since we'll use strtok() on the contents.
|
||||
char *file_buffer = new char[sizeof(char)*file_size + 1];
|
||||
|
||||
if (file_buffer == NULL) {
|
||||
BPLOG(ERROR) << "Could not allocate memory for " << map_file;
|
||||
return false;
|
||||
}
|
||||
|
||||
BPLOG(INFO) << "Opening " << map_file;
|
||||
|
||||
FILE *f = fopen(map_file.c_str(), "rt");
|
||||
if (!f) {
|
||||
string error_string;
|
||||
int error_code = ErrnoString(&error_string);
|
||||
BPLOG(ERROR) << "Could not open " << map_file <<
|
||||
", error " << error_code << ": " << error_string;
|
||||
delete [] file_buffer;
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoFileCloser closer(f);
|
||||
|
||||
int items_read = 0;
|
||||
|
||||
items_read = fread(file_buffer, 1, file_size, f);
|
||||
|
||||
if (items_read != file_size) {
|
||||
string error_string;
|
||||
int error_code = ErrnoString(&error_string);
|
||||
BPLOG(ERROR) << "Could not slurp " << map_file <<
|
||||
", error " << error_code << ": " << error_string;
|
||||
delete [] file_buffer;
|
||||
return false;
|
||||
}
|
||||
file_buffer[file_size] = '\0';
|
||||
string map_buffer(file_buffer);
|
||||
delete [] file_buffer;
|
||||
|
||||
return LoadMapFromBuffer(map_buffer);
|
||||
}
|
||||
|
||||
void BasicSourceLineResolver::Module::LookupAddress(StackFrame *frame) const {
|
||||
MemAddr address = frame->instruction - frame->module->base_address();
|
||||
|
||||
|
|
@ -543,7 +219,7 @@ WindowsFrameInfo *BasicSourceLineResolver::Module::FindWindowsFrameInfo(
|
|||
linked_ptr<Function> function;
|
||||
MemAddr function_base, function_size;
|
||||
if (functions_.RetrieveNearestRange(address, &function,
|
||||
&function_base, &function_size) &&
|
||||
&function_base, &function_size) &&
|
||||
address >= function_base && address - function_base < function_size) {
|
||||
result->parameter_size = function->parameter_size;
|
||||
result->valid |= WindowsFrameInfo::VALID_PARAMETER_SIZE;
|
||||
|
|
@ -558,7 +234,7 @@ WindowsFrameInfo *BasicSourceLineResolver::Module::FindWindowsFrameInfo(
|
|||
(!function.get() || public_address > function_base)) {
|
||||
result->parameter_size = public_symbol->parameter_size;
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -596,13 +272,6 @@ CFIFrameInfo *BasicSourceLineResolver::Module::FindCFIFrameInfo(
|
|||
return rules.release();
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::ParseCFIRuleSet(
|
||||
const string &rule_set, CFIFrameInfo *frame_info) const {
|
||||
CFIFrameInfoParseHandler handler(frame_info);
|
||||
CFIRuleParser parser(&handler);
|
||||
return parser.Parse(rule_set);
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::ParseFile(char *file_line) {
|
||||
// FILE <id> <filename>
|
||||
file_line += 5; // skip prefix
|
||||
|
|
@ -762,7 +431,7 @@ bool BasicSourceLineResolver::Module::ParseCFIFrameInfo(
|
|||
// This record has the form "STACK INIT <address> <size> <rules...>".
|
||||
char *address_field = strtok_r(NULL, " \r\n", &cursor);
|
||||
if (!address_field) return false;
|
||||
|
||||
|
||||
char *size_field = strtok_r(NULL, " \r\n", &cursor);
|
||||
if (!size_field) return false;
|
||||
|
||||
|
|
@ -784,9 +453,4 @@ bool BasicSourceLineResolver::Module::ParseCFIFrameInfo(
|
|||
return true;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::CompareString::operator()(
|
||||
const string &s1, const string &s2) const {
|
||||
return strcmp(s1.c_str(), s2.c_str()) < 0;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue