diff options
author | Todd Fiala <todd.fiala@gmail.com> | 2016-09-26 20:25:47 +0000 |
---|---|---|
committer | Todd Fiala <todd.fiala@gmail.com> | 2016-09-26 20:25:47 +0000 |
commit | 2cd84c905df4fe9a6005320a45e98cd4b8ade045 (patch) | |
tree | 4165388cdc668b91a41117d96078e15e6671dce0 /lldb/packages/Python/lldbsuite/pre_kill_hook/tests | |
parent | 9eddaeb5340035f1ed36940f7478999616a898f0 (diff) | |
download | bcm5719-llvm-2cd84c905df4fe9a6005320a45e98cd4b8ade045.tar.gz bcm5719-llvm-2cd84c905df4fe9a6005320a45e98cd4b8ade045.zip |
added Linux support for test timeout sampling
This is the Linux counterpart to the sampling support I added
on the macOS side.
This change also introduces zip-file compression if the size of
the sample output is greater than 10 KB. The Linux side can be
quite large and the textual content is averaging over a 10x
compression factor on tests that I force to time out. When
compression takes place, the filename becomes:
{session_dir}/{TestFilename.py}-{pid}.sample.zip
This support relies on the linux 'perf' tool. If it isn't
present, the behavior is to ignore pre-kill processing of
the timed out test process.
Note calling the perf tool under the timeout command appears
to nuke the profiled process. This was causing the timeout
kill logic to fail due to the process having disappeared.
I modified the kill logic to catch the case of the process
not existing, and I have it ignore the kill request in that
case. Any other exception is still raised.
Reviewers: labath
Subscribers: lldb-commits
Differential Revision: https://reviews.llvm.org/D24890
llvm-svn: 282436
Diffstat (limited to 'lldb/packages/Python/lldbsuite/pre_kill_hook/tests')
-rw-r--r-- | lldb/packages/Python/lldbsuite/pre_kill_hook/tests/test_darwin.py | 2 | ||||
-rw-r--r-- | lldb/packages/Python/lldbsuite/pre_kill_hook/tests/test_linux.py | 133 |
2 files changed, 134 insertions, 1 deletions
diff --git a/lldb/packages/Python/lldbsuite/pre_kill_hook/tests/test_darwin.py b/lldb/packages/Python/lldbsuite/pre_kill_hook/tests/test_darwin.py index 48d286c909e..810b364b07c 100644 --- a/lldb/packages/Python/lldbsuite/pre_kill_hook/tests/test_darwin.py +++ b/lldb/packages/Python/lldbsuite/pre_kill_hook/tests/test_darwin.py @@ -38,7 +38,7 @@ class DarwinPreKillTestCase(TestCase): print("parent: sending shut-down request to child") if self.process: self.child_work_queue.put("hello, child") - self.process.join() + self.process.join() if self.verbose: print("parent: child is fully shut down") diff --git a/lldb/packages/Python/lldbsuite/pre_kill_hook/tests/test_linux.py b/lldb/packages/Python/lldbsuite/pre_kill_hook/tests/test_linux.py new file mode 100644 index 00000000000..ab989df0d20 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/pre_kill_hook/tests/test_linux.py @@ -0,0 +1,133 @@ +"""Test the pre-kill hook on Linux.""" +from __future__ import print_function + +# system imports +from multiprocessing import Process, Queue +import platform +import re +import subprocess +from unittest import main, TestCase + +# third party +from six import StringIO + + +def do_child_thread(): + import os + x = 0 + while True: + x = x + 42 * os.getpid() + return x + + +def do_child_process(child_work_queue, parent_work_queue, verbose): + import os + + pid = os.getpid() + if verbose: + print("child: pid {} started, sending to parent".format(pid)) + parent_work_queue.put(pid) + + # Spin up a daemon thread to do some "work", which will show + # up in a sample of this process. + import threading + worker = threading.Thread(target=do_child_thread) + worker.daemon = True + worker.start() + + if verbose: + print("child: waiting for shut-down request from parent") + child_work_queue.get() + if verbose: + print("child: received shut-down request. Child exiting.") + + +class LinuxPreKillTestCase(TestCase): + + def __init__(self, methodName): + super(LinuxPreKillTestCase, self).__init__(methodName) + self.process = None + self.child_work_queue = None + self.verbose = False + # self.verbose = True + + def tearDown(self): + if self.verbose: + print("parent: sending shut-down request to child") + if self.process: + self.child_work_queue.put("hello, child") + self.process.join() + if self.verbose: + print("parent: child is fully shut down") + + def test_sample(self): + # Ensure we're Darwin. + if platform.system() != 'Linux': + self.skipTest("requires a Linux-based OS") + + # Ensure we have the 'perf' tool. If not, skip the test. + try: + perf_version = subprocess.check_output(["perf", "version"]) + if perf_version is None or not ( + perf_version.startswith("perf version")): + raise Exception("The perf executable doesn't appear" + " to be the Linux perf tools perf") + except Exception: + self.skipTest("requires the Linux perf tools 'perf' command") + + # Start the child process. + self.child_work_queue = Queue() + parent_work_queue = Queue() + self.process = Process(target=do_child_process, + args=(self.child_work_queue, parent_work_queue, + self.verbose)) + if self.verbose: + print("parent: starting child") + self.process.start() + + # Wait for the child to report its pid. Then we know we're running. + if self.verbose: + print("parent: waiting for child to start") + child_pid = parent_work_queue.get() + + # Sample the child process. + from linux import do_pre_kill + context_dict = { + "archs": [platform.machine()], + "platform_name": None, + "platform_url": None, + "platform_working_dir": None + } + + if self.verbose: + print("parent: running pre-kill action on child") + output_io = StringIO() + do_pre_kill(child_pid, context_dict, output_io) + output = output_io.getvalue() + + if self.verbose: + print("parent: do_pre_kill() wrote the following output:", output) + self.assertIsNotNone(output) + + # We should have a samples count entry. + # Samples: + self.assertTrue("Samples:" in output, "should have found a 'Samples:' " + "field in the sampled process output") + + # We should see an event count entry + event_count_re = re.compile(r"Event count[^:]+:\s+(\d+)") + match = event_count_re.search(output) + self.assertIsNotNone(match, "should have found the event count entry " + "in sample output") + if self.verbose: + print("cpu-clock events:", match.group(1)) + + # We should see some percentages in the file. + percentage_re = re.compile(r"\d+\.\d+%") + match = percentage_re.search(output) + self.assertIsNotNone(match, "should have found at least one percentage " + "in the sample output") + + +if __name__ == "__main__": + main() |