blob: 10d829e2abdb9c07b76c5dbc8984574dfa1c1548 [file] [log] [blame]
// Copyright 2015 The Android Open Source Project
//
// 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 "android/crashreport/CrashService_windows.h"
#include "android/base/files/PathUtils.h"
#include "android/base/StringFormat.h"
#include "android/base/system/System.h"
#include "android/base/system/Win32UnicodeString.h"
#include "android/crashreport/CrashReporter.h"
#include "android/crashreport/CrashSystem.h"
#include "android/utils/debug.h"
#include <fstream>
#include <string>
#include <sys/types.h>
#include <unistd.h>
#include <psapi.h>
#define E(...) derror(__VA_ARGS__)
#define W(...) dwarning(__VA_ARGS__)
#define D(...) VERBOSE_PRINT(init, __VA_ARGS__)
#define I(...) printf(__VA_ARGS__)
#define CMD_BUF_SIZE 1024
#define HWINFO_CMD L"dxdiag /dontskip /whql:off /64bit /t"
namespace android {
using ::android::base::PathUtils;
using ::android::base::System;
using ::android::base::Win32UnicodeString;
namespace crashreport {
HostCrashService::~HostCrashService() {
stopCrashServer();
}
void HostCrashService::OnClientConnect(
void* context,
const google_breakpad::ClientInfo* client_info) {
D("Client connected, pid = %d\n", client_info->pid());
static_cast<CrashService::ServerState*>(context)->connected += 1;
}
void HostCrashService::OnClientDumpRequest(
void* context,
const google_breakpad::ClientInfo* client_info,
const std::wstring* file_path) {
if (static_cast<CrashService::DumpRequestContext*>(context)
->file_path.empty()) {
::std::string file_path_string =
Win32UnicodeString::convertToUtf8(
file_path->c_str());
D("Client Requesting dump %s\n", file_path_string.c_str());
static_cast<CrashService::DumpRequestContext*>(context)
->file_path.assign(file_path_string.c_str());
}
}
void HostCrashService::OnClientExit(
void* context,
const google_breakpad::ClientInfo* client_info) {
D("Client exiting\n");
CrashService::ServerState* serverstate =
static_cast<CrashService::ServerState*>(context);
if (serverstate->connected > 0) {
serverstate->connected -= 1;
}
if (serverstate->connected == 0) {
serverstate->waiting = false;
}
}
bool HostCrashService::startCrashServer(const std::string& pipe) {
if (mCrashServer) {
return false;
}
initCrashServer();
Win32UnicodeString pipe_unicode(pipe.c_str(), pipe.length());
::std::wstring pipe_string(pipe_unicode.data());
Win32UnicodeString crashdir_unicode(
::android::crashreport::CrashSystem::get()
->getCrashDirectory()
.c_str());
std::wstring crashdir_wstr(crashdir_unicode.c_str());
mCrashServer.reset(new ::google_breakpad::CrashGenerationServer(
pipe_string, nullptr, OnClientConnect, &mServerState,
OnClientDumpRequest, &mDumpRequestContext, OnClientExit,
&mServerState, nullptr, nullptr, true, &crashdir_wstr));
return mCrashServer->Start();
}
bool HostCrashService::stopCrashServer() {
if (mCrashServer) {
mCrashServer.reset();
return true;
} else {
return false;
}
}
bool HostCrashService::setClient(int clientpid) {
mClientProcess.reset(OpenProcess(SYNCHRONIZE, FALSE, clientpid));
return mClientProcess.get() != nullptr;
}
bool HostCrashService::isClientAlive() {
if (!mClientProcess) {
return false;
}
if (WaitForSingleObject(mClientProcess.get(), 0) != WAIT_TIMEOUT) {
return false;
} else {
return true;
}
}
bool HostCrashService::getHWInfo() {
const std::string& dataDirectory = getDataDirectory();
if (dataDirectory.empty()) {
E("Unable to get data directory for crash report attachments");
return false;
}
std::string utf8Path = PathUtils::join(dataDirectory, kHwInfoName);
Win32UnicodeString file_path(utf8Path);
Win32UnicodeString syscmd(HWINFO_CMD);
syscmd.append(file_path);
int result = _wsystem(syscmd.c_str());
if (result != 0) {
E("Unable to get hardware info: %d", errno);
return false;
}
return true;
}
// Convenience function to convert a value to a value in kilobytes
template<typename T>
static T toKB(T value) {
return value / 1024;
}
bool HostCrashService::getMemInfo() {
const std::string& data_directory = getDataDirectory();
if (data_directory.empty()) {
E("Unable to get data directory for crash report attachments");
return false;
}
std::string path = PathUtils::join(data_directory, kMemInfoName);
// TODO: Replace ofstream when we have a good way of handling UTF-8 paths
std::ofstream fout(path.c_str());
if (!fout) {
E("Unable to open '%s' to write crash report attachment", path.c_str());
return false;
}
MEMORYSTATUSEX mem;
mem.dwLength = sizeof(mem);
if (!GlobalMemoryStatusEx(&mem)) {
DWORD error = GetLastError();
E("Failed to get global memory status: %lu", error);
fout << "ERROR: Failed to get global memory status: " << error << "\n";
return false;
}
PERFORMANCE_INFORMATION pi = { sizeof(pi) };
if (!GetPerformanceInfo(&pi, sizeof(pi))) {
DWORD error = GetLastError();
E("Failed to get performance info: %lu", error);
fout << "ERROR: Failed to get performance info: " << error << "\n";
return false;
}
size_t pageSize = pi.PageSize;
fout << "Total physical memory: " << toKB(mem.ullTotalPhys) << " kB\n"
<< "Avail physical memory: " << toKB(mem.ullAvailPhys) << " kB\n"
<< "Total page file: " << toKB(mem.ullTotalPageFile) << " kB\n"
<< "Avail page file: " << toKB(mem.ullAvailPageFile) << " kB\n"
<< "Total virtual: " << toKB(mem.ullTotalVirtual) << " kB\n"
<< "Avail virtual: " << toKB(mem.ullAvailVirtual) << " kB\n"
<< "Commit total: " << toKB(pi.CommitTotal * pageSize) << " kB\n"
<< "Commit limit: " << toKB(pi.CommitLimit * pageSize) << " kB\n"
<< "Commit peak: " << toKB(pi.CommitPeak * pageSize) << " kB\n"
<< "System cache: " << toKB(pi.SystemCache * pageSize) << " kB\n"
<< "Kernel total: " << toKB(pi.KernelTotal * pageSize) << " kB\n"
<< "Kernel paged: " << toKB(pi.KernelPaged * pageSize) << " kB\n"
<< "Kernel nonpaged: " << toKB(pi.KernelNonpaged * pageSize) << " kB\n"
<< "Handle count: " << pi.HandleCount << "\n"
<< "Process count: " << pi.ProcessCount << "\n"
<< "Thread count: " << pi.ThreadCount << "\n";
return fout.good();
}
void HostCrashService::collectProcessList()
{
if (mDataDirectory.empty()) {
return;
}
auto command = android::base::StringFormat(
"tasklist /V >%s\\%s",
mDataDirectory,
CrashReporter::kProcessListFileName);
if (system(command.c_str()) != 0) {
// try to call the "query process *" command, which used to exist
// before the taskkill
command = android::base::StringFormat(
"query process * >%s\\%s",
mDataDirectory,
CrashReporter::kProcessListFileName);
system(command.c_str());
}
}
} // namespace crashreport
} // namespace android