File manager - Edit - /usr/lib/python2.7/site-packages/redhat_support_tool/helpers/vmcorehelper.py
Back
# -*- coding: utf-8 -*- # # Copyright (c) 2012 Red Hat, Inc. # # Licensed 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. # from redhat_support_tool.helpers.confighelper import _ import redhat_support_tool.helpers.analyzer as analyzer import fnmatch import logging import os import rpm import struct import subprocess import tempfile __author__ = 'Rex White <rexwhite@redhat.com>' __author__ = 'Keith Robertson <kroberts@redhat.com>' logger = logging.getLogger("redhat_support_tool.helpers.vmcorehelper") class VMLinux(object): ''' The VMLinux class represents a vmlinux file and is primarily intended for use as a means to extract the kernel version from a vmlinux kernel debug symbol file. ''' version = None filename = None class ELFHeader(object): def __init__(self, vmlinux_file): ''' Constructor for ELFHeader. Takes a file object for the vmlinux file as an argument ''' # read the ELF header from the vmlinux file... # verify ELF "magic number" file signature self.elfHdr_magic = vmlinux_file.read(4) if self.elfHdr_magic != "\x7fELF": # ELF "magic number" file signature invalid: not a valid ELF # file msg = _('ERROR: %s is an invalid ELF file!.') print msg logger.log(logging.ERROR, msg) raise Exception(msg) # read rest of ELF identification stuff... self.elfHdr_class = vmlinux_file.read(1) # determine "endian-ness" of file and set up converters self.elfHdr_encoding = vmlinux_file.read(1) if self.elfHdr_encoding == '\x01': # little endian... self.endianness = '<' elif self.elfHdr_encoding == '\x02': # big endian... self.endianness = '>' else: msg = _('ERROR: %s has an unrecognized byte encoding.') print msg logger.log(logging.ERROR, msg) raise Exception(msg) # VMLinux.Elf64_Half = struct.Struct(endianness + 'H') # VMLinux.Elf64_Word = struct.Struct(endianness + 'I') # VMLinux.Elf64_Long = struct.Struct(endianness + 'Q') self.elfHdr_version = vmlinux_file.read(1) self.elfHdr_OSABI = vmlinux_file.read(1) self.elfHdr_ABIVersion = vmlinux_file.read(1) self.elfHdr_padding = vmlinux_file.read(7) # read rest of ELF header... self.elfHdr_ObjType = \ struct.unpack(self.endianness + 'H', vmlinux_file.read(2))[0] self.elfHdr_MachineType = \ struct.unpack(self.endianness + 'H', vmlinux_file.read(2))[0] self.elfHdr_version = \ struct.unpack(self.endianness + 'I', vmlinux_file.read(4))[0] self.elfHdr_entry = \ struct.unpack(self.endianness + 'Q', vmlinux_file.read(8))[0] self.elfHdr_ProgHdrOff = \ struct.unpack(self.endianness + 'Q', vmlinux_file.read(8))[0] self.elfHdr_SectHdrOff = \ struct.unpack(self.endianness + 'Q', vmlinux_file.read(8))[0] self.elfHdr_flags = \ struct.unpack(self.endianness + 'I', vmlinux_file.read(4))[0] self.elfHdr_HdrSize = \ struct.unpack(self.endianness + 'H', vmlinux_file.read(2))[0] self.elfHdr_ProgHdrEntSize = \ struct.unpack(self.endianness + 'H', vmlinux_file.read(2))[0] self.elfHdr_ProgHdrEntCount = \ struct.unpack(self.endianness + 'H', vmlinux_file.read(2))[0] self.elfHdr_SectHdrEntSize = \ struct.unpack(self.endianness + 'H', vmlinux_file.read(2))[0] self.elfHdr_SectHdrEntCount = \ struct.unpack(self.endianness + 'H', vmlinux_file.read(2))[0] self.elfHdr_SectNameStrTableIdx = \ struct.unpack(self.endianness + 'H', vmlinux_file.read(2))[0] def __str__(self): result = "encoding:" + repr(self.elfHdr_encoding) result += "\nObject type: " + str(self.elfHdr_ObjType) result += "\nMachine type: " + str(self.elfHdr_MachineType) result += "\nVersion: " + str(self.elfHdr_version) result += "\nEntry point addr: " + str(self.elfHdr_entry) result += "\nProgram header offset: " + str(self.elfHdr_ProgHdrOff) result += "\nSection header offset: " + str(self.elfHdr_SectHdrOff) result += "\nFlags: " + str(self.elfHdr_flags) result += "\nELF header size: " + str(self.elfHdr_HdrSize) result += "\nProgram header entry size: " + \ str(self.elfHdr_ProgHdrEntSize) result += "\nProgram header entry count: " + \ str(self.elfHdr_ProgHdrEntCount) result += "\nSection header entry size: " + \ str(self.elfHdr_SectHdrEntSize) result += "\nSection header entry count: " + \ str(self.elfHdr_SectHdrEntCount) result += "\nSection name string table index: " + \ str(self.elfHdr_SectNameStrTableIdx) return result class ELFSectionHdr(object): def __init__(self, vmlinux_file, elf_hdr): # read section table entry from file self.sect_name = struct.unpack(elf_hdr.endianness + 'I', vmlinux_file.read(4))[0] self.sect_type = struct.unpack(elf_hdr.endianness + 'I', vmlinux_file.read(4))[0] self.sect_flags = \ struct.unpack(elf_hdr.endianness + 'Q', vmlinux_file.read(8))[0] self.sect_address = \ struct.unpack(elf_hdr.endianness + 'Q', vmlinux_file.read(8))[0] self.sect_offset = \ struct.unpack(elf_hdr.endianness + 'Q', vmlinux_file.read(8))[0] self.sect_size = struct.unpack(elf_hdr.endianness + 'Q', vmlinux_file.read(8))[0] self.sect_link = struct.unpack(elf_hdr.endianness + 'I', vmlinux_file.read(4))[0] self.sect_info = struct.unpack(elf_hdr.endianness + 'I', vmlinux_file.read(4))[0] self.sect_align = \ struct.unpack(elf_hdr.endianness + 'Q', vmlinux_file.read(8))[0] self.sect_entrySize = \ struct.unpack(elf_hdr.endianness + 'Q', vmlinux_file.read(8))[0] # skip any leftover stuff at the end of this segment header vmlinux_file.seek(elf_hdr.elfHdr_SectHdrEntSize - 64, 1) def __str__(self): result = "Section name: " + str(self.sect_name) result += "\nSection type: " + str(self.sect_type) result += "\nSection flags: " + str(self.sect_flags) result += "\nSection address: {0:x}".format(self.sect_address) result += "\nSection offset: {0:x}".format(self.sect_offset) result += "\nSection size: {0:x}".format(self.sect_size) result += "\nSection link: " + str(self.sect_link) result += "\nSection info: " + str(self.sect_info) result += "\nSection aligment: {0:x}".format(self.sect_align) result += "\nSection entry size: {0:x}".format(self.sect_entrySize) return result def __init__(self, filename): ''' Constructor for VMLinux. Takes the filename of a vmlinux file as an argument ''' self.filename = filename # open file vmlinux = open(filename, 'rb') # read ELF header from file hdr = self.ELFHeader(vmlinux) # logger.log(logging.DEBUG, str(hdr)) # read ELF section headers vmlinux.seek(hdr.elfHdr_SectHdrOff, 0) sections = [] for i in range(hdr.elfHdr_SectHdrEntCount): sections.append(self.ELFSectionHdr(vmlinux, hdr)) # logger.log(logging.DEBUG, str(i)) # read the section name string table from the indicated section string_sect = sections[hdr.elfHdr_SectNameStrTableIdx] vmlinux.seek(string_sect.sect_offset, 0) # WARNING!! This can blow up, so do something clever... strings = vmlinux.read(string_sect.sect_size) # find the offset for ".rodata" ro_offset = strings.find(".rodata", 1) if ro_offset == -1: msg = _('ERROR: There is no segment named .rodata in %s') % \ filename print msg logger.log(logging.ERROR, msg) raise Exception(msg) # now find the .rodata section... # print "\nSearching for .rodata section..." for i in range(len(sections)): if sections[i].sect_name == ro_offset: # print ".rodata is segment number {0:d}".format(i) # now grab the first 192 bytes of the string starting at byte # 32 of this section... vmlinux.seek(sections[i].sect_offset + 32, 0) id_string = vmlinux.read(192).split() # validate string logger.log(logging.DEBUG, '%s id string is %s' % (filename, id_string)) if id_string[0] == 'Linux': if id_string[1] == 'version': logger.log(logging.DEBUG, '%s version is %s' % (filename, id_string[2])) self.version = str(id_string[2]).strip() def get_version(self): ''' A utility function to return the vmlinux version ''' return self.version def get_filename(self): return self.filename class VMCore(object): ''' The VMCore class represents a vmcore file, from which we can extract a backtrace ''' coreFilename = None kernelVersion = None vmlinux = None def __init__(self, filename): ''' Constructor ''' if not os.access(filename, os.R_OK): msg = _('ERROR: unable to read %s') % filename print msg raise Exception(msg) logging.log(logging.DEBUG, 'Analyzing %s', filename) # Check for crash ts = rpm.TransactionSet() mi = ts.dbMatch('provides', 'crash') if mi is None or mi.count() != 1: msg = _('ERROR: \'crash\' is not installed. Please install ' '\'crash\' as root (ie. yum install crash)') print msg logging.log(logging.ERROR, msg) raise Exception(msg) self.coreFilename = filename self.kernelVersion = self._getKernelVersion() def _getKernelVersion(self): ''' getKernelVersion() extracts and returns the OSRELEASE (kernel version) string from the target core file ''' # determine vmcore osrelease # get crash to do the heavy lifting for us try: proc = subprocess.Popen(["crash", "--osrelease", self.coreFilename], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() if proc.returncode == 0: logger.log(logging.DEBUG, 'Detected kernel version of %s is %s' % (self.coreFilename, str(stdout).strip())) return str(stdout).strip() else: msg = _('Unable to determine vmcore kernel version of ' + self.coreFilename + ': ') if not stderr: msg += _('%s' % str(stdout).strip()) else: msg += _('%s' % stderr) raise Exception(msg) except Exception, e: msg = _('ERROR: Unable to launch crash. Message: %s') % e print msg logger.log(logging.ERROR, msg) raise Exception(msg) def getKernelVersion(self): ''' getKernelVersion() extracts and returns the OSRELEASE (kernel version) string from the target core file ''' return self.kernelVersion def setDebugSymbols(self, vmlinux=None): ''' A setter function for attaching a VMLinux to this VMCore. A valid VMLinux is a pre-requisite to any BT work. ''' self.vmlinux = vmlinux def getDebugSymbols(self): return self.vmlinux def exe_crash_commands(self, commands=None): ''' A utility function which executes a set of crash commands on this object. Arguments: commands: A newline separated sequence of commands to execute. One command per-line. See the -i option in crash. Returns: The stdout from crash or None ''' retVal = None tf = None tempfilename = None if not commands: logger.log(logging.DEBUG, 'commands is None') return retVal if self.vmlinux: try: (tf, tempfilename) = tempfile.mkstemp() tf = os.fdopen(tf, 'w') tf.write(commands) tf.write('\nquit') tf.close() logger.log(logging.DEBUG, 'Crash command file is %s with contents of:\n%s' % (tempfilename, open(tempfilename, 'r').read())) cmd = ['crash', self.coreFilename, self.vmlinux.get_filename(), '-i', tempfilename] logger.log(logging.DEBUG, 'Executing: %s ' % " ".join(cmd)) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() if proc.returncode == 0: logger.log(logging.DEBUG, 'Crash output %s' % str(stdout).strip()) retVal = str(stdout).strip().replace('\r', '\n') if commands == 'bt -a': clean = analyzer.Analyzer.analyze( retVal, ["Crash bt -a Analyzer", ]) retVal = clean[0].token_string else: msg = _('ERROR: Problem executing crash command: %s' % stderr) if not stderr: logger.error('%s\nCommand: %s\nOutput:\n%s' % (msg, ' '.join(cmd), str(stdout).strip())) msg += _('\nPlease consult the Red Hat Support Tool ' 'logs for more details.') print msg raise Exception(msg) # Cleanup if tf: os.unlink(tempfilename) except Exception, e: if tf: os.unlink(tempfilename) else: logger.log(logging.DEBUG, 'There is no vmlinux objec associated with %s' % self.coreFilename) return retVal return retVal def get_debug_symbols(kernelext_dir, kernel_version=None): ''' A utility function that will search the configured debug symbol cache directory. For vmlinux files matching the provided kernel version. This must be version from /proc/version (ie. 3.6.11-1.fc17.x86_64) Returns: A VMLinux object or None ''' retVal = None logger.log(logging.DEBUG, 'Searching %s for debug symbols ' 'matching %s' % (kernelext_dir, kernel_version)) for root, dirnames, filenames in \ os.walk(kernelext_dir): for filename in fnmatch.filter(filenames, '*vmlinux*'): logger.log(logging.DEBUG, 'Inspecting %s' % filename) vm = VMLinux(os.path.join(root, filename)) if vm.get_version() == kernel_version: logger.log(logging.DEBUG, '%s is a match for %s' % (os.path.join(root, filename), kernel_version)) retVal = vm return retVal def list_extracted_vmlinuxes(kernelext_dir): debugimages = [] if os.path.exists(kernelext_dir): debugdirs = os.listdir(kernelext_dir) for pkgname in debugdirs: if os.path.exists(os.path.join(kernelext_dir, pkgname, 'vmlinux')): debugimages.append(pkgname) return debugimages if __name__ == "__main__": # enable verbose logging logging.basicConfig(level=logging.DEBUG) # create core object from vmcore file core = VMCore("/home/keith/bt/cores/vmcore") vm = get_debug_symbols(core.getKernelVersion()) core.setDebugSymbols(vm) # extract backraceexecute_crash_commandsecute_crash_commands()
| ver. 1.4 |
Github
|
.
| PHP 7.3.33 | Generation time: 0.05 |
proxy
|
phpinfo
|
Settings