mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-11-12 05:54:51 +00:00
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@711 4c0a9323-5329-0410-9bdc-e9ce6186880e
456 lines
14 KiB
C++
456 lines
14 KiB
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.
|
|
|
|
#include "google_breakpad/processor/network_source_line_resolver.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
#include "google_breakpad/processor/stack_frame.h"
|
|
#include "processor/binarystream.h"
|
|
#include "processor/cfi_frame_info.h"
|
|
#include "processor/network_interface.h"
|
|
#include "processor/network_source_line_protocol.h"
|
|
#include "processor/logging.h"
|
|
#include "processor/scoped_ptr.h"
|
|
#include "processor/udp_network.h"
|
|
#include "processor/windows_frame_info.h"
|
|
|
|
namespace google_breakpad {
|
|
|
|
using std::string;
|
|
using std::vector;
|
|
using std::dec;
|
|
using std::hex;
|
|
// Style guide forbids "using namespace", so at least shorten it.
|
|
namespace P = source_line_protocol;
|
|
|
|
NetworkSourceLineResolver::NetworkSourceLineResolver(const string &server,
|
|
unsigned short port,
|
|
int wait_milliseconds)
|
|
: wait_milliseconds_(wait_milliseconds),
|
|
initialized_(false),
|
|
sequence_(0),
|
|
net_(new UDPNetwork(server, port)) {
|
|
if (net_->Init(false))
|
|
initialized_ = true;
|
|
}
|
|
|
|
NetworkSourceLineResolver::NetworkSourceLineResolver(NetworkInterface *net,
|
|
int wait_milliseconds)
|
|
: wait_milliseconds_(wait_milliseconds),
|
|
initialized_(false),
|
|
sequence_(0),
|
|
net_(net) {
|
|
if (net_ && net->Init(false))
|
|
initialized_ = true;
|
|
}
|
|
|
|
NetworkSourceLineResolver::~NetworkSourceLineResolver() {
|
|
initialized_ = false;
|
|
}
|
|
|
|
bool NetworkSourceLineResolver::LoadModule(const CodeModule *module,
|
|
const string &map_file) {
|
|
// Just lie here and say it was loaded. The server always loads
|
|
// symbols immediately when they're found, since clients always
|
|
// will want to load them after finding them anyway. Since this class
|
|
// acts as both the symbol supplier and source line resolver,
|
|
// it's just a little optimization.
|
|
return true;
|
|
}
|
|
|
|
bool NetworkSourceLineResolver::LoadModuleUsingMapBuffer(
|
|
const CodeModule *module,
|
|
const string &map_buffer) {
|
|
// see above
|
|
return true;
|
|
}
|
|
|
|
bool NetworkSourceLineResolver::LoadModuleUsingMemoryBuffer(
|
|
const CodeModule *module,
|
|
char *memory_buffer) {
|
|
// see above
|
|
return true;
|
|
}
|
|
|
|
void NetworkSourceLineResolver::UnloadModule(const CodeModule *module) {
|
|
// no-op
|
|
}
|
|
|
|
bool NetworkSourceLineResolver::HasModule(const CodeModule *module) {
|
|
if (!initialized_ || !module)
|
|
return false;
|
|
|
|
// cache seen modules so the network round trip can be skipped
|
|
if (module_cache_.find(module->code_file()) != module_cache_.end())
|
|
return true;
|
|
|
|
// also cache modules for which symbols aren't found
|
|
if (no_symbols_cache_.find(module->debug_file() + module->debug_identifier())
|
|
!= no_symbols_cache_.end())
|
|
return false;
|
|
|
|
binarystream message;
|
|
message << P::HAS
|
|
<< module->code_file()
|
|
<< module->debug_file()
|
|
<< module->debug_identifier();
|
|
binarystream response;
|
|
bool got_response = SendMessageGetResponse(message, response);
|
|
u_int8_t response_data;
|
|
response >> response_data;
|
|
|
|
bool found = false;
|
|
if (got_response && !response.eof() && response_data == P::MODULE_LOADED) {
|
|
module_cache_.insert(module->code_file());
|
|
found = true;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
void NetworkSourceLineResolver::FillSourceLineInfo(
|
|
StackFrame *frame) {
|
|
if (!initialized_)
|
|
return;
|
|
|
|
// if don't this module isn't loaded, can't fill source line info
|
|
if (!frame->module ||
|
|
module_cache_.find(frame->module->code_file()) == module_cache_.end())
|
|
return;
|
|
|
|
// if this frame has already been seen, return the cached copy
|
|
if (FindCachedSourceLineInfo(frame)) {
|
|
BPLOG(INFO) << "Using cached source line info";
|
|
return;
|
|
}
|
|
|
|
binarystream message;
|
|
message << P::GET
|
|
<< frame->module->code_file()
|
|
<< frame->module->debug_file()
|
|
<< frame->module->debug_identifier()
|
|
<< frame->module->base_address()
|
|
<< frame->instruction;
|
|
binarystream response;
|
|
bool got_response = SendMessageGetResponse(message, response);
|
|
if (!got_response)
|
|
return;
|
|
|
|
string function_name, source_file;
|
|
u_int32_t source_line;
|
|
u_int64_t function_base, source_line_base;
|
|
response >> function_name >> function_base
|
|
>> source_file >> source_line >> source_line_base;
|
|
|
|
if (response.eof()) {
|
|
BPLOG(ERROR) << "GET response malformed";
|
|
return;
|
|
} else {
|
|
BPLOG(INFO) << "GET response: " << function_name << " "
|
|
<< hex << function_base << " " << source_file << " "
|
|
<< dec << source_line << " " << hex
|
|
<< source_line_base;
|
|
}
|
|
|
|
frame->function_name = function_name;
|
|
frame->function_base = function_base;
|
|
frame->source_file_name = source_file;
|
|
frame->source_line = source_line;
|
|
frame->source_line_base = source_line_base;
|
|
|
|
CacheSourceLineInfo(frame);
|
|
}
|
|
|
|
WindowsFrameInfo*
|
|
NetworkSourceLineResolver::FindWindowsFrameInfo(const StackFrame *frame) {
|
|
if (!initialized_)
|
|
return NULL;
|
|
|
|
// if this module isn't loaded, can't get frame info
|
|
if (!frame->module ||
|
|
module_cache_.find(frame->module->code_file()) == module_cache_.end())
|
|
return NULL;
|
|
|
|
// check the cache first
|
|
string stack_info;
|
|
|
|
if (FindCachedFrameInfo(frame, kWindowsFrameInfo, &stack_info)) {
|
|
BPLOG(INFO) << "Using cached windows frame info";
|
|
} else {
|
|
binarystream message;
|
|
message << P::GETSTACKWIN
|
|
<< frame->module->code_file()
|
|
<< frame->module->debug_file()
|
|
<< frame->module->debug_identifier()
|
|
<< frame->module->base_address()
|
|
<< frame->instruction;
|
|
binarystream response;
|
|
if (SendMessageGetResponse(message, response)) {
|
|
response >> stack_info;
|
|
CacheFrameInfo(frame, kWindowsFrameInfo, stack_info);
|
|
}
|
|
}
|
|
|
|
WindowsFrameInfo *info = NULL;
|
|
if (!stack_info.empty()) {
|
|
int type;
|
|
u_int64_t rva, code_size;
|
|
info = WindowsFrameInfo::ParseFromString(stack_info,
|
|
type,
|
|
rva,
|
|
code_size);
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
CFIFrameInfo*
|
|
NetworkSourceLineResolver::FindCFIFrameInfo(const StackFrame *frame)
|
|
{
|
|
if (!initialized_)
|
|
return NULL;
|
|
|
|
// if this module isn't loaded, can't get frame info
|
|
if (!frame->module ||
|
|
module_cache_.find(frame->module->code_file()) == module_cache_.end())
|
|
return NULL;
|
|
|
|
string stack_info;
|
|
|
|
if (FindCachedFrameInfo(frame, kCFIFrameInfo, &stack_info)) {
|
|
BPLOG(INFO) << "Using cached CFI frame info";
|
|
} else {
|
|
binarystream message;
|
|
message << P::GETSTACKCFI
|
|
<< frame->module->code_file()
|
|
<< frame->module->debug_file()
|
|
<< frame->module->debug_identifier()
|
|
<< frame->module->base_address()
|
|
<< frame->instruction;
|
|
binarystream response;
|
|
if (SendMessageGetResponse(message, response)) {
|
|
response >> stack_info;
|
|
CacheFrameInfo(frame, kCFIFrameInfo, stack_info);
|
|
}
|
|
}
|
|
|
|
if (!stack_info.empty()) {
|
|
scoped_ptr<CFIFrameInfo> info(new CFIFrameInfo());
|
|
CFIFrameInfoParseHandler handler(info.get());
|
|
CFIRuleParser parser(&handler);
|
|
if (parser.Parse(stack_info))
|
|
return info.release();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
SymbolSupplier::SymbolResult
|
|
NetworkSourceLineResolver::GetSymbolFile(const CodeModule *module,
|
|
const SystemInfo *system_info,
|
|
string *symbol_file) {
|
|
BPLOG_IF(ERROR, !symbol_file) << "NetworkSourceLineResolver::GetSymbolFile "
|
|
"requires |symbol_file|";
|
|
assert(symbol_file);
|
|
|
|
if (!initialized_)
|
|
return NOT_FOUND;
|
|
|
|
if (no_symbols_cache_.find(module->debug_file() + module->debug_identifier())
|
|
!= no_symbols_cache_.end())
|
|
return NOT_FOUND;
|
|
|
|
binarystream message;
|
|
message << P::LOAD
|
|
<< module->code_file()
|
|
<< module->debug_file()
|
|
<< module->debug_identifier();
|
|
binarystream response;
|
|
bool got_response = SendMessageGetResponse(message, response);
|
|
if (!got_response) {
|
|
// Didn't get a response, which is the same as not having symbols.
|
|
// Don't cache this, though, to force a retry if the client asks for
|
|
// symbols for the same file again.
|
|
return NOT_FOUND;
|
|
}
|
|
u_int8_t response_data;
|
|
response >> response_data;
|
|
|
|
if (response.eof()) {
|
|
BPLOG(ERROR) << "Malformed LOAD response";
|
|
return NOT_FOUND;
|
|
}
|
|
|
|
if (response_data == P::LOAD_NOT_FOUND || response_data == P::LOAD_FAIL) {
|
|
// Received NOT or FAIL, symbols not present or failed to load them.
|
|
// Same problem to the client any way you look at it.
|
|
// Cache this module to avoid pointless retry.
|
|
no_symbols_cache_.insert(module->debug_file() + module->debug_identifier());
|
|
return NOT_FOUND;
|
|
} else if (response_data == P::LOAD_INTERRUPT) {
|
|
return INTERRUPT;
|
|
}
|
|
|
|
// otherwise, OK
|
|
module_cache_.insert(module->code_file());
|
|
*symbol_file = "<loaded on server>";
|
|
return FOUND;
|
|
}
|
|
|
|
SymbolSupplier::SymbolResult
|
|
NetworkSourceLineResolver::GetSymbolFile(const CodeModule *module,
|
|
const SystemInfo *system_info,
|
|
string *symbol_file,
|
|
string *symbol_data) {
|
|
if(symbol_data)
|
|
symbol_data->clear();
|
|
return GetSymbolFile(module, system_info, symbol_file);
|
|
}
|
|
|
|
SymbolSupplier::SymbolResult
|
|
NetworkSourceLineResolver::GetCStringSymbolData(
|
|
const CodeModule *module,
|
|
const SystemInfo *system_info,
|
|
string *symbol_file,
|
|
char **symbol_data) {
|
|
if (symbol_data)
|
|
delete *symbol_data;
|
|
|
|
return GetSymbolFile(module, system_info, symbol_file);
|
|
}
|
|
|
|
bool NetworkSourceLineResolver::SendMessageGetResponse(
|
|
const binarystream &message,
|
|
binarystream &response) {
|
|
binarystream sequence_stream;
|
|
u_int16_t sent_sequence = sequence_;
|
|
sequence_stream << sequence_;
|
|
++sequence_;
|
|
string message_string = sequence_stream.str();
|
|
message_string.append(message.str());
|
|
BPLOG(INFO) << "Sending " << message_string.length() << " bytes";
|
|
if (!net_->Send(message_string.c_str(), message_string.length()))
|
|
return false;
|
|
|
|
bool done = false;
|
|
while (!done) {
|
|
if (!net_->WaitToReceive(wait_milliseconds_))
|
|
return false;
|
|
|
|
vector<char> buffer(1024);
|
|
ssize_t received_bytes;
|
|
if (!net_->Receive(&buffer[0], buffer.size(), received_bytes))
|
|
return false;
|
|
|
|
BPLOG(INFO) << "received " << received_bytes << " bytes";
|
|
buffer.resize(received_bytes);
|
|
|
|
response.str(string(&buffer[0], buffer.size()));
|
|
response.rewind();
|
|
u_int16_t read_sequence;
|
|
u_int8_t status;
|
|
response >> read_sequence >> status;
|
|
if (response.eof()) {
|
|
BPLOG(ERROR) << "malformed response, missing sequence number or status";
|
|
return false;
|
|
}
|
|
if (read_sequence < sent_sequence) // old packet
|
|
continue;
|
|
|
|
if (read_sequence != sent_sequence) {
|
|
// not expecting this packet, just error
|
|
BPLOG(ERROR) << "error, got sequence number " << read_sequence
|
|
<< ", expected " << sent_sequence;
|
|
return false;
|
|
}
|
|
|
|
// This is the expected packet, so even if it's an error this loop is done
|
|
done = true;
|
|
|
|
if (status != P::OK) {
|
|
BPLOG(ERROR) << "received an ER response packet";
|
|
return false;
|
|
}
|
|
// the caller will process the rest of response
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool NetworkSourceLineResolver::FindCachedSourceLineInfo(StackFrame *frame)
|
|
const
|
|
{
|
|
SourceCache::const_iterator iter =
|
|
source_line_info_cache_.find(frame->instruction);
|
|
if (iter == source_line_info_cache_.end())
|
|
return false;
|
|
|
|
const StackFrame &f = iter->second;
|
|
frame->function_name = f.function_name;
|
|
frame->function_base = f.function_base;
|
|
frame->source_file_name = f.source_file_name;
|
|
frame->source_line = f.source_line;
|
|
frame->source_line_base = f.source_line_base;
|
|
return true;
|
|
}
|
|
|
|
bool NetworkSourceLineResolver::FindCachedFrameInfo(
|
|
const StackFrame *frame,
|
|
FrameInfoType type,
|
|
string *info) const
|
|
{
|
|
FrameInfoCache::const_iterator iter =
|
|
frame_info_cache_[type].find(frame->instruction);
|
|
if (iter == frame_info_cache_[type].end())
|
|
return false;
|
|
|
|
*info = iter->second;
|
|
return true;
|
|
}
|
|
|
|
void NetworkSourceLineResolver::CacheSourceLineInfo(const StackFrame *frame) {
|
|
StackFrame f(*frame);
|
|
// can't hang onto this pointer, the caller owns it
|
|
f.module = NULL;
|
|
source_line_info_cache_[frame->instruction] = f;
|
|
}
|
|
|
|
void NetworkSourceLineResolver::CacheFrameInfo(
|
|
const StackFrame *frame,
|
|
FrameInfoType type,
|
|
const string &info) {
|
|
frame_info_cache_[type][frame->instruction] = info;
|
|
}
|
|
|
|
} // namespace google_breakpad
|