1 // Copyright 2016 The Chromium Authors
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 "net/log/file_net_log_observer.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/files/file.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_file.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/functional/bind.h"
16 #include "base/functional/callback.h"
17 #include "base/json/json_reader.h"
18 #include "base/json/json_writer.h"
19 #include "base/memory/raw_ptr.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/task/thread_pool/thread_pool_instance.h"
24 #include "base/test/gmock_expected_support.h"
25 #include "base/threading/thread.h"
26 #include "base/types/expected.h"
27 #include "base/types/expected_macros.h"
28 #include "base/values.h"
29 #include "build/build_config.h"
30 #include "net/base/test_completion_callback.h"
31 #include "net/log/net_log.h"
32 #include "net/log/net_log_entry.h"
33 #include "net/log/net_log_event_type.h"
34 #include "net/log/net_log_source.h"
35 #include "net/log/net_log_source_type.h"
36 #include "net/log/net_log_util.h"
37 #include "net/log/net_log_values.h"
38 #include "net/test/test_with_task_environment.h"
39 #include "net/url_request/url_request.h"
40 #include "net/url_request/url_request_context.h"
41 #include "net/url_request/url_request_test_util.h"
42 #include "testing/gtest/include/gtest/gtest.h"
43
44 namespace net {
45
46 namespace {
47
48 // Indicates the number of event files used in test cases.
49 const int kTotalNumFiles = 10;
50
51 // Used to set the total file size maximum in test cases where the file size
52 // doesn't matter.
53 const int kLargeFileSize = 100000000;
54
55 // Used to set the size of events to be sent to the observer in test cases
56 // where event size doesn't matter.
57 const size_t kDummyEventSize = 150;
58
59 // Adds |num_entries| to |logger|. The "inverse" of this is VerifyEventsInLog().
AddEntries(FileNetLogObserver * logger,int num_entries,size_t entry_size)60 void AddEntries(FileNetLogObserver* logger,
61 int num_entries,
62 size_t entry_size) {
63 // Get base size of event.
64 const int kDummyId = 0;
65 NetLogSource source(NetLogSourceType::HTTP2_SESSION, kDummyId);
66 NetLogEntry base_entry(NetLogEventType::PAC_JAVASCRIPT_ERROR, source,
67 NetLogEventPhase::BEGIN, base::TimeTicks::Now(),
68 NetLogParamsWithString("message", ""));
69 base::Value::Dict value = base_entry.ToDict();
70 std::string json;
71 base::JSONWriter::Write(value, &json);
72 size_t base_entry_size = json.size();
73
74 // The maximum value of base::TimeTicks::Now() will be the maximum value of
75 // int64_t, and if the maximum number of digits are included, the
76 // |base_entry_size| could be up to 136 characters. Check that the event
77 // format does not include additional padding.
78 DCHECK_LE(base_entry_size, 136u);
79
80 // |entry_size| should be at least as big as the largest possible base
81 // entry.
82 EXPECT_GE(entry_size, 136u);
83
84 // |entry_size| cannot be smaller than the minimum event size.
85 EXPECT_GE(entry_size, base_entry_size);
86
87 for (int i = 0; i < num_entries; i++) {
88 source = NetLogSource(NetLogSourceType::HTTP2_SESSION, i);
89 std::string id = base::NumberToString(i);
90
91 // String size accounts for the number of digits in id so that all events
92 // are the same size.
93 std::string message =
94 std::string(entry_size - base_entry_size - id.size() + 1, 'x');
95 NetLogEntry entry(NetLogEventType::PAC_JAVASCRIPT_ERROR, source,
96 NetLogEventPhase::BEGIN, base::TimeTicks::Now(),
97 NetLogParamsWithString("message", message));
98 logger->OnAddEntry(entry);
99 }
100 }
101
102 // ParsedNetLog holds the parsed contents of a NetLog file (constants, events,
103 // and polled data).
104 struct ParsedNetLog {
105 base::expected<void, std::string> InitFromFileContents(
106 const std::string& input);
107 const base::Value::Dict* GetEvent(size_t i) const;
108
109 // Initializes the ParsedNetLog by parsing a JSON file.
110 // Owner for the Value tree and a dictionary for the entire netlog.
111 base::Value root;
112
113 // The constants dictionary.
114 raw_ptr<const base::Value::Dict> constants = nullptr;
115
116 // The events list.
117 raw_ptr<const base::Value::List> events = nullptr;
118
119 // The optional polled data (may be nullptr).
120 raw_ptr<const base::Value::Dict> polled_data = nullptr;
121 };
122
InitFromFileContents(const std::string & input)123 base::expected<void, std::string> ParsedNetLog::InitFromFileContents(
124 const std::string& input) {
125 if (input.empty()) {
126 return base::unexpected("input is empty");
127 }
128
129 ASSIGN_OR_RETURN(root, base::JSONReader::ReadAndReturnValueWithError(input),
130 &base::JSONReader::Error::message);
131
132 const base::Value::Dict* dict = root.GetIfDict();
133 if (!dict) {
134 return base::unexpected("Not a dictionary");
135 }
136
137 events = dict->FindListByDottedPath("events");
138 if (!events) {
139 return base::unexpected("No events list");
140 }
141
142 constants = dict->FindDictByDottedPath("constants");
143 if (!constants) {
144 return base::unexpected("No constants dictionary");
145 }
146
147 // Polled data is optional (ignore success).
148 polled_data = dict->FindDictByDottedPath("polledData");
149
150 return base::ok();
151 }
152
153 // Returns the event at index |i|, or nullptr if there is none.
GetEvent(size_t i) const154 const base::Value::Dict* ParsedNetLog::GetEvent(size_t i) const {
155 if (!events || i >= events->size())
156 return nullptr;
157
158 return (*events)[i].GetIfDict();
159 }
160
161 // Creates a ParsedNetLog by reading a NetLog from a file. Returns nullptr on
162 // failure.
ReadNetLogFromDisk(const base::FilePath & log_path)163 base::expected<std::unique_ptr<ParsedNetLog>, std::string> ReadNetLogFromDisk(
164 const base::FilePath& log_path) {
165 std::string input;
166 if (!base::ReadFileToString(log_path, &input)) {
167 return base::unexpected("Failed reading file: " +
168 base::UTF16ToUTF8(log_path.LossyDisplayName()));
169 }
170
171 std::unique_ptr<ParsedNetLog> result = std::make_unique<ParsedNetLog>();
172
173 RETURN_IF_ERROR(result->InitFromFileContents(input));
174 return result;
175 }
176
177 // Checks that |log| contains events as emitted by AddEntries() above.
178 // |num_events_emitted| corresponds to |num_entries| of AddEntries(). Whereas
179 // |num_events_saved| is the expected number of events that have actually been
180 // written to the log (post-truncation).
VerifyEventsInLog(const ParsedNetLog * log,size_t num_events_emitted,size_t num_events_saved)181 void VerifyEventsInLog(const ParsedNetLog* log,
182 size_t num_events_emitted,
183 size_t num_events_saved) {
184 ASSERT_TRUE(log);
185 ASSERT_LE(num_events_saved, num_events_emitted);
186 ASSERT_EQ(num_events_saved, log->events->size());
187
188 // The last |num_events_saved| should all be sequential, with the last one
189 // being numbered |num_events_emitted - 1|.
190 for (size_t i = 0; i < num_events_saved; ++i) {
191 const base::Value::Dict* event = log->GetEvent(i);
192 ASSERT_TRUE(event);
193
194 size_t expected_source_id = num_events_emitted - num_events_saved + i;
195
196 std::optional<int> id_value = event->FindIntByDottedPath("source.id");
197 ASSERT_EQ(static_cast<int>(expected_source_id), id_value);
198 }
199 }
200
201 // Helper that checks whether |dict| has a string property at |key| having
202 // |value|.
ExpectDictionaryContainsProperty(const base::Value::Dict & dict,const std::string & key,const std::string & value)203 void ExpectDictionaryContainsProperty(const base::Value::Dict& dict,
204 const std::string& key,
205 const std::string& value) {
206 const std::string* actual_value = dict.FindStringByDottedPath(key);
207 ASSERT_EQ(value, *actual_value);
208 }
209
210 // Used for tests that are common to both bounded and unbounded modes of the
211 // the FileNetLogObserver. The param is true if bounded mode is used.
212 class FileNetLogObserverTest : public ::testing::TestWithParam<bool>,
213 public WithTaskEnvironment {
214 public:
SetUp()215 void SetUp() override {
216 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
217 log_path_ = temp_dir_.GetPath().AppendASCII("net-log.json");
218 }
219
TearDown()220 void TearDown() override {
221 logger_.reset();
222 // FileNetLogObserver destructor might post to message loop.
223 RunUntilIdle();
224 }
225
IsBounded() const226 bool IsBounded() const { return GetParam(); }
227
CreateAndStartObserving(std::unique_ptr<base::Value::Dict> constants,NetLogCaptureMode capture_mode=NetLogCaptureMode::kDefault)228 void CreateAndStartObserving(
229 std::unique_ptr<base::Value::Dict> constants,
230 NetLogCaptureMode capture_mode = NetLogCaptureMode::kDefault) {
231 if (IsBounded()) {
232 logger_ = FileNetLogObserver::CreateBoundedForTests(
233 log_path_, kLargeFileSize, kTotalNumFiles, capture_mode,
234 std::move(constants));
235 } else {
236 logger_ = FileNetLogObserver::CreateUnbounded(log_path_, capture_mode,
237 std::move(constants));
238 }
239
240 logger_->StartObserving(NetLog::Get());
241 }
242
CreateAndStartObservingPreExisting(std::unique_ptr<base::Value::Dict> constants)243 void CreateAndStartObservingPreExisting(
244 std::unique_ptr<base::Value::Dict> constants) {
245 ASSERT_TRUE(scratch_dir_.CreateUniqueTempDir());
246
247 base::File file(log_path_,
248 base::File::FLAG_CREATE | base::File::FLAG_WRITE);
249 EXPECT_TRUE(file.IsValid());
250 // Stick in some nonsense to make sure the file gets cleared properly
251 file.Write(0, "not json", 8);
252
253 if (IsBounded()) {
254 logger_ = FileNetLogObserver::CreateBoundedPreExisting(
255 scratch_dir_.GetPath(), std::move(file), kLargeFileSize,
256 NetLogCaptureMode::kDefault, std::move(constants));
257 } else {
258 logger_ = FileNetLogObserver::CreateUnboundedPreExisting(
259 std::move(file), NetLogCaptureMode::kDefault, std::move(constants));
260 }
261
262 logger_->StartObserving(NetLog::Get());
263 }
264
LogFileExists()265 bool LogFileExists() {
266 // The log files are written by a sequenced task runner. Drain all the
267 // scheduled tasks to ensure that the file writing ones have run before
268 // checking if they exist.
269 base::ThreadPoolInstance::Get()->FlushForTesting();
270 return base::PathExists(log_path_);
271 }
272
273 protected:
274 std::unique_ptr<FileNetLogObserver> logger_;
275 base::ScopedTempDir temp_dir_;
276 base::ScopedTempDir scratch_dir_; // used for bounded + preexisting
277 base::FilePath log_path_;
278 };
279
280 // Used for tests that are exclusive to the bounded mode of FileNetLogObserver.
281 class FileNetLogObserverBoundedTest : public ::testing::Test,
282 public WithTaskEnvironment {
283 public:
SetUp()284 void SetUp() override {
285 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
286 log_path_ = temp_dir_.GetPath().AppendASCII("net-log.json");
287 }
288
TearDown()289 void TearDown() override {
290 logger_.reset();
291 // FileNetLogObserver destructor might post to message loop.
292 RunUntilIdle();
293 }
294
CreateAndStartObserving(std::unique_ptr<base::Value::Dict> constants,uint64_t total_file_size,int num_files)295 void CreateAndStartObserving(std::unique_ptr<base::Value::Dict> constants,
296 uint64_t total_file_size,
297 int num_files) {
298 logger_ = FileNetLogObserver::CreateBoundedForTests(
299 log_path_, total_file_size, num_files, NetLogCaptureMode::kDefault,
300 std::move(constants));
301 logger_->StartObserving(NetLog::Get());
302 }
303
304 // Returns the path for an internally directory created for bounded logs (this
305 // needs to be kept in sync with the implementation).
GetInprogressDirectory() const306 base::FilePath GetInprogressDirectory() const {
307 return log_path_.AddExtension(FILE_PATH_LITERAL(".inprogress"));
308 }
309
GetEventFilePath(int index) const310 base::FilePath GetEventFilePath(int index) const {
311 return GetInprogressDirectory().AppendASCII(
312 "event_file_" + base::NumberToString(index) + ".json");
313 }
314
GetEndNetlogPath() const315 base::FilePath GetEndNetlogPath() const {
316 return GetInprogressDirectory().AppendASCII("end_netlog.json");
317 }
318
GetConstantsPath() const319 base::FilePath GetConstantsPath() const {
320 return GetInprogressDirectory().AppendASCII("constants.json");
321 }
322
323
324 protected:
325 std::unique_ptr<FileNetLogObserver> logger_;
326 base::FilePath log_path_;
327
328 private:
329 base::ScopedTempDir temp_dir_;
330 };
331
332 // Instantiates each FileNetLogObserverTest to use bounded and unbounded modes.
333 INSTANTIATE_TEST_SUITE_P(All,
334 FileNetLogObserverTest,
335 ::testing::Values(true, false));
336
337 // Tests deleting a FileNetLogObserver without first calling StopObserving().
TEST_P(FileNetLogObserverTest,ObserverDestroyedWithoutStopObserving)338 TEST_P(FileNetLogObserverTest, ObserverDestroyedWithoutStopObserving) {
339 CreateAndStartObserving(nullptr);
340
341 // Send dummy event
342 AddEntries(logger_.get(), 1, kDummyEventSize);
343
344 // The log files should have been started.
345 ASSERT_TRUE(LogFileExists());
346
347 logger_.reset();
348
349 // When the logger is re-set without having called StopObserving(), the
350 // partially written log files are deleted.
351 ASSERT_FALSE(LogFileExists());
352 }
353
354 // Same but with pre-existing file.
TEST_P(FileNetLogObserverTest,ObserverDestroyedWithoutStopObservingPreExisting)355 TEST_P(FileNetLogObserverTest,
356 ObserverDestroyedWithoutStopObservingPreExisting) {
357 CreateAndStartObservingPreExisting(nullptr);
358
359 // Send dummy event
360 AddEntries(logger_.get(), 1, kDummyEventSize);
361
362 // The log files should have been started.
363 ASSERT_TRUE(LogFileExists());
364
365 // Should also have the scratch dir, if bounded. (Can be checked since
366 // LogFileExists flushed the thread pool).
367 if (IsBounded()) {
368 ASSERT_TRUE(base::PathExists(scratch_dir_.GetPath()));
369 }
370
371 logger_.reset();
372
373 // Unlike in the non-preexisting case, the output file isn't deleted here,
374 // since the process running the observer likely won't have the sandbox
375 // permission to do so.
376 ASSERT_TRUE(LogFileExists());
377 if (IsBounded()) {
378 ASSERT_FALSE(base::PathExists(scratch_dir_.GetPath()));
379 }
380 }
381
382 // Tests calling StopObserving() with a null closure.
TEST_P(FileNetLogObserverTest,StopObservingNullClosure)383 TEST_P(FileNetLogObserverTest, StopObservingNullClosure) {
384 CreateAndStartObserving(nullptr);
385
386 // Send dummy event
387 AddEntries(logger_.get(), 1, kDummyEventSize);
388
389 // The log files should have been started.
390 ASSERT_TRUE(LogFileExists());
391
392 logger_->StopObserving(nullptr, base::OnceClosure());
393
394 logger_.reset();
395
396 // Since the logger was explicitly stopped, its files should still exist.
397 ASSERT_TRUE(LogFileExists());
398 }
399
400 // Tests creating a FileNetLogObserver using an invalid (can't be written to)
401 // path.
TEST_P(FileNetLogObserverTest,InitLogWithInvalidPath)402 TEST_P(FileNetLogObserverTest, InitLogWithInvalidPath) {
403 // Use a path to a non-existent directory.
404 log_path_ = temp_dir_.GetPath().AppendASCII("bogus").AppendASCII("path");
405
406 CreateAndStartObserving(nullptr);
407
408 // Send dummy event
409 AddEntries(logger_.get(), 1, kDummyEventSize);
410
411 // No log files should have been written, as the log writer will not create
412 // missing directories.
413 ASSERT_FALSE(LogFileExists());
414
415 logger_->StopObserving(nullptr, base::OnceClosure());
416
417 logger_.reset();
418
419 // There should still be no files.
420 ASSERT_FALSE(LogFileExists());
421 }
422
TEST_P(FileNetLogObserverTest,GeneratesValidJSONWithNoEvents)423 TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithNoEvents) {
424 TestClosure closure;
425
426 CreateAndStartObserving(nullptr);
427
428 logger_->StopObserving(nullptr, closure.closure());
429
430 closure.WaitForResult();
431
432 // Verify the written log.
433 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
434 ReadNetLogFromDisk(log_path_));
435 ASSERT_EQ(0u, log->events->size());
436 }
437
TEST_P(FileNetLogObserverTest,GeneratesValidJSONWithOneEvent)438 TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithOneEvent) {
439 TestClosure closure;
440
441 CreateAndStartObserving(nullptr);
442
443 // Send dummy event.
444 AddEntries(logger_.get(), 1, kDummyEventSize);
445
446 logger_->StopObserving(nullptr, closure.closure());
447
448 closure.WaitForResult();
449
450 // Verify the written log.
451 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
452 ReadNetLogFromDisk(log_path_));
453 ASSERT_EQ(1u, log->events->size());
454 }
455
TEST_P(FileNetLogObserverTest,GeneratesValidJSONWithOneEventPreExisting)456 TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithOneEventPreExisting) {
457 TestClosure closure;
458
459 CreateAndStartObservingPreExisting(nullptr);
460
461 // Send dummy event.
462 AddEntries(logger_.get(), 1, kDummyEventSize);
463
464 logger_->StopObserving(nullptr, closure.closure());
465
466 closure.WaitForResult();
467
468 // Verify the written log.
469 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
470 ReadNetLogFromDisk(log_path_));
471 ASSERT_EQ(1u, log->events->size());
472 }
473
TEST_P(FileNetLogObserverTest,PreExistingFileBroken)474 TEST_P(FileNetLogObserverTest, PreExistingFileBroken) {
475 // Test that pre-existing output file not being successfully open is
476 // tolerated.
477 ASSERT_TRUE(scratch_dir_.CreateUniqueTempDir());
478 base::File file;
479 EXPECT_FALSE(file.IsValid());
480 if (IsBounded())
481 logger_ = FileNetLogObserver::CreateBoundedPreExisting(
482 scratch_dir_.GetPath(), std::move(file), kLargeFileSize,
483 NetLogCaptureMode::kDefault, nullptr);
484 else
485 logger_ = FileNetLogObserver::CreateUnboundedPreExisting(
486 std::move(file), NetLogCaptureMode::kDefault, nullptr);
487 logger_->StartObserving(NetLog::Get());
488
489 // Send dummy event.
490 AddEntries(logger_.get(), 1, kDummyEventSize);
491 TestClosure closure;
492 logger_->StopObserving(nullptr, closure.closure());
493 closure.WaitForResult();
494 }
495
TEST_P(FileNetLogObserverTest,CustomConstants)496 TEST_P(FileNetLogObserverTest, CustomConstants) {
497 TestClosure closure;
498
499 const char kConstantKey[] = "magic";
500 const char kConstantString[] = "poney";
501 base::Value::Dict constants;
502 constants.SetByDottedPath(kConstantKey, kConstantString);
503
504 CreateAndStartObserving(
505 std::make_unique<base::Value::Dict>(std::move(constants)));
506
507 logger_->StopObserving(nullptr, closure.closure());
508
509 closure.WaitForResult();
510
511 // Verify the written log.
512 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
513 ReadNetLogFromDisk(log_path_));
514
515 // Check that custom constant was correctly printed.
516 ExpectDictionaryContainsProperty(*log->constants, kConstantKey,
517 kConstantString);
518 }
519
TEST_P(FileNetLogObserverTest,GeneratesValidJSONWithPolledData)520 TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithPolledData) {
521 TestClosure closure;
522
523 CreateAndStartObserving(nullptr);
524
525 // Create dummy polled data
526 const char kDummyPolledDataPath[] = "dummy_path";
527 const char kDummyPolledDataString[] = "dummy_info";
528 base::Value::Dict dummy_polled_data;
529 dummy_polled_data.SetByDottedPath(kDummyPolledDataPath,
530 kDummyPolledDataString);
531
532 logger_->StopObserving(
533 std::make_unique<base::Value>(std::move(dummy_polled_data)),
534 closure.closure());
535
536 closure.WaitForResult();
537
538 // Verify the written log.
539 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
540 ReadNetLogFromDisk(log_path_));
541 ASSERT_EQ(0u, log->events->size());
542
543 // Make sure additional information is present and validate it.
544 ASSERT_TRUE(log->polled_data);
545 ExpectDictionaryContainsProperty(*log->polled_data, kDummyPolledDataPath,
546 kDummyPolledDataString);
547 }
548
549 // Ensure that the Capture Mode is recorded as a constant in the NetLog.
TEST_P(FileNetLogObserverTest,LogModeRecorded)550 TEST_P(FileNetLogObserverTest, LogModeRecorded) {
551 struct TestCase {
552 NetLogCaptureMode capture_mode;
553 const char* expected_value;
554 } test_cases[] = {// Challenges that result in success results.
555 {NetLogCaptureMode::kEverything, "Everything"},
556 {NetLogCaptureMode::kIncludeSensitive, "IncludeSensitive"},
557 {NetLogCaptureMode::kDefault, "Default"}};
558
559 TestClosure closure;
560 for (const auto& test_case : test_cases) {
561 CreateAndStartObserving(nullptr, test_case.capture_mode);
562 logger_->StopObserving(nullptr, closure.closure());
563 closure.WaitForResult();
564 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
565 ReadNetLogFromDisk(log_path_));
566 ExpectDictionaryContainsProperty(*log->constants, "logCaptureMode",
567 test_case.expected_value);
568 }
569 }
570
571 // Adds events concurrently from several different threads. The exact order of
572 // events seen by this test is non-deterministic.
TEST_P(FileNetLogObserverTest,AddEventsFromMultipleThreads)573 TEST_P(FileNetLogObserverTest, AddEventsFromMultipleThreads) {
574 const size_t kNumThreads = 10;
575 std::vector<std::unique_ptr<base::Thread>> threads(kNumThreads);
576
577 #if BUILDFLAG(IS_FUCHSIA)
578 // TODO(https://crbug.com/959245): Diagnosting logging to determine where
579 // this test sometimes hangs.
580 LOG(ERROR) << "Create and start threads.";
581 #endif
582
583 // Start all the threads. Waiting for them to start is to hopefuly improve
584 // the odds of hitting interesting races once events start being added.
585 for (size_t i = 0; i < threads.size(); ++i) {
586 threads[i] = std::make_unique<base::Thread>("WorkerThread" +
587 base::NumberToString(i));
588 threads[i]->Start();
589 threads[i]->WaitUntilThreadStarted();
590 }
591
592 #if BUILDFLAG(IS_FUCHSIA)
593 LOG(ERROR) << "Create and start observing.";
594 #endif
595
596 CreateAndStartObserving(nullptr);
597
598 const size_t kNumEventsAddedPerThread = 200;
599
600 #if BUILDFLAG(IS_FUCHSIA)
601 LOG(ERROR) << "Posting tasks.";
602 #endif
603
604 // Add events in parallel from all the threads.
605 for (size_t i = 0; i < kNumThreads; ++i) {
606 threads[i]->task_runner()->PostTask(
607 FROM_HERE, base::BindOnce(&AddEntries, base::Unretained(logger_.get()),
608 kNumEventsAddedPerThread, kDummyEventSize));
609 }
610
611 #if BUILDFLAG(IS_FUCHSIA)
612 LOG(ERROR) << "Joining all threads.";
613 #endif
614
615 // Join all the threads.
616 threads.clear();
617
618 #if BUILDFLAG(IS_FUCHSIA)
619 LOG(ERROR) << "Stop observing.";
620 #endif
621
622 // Stop observing.
623 TestClosure closure;
624 logger_->StopObserving(nullptr, closure.closure());
625 closure.WaitForResult();
626
627 #if BUILDFLAG(IS_FUCHSIA)
628 LOG(ERROR) << "Read log from disk and verify.";
629 #endif
630
631 // Verify the written log.
632 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
633 ReadNetLogFromDisk(log_path_));
634 // Check that the expected number of events were written to disk.
635 EXPECT_EQ(kNumEventsAddedPerThread * kNumThreads, log->events->size());
636
637 #if BUILDFLAG(IS_FUCHSIA)
638 LOG(ERROR) << "Teardown.";
639 #endif
640 }
641
642 // Sends enough events to the observer to completely fill one file, but not
643 // write any events to an additional file. Checks the file bounds.
TEST_F(FileNetLogObserverBoundedTest,EqualToOneFile)644 TEST_F(FileNetLogObserverBoundedTest, EqualToOneFile) {
645 // The total size of the events is equal to the size of one file.
646 // |kNumEvents| * |kEventSize| = |kTotalFileSize| / |kTotalNumEvents|
647 const int kTotalFileSize = 5000;
648 const int kNumEvents = 2;
649 const int kEventSize = 250;
650 TestClosure closure;
651
652 CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
653
654 AddEntries(logger_.get(), kNumEvents, kEventSize);
655 logger_->StopObserving(nullptr, closure.closure());
656
657 closure.WaitForResult();
658
659 // Verify the written log.
660 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
661 ReadNetLogFromDisk(log_path_));
662 VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
663 }
664
665 // Sends enough events to fill one file, and partially fill a second file.
666 // Checks the file bounds and writing to a new file.
TEST_F(FileNetLogObserverBoundedTest,OneEventOverOneFile)667 TEST_F(FileNetLogObserverBoundedTest, OneEventOverOneFile) {
668 // The total size of the events is greater than the size of one file, and
669 // less than the size of two files. The total size of all events except one
670 // is equal to the size of one file, so the last event will be the only event
671 // in the second file.
672 // (|kNumEvents| - 1) * kEventSize = |kTotalFileSize| / |kTotalNumEvents|
673 const int kTotalFileSize = 6000;
674 const int kNumEvents = 4;
675 const int kEventSize = 200;
676 TestClosure closure;
677
678 CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
679
680 AddEntries(logger_.get(), kNumEvents, kEventSize);
681
682 logger_->StopObserving(nullptr, closure.closure());
683
684 closure.WaitForResult();
685
686 // Verify the written log.
687 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
688 ReadNetLogFromDisk(log_path_));
689 VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
690 }
691
692 // Sends enough events to the observer to completely fill two files.
TEST_F(FileNetLogObserverBoundedTest,EqualToTwoFiles)693 TEST_F(FileNetLogObserverBoundedTest, EqualToTwoFiles) {
694 // The total size of the events is equal to the total size of two files.
695 // |kNumEvents| * |kEventSize| = 2 * |kTotalFileSize| / |kTotalNumEvents|
696 const int kTotalFileSize = 6000;
697 const int kNumEvents = 6;
698 const int kEventSize = 200;
699 TestClosure closure;
700
701 CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
702
703 AddEntries(logger_.get(), kNumEvents, kEventSize);
704
705 logger_->StopObserving(nullptr, closure.closure());
706
707 closure.WaitForResult();
708
709 // Verify the written log.
710 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
711 ReadNetLogFromDisk(log_path_));
712 VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
713 }
714
715 // Sends exactly enough events to the observer to completely fill all files,
716 // so that all events fit into the event files and no files need to be
717 // overwritten.
TEST_F(FileNetLogObserverBoundedTest,FillAllFilesNoOverwriting)718 TEST_F(FileNetLogObserverBoundedTest, FillAllFilesNoOverwriting) {
719 // The total size of events is equal to the total size of all files.
720 // |kEventSize| * |kNumEvents| = |kTotalFileSize|
721 const int kTotalFileSize = 10000;
722 const int kEventSize = 200;
723 const int kFileSize = kTotalFileSize / kTotalNumFiles;
724 const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
725 TestClosure closure;
726
727 CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
728
729 AddEntries(logger_.get(), kNumEvents, kEventSize);
730
731 logger_->StopObserving(nullptr, closure.closure());
732
733 closure.WaitForResult();
734
735 // Verify the written log.
736 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
737 ReadNetLogFromDisk(log_path_));
738 VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
739 }
740
741 // Sends more events to the observer than will fill the WriteQueue, forcing the
742 // queue to drop an event. Checks that the queue drops the oldest event.
TEST_F(FileNetLogObserverBoundedTest,DropOldEventsFromWriteQueue)743 TEST_F(FileNetLogObserverBoundedTest, DropOldEventsFromWriteQueue) {
744 // The total size of events is greater than the WriteQueue's memory limit, so
745 // the oldest event must be dropped from the queue and not written to any
746 // file.
747 // |kNumEvents| * |kEventSize| > |kTotalFileSize| * 2
748 const int kTotalFileSize = 1000;
749 const int kNumEvents = 11;
750 const int kEventSize = 200;
751 const int kFileSize = kTotalFileSize / kTotalNumFiles;
752 TestClosure closure;
753
754 CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
755
756 AddEntries(logger_.get(), kNumEvents, kEventSize);
757
758 logger_->StopObserving(nullptr, closure.closure());
759
760 closure.WaitForResult();
761
762 // Verify the written log.
763 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
764 ReadNetLogFromDisk(log_path_));
765 VerifyEventsInLog(
766 log.get(), kNumEvents,
767 static_cast<size_t>(kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1)));
768 }
769
770 // Sends twice as many events as will fill all files to the observer, so that
771 // all of the event files will be filled twice, and every file will be
772 // overwritten.
TEST_F(FileNetLogObserverBoundedTest,OverwriteAllFiles)773 TEST_F(FileNetLogObserverBoundedTest, OverwriteAllFiles) {
774 // The total size of the events is much greater than twice the number of
775 // events that can fit in the event files, to make sure that the extra events
776 // are written to a file, not just dropped from the queue.
777 // |kNumEvents| * |kEventSize| >= 2 * |kTotalFileSize|
778 const int kTotalFileSize = 6000;
779 const int kNumEvents = 60;
780 const int kEventSize = 200;
781 const int kFileSize = kTotalFileSize / kTotalNumFiles;
782 TestClosure closure;
783
784 CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
785
786 AddEntries(logger_.get(), kNumEvents, kEventSize);
787
788 logger_->StopObserving(nullptr, closure.closure());
789
790 closure.WaitForResult();
791
792 // Check that the minimum number of events that should fit in event files
793 // have been written to all files.
794 int events_per_file = (kFileSize - 1) / kEventSize + 1;
795 int events_in_last_file = (kNumEvents - 1) % events_per_file + 1;
796
797 // Indicates the total number of events that should be written to all files.
798 int num_events_in_files =
799 (kTotalNumFiles - 1) * events_per_file + events_in_last_file;
800
801 // Verify the written log.
802 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
803 ReadNetLogFromDisk(log_path_));
804 VerifyEventsInLog(log.get(), kNumEvents,
805 static_cast<size_t>(num_events_in_files));
806 }
807
808 // Sends enough events to the observer to fill all event files, plus overwrite
809 // some files, without overwriting all of them. Checks that the FileWriter
810 // overwrites the file with the oldest events.
TEST_F(FileNetLogObserverBoundedTest,PartiallyOverwriteFiles)811 TEST_F(FileNetLogObserverBoundedTest, PartiallyOverwriteFiles) {
812 // The number of events sent to the observer is greater than the number of
813 // events that can fit into the event files, but the events can fit in less
814 // than twice the number of event files, so not every file will need to be
815 // overwritten.
816 // |kTotalFileSize| < |kNumEvents| * |kEventSize|
817 // |kNumEvents| * |kEventSize| <= (2 * |kTotalNumFiles| - 1) * |kFileSize|
818 const int kTotalFileSize = 6000;
819 const int kNumEvents = 50;
820 const int kEventSize = 200;
821 const int kFileSize = kTotalFileSize / kTotalNumFiles;
822 TestClosure closure;
823
824 CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
825
826 AddEntries(logger_.get(), kNumEvents, kEventSize);
827
828 logger_->StopObserving(nullptr, closure.closure());
829
830 closure.WaitForResult();
831
832 // Check that the minimum number of events that should fit in event files
833 // have been written to a file.
834 int events_per_file = (kFileSize - 1) / kEventSize + 1;
835 int events_in_last_file = kNumEvents % events_per_file;
836 if (!events_in_last_file)
837 events_in_last_file = events_per_file;
838 int num_events_in_files =
839 (kTotalNumFiles - 1) * events_per_file + events_in_last_file;
840
841 // Verify the written log.
842 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
843 ReadNetLogFromDisk(log_path_));
844 VerifyEventsInLog(log.get(), kNumEvents,
845 static_cast<size_t>(num_events_in_files));
846 }
847
848 // Start logging in bounded mode. Create directories in places where the logger
849 // expects to create files, in order to cause that file creation to fail.
850 //
851 // constants.json -- succeess
852 // event_file_0.json -- fails to open
853 // end_netlog.json -- fails to open
TEST_F(FileNetLogObserverBoundedTest,SomeFilesFailToOpen)854 TEST_F(FileNetLogObserverBoundedTest, SomeFilesFailToOpen) {
855 // The total size of events is equal to the total size of all files.
856 // |kEventSize| * |kNumEvents| = |kTotalFileSize|
857 const int kTotalFileSize = 10000;
858 const int kEventSize = 200;
859 const int kFileSize = kTotalFileSize / kTotalNumFiles;
860 const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
861 TestClosure closure;
862
863 // Create directories as a means to block files from being created by logger.
864 EXPECT_TRUE(base::CreateDirectory(GetEventFilePath(0)));
865 EXPECT_TRUE(base::CreateDirectory(GetEndNetlogPath()));
866
867 CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
868
869 AddEntries(logger_.get(), kNumEvents, kEventSize);
870
871 logger_->StopObserving(nullptr, closure.closure());
872
873 closure.WaitForResult();
874
875 // The written log is invalid (and hence can't be parsed). It is just the
876 // constants.
877 std::string log_contents;
878 ASSERT_TRUE(base::ReadFileToString(log_path_, &log_contents));
879 // TODO(eroman): Verify the partially written log file?
880
881 // Even though FileNetLogObserver didn't create the directory itself, it will
882 // unconditionally delete it. The name should be uncommon enough for this be
883 // to reasonable.
884 EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
885 }
886
887 // Start logging in bounded mode. Create a file at the path where the logger
888 // expects to create its inprogress directory to store event files. This will
889 // cause logging to completely break. open it.
TEST_F(FileNetLogObserverBoundedTest,InprogressDirectoryBlocked)890 TEST_F(FileNetLogObserverBoundedTest, InprogressDirectoryBlocked) {
891 // The total size of events is equal to the total size of all files.
892 // |kEventSize| * |kNumEvents| = |kTotalFileSize|
893 const int kTotalFileSize = 10000;
894 const int kEventSize = 200;
895 const int kFileSize = kTotalFileSize / kTotalNumFiles;
896 const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
897 TestClosure closure;
898
899 // By creating a file where a directory should be, it will not be possible to
900 // write any event files.
901 EXPECT_TRUE(base::WriteFile(GetInprogressDirectory(), "x"));
902
903 CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
904
905 AddEntries(logger_.get(), kNumEvents, kEventSize);
906
907 logger_->StopObserving(nullptr, closure.closure());
908
909 closure.WaitForResult();
910
911 // There will be a log file at the final output, however it will be empty
912 // since nothing was written to the .inprogress directory.
913 std::string log_contents;
914 ASSERT_TRUE(base::ReadFileToString(log_path_, &log_contents));
915 EXPECT_EQ("", log_contents);
916
917 // FileNetLogObserver unconditionally deletes the inprogress path (even though
918 // it didn't actually create this file and it was a file instead of a
919 // directory).
920 // TODO(eroman): Should it only delete if it is a file?
921 EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
922 }
923
924 // Start logging in bounded mode. Create a file with the same name as the 0th
925 // events file. This will prevent any events from being written.
TEST_F(FileNetLogObserverBoundedTest,BlockEventsFile0)926 TEST_F(FileNetLogObserverBoundedTest, BlockEventsFile0) {
927 // The total size of events is equal to the total size of all files.
928 // |kEventSize| * |kNumEvents| = |kTotalFileSize|
929 const int kTotalFileSize = 10000;
930 const int kEventSize = 200;
931 const int kFileSize = kTotalFileSize / kTotalNumFiles;
932 const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
933 TestClosure closure;
934
935 // Block the 0th events file.
936 EXPECT_TRUE(base::CreateDirectory(GetEventFilePath(0)));
937
938 CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
939
940 AddEntries(logger_.get(), kNumEvents, kEventSize);
941
942 logger_->StopObserving(nullptr, closure.closure());
943
944 closure.WaitForResult();
945
946 // Verify the written log.
947 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
948 ReadNetLogFromDisk(log_path_));
949 ASSERT_EQ(0u, log->events->size());
950 }
951
952 // Make sure that when using bounded mode with a pre-existing output file,
953 // a separate in-progress directory can be specified.
TEST_F(FileNetLogObserverBoundedTest,PreExistingUsesSpecifiedDir)954 TEST_F(FileNetLogObserverBoundedTest, PreExistingUsesSpecifiedDir) {
955 base::ScopedTempDir scratch_dir;
956 ASSERT_TRUE(scratch_dir.CreateUniqueTempDir());
957
958 base::File file(log_path_, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
959 ASSERT_TRUE(file.IsValid());
960
961 // Stick in some nonsense to make sure the file gets cleared properly
962 file.Write(0, "not json", 8);
963
964 logger_ = FileNetLogObserver::CreateBoundedPreExisting(
965 scratch_dir.GetPath(), std::move(file), kLargeFileSize,
966 NetLogCaptureMode::kDefault, nullptr);
967 logger_->StartObserving(NetLog::Get());
968
969 base::ThreadPoolInstance::Get()->FlushForTesting();
970 EXPECT_TRUE(base::PathExists(log_path_));
971 EXPECT_TRUE(
972 base::PathExists(scratch_dir.GetPath().AppendASCII("constants.json")));
973 EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
974
975 TestClosure closure;
976 logger_->StopObserving(nullptr, closure.closure());
977 closure.WaitForResult();
978
979 // Now the scratch dir should be gone, too.
980 EXPECT_FALSE(base::PathExists(scratch_dir.GetPath()));
981 EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
982 }
983
984 // Creates a bounded log with a very large total size and verifies that events
985 // are not dropped. This is a regression test for https://crbug.com/959929 in
986 // which the WriteQueue size was calculated by the possibly overflowed
987 // expression |total_file_size * 2|.
TEST_F(FileNetLogObserverBoundedTest,LargeWriteQueueSize)988 TEST_F(FileNetLogObserverBoundedTest, LargeWriteQueueSize) {
989 TestClosure closure;
990
991 // This is a large value such that multiplying it by 2 will overflow to a much
992 // smaller value (5).
993 uint64_t total_file_size = 0x8000000000000005;
994
995 CreateAndStartObserving(nullptr, total_file_size, kTotalNumFiles);
996
997 // Send 3 dummy events. This isn't a lot of data, however if WriteQueue was
998 // initialized using the overflowed value of |total_file_size * 2| (which is
999 // 5), then the effective limit would prevent any events from being written.
1000 AddEntries(logger_.get(), 3, kDummyEventSize);
1001
1002 logger_->StopObserving(nullptr, closure.closure());
1003
1004 closure.WaitForResult();
1005
1006 // Verify the written log.
1007 ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
1008 ReadNetLogFromDisk(log_path_));
1009 ASSERT_EQ(3u, log->events->size());
1010 }
1011
AddEntriesViaNetLog(NetLog * net_log,int num_entries)1012 void AddEntriesViaNetLog(NetLog* net_log, int num_entries) {
1013 for (int i = 0; i < num_entries; i++) {
1014 net_log->AddGlobalEntry(NetLogEventType::PAC_JAVASCRIPT_ERROR);
1015 }
1016 }
1017
TEST_P(FileNetLogObserverTest,AddEventsFromMultipleThreadsWithStopObserving)1018 TEST_P(FileNetLogObserverTest, AddEventsFromMultipleThreadsWithStopObserving) {
1019 const size_t kNumThreads = 10;
1020 std::vector<std::unique_ptr<base::Thread>> threads(kNumThreads);
1021 // Start all the threads. Waiting for them to start is to hopefully improve
1022 // the odds of hitting interesting races once events start being added.
1023 for (size_t i = 0; i < threads.size(); ++i) {
1024 threads[i] = std::make_unique<base::Thread>("WorkerThread" +
1025 base::NumberToString(i));
1026 threads[i]->Start();
1027 threads[i]->WaitUntilThreadStarted();
1028 }
1029
1030 CreateAndStartObserving(nullptr);
1031
1032 const size_t kNumEventsAddedPerThread = 200;
1033
1034 // Add events in parallel from all the threads.
1035 for (size_t i = 0; i < kNumThreads; ++i) {
1036 threads[i]->task_runner()->PostTask(
1037 FROM_HERE,
1038 base::BindOnce(&AddEntriesViaNetLog, base::Unretained(NetLog::Get()),
1039 kNumEventsAddedPerThread));
1040 }
1041
1042 // Stop observing.
1043 TestClosure closure;
1044 logger_->StopObserving(nullptr, closure.closure());
1045 closure.WaitForResult();
1046
1047 // Join all the threads.
1048 threads.clear();
1049
1050 ASSERT_TRUE(LogFileExists());
1051 }
1052
TEST_P(FileNetLogObserverTest,AddEventsFromMultipleThreadsWithoutStopObserving)1053 TEST_P(FileNetLogObserverTest,
1054 AddEventsFromMultipleThreadsWithoutStopObserving) {
1055 const size_t kNumThreads = 10;
1056 std::vector<std::unique_ptr<base::Thread>> threads(kNumThreads);
1057 // Start all the threads. Waiting for them to start is to hopefully improve
1058 // the odds of hitting interesting races once events start being added.
1059 for (size_t i = 0; i < threads.size(); ++i) {
1060 threads[i] = std::make_unique<base::Thread>("WorkerThread" +
1061 base::NumberToString(i));
1062 threads[i]->Start();
1063 threads[i]->WaitUntilThreadStarted();
1064 }
1065
1066 CreateAndStartObserving(nullptr);
1067
1068 const size_t kNumEventsAddedPerThread = 200;
1069
1070 // Add events in parallel from all the threads.
1071 for (size_t i = 0; i < kNumThreads; ++i) {
1072 threads[i]->task_runner()->PostTask(
1073 FROM_HERE,
1074 base::BindOnce(&AddEntriesViaNetLog, base::Unretained(NetLog::Get()),
1075 kNumEventsAddedPerThread));
1076 }
1077
1078 // Destroy logger.
1079 logger_.reset();
1080
1081 // Join all the threads.
1082 threads.clear();
1083
1084 // The log file doesn't exist since StopObserving() was not called.
1085 ASSERT_FALSE(LogFileExists());
1086 }
1087
1088 } // namespace
1089
1090 } // namespace net
1091