diff options
author | Sam Thursfield <sam.thursfield@codethink.co.uk> | 2017-12-22 17:57:00 +0000 |
---|---|---|
committer | Sam Thursfield <sam.thursfield@codethink.co.uk> | 2017-12-22 17:57:00 +0000 |
commit | 14ddec205cabae94df234ef2965d6f12409e2688 (patch) | |
tree | b29b6718a06baea305865591383ab0181dc48c17 | |
parent | 8c0aaa1c68017357c73055120568d24f672c4bac (diff) | |
download | buildstream-14ddec205cabae94df234ef2965d6f12409e2688.tar.gz |
Hack up initial helper for profiling the SafeHardlinks FUSE layer
This should work, but it doesn't because for some reason the subprocess
fails to write out a profile. If the equivalent process is run directly
from the commandline then it works fine no matter how we kill it. But
when run from the parent Python subprocess, sending it a signal causes
it to exit seemingly immediately.
All of SIGINT, SIGTERM and SIGHUP cause the same erroneous behaviour.
Explicitly handling SIGINT inside the subprocess also has no effect.
Trying to catching KeyboardInterrupt also doesn't have any effect.
However the process does exit with the expected returncode showing
that it was killed with whatever signal we used.
This makes no sense really and is probably not worth digging into
further.
-rw-r--r-- | profile-fuse-filesystem.py | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/profile-fuse-filesystem.py b/profile-fuse-filesystem.py new file mode 100644 index 000000000..78e6f9826 --- /dev/null +++ b/profile-fuse-filesystem.py @@ -0,0 +1,71 @@ +# Profile helper for the SafeHardlinks FUSE filesystem +# +# Run inside a project directory! + + +import os +import signal +import subprocess +import sys +import tempfile + +import buildstream +import buildstream._frontend + + +# This works by monkeypatching the .mount() method on the SafeHardlinks +# filesystem. The normal implementation in the _fuse.mount module spawns +# a multiprocessing.Process subprocess to actually run the filesystem +# and the main process carries on independently. We override that so that +# it blocks +def mount_in_process_and_block(self, mountpoint): + self._Mount__mountpoint = mountpoint + + self._Mount__operations = self.create_operations() + + print("Mounting a SafeHardlinks filesystem at {} then blocking".format(mountpoint)) + print("Profile will be written to {}".format(os.path.abspath('fuse.pstats'))) + print("Try: bwrap --bind {} / --dev /dev --proc /proc --tmpfs /tmp COMMAND".format(mountpoint)) + + with tempfile.NamedTemporaryFile('w') as f: + f.write(""" +import buildstream, sys\n +operations = buildstream._fuse.hardlinks.SafeHardlinkOps(sys.argv[2], sys.argv[3])\n +buildstream._fuse.fuse.FUSE(operations, sys.argv[1], nothreads=True, foreground=True, nonempty=True)\n + """) + f.flush() + + args = [sys.executable, '-m', 'cProfile', f.name, + mountpoint, self.directory, self.tempdir] + print(args) + + # Run the FUSE mount as subprocess with profiling enabled. + try: + p = subprocess.Popen(args) + p.wait() + except KeyboardInterrupt: + print("Terminating on KeyboardInterrupt") + p.send_signal(signal.SIGINT) + p.wait() + print("Returncode: {}".format(p.returncode)) + raise + + + # Here's how you'd run the FUSE mount in-process. Your profile gets skewed + # by the time spent staging stuff though. + #buildstream._fuse.fuse.FUSE(self._Mount__operations, + # self._Mount__mountpoint, + # nothreads=True, foreground=True, nonempty=True) + + +buildstream._fuse.hardlinks.SafeHardlinks.mount = mount_in_process_and_block + + +if len(sys.argv) != 2: + raise RuntimeError("Usage: {} ELEMENT".format(sys.argv[0])) + +element = sys.argv[1] + + +cli = buildstream._frontend.main.cli +cli.main(args=('shell', element), prog_name=cli.name) |