mbligh | b3c0c91 | 2008-11-27 00:32:45 +0000 | [diff] [blame^] | 1 | import os, itertools |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 2 | from autotest_lib.server import autotest |
| 3 | |
| 4 | |
mbligh | b3c0c91 | 2008-11-27 00:32:45 +0000 | [diff] [blame^] | 5 | PROFILER_TMPDIR = "/tmp/profilers" |
| 6 | |
| 7 | |
| 8 | # control file template for running a job that uses profiler 'name' |
| 9 | run_profiler_control = """\ |
| 10 | job.profilers.add(%s) |
| 11 | job.run_test("profiler_test") |
| 12 | job.profilers.delete(%r) |
| 13 | """ |
| 14 | |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 15 | |
| 16 | def 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 | |
| 38 | def 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 | |
mbligh | b3c0c91 | 2008-11-27 00:32:45 +0000 | [diff] [blame^] | 49 | def 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 | |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 56 | class 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. """ |
mbligh | b3c0c91 | 2008-11-27 00:32:45 +0000 | [diff] [blame^] | 68 | 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)) |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 71 | 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: |
mbligh | b3c0c91 | 2008-11-27 00:32:45 +0000 | [diff] [blame^] | 74 | tmp_dir = host.get_tmp_dir(parent=PROFILER_TMPDIR) |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 75 | 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 | |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 83 | def initialize(self, *args, **dargs): |
| 84 | validate_args(args) |
| 85 | validate_args(dargs) |
mbligh | b3c0c91 | 2008-11-27 00:32:45 +0000 | [diff] [blame^] | 86 | 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) |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 101 | |
| 102 | |
| 103 | def start(self, test): |
mbligh | b3c0c91 | 2008-11-27 00:32:45 +0000 | [diff] [blame^] | 104 | 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") |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 110 | |
| 111 | |
| 112 | def stop(self, test): |
mbligh | b3c0c91 | 2008-11-27 00:32:45 +0000 | [diff] [blame^] | 113 | self._signal_clients("stop") |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 114 | |
| 115 | |
| 116 | def report(self, test): |
mbligh | b3c0c91 | 2008-11-27 00:32:45 +0000 | [diff] [blame^] | 117 | 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 |