diff options
Diffstat (limited to 'src/processor/network_source_line_server.cc')
-rw-r--r-- | src/processor/network_source_line_server.cc | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/src/processor/network_source_line_server.cc b/src/processor/network_source_line_server.cc new file mode 100644 index 00000000..1a6ea31b --- /dev/null +++ b/src/processor/network_source_line_server.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 <arpa/inet.h> +#include <netinet/in.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <algorithm> +#include <iostream> +#include <sstream> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/stack_frame.h" +#include "processor/basic_code_module.h" +#include "processor/binarystream.h" +#include "processor/cfi_frame_info.h" +#include "processor/logging.h" +#include "processor/network_source_line_protocol.h" +#include "processor/network_source_line_server.h" +#include "processor/tokenize.h" +#include "processor/windows_frame_info.h" + +namespace google_breakpad { + +using std::dec; +using std::find; +using std::hex; +// Style guide forbids "using namespace", so at least shorten it. +namespace P = source_line_protocol; + +bool NetworkSourceLineServer::Initialize() { + if (net_->Init(true)) + initialized_ = true; + return initialized_; +} + +bool NetworkSourceLineServer::RunForever() { + if (!initialized_ && !Initialize()) + return false; + + BPLOG(INFO) << "Running forever..."; + while (true) { + RunOnce(5000); + } + // not reached + return true; +} + +bool NetworkSourceLineServer::RunOnce(int wait_milliseconds) { + if (!initialized_ && !Initialize()) + return false; + + if (!net_->WaitToReceive(wait_milliseconds)) + return false; + //TODO(ted): loop, processing packets until wait_milliseconds + // is actually exhausted? + + vector<char> buffer(1024); + ssize_t received_bytes; + if (!net_->Receive(&buffer[0], buffer.size(), received_bytes)) + return false; + buffer.resize(received_bytes); + + binarystream request(&buffer[0], buffer.size()); + binarystream response; + if (!HandleRequest(request, response)) + return false; + + string response_string = response.str(); + if (!net_->Send(response_string.c_str(), response_string.length())) + return false; + return true; +} + +bool NetworkSourceLineServer::HandleRequest(binarystream &request, + binarystream &response) { + u_int16_t sequence_number; + u_int8_t command; + request >> sequence_number >> command; + if (request.eof()) { + BPLOG(ERROR) << "Malformed request, missing sequence number or command"; + return false; + } + + response.rewind(); + response << sequence_number; + switch(command) { + case P::HAS: + HandleHas(request, response); + break; + case P::LOAD: + HandleLoad(request, response); + break; + case P::GET: + HandleGet(request, response); + break; + case P::GETSTACKWIN: + HandleGetStackWin(request, response); + break; + case P::GETSTACKCFI: + HandleGetStackCFI(request, response); + break; + default: + BPLOG(ERROR) << "Unknown command " << int(command); + response << P::ERROR; + break; + } + return true; +} + +void NetworkSourceLineServer::HandleHas(binarystream &message, + binarystream &response) { + string module_name, debug_file, debug_id; + message >> module_name >> debug_file >> debug_id; + if (message.eof()) { + BPLOG(ERROR) << "HAS message malformed"; + response << P::ERROR; + return; + } + BPLOG(INFO) << "Received message HAS " << module_name + << " " << debug_file + << " " << debug_id; + // Need to lie about the module name here, since BasicSourceLineResolver + // uses only the module name for unique modules, but we want to allow + // multiple versions of the same named module in here. + BasicCodeModule module((u_int64_t)0, (u_int64_t)0, + module_name + "|" + debug_file + "|" + debug_id, "", + debug_file, debug_id, ""); + u_int8_t data; + if (resolver_) { + data = resolver_->HasModule(&module) + ? P::MODULE_LOADED : P::MODULE_NOT_LOADED; + } else { + data = P::MODULE_NOT_LOADED; + } + response << P::OK << data; +} + +void NetworkSourceLineServer::HandleLoad(binarystream &message, + binarystream &response) { + string module_name, debug_file, debug_id; + message >> module_name >> debug_file >> debug_id; + if (message.eof()) { + BPLOG(ERROR) << "LOAD message malformed"; + response << P::ERROR; + return; + } + BPLOG(INFO) << "Received message LOAD " << module_name + << " " << debug_file + << " " << debug_id; + + u_int8_t reply; + // stub out the bare minimum here + BasicCodeModule module((u_int64_t)0, (u_int64_t)0, + module_name + "|" + debug_file + "|" + debug_id, "", + debug_file, debug_id, ""); + if (resolver_->HasModule(&module)) { + // just short-circuit the rest of this, since it's already loaded + BPLOG(INFO) << "Got LOAD for already loaded " << module_name; + UsedModule(module); + reply = P::LOAD_OK; + } else { + BPLOG(INFO) << "Looking up symbols for (" << module_name << ", " + << debug_file << ", " << debug_id << ")"; + string symbol_data, symbol_file; + SymbolSupplier::SymbolResult symbol_result; + if (supplier_) { + symbol_result = supplier_->GetSymbolFile(&module, NULL, + &symbol_file, &symbol_data); + } else { + symbol_result = SymbolSupplier::NOT_FOUND; + } + + switch (symbol_result) { + case SymbolSupplier::FOUND: { + BPLOG(INFO) << "Found symbols for " << module_name; + reply = P::LOAD_OK; + // also go ahead and load the symbols while we're here, + // since the client is just going to ask us to do this right away + // and we already have |symbol_data| here. + int numlines = CountNewlines(symbol_data); + if (!resolver_->LoadModuleUsingMapBuffer(&module, + symbol_data)) { + BPLOG(INFO) << "Failed to load symbols for " << module_name; + reply = P::LOAD_FAIL; + } else { + // save some info about this module + symbol_lines_ += numlines; + UsedModule(module); + module_symbol_lines_[module.code_file()] = numlines; + + BPLOG(INFO) << "Loaded symbols for " << module_name + << " (" << dec << numlines << " lines, " + << symbol_lines_ << " total)"; + + if (max_symbol_lines_ != 0 && symbol_lines_ > max_symbol_lines_) { + // try unloading some modules to reclaim memory + // (but not the one that was just loaded) + BPLOG(INFO) << "Exceeded limit of " << dec << max_symbol_lines_ + << " symbol lines loaded, trying to unload modules"; + TryUnloadModules(module); + } + } + } + break; + case SymbolSupplier::NOT_FOUND: + BPLOG(INFO) << "Symbols not found for " << module_name; + reply = P::LOAD_NOT_FOUND; + break; + case SymbolSupplier::INTERRUPT: + BPLOG(INFO) << "Symbol provider returned interrupt for " << module_name; + reply = P::LOAD_INTERRUPT; + break; + } + } + response << P::OK << reply; +} + +void NetworkSourceLineServer::HandleGet(binarystream &message, + binarystream &response) { + string module_name, debug_file, debug_id; + u_int64_t module_base, instruction; + message >> module_name >> debug_file >> debug_id + >> module_base >> instruction; + if (message.eof()) { + BPLOG(ERROR) << "GET message malformed"; + response << P::ERROR; + return; + } + + BPLOG(INFO) << "Received message GET " << module_name << " " + << debug_file << " " << debug_id << " " + << hex << module_base << " " << instruction; + + StackFrame frame; + if (resolver_) { + BasicCodeModule module(module_base, (u_int64_t)0, + module_name + "|" + debug_file + "|" + debug_id, "", + debug_file, debug_id, ""); + frame.module = &module; + frame.instruction = instruction; + resolver_->FillSourceLineInfo(&frame); + UsedModule(module); + } + + response << P::OK << frame.function_name << frame.function_base + << frame.source_file_name << u_int32_t(frame.source_line) + << frame.source_line_base; + BPLOG(INFO) << "Sending GET response: " << frame.function_name << " " + << hex << frame.function_base << " " + << frame.source_file_name << " " + << dec << frame.source_line << " " + << hex << frame.source_line_base; +} + +void NetworkSourceLineServer::HandleGetStackWin(binarystream &message, + binarystream &response) { + string module_name, debug_file, debug_id; + u_int64_t module_base, instruction; + message >> module_name >> debug_file >> debug_id + >> module_base >> instruction; + if (message.eof()) { + BPLOG(ERROR) << "GETSTACKWIN message malformed"; + response << P::ERROR; + return; + } + + BPLOG(INFO) << "Received message GETSTACKWIN " << module_name << " " + << debug_file << " " << debug_id << " " + << hex << module_base << " " << instruction; + + + WindowsFrameInfo *frame_info = NULL; + if (resolver_) { + StackFrame frame; + BasicCodeModule module(module_base, (u_int64_t)0, + module_name + "|" + debug_file + "|" + debug_id, "", + debug_file, debug_id, ""); + frame.module = &module; + frame.instruction = instruction; + frame_info = resolver_->FindWindowsFrameInfo(&frame); + UsedModule(module); + } + + response << P::OK << FormatWindowsFrameInfo(frame_info); + BPLOG(INFO) << "Sending GETSTACKWIN response: " + << FormatWindowsFrameInfo(frame_info); + delete frame_info; +} + +string NetworkSourceLineServer::FormatWindowsFrameInfo( + WindowsFrameInfo *frame_info) { + if (frame_info == NULL) + return ""; + + std::ostringstream stream; + // Put "0" as the type, rva and code size because the client doesn't + // actually care what these values are, but it's easier to keep the + // format consistent with the symbol files so the parsing code can be + // shared. + stream << "0 0 0 " << hex + << frame_info->prolog_size << " " + << frame_info->epilog_size << " " + << frame_info->parameter_size << " " + << frame_info->saved_register_size << " " + << frame_info->local_size << " " + << frame_info->max_stack_size << " "; + if (!frame_info->program_string.empty()) { + stream << 1 << " " << frame_info->program_string; + } else { + stream << 0 << " " << frame_info->allocates_base_pointer; + } + return stream.str(); +} + +void NetworkSourceLineServer::HandleGetStackCFI(binarystream &message, + binarystream &response) { + string module_name, debug_file, debug_id; + u_int64_t module_base, instruction; + message >> module_name >> debug_file >> debug_id + >> module_base >> instruction; + if (message.eof()) { + BPLOG(ERROR) << "GETSTACKCFI message malformed"; + response << P::ERROR; + return; + } + + BPLOG(INFO) << "Received message GETSTACKCFI " << module_name << " " + << debug_file << " " << debug_id << " " + << hex << module_base << " " << instruction; + + + CFIFrameInfo *frame_info = NULL; + if (resolver_) { + StackFrame frame; + BasicCodeModule module(module_base, (u_int64_t)0, + module_name + "|" + debug_file + "|" + debug_id, "", + debug_file, debug_id, ""); + frame.module = &module; + frame.instruction = instruction; + frame_info = resolver_->FindCFIFrameInfo(&frame); + UsedModule(module); + } + + string frame_info_string; + if (frame_info != NULL) + frame_info_string = frame_info->Serialize(); + response << P::OK << frame_info_string; + BPLOG(INFO) << "Sending GETSTACKCFI response: " + << frame_info_string; + delete frame_info; +} + +int NetworkSourceLineServer::CountNewlines(const string &str) { + int count = 0; + string::const_iterator iter = str.begin(); + while (iter != str.end()) { + if (*iter == '\n') + count++; + iter++; + } + return count; +} + +void NetworkSourceLineServer::UsedModule(const CodeModule &module) { + list<string>::iterator iter = find(modules_used_.begin(), + modules_used_.end(), + module.code_file()); + if (iter == modules_used_.end()) { + modules_used_.push_front(module.code_file()); + } else { + modules_used_.splice(modules_used_.begin(), + modules_used_, + iter); + } +} + +void NetworkSourceLineServer::TryUnloadModules( + const CodeModule &just_loaded_module) { + if (!resolver_) + return; + + while (symbol_lines_ > max_symbol_lines_) { + // never unload just_loaded_module + if (modules_used_.back() == just_loaded_module.code_file()) + break; + + string module_to_unload = modules_used_.back(); + modules_used_.pop_back(); + BasicCodeModule module(0, 0, module_to_unload, "", "", "", ""); + BPLOG(INFO) << "Unloading module " << module_to_unload; + resolver_->UnloadModule(&module); + + // reduce the symbol line count + map<string, int>::iterator iter = + module_symbol_lines_.find(module_to_unload); + if (iter != module_symbol_lines_.end()) { + symbol_lines_ -= iter->second; + module_symbol_lines_.erase(iter); + } + } +} + +} // namespace google_breakpad |