| // Copyright 2015 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_CLIENT_CRASH_REPORT_DATABASE_H_ |
| #define CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_ |
| |
| #include <stdint.h> |
| #include <time.h> |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/files/file_path.h" |
| #include "base/macros.h" |
| #include "util/file/file_io.h" |
| #include "util/file/file_reader.h" |
| #include "util/file/file_writer.h" |
| #include "util/file/scoped_remove_file.h" |
| #include "util/misc/metrics.h" |
| #include "util/misc/uuid.h" |
| |
| namespace crashpad { |
| |
| class Settings; |
| |
| //! \brief An interface for managing a collection of crash report files and |
| //! metadata associated with the crash reports. |
| //! |
| //! All Report objects that are returned by this class are logically const. |
| //! They are snapshots of the database at the time the query was run, and the |
| //! data returned is liable to change after the query is executed. |
| //! |
| //! The lifecycle of a crash report has three stages: |
| //! |
| //! 1. New: A crash report is created with PrepareNewCrashReport(), the |
| //! the client then writes the report, and then calls |
| //! FinishedWritingCrashReport() to make the report Pending. |
| //! 2. Pending: The report has been written but has not been locally |
| //! processed, or it was has been brought back from 'Completed' state by |
| //! user request. |
| //! 3. Completed: The report has been locally processed, either by uploading |
| //! it to a collection server and calling RecordUploadComplete(), or by |
| //! calling SkipReportUpload(). |
| class CrashReportDatabase { |
| public: |
| //! \brief A crash report record. |
| //! |
| //! This represents the metadata for a crash report, as well as the location |
| //! of the report itself. A CrashReportDatabase maintains at least this |
| //! information. |
| struct Report { |
| Report(); |
| |
| //! A unique identifier by which this report will always be known to the |
| //! database. |
| UUID uuid; |
| |
| //! The current location of the crash report on the client’s filesystem. |
| //! The location of a crash report may change over time, so the UUID should |
| //! be used as the canonical identifier. |
| base::FilePath file_path; |
| |
| //! An identifier issued to this crash report by a collection server. |
| std::string id; |
| |
| //! The time at which the report was generated. |
| time_t creation_time; |
| |
| //! Whether this crash report was successfully uploaded to a collection |
| //! server. |
| bool uploaded; |
| |
| //! The last timestamp at which an attempt was made to submit this crash |
| //! report to a collection server. If this is zero, then the report has |
| //! never been uploaded. If #uploaded is true, then this timestamp is the |
| //! time at which the report was uploaded, and no other attempts to upload |
| //! this report will be made. |
| time_t last_upload_attempt_time; |
| |
| //! The number of times an attempt was made to submit this report to |
| //! a collection server. If this is more than zero, then |
| //! #last_upload_attempt_time will be set to the timestamp of the most |
| //! recent attempt. |
| int upload_attempts; |
| |
| //! Whether this crash report was explicitly requested by user to be |
| //! uploaded. This can be true only if report is in the 'pending' state. |
| bool upload_explicitly_requested; |
| |
| //! The total size in bytes taken by the report, including any potential |
| //! attachments. |
| uint64_t total_size; |
| }; |
| |
| //! \brief A crash report that is in the process of being written. |
| //! |
| //! An instance of this class should be created via PrepareNewCrashReport(). |
| class NewReport { |
| public: |
| NewReport(); |
| ~NewReport(); |
| |
| //! \brief An open FileWriter with which to write the report. |
| FileWriter* Writer() const { return writer_.get(); } |
| |
| //! \brief Returns a FileReaderInterface to the report, or `nullptr` with a |
| //! message logged. |
| FileReaderInterface* Reader(); |
| |
| //! A unique identifier by which this report will always be known to the |
| //! database. |
| const UUID& ReportID() const { return uuid_; } |
| |
| //! \brief Adds an attachment to the report. |
| //! |
| //! \note This function is not yet implemented on macOS or Windows. |
| //! |
| //! \param[in] name The key and name for the attachment, which will be |
| //! included in the http upload. The attachment will not appear in the |
| //! minidump report. \a name should only use characters from the set |
| //! `[a-zA-Z0-9._-]`. |
| //! \return A FileWriter that the caller should use to write the contents of |
| //! the attachment, or `nullptr` on failure with an error logged. |
| FileWriter* AddAttachment(const std::string& name); |
| |
| private: |
| friend class CrashReportDatabaseGeneric; |
| friend class CrashReportDatabaseMac; |
| friend class CrashReportDatabaseWin; |
| |
| bool Initialize(CrashReportDatabase* database, |
| const base::FilePath& directory, |
| const base::FilePath::StringType& extension); |
| |
| std::unique_ptr<FileWriter> writer_; |
| std::unique_ptr<FileReader> reader_; |
| ScopedRemoveFile file_remover_; |
| std::vector<std::unique_ptr<FileWriter>> attachment_writers_; |
| std::vector<ScopedRemoveFile> attachment_removers_; |
| UUID uuid_; |
| CrashReportDatabase* database_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NewReport); |
| }; |
| |
| //! \brief A crash report that is in the process of being uploaded. |
| //! |
| //! An instance of this class should be created via GetReportForUploading(). |
| class UploadReport : public Report { |
| public: |
| UploadReport(); |
| virtual ~UploadReport(); |
| |
| //! \brief An open FileReader with which to read the report. |
| FileReader* Reader() const { return reader_.get(); } |
| |
| //! \brief Obtains a mapping of names to file readers for any attachments |
| //! for the report. |
| //! |
| //! This is not implemented on macOS or Windows. |
| std::map<std::string, FileReader*> GetAttachments() const { |
| return attachment_map_; |
| } |
| |
| private: |
| friend class CrashReportDatabase; |
| friend class CrashReportDatabaseGeneric; |
| friend class CrashReportDatabaseMac; |
| friend class CrashReportDatabaseWin; |
| |
| bool Initialize(const base::FilePath path, CrashReportDatabase* database); |
| void InitializeAttachments(); |
| |
| std::unique_ptr<FileReader> reader_; |
| CrashReportDatabase* database_; |
| std::vector<std::unique_ptr<FileReader>> attachment_readers_; |
| std::map<std::string, FileReader*> attachment_map_; |
| bool report_metrics_; |
| |
| DISALLOW_COPY_AND_ASSIGN(UploadReport); |
| }; |
| |
| //! \brief The result code for operations performed on a database. |
| enum OperationStatus { |
| //! \brief No error occurred. |
| kNoError = 0, |
| |
| //! \brief The report that was requested could not be located. |
| //! |
| //! This may occur when the report is present in the database but not in a |
| //! state appropriate for the requested operation, for example, if |
| //! GetReportForUploading() is called to obtain report that’s already in the |
| //! completed state. |
| kReportNotFound, |
| |
| //! \brief An error occured while performing a file operation on a crash |
| //! report. |
| //! |
| //! A database is responsible for managing both the metadata about a report |
| //! and the actual crash report itself. This error is returned when an |
| //! error occurred when managing the report file. Additional information |
| //! will be logged. |
| kFileSystemError, |
| |
| //! \brief An error occured while recording metadata for a crash report or |
| //! database-wide settings. |
| //! |
| //! A database is responsible for managing both the metadata about a report |
| //! and the actual crash report itself. This error is returned when an |
| //! error occurred when managing the metadata about a crash report or |
| //! database-wide settings. Additional information will be logged. |
| kDatabaseError, |
| |
| //! \brief The operation could not be completed because a concurrent |
| //! operation affecting the report is occurring. |
| kBusyError, |
| |
| //! \brief The report cannot be uploaded by user request as it has already |
| //! been uploaded. |
| kCannotRequestUpload, |
| }; |
| |
| virtual ~CrashReportDatabase() {} |
| |
| //! \brief Opens a database of crash reports, possibly creating it. |
| //! |
| //! \param[in] path A path to the database to be created or opened. If the |
| //! database does not yet exist, it will be created if possible. Note that |
| //! for databases implemented as directory structures, existence refers |
| //! solely to the outermost directory. |
| //! |
| //! \return A database object on success, `nullptr` on failure with an error |
| //! logged. |
| //! |
| //! \sa InitializeWithoutCreating |
| static std::unique_ptr<CrashReportDatabase> Initialize( |
| const base::FilePath& path); |
| |
| //! \brief Opens an existing database of crash reports. |
| //! |
| //! \param[in] path A path to the database to be opened. If the database does |
| //! not yet exist, it will not be created. Note that for databases |
| //! implemented as directory structures, existence refers solely to the |
| //! outermost directory. On such databases, as long as the outermost |
| //! directory is present, this method will create the inner structure. |
| //! |
| //! \return A database object on success, `nullptr` on failure with an error |
| //! logged. |
| //! |
| //! \sa Initialize |
| static std::unique_ptr<CrashReportDatabase> InitializeWithoutCreating( |
| const base::FilePath& path); |
| |
| //! \brief Returns the Settings object for this database. |
| //! |
| //! \return A weak pointer to the Settings object, which is owned by the |
| //! database. |
| virtual Settings* GetSettings() = 0; |
| |
| //! \brief Creates a record of a new crash report. |
| //! |
| //! Callers should write the crash report using the FileWriter provided. |
| //! Callers should then call FinishedWritingCrashReport() to complete report |
| //! creation. If an error is encountered while writing the crash report, no |
| //! special action needs to be taken. If FinishedWritingCrashReport() is not |
| //! called, the report will be removed from the database when \a report is |
| //! destroyed. |
| //! |
| //! \param[out] report A NewReport object containing a FileWriter with which |
| //! to write the report data. Only valid if this returns #kNoError. |
| //! |
| //! \return The operation status code. |
| virtual OperationStatus PrepareNewCrashReport( |
| std::unique_ptr<NewReport>* report) = 0; |
| |
| //! \brief Informs the database that a crash report has been successfully |
| //! written. |
| //! |
| //! \param[in] report A NewReport obtained with PrepareNewCrashReport(). The |
| //! NewReport object will be invalidated as part of this call. |
| //! \param[out] uuid The UUID of this crash report. |
| //! |
| //! \return The operation status code. |
| virtual OperationStatus FinishedWritingCrashReport( |
| std::unique_ptr<NewReport> report, |
| UUID* uuid) = 0; |
| |
| //! \brief Returns the crash report record for the unique identifier. |
| //! |
| //! \param[in] uuid The crash report record unique identifier. |
| //! \param[out] report A crash report record. Only valid if this returns |
| //! #kNoError. |
| //! |
| //! \return The operation status code. |
| virtual OperationStatus LookUpCrashReport(const UUID& uuid, |
| Report* report) = 0; |
| |
| //! \brief Returns a list of crash report records that have not been uploaded. |
| //! |
| //! \param[out] reports A list of crash report record objects. This must be |
| //! empty on entry. Only valid if this returns #kNoError. |
| //! |
| //! \return The operation status code. |
| virtual OperationStatus GetPendingReports(std::vector<Report>* reports) = 0; |
| |
| //! \brief Returns a list of crash report records that have been completed, |
| //! either by being uploaded or by skipping upload. |
| //! |
| //! \param[out] reports A list of crash report record objects. This must be |
| //! empty on entry. Only valid if this returns #kNoError. |
| //! |
| //! \return The operation status code. |
| virtual OperationStatus GetCompletedReports(std::vector<Report>* reports) = 0; |
| |
| //! \brief Obtains and locks a report object for uploading to a collection |
| //! server. |
| //! |
| //! Callers should upload the crash report using the FileReader provided. |
| //! Callers should then call RecordUploadComplete() to record a successful |
| //! upload. If RecordUploadComplete() is not called, the upload attempt will |
| //! be recorded as unsuccessful and the report lock released when \a report is |
| //! destroyed. |
| //! |
| //! \param[in] uuid The unique identifier for the crash report record. |
| //! \param[out] report A crash report record for the report to be uploaded. |
| //! Only valid if this returns #kNoError. |
| //! \param[in] report_metrics If `false`, metrics will not be recorded for |
| //! this upload attempt when RecordUploadComplete() is called or \a report |
| //! is destroyed. Metadata for the upload attempt will still be recorded |
| //! in the database. |
| //! |
| //! \return The operation status code. |
| virtual OperationStatus GetReportForUploading( |
| const UUID& uuid, |
| std::unique_ptr<const UploadReport>* report, |
| bool report_metrics = true) = 0; |
| |
| //! \brief Records a successful upload for a report and updates the last |
| //! upload attempt time as returned by |
| //! Settings::GetLastUploadAttemptTime(). |
| //! |
| //! \param[in] report A UploadReport object obtained from |
| //! GetReportForUploading(). The UploadReport object will be invalidated |
| //! and the report unlocked as part of this call. |
| //! \param[in] id The possibly empty identifier assigned to this crash report |
| //! by the collection server. |
| //! |
| //! \return The operation status code. |
| OperationStatus RecordUploadComplete( |
| std::unique_ptr<const UploadReport> report, |
| const std::string& id); |
| |
| //! \brief Moves a report from the pending state to the completed state, but |
| //! without the report being uploaded. |
| //! |
| //! This can be used if the user has disabled crash report collection, but |
| //! crash generation is still enabled in the product. |
| //! |
| //! \param[in] uuid The unique identifier for the crash report record. |
| //! \param[in] reason The reason the report upload is being skipped for |
| //! metrics tracking purposes. |
| //! |
| //! \return The operation status code. |
| virtual OperationStatus SkipReportUpload( |
| const UUID& uuid, |
| Metrics::CrashSkippedReason reason) = 0; |
| |
| //! \brief Deletes a crash report file and its associated metadata. |
| //! |
| //! \param[in] uuid The UUID of the report to delete. |
| //! |
| //! \return The operation status code. |
| virtual OperationStatus DeleteReport(const UUID& uuid) = 0; |
| |
| //! \brief Marks a crash report as explicitly requested to be uploaded by the |
| //! user and moves it to 'pending' state. |
| //! |
| //! \param[in] uuid The unique identifier for the crash report record. |
| //! |
| //! \return The operation status code. |
| virtual OperationStatus RequestUpload(const UUID& uuid) = 0; |
| |
| //! \brief Cleans the database of expired lockfiles, metadata without report |
| //! files, and report files without metadata. |
| //! |
| //! This method does nothing on the macOS and Windows implementations of the |
| //! database. |
| //! |
| //! \param[in] lockfile_ttl The number of seconds at which lockfiles or new |
| //! report files are considered expired. |
| //! \return The number of reports cleaned. |
| virtual int CleanDatabase(time_t lockfile_ttl) { return 0; } |
| |
| //! \brief Deletes the oldest crash reports and their associated metadata, |
| //! leaving only num_reports_to_keep left in the database. |
| //! |
| //! \param[in] num_reports_to_keep To number of most recent reports to leave |
| //! in the database. |
| //! |
| //! \return The operation status code. |
| virtual OperationStatus RemoveOldReports(int num_reports_to_keep) = 0; |
| |
| protected: |
| CrashReportDatabase() {} |
| |
| private: |
| //! \brief Adjusts a crash report record’s metadata to account for an upload |
| //! attempt, and updates the last upload attempt time as returned by |
| //! Settings::GetLastUploadAttemptTime(). |
| //! |
| //! \param[in] report The report object obtained from |
| //! GetReportForUploading(). |
| //! \param[in] successful Whether the upload attempt was successful. |
| //! \param[in] id The identifier assigned to this crash report by the |
| //! collection server. Must be empty if \a successful is `false`; may be |
| //! empty if it is `true`. |
| //! |
| //! \return The operation status code. |
| virtual OperationStatus RecordUploadAttempt(UploadReport* report, |
| bool successful, |
| const std::string& id) = 0; |
| |
| DISALLOW_COPY_AND_ASSIGN(CrashReportDatabase); |
| }; |
| |
| } // namespace crashpad |
| |
| #endif // CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_ |