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