blob: b2e0178a5b12afb1f7c2040289e3be83e8ca838b [file] [log] [blame]
Thieu Lec16253b2011-03-03 11:13:54 -08001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6import os
7from autotest_lib.client.common_lib import utils as client_utils
Alex Miller24c27c12012-08-09 10:24:24 -07008from autotest_lib.client.common_lib import error
Chris Masonebafbbb02012-05-16 13:41:36 -07009from autotest_lib.client.common_lib.cros import dev_server
Chris Masoned931e8c2011-11-09 13:17:16 -080010from autotest_lib.client.cros import constants
Chris Masone44e4d6c2012-08-15 14:25:53 -070011from autotest_lib.server.cros.dynamic_suite.constants import JOB_BUILD_KEY
Thieu Lec16253b2011-03-03 11:13:54 -080012from autotest_lib.server import utils
13
Chris Sosaaccb5ce2012-08-30 17:29:15 -070014
Thieu Lec16253b2011-03-03 11:13:54 -080015def generate_minidump_stacktrace(minidump_path):
16 """
17 Generates a stacktrace for the specified minidump.
18
19 This function expects the debug symbols to reside under:
20 /build/<board>/usr/lib/debug
Chris Masonebafbbb02012-05-16 13:41:36 -070021
22 @param minidump_path: absolute path to minidump to by symbolicated.
23 @raise client_utils.error.CmdError if minidump_stackwalk return code != 0.
Thieu Lec16253b2011-03-03 11:13:54 -080024 """
25 symbol_dir = '%s/../../../lib/debug' % utils.get_server_dir()
Alex Miller24c27c12012-08-09 10:24:24 -070026 logging.info('symbol_dir: %s', symbol_dir)
Michael Krebs30058702012-09-25 15:37:04 -070027 client_utils.run('minidump_stackwalk "%s" "%s" > "%s.txt"' %
Chris Masonebafbbb02012-05-16 13:41:36 -070028 (minidump_path, symbol_dir, minidump_path))
29
30
31def symbolicate_minidump_with_devserver(minidump_path, resultdir):
32 """
33 Generates a stack trace for the specified minidump by consulting devserver.
34
35 This function assumes the debug symbols have been staged on the devserver.
36
37 @param minidump_path: absolute path to minidump to by symbolicated.
38 @param resultdir: server job's result directory.
39 @raise DevServerException upon failure, HTTP or otherwise.
40 """
41 # First, look up what build we tested. If we can't find this, we can't
42 # get the right debug symbols, so we might as well give up right now.
43 keyvals = client_utils.read_keyval(resultdir)
Chris Masone44e4d6c2012-08-15 14:25:53 -070044 if JOB_BUILD_KEY not in keyvals:
Chris Masonebafbbb02012-05-16 13:41:36 -070045 raise dev_server.DevServerException(
46 'Cannot determine build being tested.')
47
Chris Sosaaccb5ce2012-08-30 17:29:15 -070048 devserver = dev_server.CrashServer.resolve(keyvals[JOB_BUILD_KEY])
Chris Masoneaa10f8e2012-05-15 13:34:21 -070049 trace_text = devserver.symbolicate_dump(
Chris Masone44e4d6c2012-08-15 14:25:53 -070050 minidump_path, keyvals[JOB_BUILD_KEY])
Chris Masonebafbbb02012-05-16 13:41:36 -070051 if not trace_text:
52 raise dev_server.DevServerException('Unknown error!!')
53 with open(minidump_path + '.txt', 'w') as trace_file:
54 trace_file.write(trace_text)
Thieu Lec16253b2011-03-03 11:13:54 -080055
56
Chris Masoned931e8c2011-11-09 13:17:16 -080057def find_and_generate_minidump_stacktraces(host_resultdir):
Thieu Lec16253b2011-03-03 11:13:54 -080058 """
59 Finds all minidump files and generates a stack trace for each.
60
61 Enumerates all files under the test results directory (recursively)
62 and generates a stack trace file for the minidumps. Minidump files are
63 identified as files with .dmp extension. The stack trace filename is
64 composed by appending the .txt extension to the minidump filename.
Alex Miller24c27c12012-08-09 10:24:24 -070065
beeps71bf47c2013-11-14 20:44:30 -080066 @param host_resultdir: Directory to walk looking for dmp files.
67
Alex Miller24c27c12012-08-09 10:24:24 -070068 @returns The list of generated minidumps.
Thieu Lec16253b2011-03-03 11:13:54 -080069 """
Alex Miller24c27c12012-08-09 10:24:24 -070070 minidumps = []
Thieu Lec16253b2011-03-03 11:13:54 -080071 for dir, subdirs, files in os.walk(host_resultdir):
72 for file in files:
73 if not file.endswith('.dmp'):
74 continue
75 minidump = os.path.join(dir, file)
Chris Masonebafbbb02012-05-16 13:41:36 -070076
77 # First, try to symbolicate locally.
78 try:
79 generate_minidump_stacktrace(minidump)
Chris Masone4245a732012-04-30 14:52:10 -070080 logging.info('Generated stack trace for dump %s', minidump)
Alex Miller24c27c12012-08-09 10:24:24 -070081 minidumps.append(minidump)
Chris Masonebafbbb02012-05-16 13:41:36 -070082 continue
83 except client_utils.error.CmdError as err:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -070084 logging.warning('Failed to generate stack trace locally for '
Chris Masonebafbbb02012-05-16 13:41:36 -070085 'dump %s (rc=%d):\n%r',
86 minidump, err.result_obj.exit_status, err)
87
88 # If that did not succeed, try to symbolicate using the dev server.
89 try:
Alex Miller24c27c12012-08-09 10:24:24 -070090 minidumps.append(minidump)
Chris Masonebafbbb02012-05-16 13:41:36 -070091 symbolicate_minidump_with_devserver(minidump, host_resultdir)
92 logging.info('Generated stack trace for dump %s', minidump)
93 continue
94 except dev_server.DevServerException as e:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -070095 logging.warning('Failed to generate stack trace on devserver for '
Chris Masonebafbbb02012-05-16 13:41:36 -070096 'dump %s:\n%r', minidump, e)
Alex Miller24c27c12012-08-09 10:24:24 -070097 return minidumps
Thieu Lec16253b2011-03-03 11:13:54 -080098
99
Chris Masoned931e8c2011-11-09 13:17:16 -0800100def fetch_orphaned_crashdumps(host, host_resultdir):
Alex Miller24c27c12012-08-09 10:24:24 -0700101 """
102 Copy all of the crashes in the crash directory over to the results folder.
103
104 @param host A host object of the device we're to pull crashes from.
105 @param host_resultdir The result directory for this host for this test run.
106 @return The list of minidumps that we pulled back from the host.
107 """
108 minidumps = []
Chris Masoned931e8c2011-11-09 13:17:16 -0800109 for file in host.list_files_glob(os.path.join(constants.CRASH_DIR, '*')):
Alex Miller24c27c12012-08-09 10:24:24 -0700110 logging.info('Collecting %s...', file)
beeps54dfb0c2013-09-11 11:53:38 -0700111 host.get_file(file, host_resultdir, preserve_perm=False)
112 minidumps.append(file)
Alex Miller24c27c12012-08-09 10:24:24 -0700113 return minidumps
Chris Masoned931e8c2011-11-09 13:17:16 -0800114
115
Thieu Lec16253b2011-03-03 11:13:54 -0800116def get_site_crashdumps(host, test_start_time):
Alex Miller24c27c12012-08-09 10:24:24 -0700117 """
118 Copy all of the crashdumps from a host to the results directory.
119
120 @param host The host object from which to pull crashes
121 @param test_start_time When the test we just ran started.
122 @return A list of all the minidumps
123 """
124 host_resultdir = getattr(getattr(host, 'job', None), 'resultdir', None)
125 infodir = os.path.join(host_resultdir, 'crashinfo.%s' % host.hostname)
Chris Masoned931e8c2011-11-09 13:17:16 -0800126 if not os.path.exists(infodir):
127 os.mkdir(infodir)
Alex Miller24c27c12012-08-09 10:24:24 -0700128
129 # TODO(milleral): handle orphans differently. crosbug.com/38202
beeps54dfb0c2013-09-11 11:53:38 -0700130 try:
131 orphans = fetch_orphaned_crashdumps(host, infodir)
132 except Exception as e:
133 orphans = []
134 logging.warning('Collection of orphaned crash dumps failed %s', e)
135
Alex Miller24c27c12012-08-09 10:24:24 -0700136 minidumps = find_and_generate_minidump_stacktraces(host_resultdir)
beeps71bf47c2013-11-14 20:44:30 -0800137
138 # Record all crashdumps in status.log of the job:
139 # - If one server job runs several client jobs we will only record
140 # crashdumps in the status.log of the high level server job.
141 # - We will record these crashdumps whether or not we successfully
142 # symbolicate them.
143 if host.job and minidumps or orphans:
144 host.job.record('INFO', None, None, 'Start crashcollection record')
145 for minidump in minidumps:
146 host.job.record('INFO', None, 'New Crash Dump', minidump)
147 for orphan in orphans:
148 host.job.record('INFO', None, 'Orphaned Crash Dump', orphan)
149 host.job.record('INFO', None, None, 'End crashcollection record')
150
Alex Miller24c27c12012-08-09 10:24:24 -0700151 orphans.extend(minidumps)
152
153 for minidump in orphans:
154 report_bug_from_crash(host, minidump)
155
156 return orphans
157
158
159def find_packages_of(host, exec_name):
160 """
161 Find the package that an executable came from.
162
163 @param host A host object that has the executable.
164 @param exec_name The name of the executable.
165 @return The name of the package that installed the executable.
166 """
167 packages = []
168
169 # TODO(milleral): It would be significantly faster to iterate through
170 # $PATH and run this than to point it at all of /
171 find = host.run('find / -executable -type f -name %s' % exec_name)
172 for full_path in find.stdout.splitlines():
173 # TODO(milleral): This currently shows scary looking error messages
174 # in the debug logs via stderr. We only look at stdout, so those
175 # get filtered, but it would be good to silence them.
176 portageq = host.run('portageq owners / %s' % full_path)
177 if portageq.stdout:
178 packages.append(portageq.stdout.splitlines()[0].strip())
179
180 # TODO(milleral): This chunk of code is here to verify that mapping
181 # executable name to package gives you one and only one package.
182 # It is highly questionable as to if this should be left in the
183 # production version of this code or not.
184 if len(packages) == 0:
185 raise error.NoUniquePackageFound('no package for %s' % exec_name)
186 if len(packages) > 1:
187 # Running through all of /usr/bin in the chroot showed this should
188 # never happen, but still maybe possible?
beepscb6f1e22013-06-28 19:14:10 -0700189 raise error.NoUniquePackageFound('Crash detection found more than one'
Alex Miller24c27c12012-08-09 10:24:24 -0700190 'package for %s: %s' % exec_name, packages)
191
192 # |len(packages) == 1| at this point, as it should be anyway
193 return packages[0]
194
195
196def report_bug_from_crash(host, minidump_path):
197 """
198 Given a host to query and a minidump, file a bug about the crash.
199
200 @param host A host object that is where the dump came from
201 @param minidump_path The path to the dump file that should be reported.
202 """
203 # TODO(milleral): Once this has actually been tested, remove the
204 # try/except. In the meantime, let's make sure nothing dies because of
205 # the fact that this code isn't very heavily tested.
206 try:
207 meta_path = os.path.splitext(minidump_path)[0] + '.meta'
208 with open(meta_path, 'r') as f:
209 for line in f.readlines():
210 parts = line.split('=')
211 if parts[0] == 'exec_name':
212 packages = find_packages_of(host, parts[1].strip())
beepscb6f1e22013-06-28 19:14:10 -0700213 logging.info('Would report crash on %s.', packages)
Alex Miller24c27c12012-08-09 10:24:24 -0700214 break
215 except Exception as e:
beepscb6f1e22013-06-28 19:14:10 -0700216 logging.warning('Crash detection failed with: %s', e)