OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "chrome/browser/chromeos/boot_times_loader.h" | 5 #include "chrome/browser/chromeos/boot_times_loader.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
11 #include "base/file_util.h" | 11 #include "base/file_util.h" |
12 #include "base/files/file_path.h" | 12 #include "base/files/file_path.h" |
13 #include "base/json/json_reader.h" | |
14 #include "base/json/json_writer.h" | |
13 #include "base/lazy_instance.h" | 15 #include "base/lazy_instance.h" |
14 #include "base/location.h" | 16 #include "base/location.h" |
15 #include "base/message_loop/message_loop.h" | 17 #include "base/message_loop/message_loop.h" |
16 #include "base/message_loop/message_loop_proxy.h" | 18 #include "base/message_loop/message_loop_proxy.h" |
17 #include "base/metrics/histogram.h" | 19 #include "base/metrics/histogram.h" |
20 #include "base/prefs/pref_service.h" | |
18 #include "base/strings/string_number_conversions.h" | 21 #include "base/strings/string_number_conversions.h" |
19 #include "base/strings/string_util.h" | 22 #include "base/strings/string_util.h" |
20 #include "base/strings/stringprintf.h" | 23 #include "base/strings/stringprintf.h" |
21 #include "base/threading/thread.h" | 24 #include "base/threading/thread.h" |
22 #include "base/threading/thread_restrictions.h" | 25 #include "base/threading/thread_restrictions.h" |
23 #include "base/time/time.h" | 26 #include "base/time/time.h" |
24 #include "chrome/browser/browser_process.h" | 27 #include "chrome/browser/browser_process.h" |
25 #include "chrome/browser/chrome_notification_types.h" | 28 #include "chrome/browser/chrome_notification_types.h" |
26 #include "chrome/browser/chromeos/login/auth/authentication_notification_details .h" | 29 #include "chrome/browser/chromeos/login/auth/authentication_notification_details .h" |
27 #include "chrome/browser/chromeos/login/users/user_manager.h" | 30 #include "chrome/browser/chromeos/login/users/user_manager.h" |
28 #include "chrome/browser/ui/browser.h" | 31 #include "chrome/browser/ui/browser.h" |
29 #include "chrome/browser/ui/browser_iterator.h" | 32 #include "chrome/browser/ui/browser_iterator.h" |
30 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 33 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
31 #include "chrome/common/chrome_switches.h" | 34 #include "chrome/common/chrome_switches.h" |
35 #include "chrome/common/pref_names.h" | |
32 #include "content/public/browser/browser_thread.h" | 36 #include "content/public/browser/browser_thread.h" |
33 #include "content/public/browser/navigation_controller.h" | 37 #include "content/public/browser/navigation_controller.h" |
34 #include "content/public/browser/notification_service.h" | 38 #include "content/public/browser/notification_service.h" |
35 #include "content/public/browser/render_widget_host_view.h" | 39 #include "content/public/browser/render_widget_host_view.h" |
36 #include "content/public/browser/web_contents.h" | 40 #include "content/public/browser/web_contents.h" |
37 | 41 |
38 using content::BrowserThread; | 42 using content::BrowserThread; |
39 using content::NavigationController; | 43 using content::NavigationController; |
40 using content::RenderWidgetHost; | 44 using content::RenderWidgetHost; |
41 using content::RenderWidgetHostView; | 45 using content::RenderWidgetHostView; |
42 using content::WebContents; | 46 using content::WebContents; |
43 | 47 |
44 namespace { | 48 namespace { |
45 | 49 |
50 const char kUptime[] = "uptime"; | |
51 const char kDisk[] = "disk"; | |
52 | |
46 RenderWidgetHost* GetRenderWidgetHost(NavigationController* tab) { | 53 RenderWidgetHost* GetRenderWidgetHost(NavigationController* tab) { |
47 WebContents* web_contents = tab->GetWebContents(); | 54 WebContents* web_contents = tab->GetWebContents(); |
48 if (web_contents) { | 55 if (web_contents) { |
49 RenderWidgetHostView* render_widget_host_view = | 56 RenderWidgetHostView* render_widget_host_view = |
50 web_contents->GetRenderWidgetHostView(); | 57 web_contents->GetRenderWidgetHostView(); |
51 if (render_widget_host_view) | 58 if (render_widget_host_view) |
52 return render_widget_host_view->GetRenderWidgetHost(); | 59 return render_widget_host_view->GetRenderWidgetHost(); |
53 } | 60 } |
54 return NULL; | 61 return NULL; |
55 } | 62 } |
56 | 63 |
57 const std::string GetTabUrl(RenderWidgetHost* rwh) { | 64 const std::string GetTabUrl(RenderWidgetHost* rwh) { |
58 RenderWidgetHostView* rwhv = rwh->GetView(); | 65 RenderWidgetHostView* rwhv = rwh->GetView(); |
59 for (chrome::BrowserIterator it; !it.done(); it.Next()) { | 66 for (chrome::BrowserIterator it; !it.done(); it.Next()) { |
60 Browser* browser = *it; | 67 Browser* browser = *it; |
61 for (int i = 0, tab_count = browser->tab_strip_model()->count(); | 68 for (int i = 0, tab_count = browser->tab_strip_model()->count(); |
62 i < tab_count; | 69 i < tab_count; |
63 ++i) { | 70 ++i) { |
64 WebContents* tab = browser->tab_strip_model()->GetWebContentsAt(i); | 71 WebContents* tab = browser->tab_strip_model()->GetWebContentsAt(i); |
65 if (tab->GetRenderWidgetHostView() == rwhv) { | 72 if (tab->GetRenderWidgetHostView() == rwhv) { |
66 return tab->GetLastCommittedURL().spec(); | 73 return tab->GetLastCommittedURL().spec(); |
67 } | 74 } |
68 } | 75 } |
69 } | 76 } |
70 return std::string(); | 77 return std::string(); |
71 } | 78 } |
72 | 79 |
80 // Appends the given buffer into the file. Returns the number of bytes | |
81 // written, or -1 on error. | |
82 // TODO(satorux): Move this to file_util. | |
83 int AppendFile(const base::FilePath& file_path, const char* data, int size) { | |
84 FILE* file = base::OpenFile(file_path, "a"); | |
85 if (!file) | |
86 return -1; | |
87 | |
88 const int num_bytes_written = fwrite(data, 1, size, file); | |
89 base::CloseFile(file); | |
90 return num_bytes_written; | |
91 } | |
92 | |
73 } // namespace | 93 } // namespace |
74 | 94 |
75 namespace chromeos { | 95 namespace chromeos { |
76 | 96 |
77 #define FPL(value) FILE_PATH_LITERAL(value) | 97 #define FPL(value) FILE_PATH_LITERAL(value) |
78 | 98 |
79 // Dir uptime & disk logs are located in. | 99 // Dir uptime & disk logs are located in. |
80 static const base::FilePath::CharType kLogPath[] = FPL("/tmp"); | 100 static const base::FilePath::CharType kLogPath[] = FPL("/tmp"); |
81 // Dir log{in,out} logs are located in. | 101 // Dir log{in,out} logs are located in. |
82 static const base::FilePath::CharType kLoginLogPath[] = | 102 static const base::FilePath::CharType kLoginLogPath[] = |
(...skipping 22 matching lines...) Expand all Loading... | |
105 | 125 |
106 // Name of file collecting login times. | 126 // Name of file collecting login times. |
107 static const base::FilePath::CharType kLoginTimes[] = FPL("login-times"); | 127 static const base::FilePath::CharType kLoginTimes[] = FPL("login-times"); |
108 | 128 |
109 // Name of file collecting logout times. | 129 // Name of file collecting logout times. |
110 static const char kLogoutTimes[] = "logout-times"; | 130 static const char kLogoutTimes[] = "logout-times"; |
111 | 131 |
112 static base::LazyInstance<BootTimesLoader> g_boot_times_loader = | 132 static base::LazyInstance<BootTimesLoader> g_boot_times_loader = |
113 LAZY_INSTANCE_INITIALIZER; | 133 LAZY_INSTANCE_INITIALIZER; |
114 | 134 |
115 BootTimesLoader::BootTimesLoader() | 135 // static |
116 : backend_(new Backend()), | 136 BootTimesLoader::Stats BootTimesLoader::Stats::GetCurrentStats() { |
117 have_registered_(false), | 137 const base::FilePath kProcUptime(FPL("/proc/uptime")); |
118 login_done_(false), | 138 const base::FilePath kDiskStat(FPL("/sys/block/sda/stat")); |
119 restart_requested_(false) { | 139 Stats stats; |
120 login_time_markers_.reserve(30); | 140 // Callers of this method expect synchronous behavior. |
121 logout_time_markers_.reserve(30); | 141 // It's safe to allow IO here, because only virtual FS are accessed. |
142 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
143 base::ReadFileToString(kProcUptime, &stats.uptime_); | |
144 base::ReadFileToString(kDiskStat, &stats.disk_); | |
145 return stats; | |
122 } | 146 } |
123 | 147 |
124 BootTimesLoader::~BootTimesLoader() {} | 148 std::string BootTimesLoader::Stats::SerializeToString() const { |
149 if (uptime_.empty() || disk_.empty()) | |
150 return std::string(); | |
151 base::DictionaryValue dictionary; | |
152 dictionary.SetString(kUptime, uptime_); | |
153 dictionary.SetString(kDisk, disk_); | |
154 | |
155 std::string result; | |
156 if (!base::JSONWriter::Write(&dictionary, &result)) { | |
157 LOG(WARNING) << "BootTimesLoader::Stats::SerializeToString(): failed."; | |
158 return result; | |
stevenjb
2014/06/02 19:11:38
nit: don't return here ('return result' occurs bel
Alexander Alekseev
2014/06/02 19:18:45
Done.
| |
159 } | |
160 | |
161 return result; | |
162 } | |
125 | 163 |
126 // static | 164 // static |
127 BootTimesLoader* BootTimesLoader::Get() { | 165 BootTimesLoader::Stats BootTimesLoader::Stats::DeserializeFromString( |
128 return g_boot_times_loader.Pointer(); | 166 const std::string& source) { |
167 if (source.empty()) | |
168 return Stats(); | |
169 | |
170 scoped_ptr<base::Value> value(base::JSONReader::Read(source)); | |
171 base::DictionaryValue* dictionary; | |
172 if (!value || !value->GetAsDictionary(&dictionary)) { | |
173 LOG(ERROR) << "BootTimesLoader::Stats::DeserializeFromString(): not a " | |
174 "dictionary: '" << source << "'"; | |
175 return Stats(); | |
176 } | |
177 | |
178 Stats result; | |
179 if (!dictionary->GetString(kUptime, &result.uptime_) || | |
180 !dictionary->GetString(kDisk, &result.disk_)) { | |
181 LOG(ERROR) | |
182 << "BootTimesLoader::Stats::DeserializeFromString(): format error: '" | |
183 << source << "'"; | |
184 return Stats(); | |
stevenjb
2014/06/02 19:11:38
nit: Here also, explicit return isn't necessary.
Alexander Alekseev
2014/06/02 19:18:45
It is dangerous, because if first call succeeds, b
stevenjb
2014/06/02 19:26:33
Good point, looks good.
| |
185 } | |
186 | |
187 return result; | |
129 } | 188 } |
130 | 189 |
131 // Appends the given buffer into the file. Returns the number of bytes | 190 bool BootTimesLoader::Stats::UptimeDouble(double* result) const { |
132 // written, or -1 on error. | 191 std::string uptime = uptime_; |
133 // TODO(satorux): Move this to file_util. | 192 const size_t space_at = uptime.find_first_of(' '); |
134 static int AppendFile(const base::FilePath& file_path, | 193 if (space_at == std::string::npos) |
135 const char* data, | 194 return false; |
136 int size) { | 195 |
137 FILE* file = base::OpenFile(file_path, "a"); | 196 uptime.resize(space_at); |
138 if (!file) { | 197 |
139 return -1; | 198 if (base::StringToDouble(uptime, result)) |
140 } | 199 return true; |
141 const int num_bytes_written = fwrite(data, 1, size, file); | 200 |
142 base::CloseFile(file); | 201 return false; |
143 return num_bytes_written; | |
144 } | 202 } |
145 | 203 |
146 static void RecordStatsDelayed(const base::FilePath::StringType& name, | 204 void BootTimesLoader::Stats::RecordStats(const std::string& name) const { |
147 const std::string& uptime, | 205 BrowserThread::PostBlockingPoolTask( |
148 const std::string& disk) { | 206 FROM_HERE, |
207 base::Bind(&BootTimesLoader::Stats::RecordStatsImpl, | |
208 base::Owned(new Stats(*this)), | |
209 name)); | |
210 } | |
211 | |
212 void BootTimesLoader::Stats::RecordStatsWithCallback( | |
213 const std::string& name, | |
214 const base::Closure& callback) const { | |
215 BrowserThread::PostBlockingPoolTaskAndReply( | |
216 FROM_HERE, | |
217 base::Bind(&BootTimesLoader::Stats::RecordStatsImpl, | |
218 base::Owned(new Stats(*this)), | |
219 name), | |
220 callback); | |
221 } | |
222 | |
223 void BootTimesLoader::Stats::RecordStatsImpl( | |
224 const base::FilePath::StringType& name) const { | |
225 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
226 | |
149 const base::FilePath log_path(kLogPath); | 227 const base::FilePath log_path(kLogPath); |
150 const base::FilePath uptime_output = | 228 const base::FilePath uptime_output = |
151 log_path.Append(base::FilePath(kUptimePrefix + name)); | 229 log_path.Append(base::FilePath(kUptimePrefix + name)); |
152 const base::FilePath disk_output = | 230 const base::FilePath disk_output = |
153 log_path.Append(base::FilePath(kDiskPrefix + name)); | 231 log_path.Append(base::FilePath(kDiskPrefix + name)); |
154 | 232 |
155 // Append numbers to the files. | 233 // Append numbers to the files. |
156 AppendFile(uptime_output, uptime.data(), uptime.size()); | 234 AppendFile(uptime_output, uptime_.data(), uptime_.size()); |
157 AppendFile(disk_output, disk.data(), disk.size()); | 235 AppendFile(disk_output, disk_.data(), disk_.size()); |
236 } | |
237 | |
238 BootTimesLoader::BootTimesLoader() | |
239 : backend_(new Backend()), | |
240 have_registered_(false), | |
241 login_done_(false), | |
242 restart_requested_(false) { | |
243 login_time_markers_.reserve(30); | |
244 logout_time_markers_.reserve(30); | |
245 } | |
246 | |
247 BootTimesLoader::~BootTimesLoader() { | |
158 } | 248 } |
159 | 249 |
160 // static | 250 // static |
251 BootTimesLoader* BootTimesLoader::Get() { | |
252 return g_boot_times_loader.Pointer(); | |
253 } | |
254 | |
255 // static | |
161 void BootTimesLoader::WriteTimes( | 256 void BootTimesLoader::WriteTimes( |
162 const std::string base_name, | 257 const std::string base_name, |
163 const std::string uma_name, | 258 const std::string uma_name, |
164 const std::string uma_prefix, | 259 const std::string uma_prefix, |
165 std::vector<TimeMarker> login_times) { | 260 std::vector<TimeMarker> login_times) { |
166 const int kMinTimeMillis = 1; | 261 const int kMinTimeMillis = 1; |
167 const int kMaxTimeMillis = 30000; | 262 const int kMaxTimeMillis = 30000; |
168 const int kNumBuckets = 100; | 263 const int kNumBuckets = 100; |
169 const base::FilePath log_path(kLoginLogPath); | 264 const base::FilePath log_path(kLoginLogPath); |
170 | 265 |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
258 // has already been terminated. | 353 // has already been terminated. |
259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | 354 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
260 !BrowserThread::IsMessageLoopValid(BrowserThread::UI)); | 355 !BrowserThread::IsMessageLoopValid(BrowserThread::UI)); |
261 | 356 |
262 WriteTimes(kLogoutTimes, | 357 WriteTimes(kLogoutTimes, |
263 (restart_requested_ ? kUmaRestart : kUmaLogout), | 358 (restart_requested_ ? kUmaRestart : kUmaLogout), |
264 kUmaLogoutPrefix, | 359 kUmaLogoutPrefix, |
265 logout_time_markers_); | 360 logout_time_markers_); |
266 } | 361 } |
267 | 362 |
268 void BootTimesLoader::RecordStats(const std::string& name, const Stats& stats) { | 363 // static |
269 BrowserThread::PostTask( | 364 void BootTimesLoader::ClearLogoutStartedLastPreference() { |
270 BrowserThread::FILE, FROM_HERE, | 365 PrefService* local_state = g_browser_process->local_state(); |
271 base::Bind(&RecordStatsDelayed, name, stats.uptime, stats.disk)); | 366 local_state->ClearPref(prefs::kLogoutStartedLast); |
272 } | 367 } |
273 | 368 |
274 BootTimesLoader::Stats BootTimesLoader::GetCurrentStats() { | 369 void BootTimesLoader::OnChromeProcessStart() { |
275 const base::FilePath kProcUptime(FPL("/proc/uptime")); | 370 PrefService* local_state = g_browser_process->local_state(); |
276 const base::FilePath kDiskStat(FPL("/sys/block/sda/stat")); | 371 const std::string logout_started_last_str = |
277 Stats stats; | 372 local_state->GetString(prefs::kLogoutStartedLast); |
278 base::ThreadRestrictions::ScopedAllowIO allow_io; | 373 if (logout_started_last_str.empty()) |
279 base::ReadFileToString(kProcUptime, &stats.uptime); | 374 return; |
280 base::ReadFileToString(kDiskStat, &stats.disk); | 375 |
281 return stats; | 376 // Note that kLogoutStartedLast is not cleared on format error to stay in |
377 // logs in case of other fatal system errors. | |
378 | |
379 const Stats logout_started_last_stats = | |
380 Stats::DeserializeFromString(logout_started_last_str); | |
381 if (logout_started_last_stats.uptime().empty()) | |
382 return; | |
383 | |
384 double logout_started_last; | |
385 double uptime; | |
386 if (!logout_started_last_stats.UptimeDouble(&logout_started_last) || | |
387 !Stats::GetCurrentStats().UptimeDouble(&uptime)) { | |
388 return; | |
389 } | |
390 | |
391 if (logout_started_last >= uptime) { | |
392 // Reboot happened. | |
393 ClearLogoutStartedLastPreference(); | |
394 return; | |
395 } | |
396 | |
397 // Write /tmp/uptime-logout-started as well. | |
398 const char kLogoutStarted[] = "logout-started"; | |
399 logout_started_last_stats.RecordStatsWithCallback( | |
400 kLogoutStarted, | |
401 base::Bind(&BootTimesLoader::ClearLogoutStartedLastPreference)); | |
402 } | |
403 | |
404 void BootTimesLoader::OnLogoutStarted(PrefService* state) { | |
405 const std::string uptime = Stats::GetCurrentStats().SerializeToString(); | |
406 if (!uptime.empty()) | |
407 state->SetString(prefs::kLogoutStartedLast, uptime); | |
282 } | 408 } |
283 | 409 |
284 void BootTimesLoader::RecordCurrentStats(const std::string& name) { | 410 void BootTimesLoader::RecordCurrentStats(const std::string& name) { |
285 RecordStats(name, GetCurrentStats()); | 411 Stats::GetCurrentStats().RecordStats(name); |
286 } | 412 } |
287 | 413 |
288 void BootTimesLoader::SaveChromeMainStats() { | 414 void BootTimesLoader::SaveChromeMainStats() { |
289 chrome_main_stats_ = GetCurrentStats(); | 415 chrome_main_stats_ = Stats::GetCurrentStats(); |
290 } | 416 } |
291 | 417 |
292 void BootTimesLoader::RecordChromeMainStats() { | 418 void BootTimesLoader::RecordChromeMainStats() { |
293 RecordStats(kChromeMain, chrome_main_stats_); | 419 chrome_main_stats_.RecordStats(kChromeMain); |
294 } | 420 } |
295 | 421 |
296 void BootTimesLoader::RecordLoginAttempted() { | 422 void BootTimesLoader::RecordLoginAttempted() { |
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 423 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
298 if (login_done_) | 424 if (login_done_) |
299 return; | 425 return; |
300 | 426 |
301 login_time_markers_.clear(); | 427 login_time_markers_.clear(); |
302 AddLoginTimeMarker("LoginStarted", false); | 428 AddLoginTimeMarker("LoginStarted", false); |
303 if (!have_registered_) { | 429 if (!have_registered_) { |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
398 GetRenderWidgetHost(&web_contents->GetController()); | 524 GetRenderWidgetHost(&web_contents->GetController()); |
399 render_widget_hosts_loading_.erase(render_widget_host); | 525 render_widget_hosts_loading_.erase(render_widget_host); |
400 break; | 526 break; |
401 } | 527 } |
402 default: | 528 default: |
403 break; | 529 break; |
404 } | 530 } |
405 } | 531 } |
406 | 532 |
407 } // namespace chromeos | 533 } // namespace chromeos |
OLD | NEW |