Bug 825957 - mirror mozcrash 0.3 from github to m-c;r=jmaher

--HG--
extra : rebase_source : 66105fc53438e7cd3e4f1b2dbaacbaa270e02f34
This commit is contained in:
Jeff Hammel 2013-01-07 16:41:35 -08:00
Родитель 8c71455159
Коммит 253f8bab17
4 изменённых файлов: 67 добавлений и 57 удалений

Просмотреть файл

@ -1,12 +0,0 @@
# Mozcrash
Package for getting a stack trace out of processes that have crashed and left behind a minidump file using the Google Breakpad library.
## Usage example
TODO
import mozcrash
#...

Просмотреть файл

@ -1,5 +1,8 @@
# This Source Code Form is subject to the terms of the Mozilla Public # This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file, # License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/. # You can obtain one at http://mozilla.org/MPL/2.0/.
"""
mozcrash is a library for getting a stack trace out of processes that have crashed and left behind a minidump file using the Google Breakpad library.
"""
from mozcrash import * from mozcrash import *

Просмотреть файл

@ -2,10 +2,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this file, # License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/. # You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import with_statement
__all__ = ['check_for_crashes'] __all__ = ['check_for_crashes']
import os, sys, glob, urllib2, tempfile, subprocess, shutil, urlparse, zipfile import os, sys, glob, urllib2, tempfile, re, subprocess, shutil, urlparse, zipfile
import mozlog import mozlog
def is_url(thing): def is_url(thing):
@ -13,7 +12,11 @@ def is_url(thing):
Return True if thing looks like a URL. Return True if thing looks like a URL.
""" """
# We want to download URLs like http://... but not Windows paths like c:\... # We want to download URLs like http://... but not Windows paths like c:\...
return len(urlparse.urlparse(thing).scheme) >= 2 parsed = urlparse.urlparse(thing)
if 'scheme' in parsed:
return len(parsed.scheme) >= 2
else:
return len(parsed[0]) >= 2
def extractall(zip, path = None): def extractall(zip, path = None):
""" """
@ -33,29 +36,38 @@ def extractall(zip, path = None):
path = os.path.split(filename)[0] path = os.path.split(filename)[0]
if not os.path.isdir(path): if not os.path.isdir(path):
os.makedirs(path) os.makedirs(path)
with open(filename, "wb") as dest:
dest.write(zip.read(name)) try:
f = open(filename, "wb")
f.write(zip.read(name))
finally:
f.close()
def check_for_crashes(dump_directory, symbols_path, def check_for_crashes(dump_directory, symbols_path,
stackwalk_binary=None, stackwalk_binary=None,
dump_save_path=None, dump_save_path=None,
test_name=None): test_name=None):
""" """
Print a stack trace for minidumps left behind by a crashing program. Print a stack trace for minidump files left behind by a crashing program.
Arguments: `dump_directory` will be searched for minidump files. Any minidump files found will
dump_directory: The directory in which to look for minidumps. have `stackwalk_binary` executed on them, with `symbols_path` passed as an extra
symbols_path: The path to symbols to use for dump processing. argument.
This can either be a path to a directory
containing Breakpad-format symbols, or a URL `stackwalk_binary` should be a path to the minidump_stackwalk binary.
to a zip file containing a set of symbols. If `stackwalk_binary` is not set, the MINIDUMP_STACKWALK environment variable
stackwalk_binary: The path to the minidump_stackwalk binary. will be checked and its value used if it is not empty.
If not set, the environment variable
MINIDUMP_STACKWALK will be checked. `symbols_path` should be a path to a directory containing symbols to use for
dump_save_path: A directory in which to copy minidump files dump processing. This can either be a path to a directory containing Breakpad-format
for safekeeping. If not set, the environment symbols, or a URL to a zip file containing a set of symbols.
variable MINIDUMP_SAVE_PATH will be checked.
test_name: The test name to be used in log output. If `dump_save_path` is set, it should be a path to a directory in which to copy minidump
files for safekeeping after a stack trace has been printed. If not set, the environment
variable MINIDUMP_SAVE_PATH will be checked and its value used if it is not empty.
If `test_name` is set it will be used as the test name in log output. If not set the
filename of the calling function will be used.
Returns True if any minidumps were found, False otherwise. Returns True if any minidumps were found, False otherwise.
""" """
@ -75,10 +87,9 @@ def check_for_crashes(dump_directory, symbols_path,
if len(dumps) == 0: if len(dumps) == 0:
return False return False
found_crash = False
remove_symbols = False remove_symbols = False
# If our symbols are at a remote URL, download them now # If our symbols are at a remote URL, download them now
if is_url(symbols_path): if symbols_path and is_url(symbols_path):
log.info("Downloading symbols from: %s", symbols_path) log.info("Downloading symbols from: %s", symbols_path)
remove_symbols = True remove_symbols = True
# Get the symbols and write them to a temporary zipfile # Get the symbols and write them to a temporary zipfile
@ -94,8 +105,9 @@ def check_for_crashes(dump_directory, symbols_path,
try: try:
for d in dumps: for d in dumps:
log.info("PROCESS-CRASH | %s | application crashed (minidump found)", test_name) stackwalk_output = []
log.info("Crash dump filename: %s", d) stackwalk_output.append("Crash dump filename: " + d)
top_frame = None
if symbols_path and stackwalk_binary and os.path.exists(stackwalk_binary): if symbols_path and stackwalk_binary and os.path.exists(stackwalk_binary):
# run minidump_stackwalk # run minidump_stackwalk
p = subprocess.Popen([stackwalk_binary, d, symbols_path], p = subprocess.Popen([stackwalk_binary, d, symbols_path],
@ -105,19 +117,36 @@ def check_for_crashes(dump_directory, symbols_path,
if len(out) > 3: if len(out) > 3:
# minidump_stackwalk is chatty, # minidump_stackwalk is chatty,
# so ignore stderr when it succeeds. # so ignore stderr when it succeeds.
print out stackwalk_output.append(out)
# The top frame of the crash is always the line after "Thread N (crashed)"
# Examples:
# 0 libc.so + 0xa888
# 0 libnss3.so!nssCertificate_Destroy [certificate.c : 102 + 0x0]
# 0 mozjs.dll!js::GlobalObject::getDebuggers() [GlobalObject.cpp:89df18f9b6da : 580 + 0x0]
# 0 libxul.so!void js::gc::MarkInternal<JSObject>(JSTracer*, JSObject**) [Marking.cpp : 92 + 0x28]
lines = out.splitlines()
for i, line in enumerate(lines):
if "(crashed)" in line:
match = re.search(r"^ 0 (?:.*!)?(?:void )?([^\[]+)", lines[i+1])
if match:
top_frame = "@ %s" % match.group(1).strip()
break
else: else:
print "stderr from minidump_stackwalk:" stackwalk_output.append("stderr from minidump_stackwalk:")
print err stackwalk_output.append(err)
if p.returncode != 0: if p.returncode != 0:
log.error("minidump_stackwalk exited with return code %d", p.returncode) stackwalk_output.append("minidump_stackwalk exited with return code %d" % p.returncode)
else: else:
if not symbols_path: if not symbols_path:
log.warn("No symbols path given, can't process dump.") stackwalk_output.append("No symbols path given, can't process dump.")
if not stackwalk_binary: if not stackwalk_binary:
log.warn("MINIDUMP_STACKWALK not set, can't process dump.") stackwalk_output.append("MINIDUMP_STACKWALK not set, can't process dump.")
elif stackwalk_binary and not os.path.exists(stackwalk_binary): elif stackwalk_binary and not os.path.exists(stackwalk_binary):
log.warn("MINIDUMP_STACKWALK binary not found: %s", stackwalk_binary) stackwalk_output.append("MINIDUMP_STACKWALK binary not found: %s" % stackwalk_binary)
if not top_frame:
top_frame = "Unknown top frame"
log.error("PROCESS-CRASH | %s | application crashed [%s]", test_name, top_frame)
print '\n'.join(stackwalk_output)
if dump_save_path is None: if dump_save_path is None:
dump_save_path = os.environ.get('MINIDUMP_SAVE_PATH', None) dump_save_path = os.environ.get('MINIDUMP_SAVE_PATH', None)
if dump_save_path: if dump_save_path:
@ -129,9 +158,8 @@ def check_for_crashes(dump_directory, symbols_path,
extra = os.path.splitext(d)[0] + ".extra" extra = os.path.splitext(d)[0] + ".extra"
if os.path.exists(extra): if os.path.exists(extra):
os.remove(extra) os.remove(extra)
found_crash = True
finally: finally:
if remove_symbols: if remove_symbols:
shutil.rmtree(symbols_path) shutil.rmtree(symbols_path)
return found_crash return True

Просмотреть файл

@ -2,26 +2,17 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this file, # License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/. # You can obtain one at http://mozilla.org/MPL/2.0/.
import os
from setuptools import setup from setuptools import setup
PACKAGE_VERSION = '0.1' PACKAGE_VERSION = '0.3'
# get documentation from the README
try:
here = os.path.dirname(os.path.abspath(__file__))
description = file(os.path.join(here, 'README.md')).read()
except (OSError, IOError):
description = ''
# dependencies # dependencies
deps = [''] deps = []
setup(name='mozcrash', setup(name='mozcrash',
version=PACKAGE_VERSION, version=PACKAGE_VERSION,
description="Package for printing stack traces from minidumps left behind by crashed processes.", description="Library for printing stack traces from minidumps left behind by crashed processes",
long_description=description, long_description="see http://mozbase.readthedocs.org/",
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
keywords='mozilla', keywords='mozilla',
author='Mozilla Automation and Tools team', author='Mozilla Automation and Tools team',