mirror of
https://github.com/yuzu-mirror/breakpad.git
synced 2025-12-06 04:42:01 +01:00
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@569 4c0a9323-5329-0410-9bdc-e9ce6186880e
436 lines
14 KiB
C++
436 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 <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
|