From 915862984c1e727bc16594aafb9f586e9a587eb3 Mon Sep 17 00:00:00 2001 From: Tao Bai Date: Tue, 28 Jan 2020 12:02:55 -0800 Subject: [PATCH] [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 Reviewed-by: Joshua Peraza --- handler/crashpad_handler.md | 12 +++ handler/handler_main.cc | 60 ++++++++++++- .../linux/crash_report_exception_handler.cc | 89 +++++++++++++++++-- .../linux/crash_report_exception_handler.h | 18 ++++ minidump/minidump_file_writer.cc | 19 +++- minidump/minidump_file_writer.h | 15 ++++ minidump/minidump_file_writer_test.cc | 63 +++++++++++++ tools/BUILD.gn | 1 - util/BUILD.gn | 10 ++- util/file/output_stream_file_writer.cc | 68 ++++++++++++++ util/file/output_stream_file_writer.h | 62 +++++++++++++ 11 files changed, 403 insertions(+), 14 deletions(-) create mode 100644 util/file/output_stream_file_writer.cc create mode 100644 util/file/output_stream_file_writer.h diff --git a/handler/crashpad_handler.md b/handler/crashpad_handler.md index f7e63a50..6d055ed1 100644 --- a/handler/crashpad_handler.md +++ b/handler/crashpad_handler.md @@ -233,6 +233,12 @@ establish the Crashpad client environment before running a program. for use with collection servers that don’t accept uploads compressed in this 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_ 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 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** Display help and exit. diff --git a/handler/handler_main.cc b/handler/handler_main.cc index 1cf655ae..81c9a00a 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -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-rate-limit don't rate limit crash uploads\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) " --pipe-name=PIPE communicate with the client over PIPE\n" #endif // OS_WIN @@ -173,6 +177,9 @@ void Usage(const base::FilePath& me) { " crash_reporter, thus skipping metrics consent\n" " checks\n" #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" " --version output version information and exit\n", me.value().c_str()); @@ -195,6 +202,10 @@ struct Options { VMAddress sanitization_information_address; int initial_client_fd; 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) std::string pipe_name; InitialClientData initial_client_data; @@ -551,6 +562,9 @@ int HandlerMain(int argc, kOptionNoPeriodicTasks, kOptionNoRateLimit, kOptionNoUploadGzip, +#if defined(OS_ANDROID) + kOptionNoWriteMinidumpToDatabase, +#endif // OS_ANDROID #if defined(OS_WIN) kOptionPipeName, #endif // OS_WIN @@ -568,6 +582,9 @@ int HandlerMain(int argc, kOptionMinidumpDirForTests, kOptionAlwaysAllowFeedback, #endif // OS_CHROMEOS +#if defined(OS_ANDROID) + kOptionWriteMinidumpToLog, +#endif // OS_ANDROID // Standard options. kOptionHelp = -2, @@ -609,6 +626,12 @@ int HandlerMain(int argc, {"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks}, {"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit}, {"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) {"pipe-name", required_argument, nullptr, kOptionPipeName}, #endif // OS_WIN @@ -647,6 +670,9 @@ int HandlerMain(int argc, nullptr, kOptionAlwaysAllowFeedback}, #endif // OS_CHROMEOS +#if defined(OS_ANDROID) + {"write-minidump-to-log", no_argument, nullptr, kOptionWriteMinidumpToLog}, +#endif // OS_ANDROID {"help", no_argument, nullptr, kOptionHelp}, {"version", no_argument, nullptr, kOptionVersion}, {nullptr, 0, nullptr, 0}, @@ -663,6 +689,9 @@ int HandlerMain(int argc, options.periodic_tasks = true; options.rate_limit = true; options.upload_gzip = true; +#if defined(OS_ANDROID) + options.write_minidump_to_database = true; +#endif int opt; while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { @@ -749,6 +778,12 @@ int HandlerMain(int argc, options.upload_gzip = false; break; } +#if defined(OS_ANDROID) + case kOptionNoWriteMinidumpToDatabase: { + options.write_minidump_to_database = false; + break; + } +#endif // OS_ANDROID #if defined(OS_WIN) case kOptionPipeName: { options.pipe_name = optarg; @@ -803,6 +838,12 @@ int HandlerMain(int argc, break; } #endif // OS_CHROMEOS +#if defined(OS_ANDROID) + case kOptionWriteMinidumpToLog: { + options.write_minidump_to_log = true; + break; + } +#endif // OS_ANDROID case kOptionHelp: { Usage(me); MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly); @@ -863,6 +904,14 @@ int HandlerMain(int argc, me, "--shared-client-connection requires --initial-client-fd"); 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 if (options.database.empty()) { @@ -966,8 +1015,17 @@ int HandlerMain(int argc, // TODO(scottmg): Process level file attachments, and for all platforms. nullptr, #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 + user_stream_sources); + #if defined(OS_LINUX) || defined(OS_ANDROID) if (options.exception_information_address) { diff --git a/handler/linux/crash_report_exception_handler.cc b/handler/linux/crash_report_exception_handler.cc index 9b4f2012..29f6d0db 100644 --- a/handler/linux/crash_report_exception_handler.cc +++ b/handler/linux/crash_report_exception_handler.cc @@ -23,23 +23,56 @@ #include "minidump/minidump_file_writer.h" #include "snapshot/linux/process_snapshot_linux.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/ptrace_client.h" #include "util/misc/implicit_cast.h" #include "util/misc/metrics.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 { + +bool WriteMinidumpLogFromFile(FileReaderInterface* file_reader) { + ZlibOutputStream stream(ZlibOutputStream::Mode::kCompress, + std::make_unique( + Base94OutputStream::Mode::kEncode, + std::make_unique())); + 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( CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, + bool write_minidump_to_database, + bool write_minidump_to_log, const UserStreamDataSources* user_stream_data_sources) : database_(database), upload_thread_(upload_thread), 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; @@ -116,6 +149,20 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection( } 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 new_report; CrashReportDatabase::OperationStatus database_status = database_->PrepareNewCrashReport(&new_report); @@ -129,9 +176,8 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection( process_snapshot->SetReportID(new_report->ReportID()); ProcessSnapshot* snapshot = - sanitized_snapshot - ? implicit_cast(sanitized_snapshot.get()) - : implicit_cast(process_snapshot.get()); + sanitized_snapshot ? implicit_cast(sanitized_snapshot) + : implicit_cast(process_snapshot); MinidumpFileWriter minidump; minidump.InitializeFromSnapshot(snapshot); @@ -144,6 +190,16 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection( 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; database_status = database_->FinishedWritingCrashReport(std::move(new_report), &uuid); @@ -163,7 +219,30 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection( } 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(sanitized_snapshot) + : implicit_cast(process_snapshot); + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(snapshot); + AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump); + + OutputStreamFileWriter writer(std::make_unique( + ZlibOutputStream::Mode::kCompress, + std::make_unique( + Base94OutputStream::Mode::kEncode, + std::make_unique()))); + if (!minidump.WriteMinidump(&writer, false /* allow_seek */)) { + LOG(ERROR) << "WriteMinidump failed"; + return false; + } + return writer.Flush(); } } // namespace crashpad diff --git a/handler/linux/crash_report_exception_handler.h b/handler/linux/crash_report_exception_handler.h index 522a77de..83d07c0b 100644 --- a/handler/linux/crash_report_exception_handler.h +++ b/handler/linux/crash_report_exception_handler.h @@ -30,6 +30,9 @@ namespace crashpad { +class ProcessSnapshotLinux; +class ProcessSnapshotSanitized; + //! \brief An exception handler that writes crash reports for exceptions //! to a CrashReportDatabase. class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { @@ -50,6 +53,10 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { //! To interoperate with Breakpad servers, the recommended practice is to //! specify values for the `"prod"` and `"ver"` keys as process //! 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 //! crash reports. For each crash report that is written, the data sources //! are called in turn. These data sources may contribute additional @@ -58,6 +65,8 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, + bool write_minidump_to_database, + bool write_minidump_to_log, const UserStreamDataSources* user_stream_data_sources); ~CrashReportExceptionHandler() override; @@ -87,9 +96,18 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { pid_t* requesting_thread_id, 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 CrashReportUploadThread* upload_thread_; // weak const std::map* process_annotations_; // weak + bool write_minidump_to_database_; + bool write_minidump_to_log_; const UserStreamDataSources* user_stream_data_sources_; // weak DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc index f29a6fae..6727a0dc 100644 --- a/minidump/minidump_file_writer.cc +++ b/minidump/minidump_file_writer.cc @@ -211,17 +211,30 @@ bool MinidumpFileWriter::AddUserExtensionStream( } bool MinidumpFileWriter::WriteEverything(FileWriterInterface* file_writer) { + return WriteMinidump(file_writer, true); +} + +bool MinidumpFileWriter::WriteMinidump(FileWriterInterface* file_writer, + bool allow_seek) { DCHECK_EQ(state(), kStateMutable); - FileOffset start_offset = file_writer->Seek(0, SEEK_CUR); - if (start_offset < 0) { - return false; + FileOffset start_offset = -1; + if (allow_seek) { + start_offset = file_writer->Seek(0, SEEK_CUR); + if (start_offset < 0) { + return false; + } + } else { + header_.Signature = MINIDUMP_SIGNATURE; } if (!MinidumpWritable::WriteEverything(file_writer)) { return false; } + if (!allow_seek) + return true; + FileOffset end_offset = file_writer->Seek(0, SEEK_CUR); if (end_offset < 0) { return false; diff --git a/minidump/minidump_file_writer.h b/minidump/minidump_file_writer.h index ce2f1d75..0a1b064f 100644 --- a/minidump/minidump_file_writer.h +++ b/minidump/minidump_file_writer.h @@ -134,6 +134,21 @@ class MinidumpFileWriter final : public internal::MinidumpWritable { //! mistaken for valid ones. 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 file’s + //! 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: // MinidumpWritable: bool Freeze() override; diff --git a/minidump/minidump_file_writer_test.cc b/minidump/minidump_file_writer_test.cc index 84832292..19eaae41 100644 --- a/minidump/minidump_file_writer_test.cc +++ b/minidump/minidump_file_writer_test.cc @@ -36,7 +36,9 @@ #include "snapshot/test/test_system_snapshot.h" #include "snapshot/test/test_thread_snapshot.h" #include "test/gtest_death.h" +#include "util/file/output_stream_file_writer.h" #include "util/file/string_file.h" +#include "util/stream/output_stream_interface.h" namespace crashpad { namespace test { @@ -88,6 +90,22 @@ class TestStream final : public internal::MinidumpStreamWriter { 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) { MinidumpFileWriter minidump_file; constexpr time_t kTimestamp = 0x155d2fb8; @@ -578,6 +596,51 @@ TEST(MinidumpFileWriter, SameStreamType) { 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(0x4d); + constexpr uint8_t kStreamValue = 0x5a; + auto stream = + std::make_unique(kStreamType, kStreamSize, kStreamValue); + ASSERT_TRUE(minidump_file.AddStream(std::move(stream))); + + std::unique_ptr string_file_output_stream = + std::make_unique(); + 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( + 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 test } // namespace crashpad diff --git a/tools/BUILD.gn b/tools/BUILD.gn index acd83b34..41f0fb69 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn @@ -56,7 +56,6 @@ crashpad_executable("base94_encoder") { ":tool_support", "../build:default_exe_manifest_win", "../third_party/mini_chromium:base", - "../third_party/zlib", "../util", ] } diff --git a/util/BUILD.gn b/util/BUILD.gn index f886d640..f057fd96 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -91,6 +91,8 @@ static_library("util") { "file/file_writer.cc", "file/file_writer.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.h", "file/string_file.cc", @@ -466,13 +468,13 @@ static_library("util") { include_dirs = [ "$root_gen_dir/third_party/crashpad/crashpad" ] } - public_deps = [ "../compat" ] - - deps += [ - "../third_party/mini_chromium:base", + public_deps = [ + "../compat", "../third_party/zlib", ] + deps += [ "../third_party/mini_chromium:base" ] + if (crashpad_is_mac) { libs = [ "bsm", diff --git a/util/file/output_stream_file_writer.cc b/util/file/output_stream_file_writer.cc new file mode 100644 index 00000000..1172019e --- /dev/null +++ b/util/file/output_stream_file_writer.cc @@ -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 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(data), size); + return flush_needed_; +} + +bool OutputStreamFileWriter::WriteIoVec(std::vector* 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(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 diff --git a/util/file/output_stream_file_writer.h b/util/file/output_stream_file_writer.h new file mode 100644 index 00000000..6bf1c2c1 --- /dev/null +++ b/util/file/output_stream_file_writer.h @@ -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 + +#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 output_stream); + ~OutputStreamFileWriter() override; + + // FileWriterInterface: + bool Write(const void* data, size_t size) override; + bool WriteIoVec(std::vector* 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 output_stream_; + bool flush_needed_; + bool flushed_; + + DISALLOW_COPY_AND_ASSIGN(OutputStreamFileWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_FILE_OUTPUT_STREAM_FILE_WRITER_H_