mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 1 | import os, shutil, tempfile, logging |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 2 | |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 3 | import common |
| 4 | from autotest_lib.client.common_lib import utils, error, profiler_manager |
Prathmesh Prabhu | 8dacdb1 | 2017-09-01 12:12:18 -0700 | [diff] [blame] | 5 | from autotest_lib.server import profiler, autotest, standalone_profiler |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 6 | |
| 7 | |
Luis Lozano | 6b784d0 | 2015-07-21 00:24:17 +0000 | [diff] [blame] | 8 | PROFILER_TMPDIR = '/tmp/profilers' |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 9 | |
| 10 | |
| 11 | def get_profiler_results_dir(autodir): |
| 12 | """ |
| 13 | Given the directory of the autotest client used to run a profiler, |
| 14 | return the remote path where profiler results will be stored. |
| 15 | """ |
mbligh | 9e44a45 | 2010-04-08 18:20:25 +0000 | [diff] [blame] | 16 | return os.path.join(autodir, 'results', 'default', 'profiler_sync', |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 17 | 'profiling') |
| 18 | |
| 19 | |
| 20 | def get_profiler_log_path(autodir): |
| 21 | """ |
| 22 | Given the directory of a profiler client, find the client log path. |
| 23 | """ |
| 24 | return os.path.join(autodir, 'results', 'default', 'debug', 'client.DEBUG') |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 25 | |
| 26 | |
| 27 | class profilers(profiler_manager.profiler_manager): |
jadmanski | cb0e161 | 2009-02-27 18:03:10 +0000 | [diff] [blame] | 28 | def __init__(self, job): |
| 29 | super(profilers, self).__init__(job) |
| 30 | self.add_log = {} |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 31 | self.start_delay = 0 |
| 32 | # maps hostname to (host object, autotest.Autotest object, Autotest |
| 33 | # install dir), where the host object is the one created specifically |
| 34 | # for profiling |
| 35 | self.installed_hosts = {} |
| 36 | self.current_test = None |
| 37 | |
| 38 | |
| 39 | def set_start_delay(self, start_delay): |
| 40 | self.start_delay = start_delay |
jadmanski | cb0e161 | 2009-02-27 18:03:10 +0000 | [diff] [blame] | 41 | |
| 42 | |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 43 | def load_profiler(self, profiler_name, args, dargs): |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 44 | newprofiler = profiler.profiler_proxy(profiler_name) |
jadmanski | 043e113 | 2008-11-19 17:10:32 +0000 | [diff] [blame] | 45 | newprofiler.initialize(*args, **dargs) |
| 46 | newprofiler.setup(*args, **dargs) # lazy setup is done client-side |
| 47 | return newprofiler |
jadmanski | 4f90925 | 2008-12-01 20:47:10 +0000 | [diff] [blame] | 48 | |
| 49 | |
jadmanski | cb0e161 | 2009-02-27 18:03:10 +0000 | [diff] [blame] | 50 | def add(self, profiler, *args, **dargs): |
| 51 | super(profilers, self).add(profiler, *args, **dargs) |
| 52 | self.add_log[profiler] = (args, dargs) |
| 53 | |
| 54 | |
| 55 | def delete(self, profiler): |
| 56 | super(profilers, self).delete(profiler) |
mbligh | 30cf9dd | 2009-11-06 03:11:52 +0000 | [diff] [blame] | 57 | if profiler in self.add_log: |
| 58 | del self.add_log[profiler] |
jadmanski | cb0e161 | 2009-02-27 18:03:10 +0000 | [diff] [blame] | 59 | |
| 60 | |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 61 | def _install_clients(self): |
| 62 | """ |
| 63 | Install autotest on any current job hosts. |
| 64 | """ |
Prathmesh Prabhu | 8dacdb1 | 2017-09-01 12:12:18 -0700 | [diff] [blame] | 65 | in_use_hosts = dict() |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 66 | # find hosts in use but not used by us |
| 67 | for host in self.job.hosts: |
Fang Deng | e341433 | 2013-12-13 15:42:34 -0800 | [diff] [blame] | 68 | if host.hostname not in self.job.machines: |
| 69 | # job.hosts include all host instances created on the fly. |
| 70 | # We only care DUTs in job.machines which are |
| 71 | # piped in from autoserv -m option. |
| 72 | continue |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 73 | autodir = host.get_autodir() |
| 74 | if not (autodir and autodir.startswith(PROFILER_TMPDIR)): |
Prathmesh Prabhu | 8dacdb1 | 2017-09-01 12:12:18 -0700 | [diff] [blame] | 75 | in_use_hosts[host.hostname] = host |
| 76 | logging.debug('Hosts currently in use: %s', set(in_use_hosts)) |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 77 | |
| 78 | # determine what valid host objects we already have installed |
| 79 | profiler_hosts = set() |
| 80 | for host, at, profiler_dir in self.installed_hosts.values(): |
| 81 | if host.path_exists(profiler_dir): |
| 82 | profiler_hosts.add(host.hostname) |
| 83 | else: |
| 84 | # the profiler was wiped out somehow, drop this install |
| 85 | logging.warning('The profiler client on %s at %s was deleted', |
| 86 | host.hostname, profiler_dir) |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 87 | del self.installed_hosts[host.hostname] |
| 88 | logging.debug('Hosts with profiler clients already installed: %s', |
| 89 | profiler_hosts) |
| 90 | |
| 91 | # install autotest on any new hosts in use |
Prathmesh Prabhu | 8dacdb1 | 2017-09-01 12:12:18 -0700 | [diff] [blame] | 92 | for hostname in set(in_use_hosts) - profiler_hosts: |
| 93 | host = in_use_hosts[hostname] |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 94 | tmp_dir = host.get_tmp_dir(parent=PROFILER_TMPDIR) |
| 95 | at = autotest.Autotest(host) |
| 96 | at.install_no_autoserv(autodir=tmp_dir) |
| 97 | self.installed_hosts[host.hostname] = (host, at, tmp_dir) |
| 98 | |
| 99 | # drop any installs from hosts no longer in job.hosts |
Prathmesh Prabhu | 8dacdb1 | 2017-09-01 12:12:18 -0700 | [diff] [blame] | 100 | for hostname in profiler_hosts - set(in_use_hosts): |
| 101 | del self.installed_hosts[hostname] |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 102 | |
| 103 | |
| 104 | def _get_hosts(self, host=None): |
| 105 | """ |
| 106 | Returns a list of (Host, Autotest, install directory) tuples for hosts |
| 107 | currently supported by this profiler. The returned Host object is always |
| 108 | the one created by this profiler, regardless of what's passed in. If |
| 109 | 'host' is not None, all entries not matching that host object are |
| 110 | filtered out of the list. |
| 111 | """ |
| 112 | if host is None: |
| 113 | return self.installed_hosts.values() |
| 114 | if host.hostname in self.installed_hosts: |
| 115 | return [self.installed_hosts[host.hostname]] |
| 116 | return [] |
| 117 | |
| 118 | |
| 119 | def _get_local_profilers_dir(self, test, hostname): |
mbligh | fc3da5b | 2010-01-06 18:37:22 +0000 | [diff] [blame] | 120 | in_machine_dir = ( |
| 121 | os.path.basename(test.job.resultdir) in test.job.machines) |
| 122 | if len(test.job.machines) > 1 and not in_machine_dir: |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 123 | local_dir = os.path.join(test.profdir, hostname) |
| 124 | if not os.path.exists(local_dir): |
| 125 | os.makedirs(local_dir) |
| 126 | else: |
| 127 | local_dir = test.profdir |
| 128 | |
| 129 | return local_dir |
| 130 | |
| 131 | |
| 132 | def _get_failure_logs(self, autodir, test, host): |
| 133 | """ |
| 134 | Collect the client logs from a profiler run and put them in a |
| 135 | file named failure-*.log. |
| 136 | """ |
| 137 | try: |
| 138 | fd, path = tempfile.mkstemp(suffix='.log', prefix='failure-', |
| 139 | dir=self._get_local_profilers_dir(test, host.hostname)) |
| 140 | os.close(fd) |
| 141 | host.get_file(get_profiler_log_path(autodir), path) |
mbligh | be3238b | 2010-01-26 21:57:19 +0000 | [diff] [blame] | 142 | # try to collect any partial profiler logs |
| 143 | self._get_profiler_logs(autodir, test, host) |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 144 | except (error.AutotestError, error.AutoservError): |
| 145 | logging.exception('Profiler failure log collection failed') |
| 146 | # swallow the exception so that we don't override an existing |
| 147 | # exception being thrown |
| 148 | |
| 149 | |
| 150 | def _get_all_failure_logs(self, test, hosts): |
| 151 | for host, at, autodir in hosts: |
| 152 | self._get_failure_logs(autodir, test, host) |
| 153 | |
| 154 | |
mbligh | be3238b | 2010-01-26 21:57:19 +0000 | [diff] [blame] | 155 | def _get_profiler_logs(self, autodir, test, host): |
| 156 | results_dir = get_profiler_results_dir(autodir) |
| 157 | local_dir = self._get_local_profilers_dir(test, host.hostname) |
| 158 | |
| 159 | self.job.remove_client_log(host.hostname, results_dir, local_dir) |
| 160 | |
| 161 | tempdir = tempfile.mkdtemp(dir=self.job.tmpdir) |
| 162 | try: |
| 163 | host.get_file(results_dir + '/', tempdir) |
| 164 | except error.AutoservRunError: |
| 165 | pass # no files to pull back, nothing we can do |
| 166 | utils.merge_trees(tempdir, local_dir) |
| 167 | shutil.rmtree(tempdir, ignore_errors=True) |
| 168 | |
| 169 | |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 170 | def _run_clients(self, test, hosts): |
| 171 | """ |
| 172 | We initialize the profilers just before start because only then we |
| 173 | know all the hosts involved. |
| 174 | """ |
| 175 | |
| 176 | hostnames = [host_info[0].hostname for host_info in hosts] |
| 177 | profilers_args = [(p.name, p.args, p.dargs) |
| 178 | for p in self.list] |
| 179 | |
| 180 | for host, at, autodir in hosts: |
| 181 | control_script = standalone_profiler.generate_test(hostnames, |
| 182 | host.hostname, |
| 183 | profilers_args, |
| 184 | 180, None) |
| 185 | try: |
| 186 | at.run(control_script, background=True) |
| 187 | except Exception: |
| 188 | self._get_failure_logs(autodir, test, host) |
| 189 | raise |
| 190 | |
| 191 | remote_results_dir = get_profiler_results_dir(autodir) |
| 192 | local_results_dir = self._get_local_profilers_dir(test, |
| 193 | host.hostname) |
| 194 | self.job.add_client_log(host.hostname, remote_results_dir, |
| 195 | local_results_dir) |
| 196 | |
| 197 | try: |
| 198 | # wait for the profilers to be added |
| 199 | standalone_profiler.wait_for_profilers(hostnames) |
| 200 | except Exception: |
| 201 | self._get_all_failure_logs(test, hosts) |
| 202 | raise |
| 203 | |
| 204 | |
| 205 | def before_start(self, test, host=None): |
| 206 | # create host objects and install the needed clients |
| 207 | # so later in start() we don't spend too much time |
| 208 | self._install_clients() |
| 209 | self._run_clients(test, self._get_hosts(host)) |
| 210 | |
| 211 | |
| 212 | def start(self, test, host=None): |
| 213 | hosts = self._get_hosts(host) |
| 214 | |
| 215 | # wait for the profilers to start |
| 216 | hostnames = [host_info[0].hostname for host_info in hosts] |
| 217 | try: |
| 218 | standalone_profiler.start_profilers(hostnames) |
| 219 | except Exception: |
| 220 | self._get_all_failure_logs(test, hosts) |
| 221 | raise |
| 222 | |
| 223 | self.current_test = test |
| 224 | |
| 225 | |
| 226 | def stop(self, test): |
| 227 | assert self.current_test == test |
| 228 | |
| 229 | hosts = self._get_hosts() |
| 230 | # wait for the profilers to stop |
| 231 | hostnames = [host_info[0].hostname for host_info in hosts] |
| 232 | try: |
| 233 | standalone_profiler.stop_profilers(hostnames) |
| 234 | except Exception: |
| 235 | self._get_all_failure_logs(test, hosts) |
| 236 | raise |
| 237 | |
| 238 | |
| 239 | def report(self, test, host=None): |
| 240 | assert self.current_test == test |
| 241 | |
| 242 | hosts = self._get_hosts(host) |
| 243 | # when running on specific hosts we cannot wait for the other |
| 244 | # hosts to sync with us |
| 245 | if not host: |
| 246 | hostnames = [host_info[0].hostname for host_info in hosts] |
| 247 | try: |
| 248 | standalone_profiler.finish_profilers(hostnames) |
| 249 | except Exception: |
| 250 | self._get_all_failure_logs(test, hosts) |
| 251 | raise |
| 252 | |
| 253 | # pull back all the results |
| 254 | for host, at, autodir in hosts: |
mbligh | be3238b | 2010-01-26 21:57:19 +0000 | [diff] [blame] | 255 | self._get_profiler_logs(autodir, test, host) |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 256 | |
| 257 | |
jadmanski | 4f90925 | 2008-12-01 20:47:10 +0000 | [diff] [blame] | 258 | def handle_reboot(self, host): |
mbligh | 15971eb | 2009-12-29 02:55:23 +0000 | [diff] [blame] | 259 | if self.current_test: |
| 260 | test = self.current_test |
| 261 | for profiler in self.list: |
| 262 | if not profiler.supports_reboot: |
| 263 | msg = 'profiler %s does not support rebooting during tests' |
| 264 | msg %= profiler.name |
| 265 | self.job.record('WARN', os.path.basename(test.outputdir), |
| 266 | None, msg) |
| 267 | |
| 268 | self.report(test, host) |
| 269 | self.before_start(test, host) |
| 270 | self.start(test, host) |