blob: acafd0cab3bc868738fb033d6d5a0fd9daeec626 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/memory_pressure/memory_pressure_stats_collector.h"
#include "base/metrics/histogram.h"
#include "base/time/tick_clock.h"
namespace memory_pressure {
namespace {
using MemoryPressureLevel = MemoryPressureListener::MemoryPressureLevel;
// Converts a memory pressure level to an UMA enumeration value.
MemoryPressureLevelUMA MemoryPressureLevelToUmaEnumValue(
MemoryPressureLevel level) {
switch (level) {
case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
return UMA_MEMORY_PRESSURE_LEVEL_NONE;
case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
return UMA_MEMORY_PRESSURE_LEVEL_MODERATE;
case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
return UMA_MEMORY_PRESSURE_LEVEL_CRITICAL;
}
NOTREACHED();
return UMA_MEMORY_PRESSURE_LEVEL_NONE;
}
// Converts an UMA enumeration value to a memory pressure level.
MemoryPressureLevel MemoryPressureLevelFromUmaEnumValue(
MemoryPressureLevelUMA level) {
switch (level) {
case UMA_MEMORY_PRESSURE_LEVEL_NONE:
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
case UMA_MEMORY_PRESSURE_LEVEL_MODERATE:
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
case UMA_MEMORY_PRESSURE_LEVEL_CRITICAL:
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
case UMA_MEMORY_PRESSURE_LEVEL_COUNT:
NOTREACHED();
break;
}
NOTREACHED();
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
// Converts a pressure state change to an UMA enumeration value.
MemoryPressureLevelChangeUMA MemoryPressureLevelChangeToUmaEnumValue(
MemoryPressureLevel old_level,
MemoryPressureLevel new_level) {
switch (old_level) {
case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: {
if (new_level == MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE)
return UMA_MEMORY_PRESSURE_LEVEL_CHANGE_NONE_TO_MODERATE;
if (new_level == MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL)
return UMA_MEMORY_PRESSURE_LEVEL_CHANGE_NONE_TO_CRITICAL;
break; // Should never happen; handled by the NOTREACHED below.
}
case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: {
if (new_level == MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE)
return UMA_MEMORY_PRESSURE_LEVEL_CHANGE_MODERATE_TO_NONE;
if (new_level == MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL)
return UMA_MEMORY_PRESSURE_LEVEL_CHANGE_MODERATE_TO_CRITICAL;
break; // Should never happen; handled by the NOTREACHED below.
}
case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: {
if (new_level == MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE)
return UMA_MEMORY_PRESSURE_LEVEL_CHANGE_CRITICAL_TO_MODERATE;
if (new_level == MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE)
return UMA_MEMORY_PRESSURE_LEVEL_CHANGE_CRITICAL_TO_MODERATE;
break; // Should never happen; handled by the NOTREACHED below.
}
}
NOTREACHED();
return UMA_MEMORY_PRESSURE_LEVEL_CHANGE_NONE_TO_MODERATE;
}
} // namespace
MemoryPressureStatsCollector::MemoryPressureStatsCollector(
base::TickClock* tick_clock)
: tick_clock_(tick_clock),
last_pressure_level_(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
}
void MemoryPressureStatsCollector::UpdateStatistics(
MemoryPressureLevel current_pressure_level) {
base::TimeTicks now = tick_clock_->NowTicks();
// Special case: first call to the collector. Observations have just started
// so there's nothing to report.
if (last_update_time_.is_null()) {
last_pressure_level_ = current_pressure_level;
last_update_time_ = now;
return;
}
// If the pressure level has transitioned then report this.
if (last_pressure_level_ != current_pressure_level)
ReportLevelChange(last_pressure_level_, current_pressure_level);
// Increment the appropriate cumulative bucket.
int index = MemoryPressureLevelToUmaEnumValue(current_pressure_level);
unreported_cumulative_time_[index] += now - last_update_time_;
// Update last pressure related state.
last_pressure_level_ = current_pressure_level;
last_update_time_ = now;
// Make reports about the amount of time spent cumulatively at each level.
for (size_t i = 0; i < arraysize(unreported_cumulative_time_); ++i) {
// Report the largest number of whole seconds possible at this moment and
// carry around the rest for a future report.
if (unreported_cumulative_time_[i].is_zero())
continue;
int64_t seconds = unreported_cumulative_time_[i].InSeconds();
if (seconds == 0)
continue;
unreported_cumulative_time_[i] -= base::TimeDelta::FromSeconds(seconds);
ReportCumulativeTime(MemoryPressureLevelFromUmaEnumValue(
static_cast<MemoryPressureLevelUMA>(i)),
static_cast<int>(seconds));
}
}
// static
void MemoryPressureStatsCollector::ReportCumulativeTime(
MemoryPressureLevel pressure_level,
int seconds) {
// Use the more primitive STATIC_HISTOGRAM_POINTER_BLOCK macro because the
// simple UMA_HISTOGRAM macros don't expose 'AddCount' functionality.
STATIC_HISTOGRAM_POINTER_BLOCK(
"Memory.PressureLevel",
AddCount(MemoryPressureLevelToUmaEnumValue(pressure_level), seconds),
base::LinearHistogram::FactoryGet(
"Memory.PressureLevel", 1, UMA_MEMORY_PRESSURE_LEVEL_COUNT,
UMA_MEMORY_PRESSURE_LEVEL_COUNT + 1,
base::HistogramBase::kUmaTargetedHistogramFlag));
}
// static
void MemoryPressureStatsCollector::ReportLevelChange(
MemoryPressureLevel old_pressure_level,
MemoryPressureLevel new_pressure_level) {
UMA_HISTOGRAM_ENUMERATION("Memory.PressureLevelChange",
MemoryPressureLevelChangeToUmaEnumValue(
old_pressure_level, new_pressure_level),
UMA_MEMORY_PRESSURE_LEVEL_CHANGE_COUNT);
}
} // namespace memory_pressure