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 #include "chrome/browser/media/webrtc/webrtc_event_log_manager_local.h"
6 
7 #include "base/files/file_util.h"
8 #include "base/stl_util.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "build/build_config.h"
12 #include "content/public/browser/browser_thread.h"
13 
14 #if defined(OS_WIN)
15 #define NumberToStringType base::NumberToString16
16 #else
17 #define NumberToStringType base::NumberToString
18 #endif
19 
20 namespace webrtc_event_logging {
21 
22 #if defined(OS_ANDROID)
23 const size_t kDefaultMaxLocalLogFileSizeBytes = 10000000;
24 const size_t kMaxNumberLocalWebRtcEventLogFiles = 3;
25 #else
26 const size_t kDefaultMaxLocalLogFileSizeBytes = 60000000;
27 const size_t kMaxNumberLocalWebRtcEventLogFiles = 5;
28 #endif
29 
WebRtcLocalEventLogManager(WebRtcLocalEventLogsObserver * observer)30 WebRtcLocalEventLogManager::WebRtcLocalEventLogManager(
31     WebRtcLocalEventLogsObserver* observer)
32     : observer_(observer),
33       clock_for_testing_(nullptr),
34       max_log_file_size_bytes_(kDefaultMaxLocalLogFileSizeBytes) {
35   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
36   DETACH_FROM_SEQUENCE(io_task_sequence_checker_);
37 }
38 
~WebRtcLocalEventLogManager()39 WebRtcLocalEventLogManager::~WebRtcLocalEventLogManager() {
40   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
41 }
42 
PeerConnectionAdded(const PeerConnectionKey & key)43 bool WebRtcLocalEventLogManager::PeerConnectionAdded(
44     const PeerConnectionKey& key) {
45   DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
46 
47   const auto insertion_result = active_peer_connections_.insert(key);
48   if (!insertion_result.second) {
49     return false;  // Attempt to re-add the PeerConnection.
50   }
51 
52   if (!base_path_.empty() &&
53       log_files_.size() < kMaxNumberLocalWebRtcEventLogFiles) {
54     // Note that success/failure of starting the local log file is unrelated
55     // to the success/failure of PeerConnectionAdded().
56     StartLogFile(key);
57   }
58 
59   return true;
60 }
61 
PeerConnectionRemoved(const PeerConnectionKey & key)62 bool WebRtcLocalEventLogManager::PeerConnectionRemoved(
63     const PeerConnectionKey& key) {
64   DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
65 
66   auto peer_connection = active_peer_connections_.find(key);
67 
68   if (peer_connection == active_peer_connections_.end()) {
69     DCHECK(log_files_.find(key) == log_files_.end());
70     return false;
71   }
72 
73   auto local_log = log_files_.find(key);
74   if (local_log != log_files_.end()) {
75     // Note that success/failure of stopping the local log file is unrelated
76     // to the success/failure of PeerConnectionRemoved().
77     CloseLogFile(local_log);
78   }
79 
80   active_peer_connections_.erase(peer_connection);
81 
82   return true;
83 }
84 
EnableLogging(const base::FilePath & base_path,size_t max_file_size_bytes)85 bool WebRtcLocalEventLogManager::EnableLogging(const base::FilePath& base_path,
86                                                size_t max_file_size_bytes) {
87   DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
88 
89   if (!base_path_.empty()) {
90     return false;
91   }
92 
93   DCHECK_EQ(log_files_.size(), 0u);
94 
95   base_path_ = base_path;
96 
97   max_log_file_size_bytes_ =
98       (max_file_size_bytes == kWebRtcEventLogManagerUnlimitedFileSize)
99           ? base::Optional<size_t>()
100           : base::Optional<size_t>(max_file_size_bytes);
101 
102   for (const PeerConnectionKey& peer_connection : active_peer_connections_) {
103     if (log_files_.size() >= kMaxNumberLocalWebRtcEventLogFiles) {
104       break;
105     }
106     StartLogFile(peer_connection);
107   }
108 
109   return true;
110 }
111 
DisableLogging()112 bool WebRtcLocalEventLogManager::DisableLogging() {
113   DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
114 
115   if (base_path_.empty()) {
116     return false;
117   }
118 
119   for (auto local_log = log_files_.begin(); local_log != log_files_.end();) {
120     local_log = CloseLogFile(local_log);
121   }
122 
123   base_path_.clear();  // Marks local-logging as disabled.
124   max_log_file_size_bytes_ = kDefaultMaxLocalLogFileSizeBytes;
125 
126   return true;
127 }
128 
EventLogWrite(const PeerConnectionKey & key,const std::string & message)129 bool WebRtcLocalEventLogManager::EventLogWrite(const PeerConnectionKey& key,
130                                                const std::string& message) {
131   DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
132   auto it = log_files_.find(key);
133   if (it == log_files_.end()) {
134     return false;
135   }
136 
137   const bool write_successful = it->second->Write(message);
138 
139   if (!write_successful || it->second->MaxSizeReached()) {
140     CloseLogFile(it);
141   }
142 
143   return write_successful;
144 }
145 
RenderProcessHostExitedDestroyed(int render_process_id)146 void WebRtcLocalEventLogManager::RenderProcessHostExitedDestroyed(
147     int render_process_id) {
148   DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
149 
150   // Remove all of the peer connections associated with this render process.
151   auto pc_it = active_peer_connections_.begin();
152   while (pc_it != active_peer_connections_.end()) {
153     if (pc_it->render_process_id == render_process_id) {
154       pc_it = active_peer_connections_.erase(pc_it);
155     } else {
156       ++pc_it;
157     }
158   }
159 
160   // Close all of the files that were associated with peer connections which
161   // belonged to this render process.
162   auto log_it = log_files_.begin();
163   while (log_it != log_files_.end()) {
164     if (log_it->first.render_process_id == render_process_id) {
165       log_it = CloseLogFile(log_it);
166     } else {
167       ++log_it;
168     }
169   }
170 }
171 
SetClockForTesting(base::Clock * clock)172 void WebRtcLocalEventLogManager::SetClockForTesting(base::Clock* clock) {
173   DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
174   clock_for_testing_ = clock;
175 }
176 
StartLogFile(const PeerConnectionKey & key)177 void WebRtcLocalEventLogManager::StartLogFile(const PeerConnectionKey& key) {
178   DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
179   DCHECK(log_files_.find(key) == log_files_.end());
180 
181   // Add some information to the name given by the caller.
182   base::FilePath file_path = GetFilePath(base_path_, key);
183   CHECK(!file_path.empty()) << "Couldn't set path for local WebRTC log file.";
184 
185   // In the unlikely case that this filename is already taken, find a unique
186   // number to append to the filename, if possible.
187   file_path = base::GetUniquePath(file_path);
188   if (file_path.empty()) {
189     return;  // No available file path was found.
190   }
191 
192   auto log_file =
193       log_file_writer_factory_.Create(file_path, max_log_file_size_bytes_);
194   if (!log_file) {
195     LOG(WARNING) << "Couldn't create and/or open local WebRTC event log file.";
196     return;
197   }
198 
199   const auto it = log_files_.emplace(key, std::move(log_file));
200   DCHECK(it.second);
201 
202   // The observer needs to be able to run on any TaskQueue.
203   if (observer_) {
204     LogFilesMap::iterator map_iter = it.first;
205     // map_iter->second is a std::unique_ptr<LogFileWriter>.
206     observer_->OnLocalLogStarted(key, map_iter->second->path());
207   }
208 }
209 
210 WebRtcLocalEventLogManager::LogFilesMap::iterator
CloseLogFile(LogFilesMap::iterator it)211 WebRtcLocalEventLogManager::CloseLogFile(LogFilesMap::iterator it) {
212   DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
213 
214   const PeerConnectionKey peer_connection = it->first;
215 
216   it->second->Close();
217   it = log_files_.erase(it);
218 
219   if (observer_) {
220     observer_->OnLocalLogStopped(peer_connection);
221   }
222 
223   return it;
224 }
225 
GetFilePath(const base::FilePath & base_path,const PeerConnectionKey & key) const226 base::FilePath WebRtcLocalEventLogManager::GetFilePath(
227     const base::FilePath& base_path,
228     const PeerConnectionKey& key) const {
229   DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
230 
231   base::Time::Exploded now;
232   if (clock_for_testing_) {
233     clock_for_testing_->Now().LocalExplode(&now);
234   } else {
235     base::Time::Now().LocalExplode(&now);
236   }
237 
238   // [user_defined]_[date]_[time]_[render_process_id]_[lid].[extension]
239   char stamp[100];
240   int written =
241       base::snprintf(stamp, base::size(stamp), "%04d%02d%02d_%02d%02d_%d_%d",
242                      now.year, now.month, now.day_of_month, now.hour,
243                      now.minute, key.render_process_id, key.lid);
244   CHECK_GT(written, 0);
245   CHECK_LT(static_cast<size_t>(written), base::size(stamp));
246 
247   return base_path.InsertBeforeExtension(FILE_PATH_LITERAL("_"))
248       .AddExtension(log_file_writer_factory_.Extension())
249       .InsertBeforeExtensionASCII(base::StringPiece(stamp));
250 }
251 
252 }  // namespace webrtc_event_logging
253