| """Provides a pre-kill method to run on Linux. |
| |
| This timeout pre-kill method relies on the Linux perf-tools |
| distribution. The appropriate way to obtain this set of tools |
| will depend on the Linux distribution. |
| |
| For Ubuntu 16.04, the invoke the following command: |
| sudo apt-get install perf-tools-unstable |
| """ |
| from __future__ import print_function |
| |
| # system imports |
| import os |
| import subprocess |
| import sys |
| import tempfile |
| |
| |
| def do_pre_kill(process_id, runner_context, output_stream, sample_time=3): |
| """Samples the given process id, and puts the output to output_stream. |
| |
| @param process_id the local process to sample. |
| |
| @param runner_context a dictionary of details about the architectures |
| and platform on which the given process is running. Expected keys are |
| archs (array of architectures), platform_name, platform_url, and |
| platform_working_dir. |
| |
| @param output_stream file-like object that should be used to write the |
| results of sampling. |
| |
| @param sample_time specifies the time in seconds that should be captured. |
| """ |
| |
| # Validate args. |
| if runner_context is None: |
| raise Exception("runner_context argument is required") |
| if not isinstance(runner_context, dict): |
| raise Exception("runner_context argument must be a dictionary") |
| |
| # We will try to run sample on the local host only if there is no URL |
| # to a remote. |
| if "platform_url" in runner_context and ( |
| runner_context["platform_url"] is not None): |
| import pprint |
| sys.stderr.write( |
| "warning: skipping timeout pre-kill sample invocation because we " |
| "don't know how to run on a remote yet. runner_context={}\n" |
| .format(pprint.pformat(runner_context))) |
| |
| # We're going to create a temp file, and immediately overwrite it with the |
| # following command. This just ensures we don't have any races in |
| # creation of the temporary sample file. |
| fileno, filename = tempfile.mkstemp(suffix='perfdata') |
| os.close(fileno) |
| fileno = None |
| |
| try: |
| with open(os.devnull, 'w') as devnull: |
| returncode = subprocess.call(['timeout', str(sample_time), 'perf', |
| 'record', '-g', '-o', filename, '-p', str(process_id)], |
| stdout=devnull, stderr=devnull) |
| if returncode == 0 or returncode == 124: |
| # This is okay - this is the timeout return code, which is totally |
| # expected. |
| pass |
| else: |
| raise Exception("failed to call 'perf record .., error: {}".format( |
| returncode)) |
| |
| with open(os.devnull, 'w') as devnull: |
| output = subprocess.check_output(['perf', 'report', '--call-graph', |
| '--stdio', '-i', filename], stderr=devnull) |
| output_stream.write(output) |
| finally: |
| os.remove(filename) |