xref: /aosp_15_r20/external/autotest/server/site_crashcollect.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Liimport logging
6*9c5db199SXin Liimport os
7*9c5db199SXin Liimport re
8*9c5db199SXin Liimport shutil
9*9c5db199SXin Lifrom autotest_lib.client.common_lib import utils as client_utils
10*9c5db199SXin Lifrom autotest_lib.client.common_lib.cros import dev_server
11*9c5db199SXin Lifrom autotest_lib.client.common_lib.cros import retry
12*9c5db199SXin Lifrom autotest_lib.client.cros import constants
13*9c5db199SXin Lifrom autotest_lib.server.cros.dynamic_suite.constants import JOB_BUILD_KEY
14*9c5db199SXin Lifrom autotest_lib.server.crashcollect import collect_log_file
15*9c5db199SXin Lifrom autotest_lib.server import utils
16*9c5db199SXin Li
17*9c5db199SXin Litry:
18*9c5db199SXin Li    from autotest_lib.utils.frozen_chromite.lib import metrics
19*9c5db199SXin Liexcept ImportError:
20*9c5db199SXin Li    metrics = client_utils.metrics_mock
21*9c5db199SXin Li
22*9c5db199SXin Li
23*9c5db199SXin Lidef generate_minidump_stacktrace(minidump_path):
24*9c5db199SXin Li    """
25*9c5db199SXin Li    Generates a stacktrace for the specified minidump.
26*9c5db199SXin Li
27*9c5db199SXin Li    This function expects the debug symbols to reside under:
28*9c5db199SXin Li        /build/<board>/usr/lib/debug
29*9c5db199SXin Li
30*9c5db199SXin Li    @param minidump_path: absolute path to minidump to by symbolicated.
31*9c5db199SXin Li    @raise client_utils.error.CmdError if minidump_stackwalk return code != 0.
32*9c5db199SXin Li    """
33*9c5db199SXin Li    symbol_dir = '%s/../../../lib/debug' % utils.get_server_dir()
34*9c5db199SXin Li    logging.info('symbol_dir: %s', symbol_dir)
35*9c5db199SXin Li    client_utils.run('minidump_stackwalk "%s" "%s" > "%s.txt"' %
36*9c5db199SXin Li                     (minidump_path, symbol_dir, minidump_path))
37*9c5db199SXin Li
38*9c5db199SXin Li
39*9c5db199SXin Lidef _resolve_crashserver():
40*9c5db199SXin Li    """
41*9c5db199SXin Li    Attempts to find a devserver / crashserver that has capacity to
42*9c5db199SXin Li    symbolicate a crashdump.
43*9c5db199SXin Li
44*9c5db199SXin Li    @raises DevServerException if no server with capacity could be found.
45*9c5db199SXin Li    @returns Hostname of resolved server, if found.
46*9c5db199SXin Li    """
47*9c5db199SXin Li    crashserver_name = dev_server.get_least_loaded_devserver(
48*9c5db199SXin Li            devserver_type=dev_server.CrashServer)
49*9c5db199SXin Li    if not crashserver_name:
50*9c5db199SXin Li        metrics.Counter('chromeos/autotest/crashcollect/could_not_resolve'
51*9c5db199SXin Li                        ).increment()
52*9c5db199SXin Li        raise dev_server.DevServerException(
53*9c5db199SXin Li                'No crash server has the capacity to symbolicate the dump.')
54*9c5db199SXin Li    else:
55*9c5db199SXin Li        metrics.Counter('chromeos/autotest/crashcollect/resolved'
56*9c5db199SXin Li                        ).increment(fields={'crash_server': crashserver_name})
57*9c5db199SXin Li    return crashserver_name
58*9c5db199SXin Li
59*9c5db199SXin Li
60*9c5db199SXin Lidef _symbolicate_minidump_with_devserver(minidump_path, resultdir,
61*9c5db199SXin Li                                        crashserver_name):
62*9c5db199SXin Li    """
63*9c5db199SXin Li    Generates a stack trace for the specified minidump by consulting devserver.
64*9c5db199SXin Li
65*9c5db199SXin Li    This function assumes the debug symbols have been staged on the devserver.
66*9c5db199SXin Li
67*9c5db199SXin Li    @param minidump_path: absolute path to minidump to by symbolicated.
68*9c5db199SXin Li    @param resultdir: server job's result directory.
69*9c5db199SXin Li    @param crashserver_name: Name of crashserver to attempt to symbolicate with.
70*9c5db199SXin Li    @raise DevServerException upon failure, HTTP or otherwise.
71*9c5db199SXin Li    """
72*9c5db199SXin Li    # First, look up what build we tested.  If we can't find this, we can't
73*9c5db199SXin Li    # get the right debug symbols, so we might as well give up right now.
74*9c5db199SXin Li    keyvals = client_utils.read_keyval(resultdir)
75*9c5db199SXin Li    if JOB_BUILD_KEY not in keyvals:
76*9c5db199SXin Li        raise dev_server.DevServerException(
77*9c5db199SXin Li            'Cannot determine build being tested.')
78*9c5db199SXin Li
79*9c5db199SXin Li    devserver = dev_server.CrashServer(crashserver_name)
80*9c5db199SXin Li
81*9c5db199SXin Li    with metrics.SecondsTimer(
82*9c5db199SXin Li            'chromeos/autotest/crashcollect/symbolicate_duration',
83*9c5db199SXin Li            fields={'crash_server': crashserver_name}):
84*9c5db199SXin Li        trace_text = devserver.symbolicate_dump(minidump_path,
85*9c5db199SXin Li                                                keyvals[JOB_BUILD_KEY])
86*9c5db199SXin Li
87*9c5db199SXin Li    if not trace_text:
88*9c5db199SXin Li        raise dev_server.DevServerException('Unknown error!!')
89*9c5db199SXin Li    with open(minidump_path + '.txt', 'w') as trace_file:
90*9c5db199SXin Li        trace_file.write(trace_text)
91*9c5db199SXin Li
92*9c5db199SXin Lidef generate_stacktrace_for_file(minidump, host_resultdir):
93*9c5db199SXin Li    """
94*9c5db199SXin Li    Tries to generate a stack trace for the file located at |minidump|.
95*9c5db199SXin Li    @param minidump: path to minidump file to generate the stacktrace for.
96*9c5db199SXin Li    @param host_resultdir: server job's result directory.
97*9c5db199SXin Li    """
98*9c5db199SXin Li    # First, try to symbolicate locally.
99*9c5db199SXin Li    try:
100*9c5db199SXin Li        logging.info('Trying to generate stack trace locally for %s', minidump)
101*9c5db199SXin Li        generate_minidump_stacktrace(minidump)
102*9c5db199SXin Li        logging.info('Generated stack trace for dump %s', minidump)
103*9c5db199SXin Li        return
104*9c5db199SXin Li    except client_utils.error.CmdError as err:
105*9c5db199SXin Li        logging.info('Failed to generate stack trace locally for '
106*9c5db199SXin Li                     'dump %s (rc=%d):\n%r',
107*9c5db199SXin Li                     minidump, err.result_obj.exit_status, err)
108*9c5db199SXin Li
109*9c5db199SXin Li    # If that did not succeed, try to symbolicate using the dev server.
110*9c5db199SXin Li    try:
111*9c5db199SXin Li        logging.info('Generating stack trace using devserver for %s', minidump)
112*9c5db199SXin Li        crashserver_name = _resolve_crashserver()
113*9c5db199SXin Li        args = (minidump, host_resultdir, crashserver_name)
114*9c5db199SXin Li        is_timeout, _ = retry.timeout(_symbolicate_minidump_with_devserver,
115*9c5db199SXin Li                                      args=args,
116*9c5db199SXin Li                                      timeout_sec=600)
117*9c5db199SXin Li        if is_timeout:
118*9c5db199SXin Li            logging.info('Generating stack trace timed out for dump %s',
119*9c5db199SXin Li                         minidump)
120*9c5db199SXin Li            metrics.Counter(
121*9c5db199SXin Li                    'chromeos/autotest/crashcollect/symbolicate_timed_out'
122*9c5db199SXin Li            ).increment(fields={'crash_server': crashserver_name})
123*9c5db199SXin Li        else:
124*9c5db199SXin Li            logging.info('Generated stack trace for dump %s', minidump)
125*9c5db199SXin Li            return
126*9c5db199SXin Li    except dev_server.DevServerException as e:
127*9c5db199SXin Li        logging.info('Failed to generate stack trace on devserver for dump '
128*9c5db199SXin Li                     '%s:\n%r', minidump, e)
129*9c5db199SXin Li
130*9c5db199SXin Li    # Symbolicating failed.
131*9c5db199SXin Li    logging.warning('Failed to generate stack trace for %s (see info logs)',
132*9c5db199SXin Li                    minidump)
133*9c5db199SXin Li
134*9c5db199SXin Lidef find_and_generate_minidump_stacktraces(host_resultdir):
135*9c5db199SXin Li    """
136*9c5db199SXin Li    Finds all minidump files and generates a stack trace for each.
137*9c5db199SXin Li
138*9c5db199SXin Li    Enumerates all files under the test results directory (recursively)
139*9c5db199SXin Li    and generates a stack trace file for the minidumps.  Minidump files are
140*9c5db199SXin Li    identified as files with .dmp extension.  The stack trace filename is
141*9c5db199SXin Li    composed by appending the .txt extension to the minidump filename.
142*9c5db199SXin Li
143*9c5db199SXin Li    @param host_resultdir: Directory to walk looking for dmp files.
144*9c5db199SXin Li
145*9c5db199SXin Li    @returns The list of all found minidump files. Each dump may or may not have
146*9c5db199SXin Li             been symbolized.
147*9c5db199SXin Li    """
148*9c5db199SXin Li    minidumps = []
149*9c5db199SXin Li    for file in _find_crashdumps(host_resultdir):
150*9c5db199SXin Li        generate_stacktrace_for_file(file, host_resultdir)
151*9c5db199SXin Li        minidumps.append(file)
152*9c5db199SXin Li    return minidumps
153*9c5db199SXin Li
154*9c5db199SXin Li
155*9c5db199SXin Lidef _find_crashdumps(host_resultdir):
156*9c5db199SXin Li    """Find crashdumps.
157*9c5db199SXin Li
158*9c5db199SXin Li    @param host_resultdir The result directory for this host for this test run.
159*9c5db199SXin Li    """
160*9c5db199SXin Li    for dir, subdirs, files in os.walk(host_resultdir):
161*9c5db199SXin Li        for file in files:
162*9c5db199SXin Li            if file.endswith('.dmp'):
163*9c5db199SXin Li                yield os.path.join(dir, file)
164*9c5db199SXin Li
165*9c5db199SXin Li
166*9c5db199SXin Lidef _find_orphaned_crashdumps(host):
167*9c5db199SXin Li    """Return file paths of crashdumps on host.
168*9c5db199SXin Li
169*9c5db199SXin Li    @param host A host object of the device.
170*9c5db199SXin Li    """
171*9c5db199SXin Li    return host.list_files_glob(os.path.join(constants.CRASH_DIR, '*'))
172*9c5db199SXin Li
173*9c5db199SXin Li
174*9c5db199SXin Lidef report_crashdumps(host):
175*9c5db199SXin Li    """Report on crashdumps for host.
176*9c5db199SXin Li
177*9c5db199SXin Li    This is run when no tests failed.  We don't process crashdumps in this
178*9c5db199SXin Li    case because of devserver load, but they should still be reported.
179*9c5db199SXin Li
180*9c5db199SXin Li    @param host A host object of the device we're to pull crashes from.
181*9c5db199SXin Li    """
182*9c5db199SXin Li    for crashfile in _find_orphaned_crashdumps(host):
183*9c5db199SXin Li        logging.warning('Host crashdump exists: %s', crashfile)
184*9c5db199SXin Li        host.job.record('INFO', None, None,
185*9c5db199SXin Li                        'Host crashdump exists: %s' % (crashfile,))
186*9c5db199SXin Li
187*9c5db199SXin Li    host_resultdir = _get_host_resultdir(host)
188*9c5db199SXin Li    for crashfile in _find_crashdumps(host_resultdir):
189*9c5db199SXin Li        logging.warning('Local crashdump exists: %s', crashfile)
190*9c5db199SXin Li        host.job.record('INFO', None, None,
191*9c5db199SXin Li                        'Local crashdump exists: %s' % (crashfile,))
192*9c5db199SXin Li
193*9c5db199SXin Li
194*9c5db199SXin Lidef fetch_orphaned_crashdumps(host, infodir):
195*9c5db199SXin Li    """
196*9c5db199SXin Li    Copy all of the crashes in the crash directory over to the results folder.
197*9c5db199SXin Li
198*9c5db199SXin Li    @param host A host object of the device we're to pull crashes from.
199*9c5db199SXin Li    @param infodir The directory to fetch crashdumps into.
200*9c5db199SXin Li    @return The list of minidumps that we pulled back from the host.
201*9c5db199SXin Li    """
202*9c5db199SXin Li    if not os.path.exists(infodir):
203*9c5db199SXin Li        os.mkdir(infodir)
204*9c5db199SXin Li    orphans = []
205*9c5db199SXin Li
206*9c5db199SXin Li    if not host.check_cached_up_status():
207*9c5db199SXin Li        logging.warning('Host %s did not answer to ping, skip fetching '
208*9c5db199SXin Li                        'orphaned crashdumps.', host.hostname)
209*9c5db199SXin Li        return orphans
210*9c5db199SXin Li
211*9c5db199SXin Li    try:
212*9c5db199SXin Li        for file in _find_orphaned_crashdumps(host):
213*9c5db199SXin Li            logging.info('Collecting %s...', file)
214*9c5db199SXin Li            collect_log_file(host, file, infodir, clean=False)
215*9c5db199SXin Li            orphans.append(file)
216*9c5db199SXin Li    except Exception as e:
217*9c5db199SXin Li        logging.warning('Collection of orphaned crash dumps failed %s', e)
218*9c5db199SXin Li    finally:
219*9c5db199SXin Li        # Delete infodir if we have no orphans
220*9c5db199SXin Li        if not orphans:
221*9c5db199SXin Li            logging.info('There are no orphaned crashes; deleting %s', infodir)
222*9c5db199SXin Li            os.rmdir(infodir)
223*9c5db199SXin Li    return orphans
224*9c5db199SXin Li
225*9c5db199SXin Li
226*9c5db199SXin Lidef _copy_to_debug_dir(host_resultdir, filename):
227*9c5db199SXin Li    """
228*9c5db199SXin Li    Copies a file to the debug dir under host_resultdir.
229*9c5db199SXin Li
230*9c5db199SXin Li    @param host_resultdir The result directory for this host for this test run.
231*9c5db199SXin Li    @param filename The full path of the file to copy to the debug folder.
232*9c5db199SXin Li    """
233*9c5db199SXin Li    debugdir = os.path.join(host_resultdir, 'debug')
234*9c5db199SXin Li    src = filename
235*9c5db199SXin Li    dst = os.path.join(debugdir, os.path.basename(filename))
236*9c5db199SXin Li
237*9c5db199SXin Li    try:
238*9c5db199SXin Li        shutil.copyfile(src, dst)
239*9c5db199SXin Li        logging.info('Copied %s to %s', src, dst)
240*9c5db199SXin Li    except IOError:
241*9c5db199SXin Li        logging.warning('Failed to copy %s to %s', src, dst)
242*9c5db199SXin Li
243*9c5db199SXin Li
244*9c5db199SXin Lidef _get_host_resultdir(host):
245*9c5db199SXin Li    """Get resultdir for host.
246*9c5db199SXin Li
247*9c5db199SXin Li    @param host A host object of the device we're to pull crashes from.
248*9c5db199SXin Li    """
249*9c5db199SXin Li    return getattr(getattr(host, 'job', None), 'resultdir', None)
250*9c5db199SXin Li
251*9c5db199SXin Li
252*9c5db199SXin Lidef get_host_infodir(host):
253*9c5db199SXin Li    """Get infodir for host.
254*9c5db199SXin Li
255*9c5db199SXin Li    @param host A host object of the device we're to pull crashes from.
256*9c5db199SXin Li    """
257*9c5db199SXin Li    host_resultdir = _get_host_resultdir(host)
258*9c5db199SXin Li    return os.path.join(host_resultdir, 'crashinfo.%s' % host.hostname)
259*9c5db199SXin Li
260*9c5db199SXin Li
261*9c5db199SXin Lidef get_site_crashdumps(host, test_start_time):
262*9c5db199SXin Li    """
263*9c5db199SXin Li    Copy all of the crashdumps from a host to the results directory.
264*9c5db199SXin Li
265*9c5db199SXin Li    @param host The host object from which to pull crashes
266*9c5db199SXin Li    @param test_start_time When the test we just ran started.
267*9c5db199SXin Li    @return A list of all the minidumps
268*9c5db199SXin Li    """
269*9c5db199SXin Li    host_resultdir = _get_host_resultdir(host)
270*9c5db199SXin Li    infodir = get_host_infodir(host)
271*9c5db199SXin Li
272*9c5db199SXin Li    orphans = fetch_orphaned_crashdumps(host, infodir)
273*9c5db199SXin Li    minidumps = find_and_generate_minidump_stacktraces(host_resultdir)
274*9c5db199SXin Li
275*9c5db199SXin Li    # Record all crashdumps in status.log of the job:
276*9c5db199SXin Li    # - If one server job runs several client jobs we will only record
277*9c5db199SXin Li    # crashdumps in the status.log of the high level server job.
278*9c5db199SXin Li    # - We will record these crashdumps whether or not we successfully
279*9c5db199SXin Li    # symbolicate them.
280*9c5db199SXin Li    if host.job and minidumps or orphans:
281*9c5db199SXin Li        host.job.record('INFO', None, None, 'Start crashcollection record')
282*9c5db199SXin Li        for minidump in minidumps:
283*9c5db199SXin Li            host.job.record('INFO', None, 'New Crash Dump', minidump)
284*9c5db199SXin Li        for orphan in orphans:
285*9c5db199SXin Li            host.job.record('INFO', None, 'Orphaned Crash Dump', orphan)
286*9c5db199SXin Li        host.job.record('INFO', None, None, 'End crashcollection record')
287*9c5db199SXin Li
288*9c5db199SXin Li    orphans.extend(minidumps)
289*9c5db199SXin Li
290*9c5db199SXin Li    for minidump in orphans:
291*9c5db199SXin Li        report_bug_from_crash(host, minidump)
292*9c5db199SXin Li
293*9c5db199SXin Li    # We copy Chrome crash information to the debug dir to assist debugging.
294*9c5db199SXin Li    # Since orphans occurred on a previous run, they are most likely not
295*9c5db199SXin Li    # relevant to the current failure, so we don't copy them.
296*9c5db199SXin Li    for minidump in minidumps:
297*9c5db199SXin Li        minidump_no_ext = os.path.splitext(minidump)[0]
298*9c5db199SXin Li        _copy_to_debug_dir(host_resultdir, minidump_no_ext + '.dmp.txt')
299*9c5db199SXin Li        _copy_to_debug_dir(host_resultdir, minidump_no_ext + '.log')
300*9c5db199SXin Li
301*9c5db199SXin Li    return orphans
302*9c5db199SXin Li
303*9c5db199SXin Li
304*9c5db199SXin Lidef find_package_of(host, exec_name):
305*9c5db199SXin Li    """
306*9c5db199SXin Li    Find the package that an executable came from.
307*9c5db199SXin Li
308*9c5db199SXin Li    @param host A host object that has the executable.
309*9c5db199SXin Li    @param exec_name Name of or path to executable.
310*9c5db199SXin Li    @return The name of the package that installed the executable.
311*9c5db199SXin Li    """
312*9c5db199SXin Li    # Run "portageq owners" on "host" to determine which package owns
313*9c5db199SXin Li    # "exec_name."  Portageq queue output consists of package names followed
314*9c5db199SXin Li    # tab-prefixed path names.  For example, owners of "python:"
315*9c5db199SXin Li    #
316*9c5db199SXin Li    # sys-devel/gdb-7.7.1-r2
317*9c5db199SXin Li    #         /usr/share/gdb/python
318*9c5db199SXin Li    # chromeos-base/dev-install-0.0.1-r711
319*9c5db199SXin Li    #         /usr/bin/python
320*9c5db199SXin Li    # dev-lang/python-2.7.3-r7
321*9c5db199SXin Li    #         /etc/env.d/python
322*9c5db199SXin Li    #
323*9c5db199SXin Li    # This gets piped into "xargs stat" to annotate each line with
324*9c5db199SXin Li    # information about the path, so we later can consider only packages
325*9c5db199SXin Li    # with executable files.  After annotation the above looks like:
326*9c5db199SXin Li    #
327*9c5db199SXin Li    # stat: cannot stat '@@@ sys-devel/gdb-7.7.1-r2 @@@': ...
328*9c5db199SXin Li    # stat: cannot stat '/usr/share/gdb/python': ...
329*9c5db199SXin Li    # stat: cannot stat '@@@ chromeos-base/dev-install-0.0.1-r711 @@@': ...
330*9c5db199SXin Li    # 755 -rwxr-xr-x /usr/bin/python
331*9c5db199SXin Li    # stat: cannot stat '@@@ dev-lang/python-2.7.3-r7 @@@': ...
332*9c5db199SXin Li    # 755 drwxr-xr-x /etc/env.d/python
333*9c5db199SXin Li    #
334*9c5db199SXin Li    # Package names are surrounded by "@@@" to facilitate parsing.  Lines
335*9c5db199SXin Li    # starting with an octal number were successfully annotated, because
336*9c5db199SXin Li    # the path existed on "host."
337*9c5db199SXin Li    # The above is then parsed to find packages which contain executable files
338*9c5db199SXin Li    # (not directories), in this case "chromeos-base/dev-install-0.0.1-r711."
339*9c5db199SXin Li    #
340*9c5db199SXin Li    # TODO(milleral): portageq can show scary looking error messages
341*9c5db199SXin Li    # in the debug logs via stderr. We only look at stdout, so those
342*9c5db199SXin Li    # get filtered, but it would be good to silence them.
343*9c5db199SXin Li    cmd = ('portageq owners / ' + exec_name +
344*9c5db199SXin Li            r'| sed -e "s/^[^\t].*/@@@ & @@@/" -e "s/^\t//"'
345*9c5db199SXin Li            r'| tr \\n \\0'
346*9c5db199SXin Li            ' | xargs -0 -r stat -L -c "%a %A %n" 2>&1')
347*9c5db199SXin Li    portageq = host.run(cmd, ignore_status=True)
348*9c5db199SXin Li
349*9c5db199SXin Li    # Parse into a set of names of packages containing an executable file.
350*9c5db199SXin Li    packages = set()
351*9c5db199SXin Li    pkg = ''
352*9c5db199SXin Li    pkg_re = re.compile('@@@ (.*) @@@')
353*9c5db199SXin Li    path_re = re.compile('^([0-7]{3,}) (.)')
354*9c5db199SXin Li    for line in portageq.stdout.splitlines():
355*9c5db199SXin Li        match = pkg_re.search(line)
356*9c5db199SXin Li        if match:
357*9c5db199SXin Li            pkg = match.group(1)
358*9c5db199SXin Li            continue
359*9c5db199SXin Li        match = path_re.match(line)
360*9c5db199SXin Li        if match:
361*9c5db199SXin Li            isexec = int(match.group(1), 8) & 0o111
362*9c5db199SXin Li            isfile = match.group(2) == '-'
363*9c5db199SXin Li            if pkg and isexec and isfile:
364*9c5db199SXin Li                packages.add(pkg)
365*9c5db199SXin Li
366*9c5db199SXin Li    # If exactly one package found it must be the one we want, return it.
367*9c5db199SXin Li    if len(packages) == 1:
368*9c5db199SXin Li        return packages.pop()
369*9c5db199SXin Li
370*9c5db199SXin Li    # TODO(milleral): Decide if it really is an error if not exactly one
371*9c5db199SXin Li    # package is found.
372*9c5db199SXin Li    # It is highly questionable as to if this should be left in the
373*9c5db199SXin Li    # production version of this code or not.
374*9c5db199SXin Li    if len(packages) == 0:
375*9c5db199SXin Li        logging.warning('find_package_of() found no packages for "%s"',
376*9c5db199SXin Li                        exec_name)
377*9c5db199SXin Li    else:
378*9c5db199SXin Li        logging.warning('find_package_of() found multiple packages for "%s": '
379*9c5db199SXin Li                        '%s', exec_name, ', '.join(packages))
380*9c5db199SXin Li    return ''
381*9c5db199SXin Li
382*9c5db199SXin Li
383*9c5db199SXin Lidef report_bug_from_crash(host, minidump_path):
384*9c5db199SXin Li    """
385*9c5db199SXin Li    Given a host to query and a minidump, file a bug about the crash.
386*9c5db199SXin Li
387*9c5db199SXin Li    @param host A host object that is where the dump came from
388*9c5db199SXin Li    @param minidump_path The path to the dump file that should be reported.
389*9c5db199SXin Li    """
390*9c5db199SXin Li    # TODO(milleral): Once this has actually been tested, remove the
391*9c5db199SXin Li    # try/except. In the meantime, let's make sure nothing dies because of
392*9c5db199SXin Li    # the fact that this code isn't very heavily tested.
393*9c5db199SXin Li    try:
394*9c5db199SXin Li        meta_path = os.path.splitext(minidump_path)[0] + '.meta'
395*9c5db199SXin Li        with open(meta_path, 'r') as f:
396*9c5db199SXin Li            for line in f.readlines():
397*9c5db199SXin Li                parts = line.split('=')
398*9c5db199SXin Li                if parts[0] == 'exec_name':
399*9c5db199SXin Li                    package = find_package_of(host, parts[1].strip())
400*9c5db199SXin Li                    if not package:
401*9c5db199SXin Li                        package = '<unknown package>'
402*9c5db199SXin Li                    logging.info('Would report crash on %s.', package)
403*9c5db199SXin Li                    break
404*9c5db199SXin Li    except Exception as e:
405*9c5db199SXin Li        logging.warning('Crash detection failed with: %s', e)
406