aboutsummaryrefslogtreecommitdiff
path: root/src/processor/network_source_line_server.cc
diff options
context:
space:
mode:
authorted.mielczarek <ted.mielczarek@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-04-08 23:06:23 +0000
committerted.mielczarek <ted.mielczarek@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-04-08 23:06:23 +0000
commitb223627d81c083a64f2ccecf2651a18111421280 (patch)
treebfe59da685835b8d13b073f11cddaf4c5997282f /src/processor/network_source_line_server.cc
parentInclude what you use. (diff)
downloadbreakpad-b223627d81c083a64f2ccecf2651a18111421280.tar.xz
provide a network source line resolver + server. r=mark,jimb at http://breakpad.appspot.com/36001
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@569 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/processor/network_source_line_server.cc')
-rw-r--r--src/processor/network_source_line_server.cc435
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