blob: 16f160c05def0e8c2983f831b854b0d049bdab2a [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
7#include <limits>
8#include <memory>
9#include <queue>
10#include <set>
11#include <string>
12#include <utility>
13#include <vector>
14
15#include "base/bind.h"
16#include "base/files/file_util.h"
17#include "base/json/json_writer.h"
18#include "base/logging.h"
19#include "base/strings/string_number_conversions.h"
20#include "base/synchronization/lock.h"
21#include "base/threading/thread.h"
22#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
30// Number of events that can build up in |write_queue_| before file thread
31// is triggered to drain the queue.
32const int kNumWriteQueueEvents = 15;
33
34} // namespace
35
36namespace net {
37
38// Used to store events to be written to file.
39using EventQueue = std::queue<std::unique_ptr<std::string>>;
40
41// WriteQueue receives events from FileNetLogObserver on the main thread and
42// holds them in a queue until they are drained from the queue and written to
43// file on the file thread.
44//
45// WriteQueue contains the resources shared between the main thread and the
46// file thread. |lock_| must be acquired to read or write to |queue_| and
47// |memory_|.
48//
49// WriteQueue is refcounted and should be destroyed once all events on the
50// file thread have finished executing.
51class FileNetLogObserver::WriteQueue
52 : public base::RefCountedThreadSafe<WriteQueue> {
53 public:
54 // |memory_max| indicates the maximum amount of memory that the virtual write
55 // queue can use. If |memory_| exceeds |memory_max_|, the |queue_| of events
56 // is overwritten.
57 explicit WriteQueue(size_t memory_max);
58
59 // Adds |event| to |queue_|. Also manages the size of |memory_|; if it
60 // exceeds |memory_max_|, then old events are dropped from |queue_| without
61 // being written to file.
62 //
63 // Returns the number of events in the |queue_|.
64 size_t AddEntryToQueue(std::unique_ptr<std::string> event);
65
66 // Swaps |queue_| with |local_queue|. |local_queue| should be empty, so that
67 // |queue_| is emptied. Resets |memory_| to 0.
68 void SwapQueue(EventQueue* local_queue);
69
70 private:
71 friend class base::RefCountedThreadSafe<WriteQueue>;
72
73 ~WriteQueue();
74
75 // Queue of events to be written shared between main thread and file thread.
76 // Main thread adds events to the queue and the file thread drains them and
77 // writes the events to file.
78 //
79 // |lock_| must be acquired to read or write to this.
80 EventQueue queue_;
81
82 // Tracks how much memory is being used by the virtual write queue.
83 // Incremented in AddEntryToQueue() when events are added to the
84 // buffer, and decremented when SwapQueue() is called and the file thread's
85 // local queue is swapped with the shared write queue.
86 //
87 // |lock_| must be acquired to read or write to this.
88 size_t memory_;
89
90 // Indicates the maximum amount of memory that the |queue_| is allowed to
91 // use.
92 const size_t memory_max_;
93
94 // Protects access to |queue_| and |memory_|.
95 //
96 // A lock is necessary because |queue_| and |memory_| are shared between the
97 // file thread and the main thread. NetLog's lock protects OnAddEntry(),
98 // which calls AddEntryToQueue(), but it does not protect access to the
99 // observer's member variables. Thus, a race condition exists if a thread is
100 // calling OnAddEntry() at the same time that the file thread is accessing
101 // |memory_| and |queue_| to write events to file. The |queue_| and |memory_|
102 // counter are necessary to bound the amount of memory that is used for the
103 // queue in the event that the file thread lags significantly behind the main
104 // thread in writing events to file.
105 base::Lock lock_;
106
107 DISALLOW_COPY_AND_ASSIGN(WriteQueue);
108};
109
110// FileWriter is an interface describing an object that drains events from a
111// WriteQueue and writes them to disk.
112class FileNetLogObserver::FileWriter {
113 public:
114 virtual ~FileWriter();
115
116 // Writes |constants_value| to disk and opens the events array (closed in
117 // Stop()).
118 virtual void Initialize(std::unique_ptr<base::Value> constants_value) = 0;
119
wangyix8ae679d2017-01-28 01:47:40120 // Closes the events array opened in Initialize() and writes |polled_data| to
121 // disk. If |polled_data| cannot be converted to proper JSON, then it
wangyixf3a337e2016-12-20 23:58:02122 // is ignored.
wangyix8ae679d2017-01-28 01:47:40123 virtual void Stop(std::unique_ptr<base::Value> polled_data) = 0;
wangyixf3a337e2016-12-20 23:58:02124
125 // Drains |queue_| from WriteQueue into a local file queue and writes the
126 // events in the queue to disk.
127 virtual void Flush(scoped_refptr<WriteQueue> write_queue) = 0;
128
129 // Deletes all netlog files. It is not valid to call any method of
130 // FileNetLogObserver after DeleteAllFiles().
131 virtual void DeleteAllFiles() = 0;
wangyix3c6b5e32017-01-28 02:54:59132
133 void FlushThenStop(scoped_refptr<WriteQueue> write_queue,
134 std::unique_ptr<base::Value> polled_data);
wangyixf3a337e2016-12-20 23:58:02135};
136
137// This implementation of FileWriter is used when the observer is in bounded
138// mode.
139// BoundedFileWriter can be constructed on any thread, and afterwards is only
140// accessed on the file thread.
141class FileNetLogObserver::BoundedFileWriter
142 : public FileNetLogObserver::FileWriter {
143 public:
144 BoundedFileWriter(const base::FilePath& directory,
145 size_t max_file_size,
146 size_t total_num_files,
147 scoped_refptr<base::SingleThreadTaskRunner> task_runner);
148
149 ~BoundedFileWriter() override;
150
151 // FileNetLogObserver::FileWriter implementation
152 void Initialize(std::unique_ptr<base::Value> constants_value) override;
wangyix8ae679d2017-01-28 01:47:40153 void Stop(std::unique_ptr<base::Value> polled_data) override;
wangyixf3a337e2016-12-20 23:58:02154 void Flush(scoped_refptr<WriteQueue> write_queue) override;
155 void DeleteAllFiles() override;
156
157 private:
158 // Increments |current_file_idx_|, and handles the clearing and openining of
159 // the new current file. Also sets |event_files_[current_file_idx_]| to point
160 // to the new current file.
161 void IncrementCurrentFile();
162
163 // Each ScopedFILE points to a netlog event file with the file name
164 // "event_file_<index>.json".
165 std::vector<base::ScopedFILE> event_files_;
166
167 // The directory where the netlog files are created.
168 const base::FilePath directory_;
169
170 // Indicates the total number of netlog event files, which does not include
171 // the constants file (constants.json), or closing file (end_netlog.json).
172 const size_t total_num_files_;
173
174 // Indicates the index of the file in |event_files_| currently being written
175 // into.
176 size_t current_file_idx_;
177
178 // Indicates the maximum size of each individual netlogging file, excluding
179 // the constant file.
180 const size_t max_file_size_;
181
182 // Task runner from the file thread.
183 const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
184
185 DISALLOW_COPY_AND_ASSIGN(BoundedFileWriter);
186};
187
188// This implementation of FileWriter is used when the observer is in unbounded
189// mode.
190// UnboundedFileWriter can be constructed on any thread, and afterwards is only
191// accessed on the file thread.
192class FileNetLogObserver::UnboundedFileWriter
193 : public FileNetLogObserver::FileWriter {
194 public:
195 UnboundedFileWriter(const base::FilePath& path,
196 scoped_refptr<base::SingleThreadTaskRunner> task_runner);
197
198 ~UnboundedFileWriter() override;
199
200 // FileNetLogObserver::FileWriter implementation
201 void Initialize(std::unique_ptr<base::Value> constants_value) override;
wangyix8ae679d2017-01-28 01:47:40202 void Stop(std::unique_ptr<base::Value> polled_data) override;
wangyixf3a337e2016-12-20 23:58:02203 void Flush(scoped_refptr<WriteQueue> write_queue) override;
204 void DeleteAllFiles() override;
205
206 private:
207 base::FilePath file_path_;
208 base::ScopedFILE file_;
209
210 // Is set to true after the first event is written to the log file.
211 bool first_event_written_;
212
213 // Task runner from the file thread.
214 const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
215
216 DISALLOW_COPY_AND_ASSIGN(UnboundedFileWriter);
217};
218
wangyix179c4802017-02-24 23:29:28219std::unique_ptr<FileNetLogObserver> FileNetLogObserver::CreateBounded(
220 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
221 const base::FilePath& directory,
222 size_t max_total_size,
223 size_t total_num_files,
224 std::unique_ptr<base::Value> constants) {
225 DCHECK_GT(total_num_files, 0u);
226 // The BoundedFileWriter uses a soft limit to write events to file that allows
227 // the size of the file to exceed the limit, but the WriteQueue uses a hard
228 // limit which the size of |WriteQueue::queue_| cannot exceed. Thus, the
229 // BoundedFileWriter may write more events to file than can be contained by
230 // the WriteQueue if they have the same size limit. The maximum size of the
231 // WriteQueue is doubled to allow |WriteQueue::queue_| to hold enough events
232 // for the BoundedFileWriter to fill all files. As long as all events have
233 // sizes <= the size of an individual event file, the discrepancy between the
234 // hard limit and the soft limit will not cause an issue.
235 // TODO(dconnol): Handle the case when the WriteQueue still doesn't
236 // contain enough events to fill all files, because of very large events
237 // relative to file size.
238 std::unique_ptr<FileWriter> file_writer(
239 new BoundedFileWriter(directory, max_total_size / total_num_files,
240 total_num_files, file_task_runner));
241
242 scoped_refptr<WriteQueue> write_queue(new WriteQueue(max_total_size * 2));
243
244 return std::unique_ptr<FileNetLogObserver>(
245 new FileNetLogObserver(file_task_runner, std::move(file_writer),
246 std::move(write_queue), std::move(constants)));
247}
248
249std::unique_ptr<FileNetLogObserver> FileNetLogObserver::CreateUnbounded(
250 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
251 const base::FilePath& log_path,
252 std::unique_ptr<base::Value> constants) {
253 std::unique_ptr<FileWriter> file_writer(
254 new UnboundedFileWriter(log_path, file_task_runner));
255
256 scoped_refptr<WriteQueue> write_queue(
257 new WriteQueue(std::numeric_limits<size_t>::max()));
258
259 return std::unique_ptr<FileNetLogObserver>(
260 new FileNetLogObserver(file_task_runner, std::move(file_writer),
261 std::move(write_queue), std::move(constants)));
262}
wangyixf3a337e2016-12-20 23:58:02263
264FileNetLogObserver::~FileNetLogObserver() {
265 if (net_log()) {
266 // StopObserving was not called.
267 file_task_runner_->PostTask(
268 FROM_HERE, base::Bind(&FileNetLogObserver::FileWriter::DeleteAllFiles,
269 base::Unretained(file_writer_)));
270 net_log()->DeprecatedRemoveObserver(this);
271 }
wangyixf3a337e2016-12-20 23:58:02272 file_task_runner_->DeleteSoon(FROM_HERE, file_writer_);
273}
274
wangyix179c4802017-02-24 23:29:28275void FileNetLogObserver::StartObserving(NetLog* net_log,
276 NetLogCaptureMode capture_mode) {
wangyixf3a337e2016-12-20 23:58:02277 net_log->DeprecatedAddObserver(this, capture_mode);
278}
279
wangyix8ae679d2017-01-28 01:47:40280void FileNetLogObserver::StopObserving(std::unique_ptr<base::Value> polled_data,
wangyixf3a337e2016-12-20 23:58:02281 const base::Closure& callback) {
wangyixf3a337e2016-12-20 23:58:02282 file_task_runner_->PostTaskAndReply(
wangyix3c6b5e32017-01-28 02:54:59283 FROM_HERE, base::Bind(&FileNetLogObserver::FileWriter::FlushThenStop,
284 base::Unretained(file_writer_), write_queue_,
285 base::Passed(&polled_data)),
wangyixf3a337e2016-12-20 23:58:02286 callback);
287
288 net_log()->DeprecatedRemoveObserver(this);
289}
290
291void FileNetLogObserver::OnAddEntry(const NetLogEntry& entry) {
292 std::unique_ptr<std::string> json(new std::string);
293
294 // If |entry| cannot be converted to proper JSON, ignore it.
295 if (!base::JSONWriter::Write(*entry.ToValue(), json.get()))
296 return;
297
298 size_t queue_size = write_queue_->AddEntryToQueue(std::move(json));
299
300 // If events build up in |write_queue_|, trigger the file thread to drain
301 // the queue. Because only 1 item is added to the queue at a time, if
302 // queue_size > kNumWriteQueueEvents a task has already been posted, or will
303 // be posted.
304 if (queue_size == kNumWriteQueueEvents) {
305 file_task_runner_->PostTask(
306 FROM_HERE, base::Bind(&FileNetLogObserver::FileWriter::Flush,
307 base::Unretained(file_writer_), write_queue_));
308 }
309}
310
wangyix179c4802017-02-24 23:29:28311FileNetLogObserver::FileNetLogObserver(
312 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
313 std::unique_ptr<FileWriter> file_writer,
314 scoped_refptr<WriteQueue> write_queue,
315 std::unique_ptr<base::Value> constants)
316 : file_task_runner_(std::move(file_task_runner)),
317 write_queue_(std::move(write_queue)),
318 file_writer_(file_writer.release()) {
319 if (!constants)
320 constants = GetNetConstants();
321 file_task_runner_->PostTask(
322 FROM_HERE,
323 base::Bind(&FileNetLogObserver::FileWriter::Initialize,
324 base::Unretained(file_writer_), base::Passed(&constants)));
325}
326
wangyixf3a337e2016-12-20 23:58:02327FileNetLogObserver::WriteQueue::WriteQueue(size_t memory_max)
328 : memory_(0), memory_max_(memory_max) {}
329
330size_t FileNetLogObserver::WriteQueue::AddEntryToQueue(
331 std::unique_ptr<std::string> event) {
332 base::AutoLock lock(lock_);
333
334 memory_ += event->size();
335 queue_.push(std::move(event));
336
337 while (memory_ > memory_max_ && !queue_.empty()) {
338 // Delete oldest events in the queue.
339 DCHECK(queue_.front());
340 memory_ -= queue_.front()->size();
341 queue_.pop();
342 }
343
344 return queue_.size();
345}
wangyix8ae679d2017-01-28 01:47:40346
wangyixf3a337e2016-12-20 23:58:02347void FileNetLogObserver::WriteQueue::SwapQueue(EventQueue* local_queue) {
348 DCHECK(local_queue->empty());
349 base::AutoLock lock(lock_);
350 queue_.swap(*local_queue);
351 memory_ = 0;
352}
353
354FileNetLogObserver::WriteQueue::~WriteQueue() {}
355
356FileNetLogObserver::FileWriter::~FileWriter() {}
357
wangyix3c6b5e32017-01-28 02:54:59358void FileNetLogObserver::FileWriter::FlushThenStop(
359 scoped_refptr<FileNetLogObserver::WriteQueue> write_queue,
360 std::unique_ptr<base::Value> polled_data) {
361 Flush(write_queue);
362 Stop(std::move(polled_data));
363}
364
wangyixf3a337e2016-12-20 23:58:02365FileNetLogObserver::BoundedFileWriter::BoundedFileWriter(
366 const base::FilePath& directory,
367 size_t max_file_size,
368 size_t total_num_files,
369 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
370 : directory_(directory),
371 total_num_files_(total_num_files),
372 current_file_idx_(0),
373 max_file_size_(max_file_size),
374 task_runner_(task_runner) {
375 event_files_.resize(total_num_files_);
376}
377
378FileNetLogObserver::BoundedFileWriter::~BoundedFileWriter() {}
379
380void FileNetLogObserver::BoundedFileWriter::Initialize(
381 std::unique_ptr<base::Value> constants_value) {
382 DCHECK(task_runner_->RunsTasksOnCurrentThread());
383
384 event_files_[current_file_idx_] = base::ScopedFILE(
385 base::OpenFile(directory_.AppendASCII("event_file_0.json"), "w"));
386
387 base::ScopedFILE constants_file(
388 base::OpenFile(directory_.AppendASCII("constants.json"), "w"));
389
390 // Print constants to file and open events array.
391 std::string json;
392
393 // It should always be possible to convert constants to JSON.
394 if (!base::JSONWriter::Write(*constants_value, &json))
395 DCHECK(false);
396 fprintf(constants_file.get(), "{\"constants\":%s,\n\"events\": [\n",
397 json.c_str());
398}
399
400void FileNetLogObserver::BoundedFileWriter::Stop(
wangyix8ae679d2017-01-28 01:47:40401 std::unique_ptr<base::Value> polled_data) {
wangyixf3a337e2016-12-20 23:58:02402 DCHECK(task_runner_->RunsTasksOnCurrentThread());
403
404 base::ScopedFILE closing_file(
405 base::OpenFile(directory_.AppendASCII("end_netlog.json"), "w"));
406
407 std::string json;
wangyix8ae679d2017-01-28 01:47:40408 if (polled_data)
409 base::JSONWriter::Write(*polled_data, &json);
wangyixf3a337e2016-12-20 23:58:02410
411 fprintf(closing_file.get(), "]%s}\n",
wangyix8ae679d2017-01-28 01:47:40412 json.empty() ? "" : (",\n\"polledData\": " + json + "\n").c_str());
wangyixf3a337e2016-12-20 23:58:02413
414 // Flush all fprintfs to disk so that files can be safely accessed on
415 // callback.
416 event_files_.clear();
417}
418
419void FileNetLogObserver::BoundedFileWriter::IncrementCurrentFile() {
420 DCHECK(task_runner_->RunsTasksOnCurrentThread());
421
422 current_file_idx_++;
423 current_file_idx_ %= total_num_files_;
424 event_files_[current_file_idx_].reset();
425 event_files_[current_file_idx_] = base::ScopedFILE(base::OpenFile(
426 directory_.AppendASCII("event_file_" +
427 base::SizeTToString(current_file_idx_) + ".json"),
428 "w"));
429}
430
431void FileNetLogObserver::BoundedFileWriter::Flush(
432 scoped_refptr<FileNetLogObserver::WriteQueue> write_queue) {
433 DCHECK(task_runner_->RunsTasksOnCurrentThread());
434
435 EventQueue local_file_queue;
436 write_queue->SwapQueue(&local_file_queue);
437
438 std::string to_print;
439 size_t file_size = ftell(event_files_[current_file_idx_].get());
440 size_t memory_freed = 0;
441
442 while (!local_file_queue.empty()) {
443 if (file_size >= max_file_size_) {
444 // The current file is full. Start a new current file.
445 IncrementCurrentFile();
446 file_size = 0;
447 }
448 fprintf(event_files_[current_file_idx_].get(), "%s,\n",
449 local_file_queue.front().get()->c_str());
450 file_size += local_file_queue.front()->size();
451 memory_freed += local_file_queue.front()->size();
452 local_file_queue.pop();
453 }
454}
455
456void FileNetLogObserver::BoundedFileWriter::DeleteAllFiles() {
457 DCHECK(task_runner_->RunsTasksOnCurrentThread());
458
459 // Reset |event_files_| to release all file handles so base::DeleteFile can
460 // safely access files.
461 event_files_.clear();
462
463 base::DeleteFile(directory_.AppendASCII("constants.json"), false);
464 base::DeleteFile(directory_.AppendASCII("end_netlog.json"), false);
465 for (size_t i = 0; i < total_num_files_; i++) {
466 base::DeleteFile(directory_.AppendASCII("event_file_" +
467 base::SizeTToString(i) + ".json"),
468 false);
469 }
470}
471
472FileNetLogObserver::UnboundedFileWriter::UnboundedFileWriter(
473 const base::FilePath& path,
474 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
475 : file_path_(path), task_runner_(task_runner) {}
476
477FileNetLogObserver::UnboundedFileWriter::~UnboundedFileWriter() {}
478
479void FileNetLogObserver::UnboundedFileWriter::Initialize(
480 std::unique_ptr<base::Value> constants_value) {
481 DCHECK(task_runner_->RunsTasksOnCurrentThread());
482
483 file_.reset(base::OpenFile(file_path_, "w"));
484 first_event_written_ = false;
485
486 // Print constants to file and open events array.
487 std::string json;
488
489 // It should always be possible to convert constants to JSON.
490 if (!base::JSONWriter::Write(*constants_value, &json))
491 DCHECK(false);
492 fprintf(file_.get(), "{\"constants\":%s,\n\"events\": [\n", json.c_str());
493}
494
495void FileNetLogObserver::UnboundedFileWriter::Stop(
wangyix8ae679d2017-01-28 01:47:40496 std::unique_ptr<base::Value> polled_data) {
wangyixf3a337e2016-12-20 23:58:02497 DCHECK(task_runner_->RunsTasksOnCurrentThread());
498
499 std::string json;
wangyix8ae679d2017-01-28 01:47:40500 if (polled_data)
501 base::JSONWriter::Write(*polled_data, &json);
wangyixf3a337e2016-12-20 23:58:02502
503 fprintf(file_.get(), "]%s}\n",
wangyix8ae679d2017-01-28 01:47:40504 json.empty() ? "" : (",\n\"polledData\": " + json + "\n").c_str());
wangyixf3a337e2016-12-20 23:58:02505
506 // Flush all fprintfs to disk so that the file can be safely accessed on
507 // callback.
508 file_.reset();
509}
510
511void FileNetLogObserver::UnboundedFileWriter::Flush(
512 scoped_refptr<FileNetLogObserver::WriteQueue> write_queue) {
513 DCHECK(task_runner_->RunsTasksOnCurrentThread());
514
515 EventQueue local_file_queue;
516 write_queue->SwapQueue(&local_file_queue);
517
518 while (!local_file_queue.empty()) {
519 if (first_event_written_) {
520 fputs(",\n", file_.get());
521 } else {
522 first_event_written_ = true;
523 }
524 fputs(local_file_queue.front()->c_str(), file_.get());
525 local_file_queue.pop();
526 }
527}
528
529void FileNetLogObserver::UnboundedFileWriter::DeleteAllFiles() {
530 DCHECK(task_runner_->RunsTasksOnCurrentThread());
531
532 // Reset |file_| to release the file handle so base::DeleteFile can
533 // safely access it.
534 file_.reset();
535 base::DeleteFile(file_path_, false);
536}
537
538} // namespace net