1 // Copyright 2017 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CHROME_BROWSER_MEDIA_WEBRTC_WEBRTC_EVENT_LOG_MANAGER_COMMON_H_ 6 #define CHROME_BROWSER_MEDIA_WEBRTC_WEBRTC_EVENT_LOG_MANAGER_COMMON_H_ 7 8 #include <memory> 9 #include <string> 10 11 #include "base/files/file_path.h" 12 #include "base/optional.h" 13 #include "base/time/time.h" 14 #include "build/build_config.h" 15 16 class Profile; 17 18 namespace content { 19 class BrowserContext; 20 } // namespace content 21 22 namespace webrtc_event_logging { 23 24 // This file is intended for: 25 // 1. Code shared between WebRtcEventLogManager, WebRtcLocalEventLogManager 26 // and WebRtcRemoteEventLogManager. 27 // 2. Code specific to either of the above classes, but which also needs 28 // to be seen by unit tests (such as constants). 29 30 extern const size_t kWebRtcEventLogManagerUnlimitedFileSize; 31 32 extern const size_t kDefaultMaxLocalLogFileSizeBytes; 33 extern const size_t kMaxNumberLocalWebRtcEventLogFiles; 34 35 extern const size_t kMaxRemoteLogFileSizeBytes; 36 37 extern const int kMaxOutputPeriodMs; 38 39 // Maximum size for a response from Crash, which is the upload ID. 40 extern const size_t kWebRtcEventLogMaxUploadIdBytes; 41 42 // The number of digits required to encode a remote-bound log ID. 43 extern const size_t kWebRtcEventLogIdLength; 44 45 // Min/max legal web-app IDs. 46 extern const size_t kMinWebRtcEventLogWebAppId; 47 extern const size_t kMaxWebRtcEventLogWebAppId; 48 49 // Sentinel value, guaranteed not to fall inside the range of min-max valid IDs. 50 extern const size_t kInvalidWebRtcEventLogWebAppId; 51 52 // Limit over the number of concurrently active (currently being written to 53 // disk) remote-bound log files. This limits IO operations, and so it is 54 // applied globally (all browser contexts are limited together). 55 extern const size_t kMaxActiveRemoteBoundWebRtcEventLogs; 56 57 // Limit over the number of pending logs (logs stored on disk and awaiting to 58 // be uploaded to a remote server). This limit avoids excessive storage. If a 59 // user chooses to have multiple profiles (and hence browser contexts) on a 60 // system, it is assumed that the user has enough storage to accommodate 61 // the increased storage consumption that comes with it. Therefore, this 62 // limit is applied per browser context. 63 extern const size_t kMaxPendingRemoteBoundWebRtcEventLogs; 64 65 // Max number of history files that may be kept; after this number is exceeded, 66 // the oldest logs should be pruned. 67 extern const size_t kMaxWebRtcEventLogHistoryFiles; 68 69 // Overhead incurred by GZIP due to its header and footer. 70 extern const size_t kGzipOverheadBytes; 71 72 // Remote-bound log files' names will be of the format: 73 // [prefix]_[web_app_id]_[log_id].[ext] 74 // Where: 75 // * |prefix| is equal to kRemoteBoundWebRtcEventLogFileNamePrefix. 76 // * |web_app_id| is a number between kMinWebRtcEventLogWebAppId and 77 // kMaxWebRtcEventLogWebAppId, with zero padding. 78 // * |log_id| is composed of 32 random characters from '0'-'9' and 'A'-'F'. 79 // * |ext| is the extension determined by the used LogCompressor::Factory, 80 // which will be either kWebRtcEventLogUncompressedExtension or 81 // kWebRtcEventLogGzippedExtension. 82 extern const char kRemoteBoundWebRtcEventLogFileNamePrefix[]; 83 extern const base::FilePath::CharType kWebRtcEventLogUncompressedExtension[]; 84 extern const base::FilePath::CharType kWebRtcEventLogGzippedExtension[]; 85 86 // Logs themselves are kept on disk for kRemoteBoundWebRtcEventLogsMaxRetention, 87 // or until uploaded. Smaller history files are kept for a longer time, allowing 88 // Chrome to display on chrome://webrtc-logs/ that these files were captured 89 // and later uploaded. 90 extern const base::FilePath::CharType kWebRtcEventLogHistoryExtension[]; 91 92 // Remote-bound event logs will not be uploaded if the time since their last 93 // modification (meaning the time when they were completed) exceeds this value. 94 // Such expired files will be purged from disk when examined. 95 extern const base::TimeDelta kRemoteBoundWebRtcEventLogsMaxRetention; 96 97 // These are made globally visible so that unit tests may check for them. 98 extern const char kStartRemoteLoggingFailureAlreadyLogging[]; 99 extern const char kStartRemoteLoggingFailureDeadRenderProcessHost[]; 100 extern const char kStartRemoteLoggingFailureFeatureDisabled[]; 101 extern const char kStartRemoteLoggingFailureFileCreationError[]; 102 extern const char kStartRemoteLoggingFailureFilePathUsedHistory[]; 103 extern const char kStartRemoteLoggingFailureFilePathUsedLog[]; 104 extern const char kStartRemoteLoggingFailureIllegalWebAppId[]; 105 extern const char kStartRemoteLoggingFailureLoggingDisabledBrowserContext[]; 106 extern const char kStartRemoteLoggingFailureMaxSizeTooLarge[]; 107 extern const char kStartRemoteLoggingFailureMaxSizeTooSmall[]; 108 extern const char kStartRemoteLoggingFailureNoAdditionalActiveLogsAllowed[]; 109 extern const char kStartRemoteLoggingFailureOutputPeriodMsTooLarge[]; 110 extern const char kStartRemoteLoggingFailureUnknownOrInactivePeerConnection[]; 111 extern const char kStartRemoteLoggingFailureUnlimitedSizeDisallowed[]; 112 113 // Values for the histogram for the result of the API call to collect 114 // a WebRTC event log. 115 // Must match the numbering of WebRtcEventLoggingApiEnum in enums.xml. 116 // These values are persisted to logs. Entries should not be renumbered and 117 // numeric values should never be reused. 118 enum class WebRtcEventLoggingApiUma { 119 kSuccess = 0, // Log successfully collected. 120 kDeadRph = 1, // Log not collected. 121 kFeatureDisabled = 2, // Log not collected. 122 kIncognito = 3, // Log not collected. 123 kInvalidArguments = 4, // Log not collected. 124 kIllegalSessionId = 5, // Log not collected. 125 kDisabledBrowserContext = 6, // Log not collected. 126 kUnknownOrInvalidPeerConnection = 7, // Log not collected. 127 kAlreadyLogging = 8, // Log not collected. 128 kNoAdditionalLogsAllowed = 9, // Log not collected. 129 kLogPathNotAvailable = 10, // Log not collected. 130 kHistoryPathNotAvailable = 11, // Log not collected. 131 kFileCreationError = 12, // Log not collected. 132 kMaxValue = kFileCreationError 133 }; 134 135 void UmaRecordWebRtcEventLoggingApi(WebRtcEventLoggingApiUma result); 136 137 // Values for the histogram for the result of the upload of a WebRTC event log. 138 // Must match the numbering of WebRtcEventLoggingUploadEnum in enums.xml. 139 // These values are persisted to logs. Entries should not be renumbered and 140 // numeric values should never be reused. 141 enum class WebRtcEventLoggingUploadUma { 142 kSuccess = 0, // Uploaded successfully. 143 kLogFileWriteError = 1, // Will not be uploaded. 144 kActiveLogCancelledDueToCacheClear = 2, // Will not be uploaded. 145 kPendingLogDeletedDueToCacheClear = 3, // Will not be uploaded. 146 kHistoryFileCreationError = 4, // Will not be uploaded. 147 kHistoryFileWriteError = 5, // Will not be uploaded. 148 kLogFileReadError = 6, // Will not be uploaded. 149 kLogFileNameError = 7, // Will not be uploaded. 150 kUploadCancelled = 8, // Upload started then cancelled. 151 kUploadFailure = 9, // Upload attempted and failed. 152 kIncompletePastUpload = 10, // Upload attempted and failed. 153 kExpiredLogFileAtChromeStart = 11, // Expired before upload opportunity. 154 kExpiredLogFileDuringSession = 12, // Expired before upload opportunity. 155 kMaxValue = kExpiredLogFileDuringSession 156 }; 157 158 void UmaRecordWebRtcEventLoggingUpload(WebRtcEventLoggingUploadUma result); 159 160 // Success is signalled by 0. 161 // All negative values signal errors. 162 // Positive values are not used. 163 void UmaRecordWebRtcEventLoggingNetErrorType(int net_error); 164 165 // For a given Chrome session, this is a unique key for PeerConnections. 166 // It's not, however, unique between sessions (after Chrome is restarted). 167 struct WebRtcEventLogPeerConnectionKey { 168 using BrowserContextId = uintptr_t; 169 WebRtcEventLogPeerConnectionKeyWebRtcEventLogPeerConnectionKey170 constexpr WebRtcEventLogPeerConnectionKey() 171 : WebRtcEventLogPeerConnectionKey( 172 /* render_process_id = */ 0, 173 /* lid = */ 0, 174 reinterpret_cast<BrowserContextId>(nullptr)) {} 175 WebRtcEventLogPeerConnectionKeyWebRtcEventLogPeerConnectionKey176 constexpr WebRtcEventLogPeerConnectionKey(int render_process_id, 177 int lid, 178 BrowserContextId browser_context_id) 179 : render_process_id(render_process_id), 180 lid(lid), 181 browser_context_id(browser_context_id) {} 182 183 bool operator==(const WebRtcEventLogPeerConnectionKey& other) const { 184 // Each RPH is associated with exactly one BrowserContext. 185 DCHECK(render_process_id != other.render_process_id || 186 browser_context_id == other.browser_context_id); 187 188 const bool equal = std::tie(render_process_id, lid) == 189 std::tie(other.render_process_id, other.lid); 190 return equal; 191 } 192 193 bool operator<(const WebRtcEventLogPeerConnectionKey& other) const { 194 // Each RPH is associated with exactly one BrowserContext. 195 DCHECK(render_process_id != other.render_process_id || 196 browser_context_id == other.browser_context_id); 197 198 return std::tie(render_process_id, lid) < 199 std::tie(other.render_process_id, other.lid); 200 } 201 202 // These two fields are the actual key; any peer connection is uniquely 203 // identifiable by the renderer process in which it lives, and its ID within 204 // that process. 205 int render_process_id; 206 int lid; // Renderer-local PeerConnection ID. 207 208 // The BrowserContext is not actually part of the key, but each PeerConnection 209 // is associated with a BrowserContext, and that BrowserContext is almost 210 // always necessary, so it makes sense to remember it along with the key. 211 BrowserContextId browser_context_id; 212 }; 213 214 // Sentinel value for an unknown BrowserContext. 215 extern const WebRtcEventLogPeerConnectionKey::BrowserContextId 216 kNullBrowserContextId; 217 218 // Holds housekeeping information about log files. 219 struct WebRtcLogFileInfo { WebRtcLogFileInfoWebRtcLogFileInfo220 WebRtcLogFileInfo( 221 WebRtcEventLogPeerConnectionKey::BrowserContextId browser_context_id, 222 const base::FilePath& path, 223 base::Time last_modified) 224 : browser_context_id(browser_context_id), 225 path(path), 226 last_modified(last_modified) {} 227 WebRtcLogFileInfoWebRtcLogFileInfo228 WebRtcLogFileInfo(const WebRtcLogFileInfo& other) 229 : browser_context_id(other.browser_context_id), 230 path(other.path), 231 last_modified(other.last_modified) {} 232 233 bool operator<(const WebRtcLogFileInfo& other) const { 234 if (last_modified != other.last_modified) { 235 return last_modified < other.last_modified; 236 } 237 return path < other.path; // Break ties arbitrarily, but consistently. 238 } 239 240 // The BrowserContext which produced this file. 241 const WebRtcEventLogPeerConnectionKey::BrowserContextId browser_context_id; 242 243 // The path to the log file itself. 244 const base::FilePath path; 245 246 // |last_modified| recorded at BrowserContext initialization. Chrome will 247 // not modify it afterwards, and neither should the user. 248 const base::Time last_modified; 249 }; 250 251 // An observer for notifications of local log files being started/stopped, and 252 // the paths which will be used for these logs. 253 class WebRtcLocalEventLogsObserver { 254 public: 255 virtual void OnLocalLogStarted(WebRtcEventLogPeerConnectionKey key, 256 const base::FilePath& file_path) = 0; 257 virtual void OnLocalLogStopped(WebRtcEventLogPeerConnectionKey key) = 0; 258 259 protected: 260 virtual ~WebRtcLocalEventLogsObserver() = default; 261 }; 262 263 // An observer for notifications of remote-bound log files being 264 // started/stopped. The start event would likely only interest unit tests 265 // (because it exposes the randomized filename to them). The stop event is of 266 // general interest, because it would often mean that WebRTC can stop sending 267 // us event logs for this peer connection. 268 // Some cases where OnRemoteLogStopped would be called include: 269 // 1. The PeerConnection has become inactive. 270 // 2. The file's maximum size has been reached. 271 // 3. Any type of error while writing to the file. 272 class WebRtcRemoteEventLogsObserver { 273 public: 274 virtual void OnRemoteLogStarted(WebRtcEventLogPeerConnectionKey key, 275 const base::FilePath& file_path, 276 int output_period_ms) = 0; 277 virtual void OnRemoteLogStopped(WebRtcEventLogPeerConnectionKey key) = 0; 278 279 protected: 280 virtual ~WebRtcRemoteEventLogsObserver() = default; 281 }; 282 283 // Writes a log to a file while observing a maximum size. 284 class LogFileWriter { 285 public: 286 class Factory { 287 public: 288 virtual ~Factory() = default; 289 290 // The smallest size a log file of this type may assume. 291 virtual size_t MinFileSizeBytes() const = 0; 292 293 // The extension type associated with this type of log files. 294 virtual base::FilePath::StringPieceType Extension() const = 0; 295 296 // Instantiate and initialize a LogFileWriter. 297 // If creation or initialization fail, an empty unique_ptr will be returned, 298 // and it will be guaranteed that the file itself is not created. (If |path| 299 // had pointed to an existing file, that file will be deleted.) 300 // If !max_file_size_bytes.has_value(), the LogFileWriter is unlimited. 301 virtual std::unique_ptr<LogFileWriter> Create( 302 const base::FilePath& path, 303 base::Optional<size_t> max_file_size_bytes) const = 0; 304 }; 305 306 virtual ~LogFileWriter() = default; 307 308 // Init() must be called on each LogFileWriter exactly once, before it's used. 309 // If initialization fails, no further actions may be performed on the object 310 // other than Close() and Delete(). 311 virtual bool Init() = 0; 312 313 // Getter for the path of the file |this| wraps. 314 virtual const base::FilePath& path() const = 0; 315 316 // Whether the maximum file size was reached. 317 virtual bool MaxSizeReached() const = 0; 318 319 // Writes to the log file while respecting the file's size limit. 320 // True is returned if and only if the message was written to the file in 321 // it entirety. That is, |false| is returned either if a genuine error 322 // occurs, or when the budget does not allow the next write. 323 // If |false| is ever returned, only Close() and Delete() may subsequently 324 // be called. 325 // The function does *not* close the file. 326 // The function may not be called if MaxSizeReached(). 327 virtual bool Write(const std::string& input) = 0; 328 329 // If the file was successfully closed, true is returned, and the file may 330 // now be used. Otherwise, the file is deleted, and false is returned. 331 virtual bool Close() = 0; 332 333 // Delete the file from disk. 334 virtual void Delete() = 0; 335 }; 336 337 // Produces LogFileWriter instances that perform no compression. 338 class BaseLogFileWriterFactory : public LogFileWriter::Factory { 339 public: 340 ~BaseLogFileWriterFactory() override = default; 341 342 size_t MinFileSizeBytes() const override; 343 344 base::FilePath::StringPieceType Extension() const override; 345 346 std::unique_ptr<LogFileWriter> Create( 347 const base::FilePath& path, 348 base::Optional<size_t> max_file_size_bytes) const override; 349 }; 350 351 // Interface for a class that provides compression of a stream, while attempting 352 // to observe a limit on the size. 353 // 354 // One should note that: 355 // * For compressors that use a footer, to guarantee proper decompression, 356 // the footer must be written to the file. 357 // * In such a case, usually, nothing can be omitted from the file, or the 358 // footer's CRC (if used) would be wrong. 359 // * Determining a string's size pre-compression, without performing the actual 360 // compression, is heuristic in nature. 361 // 362 // Therefore, compression might terminate (FULL) earlier than it 363 // must, or even in theory (which we attempt to avoid in practice) exceed the 364 // size allowed it, in which case the file will be discarded (ERROR). 365 class LogCompressor { 366 public: 367 // By subclassing this factory, concrete implementations of LogCompressor can 368 // be produced by unit tests, while keeping their definition in the .cc file. 369 // (Only the factory needs to be declared in the header.) 370 class Factory { 371 public: 372 virtual ~Factory() = default; 373 374 // The smallest size a log file of this type may assume. 375 virtual size_t MinSizeBytes() const = 0; 376 377 // Returns a LogCompressor if the parameters are valid and all 378 // initializations are successful; en empty unique_ptr otherwise. 379 // If !max_size_bytes.has_value(), an unlimited compressor is created. 380 virtual std::unique_ptr<LogCompressor> Create( 381 base::Optional<size_t> max_size_bytes) const = 0; 382 }; 383 384 // Result of a call to Compress(). 385 // * OK and ERROR_ENCOUNTERED are self-explanatory. 386 // * DISALLOWED means that, due to budget constraints, the input could 387 // not be compressed. The stream is still in a legal state, but only 388 // a call to CreateFooter() is now allowed. 389 enum class Result { OK, DISALLOWED, ERROR_ENCOUNTERED }; 390 391 virtual ~LogCompressor() = default; 392 393 // Produces a compression header and writes it to |output|. 394 // The size does not count towards the max size limit. 395 // Guaranteed not to fail (nothing can realistically go wrong). 396 virtual void CreateHeader(std::string* output) = 0; 397 398 // Compresses |input| into |output|. 399 // * If compression succeeded, and the budget was observed, OK is returned. 400 // * If the compressor thinks the string, once compressed, will exceed the 401 // maximum size (when combined with previously compressed strings), 402 // compression will not be done, and DISALLOWED will be returned. 403 // This allows producing a valid footer without exceeding the size limit. 404 // * Unexpected errors in the underlying compressor (e.g. zlib, etc.), 405 // or unexpectedly getting a compressed string which exceeds the budget, 406 // will return ERROR_ENCOUNTERED. 407 // This function may not be called again if DISALLOWED or ERROR_ENCOUNTERED 408 // were ever returned before, or after CreateFooter() was called. 409 virtual Result Compress(const std::string& input, std::string* output) = 0; 410 411 // Produces a compression footer and writes it to |output|. 412 // The footer does not count towards the max size limit. 413 // May not be called more than once, or if Compress() returned ERROR. 414 virtual bool CreateFooter(std::string* output) = 0; 415 }; 416 417 // Estimates the compressed size, without performing compression (except in 418 // unit tests, where performance is of lesser importance). 419 // This interface allows unit tests to simulate specific cases, such as 420 // over/under-estimation, and show that the code using the LogCompressor 421 // deals with them correctly. (E.g., if the estimation expects the compression 422 // to not go over-budget, but then it does.) 423 // The estimator is expected to be stateful. That is, the order of calls to 424 // EstimateCompressedSize() should correspond to the order of calls 425 // to Compress(). 426 class CompressedSizeEstimator { 427 public: 428 class Factory { 429 public: 430 virtual ~Factory() = default; 431 virtual std::unique_ptr<CompressedSizeEstimator> Create() const = 0; 432 }; 433 434 virtual ~CompressedSizeEstimator() = default; 435 436 virtual size_t EstimateCompressedSize(const std::string& input) const = 0; 437 }; 438 439 // Provides a conservative estimation of the number of bytes required to 440 // compress a string using GZIP. This estimation is not expected to ever 441 // be overly optimistic, but the code using it should nevertheless be prepared 442 // to deal with that theoretical possibility. 443 class DefaultGzippedSizeEstimator : public CompressedSizeEstimator { 444 public: 445 class Factory : public CompressedSizeEstimator::Factory { 446 public: 447 ~Factory() override = default; 448 449 std::unique_ptr<CompressedSizeEstimator> Create() const override; 450 }; 451 452 ~DefaultGzippedSizeEstimator() override = default; 453 454 size_t EstimateCompressedSize(const std::string& input) const override; 455 }; 456 457 // Interface for producing LogCompressorGzip objects. 458 class GzipLogCompressorFactory : public LogCompressor::Factory { 459 public: 460 explicit GzipLogCompressorFactory( 461 std::unique_ptr<CompressedSizeEstimator::Factory> estimator_factory); 462 ~GzipLogCompressorFactory() override; 463 464 size_t MinSizeBytes() const override; 465 466 std::unique_ptr<LogCompressor> Create( 467 base::Optional<size_t> max_size_bytes) const override; 468 469 private: 470 std::unique_ptr<CompressedSizeEstimator::Factory> estimator_factory_; 471 }; 472 473 // Produces LogFileWriter instances that perform compression using GZIP. 474 class GzippedLogFileWriterFactory : public LogFileWriter::Factory { 475 public: 476 explicit GzippedLogFileWriterFactory( 477 std::unique_ptr<GzipLogCompressorFactory> gzip_compressor_factory); 478 479 ~GzippedLogFileWriterFactory() override; 480 481 size_t MinFileSizeBytes() const override; 482 483 base::FilePath::StringPieceType Extension() const override; 484 485 std::unique_ptr<LogFileWriter> Create( 486 const base::FilePath& path, 487 base::Optional<size_t> max_file_size_bytes) const override; 488 489 private: 490 std::unique_ptr<GzipLogCompressorFactory> gzip_compressor_factory_; 491 }; 492 493 // Create a random identifier of 32 hexadecimal (uppercase) characters. 494 std::string CreateWebRtcEventLogId(); 495 496 // Translate a BrowserContext into an ID. This lets us associate PeerConnections 497 // with BrowserContexts, while making sure that we never call the 498 // BrowserContext's methods outside of the UI thread (because we can't call them 499 // at all without a cast that would alert us to the danger). 500 WebRtcEventLogPeerConnectionKey::BrowserContextId GetBrowserContextId( 501 const content::BrowserContext* browser_context); 502 503 // Fetches the BrowserContext associated with the render process ID, then 504 // returns its BrowserContextId. (If the render process has already died, 505 // it would have no BrowserContext associated, so the ID associated with a 506 // null BrowserContext will be returned.) 507 WebRtcEventLogPeerConnectionKey::BrowserContextId GetBrowserContextId( 508 int render_process_id); 509 510 // Given a BrowserContext's directory, return the path to the directory where 511 // we store the pending remote-bound logs associated with this BrowserContext. 512 // This function may be called on any task queue. 513 base::FilePath GetRemoteBoundWebRtcEventLogsDir( 514 const base::FilePath& browser_context_dir); 515 516 // Produce the path to a remote-bound WebRTC event log file with the given 517 // log ID, web-app ID and extension, in the given directory. 518 base::FilePath WebRtcEventLogPath( 519 const base::FilePath& remote_logs_dir, 520 const std::string& log_id, 521 size_t web_app_id, 522 const base::FilePath::StringPieceType& extension); 523 524 // Checks whether the path/filename would be a valid reference to a remote-bound 525 // even log. These functions do not examine the file's content or its extension. 526 bool IsValidRemoteBoundLogFilename(const std::string& filename); 527 bool IsValidRemoteBoundLogFilePath(const base::FilePath& path); 528 529 // Given WebRTC event log's path, return the path to the history file that 530 // is, or would be, associated with it. 531 base::FilePath GetWebRtcEventLogHistoryFilePath(const base::FilePath& path); 532 533 // Attempts to extract the local ID from the file's path. Returns the empty 534 // string in case of an error. 535 std::string ExtractRemoteBoundWebRtcEventLogLocalIdFromPath( 536 const base::FilePath& path); 537 538 // Attempts to extract the web-app ID from the file's path. 539 // Returns kInvalidWebRtcEventLogWebAppId in case of an error. 540 size_t ExtractRemoteBoundWebRtcEventLogWebAppIdFromPath( 541 const base::FilePath& path); 542 543 // Used to determine the default value for the policy controlling event logging. 544 bool DoesProfileDefaultToLoggingEnabled(const Profile* const profile); 545 546 } // namespace webrtc_event_logging 547 548 #endif // CHROME_BROWSER_MEDIA_WEBRTC_WEBRTC_EVENT_LOG_MANAGER_COMMON_H_ 549