Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash History: initial implementation. #38

Open
wants to merge 3 commits into
base: backtrace
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backtrace/test/test_linux.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ def test_crashpad_uploads
assert_equal payload['format'], 'minidump'
end

def test_crash_loop_detection
exe = 'examples/linux/crash_loop_detection/crash_loop_detection_linux'
def test_crash_history
exe = 'examples/linux/crash_history/crash_history_linux'
ce = Crashpad::Execution.new executable: exe

Dir.mktmpdir do |dir|
Expand Down
4 changes: 2 additions & 2 deletions client/crashpad_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class CrashpadClient {
//! This function must be called prior to `StartHandler()`
//!
//! \return `true` on success. Otherwise `false` with a message logged.
bool EnableCrashLoopDetection();
bool EnableCrashHistory();

//! \brief Detects if safe mode should be enabled
//!
Expand Down Expand Up @@ -853,7 +853,7 @@ class CrashpadClient {
std::wstring ipc_pipe_;
ScopedKernelHANDLE handler_start_thread_;
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
bool crash_loop_detection_ = false;
bool crash_history_ = false;
UUID run_uuid_;
std::set<int> unhandled_signals_;
#endif // BUILDFLAG(IS_APPLE)
Expand Down
24 changes: 12 additions & 12 deletions client/crashpad_client_linux.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
#include "build/buildflag.h"
#include "client/client_argv_handling.h"
#include "third_party/lss/lss.h"
#include "util/backtrace/crash_loop_detection.h"
#include "util/backtrace/crash_history.h"
#include "util/file/file_io.h"
#include "util/file/filesystem.h"
#include "util/linux/exception_handler_client.h"
Expand Down Expand Up @@ -471,9 +471,9 @@ bool CrashpadClient::StartHandler(
argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get()));
argv.push_back("--shared-client-connection");

if (crash_loop_detection_) {
namespace clc = backtrace::crash_loop_detection;
DCHECK(clc::CrashLoopDetectionAppend(database, run_uuid_));
if (crash_history_) {
namespace ch = backtrace::crash_history;
DCHECK(ch::Append(database, run_uuid_));
argv.push_back("--annotation=run-uuid=" + run_uuid_.ToString());
}

Expand All @@ -498,10 +498,10 @@ bool CrashpadClient::StartHandler(
}

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || DOXYGEN
bool CrashpadClient::EnableCrashLoopDetection()
bool CrashpadClient::EnableCrashHistory()
{
crash_loop_detection_ = run_uuid_.InitializeWithNew();
return crash_loop_detection_;
crash_history_ = run_uuid_.InitializeWithNew();
return crash_history_;
}

bool CrashpadClient::IsSafeModeRequired(const base::FilePath& database)
Expand All @@ -511,8 +511,8 @@ bool CrashpadClient::IsSafeModeRequired(const base::FilePath& database)

int CrashpadClient::ConsecutiveCrashesCount(const base::FilePath& database)
{
namespace clc = backtrace::crash_loop_detection;
return clc::ConsecutiveCrashesCount(database);
namespace ch = backtrace::crash_history;
return ch::ConsecutiveCrashesCount(database);
}
#endif

Expand Down Expand Up @@ -741,9 +741,9 @@ bool CrashpadClient::StartHandlerAtCrash(
backtrace::android_cert_store::create(database);
#endif

if (crash_loop_detection_) {
namespace clc = backtrace::crash_loop_detection;
bool ok = clc::CrashLoopDetectionAppend(database, run_uuid_);
if (crash_history_) {
namespace ch = backtrace::crash_history;
bool ok = ch::Append(database, run_uuid_);
DCHECK(ok);
argv.push_back("--annotation=run-uuid=" + run_uuid_.ToString());
}
Expand Down
2 changes: 1 addition & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ endif (WIN32)

if (LINUX)
add_subdirectory(linux/demo)
add_subdirectory(linux/crash_loop_detection)
add_subdirectory(linux/crash_history)
endif (LINUX)
10 changes: 10 additions & 0 deletions examples/linux/crash_history/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.10)
project(crash_history_linux LANGUAGES CXX)

if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
add_subdirectory(crashpad)
endif ()

add_executable(crash_history_linux crash_history.cpp)
target_compile_features(crash_history_linux PRIVATE cxx_std_14)
target_link_libraries(crash_history_linux PRIVATE client)
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
# Linux demo app for crash loop detection
# Linux demo app for Crash History

This demo app immediately crashes and sends report to the Backtrace universe
selected by the user. This demo enables Backtrace's Safe Mode/crash loop
detection feature.
selected by the user. This demo enables Backtrace's Crash History feature.

# Build instructions

Assumptions:
- crashpad is checked out to `~/crashpad`
- the demo app has been copied to `~/crash_loop_detection`
- the demo app has been copied to `~/crash_history`

1. Update your handler path:
```cpp
std::string handler_path("/home/myusername/crash_loop_detection/build/crashpad/handler/handler");
std::string handler_path("/home/myusername/crash_history/build/crashpad/handler/handler");
```
2. Update your upload URL
```cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ startCrashHandler(std::string const& url, std::string const& handler_path,

auto client = CrashpadClient{};

client.EnableCrashLoopDetection();
client.EnableCrashHistory();

std::cout << "Last Runs crashed: " <<
CrashpadClient::ConsecutiveCrashesCount(db) << "\n";
Expand Down
10 changes: 0 additions & 10 deletions examples/linux/crash_loop_detection/CMakeLists.txt

This file was deleted.

8 changes: 4 additions & 4 deletions handler/linux/crash_report_exception_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include "minidump/minidump_file_writer.h"
#include "snapshot/linux/process_snapshot_linux.h"
#include "snapshot/sanitized/process_snapshot_sanitized.h"
#include "util/backtrace/crash_loop_detection.h"
#include "util/backtrace/crash_history.h"
#include "util/file/file_helper.h"
#include "util/file/file_reader.h"
#include "util/file/output_stream_file_writer.h"
Expand Down Expand Up @@ -132,12 +132,12 @@ bool CrashReportExceptionHandler::HandleException(
{
auto it = process_annotations_->find("run-uuid");
if (it != process_annotations_->cend()) {
namespace cld = backtrace::crash_loop_detection;
namespace ch = backtrace::crash_history;
UUID uuid;
uuid.InitializeFromString(it->second);
auto success = cld::CrashLoopDetectionSetCrashed(database_->DatabasePath(), uuid);
auto success = ch::SetCrashed(database_->DatabasePath(), uuid);
if (!success)
LOG(ERROR) << "Failed to set crash loop detection data for '" << it->second
LOG(ERROR) << "Failed to set crash history data for '" << it->second
<< "'";
}
}
Expand Down
4 changes: 2 additions & 2 deletions util/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
set(CRASHPAD_UTIL_LIBRARY_FILES
./backtrace/crash_loop_detection.cc
./backtrace/crash_loop_detection.h
./backtrace/crash_history.cc
./backtrace/crash_history.h
./file/delimited_file_reader.cc
./file/delimited_file_reader.h
./file/directory_reader.h
Expand Down
134 changes: 134 additions & 0 deletions util/backtrace/crash_history.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#include "crash_history.h"

#include <ctime>

#include <iostream>
#include <deque>
#include <fstream>
#include <limits>
#include <sstream>
#include <string>
#include <vector>

#include "build/build_config.h"
#include "util/string/split_string.h"

namespace crashpad {
namespace backtrace {
namespace crash_history {

static auto CsvFileName(const base::FilePath& database)
{
#if BUILDFLAG(IS_WIN)
return database.value() + L"/crash_history.csv";
#else
return database.value() + "/crash_history.csv";
#endif
}

static std::vector<RunEntry>
LoadCrashData(const base::FilePath& database, int max_entries = std::numeric_limits<int>::max())
{
std::vector<RunEntry> entries;
auto csv_file = CsvFileName(database);
std::ifstream f(csv_file);

for (std::string line; std::getline(f, line);) {
if (line.size() && line.back() == '\n')
line.pop_back();
auto split = SplitString(line, ',');
if (split.size() < 3)
continue;
RunEntry entry;
if (!entry.uuid.InitializeFromString(split[0]))
continue;
entry.crashed = split[1] == "1";
try {
entry.start_time = std::stoll(split[2]);
if (split.size() > 3) {
entry.crash_time = std::stoll(split[3]);
}
} catch (const std::invalid_argument& arg) {
continue;
}
entries.push_back(std::move(entry));
}

if (entries.size() > max_entries)
entries.erase(entries.begin(), entries.end() - max_entries);
return entries;
}

static bool WriteCrashData(const base::FilePath& database,
const std::vector<RunEntry>& data)
{
std::deque<std::string> lines;
std::ofstream f(CsvFileName(database), std::ios::trunc);

if (!f)
return false;

for (const auto& entry : data) {
std::stringstream str;
str << entry.uuid.ToString() << ','
<< (entry.crashed ? "1" : "0") << ','
<< entry.start_time;
if (entry.crash_time)
str << ',' << entry.crash_time.value();
str << '\n';

auto line = str.str();
if (line.size()) {
if (&entry == &data.back())
line.pop_back();
}
f << line;
}

return true;
}

bool Append(const base::FilePath& database, UUID uuid, int max_entries)
{
auto entries = LoadCrashData(database, max_entries - 1);

time_t current_time = time(nullptr);
RunEntry entry = {uuid, false, current_time, std::nullopt};
entries.push_back(std::move(entry));

return WriteCrashData(database, entries);
}

bool SetCrashed(const base::FilePath& database, UUID uuid)
{
auto entries = LoadCrashData(database);
bool success{};
std::string uuid_str = uuid.ToString();

for (auto& entry : entries) {
if (entry.uuid == uuid) {
success = true;
entry.crashed = true;
entry.crash_time = time(nullptr);
break;
}
}

return success && WriteCrashData(database, entries);
}

int ConsecutiveCrashesCount(const base::FilePath& database)
{
auto entries = LoadCrashData(database);

auto is_crashing = [](const auto& entry) {
return !entry.crashed;
};

auto it = std::find_if(entries.rbegin(), entries.rend(), is_crashing);
return std::distance(entries.rbegin(), it);
}

} // namespace crash_history
} // namespace backtrace
} // namespace crashpad
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,35 @@

#pragma once

#include <cstdint>
#include <optional>
#include <vector>

#include "base/files/file_path.h"
#include "util/misc/uuid.h"

namespace crashpad {
namespace backtrace {
namespace crash_loop_detection {
namespace crash_history {

struct RunEntry
{
UUID uuid;
bool crashed;
time_t start_time;
std::optional<time_t> crash_time;
};

static constexpr int crash_loop_detection_max_entries = 10;
static constexpr int default_max_entries = 10;

bool CrashLoopDetectionAppend(const base::FilePath& database,
UUID uuid,
int max_entries = crash_loop_detection_max_entries);
bool Append(const base::FilePath& database,
UUID uuid,
int max_entries = default_max_entries);

bool CrashLoopDetectionSetCrashed(const base::FilePath& database, UUID uuid);
bool SetCrashed(const base::FilePath& database, UUID uuid);

int ConsecutiveCrashesCount(const base::FilePath& database);

} // crash_loop_detection
} // crash_history
} // backtrace
} // crashpad
Loading