• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Facade to access the system-related functionality."""
6
7import StringIO
8import os
9import threading
10import time
11
12from autotest_lib.client.bin import utils
13
14
15class SystemFacadeNativeError(Exception):
16    """Error in SystemFacadeNative."""
17    pass
18
19
20class SystemFacadeNative(object):
21    """Facede to access the system-related functionality.
22
23    The methods inside this class only accept Python native types.
24
25    """
26    SCALING_GOVERNOR_MODES = [
27            'interactive',
28            'performance',
29            'ondemand',
30            'powersave',
31            'sched'
32            ]
33
34    def __init__(self):
35        self._bg_worker = None
36
37    def set_scaling_governor_mode(self, index, mode):
38        """Set mode of CPU scaling governor on one CPU.
39
40        @param index: CPU index starting from 0.
41
42        @param mode: Mode of scaling governor, accept 'interactive' or
43                     'performance'.
44
45        @returns: The original mode.
46
47        """
48        if mode not in self.SCALING_GOVERNOR_MODES:
49            raise SystemFacadeNativeError('mode %s is invalid' % mode)
50
51        governor_path = os.path.join(
52                '/sys/devices/system/cpu/cpu%d' % index,
53                'cpufreq/scaling_governor')
54        if not os.path.exists(governor_path):
55            raise SystemFacadeNativeError(
56                    'scaling governor of CPU %d is not available' % index)
57
58        original_mode = utils.read_one_line(governor_path)
59        utils.open_write_close(governor_path, mode)
60
61        return original_mode
62
63
64    def get_cpu_usage(self):
65        """Returns machine's CPU usage.
66
67        Returns:
68            A dictionary with 'user', 'nice', 'system' and 'idle' values.
69            Sample dictionary:
70            {
71                'user': 254544,
72                'nice': 9,
73                'system': 254768,
74                'idle': 2859878,
75            }
76        """
77        return utils.get_cpu_usage()
78
79
80    def compute_active_cpu_time(self, cpu_usage_start, cpu_usage_end):
81        """Computes the fraction of CPU time spent non-idling.
82
83        This function should be invoked using before/after values from calls to
84        get_cpu_usage().
85        """
86        return utils.compute_active_cpu_time(cpu_usage_start,
87                                                  cpu_usage_end)
88
89
90    def get_mem_total(self):
91        """Returns the total memory available in the system in MBytes."""
92        return utils.get_mem_total()
93
94
95    def get_mem_free(self):
96        """Returns the currently free memory in the system in MBytes."""
97        return utils.get_mem_free()
98
99    def get_mem_free_plus_buffers_and_cached(self):
100        """
101        Returns the free memory in MBytes, counting buffers and cached as free.
102
103        This is most often the most interesting number since buffers and cached
104        memory can be reclaimed on demand. Note however, that there are cases
105        where this as misleading as well, for example used tmpfs space
106        count as Cached but can not be reclaimed on demand.
107        See https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt.
108        """
109        return utils.get_mem_free_plus_buffers_and_cached()
110
111    def get_ec_temperatures(self):
112        """Uses ectool to return a list of all sensor temperatures in Celsius.
113        """
114        return utils.get_ec_temperatures()
115
116    def get_current_temperature_max(self):
117        """
118        Returns the highest reported board temperature (all sensors) in Celsius.
119        """
120        return utils.get_current_temperature_max()
121
122    def get_current_board(self):
123        """Returns the current device board name."""
124        return utils.get_current_board()
125
126
127    def get_chromeos_release_version(self):
128        """Returns chromeos version in device under test as string. None on
129        fail.
130        """
131        return utils.get_chromeos_release_version()
132
133    def get_num_allocated_file_handles(self):
134        """
135        Returns the number of currently allocated file handles.
136        """
137        return utils.get_num_allocated_file_handles()
138
139    def get_storage_statistics(self, device=None):
140        """
141        Fetches statistics for a storage device.
142        """
143        return utils.get_storage_statistics(device)
144
145    def start_bg_worker(self, command):
146        """
147        Start executing the command in a background worker.
148        """
149        self._bg_worker = BackgroundWorker(command, do_process_output=True)
150        self._bg_worker.start()
151
152    def get_and_discard_bg_worker_output(self):
153        """
154        Returns the output collected so far since the last call to this method.
155        """
156        if self._bg_worker is None:
157            SystemFacadeNativeError('Background worker has not been started.')
158
159        return self._bg_worker.get_and_discard_output()
160
161    def stop_bg_worker(self):
162        """
163        Stop the worker.
164        """
165        if self._bg_worker is None:
166            SystemFacadeNativeError('Background worker has not been started.')
167
168        self._bg_worker.stop()
169        self._bg_worker = None
170
171
172class BackgroundWorker(object):
173    """
174    Worker intended for executing a command in the background and collecting its
175    output.
176    """
177
178    def __init__(self, command, do_process_output=False):
179        self._bg_job = None
180        self._command = command
181        self._do_process_output = do_process_output
182        self._output_lock = threading.Lock()
183        self._process_output_thread = None
184        self._stdout = StringIO.StringIO()
185
186    def start(self):
187        """
188        Start executing the command.
189        """
190        self._bg_job = utils.BgJob(self._command, stdout_tee=self._stdout)
191        self._bg_job.sp.poll()
192        if self._bg_job.sp.returncode is not None:
193            self._exit_bg_job()
194
195        if self._do_process_output:
196            self._process_output_thread = threading.Thread(
197                    target=self._process_output)
198            self._process_output_thread.start()
199
200    def _process_output(self, sleep_interval=0.01):
201        while self._do_process_output:
202            with self._output_lock:
203                self._bg_job.process_output()
204            time.sleep(sleep_interval)
205
206    def get_and_discard_output(self):
207        """
208        Returns the output collected so far and then clears the output buffer.
209        In other words, subsequent calls to this method will not include output
210        that has already been returned before.
211        """
212        output = ""
213        with self._output_lock:
214            self._stdout.flush()
215            output = self._stdout.getvalue()
216            self._stdout.truncate(0)
217            self._stdout.seek(0)
218        return output
219
220    def stop(self):
221        """
222        Stop executing the command.
223        """
224        if self._do_process_output:
225            self._do_process_output = False
226            self._process_output_thread.join(1)
227        self._exit_bg_job()
228
229    def _exit_bg_job(self):
230        utils.nuke_subprocess(self._bg_job.sp)
231        utils.join_bg_jobs([self._bg_job])
232        if self._bg_job.result.exit_status > 0:
233            raise SystemFacadeNativeError('Background job failed: %s' %
234                                          self._bg_job.result.command)
235