OLD | NEW |
| (Empty) |
1 // Copyright 2013 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_log_uploader.h" | |
6 | |
7 #include <stddef.h> | |
8 #include <utility> | |
9 | |
10 #include "base/files/file_enumerator.h" | |
11 #include "base/files/file_path.h" | |
12 #include "base/files/file_util.h" | |
13 #include "base/logging.h" | |
14 #include "base/pickle.h" | |
15 #include "base/strings/string_number_conversions.h" | |
16 #include "base/strings/string_split.h" | |
17 #include "base/strings/stringprintf.h" | |
18 #include "base/time/time.h" | |
19 #include "build/build_config.h" | |
20 #include "chrome/browser/browser_process.h" | |
21 #include "chrome/browser/media/media_url_constants.h" | |
22 #include "chrome/browser/media/webrtc_log_list.h" | |
23 #include "chrome/browser/media/webrtc_log_util.h" | |
24 #include "chrome/common/partial_circular_buffer.h" | |
25 #include "components/version_info/version_info.h" | |
26 #include "content/public/browser/browser_thread.h" | |
27 #include "net/base/mime_util.h" | |
28 #include "net/url_request/url_fetcher.h" | |
29 #include "third_party/zlib/zlib.h" | |
30 | |
31 using content::BrowserThread; | |
32 | |
33 namespace { | |
34 | |
35 const int kLogCountLimit = 5; | |
36 const uint32_t kIntermediateCompressionBufferBytes = 256 * 1024; // 256 KB | |
37 const int kLogListLimitLines = 50; | |
38 | |
39 const char kUploadContentType[] = "multipart/form-data"; | |
40 const char kMultipartBoundary[] = | |
41 "----**--yradnuoBgoLtrapitluMklaTelgooG--**----"; | |
42 | |
43 const int kHttpResponseOk = 200; | |
44 | |
45 // Adds the header section for a gzip file to the multipart |post_data|. | |
46 void AddMultipartFileContentHeader(std::string* post_data, | |
47 const std::string& content_name) { | |
48 post_data->append("--"); | |
49 post_data->append(kMultipartBoundary); | |
50 post_data->append("\r\nContent-Disposition: form-data; name=\""); | |
51 post_data->append(content_name); | |
52 post_data->append("\"; filename=\""); | |
53 post_data->append(content_name + ".gz"); | |
54 post_data->append("\"\r\nContent-Type: application/gzip\r\n\r\n"); | |
55 } | |
56 | |
57 // Adds |compressed_log| to |post_data|. | |
58 void AddLogData(std::string* post_data, | |
59 const std::string& compressed_log) { | |
60 AddMultipartFileContentHeader(post_data, "webrtc_log"); | |
61 post_data->append(compressed_log); | |
62 post_data->append("\r\n"); | |
63 } | |
64 | |
65 // Adds the RTP dump data to |post_data|. | |
66 void AddRtpDumpData(std::string* post_data, | |
67 const std::string& name, | |
68 const std::string& dump_data) { | |
69 AddMultipartFileContentHeader(post_data, name); | |
70 post_data->append(dump_data.data(), dump_data.size()); | |
71 post_data->append("\r\n"); | |
72 } | |
73 | |
74 } // namespace | |
75 | |
76 WebRtcLogUploadDoneData::WebRtcLogUploadDoneData() {} | |
77 | |
78 WebRtcLogUploadDoneData::WebRtcLogUploadDoneData( | |
79 const WebRtcLogUploadDoneData& other) = default; | |
80 | |
81 WebRtcLogUploadDoneData::~WebRtcLogUploadDoneData() {} | |
82 | |
83 WebRtcLogUploader::WebRtcLogUploader() | |
84 : log_count_(0), | |
85 post_data_(NULL), | |
86 shutting_down_(false) { | |
87 file_thread_checker_.DetachFromThread(); | |
88 } | |
89 | |
90 WebRtcLogUploader::~WebRtcLogUploader() { | |
91 DCHECK(create_thread_checker_.CalledOnValidThread()); | |
92 DCHECK(upload_done_data_.empty()); | |
93 DCHECK(shutting_down_); | |
94 } | |
95 | |
96 bool WebRtcLogUploader::ApplyForStartLogging() { | |
97 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
98 if (log_count_ < kLogCountLimit && !shutting_down_) { | |
99 ++log_count_; | |
100 return true; | |
101 } | |
102 return false; | |
103 } | |
104 | |
105 void WebRtcLogUploader::LoggingStoppedDontUpload() { | |
106 DecreaseLogCount(); | |
107 } | |
108 | |
109 void WebRtcLogUploader::LoggingStoppedDoUpload( | |
110 std::unique_ptr<WebRtcLogBuffer> log_buffer, | |
111 std::unique_ptr<MetaDataMap> meta_data, | |
112 const WebRtcLogUploadDoneData& upload_done_data) { | |
113 DCHECK(file_thread_checker_.CalledOnValidThread()); | |
114 DCHECK(log_buffer.get()); | |
115 DCHECK(meta_data.get()); | |
116 DCHECK(!upload_done_data.log_path.empty()); | |
117 | |
118 std::string compressed_log; | |
119 CompressLog(&compressed_log, log_buffer.get()); | |
120 | |
121 std::string local_log_id; | |
122 | |
123 if (base::PathExists(upload_done_data.log_path)) { | |
124 WebRtcLogUtil::DeleteOldWebRtcLogFiles(upload_done_data.log_path); | |
125 | |
126 local_log_id = base::DoubleToString(base::Time::Now().ToDoubleT()); | |
127 base::FilePath log_file_path = | |
128 upload_done_data.log_path.AppendASCII(local_log_id) | |
129 .AddExtension(FILE_PATH_LITERAL(".gz")); | |
130 WriteCompressedLogToFile(compressed_log, log_file_path); | |
131 | |
132 base::FilePath log_list_path = | |
133 WebRtcLogList::GetWebRtcLogListFileForDirectory( | |
134 upload_done_data.log_path); | |
135 AddLocallyStoredLogInfoToUploadListFile(log_list_path, local_log_id); | |
136 } | |
137 | |
138 WebRtcLogUploadDoneData upload_done_data_with_log_id = upload_done_data; | |
139 upload_done_data_with_log_id.local_log_id = local_log_id; | |
140 PrepareMultipartPostData(compressed_log, std::move(meta_data), | |
141 upload_done_data_with_log_id); | |
142 } | |
143 | |
144 void WebRtcLogUploader::PrepareMultipartPostData( | |
145 const std::string& compressed_log, | |
146 std::unique_ptr<MetaDataMap> meta_data, | |
147 const WebRtcLogUploadDoneData& upload_done_data) { | |
148 DCHECK(file_thread_checker_.CalledOnValidThread()); | |
149 DCHECK(!compressed_log.empty()); | |
150 DCHECK(meta_data.get()); | |
151 | |
152 std::unique_ptr<std::string> post_data(new std::string()); | |
153 SetupMultipart(post_data.get(), | |
154 compressed_log, | |
155 upload_done_data.incoming_rtp_dump, | |
156 upload_done_data.outgoing_rtp_dump, | |
157 *meta_data.get()); | |
158 | |
159 // If a test has set the test string pointer, write to it and skip uploading. | |
160 // Still fire the upload callback so that we can run an extension API test | |
161 // using the test framework for that without hanging. | |
162 // TODO(grunell): Remove this when the api test for this feature is fully | |
163 // implemented according to the test plan. http://crbug.com/257329. | |
164 if (post_data_) { | |
165 *post_data_ = *post_data; | |
166 NotifyUploadDone(kHttpResponseOk, "", upload_done_data); | |
167 return; | |
168 } | |
169 | |
170 BrowserThread::PostTask( | |
171 BrowserThread::IO, FROM_HERE, | |
172 base::Bind(&WebRtcLogUploader::UploadCompressedLog, | |
173 base::Unretained(this), upload_done_data, | |
174 base::Passed(&post_data))); | |
175 } | |
176 | |
177 void WebRtcLogUploader::UploadStoredLog( | |
178 const WebRtcLogUploadDoneData& upload_data) { | |
179 DCHECK(file_thread_checker_.CalledOnValidThread()); | |
180 DCHECK(!upload_data.local_log_id.empty()); | |
181 DCHECK(!upload_data.log_path.empty()); | |
182 | |
183 base::FilePath native_log_path = | |
184 upload_data.log_path.AppendASCII(upload_data.local_log_id) | |
185 .AddExtension(FILE_PATH_LITERAL(".gz")); | |
186 | |
187 std::string compressed_log; | |
188 if (!base::ReadFileToString(native_log_path, &compressed_log)) { | |
189 DPLOG(WARNING) << "Could not read WebRTC log file."; | |
190 BrowserThread::PostTask( | |
191 BrowserThread::UI, FROM_HERE, | |
192 base::Bind(upload_data.callback, false, "", "Log doesn't exist.")); | |
193 return; | |
194 } | |
195 | |
196 WebRtcLogUploadDoneData upload_data_with_rtp = upload_data; | |
197 | |
198 // Optimistically set the rtp paths to what they should be if they exist. | |
199 upload_data_with_rtp.incoming_rtp_dump = | |
200 upload_data.log_path.AppendASCII(upload_data.local_log_id) | |
201 .AddExtension(FILE_PATH_LITERAL(".rtp_in")); | |
202 | |
203 upload_data_with_rtp.outgoing_rtp_dump = | |
204 upload_data.log_path.AppendASCII(upload_data.local_log_id) | |
205 .AddExtension(FILE_PATH_LITERAL(".rtp_out")); | |
206 | |
207 std::unique_ptr<MetaDataMap> meta_data(new MetaDataMap()); | |
208 { | |
209 std::string meta_data_contents; | |
210 base::FilePath meta_path = | |
211 upload_data.log_path.AppendASCII(upload_data.local_log_id) | |
212 .AddExtension(FILE_PATH_LITERAL(".meta")); | |
213 if (base::ReadFileToString(meta_path, &meta_data_contents) && | |
214 !meta_data_contents.empty()) { | |
215 base::Pickle pickle(&meta_data_contents[0], meta_data_contents.size()); | |
216 base::PickleIterator it(pickle); | |
217 std::string key, value; | |
218 while (it.ReadString(&key) && it.ReadString(&value)) | |
219 (*meta_data.get())[key] = value; | |
220 } | |
221 } | |
222 | |
223 PrepareMultipartPostData(compressed_log, std::move(meta_data), | |
224 upload_data_with_rtp); | |
225 } | |
226 | |
227 void WebRtcLogUploader::LoggingStoppedDoStore( | |
228 const WebRtcLogPaths& log_paths, | |
229 const std::string& log_id, | |
230 std::unique_ptr<WebRtcLogBuffer> log_buffer, | |
231 std::unique_ptr<MetaDataMap> meta_data, | |
232 const WebRtcLoggingHandlerHost::GenericDoneCallback& done_callback) { | |
233 DCHECK(file_thread_checker_.CalledOnValidThread()); | |
234 DCHECK(!log_id.empty()); | |
235 DCHECK(log_buffer.get()); | |
236 DCHECK(!log_paths.log_path.empty()); | |
237 | |
238 WebRtcLogUtil::DeleteOldWebRtcLogFiles(log_paths.log_path); | |
239 | |
240 base::FilePath log_list_path = | |
241 WebRtcLogList::GetWebRtcLogListFileForDirectory(log_paths.log_path); | |
242 | |
243 // Store the native log with a ".gz" extension. | |
244 std::string compressed_log; | |
245 CompressLog(&compressed_log, log_buffer.get()); | |
246 base::FilePath native_log_path = log_paths.log_path.AppendASCII(log_id) | |
247 .AddExtension(FILE_PATH_LITERAL(".gz")); | |
248 WriteCompressedLogToFile(compressed_log, native_log_path); | |
249 AddLocallyStoredLogInfoToUploadListFile(log_list_path, log_id); | |
250 | |
251 // Move the rtp dump files to the log directory with a name of | |
252 // <log id>.rtp_[in|out]. | |
253 if (!log_paths.incoming_rtp_dump.empty()) { | |
254 base::FilePath rtp_path = log_paths.log_path.AppendASCII(log_id) | |
255 .AddExtension(FILE_PATH_LITERAL(".rtp_in")); | |
256 base::Move(log_paths.incoming_rtp_dump, rtp_path); | |
257 } | |
258 | |
259 if (!log_paths.outgoing_rtp_dump.empty()) { | |
260 base::FilePath rtp_path = log_paths.log_path.AppendASCII(log_id) | |
261 .AddExtension(FILE_PATH_LITERAL(".rtp_out")); | |
262 base::Move(log_paths.outgoing_rtp_dump, rtp_path); | |
263 } | |
264 | |
265 if (meta_data.get() && !meta_data->empty()) { | |
266 base::Pickle pickle; | |
267 for (const auto& it : *meta_data.get()) { | |
268 pickle.WriteString(it.first); | |
269 pickle.WriteString(it.second); | |
270 } | |
271 base::FilePath meta_path = log_paths.log_path.AppendASCII(log_id) | |
272 .AddExtension(FILE_PATH_LITERAL(".meta")); | |
273 base::WriteFile(meta_path, static_cast<const char*>(pickle.data()), | |
274 pickle.size()); | |
275 } | |
276 | |
277 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
278 base::Bind(done_callback, true, "")); | |
279 | |
280 BrowserThread::PostTask( | |
281 BrowserThread::IO, FROM_HERE, | |
282 base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this))); | |
283 } | |
284 | |
285 void WebRtcLogUploader::StartShutdown() { | |
286 DCHECK(create_thread_checker_.CalledOnValidThread()); | |
287 | |
288 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
289 base::Bind(&WebRtcLogUploader::ShutdownOnIOThread, | |
290 base::Unretained(this))); | |
291 } | |
292 | |
293 void WebRtcLogUploader::OnURLFetchComplete( | |
294 const net::URLFetcher* source) { | |
295 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
296 DCHECK(upload_done_data_.find(source) != upload_done_data_.end()); | |
297 DCHECK(!shutting_down_); | |
298 int response_code = source->GetResponseCode(); | |
299 UploadDoneDataMap::iterator it = upload_done_data_.find(source); | |
300 if (it != upload_done_data_.end()) { | |
301 // The log path can be empty here if we failed getting it before. We still | |
302 // upload the log if that's the case. | |
303 std::string report_id; | |
304 if (response_code == kHttpResponseOk && | |
305 source->GetResponseAsString(&report_id) && | |
306 !it->second.log_path.empty()) { | |
307 // TODO(jiayl): Add the RTP dump records to chrome://webrtc-logs. | |
308 base::FilePath log_list_path = | |
309 WebRtcLogList::GetWebRtcLogListFileForDirectory(it->second.log_path); | |
310 BrowserThread::PostTask( | |
311 BrowserThread::FILE, FROM_HERE, | |
312 base::Bind(&WebRtcLogUploader::AddUploadedLogInfoToUploadListFile, | |
313 base::Unretained(this), log_list_path, | |
314 it->second.local_log_id, report_id)); | |
315 } | |
316 NotifyUploadDone(response_code, report_id, it->second); | |
317 upload_done_data_.erase(it); | |
318 } | |
319 | |
320 delete source; | |
321 } | |
322 | |
323 void WebRtcLogUploader::OnURLFetchUploadProgress(const net::URLFetcher* source, | |
324 int64_t current, | |
325 int64_t total) {} | |
326 | |
327 void WebRtcLogUploader::SetupMultipart( | |
328 std::string* post_data, | |
329 const std::string& compressed_log, | |
330 const base::FilePath& incoming_rtp_dump, | |
331 const base::FilePath& outgoing_rtp_dump, | |
332 const std::map<std::string, std::string>& meta_data) { | |
333 #if defined(OS_WIN) | |
334 const char product[] = "Chrome"; | |
335 #elif defined(OS_MACOSX) | |
336 const char product[] = "Chrome_Mac"; | |
337 #elif defined(OS_LINUX) | |
338 #if !defined(ADDRESS_SANITIZER) | |
339 const char product[] = "Chrome_Linux"; | |
340 #else | |
341 const char product[] = "Chrome_Linux_ASan"; | |
342 #endif | |
343 #elif defined(OS_ANDROID) | |
344 const char product[] = "Chrome_Android"; | |
345 #elif defined(OS_CHROMEOS) | |
346 const char product[] = "Chrome_ChromeOS"; | |
347 #else | |
348 #error Platform not supported. | |
349 #endif | |
350 net::AddMultipartValueForUpload("prod", product, kMultipartBoundary, | |
351 "", post_data); | |
352 net::AddMultipartValueForUpload("ver", | |
353 version_info::GetVersionNumber() + "-webrtc", | |
354 kMultipartBoundary, "", post_data); | |
355 net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary, | |
356 "", post_data); | |
357 net::AddMultipartValueForUpload("type", "webrtc_log", kMultipartBoundary, | |
358 "", post_data); | |
359 | |
360 // Add custom meta data. | |
361 for (const auto& it : meta_data) { | |
362 net::AddMultipartValueForUpload(it.first, it.second, kMultipartBoundary, "", | |
363 post_data); | |
364 } | |
365 | |
366 AddLogData(post_data, compressed_log); | |
367 | |
368 // Add the rtp dumps if they exist. | |
369 base::FilePath rtp_dumps[2] = {incoming_rtp_dump, outgoing_rtp_dump}; | |
370 static const char* kRtpDumpNames[2] = {"rtpdump_recv", "rtpdump_send"}; | |
371 | |
372 for (size_t i = 0; i < 2; ++i) { | |
373 if (!rtp_dumps[i].empty() && base::PathExists(rtp_dumps[i])) { | |
374 std::string dump_data; | |
375 if (base::ReadFileToString(rtp_dumps[i], &dump_data)) | |
376 AddRtpDumpData(post_data, kRtpDumpNames[i], dump_data); | |
377 } | |
378 } | |
379 | |
380 net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data); | |
381 } | |
382 | |
383 void WebRtcLogUploader::CompressLog(std::string* compressed_log, | |
384 WebRtcLogBuffer* buffer) { | |
385 z_stream stream = {0}; | |
386 int result = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, | |
387 // windowBits = 15 is default, 16 is added to | |
388 // produce a gzip header + trailer. | |
389 15 + 16, | |
390 8, // memLevel = 8 is default. | |
391 Z_DEFAULT_STRATEGY); | |
392 DCHECK_EQ(Z_OK, result); | |
393 | |
394 uint8_t intermediate_buffer[kIntermediateCompressionBufferBytes] = {0}; | |
395 ResizeForNextOutput(compressed_log, &stream); | |
396 uint32_t read = 0; | |
397 | |
398 PartialCircularBuffer read_buffer(buffer->Read()); | |
399 do { | |
400 if (stream.avail_in == 0) { | |
401 read = read_buffer.Read(&intermediate_buffer[0], | |
402 sizeof(intermediate_buffer)); | |
403 stream.next_in = &intermediate_buffer[0]; | |
404 stream.avail_in = read; | |
405 if (read != kIntermediateCompressionBufferBytes) | |
406 break; | |
407 } | |
408 result = deflate(&stream, Z_SYNC_FLUSH); | |
409 DCHECK_EQ(Z_OK, result); | |
410 if (stream.avail_out == 0) | |
411 ResizeForNextOutput(compressed_log, &stream); | |
412 } while (true); | |
413 | |
414 // Ensure we have enough room in the output buffer. Easier to always just do a | |
415 // resize than looping around and resize if needed. | |
416 if (stream.avail_out < kIntermediateCompressionBufferBytes) | |
417 ResizeForNextOutput(compressed_log, &stream); | |
418 | |
419 result = deflate(&stream, Z_FINISH); | |
420 DCHECK_EQ(Z_STREAM_END, result); | |
421 result = deflateEnd(&stream); | |
422 DCHECK_EQ(Z_OK, result); | |
423 | |
424 compressed_log->resize(compressed_log->size() - stream.avail_out); | |
425 } | |
426 | |
427 void WebRtcLogUploader::ResizeForNextOutput(std::string* compressed_log, | |
428 z_stream* stream) { | |
429 size_t old_size = compressed_log->size() - stream->avail_out; | |
430 compressed_log->resize(old_size + kIntermediateCompressionBufferBytes); | |
431 stream->next_out = reinterpret_cast<unsigned char*>( | |
432 &(*compressed_log)[old_size]); | |
433 stream->avail_out = kIntermediateCompressionBufferBytes; | |
434 } | |
435 | |
436 void WebRtcLogUploader::UploadCompressedLog( | |
437 const WebRtcLogUploadDoneData& upload_done_data, | |
438 std::unique_ptr<std::string> post_data) { | |
439 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
440 | |
441 DecreaseLogCount(); | |
442 | |
443 if (shutting_down_) | |
444 return; | |
445 | |
446 std::string content_type = kUploadContentType; | |
447 content_type.append("; boundary="); | |
448 content_type.append(kMultipartBoundary); | |
449 | |
450 std::unique_ptr<net::URLFetcher> url_fetcher(net::URLFetcher::Create( | |
451 GURL(chrome::kUploadURL), net::URLFetcher::POST, this)); | |
452 url_fetcher->SetUploadData(content_type, *post_data); | |
453 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
454 base::Bind(&WebRtcLogUploader::SetRequestContextOnUIThread, | |
455 base::Unretained(this), base::Unretained(url_fetcher.release()), | |
456 upload_done_data)); | |
457 } | |
458 | |
459 void WebRtcLogUploader::SetRequestContextOnUIThread( | |
460 net::URLFetcher* url_fetcher, const WebRtcLogUploadDoneData& data) { | |
461 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
462 url_fetcher->SetRequestContext(g_browser_process->system_request_context()); | |
463 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
464 base::Bind(&WebRtcLogUploader::StartAndTrackRequestContext, | |
465 base::Unretained(this), base::Unretained(url_fetcher), data)); | |
466 } | |
467 | |
468 void WebRtcLogUploader::StartAndTrackRequestContext( | |
469 net::URLFetcher* url_fetcher, const WebRtcLogUploadDoneData& data) { | |
470 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
471 url_fetcher->Start(); | |
472 upload_done_data_[url_fetcher] = data; | |
473 } | |
474 | |
475 void WebRtcLogUploader::DecreaseLogCount() { | |
476 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
477 --log_count_; | |
478 } | |
479 | |
480 void WebRtcLogUploader::ShutdownOnIOThread() { | |
481 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
482 DCHECK(!shutting_down_); | |
483 | |
484 // Delete all URLFetchers first and clear the upload done map. | |
485 for (const auto& it : upload_done_data_) | |
486 delete it.first; | |
487 | |
488 upload_done_data_.clear(); | |
489 shutting_down_ = true; | |
490 } | |
491 | |
492 void WebRtcLogUploader::WriteCompressedLogToFile( | |
493 const std::string& compressed_log, | |
494 const base::FilePath& log_file_path) { | |
495 DCHECK(file_thread_checker_.CalledOnValidThread()); | |
496 DCHECK(!compressed_log.empty()); | |
497 base::WriteFile(log_file_path, &compressed_log[0], compressed_log.size()); | |
498 } | |
499 | |
500 void WebRtcLogUploader::AddLocallyStoredLogInfoToUploadListFile( | |
501 const base::FilePath& upload_list_path, | |
502 const std::string& local_log_id) { | |
503 DCHECK(file_thread_checker_.CalledOnValidThread()); | |
504 DCHECK(!upload_list_path.empty()); | |
505 DCHECK(!local_log_id.empty()); | |
506 | |
507 std::string contents; | |
508 | |
509 if (base::PathExists(upload_list_path)) { | |
510 if (!base::ReadFileToString(upload_list_path, &contents)) { | |
511 DPLOG(WARNING) << "Could not read WebRTC log list file."; | |
512 return; | |
513 } | |
514 | |
515 // Limit the number of log entries to |kLogListLimitLines| - 1, to make room | |
516 // for the new entry. Each line including the last ends with a '\n', so hit | |
517 // n will be before line n-1 (from the back). | |
518 int lf_count = 0; | |
519 int i = contents.size() - 1; | |
520 for (; i >= 0 && lf_count < kLogListLimitLines; --i) { | |
521 if (contents[i] == '\n') | |
522 ++lf_count; | |
523 } | |
524 if (lf_count >= kLogListLimitLines) { | |
525 // + 1 to compensate for the for loop decrease before the conditional | |
526 // check and + 1 to get the length. | |
527 contents.erase(0, i + 2); | |
528 } | |
529 } | |
530 | |
531 // Write the capture time and log ID to the log list file. Leave the upload | |
532 // time and report ID empty. | |
533 contents += ",," + local_log_id + | |
534 "," + base::DoubleToString(base::Time::Now().ToDoubleT()) + '\n'; | |
535 | |
536 int written = | |
537 base::WriteFile(upload_list_path, &contents[0], contents.size()); | |
538 if (written != static_cast<int>(contents.size())) { | |
539 DPLOG(WARNING) << "Could not write all data to WebRTC log list file: " | |
540 << written; | |
541 } | |
542 } | |
543 | |
544 void WebRtcLogUploader::AddUploadedLogInfoToUploadListFile( | |
545 const base::FilePath& upload_list_path, | |
546 const std::string& local_log_id, | |
547 const std::string& report_id) { | |
548 DCHECK(file_thread_checker_.CalledOnValidThread()); | |
549 DCHECK(!upload_list_path.empty()); | |
550 DCHECK(!local_log_id.empty()); | |
551 DCHECK(!report_id.empty()); | |
552 | |
553 std::string contents; | |
554 | |
555 if (base::PathExists(upload_list_path)) { | |
556 if (!base::ReadFileToString(upload_list_path, &contents)) { | |
557 DPLOG(WARNING) << "Could not read WebRTC log list file."; | |
558 return; | |
559 } | |
560 } | |
561 | |
562 // Write the Unix time and report ID to the log list file. We should be able | |
563 // to find the local log ID, in that case insert the data into the existing | |
564 // line. Otherwise add it in the end. | |
565 base::Time time_now = base::Time::Now(); | |
566 std::string time_now_str = base::DoubleToString(time_now.ToDoubleT()); | |
567 size_t pos = contents.find(",," + local_log_id); | |
568 if (pos != std::string::npos) { | |
569 contents.insert(pos, time_now_str); | |
570 contents.insert(pos + time_now_str.length() + 1, report_id); | |
571 } else { | |
572 contents += time_now_str + "," + report_id + ",," + time_now_str + "\n"; | |
573 } | |
574 | |
575 int written = | |
576 base::WriteFile(upload_list_path, &contents[0], contents.size()); | |
577 if (written != static_cast<int>(contents.size())) { | |
578 DPLOG(WARNING) << "Could not write all data to WebRTC log list file: " | |
579 << written; | |
580 } | |
581 } | |
582 | |
583 void WebRtcLogUploader::NotifyUploadDone( | |
584 int response_code, | |
585 const std::string& report_id, | |
586 const WebRtcLogUploadDoneData& upload_done_data) { | |
587 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
588 base::Bind(&WebRtcLoggingHandlerHost::UploadLogDone, | |
589 upload_done_data.host)); | |
590 if (!upload_done_data.callback.is_null()) { | |
591 bool success = response_code == kHttpResponseOk; | |
592 std::string error_message; | |
593 if (!success) { | |
594 error_message = "Uploading failed, response code: " + | |
595 base::IntToString(response_code); | |
596 } | |
597 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
598 base::Bind(upload_done_data.callback, success, | |
599 report_id, error_message)); | |
600 } | |
601 } | |
OLD | NEW |