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

Side by Side Diff: components/startup_metric_utils/browser/startup_metric_utils.cc

Issue 1637493002: Add SameVersionStartupCounts suffix to startup HardFault and Temperature histograms. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@b1_startup_count_metric
Patch Set: Created 4 years, 10 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 2013 The Chromium Authors. All rights reserved. 1 // Copyright 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/startup_metric_utils/browser/startup_metric_utils.h" 5 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include "base/containers/hash_tables.h" 9 #include "base/containers/hash_tables.h"
10 #include "base/environment.h" 10 #include "base/environment.h"
11 #include "base/lazy_instance.h" 11 #include "base/lazy_instance.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
13 #include "base/metrics/histogram_macros.h" 14 #include "base/metrics/histogram_macros.h"
14 #include "base/prefs/pref_registry_simple.h" 15 #include "base/prefs/pref_registry_simple.h"
15 #include "base/prefs/pref_service.h" 16 #include "base/prefs/pref_service.h"
16 #include "base/process/process_info.h" 17 #include "base/process/process_info.h"
17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/string_number_conversions.h"
18 #include "base/sys_info.h" 19 #include "base/sys_info.h"
19 #include "base/threading/platform_thread.h" 20 #include "base/threading/platform_thread.h"
20 #include "base/trace_event/trace_event.h" 21 #include "base/trace_event/trace_event.h"
21 #include "build/build_config.h" 22 #include "build/build_config.h"
22 #include "components/startup_metric_utils/browser/pref_names.h" 23 #include "components/startup_metric_utils/browser/pref_names.h"
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 type(basename ".ColdStartup", kValue); \ 131 type(basename ".ColdStartup", kValue); \
131 break; \ 132 break; \
132 case WARM_STARTUP_TEMPERATURE: \ 133 case WARM_STARTUP_TEMPERATURE: \
133 type(basename ".WarmStartup", kValue); \ 134 type(basename ".WarmStartup", kValue); \
134 break; \ 135 break; \
135 case LUKEWARM_STARTUP_TEMPERATURE: \ 136 case LUKEWARM_STARTUP_TEMPERATURE: \
136 type(basename ".LukewarmStartup", kValue); \ 137 type(basename ".LukewarmStartup", kValue); \
137 break; \ 138 break; \
138 case UNDETERMINED_STARTUP_TEMPERATURE: \ 139 case UNDETERMINED_STARTUP_TEMPERATURE: \
139 break; \ 140 break; \
140 case STARTUP_TEMPERATURE_COUNT: \
141 NOTREACHED(); \
142 break; \
143 } \ 141 } \
144 } 142 }
145 143
146 #define UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( \ 144 #define UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( \
147 type, basename, begin_ticks, end_ticks) \ 145 type, basename, begin_ticks, end_ticks) \
148 { \ 146 { \
149 UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE(type, basename, \ 147 UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE(type, basename, \
150 end_ticks - begin_ticks) \ 148 end_ticks - begin_ticks) \
151 TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1( \ 149 TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1( \
152 "startup", basename, 0, begin_ticks.ToInternalValue(), "Temperature", \ 150 "startup", basename, 0, begin_ticks.ToInternalValue(), "Temperature", \
(...skipping 21 matching lines...) Expand all
174 GetSystemUptimeOnProcessLaunch(); 172 GetSystemUptimeOnProcessLaunch();
175 if (system_uptime_on_process_launch.is_zero()) 173 if (system_uptime_on_process_launch.is_zero())
176 return; 174 return;
177 175
178 UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE(UMA_HISTOGRAM_LONG_TIMES_100, 176 UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE(UMA_HISTOGRAM_LONG_TIMES_100,
179 "Startup.SystemUptime", 177 "Startup.SystemUptime",
180 GetSystemUptimeOnProcessLaunch()); 178 GetSystemUptimeOnProcessLaunch());
181 } 179 }
182 180
183 // On Windows, records the number of hard-faults that have occurred in the 181 // On Windows, records the number of hard-faults that have occurred in the
184 // current chrome.exe process since it was started. This is a nop on other 182 // current chrome.exe process since it was started. A version of the histograms
185 // platforms. 183 // recorded in this method suffixed by |same_version_startup_count| will also be
186 void RecordHardFaultHistogram(bool is_first_run) { 184 // recorded. This is a nop on other platforms.
185 void RecordHardFaultHistogram(int same_version_startup_count) {
187 #if defined(OS_WIN) 186 #if defined(OS_WIN)
188 uint32_t hard_fault_count = 0; 187 uint32_t hard_fault_count = 0;
189 188
190 // Don't log a histogram value if unable to get the hard fault count. 189 // Don't record histograms if unable to get the hard fault count.
191 if (!GetHardFaultCountForCurrentProcess(&hard_fault_count)) 190 if (!GetHardFaultCountForCurrentProcess(&hard_fault_count))
192 return; 191 return;
193 192
193 // Histograms below will be suffixed by |same_version_startup_count| up to
194 // |kMaxSameVersionCountRecorded|, higher counts will be grouped in the
195 // ".Over" suffix. Make sure to reflect changes to
196 // kMaxSameVersionCountRecorded in the "SameVersionStartupCounts" histogram
197 // suffix.
198 const int kMaxSameVersionCountRecorded = 9;
199 std::string same_version_startup_count_suffix = ".";
200 DCHECK_GE(same_version_startup_count, 1);
201 if (same_version_startup_count <= kMaxSameVersionCountRecorded) {
202 same_version_startup_count_suffix.append(
203 base::IntToString(same_version_startup_count));
204 } else {
205 same_version_startup_count_suffix.append("Over");
206 }
207
194 // Hard fault counts are expected to be in the thousands range, 208 // Hard fault counts are expected to be in the thousands range,
195 // corresponding to faulting in ~10s of MBs of code ~10s of KBs at a time. 209 // corresponding to faulting in ~10s of MBs of code ~10s of KBs at a time.
196 // (Observed to vary from 1000 to 10000 on various test machines and 210 // (Observed to vary from 1000 to 10000 on various test machines and
197 // platforms.) 211 // platforms.)
198 if (is_first_run) { 212 const char kHardFaulCountHistogram[] =
fdoray 2016/01/25 18:48:00 kHardFaultCount ^
gab 2016/01/25 21:32:26 Done.
199 UMA_HISTOGRAM_CUSTOM_COUNTS( 213 "Startup.BrowserMessageLoopStartHardFaultCount";
200 "Startup.BrowserMessageLoopStartHardFaultCount.FirstRun", 214 UMA_HISTOGRAM_CUSTOM_COUNTS(kHardFaulCountHistogram, hard_fault_count, 1,
201 hard_fault_count, 215 40000, 50);
202 0, 40000, 50); 216 // Also record the hard fault count histogram suffixed by the number of
203 } else { 217 // startups this specific version has been through.
204 UMA_HISTOGRAM_CUSTOM_COUNTS( 218 // Factory properties copied from UMA_HISTOGRAM_CUSTOM_COUNTS macro.
205 "Startup.BrowserMessageLoopStartHardFaultCount", 219 base::Histogram::FactoryGet(
206 hard_fault_count, 220 kHardFaulCountHistogram + same_version_startup_count_suffix, 1, 40000, 50,
207 0, 40000, 50); 221 base::HistogramBase::kUmaTargetedHistogramFlag)
208 } 222 ->Add(hard_fault_count);
fdoray 2016/01/25 18:48:00 Why can't you use the UMA_HISTOGRAM_CUSTOM_COUNTS
gab 2016/01/25 21:32:26 Because the name is dynamic and the macros end up
209 223
210 // Determine the startup type based on the number of observed hard faults. 224 // Determine the startup type based on the number of observed hard faults.
211 DCHECK_EQ(UNDETERMINED_STARTUP_TEMPERATURE, g_startup_temperature); 225 DCHECK_EQ(UNDETERMINED_STARTUP_TEMPERATURE, g_startup_temperature);
212 if (hard_fault_count < WARM_START_HARD_FAULT_COUNT_THRESHOLD) { 226 if (hard_fault_count < WARM_START_HARD_FAULT_COUNT_THRESHOLD) {
213 g_startup_temperature = WARM_STARTUP_TEMPERATURE; 227 g_startup_temperature = WARM_STARTUP_TEMPERATURE;
214 } else if (hard_fault_count >= COLD_START_HARD_FAULT_COUNT_THRESHOLD) { 228 } else if (hard_fault_count >= COLD_START_HARD_FAULT_COUNT_THRESHOLD) {
215 g_startup_temperature = COLD_STARTUP_TEMPERATURE; 229 g_startup_temperature = COLD_STARTUP_TEMPERATURE;
216 } else { 230 } else {
217 g_startup_temperature = LUKEWARM_STARTUP_TEMPERATURE; 231 g_startup_temperature = LUKEWARM_STARTUP_TEMPERATURE;
218 } 232 }
219 233
220 // Record the startup 'temperature'. 234 // Record the startup 'temperature'.
221 UMA_HISTOGRAM_ENUMERATION( 235 const char kStartupTemperatureHistogram[] = "Startup.Temperature";
222 "Startup.Temperature", g_startup_temperature, STARTUP_TEMPERATURE_COUNT); 236 UMA_HISTOGRAM_ENUMERATION(kStartupTemperatureHistogram, g_startup_temperature,
237 STARTUP_TEMPERATURE_MAX);
238 // As well as its suffixed twin.
239 // Factory properties copied from UMA_HISTOGRAM_ENUMERATION macro.
240 base::LinearHistogram::FactoryGet(
241 kStartupTemperatureHistogram + same_version_startup_count_suffix, 1,
242 STARTUP_TEMPERATURE_MAX, STARTUP_TEMPERATURE_MAX + 1,
243 base::HistogramBase::kUmaTargetedHistogramFlag)
244 ->Add(g_startup_temperature);
fdoray 2016/01/25 18:48:00 Why can't you use the UMA_HISTOGRAM_ENUMERATION ma
gab 2016/01/25 21:32:26 Same.
223 #endif // defined(OS_WIN) 245 #endif // defined(OS_WIN)
224 } 246 }
225 247
226 // Converts a base::Time value to a base::TimeTicks value. The conversion isn't 248 // Converts a base::Time value to a base::TimeTicks value. The conversion isn't
227 // exact, but by capturing Time::Now() as early as possible, the likelihood of a 249 // exact, but by capturing Time::Now() as early as possible, the likelihood of a
228 // clock change between it and process start is as low as possible. There is 250 // clock change between it and process start is as low as possible. There is
229 // also the time taken to synchronously resolve base::Time::Now() and 251 // also the time taken to synchronously resolve base::Time::Now() and
230 // base::TimeTicks::Now() at play, but in practice it is pretty much instant 252 // base::TimeTicks::Now() at play, but in practice it is pretty much instant
231 // compared to multi-seconds startup timings. 253 // compared to multi-seconds startup timings.
232 base::TimeTicks StartupTimeToTimeTicks(const base::Time& time) { 254 base::TimeTicks StartupTimeToTimeTicks(const base::Time& time) {
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
336 g_browser_main_entry_point_ticks.Get().ToInternalValue()); 358 g_browser_main_entry_point_ticks.Get().ToInternalValue());
337 359
338 if (!g_process_creation_ticks.Get().is_null()) 360 if (!g_process_creation_ticks.Get().is_null())
339 { 361 {
340 TRACE_EVENT_INSTANT_WITH_TIMESTAMP0( 362 TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
341 "startup", "Startup.BrowserProcessCreation", 0, 363 "startup", "Startup.BrowserProcessCreation", 0,
342 g_process_creation_ticks.Get().ToInternalValue()); 364 g_process_creation_ticks.Get().ToInternalValue());
343 } 365 }
344 } 366 }
345 367
368 // Logs the Startup.TimeSinceLastStartup histogram. Obtains the timestamp of the
369 // last startup from |pref_service| and overwrites it with the timestamp of the
370 // current startup. If the startup temperature has been set by
371 // RecordBrowserMainMessageLoopStart, the time since last startup is also logged
372 // to an histogram suffixed with the startup temperature.
373 void RecordTimeSinceLastStartup(PrefService* pref_service) {
374 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
375 DCHECK(pref_service);
376
377 // Get the timestamp of the current startup.
378 const base::Time process_start_time =
379 base::CurrentProcessInfo::CreationTime();
380
381 // Get the timestamp of the last startup from |pref_service|.
382 const int64_t last_startup_timestamp_internal =
383 pref_service->GetInt64(prefs::kLastStartupTimestamp);
384 if (last_startup_timestamp_internal != 0) {
385 // Log the Startup.TimeSinceLastStartup histogram.
386 const base::Time last_startup_timestamp =
387 base::Time::FromInternalValue(last_startup_timestamp_internal);
388 const base::TimeDelta time_since_last_startup =
389 process_start_time - last_startup_timestamp;
390 const int minutes_since_last_startup = time_since_last_startup.InMinutes();
391
392 // Ignore negative values, which can be caused by system clock changes.
393 if (minutes_since_last_startup >= 0) {
394 UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE(
395 UMA_HISTOGRAM_TIME_IN_MINUTES_MONTH_RANGE,
396 "Startup.TimeSinceLastStartup", minutes_since_last_startup);
397 }
398 }
399
400 // Write the timestamp of the current startup in |pref_service|.
401 pref_service->SetInt64(prefs::kLastStartupTimestamp,
402 process_start_time.ToInternalValue());
403 #endif // defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
404 }
405
406 // Logs the Startup.SameVersionStartupCount histogram. Relies on |pref_service|
407 // to know information about the previous startups and store information for
408 // future ones. Returns the number of startups with the same version count that
409 // was logged.
410 int RecordSameVersionStartupCount(PrefService* pref_service) {
411 DCHECK(pref_service);
412
413 const std::string current_version = version_info::GetVersionNumber();
414
415 int startups_with_current_version = 0;
416 if (current_version == pref_service->GetString(prefs::kLastStartupVersion)) {
417 startups_with_current_version =
418 pref_service->GetInteger(prefs::kSameVersionStartupCount);
419 ++startups_with_current_version;
420 pref_service->SetInteger(prefs::kSameVersionStartupCount,
421 startups_with_current_version);
422 } else {
423 startups_with_current_version = 1;
424 pref_service->SetString(prefs::kLastStartupVersion, current_version);
425 pref_service->SetInteger(prefs::kSameVersionStartupCount, 1);
426 }
427
428 UMA_HISTOGRAM_COUNTS_100("Startup.SameVersionStartupCount",
429 startups_with_current_version);
430 return startups_with_current_version;
431 }
432
346 } // namespace 433 } // namespace
347 434
348 #if defined(OS_WIN) 435 #if defined(OS_WIN)
349 bool GetHardFaultCountForCurrentProcess(uint32_t* hard_fault_count) { 436 bool GetHardFaultCountForCurrentProcess(uint32_t* hard_fault_count) {
350 DCHECK(hard_fault_count); 437 DCHECK(hard_fault_count);
351 438
352 if (base::win::GetVersion() < base::win::VERSION_WIN7) 439 if (base::win::GetVersion() < base::win::VERSION_WIN7)
353 return false; 440 return false;
354 441
355 // Get the function pointer. 442 // Get the function pointer.
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
437 } 524 }
438 525
439 void RecordExeMainEntryPointTime(const base::Time& time) { 526 void RecordExeMainEntryPointTime(const base::Time& time) {
440 const std::string exe_load_ticks = 527 const std::string exe_load_ticks =
441 base::Int64ToString(StartupTimeToTimeTicks(time).ToInternalValue()); 528 base::Int64ToString(StartupTimeToTimeTicks(time).ToInternalValue());
442 scoped_ptr<base::Environment> env(base::Environment::Create()); 529 scoped_ptr<base::Environment> env(base::Environment::Create());
443 env->SetVar(kChromeMainTicksEnvVar, exe_load_ticks); 530 env->SetVar(kChromeMainTicksEnvVar, exe_load_ticks);
444 } 531 }
445 532
446 void RecordBrowserMainMessageLoopStart(const base::TimeTicks& ticks, 533 void RecordBrowserMainMessageLoopStart(const base::TimeTicks& ticks,
447 bool is_first_run) { 534 bool is_first_run,
535 PrefService* pref_service) {
448 AddStartupEventsForTelemetry(); 536 AddStartupEventsForTelemetry();
449 RecordHardFaultHistogram(is_first_run); 537 RecordTimeSinceLastStartup(pref_service);
fdoray 2016/01/25 18:48:00 RecordTimeSinceLastStartup needs to be called afte
gab 2016/01/25 21:32:26 Good catch :-) -- next on my list is to make that
538 int same_version_startup_count = RecordSameVersionStartupCount(pref_service);
539 RecordHardFaultHistogram(same_version_startup_count);
450 RecordSystemUptimeHistogram(); 540 RecordSystemUptimeHistogram();
451 RecordMainEntryTimeHistogram(); 541 RecordMainEntryTimeHistogram();
452 542
453 const base::TimeTicks& process_creation_ticks = 543 const base::TimeTicks& process_creation_ticks =
454 g_process_creation_ticks.Get(); 544 g_process_creation_ticks.Get();
455 if (!is_first_run && !process_creation_ticks.is_null()) { 545 if (!is_first_run && !process_creation_ticks.is_null()) {
456 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( 546 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE(
457 UMA_HISTOGRAM_LONG_TIMES_100, "Startup.BrowserMessageLoopStartTime", 547 UMA_HISTOGRAM_LONG_TIMES_100, "Startup.BrowserMessageLoopStartTime",
458 process_creation_ticks, ticks); 548 process_creation_ticks, ticks);
459 } 549 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 588
499 // Process create to chrome.dll:main(). Reported as a histogram only as 589 // Process create to chrome.dll:main(). Reported as a histogram only as
500 // the other two events above are sufficient for tracing purposes. 590 // the other two events above are sufficient for tracing purposes.
501 UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE( 591 UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE(
502 UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ProcessCreateToDllMain", 592 UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ProcessCreateToDllMain",
503 g_browser_main_entry_point_ticks.Get() - process_creation_ticks); 593 g_browser_main_entry_point_ticks.Get() - process_creation_ticks);
504 } 594 }
505 } 595 }
506 } 596 }
507 597
508 void RecordTimeSinceLastStartup(PrefService* pref_service) {
509 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
510 DCHECK(pref_service);
511
512 // Get the timestamp of the current startup.
513 const base::Time process_start_time =
514 base::CurrentProcessInfo::CreationTime();
515
516 // Get the timestamp of the last startup from |pref_service|.
517 const int64_t last_startup_timestamp_internal =
518 pref_service->GetInt64(prefs::kLastStartupTimestamp);
519 if (last_startup_timestamp_internal != 0) {
520 // Log the Startup.TimeSinceLastStartup histogram.
521 const base::Time last_startup_timestamp =
522 base::Time::FromInternalValue(last_startup_timestamp_internal);
523 const base::TimeDelta time_since_last_startup =
524 process_start_time - last_startup_timestamp;
525 const int minutes_since_last_startup = time_since_last_startup.InMinutes();
526
527 // Ignore negative values, which can be caused by system clock changes.
528 if (minutes_since_last_startup >= 0) {
529 UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE(
530 UMA_HISTOGRAM_TIME_IN_MINUTES_MONTH_RANGE,
531 "Startup.TimeSinceLastStartup", minutes_since_last_startup);
532 }
533 }
534
535 // Write the timestamp of the current startup in |pref_service|.
536 pref_service->SetInt64(prefs::kLastStartupTimestamp,
537 process_start_time.ToInternalValue());
538 #endif // defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
539 }
540
541 void RecordStartupCount(PrefService* pref_service) {
542 DCHECK(pref_service);
543
544 const std::string current_version = version_info::GetVersionNumber();
545
546 int startups_with_current_version = 0;
547 if (current_version == pref_service->GetString(prefs::kLastStartupVersion)) {
548 startups_with_current_version =
549 pref_service->GetInteger(prefs::kSameVersionStartupCount);
550 ++startups_with_current_version;
551 pref_service->SetInteger(prefs::kSameVersionStartupCount,
552 startups_with_current_version);
553 } else {
554 startups_with_current_version = 1;
555 pref_service->SetString(prefs::kLastStartupVersion, current_version);
556 pref_service->SetInteger(prefs::kSameVersionStartupCount, 1);
557 }
558
559 UMA_HISTOGRAM_COUNTS_100("Startup.SameVersionStartupCount",
560 startups_with_current_version);
561 }
562
563 void RecordBrowserWindowDisplay(const base::TimeTicks& ticks) { 598 void RecordBrowserWindowDisplay(const base::TimeTicks& ticks) {
564 static bool is_first_call = true; 599 static bool is_first_call = true;
565 if (!is_first_call || ticks.is_null()) 600 if (!is_first_call || ticks.is_null())
566 return; 601 return;
567 is_first_call = false; 602 is_first_call = false;
568 if (WasNonBrowserUIDisplayed() || g_process_creation_ticks.Get().is_null()) 603 if (WasNonBrowserUIDisplayed() || g_process_creation_ticks.Get().is_null())
569 return; 604 return;
570 605
571 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( 606 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE(
572 UMA_HISTOGRAM_LONG_TIMES, "Startup.BrowserWindowDisplay", 607 UMA_HISTOGRAM_LONG_TIMES, "Startup.BrowserWindowDisplay",
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
652 687
653 base::TimeTicks MainEntryPointTicks() { 688 base::TimeTicks MainEntryPointTicks() {
654 return g_browser_main_entry_point_ticks.Get(); 689 return g_browser_main_entry_point_ticks.Get();
655 } 690 }
656 691
657 StartupTemperature GetStartupTemperature() { 692 StartupTemperature GetStartupTemperature() {
658 return g_startup_temperature; 693 return g_startup_temperature;
659 } 694 }
660 695
661 } // namespace startup_metric_utils 696 } // namespace startup_metric_utils
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698