| # Copyright (c) 2013 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 cStringIO, dbus, gzip, logging, subprocess |
| |
| from autotest_lib.client.bin import test |
| from autotest_lib.client.common_lib import error |
| |
| |
| class platform_DebugDaemonGetPerfData(test.test): |
| """ |
| This autotest tests the collection of perf data. It calls perf indirectly |
| through debugd -> quipper -> perf. |
| |
| The perf data is collected both when the system is idle and when there is a |
| process running in the background. |
| |
| The perf data is collected over various durations. |
| """ |
| |
| version = 1 |
| |
| # A list of durations over which to gather perf data using quipper (given in |
| # seconds), plus the number of times to run perf with each duration. |
| # e.g. the entry "1: 50" means to run perf for 1 second 50 times. |
| _profile_duration_and_repetitions = [ |
| (1, 3), |
| (5, 1) |
| ] |
| |
| # Commands to repeatedly run in the background when collecting perf data. |
| _system_profile_commands = { |
| 'idle' : 'sleep 1', |
| 'busy' : 'ls', |
| } |
| |
| _dbus_debugd_object = '/org/chromium/debugd' |
| _dbus_debugd_name = 'org.chromium.debugd' |
| |
| def gzip_string(self, string): |
| """ |
| Gzip a string. |
| |
| @param string: The input string to be gzipped. |
| |
| Returns: |
| The gzipped string. |
| """ |
| string_file = cStringIO.StringIO() |
| gzip_file = gzip.GzipFile(fileobj=string_file, mode='wb') |
| gzip_file.write(string) |
| gzip_file.close() |
| return string_file.getvalue() |
| |
| |
| def validate_get_perf_method(self, get_perf_method, duration, num_reps, |
| profile_type): |
| """ |
| Validate a debugd method that returns perf data. |
| |
| @param get_perf_method: The debugd method to test. |
| |
| @param duration: The duration to use for perf data collection. |
| |
| @param num_reps: Number of times to run. |
| |
| @param profile_type: A label to use for storing into perf keyvals. |
| """ |
| iface_function = getattr(self.dbus_iface, get_perf_method) |
| result_total_size = 0 |
| result_zipped_total_size = 0 |
| for _ in range(num_reps): |
| result = iface_function(duration) |
| if not result: |
| raise error.TestFail('No perf output found: %s' % result) |
| logging.info('%s() for %s seconds returned %d items', |
| get_perf_method, duration, len(result)) |
| if len(result) < 10: |
| raise error.TestFail('Perf output too small') |
| |
| # Convert |result| from an array of dbus.Bytes to a string. |
| result = ''.join(chr(b) for b in result) |
| |
| # If there was an error in collecting a profile with quipper, debugd |
| # will output an error message. Make sure to check for this message. |
| # It is found in PerfTool::GetPerfDataHelper() in |
| # debugd/src/perf_tool.cc. |
| if result.startswith('<process exited with status: '): |
| raise error.TestFail('Quipper failed: %s' % result) |
| |
| result_total_size += len(result) |
| result_zipped_total_size += len(self.gzip_string(result)) |
| |
| key = 'mean_%s_size_%s_%d' % (get_perf_method, profile_type, duration) |
| keyvals = {} |
| if num_reps > 0: |
| keyvals[key] = result_total_size / num_reps |
| keyvals[key + '_zipped'] = result_zipped_total_size / num_reps |
| self.write_perf_keyval(keyvals) |
| |
| |
| def run_once(self, *args, **kwargs): |
| """ |
| Primary autotest function. |
| """ |
| |
| bus = dbus.SystemBus() |
| proxy = bus.get_object(self._dbus_debugd_name, self._dbus_debugd_object) |
| self.dbus_iface = dbus.Interface(proxy, |
| dbus_interface=self._dbus_debugd_name) |
| |
| get_perf_methods = ['GetRichPerfData'] |
| |
| # Open /dev/null to redirect unnecessary output. |
| devnull = open('/dev/null', 'w') |
| |
| for profile_type in self._system_profile_commands: |
| # Repeatedly run the comand for the current profile. |
| cmd = 'while true; do %s; done' % \ |
| self._system_profile_commands[profile_type] |
| process = subprocess.Popen(cmd, stdout=devnull, shell=True) |
| |
| for duration, num_reps in self._profile_duration_and_repetitions: |
| # Collect perf data from debugd. |
| for get_perf_method in get_perf_methods: |
| self.validate_get_perf_method(get_perf_method, duration, |
| num_reps, profile_type) |
| |
| # Terminate the process and actually wait for it to terminate. |
| process.terminate() |
| while process.poll() == None: |
| pass |
| |
| devnull.close() |