wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 1 | // 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 Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 7 | #include <algorithm> |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 8 | #include <memory> |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 9 | #include <string> |
| 10 | #include <utility> |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 11 | |
| 12 | #include "base/bind.h" |
Brett Wilson | c6a0c82 | 2017-09-12 00:04:29 | [diff] [blame] | 13 | #include "base/containers/queue.h" |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 14 | #include "base/files/file.h" |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 15 | #include "base/files/file_util.h" |
| 16 | #include "base/json/json_writer.h" |
| 17 | #include "base/logging.h" |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 18 | #include "base/sequenced_task_runner.h" |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 19 | #include "base/strings/string_number_conversions.h" |
| 20 | #include "base/synchronization/lock.h" |
Gabriel Charette | 44db142 | 2018-08-06 11:19:33 | [diff] [blame] | 21 | #include "base/task/post_task.h" |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 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 | |
| 28 | namespace { |
| 29 | |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 30 | // 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. |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 32 | const int kNumWriteQueueEvents = 15; |
| 33 | |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 34 | // TODO(eroman): Should use something other than 10 for number of files? |
| 35 | const int kDefaultNumFiles = 10; |
| 36 | |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 37 | scoped_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 Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 48 | // Truncates a file, also reseting the seek position. |
| 49 | void 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 56 | // Opens |path| in write mode. |
| 57 | base::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 Roman | a96b043 | 2017-07-12 21:46:18 | [diff] [blame] | 61 | return result; |
| 62 | } |
| 63 | |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 64 | // Helper that writes data to a file. |file->IsValid()| may be false, |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 65 | // 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 67 | size_t WriteToFile(base::File* file, |
Eric Roman | a96b043 | 2017-07-12 21:46:18 | [diff] [blame] | 68 | base::StringPiece data1, |
| 69 | base::StringPiece data2 = base::StringPiece(), |
| 70 | base::StringPiece data3 = base::StringPiece()) { |
| 71 | size_t bytes_written = 0; |
| 72 | |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 73 | if (file->IsValid()) { |
Eric Roman | a96b043 | 2017-07-12 21:46:18 | [diff] [blame] | 74 | // Append each of data1, data2 and data3. |
| 75 | if (!data1.empty()) |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 76 | bytes_written += |
| 77 | std::max(0, file->WriteAtCurrentPos(data1.data(), data1.size())); |
Eric Roman | a96b043 | 2017-07-12 21:46:18 | [diff] [blame] | 78 | if (!data2.empty()) |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 79 | bytes_written += |
| 80 | std::max(0, file->WriteAtCurrentPos(data2.data(), data2.size())); |
Eric Roman | a96b043 | 2017-07-12 21:46:18 | [diff] [blame] | 81 | if (!data3.empty()) |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 82 | bytes_written += |
| 83 | std::max(0, file->WriteAtCurrentPos(data3.data(), data3.size())); |
Eric Roman | a96b043 | 2017-07-12 21:46:18 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | return bytes_written; |
| 87 | } |
| 88 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 89 | // Copies all of the data at |source_path| and appends it to |destination_file|, |
| 90 | // then deletes |source_path|. |
| 91 | void AppendToFileThenDelete(const base::FilePath& source_path, |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 92 | base::File* destination_file, |
Eric Roman | 85c39b6 | 2017-07-18 20:34:54 | [diff] [blame] | 93 | char* read_buffer, |
| 94 | size_t read_buffer_size) { |
| 95 | base::ScopedFILE source_file(base::OpenFile(source_path, "rb")); |
| 96 | if (!source_file) |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 97 | return; |
| 98 | |
Eric Roman | 85c39b6 | 2017-07-18 20:34:54 | [diff] [blame] | 99 | // 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 Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 110 | base::DeleteFile(source_path, false); |
| 111 | } |
| 112 | |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 113 | base::FilePath SiblingInprogressDirectory(const base::FilePath& log_path) { |
| 114 | return log_path.AddExtension(FILE_PATH_LITERAL(".inprogress")); |
| 115 | } |
| 116 | |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 117 | } // namespace |
| 118 | |
| 119 | namespace net { |
| 120 | |
| 121 | // Used to store events to be written to file. |
Brett Wilson | c6a0c82 | 2017-09-12 00:04:29 | [diff] [blame] | 122 | using EventQueue = base::queue<std::unique_ptr<std::string>>; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 123 | |
| 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 |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 126 | // file on the file task runner. |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 127 | // |
| 128 | // WriteQueue contains the resources shared between the main thread and the |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 129 | // file task runner. |lock_| must be acquired to read or write to |queue_| and |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 130 | // |memory_|. |
| 131 | // |
| 132 | // WriteQueue is refcounted and should be destroyed once all events on the |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 133 | // file task runner have finished executing. |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 134 | class 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 Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 140 | explicit WriteQueue(uint64_t memory_max); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 141 | |
| 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 | |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 158 | // 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. |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 161 | // |
| 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 |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 167 | // buffer, and decremented when SwapQueue() is called and the file task |
| 168 | // runner's local queue is swapped with the shared write queue. |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 169 | // |
| 170 | // |lock_| must be acquired to read or write to this. |
Maks Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 171 | uint64_t memory_; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 172 | |
| 173 | // Indicates the maximum amount of memory that the |queue_| is allowed to |
| 174 | // use. |
Maks Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 175 | const uint64_t memory_max_; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 176 | |
| 177 | // Protects access to |queue_| and |memory_|. |
| 178 | // |
| 179 | // A lock is necessary because |queue_| and |memory_| are shared between the |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 180 | // file task runner and the main thread. NetLog's lock protects OnAddEntry(), |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 181 | // 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 |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 183 | // 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. |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 188 | base::Lock lock_; |
| 189 | |
| 190 | DISALLOW_COPY_AND_ASSIGN(WriteQueue); |
| 191 | }; |
| 192 | |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 193 | // 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. |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 196 | class FileNetLogObserver::FileWriter { |
| 197 | public: |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 198 | // If max_event_file_size == kNoLimit, then no limit is enforced. |
| 199 | FileWriter(const base::FilePath& log_path, |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 200 | const base::FilePath& inprogress_dir_path, |
| 201 | base::Optional<base::File> pre_existing_log_file, |
Maks Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 202 | uint64_t max_event_file_size, |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 203 | size_t total_num_event_files, |
| 204 | scoped_refptr<base::SequencedTaskRunner> task_runner); |
| 205 | |
| 206 | ~FileWriter(); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 207 | |
| 208 | // Writes |constants_value| to disk and opens the events array (closed in |
| 209 | // Stop()). |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 210 | void Initialize(std::unique_ptr<base::Value> constants_value); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 211 | |
wangyix | 8ae679d | 2017-01-28 01:47:40 | [diff] [blame] | 212 | // 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 |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 214 | // is ignored. |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 215 | void Stop(std::unique_ptr<base::Value> polled_data); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 216 | |
| 217 | // Drains |queue_| from WriteQueue into a local file queue and writes the |
| 218 | // events in the queue to disk. |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 219 | void Flush(scoped_refptr<WriteQueue> write_queue); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 220 | |
| 221 | // Deletes all netlog files. It is not valid to call any method of |
| 222 | // FileNetLogObserver after DeleteAllFiles(). |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 223 | void DeleteAllFiles(); |
wangyix | 3c6b5e3 | 2017-01-28 02:54:59 | [diff] [blame] | 224 | |
| 225 | void FlushThenStop(scoped_refptr<WriteQueue> write_queue, |
| 226 | std::unique_ptr<base::Value> polled_data); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 227 | |
| 228 | private: |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 229 | // 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; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 236 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 237 | // 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 Roman | 772bd8d8 | 2017-07-13 21:15:37 | [diff] [blame] | 241 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 242 | // Returns the path to the event file having |index|. This looks like |
Eric Roman | 772bd8d8 | 2017-07-13 21:15:37 | [diff] [blame] | 243 | // "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 Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 254 | // 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 266 | base::File* file); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 267 | |
| 268 | // Writes |polled_data| to a file. |
| 269 | static void WritePolledDataToFile(std::unique_ptr<base::Value> polled_data, |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 270 | base::File* file); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 271 | |
| 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 275 | void RewindIfWroteEventBytes(base::File* file) const; |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 276 | |
| 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 283 | void CreateInprogressDirectory(); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 284 | |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 285 | // 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 288 | base::File final_log_file_; |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 289 | |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 290 | // 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 300 | // 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 Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 305 | uint64_t current_event_file_size_; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 306 | |
Eric Roman | 772bd8d8 | 2017-07-13 21:15:37 | [diff] [blame] | 307 | // Indicates the total number of netlog event files allowed. |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 308 | // (The files GetConstantsFilePath() and GetClosingFilePath() do |
| 309 | // not count against the total.) |
| 310 | const size_t total_num_event_files_; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 311 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 312 | // 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_; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 315 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 316 | // Indicates the maximum size of each individual events file. May be kNoLimit |
| 317 | // to indicate that it can grow arbitrarily large. |
Maks Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 318 | const uint64_t max_event_file_size_; |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 319 | |
| 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_; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 323 | |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 324 | // Task runner for doing file operations. |
| 325 | const scoped_refptr<base::SequencedTaskRunner> task_runner_; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 326 | |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 327 | DISALLOW_COPY_AND_ASSIGN(FileWriter); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 328 | }; |
| 329 | |
wangyix | 179c480 | 2017-02-24 23:29:28 | [diff] [blame] | 330 | std::unique_ptr<FileNetLogObserver> FileNetLogObserver::CreateBounded( |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 331 | const base::FilePath& log_path, |
Maks Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 332 | uint64_t max_total_size, |
wangyix | 179c480 | 2017-02-24 23:29:28 | [diff] [blame] | 333 | std::unique_ptr<base::Value> constants) { |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 334 | return CreateInternal(log_path, SiblingInprogressDirectory(log_path), |
| 335 | base::nullopt, max_total_size, kDefaultNumFiles, |
| 336 | std::move(constants)); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 337 | } |
| 338 | |
wangyix | 179c480 | 2017-02-24 23:29:28 | [diff] [blame] | 339 | std::unique_ptr<FileNetLogObserver> FileNetLogObserver::CreateUnbounded( |
wangyix | 179c480 | 2017-02-24 23:29:28 | [diff] [blame] | 340 | const base::FilePath& log_path, |
| 341 | std::unique_ptr<base::Value> constants) { |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 342 | return CreateInternal(log_path, base::FilePath(), base::nullopt, kNoLimit, |
| 343 | kDefaultNumFiles, std::move(constants)); |
| 344 | } |
| 345 | |
| 346 | std::unique_ptr<FileNetLogObserver> |
| 347 | FileNetLogObserver::CreateBoundedPreExisting( |
| 348 | const base::FilePath& inprogress_dir_path, |
| 349 | base::File output_file, |
Maks Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 350 | uint64_t max_total_size, |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 351 | 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 | |
| 357 | std::unique_ptr<FileNetLogObserver> |
| 358 | FileNetLogObserver::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)); |
wangyix | 179c480 | 2017-02-24 23:29:28 | [diff] [blame] | 364 | } |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 365 | |
| 366 | FileNetLogObserver::~FileNetLogObserver() { |
| 367 | if (net_log()) { |
| 368 | // StopObserving was not called. |
Eric Roman | 68318e7 | 2017-07-19 02:12:15 | [diff] [blame] | 369 | net_log()->RemoveObserver(this); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 370 | file_task_runner_->PostTask( |
| 371 | FROM_HERE, base::Bind(&FileNetLogObserver::FileWriter::DeleteAllFiles, |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 372 | base::Unretained(file_writer_.get()))); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 373 | } |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 374 | file_task_runner_->DeleteSoon(FROM_HERE, file_writer_.release()); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 375 | } |
| 376 | |
wangyix | 179c480 | 2017-02-24 23:29:28 | [diff] [blame] | 377 | void FileNetLogObserver::StartObserving(NetLog* net_log, |
| 378 | NetLogCaptureMode capture_mode) { |
Eric Roman | 68318e7 | 2017-07-19 02:12:15 | [diff] [blame] | 379 | net_log->AddObserver(this, capture_mode); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 380 | } |
| 381 | |
wangyix | 8ae679d | 2017-01-28 01:47:40 | [diff] [blame] | 382 | void FileNetLogObserver::StopObserving(std::unique_ptr<base::Value> polled_data, |
eroman | 81b0a99 | 2017-07-08 01:29:34 | [diff] [blame] | 383 | base::OnceClosure optional_callback) { |
Eric Roman | 68318e7 | 2017-07-19 02:12:15 | [diff] [blame] | 384 | net_log()->RemoveObserver(this); |
pauljensen | 316c57d | 2017-06-09 01:12:53 | [diff] [blame] | 385 | |
eroman | 81b0a99 | 2017-07-08 01:29:34 | [diff] [blame] | 386 | base::OnceClosure bound_flush_then_stop = |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 387 | base::Bind(&FileNetLogObserver::FileWriter::FlushThenStop, |
| 388 | base::Unretained(file_writer_.get()), write_queue_, |
eroman | 81b0a99 | 2017-07-08 01:29:34 | [diff] [blame] | 389 | 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 | } |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 399 | } |
| 400 | |
| 401 | void FileNetLogObserver::OnAddEntry(const NetLogEntry& entry) { |
| 402 | std::unique_ptr<std::string> json(new std::string); |
| 403 | |
Eric Roman | aefc98c | 2018-12-18 21:38:01 | [diff] [blame] | 404 | *json = SerializeNetLogValueToJson(*entry.ToValue()); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 405 | |
| 406 | size_t queue_size = write_queue_->AddEntryToQueue(std::move(json)); |
| 407 | |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 408 | // If events build up in |write_queue_|, trigger the file task runner to drain |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 409 | // 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( |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 414 | FROM_HERE, |
| 415 | base::Bind(&FileNetLogObserver::FileWriter::Flush, |
| 416 | base::Unretained(file_writer_.get()), write_queue_)); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 417 | } |
| 418 | } |
| 419 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 420 | std::unique_ptr<FileNetLogObserver> FileNetLogObserver::CreateBoundedForTests( |
| 421 | const base::FilePath& log_path, |
Maks Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 422 | uint64_t max_total_size, |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 423 | size_t total_num_event_files, |
| 424 | std::unique_ptr<base::Value> constants) { |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 425 | return CreateInternal(log_path, SiblingInprogressDirectory(log_path), |
| 426 | base::nullopt, max_total_size, total_num_event_files, |
| 427 | std::move(constants)); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 428 | } |
| 429 | |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 430 | std::unique_ptr<FileNetLogObserver> FileNetLogObserver::CreateInternal( |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 431 | const base::FilePath& log_path, |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 432 | const base::FilePath& inprogress_dir_path, |
| 433 | base::Optional<base::File> pre_existing_log_file, |
Maks Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 434 | uint64_t max_total_size, |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 435 | 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 Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 442 | const uint64_t max_event_file_size = |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 443 | 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 Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 459 | log_path, inprogress_dir_path, std::move(pre_existing_log_file), |
| 460 | max_event_file_size, total_num_event_files, file_task_runner)); |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 461 | |
| 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 | |
wangyix | 179c480 | 2017-02-24 23:29:28 | [diff] [blame] | 469 | FileNetLogObserver::FileNetLogObserver( |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 470 | scoped_refptr<base::SequencedTaskRunner> file_task_runner, |
wangyix | 179c480 | 2017-02-24 23:29:28 | [diff] [blame] | 471 | 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)), |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 476 | file_writer_(std::move(file_writer)) { |
wangyix | 179c480 | 2017-02-24 23:29:28 | [diff] [blame] | 477 | if (!constants) |
| 478 | constants = GetNetConstants(); |
| 479 | file_task_runner_->PostTask( |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 480 | FROM_HERE, base::Bind(&FileNetLogObserver::FileWriter::Initialize, |
| 481 | base::Unretained(file_writer_.get()), |
| 482 | base::Passed(&constants))); |
wangyix | 179c480 | 2017-02-24 23:29:28 | [diff] [blame] | 483 | } |
| 484 | |
Maks Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 485 | FileNetLogObserver::WriteQueue::WriteQueue(uint64_t memory_max) |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 486 | : memory_(0), memory_max_(memory_max) {} |
| 487 | |
| 488 | size_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 | } |
wangyix | 8ae679d | 2017-01-28 01:47:40 | [diff] [blame] | 504 | |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 505 | void 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 Watkins | 6a33f76 | 2017-12-04 03:39:25 | [diff] [blame] | 512 | FileNetLogObserver::WriteQueue::~WriteQueue() = default; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 513 | |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 514 | FileNetLogObserver::FileWriter::FileWriter( |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 515 | const base::FilePath& log_path, |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 516 | const base::FilePath& inprogress_dir_path, |
| 517 | base::Optional<base::File> pre_existing_log_file, |
Maks Orlovich | 25d9ec2 | 2018-04-26 03:28:16 | [diff] [blame] | 518 | uint64_t max_event_file_size, |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 519 | size_t total_num_event_files, |
eroman | fc4bcba | 2017-07-07 23:48:34 | [diff] [blame] | 520 | scoped_refptr<base::SequencedTaskRunner> task_runner) |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 521 | : final_log_path_(log_path), |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 522 | inprogress_dir_path_(inprogress_dir_path), |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 523 | 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 Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 527 | 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 | } |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 536 | |
Chris Watkins | 6a33f76 | 2017-12-04 03:39:25 | [diff] [blame] | 537 | FileNetLogObserver::FileWriter::~FileWriter() = default; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 538 | |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 539 | void FileNetLogObserver::FileWriter::Initialize( |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 540 | std::unique_ptr<base::Value> constants_value) { |
peary2 | aeac7780 | 2017-06-01 04:11:16 | [diff] [blame] | 541 | DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 542 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 543 | // Open the final log file, and keep it open for the duration of logging (even |
| 544 | // in bounded mode). |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 545 | if (!final_log_path_.empty()) |
| 546 | final_log_file_ = OpenFileForWrite(final_log_path_); |
| 547 | else |
| 548 | TruncateFile(&final_log_file_); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 549 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 550 | if (IsBounded()) { |
| 551 | CreateInprogressDirectory(); |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 552 | base::File constants_file = OpenFileForWrite(GetConstantsFilePath()); |
| 553 | WriteConstantsToFile(std::move(constants_value), &constants_file); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 554 | } else { |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 555 | WriteConstantsToFile(std::move(constants_value), &final_log_file_); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 556 | } |
| 557 | } |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 558 | |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 559 | void FileNetLogObserver::FileWriter::Stop( |
wangyix | 8ae679d | 2017-01-28 01:47:40 | [diff] [blame] | 560 | std::unique_ptr<base::Value> polled_data) { |
peary2 | aeac7780 | 2017-06-01 04:11:16 | [diff] [blame] | 561 | DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 562 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 563 | // Write out the polled data. |
| 564 | if (IsBounded()) { |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 565 | base::File closing_file = OpenFileForWrite(GetClosingFilePath()); |
| 566 | WritePolledDataToFile(std::move(polled_data), &closing_file); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 567 | } else { |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 568 | RewindIfWroteEventBytes(&final_log_file_); |
| 569 | WritePolledDataToFile(std::move(polled_data), &final_log_file_); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 570 | } |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 571 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 572 | // If operating in bounded mode, the events were written to separate files |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 573 | // within |inprogress_dir_path_|. Assemble them into the final destination |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 574 | // file. |
| 575 | if (IsBounded()) |
| 576 | StitchFinalLogFile(); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 577 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 578 | // Ensure the final log file has been flushed. |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 579 | final_log_file_.Close(); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 580 | } |
| 581 | |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 582 | void FileNetLogObserver::FileWriter::Flush( |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 583 | scoped_refptr<FileNetLogObserver::WriteQueue> write_queue) { |
peary2 | aeac7780 | 2017-06-01 04:11:16 | [diff] [blame] | 584 | DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 585 | |
| 586 | EventQueue local_file_queue; |
| 587 | write_queue->SwapQueue(&local_file_queue); |
| 588 | |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 589 | while (!local_file_queue.empty()) { |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 590 | base::File* output_file; |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 591 | |
| 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 599 | output_file = ¤t_event_file_; |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 600 | } else { |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 601 | output_file = &final_log_file_; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 602 | } |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 603 | |
| 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 | |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 613 | local_file_queue.pop(); |
| 614 | } |
| 615 | } |
| 616 | |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 617 | void FileNetLogObserver::FileWriter::DeleteAllFiles() { |
peary2 | aeac7780 | 2017-06-01 04:11:16 | [diff] [blame] | 618 | DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 619 | |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 620 | final_log_file_.Close(); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 621 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 622 | if (IsBounded()) { |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 623 | current_event_file_.Close(); |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 624 | base::DeleteFile(inprogress_dir_path_, true); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 625 | } |
| 626 | |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 627 | // 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); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 631 | } |
| 632 | |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 633 | void 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 | |
| 640 | bool FileNetLogObserver::FileWriter::IsUnbounded() const { |
| 641 | return max_event_file_size_ == kNoLimit; |
| 642 | } |
| 643 | |
| 644 | bool FileNetLogObserver::FileWriter::IsBounded() const { |
| 645 | return !IsUnbounded(); |
| 646 | } |
| 647 | |
| 648 | void 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 Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 658 | base::FilePath FileNetLogObserver::FileWriter::GetEventFilePath( |
| 659 | size_t index) const { |
| 660 | DCHECK_LT(index, total_num_event_files_); |
| 661 | DCHECK(IsBounded()); |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 662 | return inprogress_dir_path_.AppendASCII( |
Brett Wilson | 5accd24 | 2017-11-30 22:07:32 | [diff] [blame] | 663 | "event_file_" + base::NumberToString(index) + ".json"); |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 664 | } |
| 665 | |
| 666 | base::FilePath FileNetLogObserver::FileWriter::GetConstantsFilePath() const { |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 667 | return inprogress_dir_path_.AppendASCII("constants.json"); |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 668 | } |
| 669 | |
| 670 | base::FilePath FileNetLogObserver::FileWriter::GetClosingFilePath() const { |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 671 | return inprogress_dir_path_.AppendASCII("end_netlog.json"); |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 672 | } |
| 673 | |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 674 | size_t FileNetLogObserver::FileWriter::FileNumberToIndex( |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 675 | 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_; |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 679 | } |
| 680 | |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 681 | void FileNetLogObserver::FileWriter::WriteConstantsToFile( |
| 682 | std::unique_ptr<base::Value> constants_value, |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 683 | base::File* file) { |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 684 | // Print constants to file and open events array. |
Eric Roman | aefc98c | 2018-12-18 21:38:01 | [diff] [blame] | 685 | std::string json = SerializeNetLogValueToJson(*constants_value); |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 686 | WriteToFile(file, "{\"constants\":", json, ",\n\"events\": [\n"); |
| 687 | } |
| 688 | |
| 689 | void FileNetLogObserver::FileWriter::WritePolledDataToFile( |
| 690 | std::unique_ptr<base::Value> polled_data, |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 691 | base::File* file) { |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 692 | // 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 707 | void FileNetLogObserver::FileWriter::RewindIfWroteEventBytes( |
| 708 | base::File* file) const { |
| 709 | if (file->IsValid() && wrote_event_bytes_) { |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 710 | // 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 713 | file->Seek(base::File::FROM_END, -2); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 714 | } |
| 715 | } |
| 716 | |
Eric Roman | 651b273 | 2017-07-18 23:09:02 | [diff] [blame] | 717 | void FileNetLogObserver::FileWriter::StitchFinalLogFile() { |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 718 | // Make sure all the events files are flushed (as will read them next). |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 719 | current_event_file_.Close(); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 720 | |
Eric Roman | 85c39b6 | 2017-07-18 20:34:54 | [diff] [blame] | 721 | // 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 Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 726 | if (final_log_file_.IsValid()) { |
| 727 | // Truncate the final log file. |
| 728 | TruncateFile(&final_log_file_); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 729 | |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 730 | // Append the constants file. |
| 731 | AppendToFileThenDelete(GetConstantsFilePath(), &final_log_file_, |
| 732 | read_buffer.get(), kReadBufferSize); |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 733 | |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 734 | // 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 Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 755 | } |
| 756 | |
Eric Roman | 345b03df | 2017-07-18 20:08:37 | [diff] [blame] | 757 | // Delete the inprogress directory (and anything that may still be left inside |
| 758 | // it). |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 759 | base::DeleteFile(inprogress_dir_path_, true); |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 760 | } |
| 761 | |
Maks Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 762 | void FileNetLogObserver::FileWriter::CreateInprogressDirectory() { |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 763 | DCHECK(IsBounded()); |
| 764 | |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 765 | // 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 769 | if (!final_log_file_.IsValid()) |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 770 | return; |
| 771 | |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 772 | if (!base::CreateDirectory(inprogress_dir_path_)) { |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 773 | LOG(WARNING) << "Failed creating directory: " |
Maks Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 774 | << inprogress_dir_path_.value(); |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 775 | 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 Orlovich | 3cb88366 | 2018-04-19 01:53:17 | [diff] [blame] | 780 | std::string in_progress_path = inprogress_dir_path_.AsUTF8Unsafe(); |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 781 | |
| 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 Orlovich | 4c4757b | 2018-04-12 14:15:15 | [diff] [blame] | 788 | &final_log_file_, "Logging is in progress writing data to:\n ", |
Eric Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 789 | 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 Roman | 668c192 | 2017-07-19 22:11:16 | [diff] [blame] | 799 | } |
| 800 | |
Eric Roman | aefc98c | 2018-12-18 21:38:01 | [diff] [blame] | 801 | std::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 | |
wangyix | f3a337e | 2016-12-20 23:58:02 | [diff] [blame] | 817 | } // namespace net |