blob: 6c04672d2cfd4923d263c24e763a536b06bfe552 [file] [log] [blame]
#!/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# Merged from https://github.com/greenplum-db/gpdb/blob/master/gpMgmt/sbin/packcore
'''
USAGE: ./packcore -b|--binary BINARY COREFILE
where BINARY is the path to postgres binary
where COREFILE is the core file
'''
import glob
import os
import re
import string
import shutil
import stat
import sys
from optparse import OptionParser
from subprocess import Popen, PIPE
def _getPlatformInfo():
if which('lsb_release') is None:
for file in glob.glob('/etc/*release'):
shutil.copy(file, '.')
else:
Popen('/usr/bin/lsb_release -a > ./lsb_release.out', shell=True)
Popen('uname -r > uname.out', shell=True)
def _getFileInfo(coreFile):
cmd = Popen('/usr/bin/file ' + coreFile, shell=True, stdout=PIPE)
return cmd.communicate()[0]
def _isCore(fileCmdOutput):
if fileCmdOutput.find('LSB core file') is -1:
return False
return True
def _findBinary(fileCmdOutput):
start = string.find(fileCmdOutput, "'") + 1
end = string.find(fileCmdOutput, "'", start)
cmd = fileCmdOutput[start:end].split()[0].translate(None, string.punctuation)
if os.path.isabs(cmd):
return cmd
return which(cmd)
def _getLibraryListWithLDD(binary):
# We manually seed this with few libraries that are missed
# This may not be needed for all processes, but will round out the
# postgres binary debugging
# TODO: Identify methods to distinguish a 32 vs. 64 bit executable
libraries = ['/lib64/libgcc_s.so.1', '/lib64/libnss_files.so.2', '/lib/libgcc_s.so.1', '/lib/libnss_files.so.2']
ldd_output = Popen('ldd `which postgres`', shell=True, stdout=PIPE)
for line in ldd_output.stdout:
match = re.search(r'(\S+) \(0x', line)
if match and match.group(1):
libraries.append(match.group(1).strip())
return libraries
def _getLibraryListWithGDB(coreFile, binary):
gdb = which('gdb')
if gdb is None:
return False
libraries = []
# fix for issues with PYTHONPATH and PYTHONHOME
environ = os.environ.copy()
for key in ('PYTHONHOME', 'PYTHONPATH', 'LD_LIBRARY_PATH'):
if key in environ:
del environ[key]
cmd = Popen(gdb + ' --eval-command="quit" ' + binary + ' -c ' + coreFile, shell=True, stdout=PIPE, stderr=PIPE)
result = cmd.communicate()[0]
for line in result.splitlines():
if line.find('Reading symbols') is 0:
end = line.find('...')
libraries.append(line[21:end])
return libraries
def _copyFilePath(src, dst):
srcDir = os.path.dirname(src)
if srcDir.find('/') is 0:
srcDir = srcDir[1:]
dstDir = os.path.join(dst, srcDir)
if not os.path.exists(dstDir):
os.makedirs(dstDir)
shutil.copy(src, dstDir)
def _generateGDBScript(b, c):
with open('runGDB.sh', 'w') as f1:
print >>f1, '#!/bin/bash'
print >>f1, 'unset PYTHONHOME'
print >>f1, 'unset PYTHONPATH'
print >>f1, 'curDIR=`pwd`'
print >>f1, '/usr/bin/gdb --eval-command="set sysroot $curDIR" --eval-command="core %s" %s' % (c, b)
os.chmod('runGDB.sh', 0755)
# This is taken from Python 3.3:
def which(cmd, mode=os.F_OK | os.X_OK, path=None):
"""Given a command, mode, and a PATH string, return the path which
conforms to the given mode on the PATH, or None if there is no such
file.
`mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
of os.environ.get("PATH"), or can be overridden with a custom search
path.
"""
# Check that a given file can be accessed with the correct mode.
# Additionally check that `file` is not a directory, as on Windows
# directories pass the os.access check.
def _access_check(fn, mode):
return (os.path.exists(fn) and os.access(fn, mode)
and not os.path.isdir(fn))
# If we're given a path with a directory part, look it up directly rather
# than referring to PATH directories. This includes checking relative to the
# current directory, e.g. ./script
if os.path.dirname(cmd):
if _access_check(cmd, mode):
return cmd
return None
if path is None:
path = os.environ.get("PATH", os.defpath)
if not path:
return None
path = path.split(os.pathsep)
if sys.platform == "win32":
# The current directory takes precedence on Windows.
if not os.curdir in path:
path.insert(0, os.curdir)
# PATHEXT is necessary to check on Windows.
pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
# See if the given file matches any of the expected path extensions.
# This will allow us to short circuit when given "python.exe".
# If it does match, only test that one, otherwise we have to try
# others.
if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
files = [cmd]
else:
files = [cmd + ext for ext in pathext]
else:
# On other platforms you don't have things like PATHEXT to tell you
# what file suffixes are executable, so just pass on cmd as-is.
files = [cmd]
seen = set()
for dir in path:
normdir = os.path.normcase(dir)
if not normdir in seen:
seen.add(normdir)
for thefile in files:
name = os.path.join(dir, thefile)
if _access_check(name, mode):
return name
return None
def packCoreFile(coreFile, binary):
packDir = './packcore-' + os.path.basename(coreFile)
oldDir = os.getcwd()
try:
os.mkdir(packDir)
os.chdir(packDir)
shutil.copy(coreFile, '.')
_getPlatformInfo()
shutil.copy(binary, '.')
libraries = _getLibraryListWithGDB(coreFile, binary)
if libraries is False:
libraries = _getLibraryListWithLDD(binary)
for lib in libraries:
try:
_copyFilePath(lib, '.')
except IOError:
continue
_generateGDBScript(os.path.basename(binary), os.path.basename(coreFile))
os.chdir(oldDir)
cmd = Popen('tar zcf packcore-' + os.path.basename(coreFile) + '.tgz ' + packDir, shell=True)
cmd.wait()
finally:
os.chdir(oldDir)
Popen('rm -rf ' + packDir, shell=True)
def parseArgs():
u = '''%prog [options] core_file
This will create an archive with the core file and all required
libraries for analysis. The preference is to use GDB so that we can
resolve dependencies for extensions.'''
parser = OptionParser(version='%prog: 0.4beta', usage=u)
parser.add_option('-b', '--binary', action='store', type='string', dest='binary', metavar='PROGRAMME', help='The full path to the binary that created the core file. Used when packcore cannot determine the source binary')
(option, args) = parser.parse_args()
if len(args) != 1:
parser.error('Please specify a core file')
sys.exit(1)
return (option, args)
def main():
# Check python vesion
if sys.hexversion < 0x020600f0:
sys.stderr.write('packcore requires a minimum python version of 2.6. Current version is:\n' + sys.version)
sys.exit(1)
(options, args) = parseArgs()
coreFile = os.path.abspath(args[0])
fileCmd = _getFileInfo(coreFile)
if not _isCore(fileCmd):
sys.stderr.write(args[0] + ' is not a valid core file\n')
sys.exit(1)
if options.binary:
binary = which(options.binary)
else:
binary = _findBinary(fileCmd)
if not binary:
sys.stderr.write("Unable to find full path to binary for core file\n")
sys.exit(1)
packCoreFile(coreFile, binary)
sys.exit(0)
if __name__ == "__main__":
main()