blob: f31acf28c9a30fd3482c91b5dde875de6f207f33 [file] [log] [blame]
wangyixf3a337e2016-12-20 23:58:021// Copyright 2016 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 "net/log/file_net_log_observer.h"
6
Eric Roman345b03df2017-07-18 20:08:377#include <algorithm>
wangyixf3a337e2016-12-20 23:58:028#include <memory>
wangyixf3a337e2016-12-20 23:58:029#include <string>
10#include <utility>
wangyixf3a337e2016-12-20 23:58:0211
12#include "base/bind.h"
Brett Wilsonc6a0c822017-09-12 00:04:2913#include "base/containers/queue.h"
Maks Orlovich4c4757b2018-04-12 14:15:1514#include "base/files/file.h"
wangyixf3a337e2016-12-20 23:58:0215#include "base/files/file_util.h"
16#include "base/json/json_writer.h"
17#include "base/logging.h"
eromanfc4bcba2017-07-07 23:48:3418#include "base/sequenced_task_runner.h"
wangyixf3a337e2016-12-20 23:58:0219#include "base/strings/string_number_conversions.h"
20#include "base/synchronization/lock.h"
Gabriel Charette44db1422018-08-06 11:19:3321#include "base/task/post_task.h"
wangyixf3a337e2016-12-20 23:58:0222#include "base/values.h"
23#include "net/log/net_log_capture_mode.h"
24#include "net/log/net_log_entry.h"
25#include "net/log/net_log_util.h"
26#include "net/url_request/url_request_context.h"
27
28namespace {
29
eromanfc4bcba2017-07-07 23:48:3430// Number of events that can build up in |write_queue_| before a task is posted
31// to the file task runner to flush them to disk.
wangyixf3a337e2016-12-20 23:58:0232const int kNumWriteQueueEvents = 15;
33
Maks Orlovich3cb883662018-04-19 01:53:1734// TODO(eroman): Should use something other than 10 for number of files?
35const int kDefaultNumFiles = 10;
36
eromanfc4bcba2017-07-07 23:48:3437scoped_refptr<base::SequencedTaskRunner> CreateFileTaskRunner() {
38 // The tasks posted to this sequenced task runner do synchronous File I/O for
39 // the purposes of writing NetLog files.
40 //
41 // These intentionally block shutdown to ensure the log file has finished
42 // being written.
43 return base::CreateSequencedTaskRunnerWithTraits(
44 {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
45 base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
46}
47
Maks Orlovich3cb883662018-04-19 01:53:1748// Truncates a file, also reseting the seek position.
49void TruncateFile(base::File* file) {
50 if (!file->IsValid())
51 return;
52 file->Seek(base::File::FROM_BEGIN, 0);
53 file->SetLength(0);
54}
55
Maks Orlovich4c4757b2018-04-12 14:15:1556// Opens |path| in write mode.
57base::File OpenFileForWrite(const base::FilePath& path) {
58 base::File result(path,
59 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
60 LOG_IF(ERROR, !result.IsValid()) << "Failed opening: " << path.value();
Eric Romana96b0432017-07-12 21:46:1861 return result;
62}
63
Maks Orlovich4c4757b2018-04-12 14:15:1564// Helper that writes data to a file. |file->IsValid()| may be false,
Eric Roman345b03df2017-07-18 20:08:3765// in which case nothing will be written. Returns the number of bytes
66// successfully written (may be less than input data in case of errors).
Maks Orlovich4c4757b2018-04-12 14:15:1567size_t WriteToFile(base::File* file,
Eric Romana96b0432017-07-12 21:46:1868 base::StringPiece data1,
69 base::StringPiece data2 = base::StringPiece(),
70 base::StringPiece data3 = base::StringPiece()) {
71 size_t bytes_written = 0;
72
Maks Orlovich4c4757b2018-04-12 14:15:1573 if (file->IsValid()) {
Eric Romana96b0432017-07-12 21:46:1874 // Append each of data1, data2 and data3.
75 if (!data1.empty())
Maks Orlovich4c4757b2018-04-12 14:15:1576 bytes_written +=
77 std::max(0, file->WriteAtCurrentPos(data1.data(), data1.size()));
Eric Romana96b0432017-07-12 21:46:1878 if (!data2.empty())
Maks Orlovich4c4757b2018-04-12 14:15:1579 bytes_written +=
80 std::max(0, file->WriteAtCurrentPos(data2.data(), data2.size()));
Eric Romana96b0432017-07-12 21:46:1881 if (!data3.empty())
Maks Orlovich4c4757b2018-04-12 14:15:1582 bytes_written +=
83 std::max(0, file->WriteAtCurrentPos(data3.data(), data3.size()));
Eric Romana96b0432017-07-12 21:46:1884 }
85
86 return bytes_written;
87}
88
Eric Roman345b03df2017-07-18 20:08:3789// Copies all of the data at |source_path| and appends it to |destination_file|,
90// then deletes |source_path|.
91void AppendToFileThenDelete(const base::FilePath& source_path,
Maks Orlovich4c4757b2018-04-12 14:15:1592 base::File* destination_file,
Eric Roman85c39b62017-07-18 20:34:5493 char* read_buffer,
94 size_t read_buffer_size) {
95 base::ScopedFILE source_file(base::OpenFile(source_path, "rb"));
96 if (!source_file)
Eric Roman345b03df2017-07-18 20:08:3797 return;
98
Eric Roman85c39b62017-07-18 20:34:5499 // Read |source_path|'s contents in chunks of read_buffer_size and append
100 // to |destination_file|.
101 size_t num_bytes_read;
102 while ((num_bytes_read =
103 fread(read_buffer, 1, read_buffer_size, source_file.get())) > 0) {
104 WriteToFile(destination_file,
105 base::StringPiece(read_buffer, num_bytes_read));
106 }
107
108 // Now that it has been copied, delete the source file.
109 source_file.reset();
Eric Roman345b03df2017-07-18 20:08:37110 base::DeleteFile(source_path, false);
111}
112
Maks Orlovich3cb883662018-04-19 01:53:17113base::FilePath SiblingInprogressDirectory(const base::FilePath& log_path) {
114 return log_path.AddExtension(FILE_PATH_LITERAL(".inprogress"));
115}
116
wangyixf3a337e2016-12-20 23:58:02117} // namespace
118
119namespace net {
120
121// Used to store events to be written to file.
Brett Wilsonc6a0c822017-09-12 00:04:29122using EventQueue = base::queue<std::unique_ptr<std::string>>;
wangyixf3a337e2016-12-20 23:58:02123
124// WriteQueue receives events from FileNetLogObserver on the main thread and
125// holds them in a queue until they are drained from the queue and written to
eromanfc4bcba2017-07-07 23:48:34126// file on the file task runner.
wangyixf3a337e2016-12-20 23:58:02127//
128// WriteQueue contains the resources shared between the main thread and the
eromanfc4bcba2017-07-07 23:48:34129// file task runner. |lock_| must be acquired to read or write to |queue_| and
wangyixf3a337e2016-12-20 23:58:02130// |memory_|.
131//
132// WriteQueue is refcounted and should be destroyed once all events on the
eromanfc4bcba2017-07-07 23:48:34133// file task runner have finished executing.
wangyixf3a337e2016-12-20 23:58:02134class FileNetLogObserver::WriteQueue
135 : public base::RefCountedThreadSafe<WriteQueue> {
136 public:
137 // |memory_max| indicates the maximum amount of memory that the virtual write
138 // queue can use. If |memory_| exceeds |memory_max_|, the |queue_| of events
139 // is overwritten.
Maks Orlovich25d9ec22018-04-26 03:28:16140 explicit WriteQueue(uint64_t memory_max);
wangyixf3a337e2016-12-20 23:58:02141
142 // Adds |event| to |queue_|. Also manages the size of |memory_|; if it
143 // exceeds |memory_max_|, then old events are dropped from |queue_| without
144 // being written to file.
145 //
146 // Returns the number of events in the |queue_|.
147 size_t AddEntryToQueue(std::unique_ptr<std::string> event);
148
149 // Swaps |queue_| with |local_queue|. |local_queue| should be empty, so that
150 // |queue_| is emptied. Resets |memory_| to 0.
151 void SwapQueue(EventQueue* local_queue);
152
153 private:
154 friend class base::RefCountedThreadSafe<WriteQueue>;
155
156 ~WriteQueue();
157
eromanfc4bcba2017-07-07 23:48:34158 // Queue of events to be written, shared between main thread and file task
159 // runner. Main thread adds events to the queue and the file task runner
160 // drains them and writes the events to file.
wangyixf3a337e2016-12-20 23:58:02161 //
162 // |lock_| must be acquired to read or write to this.
163 EventQueue queue_;
164
165 // Tracks how much memory is being used by the virtual write queue.
166 // Incremented in AddEntryToQueue() when events are added to the
eromanfc4bcba2017-07-07 23:48:34167 // buffer, and decremented when SwapQueue() is called and the file task
168 // runner's local queue is swapped with the shared write queue.
wangyixf3a337e2016-12-20 23:58:02169 //
170 // |lock_| must be acquired to read or write to this.
Maks Orlovich25d9ec22018-04-26 03:28:16171 uint64_t memory_;
wangyixf3a337e2016-12-20 23:58:02172
173 // Indicates the maximum amount of memory that the |queue_| is allowed to
174 // use.
Maks Orlovich25d9ec22018-04-26 03:28:16175 const uint64_t memory_max_;
wangyixf3a337e2016-12-20 23:58:02176
177 // Protects access to |queue_| and |memory_|.
178 //
179 // A lock is necessary because |queue_| and |memory_| are shared between the
eromanfc4bcba2017-07-07 23:48:34180 // file task runner and the main thread. NetLog's lock protects OnAddEntry(),
wangyixf3a337e2016-12-20 23:58:02181 // which calls AddEntryToQueue(), but it does not protect access to the
182 // observer's member variables. Thus, a race condition exists if a thread is
eromanfc4bcba2017-07-07 23:48:34183 // calling OnAddEntry() at the same time that the file task runner is
184 // accessing |memory_| and |queue_| to write events to file. The |queue_| and
185 // |memory_| counter are necessary to bound the amount of memory that is used
186 // for the queue in the event that the file task runner lags significantly
187 // behind the main thread in writing events to file.
wangyixf3a337e2016-12-20 23:58:02188 base::Lock lock_;
189
190 DISALLOW_COPY_AND_ASSIGN(WriteQueue);
191};
192
Eric Roman651b2732017-07-18 23:09:02193// FileWriter is responsible for draining events from a WriteQueue and writing
194// them to disk. FileWriter can be constructed on any thread, and
195// afterwards is only accessed on the file task runner.
wangyixf3a337e2016-12-20 23:58:02196class FileNetLogObserver::FileWriter {
197 public:
Eric Roman651b2732017-07-18 23:09:02198 // If max_event_file_size == kNoLimit, then no limit is enforced.
199 FileWriter(const base::FilePath& log_path,
Maks Orlovich3cb883662018-04-19 01:53:17200 const base::FilePath& inprogress_dir_path,
201 base::Optional<base::File> pre_existing_log_file,
Maks Orlovich25d9ec22018-04-26 03:28:16202 uint64_t max_event_file_size,
Eric Roman651b2732017-07-18 23:09:02203 size_t total_num_event_files,
204 scoped_refptr<base::SequencedTaskRunner> task_runner);
205
206 ~FileWriter();
wangyixf3a337e2016-12-20 23:58:02207
208 // Writes |constants_value| to disk and opens the events array (closed in
209 // Stop()).
Eric Roman651b2732017-07-18 23:09:02210 void Initialize(std::unique_ptr<base::Value> constants_value);
wangyixf3a337e2016-12-20 23:58:02211
wangyix8ae679d2017-01-28 01:47:40212 // Closes the events array opened in Initialize() and writes |polled_data| to
213 // disk. If |polled_data| cannot be converted to proper JSON, then it
wangyixf3a337e2016-12-20 23:58:02214 // is ignored.
Eric Roman651b2732017-07-18 23:09:02215 void Stop(std::unique_ptr<base::Value> polled_data);
wangyixf3a337e2016-12-20 23:58:02216
217 // Drains |queue_| from WriteQueue into a local file queue and writes the
218 // events in the queue to disk.
Eric Roman651b2732017-07-18 23:09:02219 void Flush(scoped_refptr<WriteQueue> write_queue);
wangyixf3a337e2016-12-20 23:58:02220
221 // Deletes all netlog files. It is not valid to call any method of
222 // FileNetLogObserver after DeleteAllFiles().
Eric Roman651b2732017-07-18 23:09:02223 void DeleteAllFiles();
wangyix3c6b5e32017-01-28 02:54:59224
225 void FlushThenStop(scoped_refptr<WriteQueue> write_queue,
226 std::unique_ptr<base::Value> polled_data);
wangyixf3a337e2016-12-20 23:58:02227
228 private:
Eric Roman345b03df2017-07-18 20:08:37229 // Returns true if there is no file size bound to enforce.
230 //
231 // When operating in unbounded mode, the implementation is optimized to stream
232 // writes to a single file, rather than chunking them across temporary event
233 // files.
234 bool IsUnbounded() const;
235 bool IsBounded() const;
wangyixf3a337e2016-12-20 23:58:02236
Eric Roman345b03df2017-07-18 20:08:37237 // Increments |current_event_file_number_|, and updates all state relating to
238 // the current event file (open file handle, num bytes written, current file
239 // number).
240 void IncrementCurrentEventFile();
Eric Roman772bd8d82017-07-13 21:15:37241
Eric Roman345b03df2017-07-18 20:08:37242 // Returns the path to the event file having |index|. This looks like
Eric Roman772bd8d82017-07-13 21:15:37243 // "LOGDIR/event_file_<index>.json".
244 base::FilePath GetEventFilePath(size_t index) const;
245
246 // Gets the file path where constants are saved at the start of
247 // logging. This looks like "LOGDIR/constants.json".
248 base::FilePath GetConstantsFilePath() const;
249
250 // Gets the file path where the final data is written at the end of logging.
251 // This looks like "LOGDIR/end_netlog.json".
252 base::FilePath GetClosingFilePath() const;
253
Eric Roman345b03df2017-07-18 20:08:37254 // Returns the corresponding index for |file_number|. File "numbers" are a
255 // monotonically increasing identifier that start at 1 (a value of zero means
256 // it is uninitialized), whereas the file "index" is a bounded value that
257 // wraps and identifies the file path to use.
258 //
259 // Keeping track of the current number rather than index makes it a bit easier
260 // to assemble a file at the end, since it is unambiguous which paths have
261 // been used/re-used.
262 size_t FileNumberToIndex(size_t file_number) const;
263
264 // Writes |constants_value| to a file.
265 static void WriteConstantsToFile(std::unique_ptr<base::Value> constants_value,
Maks Orlovich4c4757b2018-04-12 14:15:15266 base::File* file);
Eric Roman345b03df2017-07-18 20:08:37267
268 // Writes |polled_data| to a file.
269 static void WritePolledDataToFile(std::unique_ptr<base::Value> polled_data,
Maks Orlovich4c4757b2018-04-12 14:15:15270 base::File* file);
Eric Roman345b03df2017-07-18 20:08:37271
272 // If any events were written (wrote_event_bytes_), rewinds |file| by 2 bytes
273 // in order to overwrite the trailing ",\n" that was written by the last event
274 // line.
Maks Orlovich4c4757b2018-04-12 14:15:15275 void RewindIfWroteEventBytes(base::File* file) const;
Eric Roman345b03df2017-07-18 20:08:37276
277 // Concatenates all the log files to assemble the final
278 // |final_log_file_|. This single "stitched" file is what other
279 // log ingesting tools expect.
280 void StitchFinalLogFile();
281
282 // Creates the .inprogress directory used by bounded mode.
Maks Orlovich4c4757b2018-04-12 14:15:15283 void CreateInprogressDirectory();
Eric Roman345b03df2017-07-18 20:08:37284
Maks Orlovich3cb883662018-04-19 01:53:17285 // The file the final netlog is written to. In bounded mode this is mostly
286 // written to once logging is stopped, whereas in unbounded mode events will
287 // be directly written to it.
Maks Orlovich4c4757b2018-04-12 14:15:15288 base::File final_log_file_;
Eric Roman345b03df2017-07-18 20:08:37289
Maks Orlovich3cb883662018-04-19 01:53:17290 // If non-empty, this is the path to |final_log_file_| created and owned
291 // by FileWriter itself (rather than passed in to Create*PreExisting
292 // methods of FileNetLogObserver).
293 const base::FilePath final_log_path_;
294
295 // Path to a (temporary) directory where files are written in bounded mode.
296 // When logging is stopped these files are stitched together and written
297 // to the final log path.
298 const base::FilePath inprogress_dir_path_;
299
Maks Orlovich4c4757b2018-04-12 14:15:15300 // Holds the numbered events file where data is currently being written to.
301 // The file path of this file is GetEventFilePath(current_event_file_number_).
302 // The file may be !IsValid() if an error previously occurred opening the
303 // file, or logging has been stopped.
304 base::File current_event_file_;
Maks Orlovich25d9ec22018-04-26 03:28:16305 uint64_t current_event_file_size_;
wangyixf3a337e2016-12-20 23:58:02306
Eric Roman772bd8d82017-07-13 21:15:37307 // Indicates the total number of netlog event files allowed.
Eric Roman345b03df2017-07-18 20:08:37308 // (The files GetConstantsFilePath() and GetClosingFilePath() do
309 // not count against the total.)
310 const size_t total_num_event_files_;
wangyixf3a337e2016-12-20 23:58:02311
Eric Roman345b03df2017-07-18 20:08:37312 // Counter for the events file currently being written into. See
313 // FileNumberToIndex() for an explanation of what "number" vs "index" mean.
314 size_t current_event_file_number_;
wangyixf3a337e2016-12-20 23:58:02315
Eric Roman345b03df2017-07-18 20:08:37316 // Indicates the maximum size of each individual events file. May be kNoLimit
317 // to indicate that it can grow arbitrarily large.
Maks Orlovich25d9ec22018-04-26 03:28:16318 const uint64_t max_event_file_size_;
Eric Roman345b03df2017-07-18 20:08:37319
320 // Whether any bytes were written for events. This is used to properly format
321 // JSON (events list shouldn't end with a comma).
322 bool wrote_event_bytes_;
wangyixf3a337e2016-12-20 23:58:02323
eromanfc4bcba2017-07-07 23:48:34324 // Task runner for doing file operations.
325 const scoped_refptr<base::SequencedTaskRunner> task_runner_;
wangyixf3a337e2016-12-20 23:58:02326
Eric Roman651b2732017-07-18 23:09:02327 DISALLOW_COPY_AND_ASSIGN(FileWriter);
wangyixf3a337e2016-12-20 23:58:02328};
329
wangyix179c4802017-02-24 23:29:28330std::unique_ptr<FileNetLogObserver> FileNetLogObserver::CreateBounded(
Eric Roman345b03df2017-07-18 20:08:37331 const base::FilePath& log_path,
Maks Orlovich25d9ec22018-04-26 03:28:16332 uint64_t max_total_size,
wangyix179c4802017-02-24 23:29:28333 std::unique_ptr<base::Value> constants) {
Maks Orlovich3cb883662018-04-19 01:53:17334 return CreateInternal(log_path, SiblingInprogressDirectory(log_path),
335 base::nullopt, max_total_size, kDefaultNumFiles,
336 std::move(constants));
Eric Roman345b03df2017-07-18 20:08:37337}
338
wangyix179c4802017-02-24 23:29:28339std::unique_ptr<FileNetLogObserver> FileNetLogObserver::CreateUnbounded(
wangyix179c4802017-02-24 23:29:28340 const base::FilePath& log_path,
341 std::unique_ptr<base::Value> constants) {
Maks Orlovich3cb883662018-04-19 01:53:17342 return CreateInternal(log_path, base::FilePath(), base::nullopt, kNoLimit,
343 kDefaultNumFiles, std::move(constants));
344}
345
346std::unique_ptr<FileNetLogObserver>
347FileNetLogObserver::CreateBoundedPreExisting(
348 const base::FilePath& inprogress_dir_path,
349 base::File output_file,
Maks Orlovich25d9ec22018-04-26 03:28:16350 uint64_t max_total_size,
Maks Orlovich3cb883662018-04-19 01:53:17351 std::unique_ptr<base::Value> constants) {
352 return CreateInternal(base::FilePath(), inprogress_dir_path,
353 base::make_optional<base::File>(std::move(output_file)),
354 max_total_size, kDefaultNumFiles, std::move(constants));
355}
356
357std::unique_ptr<FileNetLogObserver>
358FileNetLogObserver::CreateUnboundedPreExisting(
359 base::File output_file,
360 std::unique_ptr<base::Value> constants) {
361 return CreateInternal(base::FilePath(), base::FilePath(),
362 base::make_optional<base::File>(std::move(output_file)),
363 kNoLimit, kDefaultNumFiles, std::move(constants));
wangyix179c4802017-02-24 23:29:28364}
wangyixf3a337e2016-12-20 23:58:02365
366FileNetLogObserver::~FileNetLogObserver() {
367 if (net_log()) {
368 // StopObserving was not called.
Eric Roman68318e72017-07-19 02:12:15369 net_log()->RemoveObserver(this);
wangyixf3a337e2016-12-20 23:58:02370 file_task_runner_->PostTask(
371 FROM_HERE, base::Bind(&FileNetLogObserver::FileWriter::DeleteAllFiles,
eromanfc4bcba2017-07-07 23:48:34372 base::Unretained(file_writer_.get())));
wangyixf3a337e2016-12-20 23:58:02373 }
eromanfc4bcba2017-07-07 23:48:34374 file_task_runner_->DeleteSoon(FROM_HERE, file_writer_.release());
wangyixf3a337e2016-12-20 23:58:02375}
376
wangyix179c4802017-02-24 23:29:28377void FileNetLogObserver::StartObserving(NetLog* net_log,
378 NetLogCaptureMode capture_mode) {
Eric Roman68318e72017-07-19 02:12:15379 net_log->AddObserver(this, capture_mode);
wangyixf3a337e2016-12-20 23:58:02380}
381
wangyix8ae679d2017-01-28 01:47:40382void FileNetLogObserver::StopObserving(std::unique_ptr<base::Value> polled_data,
eroman81b0a992017-07-08 01:29:34383 base::OnceClosure optional_callback) {
Eric Roman68318e72017-07-19 02:12:15384 net_log()->RemoveObserver(this);
pauljensen316c57d2017-06-09 01:12:53385
eroman81b0a992017-07-08 01:29:34386 base::OnceClosure bound_flush_then_stop =
eromanfc4bcba2017-07-07 23:48:34387 base::Bind(&FileNetLogObserver::FileWriter::FlushThenStop,
388 base::Unretained(file_writer_.get()), write_queue_,
eroman81b0a992017-07-08 01:29:34389 base::Passed(&polled_data));
390
391 // Note that PostTaskAndReply() requires a non-null closure.
392 if (!optional_callback.is_null()) {
393 file_task_runner_->PostTaskAndReply(FROM_HERE,
394 std::move(bound_flush_then_stop),
395 std::move(optional_callback));
396 } else {
397 file_task_runner_->PostTask(FROM_HERE, std::move(bound_flush_then_stop));
398 }
wangyixf3a337e2016-12-20 23:58:02399}
400
401void FileNetLogObserver::OnAddEntry(const NetLogEntry& entry) {
402 std::unique_ptr<std::string> json(new std::string);
403
Eric Romanaefc98c2018-12-18 21:38:01404 *json = SerializeNetLogValueToJson(*entry.ToValue());
wangyixf3a337e2016-12-20 23:58:02405
406 size_t queue_size = write_queue_->AddEntryToQueue(std::move(json));
407
eromanfc4bcba2017-07-07 23:48:34408 // If events build up in |write_queue_|, trigger the file task runner to drain
wangyixf3a337e2016-12-20 23:58:02409 // the queue. Because only 1 item is added to the queue at a time, if
410 // queue_size > kNumWriteQueueEvents a task has already been posted, or will
411 // be posted.
412 if (queue_size == kNumWriteQueueEvents) {
413 file_task_runner_->PostTask(
eromanfc4bcba2017-07-07 23:48:34414 FROM_HERE,
415 base::Bind(&FileNetLogObserver::FileWriter::Flush,
416 base::Unretained(file_writer_.get()), write_queue_));
wangyixf3a337e2016-12-20 23:58:02417 }
418}
419
Eric Roman345b03df2017-07-18 20:08:37420std::unique_ptr<FileNetLogObserver> FileNetLogObserver::CreateBoundedForTests(
421 const base::FilePath& log_path,
Maks Orlovich25d9ec22018-04-26 03:28:16422 uint64_t max_total_size,
Eric Roman345b03df2017-07-18 20:08:37423 size_t total_num_event_files,
424 std::unique_ptr<base::Value> constants) {
Maks Orlovich3cb883662018-04-19 01:53:17425 return CreateInternal(log_path, SiblingInprogressDirectory(log_path),
426 base::nullopt, max_total_size, total_num_event_files,
427 std::move(constants));
Eric Roman345b03df2017-07-18 20:08:37428}
429
Maks Orlovich3cb883662018-04-19 01:53:17430std::unique_ptr<FileNetLogObserver> FileNetLogObserver::CreateInternal(
Eric Roman668c1922017-07-19 22:11:16431 const base::FilePath& log_path,
Maks Orlovich3cb883662018-04-19 01:53:17432 const base::FilePath& inprogress_dir_path,
433 base::Optional<base::File> pre_existing_log_file,
Maks Orlovich25d9ec22018-04-26 03:28:16434 uint64_t max_total_size,
Eric Roman668c1922017-07-19 22:11:16435 size_t total_num_event_files,
436 std::unique_ptr<base::Value> constants) {
437 DCHECK_GT(total_num_event_files, 0u);
438
439 scoped_refptr<base::SequencedTaskRunner> file_task_runner =
440 CreateFileTaskRunner();
441
Maks Orlovich25d9ec22018-04-26 03:28:16442 const uint64_t max_event_file_size =
Eric Roman668c1922017-07-19 22:11:16443 max_total_size == kNoLimit ? kNoLimit
444 : max_total_size / total_num_event_files;
445
446 // The FileWriter uses a soft limit to write events to file that allows
447 // the size of the file to exceed the limit, but the WriteQueue uses a hard
448 // limit which the size of |WriteQueue::queue_| cannot exceed. Thus, the
449 // FileWriter may write more events to file than can be contained by
450 // the WriteQueue if they have the same size limit. The maximum size of the
451 // WriteQueue is doubled to allow |WriteQueue::queue_| to hold enough events
452 // for the FileWriter to fill all files. As long as all events have
453 // sizes <= the size of an individual event file, the discrepancy between the
454 // hard limit and the soft limit will not cause an issue.
455 // TODO(dconnol): Handle the case when the WriteQueue still doesn't
456 // contain enough events to fill all files, because of very large events
457 // relative to file size.
458 std::unique_ptr<FileWriter> file_writer(new FileWriter(
Maks Orlovich3cb883662018-04-19 01:53:17459 log_path, inprogress_dir_path, std::move(pre_existing_log_file),
460 max_event_file_size, total_num_event_files, file_task_runner));
Eric Roman668c1922017-07-19 22:11:16461
462 scoped_refptr<WriteQueue> write_queue(new WriteQueue(max_total_size * 2));
463
464 return std::unique_ptr<FileNetLogObserver>(
465 new FileNetLogObserver(file_task_runner, std::move(file_writer),
466 std::move(write_queue), std::move(constants)));
467}
468
wangyix179c4802017-02-24 23:29:28469FileNetLogObserver::FileNetLogObserver(
eromanfc4bcba2017-07-07 23:48:34470 scoped_refptr<base::SequencedTaskRunner> file_task_runner,
wangyix179c4802017-02-24 23:29:28471 std::unique_ptr<FileWriter> file_writer,
472 scoped_refptr<WriteQueue> write_queue,
473 std::unique_ptr<base::Value> constants)
474 : file_task_runner_(std::move(file_task_runner)),
475 write_queue_(std::move(write_queue)),
eromanfc4bcba2017-07-07 23:48:34476 file_writer_(std::move(file_writer)) {
wangyix179c4802017-02-24 23:29:28477 if (!constants)
478 constants = GetNetConstants();
479 file_task_runner_->PostTask(
eromanfc4bcba2017-07-07 23:48:34480 FROM_HERE, base::Bind(&FileNetLogObserver::FileWriter::Initialize,
481 base::Unretained(file_writer_.get()),
482 base::Passed(&constants)));
wangyix179c4802017-02-24 23:29:28483}
484
Maks Orlovich25d9ec22018-04-26 03:28:16485FileNetLogObserver::WriteQueue::WriteQueue(uint64_t memory_max)
wangyixf3a337e2016-12-20 23:58:02486 : memory_(0), memory_max_(memory_max) {}
487
488size_t FileNetLogObserver::WriteQueue::AddEntryToQueue(
489 std::unique_ptr<std::string> event) {
490 base::AutoLock lock(lock_);
491
492 memory_ += event->size();
493 queue_.push(std::move(event));
494
495 while (memory_ > memory_max_ && !queue_.empty()) {
496 // Delete oldest events in the queue.
497 DCHECK(queue_.front());
498 memory_ -= queue_.front()->size();
499 queue_.pop();
500 }
501
502 return queue_.size();
503}
wangyix8ae679d2017-01-28 01:47:40504
wangyixf3a337e2016-12-20 23:58:02505void FileNetLogObserver::WriteQueue::SwapQueue(EventQueue* local_queue) {
506 DCHECK(local_queue->empty());
507 base::AutoLock lock(lock_);
508 queue_.swap(*local_queue);
509 memory_ = 0;
510}
511
Chris Watkins6a33f762017-12-04 03:39:25512FileNetLogObserver::WriteQueue::~WriteQueue() = default;
wangyixf3a337e2016-12-20 23:58:02513
Eric Roman651b2732017-07-18 23:09:02514FileNetLogObserver::FileWriter::FileWriter(
Eric Roman345b03df2017-07-18 20:08:37515 const base::FilePath& log_path,
Maks Orlovich3cb883662018-04-19 01:53:17516 const base::FilePath& inprogress_dir_path,
517 base::Optional<base::File> pre_existing_log_file,
Maks Orlovich25d9ec22018-04-26 03:28:16518 uint64_t max_event_file_size,
Eric Roman345b03df2017-07-18 20:08:37519 size_t total_num_event_files,
eromanfc4bcba2017-07-07 23:48:34520 scoped_refptr<base::SequencedTaskRunner> task_runner)
Eric Roman345b03df2017-07-18 20:08:37521 : final_log_path_(log_path),
Maks Orlovich3cb883662018-04-19 01:53:17522 inprogress_dir_path_(inprogress_dir_path),
Eric Roman345b03df2017-07-18 20:08:37523 total_num_event_files_(total_num_event_files),
524 current_event_file_number_(0),
525 max_event_file_size_(max_event_file_size),
526 wrote_event_bytes_(false),
Maks Orlovich3cb883662018-04-19 01:53:17527 task_runner_(std::move(task_runner)) {
528 DCHECK_EQ(pre_existing_log_file.has_value(), log_path.empty());
529 DCHECK_EQ(IsBounded(), !inprogress_dir_path.empty());
530
531 if (pre_existing_log_file.has_value()) {
532 // pre_existing_log_file.IsValid() being false is fine.
533 final_log_file_ = std::move(pre_existing_log_file.value());
534 }
535}
wangyixf3a337e2016-12-20 23:58:02536
Chris Watkins6a33f762017-12-04 03:39:25537FileNetLogObserver::FileWriter::~FileWriter() = default;
wangyixf3a337e2016-12-20 23:58:02538
Eric Roman651b2732017-07-18 23:09:02539void FileNetLogObserver::FileWriter::Initialize(
wangyixf3a337e2016-12-20 23:58:02540 std::unique_ptr<base::Value> constants_value) {
peary2aeac77802017-06-01 04:11:16541 DCHECK(task_runner_->RunsTasksInCurrentSequence());
wangyixf3a337e2016-12-20 23:58:02542
Eric Roman345b03df2017-07-18 20:08:37543 // Open the final log file, and keep it open for the duration of logging (even
544 // in bounded mode).
Maks Orlovich3cb883662018-04-19 01:53:17545 if (!final_log_path_.empty())
546 final_log_file_ = OpenFileForWrite(final_log_path_);
547 else
548 TruncateFile(&final_log_file_);
wangyixf3a337e2016-12-20 23:58:02549
Eric Roman345b03df2017-07-18 20:08:37550 if (IsBounded()) {
551 CreateInprogressDirectory();
Maks Orlovich4c4757b2018-04-12 14:15:15552 base::File constants_file = OpenFileForWrite(GetConstantsFilePath());
553 WriteConstantsToFile(std::move(constants_value), &constants_file);
Eric Roman345b03df2017-07-18 20:08:37554 } else {
Maks Orlovich4c4757b2018-04-12 14:15:15555 WriteConstantsToFile(std::move(constants_value), &final_log_file_);
Eric Roman345b03df2017-07-18 20:08:37556 }
557}
wangyixf3a337e2016-12-20 23:58:02558
Eric Roman651b2732017-07-18 23:09:02559void FileNetLogObserver::FileWriter::Stop(
wangyix8ae679d2017-01-28 01:47:40560 std::unique_ptr<base::Value> polled_data) {
peary2aeac77802017-06-01 04:11:16561 DCHECK(task_runner_->RunsTasksInCurrentSequence());
wangyixf3a337e2016-12-20 23:58:02562
Eric Roman345b03df2017-07-18 20:08:37563 // Write out the polled data.
564 if (IsBounded()) {
Maks Orlovich4c4757b2018-04-12 14:15:15565 base::File closing_file = OpenFileForWrite(GetClosingFilePath());
566 WritePolledDataToFile(std::move(polled_data), &closing_file);
Eric Roman345b03df2017-07-18 20:08:37567 } else {
Maks Orlovich4c4757b2018-04-12 14:15:15568 RewindIfWroteEventBytes(&final_log_file_);
569 WritePolledDataToFile(std::move(polled_data), &final_log_file_);
Eric Roman345b03df2017-07-18 20:08:37570 }
wangyixf3a337e2016-12-20 23:58:02571
Eric Roman345b03df2017-07-18 20:08:37572 // If operating in bounded mode, the events were written to separate files
Maks Orlovich3cb883662018-04-19 01:53:17573 // within |inprogress_dir_path_|. Assemble them into the final destination
Eric Roman345b03df2017-07-18 20:08:37574 // file.
575 if (IsBounded())
576 StitchFinalLogFile();
wangyixf3a337e2016-12-20 23:58:02577
Eric Roman345b03df2017-07-18 20:08:37578 // Ensure the final log file has been flushed.
Maks Orlovich4c4757b2018-04-12 14:15:15579 final_log_file_.Close();
wangyixf3a337e2016-12-20 23:58:02580}
581
Eric Roman651b2732017-07-18 23:09:02582void FileNetLogObserver::FileWriter::Flush(
wangyixf3a337e2016-12-20 23:58:02583 scoped_refptr<FileNetLogObserver::WriteQueue> write_queue) {
peary2aeac77802017-06-01 04:11:16584 DCHECK(task_runner_->RunsTasksInCurrentSequence());
wangyixf3a337e2016-12-20 23:58:02585
586 EventQueue local_file_queue;
587 write_queue->SwapQueue(&local_file_queue);
588
wangyixf3a337e2016-12-20 23:58:02589 while (!local_file_queue.empty()) {
Maks Orlovich4c4757b2018-04-12 14:15:15590 base::File* output_file;
Eric Roman345b03df2017-07-18 20:08:37591
592 // If in bounded mode, output events to the current event file. Otherwise
593 // output events to the final log path.
594 if (IsBounded()) {
595 if (current_event_file_number_ == 0 ||
596 current_event_file_size_ >= max_event_file_size_) {
597 IncrementCurrentEventFile();
598 }
Maks Orlovich4c4757b2018-04-12 14:15:15599 output_file = &current_event_file_;
Eric Roman345b03df2017-07-18 20:08:37600 } else {
Maks Orlovich4c4757b2018-04-12 14:15:15601 output_file = &final_log_file_;
wangyixf3a337e2016-12-20 23:58:02602 }
Eric Roman345b03df2017-07-18 20:08:37603
604 size_t bytes_written =
605 WriteToFile(output_file, *local_file_queue.front(), ",\n");
606
607 wrote_event_bytes_ |= bytes_written > 0;
608
609 // Keep track of the filesize for current event file when in bounded mode.
610 if (IsBounded())
611 current_event_file_size_ += bytes_written;
612
wangyixf3a337e2016-12-20 23:58:02613 local_file_queue.pop();
614 }
615}
616
Eric Roman651b2732017-07-18 23:09:02617void FileNetLogObserver::FileWriter::DeleteAllFiles() {
peary2aeac77802017-06-01 04:11:16618 DCHECK(task_runner_->RunsTasksInCurrentSequence());
wangyixf3a337e2016-12-20 23:58:02619
Maks Orlovich4c4757b2018-04-12 14:15:15620 final_log_file_.Close();
wangyixf3a337e2016-12-20 23:58:02621
Eric Roman345b03df2017-07-18 20:08:37622 if (IsBounded()) {
Maks Orlovich4c4757b2018-04-12 14:15:15623 current_event_file_.Close();
Maks Orlovich3cb883662018-04-19 01:53:17624 base::DeleteFile(inprogress_dir_path_, true);
Eric Roman345b03df2017-07-18 20:08:37625 }
626
Maks Orlovich3cb883662018-04-19 01:53:17627 // Only delete |final_log_file_| if it was created internally.
628 // (If it was provided as a base::File by the caller, don't try to delete it).
629 if (!final_log_path_.empty())
630 base::DeleteFile(final_log_path_, false);
wangyixf3a337e2016-12-20 23:58:02631}
632
Eric Roman668c1922017-07-19 22:11:16633void FileNetLogObserver::FileWriter::FlushThenStop(
634 scoped_refptr<FileNetLogObserver::WriteQueue> write_queue,
635 std::unique_ptr<base::Value> polled_data) {
636 Flush(write_queue);
637 Stop(std::move(polled_data));
638}
639
640bool FileNetLogObserver::FileWriter::IsUnbounded() const {
641 return max_event_file_size_ == kNoLimit;
642}
643
644bool FileNetLogObserver::FileWriter::IsBounded() const {
645 return !IsUnbounded();
646}
647
648void FileNetLogObserver::FileWriter::IncrementCurrentEventFile() {
649 DCHECK(task_runner_->RunsTasksInCurrentSequence());
650 DCHECK(IsBounded());
651
652 current_event_file_number_++;
653 current_event_file_ = OpenFileForWrite(
654 GetEventFilePath(FileNumberToIndex(current_event_file_number_)));
655 current_event_file_size_ = 0;
656}
657
Eric Roman668c1922017-07-19 22:11:16658base::FilePath FileNetLogObserver::FileWriter::GetEventFilePath(
659 size_t index) const {
660 DCHECK_LT(index, total_num_event_files_);
661 DCHECK(IsBounded());
Maks Orlovich3cb883662018-04-19 01:53:17662 return inprogress_dir_path_.AppendASCII(
Brett Wilson5accd242017-11-30 22:07:32663 "event_file_" + base::NumberToString(index) + ".json");
Eric Roman668c1922017-07-19 22:11:16664}
665
666base::FilePath FileNetLogObserver::FileWriter::GetConstantsFilePath() const {
Maks Orlovich3cb883662018-04-19 01:53:17667 return inprogress_dir_path_.AppendASCII("constants.json");
Eric Roman668c1922017-07-19 22:11:16668}
669
670base::FilePath FileNetLogObserver::FileWriter::GetClosingFilePath() const {
Maks Orlovich3cb883662018-04-19 01:53:17671 return inprogress_dir_path_.AppendASCII("end_netlog.json");
Eric Roman668c1922017-07-19 22:11:16672}
673
Eric Roman651b2732017-07-18 23:09:02674size_t FileNetLogObserver::FileWriter::FileNumberToIndex(
Eric Roman345b03df2017-07-18 20:08:37675 size_t file_number) const {
676 DCHECK_GT(file_number, 0u);
677 // Note that "file numbers" start at 1 not 0.
678 return (file_number - 1) % total_num_event_files_;
wangyixf3a337e2016-12-20 23:58:02679}
680
Eric Roman668c1922017-07-19 22:11:16681void FileNetLogObserver::FileWriter::WriteConstantsToFile(
682 std::unique_ptr<base::Value> constants_value,
Maks Orlovich4c4757b2018-04-12 14:15:15683 base::File* file) {
Eric Roman668c1922017-07-19 22:11:16684 // Print constants to file and open events array.
Eric Romanaefc98c2018-12-18 21:38:01685 std::string json = SerializeNetLogValueToJson(*constants_value);
Eric Roman668c1922017-07-19 22:11:16686 WriteToFile(file, "{\"constants\":", json, ",\n\"events\": [\n");
687}
688
689void FileNetLogObserver::FileWriter::WritePolledDataToFile(
690 std::unique_ptr<base::Value> polled_data,
Maks Orlovich4c4757b2018-04-12 14:15:15691 base::File* file) {
Eric Roman668c1922017-07-19 22:11:16692 // Close the events array.
693 WriteToFile(file, "]");
694
695 // Write the polled data (if any).
696 if (polled_data) {
697 std::string polled_data_json;
698 base::JSONWriter::Write(*polled_data, &polled_data_json);
699 if (!polled_data_json.empty())
700 WriteToFile(file, ",\n\"polledData\": ", polled_data_json, "\n");
701 }
702
703 // Close the log.
704 WriteToFile(file, "}\n");
705}
706
Maks Orlovich4c4757b2018-04-12 14:15:15707void FileNetLogObserver::FileWriter::RewindIfWroteEventBytes(
708 base::File* file) const {
709 if (file->IsValid() && wrote_event_bytes_) {
Eric Roman345b03df2017-07-18 20:08:37710 // To be valid JSON the events array should not end with a comma. If events
711 // were written though, they will have been terminated with "\n," so strip
712 // it before closing the events array.
Maks Orlovich4c4757b2018-04-12 14:15:15713 file->Seek(base::File::FROM_END, -2);
wangyixf3a337e2016-12-20 23:58:02714 }
715}
716
Eric Roman651b2732017-07-18 23:09:02717void FileNetLogObserver::FileWriter::StitchFinalLogFile() {
Eric Roman345b03df2017-07-18 20:08:37718 // Make sure all the events files are flushed (as will read them next).
Maks Orlovich4c4757b2018-04-12 14:15:15719 current_event_file_.Close();
wangyixf3a337e2016-12-20 23:58:02720
Eric Roman85c39b62017-07-18 20:34:54721 // Allocate a 64K buffer used for reading the files. At most kReadBufferSize
722 // bytes will be in memory at a time.
723 const size_t kReadBufferSize = 1 << 16; // 64KiB
724 std::unique_ptr<char[]> read_buffer(new char[kReadBufferSize]);
725
Maks Orlovich3cb883662018-04-19 01:53:17726 if (final_log_file_.IsValid()) {
727 // Truncate the final log file.
728 TruncateFile(&final_log_file_);
Eric Roman345b03df2017-07-18 20:08:37729
Maks Orlovich3cb883662018-04-19 01:53:17730 // Append the constants file.
731 AppendToFileThenDelete(GetConstantsFilePath(), &final_log_file_,
732 read_buffer.get(), kReadBufferSize);
Eric Roman345b03df2017-07-18 20:08:37733
Maks Orlovich3cb883662018-04-19 01:53:17734 // Iterate over the events files, from oldest to most recent, and append
735 // them to the final destination. Note that "file numbers" start at 1 not 0.
736 size_t end_filenumber = current_event_file_number_ + 1;
737 size_t begin_filenumber =
738 current_event_file_number_ <= total_num_event_files_
739 ? 1
740 : end_filenumber - total_num_event_files_;
741 for (size_t filenumber = begin_filenumber; filenumber < end_filenumber;
742 ++filenumber) {
743 AppendToFileThenDelete(GetEventFilePath(FileNumberToIndex(filenumber)),
744 &final_log_file_, read_buffer.get(),
745 kReadBufferSize);
746 }
747
748 // Account for the final event line ending in a ",\n". Strip it to form
749 // valid JSON.
750 RewindIfWroteEventBytes(&final_log_file_);
751
752 // Append the polled data.
753 AppendToFileThenDelete(GetClosingFilePath(), &final_log_file_,
754 read_buffer.get(), kReadBufferSize);
Eric Roman345b03df2017-07-18 20:08:37755 }
756
Eric Roman345b03df2017-07-18 20:08:37757 // Delete the inprogress directory (and anything that may still be left inside
758 // it).
Maks Orlovich3cb883662018-04-19 01:53:17759 base::DeleteFile(inprogress_dir_path_, true);
wangyixf3a337e2016-12-20 23:58:02760}
761
Maks Orlovich4c4757b2018-04-12 14:15:15762void FileNetLogObserver::FileWriter::CreateInprogressDirectory() {
Eric Roman668c1922017-07-19 22:11:16763 DCHECK(IsBounded());
764
Maks Orlovich3cb883662018-04-19 01:53:17765 // If an output file couldn't be created, either creation of intermediate
766 // files will also fail (if they're in a sibling directory), or are they are
767 // hidden somewhere the user would be unlikely to find them, so there is
768 // little reason to progress.
Maks Orlovich4c4757b2018-04-12 14:15:15769 if (!final_log_file_.IsValid())
Eric Roman668c1922017-07-19 22:11:16770 return;
771
Maks Orlovich3cb883662018-04-19 01:53:17772 if (!base::CreateDirectory(inprogress_dir_path_)) {
Eric Roman668c1922017-07-19 22:11:16773 LOG(WARNING) << "Failed creating directory: "
Maks Orlovich3cb883662018-04-19 01:53:17774 << inprogress_dir_path_.value();
Eric Roman668c1922017-07-19 22:11:16775 return;
776 }
777
778 // It is OK if the path is wrong due to encoding - this is really just a
779 // convenience display for the user in understanding what the file means.
Maks Orlovich3cb883662018-04-19 01:53:17780 std::string in_progress_path = inprogress_dir_path_.AsUTF8Unsafe();
Eric Roman668c1922017-07-19 22:11:16781
782 // Since |final_log_file_| will not be written to until the very end, leave
783 // some data in it explaining that the real data is currently in the
784 // .inprogress directory. This ordinarily won't be visible (overwritten when
785 // stopping) however if logging does not end gracefully the comments are
786 // useful for recovery.
787 WriteToFile(
Maks Orlovich4c4757b2018-04-12 14:15:15788 &final_log_file_, "Logging is in progress writing data to:\n ",
Eric Roman668c1922017-07-19 22:11:16789 in_progress_path,
790 "\n\n"
791 "That data will be stitched into a single file (this one) once logging\n"
792 "has stopped.\n"
793 "\n"
794 "If logging was interrupted, you can stitch a NetLog file out of the\n"
795 ".inprogress directory manually using:\n"
796 "\n"
797 "https://chromium.googlesource.com/chromium/src/+/master/net/tools/"
798 "stitch_net_log_files.py\n");
Eric Roman668c1922017-07-19 22:11:16799}
800
Eric Romanaefc98c2018-12-18 21:38:01801std::string SerializeNetLogValueToJson(const base::Value& value) {
802 // Omit trailing ".0" when printing a DOUBLE that is representable as a 64-bit
803 // integer. This makes the values returned by NetLogNumberValue() look more
804 // pleasant (for representing integers between 32 and 53 bits large).
805 int options = base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION;
806
807 std::string json;
808 bool ok = base::JSONWriter::WriteWithOptions(value, options, &json);
809
810 // Serialization shouldn't fail. However it can if a consumer has passed a
811 // parameter of type BINARY, since JSON serialization can't handle that.
812 DCHECK(ok);
813
814 return json;
815}
816
wangyixf3a337e2016-12-20 23:58:02817} // namespace net