1# Lint as: python2, python3 2# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import hashlib, logging, multiprocessing, os, time 7from autotest_lib.client.bin import test, utils 8from autotest_lib.client.common_lib import error 9from autotest_lib.client.cros.power import sys_power 10 11SUSPEND_BURN_SECONDS = 10 12RESUME_BURN_SECONDS = 5 13MIN_CPU_USAGE = .95 14 15PROC_STAT_CPU_FIELDS = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 16 'softirq', 'steal', 'guest', 'guest_nice'] 17PROC_STAT_CPU_IDLE_FIELDS = ['idle', 'iowait'] 18 19SYSFS_CPUQUIET_ENABLE = '/sys/devices/system/cpu/cpuquiet/tegra_cpuquiet/enable' 20 21def cpu_stress(): 22 sha512_hash = open('/dev/urandom', 'rb').read(64) 23 while True: 24 sha512_hash = hashlib.sha512(sha512_hash).digest() 25 26 27def get_system_times(): 28 proc_stat = utils.read_file('/proc/stat') 29 for line in proc_stat.split('\n'): 30 if line.startswith('cpu '): 31 times = line[4:].strip().split(' ') 32 times = [int(jiffies) for jiffies in times] 33 return dict(zip(PROC_STAT_CPU_FIELDS, times)) 34 35 36def get_avg_cpu_usage(pre_times, post_times): 37 diff_times = {} 38 39 for field in PROC_STAT_CPU_FIELDS: 40 diff_times[field] = post_times[field] - pre_times[field] 41 42 idle_time = sum(diff_times[field] for field in PROC_STAT_CPU_IDLE_FIELDS) 43 total_time = sum(diff_times[field] for field in PROC_STAT_CPU_FIELDS) 44 45 return float(total_time - idle_time) / total_time 46 47 48def sleep_and_measure_cpu(sleep_seconds): 49 pre_times = get_system_times() 50 time.sleep(sleep_seconds) 51 post_times = get_system_times() 52 53 avg_cpu_usage = get_avg_cpu_usage(pre_times, post_times) 54 logging.info('average CPU utilization, last %ds: %s%%', 55 sleep_seconds, avg_cpu_usage * 100.) 56 return avg_cpu_usage 57 58 59class power_HotCPUSuspend(test.test): 60 """Suspend the system with 100% CPU usage.""" 61 62 version = 1 63 64 def initialize(self): 65 # Store the setting if the system has CPUQuiet feature 66 if os.path.exists(SYSFS_CPUQUIET_ENABLE): 67 self.is_cpuquiet_enabled = utils.read_file(SYSFS_CPUQUIET_ENABLE) 68 utils.write_one_line(SYSFS_CPUQUIET_ENABLE, '0') 69 70 def run_once(self): 71 # create processs pool with enough workers to spin all CPUs 72 cpus = multiprocessing.cpu_count() 73 logging.info('found %d cpus', cpus) 74 workers = max(16, cpus * 2) 75 pool = multiprocessing.Pool(workers) 76 77 try: 78 # fill all CPUs with a spinning task 79 logging.info('starting %d workers', workers) 80 results = [pool.apply_async(cpu_stress) for _ in range(workers)] 81 82 # wait for things to settle 83 logging.info('spinning for %d seconds', SUSPEND_BURN_SECONDS) 84 if sleep_and_measure_cpu(SUSPEND_BURN_SECONDS) < MIN_CPU_USAGE: 85 # There should be no idle time accounted while we're spinning. 86 raise error.TestError('unexpected CPU idle time while spinning') 87 88 # go to suspend 89 sys_power.kernel_suspend(10) 90 91 # keep spinning after userland resumes 92 logging.info('spinning for %d more seconds', RESUME_BURN_SECONDS) 93 if sleep_and_measure_cpu(RESUME_BURN_SECONDS) < MIN_CPU_USAGE: 94 # There should be no idle time accounted while we're spinning. 95 raise error.TestError('unexpected CPU idle time after resume') 96 97 # check workers: if computation completed, something is wrong 98 for result in results: 99 if result.ready(): 100 logging.error('worker finished: %s', result.get()) 101 raise error.TestError('worker terminated!') 102 103 finally: 104 # kill off the workers 105 logging.info('killing %d workers', workers) 106 pool.terminate() 107 108 def cleanup(self): 109 # Restore the original setting if system has CPUQuiet feature 110 if os.path.exists(SYSFS_CPUQUIET_ENABLE): 111 utils.open_write_close( 112 SYSFS_CPUQUIET_ENABLE, self.is_cpuquiet_enabled) 113