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