blob: 69fd3f3e0c316000861368ee23dacd6beee4e426 [file] [log] [blame]
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "syzygy/agent/asan/asan_logger.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/logging.h"
#include "base/debug/stack_trace.h"
#include "base/memory/scoped_ptr.h"
#include "base/process/launch.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "syzygy/trace/rpc/logger_rpc.h"
#include "syzygy/trace/rpc/rpc_helpers.h"
namespace agent {
namespace asan {
namespace {
using trace::client::GetInstanceString;
AsanLogger* logger_instance = NULL;
void InitExecutionContext(const CONTEXT& rtl_context,
ExecutionContext* exc_context) {
DCHECK(exc_context != NULL);
exc_context->edi = rtl_context.Edi;
exc_context->esi = rtl_context.Esi;
exc_context->ebx = rtl_context.Ebx;
exc_context->edx = rtl_context.Edx;
exc_context->ecx = rtl_context.Ecx;
exc_context->eax = rtl_context.Eax;
exc_context->ebp = rtl_context.Ebp;
exc_context->eip = rtl_context.Eip;
exc_context->seg_cs = rtl_context.SegCs;
exc_context->eflags = rtl_context.EFlags;
exc_context->esp = rtl_context.Esp;
exc_context->seg_ss = rtl_context.SegSs;
}
} // namespace
AsanLogger::AsanLogger() : log_as_text_(true), minidump_on_failure_(false) {
}
void AsanLogger::Init() {
bool success = rpc_binding_.Open(
kLoggerRpcProtocol,
GetInstanceString(kLoggerRpcEndpointRoot, instance_id_));
// TODO(rogerm): Add a notion of a session to the logger interface. Opening
// a session (either here, or on first use) allows for better management
// of symbol context across trace log messages for a given process.
if (success) {
const CommandLine* command_line = CommandLine::ForCurrentProcess();
std::string message = base::StringPrintf(
"PID=%d; cmd-line='%ls'\n",
::GetCurrentProcessId(),
command_line->GetCommandLineString().c_str());
success = trace::client::InvokeRpc(
&LoggerClient_Write,
rpc_binding_.Get(),
reinterpret_cast<const unsigned char*>(message.c_str())).succeeded();
if (!success)
rpc_binding_.Close();
}
}
void AsanLogger::Stop() {
if (rpc_binding_.Get() != NULL) {
trace::client::InvokeRpc(
&LoggerClient_Stop,
rpc_binding_.Get());
}
}
void AsanLogger::Write(const std::string& message) {
// If we're bound to a logging endpoint, log the message there.
if (rpc_binding_.Get() != NULL) {
trace::client::InvokeRpc(
&LoggerClient_Write,
rpc_binding_.Get(),
reinterpret_cast<const unsigned char*>(message.c_str()));
}
}
void AsanLogger::WriteWithContext(const std::string& message,
const CONTEXT& context) {
// If we're bound to a logging endpoint, log the message there.
if (rpc_binding_.Get() != NULL) {
ExecutionContext exec_context = {};
InitExecutionContext(context, &exec_context);
trace::client::InvokeRpc(
&LoggerClient_WriteWithContext,
rpc_binding_.Get(),
reinterpret_cast<const unsigned char*>(message.c_str()),
&exec_context);
}
}
void AsanLogger::WriteWithStackTrace(const std::string& message,
const void * const * trace_data,
size_t trace_length) {
// If we're bound to a logging endpoint, log the message there.
if (rpc_binding_.Get() != NULL) {
trace::client::InvokeRpc(
&LoggerClient_WriteWithTrace,
rpc_binding_.Get(),
reinterpret_cast<const unsigned char*>(message.c_str()),
reinterpret_cast<const DWORD*>(trace_data),
trace_length);
}
}
void AsanLogger::SaveMiniDump(CONTEXT* context, AsanErrorInfo* error_info) {
DCHECK(context != NULL);
DCHECK(error_info != NULL);
if (rpc_binding_.Get() == NULL)
return;
EXCEPTION_RECORD exception = {};
exception.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
exception.ExceptionAddress = reinterpret_cast<PVOID>(context->Eip);
exception.NumberParameters = 2;
exception.ExceptionInformation[0] = reinterpret_cast<ULONG_PTR>(context);
exception.ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(error_info);
const EXCEPTION_POINTERS pointers = { &exception, context };
trace::client::InvokeRpc(&LoggerClient_SaveMiniDump,
rpc_binding_.Get(),
::GetCurrentThreadId(),
reinterpret_cast<unsigned long>(&pointers),
0);
}
} // namespace asan
} // namespace agent