diff options
Diffstat (limited to 'src/processor/network_source_line_resolver.cc')
-rw-r--r-- | src/processor/network_source_line_resolver.cc | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/src/processor/network_source_line_resolver.cc b/src/processor/network_source_line_resolver.cc new file mode 100644 index 00000000..5fc5b1ea --- /dev/null +++ b/src/processor/network_source_line_resolver.cc @@ -0,0 +1,435 @@ +// 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 <stdlib.h> +#include <string.h> + +#include <cassert> +#include <sstream> +#include <vector> + +#include "google_breakpad/processor/network_source_line_resolver.h" +#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; +} + +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); +} + +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 |