[log minidump] add option to log minidump in handler_main

- Add option to log minidump in handler_main, also add option to
  disable to dump minidump and generate report.
- Implement log minidump in CrashReportExceptionHandler.

Bug: crashpad:308
Change-Id: I8d2f7e118912011a8416f1ec36c9ee9d561d06e6
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/1995825
Commit-Queue: Tao Bai <michaelbai@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Tao Bai 2020-01-28 12:02:55 -08:00 committed by Commit Bot
parent f36f46d1ec
commit 915862984c
11 changed files with 403 additions and 14 deletions

View File

@ -233,6 +233,12 @@ establish the Crashpad client environment before running a program.
for use with collection servers that dont accept uploads compressed in this for use with collection servers that dont accept uploads compressed in this
way. way.
* **--no-write-minidump-to-database**
Do not write the minidump to database. Normally, the minidump is written to
database for upload. Use this option with **--write-minidump-to-log** to
only write the minidump to log. This option is only available to Android.
* **--pipe-name**=_PIPE_ * **--pipe-name**=_PIPE_
Listen on the given pipe name for connections from clients. _PIPE_ must be of Listen on the given pipe name for connections from clients. _PIPE_ must be of
@ -286,6 +292,12 @@ establish the Crashpad client environment before running a program.
is still used for Crashpad settings. This option is only valid on Chromium is still used for Crashpad settings. This option is only valid on Chromium
OS. OS.
* **--write-minidump-to-log**
Write the minidump to log. By default the minidump is only written to
database. Use this option with **--no-write-minidump-to-database** to only
write the minidump to log. This option is only available to Android.
* **--help** * **--help**
Display help and exit. Display help and exit.

View File

@ -143,6 +143,10 @@ void Usage(const base::FilePath& me) {
" --no-periodic-tasks don't scan for new reports or prune the database\n" " --no-periodic-tasks don't scan for new reports or prune the database\n"
" --no-rate-limit don't rate limit crash uploads\n" " --no-rate-limit don't rate limit crash uploads\n"
" --no-upload-gzip don't use gzip compression when uploading\n" " --no-upload-gzip don't use gzip compression when uploading\n"
#if defined(OS_ANDROID)
" --no-write-minidump-to-database\n"
" don't write minidump to database\n"
#endif // OS_ANDROID
#if defined(OS_WIN) #if defined(OS_WIN)
" --pipe-name=PIPE communicate with the client over PIPE\n" " --pipe-name=PIPE communicate with the client over PIPE\n"
#endif // OS_WIN #endif // OS_WIN
@ -173,6 +177,9 @@ void Usage(const base::FilePath& me) {
" crash_reporter, thus skipping metrics consent\n" " crash_reporter, thus skipping metrics consent\n"
" checks\n" " checks\n"
#endif // OS_CHROMEOS #endif // OS_CHROMEOS
#if defined(OS_ANDROID)
" --write-minidump-to-log write minidump to log\n"
#endif // OS_ANDROID
" --help display this help and exit\n" " --help display this help and exit\n"
" --version output version information and exit\n", " --version output version information and exit\n",
me.value().c_str()); me.value().c_str());
@ -195,6 +202,10 @@ struct Options {
VMAddress sanitization_information_address; VMAddress sanitization_information_address;
int initial_client_fd; int initial_client_fd;
bool shared_client_connection; bool shared_client_connection;
#if defined(OS_ANDROID)
bool write_minidump_to_log;
bool write_minidump_to_database;
#endif // OS_ANDROID
#elif defined(OS_WIN) #elif defined(OS_WIN)
std::string pipe_name; std::string pipe_name;
InitialClientData initial_client_data; InitialClientData initial_client_data;
@ -551,6 +562,9 @@ int HandlerMain(int argc,
kOptionNoPeriodicTasks, kOptionNoPeriodicTasks,
kOptionNoRateLimit, kOptionNoRateLimit,
kOptionNoUploadGzip, kOptionNoUploadGzip,
#if defined(OS_ANDROID)
kOptionNoWriteMinidumpToDatabase,
#endif // OS_ANDROID
#if defined(OS_WIN) #if defined(OS_WIN)
kOptionPipeName, kOptionPipeName,
#endif // OS_WIN #endif // OS_WIN
@ -568,6 +582,9 @@ int HandlerMain(int argc,
kOptionMinidumpDirForTests, kOptionMinidumpDirForTests,
kOptionAlwaysAllowFeedback, kOptionAlwaysAllowFeedback,
#endif // OS_CHROMEOS #endif // OS_CHROMEOS
#if defined(OS_ANDROID)
kOptionWriteMinidumpToLog,
#endif // OS_ANDROID
// Standard options. // Standard options.
kOptionHelp = -2, kOptionHelp = -2,
@ -609,6 +626,12 @@ int HandlerMain(int argc,
{"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks}, {"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks},
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit}, {"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
{"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip}, {"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip},
#if defined(OS_ANDROID)
{"no-write-minidump-to-database",
no_argument,
nullptr,
kOptionNoWriteMinidumpToDatabase},
#endif // OS_ANDROID
#if defined(OS_WIN) #if defined(OS_WIN)
{"pipe-name", required_argument, nullptr, kOptionPipeName}, {"pipe-name", required_argument, nullptr, kOptionPipeName},
#endif // OS_WIN #endif // OS_WIN
@ -647,6 +670,9 @@ int HandlerMain(int argc,
nullptr, nullptr,
kOptionAlwaysAllowFeedback}, kOptionAlwaysAllowFeedback},
#endif // OS_CHROMEOS #endif // OS_CHROMEOS
#if defined(OS_ANDROID)
{"write-minidump-to-log", no_argument, nullptr, kOptionWriteMinidumpToLog},
#endif // OS_ANDROID
{"help", no_argument, nullptr, kOptionHelp}, {"help", no_argument, nullptr, kOptionHelp},
{"version", no_argument, nullptr, kOptionVersion}, {"version", no_argument, nullptr, kOptionVersion},
{nullptr, 0, nullptr, 0}, {nullptr, 0, nullptr, 0},
@ -663,6 +689,9 @@ int HandlerMain(int argc,
options.periodic_tasks = true; options.periodic_tasks = true;
options.rate_limit = true; options.rate_limit = true;
options.upload_gzip = true; options.upload_gzip = true;
#if defined(OS_ANDROID)
options.write_minidump_to_database = true;
#endif
int opt; int opt;
while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
@ -749,6 +778,12 @@ int HandlerMain(int argc,
options.upload_gzip = false; options.upload_gzip = false;
break; break;
} }
#if defined(OS_ANDROID)
case kOptionNoWriteMinidumpToDatabase: {
options.write_minidump_to_database = false;
break;
}
#endif // OS_ANDROID
#if defined(OS_WIN) #if defined(OS_WIN)
case kOptionPipeName: { case kOptionPipeName: {
options.pipe_name = optarg; options.pipe_name = optarg;
@ -803,6 +838,12 @@ int HandlerMain(int argc,
break; break;
} }
#endif // OS_CHROMEOS #endif // OS_CHROMEOS
#if defined(OS_ANDROID)
case kOptionWriteMinidumpToLog: {
options.write_minidump_to_log = true;
break;
}
#endif // OS_ANDROID
case kOptionHelp: { case kOptionHelp: {
Usage(me); Usage(me);
MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly); MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
@ -863,6 +904,14 @@ int HandlerMain(int argc,
me, "--shared-client-connection requires --initial-client-fd"); me, "--shared-client-connection requires --initial-client-fd");
return ExitFailure(); return ExitFailure();
} }
#if defined(OS_ANDROID)
if (!options.write_minidump_to_log && !options.write_minidump_to_database) {
ToolSupport::UsageHint(me,
"--no_write_minidump_to_database is required to use "
"with --write_minidump_to_log.");
ExitFailure();
}
#endif // OS_ANDROID
#endif // OS_MACOSX #endif // OS_MACOSX
if (options.database.empty()) { if (options.database.empty()) {
@ -966,8 +1015,17 @@ int HandlerMain(int argc,
// TODO(scottmg): Process level file attachments, and for all platforms. // TODO(scottmg): Process level file attachments, and for all platforms.
nullptr, nullptr,
#endif #endif
user_stream_sources); #if defined(OS_ANDROID)
options.write_minidump_to_database,
options.write_minidump_to_log,
#endif // OS_ANDROID
#if defined(OS_LINUX)
true,
false,
#endif // OS_LINUX
#endif // OS_CHROMEOS #endif // OS_CHROMEOS
user_stream_sources);
#if defined(OS_LINUX) || defined(OS_ANDROID) #if defined(OS_LINUX) || defined(OS_ANDROID)
if (options.exception_information_address) { if (options.exception_information_address) {

View File

@ -23,23 +23,56 @@
#include "minidump/minidump_file_writer.h" #include "minidump/minidump_file_writer.h"
#include "snapshot/linux/process_snapshot_linux.h" #include "snapshot/linux/process_snapshot_linux.h"
#include "snapshot/sanitized/process_snapshot_sanitized.h" #include "snapshot/sanitized/process_snapshot_sanitized.h"
#include "util/file/file_reader.h"
#include "util/file/output_stream_file_writer.h"
#include "util/linux/direct_ptrace_connection.h" #include "util/linux/direct_ptrace_connection.h"
#include "util/linux/ptrace_client.h" #include "util/linux/ptrace_client.h"
#include "util/misc/implicit_cast.h" #include "util/misc/implicit_cast.h"
#include "util/misc/metrics.h" #include "util/misc/metrics.h"
#include "util/misc/uuid.h" #include "util/misc/uuid.h"
#include "util/stream/base94_output_stream.h"
#include "util/stream/log_output_stream.h"
#include "util/stream/zlib_output_stream.h"
namespace crashpad { namespace crashpad {
namespace {
bool WriteMinidumpLogFromFile(FileReaderInterface* file_reader) {
ZlibOutputStream stream(ZlibOutputStream::Mode::kCompress,
std::make_unique<Base94OutputStream>(
Base94OutputStream::Mode::kEncode,
std::make_unique<LogOutputStream>()));
FileOperationResult read_result;
do {
uint8_t buffer[4096];
read_result = file_reader->Read(buffer, sizeof(buffer));
if (read_result < 0)
return false;
if (read_result > 0 && (!stream.Write(buffer, read_result)))
return false;
} while (read_result > 0);
return stream.Flush();
}
} // namespace
CrashReportExceptionHandler::CrashReportExceptionHandler( CrashReportExceptionHandler::CrashReportExceptionHandler(
CrashReportDatabase* database, CrashReportDatabase* database,
CrashReportUploadThread* upload_thread, CrashReportUploadThread* upload_thread,
const std::map<std::string, std::string>* process_annotations, const std::map<std::string, std::string>* process_annotations,
bool write_minidump_to_database,
bool write_minidump_to_log,
const UserStreamDataSources* user_stream_data_sources) const UserStreamDataSources* user_stream_data_sources)
: database_(database), : database_(database),
upload_thread_(upload_thread), upload_thread_(upload_thread),
process_annotations_(process_annotations), process_annotations_(process_annotations),
user_stream_data_sources_(user_stream_data_sources) {} write_minidump_to_database_(write_minidump_to_database),
write_minidump_to_log_(write_minidump_to_log),
user_stream_data_sources_(user_stream_data_sources) {
DCHECK(write_minidump_to_database_ | write_minidump_to_log_);
}
CrashReportExceptionHandler::~CrashReportExceptionHandler() = default; CrashReportExceptionHandler::~CrashReportExceptionHandler() = default;
@ -116,6 +149,20 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection(
} }
process_snapshot->SetClientID(client_id); process_snapshot->SetClientID(client_id);
return write_minidump_to_database_
? WriteMinidumpToDatabase(process_snapshot.get(),
sanitized_snapshot.get(),
write_minidump_to_log_,
local_report_id)
: WriteMinidumpToLog(process_snapshot.get(),
sanitized_snapshot.get());
}
bool CrashReportExceptionHandler::WriteMinidumpToDatabase(
ProcessSnapshotLinux* process_snapshot,
ProcessSnapshotSanitized* sanitized_snapshot,
bool write_minidump_to_log,
UUID* local_report_id) {
std::unique_ptr<CrashReportDatabase::NewReport> new_report; std::unique_ptr<CrashReportDatabase::NewReport> new_report;
CrashReportDatabase::OperationStatus database_status = CrashReportDatabase::OperationStatus database_status =
database_->PrepareNewCrashReport(&new_report); database_->PrepareNewCrashReport(&new_report);
@ -129,9 +176,8 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection(
process_snapshot->SetReportID(new_report->ReportID()); process_snapshot->SetReportID(new_report->ReportID());
ProcessSnapshot* snapshot = ProcessSnapshot* snapshot =
sanitized_snapshot sanitized_snapshot ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot)
? implicit_cast<ProcessSnapshot*>(sanitized_snapshot.get()) : implicit_cast<ProcessSnapshot*>(process_snapshot);
: implicit_cast<ProcessSnapshot*>(process_snapshot.get());
MinidumpFileWriter minidump; MinidumpFileWriter minidump;
minidump.InitializeFromSnapshot(snapshot); minidump.InitializeFromSnapshot(snapshot);
@ -144,6 +190,16 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection(
return false; return false;
} }
bool write_minidump_to_log_succeed = false;
if (write_minidump_to_log) {
if (auto* file_reader = new_report->Reader()) {
if (WriteMinidumpLogFromFile(file_reader))
write_minidump_to_log_succeed = true;
else
LOG(ERROR) << "WriteMinidumpLogFromFile failed";
}
}
UUID uuid; UUID uuid;
database_status = database_status =
database_->FinishedWritingCrashReport(std::move(new_report), &uuid); database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
@ -163,7 +219,30 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection(
} }
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess); Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
return true;
return write_minidump_to_log ? write_minidump_to_log_succeed : true;
}
bool CrashReportExceptionHandler::WriteMinidumpToLog(
ProcessSnapshotLinux* process_snapshot,
ProcessSnapshotSanitized* sanitized_snapshot) {
ProcessSnapshot* snapshot =
sanitized_snapshot ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot)
: implicit_cast<ProcessSnapshot*>(process_snapshot);
MinidumpFileWriter minidump;
minidump.InitializeFromSnapshot(snapshot);
AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
OutputStreamFileWriter writer(std::make_unique<ZlibOutputStream>(
ZlibOutputStream::Mode::kCompress,
std::make_unique<Base94OutputStream>(
Base94OutputStream::Mode::kEncode,
std::make_unique<LogOutputStream>())));
if (!minidump.WriteMinidump(&writer, false /* allow_seek */)) {
LOG(ERROR) << "WriteMinidump failed";
return false;
}
return writer.Flush();
} }
} // namespace crashpad } // namespace crashpad

View File

@ -30,6 +30,9 @@
namespace crashpad { namespace crashpad {
class ProcessSnapshotLinux;
class ProcessSnapshotSanitized;
//! \brief An exception handler that writes crash reports for exceptions //! \brief An exception handler that writes crash reports for exceptions
//! to a CrashReportDatabase. //! to a CrashReportDatabase.
class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
@ -50,6 +53,10 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
//! To interoperate with Breakpad servers, the recommended practice is to //! To interoperate with Breakpad servers, the recommended practice is to
//! specify values for the `"prod"` and `"ver"` keys as process //! specify values for the `"prod"` and `"ver"` keys as process
//! annotations. //! annotations.
//! \param[in] write_minidump_to_database Whether the minidump shall be
//! written to database.
//! \param[in] write_minidump_to_log Whether the minidump shall be written to
//! log.
//! \param[in] user_stream_data_sources Data sources to be used to extend //! \param[in] user_stream_data_sources Data sources to be used to extend
//! crash reports. For each crash report that is written, the data sources //! crash reports. For each crash report that is written, the data sources
//! are called in turn. These data sources may contribute additional //! are called in turn. These data sources may contribute additional
@ -58,6 +65,8 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
CrashReportDatabase* database, CrashReportDatabase* database,
CrashReportUploadThread* upload_thread, CrashReportUploadThread* upload_thread,
const std::map<std::string, std::string>* process_annotations, const std::map<std::string, std::string>* process_annotations,
bool write_minidump_to_database,
bool write_minidump_to_log,
const UserStreamDataSources* user_stream_data_sources); const UserStreamDataSources* user_stream_data_sources);
~CrashReportExceptionHandler() override; ~CrashReportExceptionHandler() override;
@ -87,9 +96,18 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
pid_t* requesting_thread_id, pid_t* requesting_thread_id,
UUID* local_report_id = nullptr); UUID* local_report_id = nullptr);
bool WriteMinidumpToDatabase(ProcessSnapshotLinux* process_snapshot,
ProcessSnapshotSanitized* sanitized_snapshot,
bool write_minidump_to_log,
UUID* local_report_id);
bool WriteMinidumpToLog(ProcessSnapshotLinux* process_snapshot,
ProcessSnapshotSanitized* sanitized_snapshot);
CrashReportDatabase* database_; // weak CrashReportDatabase* database_; // weak
CrashReportUploadThread* upload_thread_; // weak CrashReportUploadThread* upload_thread_; // weak
const std::map<std::string, std::string>* process_annotations_; // weak const std::map<std::string, std::string>* process_annotations_; // weak
bool write_minidump_to_database_;
bool write_minidump_to_log_;
const UserStreamDataSources* user_stream_data_sources_; // weak const UserStreamDataSources* user_stream_data_sources_; // weak
DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);

View File

@ -211,17 +211,30 @@ bool MinidumpFileWriter::AddUserExtensionStream(
} }
bool MinidumpFileWriter::WriteEverything(FileWriterInterface* file_writer) { bool MinidumpFileWriter::WriteEverything(FileWriterInterface* file_writer) {
return WriteMinidump(file_writer, true);
}
bool MinidumpFileWriter::WriteMinidump(FileWriterInterface* file_writer,
bool allow_seek) {
DCHECK_EQ(state(), kStateMutable); DCHECK_EQ(state(), kStateMutable);
FileOffset start_offset = file_writer->Seek(0, SEEK_CUR); FileOffset start_offset = -1;
if (start_offset < 0) { if (allow_seek) {
return false; start_offset = file_writer->Seek(0, SEEK_CUR);
if (start_offset < 0) {
return false;
}
} else {
header_.Signature = MINIDUMP_SIGNATURE;
} }
if (!MinidumpWritable::WriteEverything(file_writer)) { if (!MinidumpWritable::WriteEverything(file_writer)) {
return false; return false;
} }
if (!allow_seek)
return true;
FileOffset end_offset = file_writer->Seek(0, SEEK_CUR); FileOffset end_offset = file_writer->Seek(0, SEEK_CUR);
if (end_offset < 0) { if (end_offset < 0) {
return false; return false;

View File

@ -134,6 +134,21 @@ class MinidumpFileWriter final : public internal::MinidumpWritable {
//! mistaken for valid ones. //! mistaken for valid ones.
bool WriteEverything(FileWriterInterface* file_writer) override; bool WriteEverything(FileWriterInterface* file_writer) override;
//! \brief Writes this object to a minidump file.
//!
//! Same as \a WriteEverything, but give the option to disable the seek. It
//! is typically used to write to stream backed \a FileWriterInterface which
//! doesn't support seek.
//!
//! \param[in] file_writer The file writer to receive the minidump files
//! content.
//!
//! \param[in] allow_seek Whether seek is allowed.
//!
//! \return `true` on success. `false` on failure, with an appropriate message
//! logged.
bool WriteMinidump(FileWriterInterface* file_writer, bool allow_seek);
protected: protected:
// MinidumpWritable: // MinidumpWritable:
bool Freeze() override; bool Freeze() override;

View File

@ -36,7 +36,9 @@
#include "snapshot/test/test_system_snapshot.h" #include "snapshot/test/test_system_snapshot.h"
#include "snapshot/test/test_thread_snapshot.h" #include "snapshot/test/test_thread_snapshot.h"
#include "test/gtest_death.h" #include "test/gtest_death.h"
#include "util/file/output_stream_file_writer.h"
#include "util/file/string_file.h" #include "util/file/string_file.h"
#include "util/stream/output_stream_interface.h"
namespace crashpad { namespace crashpad {
namespace test { namespace test {
@ -88,6 +90,22 @@ class TestStream final : public internal::MinidumpStreamWriter {
DISALLOW_COPY_AND_ASSIGN(TestStream); DISALLOW_COPY_AND_ASSIGN(TestStream);
}; };
class StringFileOutputStream : public OutputStreamInterface {
public:
StringFileOutputStream() = default;
~StringFileOutputStream() override = default;
bool Write(const uint8_t* data, size_t size) override {
return string_file_.Write(data, size);
}
bool Flush() override { return true; }
const StringFile& string_file() const { return string_file_; }
private:
StringFile string_file_;
DISALLOW_COPY_AND_ASSIGN(StringFileOutputStream);
};
TEST(MinidumpFileWriter, OneStream) { TEST(MinidumpFileWriter, OneStream) {
MinidumpFileWriter minidump_file; MinidumpFileWriter minidump_file;
constexpr time_t kTimestamp = 0x155d2fb8; constexpr time_t kTimestamp = 0x155d2fb8;
@ -578,6 +596,51 @@ TEST(MinidumpFileWriter, SameStreamType) {
EXPECT_EQ(memcmp(stream_data, expected_stream.c_str(), kStream0Size), 0); EXPECT_EQ(memcmp(stream_data, expected_stream.c_str(), kStream0Size), 0);
} }
TEST(MinidumpFileWriter, WriteMinidumpDisallowSeek) {
MinidumpFileWriter minidump_file;
constexpr time_t kTimestamp = 0x155d2fb8;
minidump_file.SetTimestamp(kTimestamp);
constexpr size_t kStreamSize = 5;
constexpr MinidumpStreamType kStreamType =
static_cast<MinidumpStreamType>(0x4d);
constexpr uint8_t kStreamValue = 0x5a;
auto stream =
std::make_unique<TestStream>(kStreamType, kStreamSize, kStreamValue);
ASSERT_TRUE(minidump_file.AddStream(std::move(stream)));
std::unique_ptr<StringFileOutputStream> string_file_output_stream =
std::make_unique<StringFileOutputStream>();
const StringFile& string_file = string_file_output_stream->string_file();
OutputStreamFileWriter output_stream(std::move(string_file_output_stream));
ASSERT_TRUE(minidump_file.WriteMinidump(&output_stream, false));
ASSERT_TRUE(output_stream.Flush());
constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
constexpr size_t kStreamOffset =
kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
constexpr size_t kFileSize = kStreamOffset + kStreamSize;
ASSERT_EQ(string_file.string().size(), kFileSize);
const MINIDUMP_DIRECTORY* directory;
const MINIDUMP_HEADER* header =
MinidumpHeaderAtStart(string_file.string(), &directory);
ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp));
ASSERT_TRUE(directory);
EXPECT_EQ(directory[0].StreamType, kStreamType);
EXPECT_EQ(directory[0].Location.DataSize, kStreamSize);
EXPECT_EQ(directory[0].Location.Rva, kStreamOffset);
const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor<uint8_t>(
string_file.string(), directory[0].Location);
ASSERT_TRUE(stream_data);
std::string expected_stream(kStreamSize, kStreamValue);
EXPECT_EQ(memcmp(stream_data, expected_stream.c_str(), kStreamSize), 0);
}
} // namespace } // namespace
} // namespace test } // namespace test
} // namespace crashpad } // namespace crashpad

View File

@ -56,7 +56,6 @@ crashpad_executable("base94_encoder") {
":tool_support", ":tool_support",
"../build:default_exe_manifest_win", "../build:default_exe_manifest_win",
"../third_party/mini_chromium:base", "../third_party/mini_chromium:base",
"../third_party/zlib",
"../util", "../util",
] ]
} }

View File

@ -91,6 +91,8 @@ static_library("util") {
"file/file_writer.cc", "file/file_writer.cc",
"file/file_writer.h", "file/file_writer.h",
"file/filesystem.h", "file/filesystem.h",
"file/output_stream_file_writer.cc",
"file/output_stream_file_writer.h",
"file/scoped_remove_file.cc", "file/scoped_remove_file.cc",
"file/scoped_remove_file.h", "file/scoped_remove_file.h",
"file/string_file.cc", "file/string_file.cc",
@ -466,13 +468,13 @@ static_library("util") {
include_dirs = [ "$root_gen_dir/third_party/crashpad/crashpad" ] include_dirs = [ "$root_gen_dir/third_party/crashpad/crashpad" ]
} }
public_deps = [ "../compat" ] public_deps = [
"../compat",
deps += [
"../third_party/mini_chromium:base",
"../third_party/zlib", "../third_party/zlib",
] ]
deps += [ "../third_party/mini_chromium:base" ]
if (crashpad_is_mac) { if (crashpad_is_mac) {
libs = [ libs = [
"bsm", "bsm",

View File

@ -0,0 +1,68 @@
// Copyright 2020 The Crashpad Authors. 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 "util/file/output_stream_file_writer.h"
#include "base/logging.h"
#include "util/stream/output_stream_interface.h"
namespace crashpad {
OutputStreamFileWriter::OutputStreamFileWriter(
std::unique_ptr<OutputStreamInterface> output_stream)
: output_stream_(std::move(output_stream)),
flush_needed_(false),
flushed_(false) {}
OutputStreamFileWriter::~OutputStreamFileWriter() {
DCHECK(!flush_needed_);
}
bool OutputStreamFileWriter::Write(const void* data, size_t size) {
DCHECK(!flushed_);
flush_needed_ =
output_stream_->Write(static_cast<const uint8_t*>(data), size);
return flush_needed_;
}
bool OutputStreamFileWriter::WriteIoVec(std::vector<WritableIoVec>* iovecs) {
DCHECK(!flushed_);
flush_needed_ = true;
if (iovecs->empty()) {
LOG(ERROR) << "no iovecs";
flush_needed_ = false;
return false;
}
for (const WritableIoVec& iov : *iovecs) {
if (!output_stream_->Write(static_cast<const uint8_t*>(iov.iov_base),
iov.iov_len)) {
flush_needed_ = false;
return false;
}
}
return true;
}
FileOffset OutputStreamFileWriter::Seek(FileOffset offset, int whence) {
NOTREACHED();
return -1;
}
bool OutputStreamFileWriter::Flush() {
flush_needed_ = false;
flushed_ = true;
return output_stream_->Flush();
}
} // namespace crashpad

View File

@ -0,0 +1,62 @@
// Copyright 2020 The Crashpad Authors. 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.
#ifndef CRASHPAD_UTIL_FILE_OUTPUT_STREAM_FILE_WRITER_H_
#define CRASHPAD_UTIL_FILE_OUTPUT_STREAM_FILE_WRITER_H_
#include <memory>
#include "base/macros.h"
#include "util/file/file_writer.h"
namespace crashpad {
class OutputStreamInterface;
//! \brief A file writer backed by a OutputSteamInterface.
//! \note The \a Seek related methods don't work and shouldn't be invoked.
class OutputStreamFileWriter : public FileWriterInterface {
public:
//! \param[in] output_stream The output stream that this object writes to.
explicit OutputStreamFileWriter(
std::unique_ptr<OutputStreamInterface> output_stream);
~OutputStreamFileWriter() override;
// FileWriterInterface:
bool Write(const void* data, size_t size) override;
bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override;
// FileSeekerInterface:
//! \copydoc FileWriterInterface::Seek()
//!
//! \note This method doesn't work and shouldn't be invoked.
FileOffset Seek(FileOffset offset, int whence) override;
//! \brief Flush data to output_stream.
//!
//! Either \a Write() or \a WriteIoVec() can't be called afterwards.
bool Flush();
private:
std::unique_ptr<OutputStreamInterface> output_stream_;
bool flush_needed_;
bool flushed_;
DISALLOW_COPY_AND_ASSIGN(OutputStreamFileWriter);
};
} // namespace crashpad
#endif // CRASHPAD_UTIL_FILE_OUTPUT_STREAM_FILE_WRITER_H_