Adds support for using client-side profilers from the server side.
This makes a few significant infrastructure changes in order the
support this:
- it modifies autotest.py to allow "background" clients to be
launched, where the server simply kicks off a client as a
background process without bothering to monitor it
- it adds a profiler_test test which is not a "real" test but is
instead used to allow the server to start & stop the profilers
in a reasonably controlled way
Currently, this still lacks any support for dealing with reboots; if
a test reboots the remote machine then the profilers will not be
restarted and the logs collected will only go as far as the first
reboot.
Risk: Medium
Visibility: You can use profilers from the server side (via
job.profilers.add and job.profilers.delete)
Signed-off-by: John Admanski <jadmanski@google.com>
git-svn-id: http://test.kernel.org/svn/autotest/trunk@2514 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/server/profiler.py b/server/profiler.py
index a23c655..63b2e4d 100644
--- a/server/profiler.py
+++ b/server/profiler.py
@@ -1,7 +1,17 @@
-import itertools
+import os, itertools
from autotest_lib.server import autotest
+PROFILER_TMPDIR = "/tmp/profilers"
+
+
+# control file template for running a job that uses profiler 'name'
+run_profiler_control = """\
+job.profilers.add(%s)
+job.run_test("profiler_test")
+job.profilers.delete(%r)
+"""
+
def get_unpassable_types(arg):
""" Given an argument, returns a set of types contained in arg that are
@@ -36,6 +46,13 @@
raise TypeError(msg)
+def encode_args(profiler, args, dargs):
+ parts = [repr(profiler)]
+ parts += [repr(arg) for arg in dargs]
+ parts += ["%s=%r" % darg for darg in dargs.iteritems()]
+ return ", ".join(parts)
+
+
class profiler_proxy(object):
""" This is a server-side class that acts as a proxy to a real client-side
profiler class."""
@@ -48,11 +65,13 @@
def _install(self):
""" Install autotest on any current job hosts. """
- current_job_hosts = self.job.hosts
+ current_job_hosts = set(host for host in self.job.hosts
+ if not host.get_autodir() or
+ host.get_autodir().startswith(PROFILER_TMPDIR))
current_profiler_hosts = set(self.installed_hosts.keys())
# install autotest on any new hosts in job.hosts
for host in current_job_hosts - current_profiler_hosts:
- tmp_dir = host.get_tmp_dir(parent="/tmp/profilers")
+ tmp_dir = host.get_tmp_dir(parent=PROFILER_TMPDIR)
at = autotest.Autotest(host)
at.install(autodir=tmp_dir)
self.installed_hosts[host] = at
@@ -61,24 +80,50 @@
del self.installed_hosts[host]
- def setup(self, *args, **dargs):
- validate_args(args)
- validate_args(dargs)
- self._install()
-
-
def initialize(self, *args, **dargs):
validate_args(args)
validate_args(dargs)
+ self.args, self.dargs = args, dargs
+
+
+ def setup(self, *args, **dargs):
+ assert self.args == args and self.dargs == dargs
+ # the actual setup happens lazily at start()
+
+
+ def _signal_clients(self, command):
+ """ Signal to each client that it should execute profilers.command
+ by writing a byte into AUTODIR/profilers.command. """
+ for host in self.installed_hosts.iterkeys():
+ autodir = host.get_autodir()
+ path = os.path.join(autodir, "profiler.%s" % command)
+ host.run("echo A > %s" % path)
def start(self, test):
- pass
+ self._install()
+ encoded_args = encode_args(self.name, self.args, self.dargs)
+ control_script = run_profiler_control % (encoded_args, self.name)
+ for at in self.installed_hosts.itervalues():
+ at.run(control_script, background=True)
+ self._signal_clients("start")
def stop(self, test):
- pass
+ self._signal_clients("stop")
def report(self, test):
- pass
+ self._signal_clients("report")
+ # pull back all the results
+ for host in self.installed_hosts.iterkeys():
+ results_dir = os.path.join(host.get_autodir(), "results",
+ "default", "profiler_test",
+ "profiling") + "/"
+ local_dir = os.path.join(test.profdir, host.hostname)
+ if not os.path.exists(local_dir):
+ os.makedirs(local_dir)
+ try:
+ host.get_file(results_dir, local_dir)
+ except error.AutoservRunError:
+ pass # no files to pull back, nothing we can do