diff options
Diffstat (limited to 'chromium/chrome/browser/media/webrtc/webrtc_event_log_history.cc')
-rw-r--r-- | chromium/chrome/browser/media/webrtc/webrtc_event_log_history.cc | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_event_log_history.cc b/chromium/chrome/browser/media/webrtc/webrtc_event_log_history.cc new file mode 100644 index 00000000000..59f14e44920 --- /dev/null +++ b/chromium/chrome/browser/media/webrtc/webrtc_event_log_history.cc @@ -0,0 +1,423 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/media/webrtc/webrtc_event_log_history.h" + +#include <limits> +#include <utility> +#include <vector> + +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "chrome/browser/media/webrtc/webrtc_event_log_manager_common.h" + +namespace webrtc_event_logging { + +const size_t kWebRtcEventLogMaxUploadIdBytes = 100; + +namespace { +// Compactness is not important for these few and small files; we therefore +// go with a human-readable format. +const char kCaptureTimeLinePrefix[] = + "Capture time (seconds since UNIX epoch): "; +const char kUploadTimeLinePrefix[] = "Upload time (seconds since UNIX epoch): "; +const char kUploadIdLinePrefix[] = "Upload ID: "; + +// No need to use \r\n for Windows; better have a consistent file format +// between platforms. +const char kEOL[] = "\n"; +static_assert(base::size(kEOL) == 1 + 1 /* +1 for the implicit \0. */, + "SplitString relies on this being a single character."); + +// |time| must *not* be earlier than UNIX epoch start. If it is, the empty +// string is returned. +std::string DeltaFromEpochSeconds(base::Time time) { + if (time.is_null() || time.is_min() || time.is_max()) { + LOG(ERROR) << "Not a valid time (" << time << ")."; + return std::string(); + } + + const base::Time epoch = base::Time::UnixEpoch(); + if (time < epoch) { + LOG(WARNING) << "Time to go back to the future."; + return std::string(); + } + + return base::NumberToString((time - epoch).InSeconds()); +} + +// Helper for ParseTime; see its documentation for details. +base::Time StringToTime(const std::string& time) { + int64_t seconds_from_epoch; + if (!base::StringToInt64(time, &seconds_from_epoch) || + seconds_from_epoch < 0) { + LOG(WARNING) << "Error encountered while reading time."; + return base::Time(); + } + + return base::Time::UnixEpoch() + + base::TimeDelta::FromSeconds(seconds_from_epoch); +} + +// Convert a history file's timestamp, which is the number of seconds since +// UNIX epoch, into a base::Time object. +// This function errors on timestamps from UNIX epoch or before it. +bool ParseTime(const std::string& line, + const std::string& prefix, + base::Time* out) { + DCHECK(line.find(prefix) == 0); + DCHECK(out); + + if (!out->is_null()) { + LOG(WARNING) << "Repeated line."; + return false; + } + + const base::Time time = StringToTime(line.substr(prefix.length())); + if (time.is_null()) { + LOG(WARNING) << "Null time."; + return false; + } + + *out = time; + + return true; +} + +bool ParseString(const std::string& line, + const std::string& prefix, + std::string* out) { + DCHECK(line.find(prefix) == 0); + DCHECK(out); + + if (!out->empty()) { + LOG(WARNING) << "Repeated line."; + return false; + } + + *out = line.substr(prefix.length()); + + if (out->empty()) { + LOG(WARNING) << "Empty string."; + return false; + } + + return true; +} +} // namespace + +std::unique_ptr<WebRtcEventLogHistoryFileWriter> +WebRtcEventLogHistoryFileWriter::Create(const base::FilePath& path) { + auto history_file_writer = + base::WrapUnique(new WebRtcEventLogHistoryFileWriter(path)); + if (!history_file_writer->Init()) { + LOG(WARNING) << "Initialization of history file writer failed."; + return nullptr; + } + return history_file_writer; +} + +WebRtcEventLogHistoryFileWriter::WebRtcEventLogHistoryFileWriter( + const base::FilePath& path) + : path_(path), valid_(false) {} + +bool WebRtcEventLogHistoryFileWriter::Init() { + DCHECK(!valid_); + + if (base::PathExists(path_)) { + if (!base::DeleteFile(path_, /*recursive=*/false)) { + LOG(ERROR) << "History file already exists, and could not be deleted."; + return false; + } else { + LOG(WARNING) << "History file already existed; deleted."; + } + } + + // Attempt to create the file. + constexpr int file_flags = base::File::FLAG_CREATE | base::File::FLAG_WRITE | + base::File::FLAG_EXCLUSIVE_WRITE; + file_.Initialize(path_, file_flags); + if (!file_.IsValid() || !file_.created()) { + LOG(WARNING) << "Couldn't create history file."; + if (!base::DeleteFile(path_, /*recursive=*/false)) { + LOG(ERROR) << "Failed to delete " << path_ << "."; + } + return false; + } + + valid_ = true; + return true; +} + +bool WebRtcEventLogHistoryFileWriter::WriteCaptureTime( + base::Time capture_time) { + DCHECK(valid_); + + if (capture_time.is_null()) { + valid_ = false; + return false; + } + + const std::string delta_seconds = DeltaFromEpochSeconds(capture_time); + if (delta_seconds.empty()) { + valid_ = false; + return false; + } + + const bool written = Write(kCaptureTimeLinePrefix + delta_seconds + kEOL); + if (!written) { + // Error logged by Write(). + valid_ = false; + return false; + } + + return true; +} + +bool WebRtcEventLogHistoryFileWriter::WriteUploadTime(base::Time upload_time) { + DCHECK(valid_); + + if (upload_time.is_null()) { + valid_ = false; + return false; + } + + const std::string delta_seconds = DeltaFromEpochSeconds(upload_time); + if (delta_seconds.empty()) { + valid_ = false; + return false; + } + + const bool written = Write(kUploadTimeLinePrefix + delta_seconds + kEOL); + if (!written) { + valid_ = false; + return false; + } + + return true; +} + +bool WebRtcEventLogHistoryFileWriter::WriteUploadId( + const std::string& upload_id) { + DCHECK(valid_); + DCHECK(!upload_id.empty()); + DCHECK_LE(upload_id.length(), kWebRtcEventLogMaxUploadIdBytes); + + const bool written = Write(kUploadIdLinePrefix + upload_id + kEOL); + if (!written) { + valid_ = false; + return false; + } + + return true; +} + +void WebRtcEventLogHistoryFileWriter::Delete() { + if (!base::DeleteFile(path_, /*recursive=*/false)) { + LOG(ERROR) << "History file could not be deleted."; + } + + valid_ = false; // Like was already false. +} + +base::FilePath WebRtcEventLogHistoryFileWriter::path() const { + DCHECK(valid_); // Can be performed on invalid objects, but likely shouldn't. + return path_; +} + +bool WebRtcEventLogHistoryFileWriter::Write(const std::string& str) { + DCHECK(valid_); + DCHECK(!str.empty()); + DCHECK_LE(str.length(), static_cast<size_t>(std::numeric_limits<int>::max())); + + const int written = file_.WriteAtCurrentPos(str.c_str(), str.length()); + if (written != static_cast<int>(str.length())) { + LOG(WARNING) << "Writing to history file failed."; + valid_ = false; + return false; + } + + // Writes to the history file are infrequent, and happen on a |task_runner_| + // dedicated to event logs. We can therefore afford to Flush() after every + // write, giving us greater confidence that information would not get lost if, + // e.g., Chrome crashes. + file_.Flush(); + + return true; +} + +std::unique_ptr<WebRtcEventLogHistoryFileReader> +WebRtcEventLogHistoryFileReader::Create(const base::FilePath& path) { + auto history_file_reader = + base::WrapUnique(new WebRtcEventLogHistoryFileReader(path)); + if (!history_file_reader->Init()) { + LOG(WARNING) << "Initialization of history file reader failed."; + return nullptr; + } + return history_file_reader; +} + +WebRtcEventLogHistoryFileReader::WebRtcEventLogHistoryFileReader( + const base::FilePath& path) + : path_(path), + local_id_(ExtractRemoteBoundWebRtcEventLogLocalIdFromPath(path_)), + valid_(false) {} + +WebRtcEventLogHistoryFileReader::WebRtcEventLogHistoryFileReader( + WebRtcEventLogHistoryFileReader&& other) + : path_(other.path_), + local_id_(other.local_id_), + capture_time_(other.capture_time_), + upload_time_(other.upload_time_), + upload_id_(other.upload_id_), + valid_(other.valid_) { + other.valid_ = false; +} + +bool WebRtcEventLogHistoryFileReader::Init() { + DCHECK(!valid_); + + if (local_id_.empty()) { + LOG(WARNING) << "Unknown local ID."; + return false; + } + + if (local_id_.length() > kWebRtcEventLogMaxUploadIdBytes) { + LOG(WARNING) << "Excessively long local ID."; + return false; + } + + if (!base::PathExists(path_)) { + LOG(WARNING) << "File does not exist."; + return false; + } + + constexpr int file_flags = base::File::FLAG_OPEN | base::File::FLAG_READ; + base::File file(path_, file_flags); + if (!file.IsValid()) { + LOG(WARNING) << "Couldn't read history file."; + if (!base::DeleteFile(path_, /*recursive=*/false)) { + LOG(ERROR) << "Failed to delete " << path_ << "."; + } + return false; + } + + constexpr size_t kMaxHistoryFileSizeBytes = 1024; + static_assert(kWebRtcEventLogMaxUploadIdBytes < kMaxHistoryFileSizeBytes, ""); + + std::string file_contents; + file_contents.resize(kMaxHistoryFileSizeBytes); + const int read_bytes = file.Read(0, &file_contents[0], file_contents.size()); + if (read_bytes < 0) { + LOG(WARNING) << "Couldn't read contents of history file."; + return false; + } + DCHECK_LE(static_cast<size_t>(read_bytes), file_contents.size()); + file_contents.resize(static_cast<size_t>(read_bytes)); + // Note: In excessively long files, the rest of the file will be ignored; the + // beginning of the file will encounter a parse error. + + if (!Parse(file_contents)) { + LOG(WARNING) << "Parsing of history file failed."; + return false; + } + + valid_ = true; + return true; +} + +std::string WebRtcEventLogHistoryFileReader::LocalId() const { + DCHECK(valid_); + DCHECK(!local_id_.empty()); + return local_id_; +} + +base::Time WebRtcEventLogHistoryFileReader::CaptureTime() const { + DCHECK(valid_); + DCHECK(!capture_time_.is_null()); + return capture_time_; +} + +base::Time WebRtcEventLogHistoryFileReader::UploadTime() const { + DCHECK(valid_); + return upload_time_; // May be null (which indicates "unset"). +} + +std::string WebRtcEventLogHistoryFileReader::UploadId() const { + DCHECK(valid_); + return upload_id_; +} + +base::FilePath WebRtcEventLogHistoryFileReader::path() const { + DCHECK(valid_); + return path_; +} + +bool WebRtcEventLogHistoryFileReader::operator<( + const WebRtcEventLogHistoryFileReader& other) const { + DCHECK(valid_); + DCHECK(!capture_time_.is_null()); + DCHECK(other.valid_); + DCHECK(!other.capture_time_.is_null()); + if (capture_time_ == other.capture_time_) { + // Resolve ties arbitrarily, but consistently (Local IDs are unique). + return LocalId() < other.LocalId(); + } + return (capture_time_ < other.capture_time_); +} + +bool WebRtcEventLogHistoryFileReader::Parse(const std::string& file_contents) { + DCHECK(!valid_); + DCHECK(capture_time_.is_null()); + DCHECK(upload_time_.is_null()); + DCHECK(upload_id_.empty()); + + const std::vector<std::string> lines = + base::SplitString(file_contents, kEOL, base::TRIM_WHITESPACE, + base::SplitResult::SPLIT_WANT_NONEMPTY); + + for (const std::string& line : lines) { + if (line.find(kCaptureTimeLinePrefix) == 0) { + if (!ParseTime(line, kCaptureTimeLinePrefix, &capture_time_)) { + return false; + } + } else if (line.find(kUploadTimeLinePrefix) == 0) { + if (!ParseTime(line, kUploadTimeLinePrefix, &upload_time_)) { + return false; + } + } else if (line.find(kUploadIdLinePrefix) == 0) { + if (!ParseString(line, kUploadIdLinePrefix, &upload_id_)) { + return false; + } + } else { + LOG(WARNING) << "Unrecognized line in history file."; + return false; + } + } + + if (capture_time_.is_null()) { + LOG(WARNING) << "Incomplete history file; capture time unknown."; + return false; + } + + if (!upload_id_.empty() && upload_time_.is_null()) { + LOG(WARNING) << "Incomplete history file; upload time known, " + << "but ID unknown."; + return false; + } + + if (!upload_time_.is_null() && upload_time_ < capture_time_) { + LOG(WARNING) << "Defective history file; claims to have been uploaded " + << "before being captured."; + return false; + } + + return true; +} + +} // namespace webrtc_event_logging |