blob: 8dd911fe21fad3f0882a272ec36f4d8501de0994 [file] [log] [blame]
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import csv
import logging
import os
import time
from autotest_lib.client.bin import site_utils
from autotest_lib.client.bin import test
TEST_DURATION = 86100 # Time (secs) of test length, 23:55 hrs.
SAMPLE_INTERVAL = 60 # Time (secs) between measurement samples.
REPORT_INTERVAL = 3600 # Time (secs) between perf data reports.
STABILIZATION_DURATION = 60 # Time for test stabilization (secs).
TMP_DIRECTORY = '/tmp/'
PERF_FILE_PREFIX = 'perf'
EXIT_FLAG_FILE = TMP_DIRECTORY + 'longevity_terminate'
OLD_FILE_AGE = 10080 # Time (mins) since file was written/modified.
CMD_REMOVE_OLD_FILES = ('find %s -name %s* -type f -mmin +%s -delete' %
(TMP_DIRECTORY, PERF_FILE_PREFIX, OLD_FILE_AGE))
class longevity_Tracker(test.test):
"""Monitors device stability over long periods of time."""
version = 1
def get_cpu_usage(self):
"""Computes percent CPU in active use for the sample interval.
Note: This method introduces a sleep period into the test. It
measures total CPU times at a start and end point, then computes
percent usage between those points.
@returns float of percent active use of CPU.
"""
# Time between measurements is ~90% of the sample interval.
measurement_time_delta = SAMPLE_INTERVAL * 0.90
cpu_usage_start = site_utils.get_cpu_usage()
time.sleep(measurement_time_delta)
cpu_usage_end = site_utils.get_cpu_usage()
return site_utils.compute_active_cpu_time(cpu_usage_start,
cpu_usage_end) * 100
def get_mem_usage(self):
"""Computes percent memory in use.
@returns float of percentage memory used of total memory.
"""
total_memory = site_utils.get_mem_total()
return (total_memory - site_utils.get_mem_free()) * 100 / total_memory
def get_perf_metrics(self, cpu_values, mem_values):
"""Computes metrics for performance dashboard.
@param cpu_values: list of sampled CPU usage values.
@param mem_values: list of sampled Memory usage values.
@returns metrics for CPU and Memory usage.
"""
cpu_metric = []
mem_metric = []
if cpu_values and mem_values:
cpu_metric = sorted(cpu_values)[len(cpu_values) // 2]
mem_metric = sorted(mem_values)[len(mem_values) // 2]
return (cpu_metric, mem_metric)
def send_perf_metrics(self, metrics):
"""Send performance metrics to Performance Dashboard.
@param metrics: Performance metrics for CPU and Memory usage.
"""
logging.info('Debug: send_perf_metrics() = %s', metrics)
# TODO(scunningham): Code to upload perf data.
return
def elapsed_time(self, mark_time):
"""Get time elapsed since |mark_time|.
@param mark_time: point in time from which elapsed time is measured.
@returns time elapsed since the marked time.
"""
return time.time() - mark_time
def modulo_time(self, timer, interval):
"""Get time eplased on |timer| for the |interval| modulus.
Value returned is used to adjust the timer so that it is synchronized
with the current interval.
@param timer: time on timer, in seconds.
@param interval: period of time in seconds.
@returns time elapsed from the start of the current interval.
"""
return timer % int(interval)
def syncup_time(self, timer, interval):
"""Get time remaining on |timer| for the |interval| modulus.
Value returned is used to induce sleep just long enough to put the
process back in sync with the timer.
@param timer: time on timer, in seconds.
@param interval: period of time in seconds.
@returns time remaining till the end of the current interval.
"""
return interval - (timer % int(interval))
def run_once(self):
# Test runs 23:00 hrs, or until the exit flag file is seen.
# Delete exit flag file at start of test.
if os.path.isfile(EXIT_FLAG_FILE):
os.remove(EXIT_FLAG_FILE)
# Allow system to stabilize before start taking measurements.
test_start_time = time.time()
time.sleep(STABILIZATION_DURATION)
perf_keyval = {}
cpu_values = []
mem_values = []
file_name = (PERF_FILE_PREFIX +
time.strftime('_%Y-%m-%d_%H-%M') + '.csv')
perf_file = open(TMP_DIRECTORY + file_name, 'w')
writer = csv.writer(perf_file)
writer.writerow(['time', 'cpu', 'memory'])
# Align time of loop start with the sample interval.
test_elapsed_time = self.elapsed_time(test_start_time)
time.sleep(self.syncup_time(test_elapsed_time, SAMPLE_INTERVAL))
test_elapsed_time = self.elapsed_time(test_start_time)
report_start_time = time.time()
report_prev_time = report_start_time
report_elapsed_prev_time = self.elapsed_time(report_prev_time)
offset = self.modulo_time(report_elapsed_prev_time, REPORT_INTERVAL)
report_timer = report_elapsed_prev_time + offset
while self.elapsed_time(test_start_time) <= TEST_DURATION:
if os.path.isfile(EXIT_FLAG_FILE):
logging.info('Exit flag file detected. Exiting test.')
break
perf_keyval['cpu_usage'] = self.get_cpu_usage()
perf_keyval['memory_usage'] = self.get_mem_usage()
cpu_values.append(perf_keyval['cpu_usage'])
mem_values.append(perf_keyval['memory_usage'])
# self.write_perf_keyval(perf_keyval) # Need for perf dashboard?
writer.writerow([time.strftime('%Y/%m/%d %H:%M:%S'),
perf_keyval['cpu_usage'],
perf_keyval['memory_usage']])
# Periodically calculate and report metrics for perf dashboard.
report_elapsed_prev_time = self.elapsed_time(report_prev_time)
report_timer = report_elapsed_prev_time + offset
if report_timer >= REPORT_INTERVAL:
metrics = self.get_perf_metrics(cpu_values, mem_values)
self.send_perf_metrics(metrics)
writer.writerow(['Report Time = %s' %
time.strftime('%Y/%m/%d %H:%M:%S'),
' CPU Usage (median) = %s' % metrics[0],
' RAM Usage (median) = %s' % metrics[1]])
cpu_values = []
mem_values = []
# Set the report previous time to the current time.
report_prev_time = time.time()
report_elapsed_prev_time = self.elapsed_time(report_prev_time)
# Calculate offset based on the original report start time.
report_elapsed_time = self.elapsed_time(report_start_time)
offset = self.modulo_time(report_elapsed_time, REPORT_INTERVAL)
# Set the timer to time elapsed plus offset to next interval.
report_timer = report_elapsed_prev_time + offset
# Sync the loop time to the sample interval.
test_elapsed_time = self.elapsed_time(test_start_time)
time.sleep(self.syncup_time(test_elapsed_time, SAMPLE_INTERVAL))
perf_file.close()
def cleanup(self):
"""Delete aged perf data files and exit flag file."""
os.system(CMD_REMOVE_OLD_FILES)
if os.path.isfile(EXIT_FLAG_FILE):
os.remove(EXIT_FLAG_FILE)