breakpad/src/processor/network_source_line_resolver.cc
2010-10-07 20:31:36 +00:00

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