blob: 63b2e4dc321ee6cd548ec5d277f7b3220585ea5d [file] [log] [blame]
mblighb3c0c912008-11-27 00:32:45 +00001import os, itertools
jadmanski043e1132008-11-19 17:10:32 +00002from autotest_lib.server import autotest
3
4
mblighb3c0c912008-11-27 00:32:45 +00005PROFILER_TMPDIR = "/tmp/profilers"
6
7
8# control file template for running a job that uses profiler 'name'
9run_profiler_control = """\
10job.profilers.add(%s)
11job.run_test("profiler_test")
12job.profilers.delete(%r)
13"""
14
jadmanski043e1132008-11-19 17:10:32 +000015
16def get_unpassable_types(arg):
17 """ Given an argument, returns a set of types contained in arg that are
18 unpassable. If arg is an atomic type (e.g. int) it either returns an
19 empty set (if the type is passable) or a singleton of the type (if the
20 type is not passable). """
21 if isinstance(arg, (basestring, int, long)):
22 return set()
23 elif isinstance(arg, (list, tuple, set, frozenset, dict)):
24 if isinstance(arg, dict):
25 # keys and values must both be passable
26 parts = itertools.chain(arg.iterkeys(), arg.itervalues())
27 else:
28 # for all other containers we just iterate
29 parts = iter(arg)
30 types = set()
31 for part in parts:
32 types |= get_unpassable_types(arg)
33 return types
34 else:
35 return set([type(arg)])
36
37
38def validate_args(args):
39 """ Validates arguments. Lists and dictionaries are valid argument types,
40 so you can pass *args and **dargs in directly, rather than having to
41 iterate over them yourself. """
42 unpassable_types = get_unpassable_types(args)
43 if unpassable_types:
44 msg = "arguments of type '%s' cannot be passed to remote profilers"
45 msg %= ", ".join(t.__name__ for t in unpassable_types)
46 raise TypeError(msg)
47
48
mblighb3c0c912008-11-27 00:32:45 +000049def encode_args(profiler, args, dargs):
50 parts = [repr(profiler)]
51 parts += [repr(arg) for arg in dargs]
52 parts += ["%s=%r" % darg for darg in dargs.iteritems()]
53 return ", ".join(parts)
54
55
jadmanski043e1132008-11-19 17:10:32 +000056class profiler_proxy(object):
57 """ This is a server-side class that acts as a proxy to a real client-side
58 profiler class."""
59
60 def __init__(self, job, profiler_name):
61 self.job = job
62 self.name = profiler_name
63 self.installed_hosts = {}
64
65
66 def _install(self):
67 """ Install autotest on any current job hosts. """
mblighb3c0c912008-11-27 00:32:45 +000068 current_job_hosts = set(host for host in self.job.hosts
69 if not host.get_autodir() or
70 host.get_autodir().startswith(PROFILER_TMPDIR))
jadmanski043e1132008-11-19 17:10:32 +000071 current_profiler_hosts = set(self.installed_hosts.keys())
72 # install autotest on any new hosts in job.hosts
73 for host in current_job_hosts - current_profiler_hosts:
mblighb3c0c912008-11-27 00:32:45 +000074 tmp_dir = host.get_tmp_dir(parent=PROFILER_TMPDIR)
jadmanski043e1132008-11-19 17:10:32 +000075 at = autotest.Autotest(host)
76 at.install(autodir=tmp_dir)
77 self.installed_hosts[host] = at
78 # drop any installs from hosts no longer in job.hosts
79 for host in current_profiler_hosts - current_job_hosts:
80 del self.installed_hosts[host]
81
82
jadmanski043e1132008-11-19 17:10:32 +000083 def initialize(self, *args, **dargs):
84 validate_args(args)
85 validate_args(dargs)
mblighb3c0c912008-11-27 00:32:45 +000086 self.args, self.dargs = args, dargs
87
88
89 def setup(self, *args, **dargs):
90 assert self.args == args and self.dargs == dargs
91 # the actual setup happens lazily at start()
92
93
94 def _signal_clients(self, command):
95 """ Signal to each client that it should execute profilers.command
96 by writing a byte into AUTODIR/profilers.command. """
97 for host in self.installed_hosts.iterkeys():
98 autodir = host.get_autodir()
99 path = os.path.join(autodir, "profiler.%s" % command)
100 host.run("echo A > %s" % path)
jadmanski043e1132008-11-19 17:10:32 +0000101
102
103 def start(self, test):
mblighb3c0c912008-11-27 00:32:45 +0000104 self._install()
105 encoded_args = encode_args(self.name, self.args, self.dargs)
106 control_script = run_profiler_control % (encoded_args, self.name)
107 for at in self.installed_hosts.itervalues():
108 at.run(control_script, background=True)
109 self._signal_clients("start")
jadmanski043e1132008-11-19 17:10:32 +0000110
111
112 def stop(self, test):
mblighb3c0c912008-11-27 00:32:45 +0000113 self._signal_clients("stop")
jadmanski043e1132008-11-19 17:10:32 +0000114
115
116 def report(self, test):
mblighb3c0c912008-11-27 00:32:45 +0000117 self._signal_clients("report")
118 # pull back all the results
119 for host in self.installed_hosts.iterkeys():
120 results_dir = os.path.join(host.get_autodir(), "results",
121 "default", "profiler_test",
122 "profiling") + "/"
123 local_dir = os.path.join(test.profdir, host.hostname)
124 if not os.path.exists(local_dir):
125 os.makedirs(local_dir)
126 try:
127 host.get_file(results_dir, local_dir)
128 except error.AutoservRunError:
129 pass # no files to pull back, nothing we can do