Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(434)

Side by Side Diff: components/net_log/net_log_file_writer.cc

Issue 2603523002: Move net-export thread-hopping code into NetLogFileWriter and add IO polled data. (Closed)
Patch Set: Fixed nits; callbacks are now always executed regardless of failure or no-op Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/net_log/net_log_file_writer.h" 5 #include "components/net_log/net_log_file_writer.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "base/bind.h"
10 #include "base/callback.h"
9 #include "base/files/file_path.h" 11 #include "base/files/file_path.h"
10 #include "base/files/file_util.h" 12 #include "base/files/file_util.h"
11 #include "base/files/scoped_file.h" 13 #include "base/files/scoped_file.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/task_runner_util.h"
12 #include "base/values.h" 16 #include "base/values.h"
13 #include "build/build_config.h" 17 #include "build/build_config.h"
14 #include "components/net_log/chrome_net_log.h" 18 #include "components/net_log/chrome_net_log.h"
15 #include "net/log/write_to_file_net_log_observer.h" 19 #include "net/log/file_net_log_observer.h"
20 #include "net/log/net_log_util.h"
21 #include "net/url_request/url_request_context_getter.h"
16 22
17 namespace net_log { 23 namespace net_log {
18 24
19 // Path of logs if relative to default temporary directory of 25 // Path of logs relative to default temporary directory given by
20 // base::GetTempDir(). Must be kept in sync with 26 // base::GetTempDir(). Must be kept in sync with
21 // chrome/android/java/res/xml/file_paths.xml. Only used if 27 // chrome/android/java/res/xml/file_paths.xml. Only used if not saving log file
22 // not saving log file to a custom path. 28 // to a custom path.
23 base::FilePath::CharType kLogRelativePath[] = 29 base::FilePath::CharType kLogRelativePath[] =
24 FILE_PATH_LITERAL("net-export/chrome-net-export-log.json"); 30 FILE_PATH_LITERAL("net-export/chrome-net-export-log.json");
25 31
26 // Old path used by net-export. Used to delete old files. 32 // Old path used by net-export. Used to delete old files.
27 // TODO(mmenke): Should remove at some point. Added in M46. 33 // TODO(mmenke): Should remove at some point. Added in M46.
28 base::FilePath::CharType kOldLogRelativePath[] = 34 base::FilePath::CharType kOldLogRelativePath[] =
29 FILE_PATH_LITERAL("chrome-net-export-log.json"); 35 FILE_PATH_LITERAL("chrome-net-export-log.json");
30 36
31 NetLogFileWriter::~NetLogFileWriter() { 37 NetLogFileWriter::~NetLogFileWriter() {
32 if (write_to_file_observer_) 38 if (write_to_file_observer_)
33 write_to_file_observer_->StopObserving(nullptr); 39 write_to_file_observer_->StopObserving(nullptr, base::Bind([] {}));
34 }
35
36 void NetLogFileWriter::ProcessCommand(Command command) {
37 DCHECK(thread_checker_.CalledOnValidThread());
38 if (!EnsureInit())
39 return;
40
41 switch (command) {
42 case DO_START_LOG_BYTES:
43 StartNetLog(LOG_TYPE_LOG_BYTES);
44 break;
45 case DO_START:
46 StartNetLog(LOG_TYPE_NORMAL);
47 break;
48 case DO_START_STRIP_PRIVATE_DATA:
49 StartNetLog(LOG_TYPE_STRIP_PRIVATE_DATA);
50 break;
51 case DO_STOP:
52 StopNetLog();
53 break;
54 default:
55 NOTREACHED();
56 break;
57 }
58 }
59
60 bool NetLogFileWriter::GetFilePath(base::FilePath* path) {
61 DCHECK(thread_checker_.CalledOnValidThread());
62 if (log_type_ == LOG_TYPE_NONE || state_ == STATE_LOGGING)
63 return false;
64
65 if (!NetExportLogExists())
66 return false;
67
68 DCHECK(!log_path_.empty());
69 #if defined(OS_POSIX)
70 // Users, group and others can read, write and traverse.
71 int mode = base::FILE_PERMISSION_MASK;
72 base::SetPosixFilePermissions(log_path_, mode);
73 #endif // defined(OS_POSIX)
74
75 *path = log_path_;
76 return true;
77 }
78
79 base::DictionaryValue* NetLogFileWriter::GetState() {
80 DCHECK(thread_checker_.CalledOnValidThread());
81 base::DictionaryValue* dict = new base::DictionaryValue;
82
83 EnsureInit();
84
85 #ifndef NDEBUG
86 dict->SetString("file", log_path_.LossyDisplayName());
87 #endif // NDEBUG
88
89 switch (state_) {
90 case STATE_NOT_LOGGING:
91 dict->SetString("state", "NOT_LOGGING");
92 break;
93 case STATE_LOGGING:
94 dict->SetString("state", "LOGGING");
95 break;
96 case STATE_UNINITIALIZED:
97 dict->SetString("state", "UNINITIALIZED");
98 break;
99 }
100
101 switch (log_type_) {
102 case LOG_TYPE_NONE:
103 dict->SetString("logType", "NONE");
104 break;
105 case LOG_TYPE_UNKNOWN:
106 dict->SetString("logType", "UNKNOWN");
107 break;
108 case LOG_TYPE_LOG_BYTES:
109 dict->SetString("logType", "LOG_BYTES");
110 break;
111 case LOG_TYPE_NORMAL:
112 dict->SetString("logType", "NORMAL");
113 break;
114 case LOG_TYPE_STRIP_PRIVATE_DATA:
115 dict->SetString("logType", "STRIP_PRIVATE_DATA");
116 break;
117 }
118
119 return dict;
120 } 40 }
121 41
122 NetLogFileWriter::NetLogFileWriter( 42 NetLogFileWriter::NetLogFileWriter(
123 ChromeNetLog* chrome_net_log, 43 ChromeNetLog* chrome_net_log,
124 const base::CommandLine::StringType& command_line_string, 44 const base::CommandLine::StringType& command_line_string,
125 const std::string& channel_string) 45 const std::string& channel_string)
126 : state_(STATE_UNINITIALIZED), 46 : state_(STATE_UNINITIALIZED),
127 log_type_(LOG_TYPE_NONE), 47 log_exists_(false),
48 log_capture_mode_known_(false),
49 log_capture_mode_(net::NetLogCaptureMode::Default()),
128 chrome_net_log_(chrome_net_log), 50 chrome_net_log_(chrome_net_log),
129 command_line_string_(command_line_string), 51 command_line_string_(command_line_string),
130 channel_string_(channel_string) { 52 channel_string_(channel_string),
131 // NetLogFileWriter can be created on one thread and used on another. 53 weak_ptr_factory_(this) {}
132 thread_checker_.DetachFromThread(); 54
133 } 55 void NetLogFileWriter::StartNetLog(const base::FilePath& log_path,
134 56 net::NetLogCaptureMode capture_mode,
135 bool NetLogFileWriter::GetNetExportLogBaseDirectory( 57 const StateCallback& state_callback) {
136 base::FilePath* path) const { 58 DCHECK(thread_checker_.CalledOnValidThread());
137 DCHECK(thread_checker_.CalledOnValidThread()); 59 if (state_ == STATE_UNINITIALIZED) {
138 return base::GetTempDir(path); 60 InitAndCallback(base::Bind(&NetLogFileWriter::StartLogging,
eroman 2017/01/11 19:18:38 Side-comment: A common idiom that we use in //net
wangyix1 2017/01/14 02:15:47 Acknowledged.
139 } 61 weak_ptr_factory_.GetWeakPtr(), log_path,
140 62 capture_mode, state_callback));
141 net::NetLogCaptureMode NetLogFileWriter::GetCaptureModeForLogType( 63 } else if (state_ == STATE_NOT_LOGGING) {
eroman 2017/01/11 19:18:38 optional: I suggest using a switch statement rathe
wangyix1 2017/01/14 02:15:46 Acknowledged.
142 LogType log_type) { 64 StartLogging(log_path, capture_mode, state_callback);
143 switch (log_type) { 65 } else {
144 case LOG_TYPE_LOG_BYTES: 66 RunStateCallback(state_callback);
145 return net::NetLogCaptureMode::IncludeSocketBytes(); 67 }
146 case LOG_TYPE_NORMAL: 68 }
147 return net::NetLogCaptureMode::IncludeCookiesAndCredentials(); 69
148 case LOG_TYPE_STRIP_PRIVATE_DATA: 70 void NetLogFileWriter::StartLogging(const base::FilePath& log_path,
149 return net::NetLogCaptureMode::Default(); 71 net::NetLogCaptureMode capture_mode,
150 case LOG_TYPE_NONE: 72 const StateCallback& state_callback) {
151 case LOG_TYPE_UNKNOWN: 73 DCHECK(thread_checker_.CalledOnValidThread());
152 NOTREACHED(); 74 DCHECK(file_task_runner_);
153 } 75
154 return net::NetLogCaptureMode::Default(); 76 // Check if NetLogFileWriter is properly initialized.
155 } 77 if (state_ == STATE_NOT_LOGGING) {
156 78 if (!log_path.empty())
157 bool NetLogFileWriter::EnsureInit() { 79 log_path_ = log_path;
158 DCHECK(thread_checker_.CalledOnValidThread()); 80
159 if (state_ != STATE_UNINITIALIZED) 81 state_ = STATE_LOGGING;
160 return true; 82 log_exists_ = true;
161 83 log_capture_mode_known_ = true;
162 if (log_path_.empty() && !SetUpDefaultNetExportLogPath()) 84 log_capture_mode_ = capture_mode;
163 return false; 85
164 86 DCHECK(!log_path_.empty());
165 state_ = STATE_NOT_LOGGING; 87
166 if (NetExportLogExists()) 88 std::unique_ptr<base::Value> constants(
167 log_type_ = LOG_TYPE_UNKNOWN; 89 ChromeNetLog::GetConstants(command_line_string_, channel_string_));
168 else 90 write_to_file_observer_.reset(
169 log_type_ = LOG_TYPE_NONE; 91 new net::FileNetLogObserver(file_task_runner_));
170 92 write_to_file_observer_->StartObservingUnbounded(
171 return true; 93 chrome_net_log_, capture_mode, log_path_, std::move(constants),
172 } 94 nullptr);
173 95 } else {
174 void NetLogFileWriter::StartNetLog(LogType log_type) { 96 // Only way to get here is if StartLogging() was called as a result of a
175 DCHECK(thread_checker_.CalledOnValidThread()); 97 // StartNetLog() that triggered initialization and the initialization
176 if (state_ == STATE_LOGGING) 98 // failed. In that case, simply run |state_callback| and nothing else.
177 return; 99 DCHECK_EQ(state_, STATE_UNINITIALIZED);
178 100 }
179 DCHECK_NE(STATE_UNINITIALIZED, state_); 101
180 DCHECK(!log_path_.empty()); 102 RunStateCallback(state_callback);
181 103 }
182 // Try to make sure we can create the file. 104
183 // TODO(rtenneti): Find a better for doing the following. Surface some error 105 void NetLogFileWriter::StopNetLog(
184 // to the user if we couldn't create the file. 106 std::unique_ptr<base::DictionaryValue> polled_data,
185 base::ScopedFILE file(base::OpenFile(log_path_, "w")); 107 scoped_refptr<net::URLRequestContextGetter> context_getter,
186 if (!file) 108 const StateCallback& state_callback) {
187 return; 109 DCHECK(thread_checker_.CalledOnValidThread());
188 110 DCHECK(net_task_runner_);
189 log_type_ = log_type; 111 DCHECK(context_getter);
190 state_ = STATE_LOGGING; 112 if (state_ == STATE_LOGGING) {
191 113 // Stopping the log requires first grabbing the net info on the net thread.
192 std::unique_ptr<base::Value> constants( 114 // Before we post that task to the net thread, change state to
eroman 2017/01/11 19:18:38 Can you avoid using "we" pronoun throughout this c
wangyix1 2017/01/14 02:15:46 Done.
193 ChromeNetLog::GetConstants(command_line_string_, channel_string_)); 115 // STATE_STOP_LOGGING so that if the NetLogFileWriter receives a command
194 write_to_file_observer_.reset(new net::WriteToFileNetLogObserver()); 116 // while the net info is being gathered on the net thread, we can check
195 write_to_file_observer_->set_capture_mode(GetCaptureModeForLogType(log_type)); 117 // the state and possibly choose to prevent that command from being
196 write_to_file_observer_->StartObserving(chrome_net_log_, std::move(file), 118 // executed. It's the responsibility of the commands (StartNetLog(),
197 constants.get(), nullptr); 119 // StopNetLog(), GetState()) to check the state before performing their
198 } 120 // actions.
199 121 state_ = STATE_STOPPING_LOG;
200 void NetLogFileWriter::StopNetLog() { 122
201 DCHECK(thread_checker_.CalledOnValidThread()); 123 base::PostTaskAndReplyWithResult(
202 if (state_ != STATE_LOGGING) 124 net_task_runner_.get(), FROM_HERE,
203 return; 125 base::Bind(&NetLogFileWriter::AddNetInfo, context_getter,
204 126 base::Passed(&polled_data)),
205 write_to_file_observer_->StopObserving(nullptr); 127 base::Bind(&NetLogFileWriter::StopLogging,
128 weak_ptr_factory_.GetWeakPtr(), state_callback));
129 } else {
130 RunStateCallback(state_callback);
131 }
132 }
133
134 std::unique_ptr<base::DictionaryValue> NetLogFileWriter::AddNetInfo(
135 scoped_refptr<net::URLRequestContextGetter> context_getter,
136 std::unique_ptr<base::DictionaryValue> polled_data) {
137 DCHECK(context_getter);
138 std::unique_ptr<base::DictionaryValue> net_info = net::GetNetInfo(
139 context_getter->GetURLRequestContext(), net::NET_INFO_ALL_SOURCES);
140 if (polled_data)
141 net_info->MergeDictionary(polled_data.get());
142 return net_info;
143 }
144
145 void NetLogFileWriter::StopLogging(
146 const StateCallback& state_callback,
147 std::unique_ptr<base::DictionaryValue> polled_data) {
148 DCHECK(thread_checker_.CalledOnValidThread());
149 DCHECK_EQ(state_, STATE_STOPPING_LOG);
150
151 write_to_file_observer_->StopObserving(
152 std::move(polled_data),
153 base::Bind(&NetLogFileWriter::ResetObserverThenSetStateNotLogging,
154 weak_ptr_factory_.GetWeakPtr(), state_callback));
155 }
156
157 void NetLogFileWriter::ResetObserverThenSetStateNotLogging(
158 const StateCallback& state_callback) {
159 DCHECK(thread_checker_.CalledOnValidThread());
206 write_to_file_observer_.reset(); 160 write_to_file_observer_.reset();
207 state_ = STATE_NOT_LOGGING; 161 state_ = STATE_NOT_LOGGING;
208 } 162
209 163 RunStateCallback(state_callback);
210 void NetLogFileWriter::SetUpNetExportLogPath( 164 }
211 const base::FilePath& custom_path) { 165
212 DCHECK(thread_checker_.CalledOnValidThread()); 166 void NetLogFileWriter::GetState(const StateCallback& state_callback) {
213 167 DCHECK(thread_checker_.CalledOnValidThread());
214 // The directory should always exist because the custom path 168 if (state_ == STATE_UNINITIALIZED) {
215 // is taken from a file selector dialog window. 169 InitAndCallback(base::Bind(&NetLogFileWriter::RunStateCallback,
216 DCHECK(base::PathExists(custom_path.DirName())); 170 weak_ptr_factory_.GetWeakPtr(), state_callback));
217 171 } else {
218 log_path_ = custom_path; 172 RunStateCallback(state_callback);
219 } 173 }
220 174 }
221 bool NetLogFileWriter::SetUpDefaultNetExportLogPath() { 175
222 DCHECK(thread_checker_.CalledOnValidThread()); 176 std::unique_ptr<base::DictionaryValue> NetLogFileWriter::GetState() const {
177 DCHECK(thread_checker_.CalledOnValidThread());
178 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
179
180 #ifndef NDEBUG
181 dict->SetString("file", log_path_.LossyDisplayName());
182 #endif // NDEBUG
183
184 switch (state_) {
185 case STATE_UNINITIALIZED:
186 dict->SetString("state", "UNINITIALIZED");
187 break;
188 case STATE_INITIALIZING:
189 dict->SetString("state", "INITIALIZING");
190 break;
191 case STATE_NOT_LOGGING:
192 dict->SetString("state", "NOT_LOGGING");
193 break;
194 case STATE_LOGGING:
195 dict->SetString("state", "LOGGING");
196 break;
197 case STATE_STOPPING_LOG:
198 dict->SetString("state", "STOPPING_LOG");
199 break;
200 }
201
202 dict->SetBoolean("logExists", log_exists_);
203 dict->SetBoolean("logCaptureModeKnown", log_capture_mode_known_);
204 dict->SetString("captureMode", CaptureModeToString(log_capture_mode_));
205
206 return dict;
207 }
208
209 void NetLogFileWriter::RunStateCallback(
210 const StateCallback& state_callback) const {
211 DCHECK(thread_checker_.CalledOnValidThread());
212 state_callback.Run(GetState());
213 }
214
215 void NetLogFileWriter::GetFilePath(
216 const FilePathCallback& path_callback) const {
217 DCHECK(thread_checker_.CalledOnValidThread());
218 if (!log_exists_ || state_ == STATE_LOGGING) {
219 path_callback.Run(base::FilePath());
220 return;
221 }
222
223 DCHECK(file_task_runner_);
224 DCHECK(!log_path_.empty());
225
226 base::PostTaskAndReplyWithResult(
227 file_task_runner_.get(), FROM_HERE,
228 base::Bind(&NetLogFileWriter::SetPosixFilePermissionsAll, log_path_),
229 base::Bind(&NetLogFileWriter::RunFilePathCallback, path_callback,
230 log_path_));
231 }
232
233 bool NetLogFileWriter::SetPosixFilePermissionsAll(const base::FilePath& path) {
234 if (!base::PathExists(path))
235 return false;
236 #if defined(OS_POSIX)
237 return base::SetPosixFilePermissions(path, base::FILE_PERMISSION_MASK);
238 #else
239 return true;
240 #endif
241 }
242
243 void NetLogFileWriter::RunFilePathCallback(
244 const FilePathCallback& path_callback,
245 const base::FilePath& path,
246 bool set_file_permissions_success) {
247 if (set_file_permissions_success)
248 path_callback.Run(path);
249 else
250 path_callback.Run(base::FilePath());
251 }
252
253 void NetLogFileWriter::InitAndCallback(const base::Closure& callback) {
254 DCHECK(thread_checker_.CalledOnValidThread());
255 DCHECK(file_task_runner_);
256
257 // Before posting the file thread tasks, change state to STATE_INITIALIZING so
258 // that if the NetLogFileWriter receives a command while initialization tasks
259 // are running on the file thread, we can check the state and possibly choose
260 // to prevent that command from being executed. It's the responsibility of the
261 // commands (StartNetLog(), StopNetLog(), GetState()) to check the state
262 // before performing their actions.
263 DCHECK_EQ(state_, STATE_UNINITIALIZED);
264 state_ = STATE_INITIALIZING;
265
266 base::PostTaskAndReplyWithResult(
267 file_task_runner_.get(), FROM_HERE,
268 base::Bind(&NetLogFileWriter::SetUpDefaultLogPath),
269 base::Bind(&NetLogFileWriter::InitStateAndCallback,
270 weak_ptr_factory_.GetWeakPtr(), callback));
271 }
272
273 NetLogFileWriter::DefaultLogPathResults
274 NetLogFileWriter::SetUpDefaultLogPath() {
275 DefaultLogPathResults results;
276 results.default_log_path_success = false;
eroman 2017/01/11 19:18:38 Can you also initialize log_exists = false ? (as t
wangyix1 2017/01/14 02:15:47 Done.
277
223 base::FilePath temp_dir; 278 base::FilePath temp_dir;
224 if (!GetNetExportLogBaseDirectory(&temp_dir)) 279 if (!base::GetTempDir(&temp_dir))
225 return false; 280 return results;
226 281
227 // Delete log file at old location, if present. 282 // Delete log file at old location, if present.
228 DeleteFile(temp_dir.Append(kOldLogRelativePath), false); 283 base::DeleteFile(temp_dir.Append(kOldLogRelativePath), false);
229 284
230 base::FilePath log_path = temp_dir.Append(kLogRelativePath); 285 results.default_log_path = temp_dir.Append(kLogRelativePath);
231 286 if (!base::CreateDirectoryAndGetError(results.default_log_path.DirName(),
232 if (!base::CreateDirectoryAndGetError(log_path.DirName(), nullptr)) { 287 nullptr))
233 return false; 288 return results;
234 } 289
235 290 results.log_exists = base::PathExists(results.default_log_path);
236 log_path_ = log_path; 291 results.default_log_path_success = true;
237 return true; 292 return results;
238 } 293 }
239 294
240 bool NetLogFileWriter::NetExportLogExists() const { 295 void NetLogFileWriter::InitStateAndCallback(
241 DCHECK(thread_checker_.CalledOnValidThread()); 296 const base::Closure& callback,
242 DCHECK(!log_path_.empty()); 297 const DefaultLogPathResults& results) {
243 return base::PathExists(log_path_); 298 DCHECK(thread_checker_.CalledOnValidThread());
299 DCHECK_EQ(state_, STATE_INITIALIZING);
300
301 if (results.default_log_path_success) {
302 state_ = STATE_NOT_LOGGING;
303 log_path_ = results.default_log_path;
304 log_exists_ = results.log_exists;
305 DCHECK(!log_capture_mode_known_);
306 } else {
307 state_ = STATE_UNINITIALIZED;
308 }
309
310 callback.Run();
311 }
312
313 void NetLogFileWriter::SetTaskRunners(
314 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
315 scoped_refptr<base::SingleThreadTaskRunner> net_task_runner) {
316 DCHECK(thread_checker_.CalledOnValidThread());
317 if (file_task_runner_)
318 DCHECK_EQ(file_task_runner, file_task_runner_);
319 file_task_runner_ = file_task_runner;
320
321 if (net_task_runner_)
322 DCHECK_EQ(net_task_runner, net_task_runner_);
323 net_task_runner_ = net_task_runner;
324 }
325
326 std::string NetLogFileWriter::CaptureModeToString(
327 net::NetLogCaptureMode capture_mode) {
328 if (capture_mode == net::NetLogCaptureMode::Default()) {
329 return "STRIP_PRIVATE_DATA";
330 } else if (capture_mode ==
331 net::NetLogCaptureMode::IncludeCookiesAndCredentials()) {
332 return "NORMAL";
333 } else {
eroman 2017/01/11 19:18:38 Please change the default case. If |capture_mode|
wangyix1 2017/01/14 02:15:46 Done.
334 DCHECK(capture_mode == net::NetLogCaptureMode::IncludeSocketBytes());
335 return "LOG_BYTES";
336 }
337 }
338
339 net::NetLogCaptureMode NetLogFileWriter::CaptureModeFromString(
340 const std::string& capture_mode_string) {
341 if (capture_mode_string == "STRIP_PRIVATE_DATA") {
342 return net::NetLogCaptureMode::Default();
343 } else if (capture_mode_string == "NORMAL") {
344 return net::NetLogCaptureMode::IncludeCookiesAndCredentials();
345 } else {
346 DCHECK_EQ(capture_mode_string, "LOG_BYTES");
eroman 2017/01/11 19:18:38 Same comment as above. The default (unrecognized
wangyix1 2017/01/14 02:15:46 Done.
347 return net::NetLogCaptureMode::IncludeSocketBytes();
348 }
244 } 349 }
245 350
246 } // namespace net_log 351 } // namespace net_log
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698