[Cluster-devel] [PATCH] gfs2_trace: Added a script called gfs2_trace for kernel tracing debugging.
Andrew Price
anprice at redhat.com
Wed Dec 19 15:36:54 UTC 2012
Hi Shane,
Looks good to me,
Andy
On 19/12/12 15:14, sbradley at redhat.com wrote:
> From: Shane Bradley <sbradley at redhat.com>
>
> The script gfs2_trace is a tool used for debugging kernel tracing events. The
> script can enabled or disable all or selected GFS2 kernel tracing events. The
> script can capture the trace events and write them to a specific file which will
> be tarred up after the file is written.
>
> Signed-off-by: Shane Bradley <sbradley at redhat.com>
> ---
> gfs2/man/Makefile.am | 3 +-
> gfs2/man/gfs2_trace.8 | 45 +++
> gfs2/scripts/Makefile.am | 2 +-
> gfs2/scripts/gfs2_trace | 790 +++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 838 insertions(+), 2 deletions(-)
> create mode 100644 gfs2/man/gfs2_trace.8
> create mode 100644 gfs2/scripts/gfs2_trace
>
> diff --git a/gfs2/man/Makefile.am b/gfs2/man/Makefile.am
> index 8655a76..82eab9a 100644
> --- a/gfs2/man/Makefile.am
> +++ b/gfs2/man/Makefile.am
> @@ -8,4 +8,5 @@ dist_man_MANS = fsck.gfs2.8 \
> gfs2_jadd.8 \
> mkfs.gfs2.8 \
> tunegfs2.8 \
> - gfs2_lockcapture.8
> + gfs2_lockcapture.8 \
> + gfs2_trace.8
> diff --git a/gfs2/man/gfs2_trace.8 b/gfs2/man/gfs2_trace.8
> new file mode 100644
> index 0000000..dd98072
> --- /dev/null
> +++ b/gfs2/man/gfs2_trace.8
> @@ -0,0 +1,45 @@
> +.TH gfs2_trace 8
> +
> +.SH NAME
> +gfs2_trace \- can enable trace events, disable trace events, and capture data from GFS2 trace events.
> +
> +.SH SYNOPSIS
> +.B gfs2_trace \fR[-dqEN] [-e \fItrace event name]\fR [-n \fItrace event name]\fR [-o \fIoutput filename]\fR
> +.PP
> +
> +.SH DESCRIPTION
> +\fIgfs2_trace\fR can enabled and disable trace events on all trace events or selected trace events. \fIgfs2_trace\fR can
> +capture the output of the trace events and write the output to a file. When capturing trace events, the script will exit
> +when control-c is pressed. The trace events will be then written to the selected file.
> +.PP
> +
> +.SH OPTIONS
> +.TP
> +\fB-h, --help\fP
> +Prints out a short usage message and exits.
> +.TP
> +\fB-d, --debug\fP
> +enables debug logging.
> +.TP
> +\fB-q, --quiet\fP
> +disables logging to console.
> +.TP
> +\fB-l, --list\fP
> +lists the enabled state and filters for the GFS2 trace events
> +.TP
> +\fB-E, --enable_all_trace_events\fP
> +enables all trace_events for GFS2
> +.TP
> +\fB-e \fI<trace event name>, \fB----enable_trace_event\fR=\fI<trace event name>\fP
> +selected trace_events that will be enabled for GFS2
> +.TP
> +\fB-N, --disable_all_trace_events\fP
> +disables all trace_events for GFS2
> +.TP
> +\fB-n \fI<trace event name>, \fB----disable_trace_event\fR=\fI<trace event name>\fP
> +selected trace_events that will be enabled for GFS2
> +.TP
> +\fB-c \fI<output filename>, \fB--capture\fR=\fI<output filename>\fP
> +enables capturing of trace events and will save the data to a file
> +.
> +.SH SEE ALSO
> diff --git a/gfs2/scripts/Makefile.am b/gfs2/scripts/Makefile.am
> index b88580e..2c51222 100644
> --- a/gfs2/scripts/Makefile.am
> +++ b/gfs2/scripts/Makefile.am
> @@ -9,4 +9,4 @@ sbindir := $(shell rpl=0; test '$(exec_prefix):$(sbindir)' = /usr:/usr/sbin \
> test $$rpl = 1 && echo /sbin || echo '$(exec_prefix)/sbin')
>
>
> -dist_sbin_SCRIPTS = gfs2_lockcapture
> +dist_sbin_SCRIPTS = gfs2_lockcapture gfs2_trace
> diff --git a/gfs2/scripts/gfs2_trace b/gfs2/scripts/gfs2_trace
> new file mode 100644
> index 0000000..38b3d18
> --- /dev/null
> +++ b/gfs2/scripts/gfs2_trace
> @@ -0,0 +1,790 @@
> +#!/usr/bin/env python
> +"""
> +This script will enable or disable trace events for GFS2. The script can capture
> +trace events and write the trace events captured to a file.
> +
> +When capturing events, hit "control-c" to exit and then the capture events will be
> +written to a file. A file will be created by reading this pipe:
> +/sys/kernel/debug/tracing/trace_pipe
> +
> +The debug directory is required to be mounted which will be mounted if not
> +mounted. The trace events are located in this directory
> +/sys/kernel/debug/tracing/events/gfs2.
> +
> +The file that can be used to validate what fields are valid for "filters" is
> +described in the format file. For example:
> +/sys/kernel/debug/tracing/events/gfs2/*/format
> +
> + at author : Shane Bradley
> + at contact : sbradley at redhat.com
> + at version : 0.9
> + at copyright : GPLv2
> +"""
> +import sys
> +import os
> +import os.path
> +import logging
> +import platform
> +import fileinput
> +import tarfile
> +import subprocess
> +from optparse import OptionParser, Option
> +
> +# #####################################################################
> +# Global Vars:
> +# #####################################################################
> +"""
> + at cvar VERSION_NUMBER: The version number of this script.
> + at type VERSION_NUMBER: String
> + at cvar MAIN_LOGGER_NAME: The name of the logger.
> + at type MAIN_LOGGER_NAME: String
> + at cvar PATH_TO_DEBUG_DIR: The path to the debug directory for the linux kernel.
> + at type PATH_TO_DEBUG_DIR: String
> + at cvar PATH_TO_PID_FILENAME: The path to the pid file that will be used to make
> +sure only 1 instance of this script is running at any time.
> + at type PATH_TO_PID_FILENAME: String
> + at cvar PATH_TO_GFS2_TRACE_EVENTS_DIR: The path to the directory that contains
> +all the GFS2 trace events.
> + at type PATH_TO_GFS2_TRACE_EVENTS_DIR: String
> + at cvar PATH_TO_TRACE_PIPE: The path to the tracing pipe.
> + at type PATH_TO_TRACE_PIPE: String
> +"""
> +VERSION_NUMBER = "0.9-1"
> +MAIN_LOGGER_NAME = "gfs2trace"
> +PATH_TO_DEBUG_DIR="/sys/kernel/debug"
> +PATH_TO_PID_FILENAME = "/var/run/%s.pid" %(os.path.basename(sys.argv[0]))
> +PATH_TO_GFS2_TRACE_EVENTS_DIR="%s/tracing/events/gfs2" %(PATH_TO_DEBUG_DIR)
> +PATH_TO_TRACE_PIPE="%s/tracing/trace_pipe" %(PATH_TO_DEBUG_DIR)
> +
> +class FileUtils:
> + """
> + A class that provides static functions for files such as reading and
> + writing.
> + """
> + def getDataFromFile(pathToSrcFile) :
> + """
> + This function will return the data in an array. Where each newline in file
> + is a seperate item in the array. This should really just be used on
> + relatively small files.
> +
> + None is returned if no file is found.
> +
> + @return: Returns an array of Strings, where each newline in file is an item
> + in the array.
> + @rtype: Array
> +
> + @param pathToSrcFile: The path to the file which will be read.
> + @type pathToSrcFile: String
> + """
> + if (len(pathToSrcFile) > 0) :
> + try:
> + fin = open(pathToSrcFile, "r")
> + data = fin.readlines()
> + fin.close()
> + return data
> + except (IOError, os.error):
> + message = "An error occured reading the file: %s." %(pathToSrcFile)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + return None
> + getDataFromFile = staticmethod(getDataFromFile)
> +
> + def writeToFile(pathToFilename, data, appendToFile=True, createFile=False):
> + """
> + This function will write a string to a file.
> +
> + @return: Returns True if the string was successfully written to the file,
> + otherwise False is returned.
> + @rtype: Boolean
> +
> + @param pathToFilename: The path to the file that will have a string written
> + to it.
> + @type pathToFilename: String
> + @param data: The string that will be written to the file.
> + @type data: String
> + @param appendToFile: If True then the data will be appened to the file, if
> + False then the data will overwrite the contents of the file.
> + @type appendToFile: Boolean
> + @param createFile: If True then the file will be created if it does not
> + exists, if False then file will not be created if it does not exist
> + resulting in no data being written to the file.
> + @type createFile: Boolean
> + """
> + [parentDir, filename] = os.path.split(pathToFilename)
> + if (os.path.isfile(pathToFilename) or (os.path.isdir(parentDir) and createFile)):
> + try:
> + filemode = "w"
> + if (appendToFile):
> + filemode = "a"
> + fout = open(pathToFilename, filemode)
> + fout.write(data + "\n")
> + fout.close()
> + return True
> + except UnicodeEncodeError, e:
> + message = "There was a unicode encode error writing to the file: %s." %(pathToFilename)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + return False
> + except IOError:
> + message = "There was an error writing to the file: %s." %(pathToFilename)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + return False
> + return False
> + writeToFile = staticmethod(writeToFile)
> +
> +class TraceEvent:
> + """
> + A class that reprensents a trace event.
> + """
> + def __init__(self, pathToTraceEvent):
> + """
> + @param pathToTraceEvent: The path to the trace event directory.
> + @type pathToTraceEvent: String
> + """
> + self.__pathToTraceEvent = pathToTraceEvent
> +
> + def __str__(self):
> + """
> + Returns a string representation of a TraceEvent.
> +
> + @return: Returns a string representation of a TraceEvent.
> + @rtype: String
> + """
> + return "%s: %s" %(self.getName(), self.getEnable())
> +
> + def __getEventPathItem(self, pathToFilename):
> + """
> + Returns the data contain in the file. If file does not exist then empty
> + array is returned.
> +
> + @return: Returns the data contained in the file. If file does not exist
> + then empty array is returned.
> + @rtype: Array
> +
> + @param pathToFilename: The path to the filename of the file in the trace
> + event's directory.
> + @type pathToFilename: String
> + """
> + output = FileUtils.getDataFromFile(pathToFilename)
> + if (output == None):
> + return []
> + return output
> +
> + def __setEventPathItem(self, pathToFilename, contentsOfEventItem, appendToFile=True):
> + """
> + This function will write data to a file in the trace event's directory.
> +
> + @return: Returns True is data was successfully written.
> + @rtype: Boolean
> + """
> + return FileUtils.writeToFile(pathToFilename, contentsOfEventItem, appendToFile)
> +
> + def getPathToTraceEvent(self):
> + """
> + Returns the path to the trace event directory.
> +
> + @return: Returns the path to the trace event directory.
> + @rtype: String
> + """
> + return self.__pathToTraceEvent
> +
> + def getName(self):
> + """
> + Returns the shortname of the trace event.
> +
> + @return: Returns the shortname of the trace event.
> + @rtype: String
> + """
> + return os.path.basename(self.getPathToTraceEvent())
> +
> + def getEnable(self):
> + """
> + Returns the contents of the file "enable" in the trace event directory.
> +
> + @return: Returns the contents of the file "enable" in the trace event
> + directory.
> + @rtype: String
> + """
> + fileContents = self.__getEventPathItem(os.path.join(self.getPathToTraceEvent(), "enable"))
> + if (len(fileContents) > 0):
> + return fileContents[0].rstrip()
> + else:
> + return ""
> +
> + def getFilter(self):
> + """
> + Returns the contents of the file "filter" in the trace event
> + directory. Each line in the file is appened to an array that will be
> + returned.
> +
> + @return: Returns the contents of the file "filter" in the trace event
> + directory.
> + @rtype: Array
> + """
> + return self.__getEventPathItem(os.path.join(self.getPathToTraceEvent(), "filter"))
> +
> + def getFormat(self):
> + """
> + Returns the contents of the file "format" in the trace event
> + directory. Each line in the file is appened to an array that will be
> + returned.
> +
> + @return: Returns the contents of the file "format" in the trace event
> + directory.
> + @rtype: Array
> + """
> + return self.__getEventPathItem(os.path.join(self.getPathToTraceEvent(), "format"))
> +
> + def getID(self):
> + """
> + Returns the contents of the file "id" in the trace event directory.
> +
> + @return: Returns the contents of the file "id" in the trace event
> + directory.
> + @rtype: String
> + """
> + fileContents = self.__getEventPathItem(os.path.join(self.getPathToTraceEvent(), "id"))
> + if (len(fileContents) > 0):
> + return fileContents[0].rstrip()
> + else:
> + return ""
> +
> + def setEventEnable(self, eventEnableString):
> + """
> + Set the trace event to either enabled(1) or disabled(0) by writing to
> + the trace event's "enable" file.
> +
> + @param eventEnableString: The value of the string should be 1 for
> + enabled or 0 for disabled.
> + @param eventEnableString: String
> + """
> + if ((eventEnableString == "0") or (eventEnableString == "1")):
> + self.__setEventPathItem(os.path.join(self.getPathToTraceEvent(), "enable"), eventEnableString, appendToFile=False)
> + else:
> + message = "The trace event \"enable\" file only accepts the values of 0 or 1. The value %s will not be written: %s." %(eventEnableString)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> +
> +class TraceEvents:
> + """
> + A class that is a container for multiple trace events that are located in a
> + directory.
> + """
> + def __init__(self, pathToTraceEvents):
> + """
> + @param pathToTraceEvents: The path to the directory that contains trace
> + events.
> + @type pathToTraceEvents: String
> + """
> + self.__pathToTraceEvents = pathToTraceEvents
> + self.__traceEventsMap = self.__generateTraceEvents()
> +
> + def __generateTraceEvents(self):
> + """
> + Generates a map of all the trace events.
> +
> + @return: Returns a map of all the TraceEvent found.
> + @rtype: Dict
> + """
> + traceEventsMap = {}
> + if (not os.path.exists(self.__pathToTraceEvents)):
> + message = "The path does not exist: %s" %(self.__pathToTraceEvents)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + return traceEventsMap
> + elif (os.path.isdir(self.__pathToTraceEvents)):
> + dirlist = []
> + try:
> + dirlist = os.listdir(self.__pathToTraceEvents)
> + except OSError:
> + message = "There was error listing contents of the directory: %s" %(self.__pathToTraceEvents)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + for item in dirlist:
> + pathToItem = os.path.join(PATH_TO_GFS2_TRACE_EVENTS_DIR, item)
> + if (os.path.isdir(pathToItem)):
> + traceEvent = TraceEvent(pathToItem)
> + traceEventsMap[traceEvent.getName()] = traceEvent
> + return traceEventsMap
> +
> + def getPathToTraceEvents(self):
> + """
> + Returns the path to the directory that contains all the trace events.
> +
> + @return: Return the path to the directory that contains all the trace
> + events.
> + @rtype: String
> + """
> + return self.__pathToTraceEvents
> +
> + def getTraceEventNames(self):
> + """
> + Returns a list of all the trace event names found.
> +
> + @return: Returns a list of all the trace event names found.
> + @rtype: Array
> + """
> + return self.__traceEventsMap.keys()
> +
> + def getTraceEvent(self, traceEventName):
> + """
> + Returns a TraceEvent that matches the traceEventName. If no match is
> + found then None is returned.
> +
> + @return: Returns a TraceEvent that matches the traceEventName. If no
> + match is found then None is returned.
> + @rtype: TraceEvent
> + """
> + if (self.__traceEventsMap.has_key(traceEventName)):
> + return self.__traceEventsMap.get(traceEventName)
> + return None
> +
> +# #####################################################################
> +# Helper functions.
> +# #####################################################################
> +def runCommand(command, listOfCommandOptions, standardOut=subprocess.PIPE, standardError=subprocess.PIPE):
> + """
> + This function will execute a command. It will return True if the return code
> + was zero, otherwise False is returned.
> +
> + @return: Returns True if the return code was zero, otherwise False is
> + returned.
> + @rtype: Boolean
> +
> + @param command: The command that will be executed.
> + @type command: String
> + @param listOfCommandOptions: The list of options for the command that will
> + be executed.
> + @type listOfCommandOptions: Array
> + @param standardOut: The pipe that will be used to write standard output. By
> + default the pipe that is used is subprocess.PIPE.
> + @type standardOut: Pipe
> + @param standardError: The pipe that will be used to write standard error. By
> + default the pipe that is used is subprocess.PIPE.
> + @type standardError: Pipe
> + """
> + stdout = ""
> + stderr = ""
> + try:
> + commandList = [command]
> + commandList += listOfCommandOptions
> + task = subprocess.Popen(commandList, stdout=standardOut, stderr=standardError)
> + task.wait()
> + (stdout, stderr) = task.communicate()
> + return (task.returncode == 0)
> + except OSError:
> + commandOptionString = ""
> + for option in listOfCommandOptions:
> + commandOptionString += "%s " %(option)
> + message = "An error occurred running the command: $ %s %s\n" %(command, commandOptionString)
> + if (len(stdout) > 0):
> + message += stdout
> + message += "\n"
> + if (len(stderr) > 0):
> + message += stderr
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + return False
> +
> +def mountFilesystem(filesystemType, pathToDevice, pathToMountPoint):
> + """
> + This function will attempt to mount a filesystem. If the filesystem is
> + already mounted or the filesystem was successfully mounted then True is
> + returned, otherwise False is returned.
> +
> + @return: If the filesystem is already mounted or the filesystem was
> + successfully mounted then True is returned, otherwise False is returned.
> + @rtype: Boolean
> +
> + @param filesystemType: The type of filesystem that will be mounted.
> + @type filesystemType: String
> + @param pathToDevice: The path to the device that will be mounted.
> + @type pathToDevice: String
> + @param pathToMountPoint: The path to the directory that will be used as the
> + mount point for the device.
> + @type pathToMountPoint: String
> + """
> + if (os.path.ismount(PATH_TO_DEBUG_DIR)):
> + return True
> + listOfCommandOptions = ["-t", filesystemType, pathToDevice, pathToMountPoint]
> + if (not runCommand("mount", listOfCommandOptions)):
> + message = "There was an error mounting the filesystem type %s for the device %s to the mount point %s." %(filesystemType, pathToDevice, pathToMountPoint)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + return os.path.ismount(PATH_TO_DEBUG_DIR)
> +
> +def exitScript(removePidFile=True, errorCode=0):
> + """
> + This function will cause the script to exit or quit. It will return an error
> + code and will remove the pid file that was created.
> +
> + @param removePidFile: If True(default) then the pid file will be remove
> + before the script exits.
> + @type removePidFile: Boolean
> + @param errorCode: The exit code that will be returned. The default value is 0.
> + @type errorCode: Int
> + """
> + if (removePidFile):
> + message = "Removing the pid file: %s" %(PATH_TO_PID_FILENAME)
> + logging.getLogger(MAIN_LOGGER_NAME).debug(message)
> + if (os.path.exists(PATH_TO_PID_FILENAME)):
> + try:
> + os.remove(PATH_TO_PID_FILENAME)
> + except IOError:
> + message = "There was an error removing the file: %s." %(PATH_TO_PID_FILENAME)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + message = "The script will exit."
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + sys.exit(errorCode)
> +
> +def getMountedGFS2Filesystems():
> + """
> + This function returns a list of all the mounted GFS2 filesystems.
> +
> + @return: Returns a list of all the mounted GFS2 filesystems.
> + @rtype: Array
> + """
> + fsType = "gfs2"
> + listOfMountedFilesystems = []
> + dataOutput = FileUtils.getDataFromFile("/proc/mounts")
> + if (not dataOutput == None):
> + for line in dataOutput:
> + splitLine = line.split()
> + if (len(splitLine) > 0):
> + if (splitLine[2] == fsType):
> + listOfMountedFilesystems.append(line)
> + return listOfMountedFilesystems
> +# ##############################################################################
> +# Get user selected options
> +# ##############################################################################
> +def __getOptions(version) :
> + """
> + This function creates the OptionParser and returns commandline
> + a tuple of the selected commandline options and commandline args.
> +
> + The cmdlineOpts which is the options user selected and cmdLineArgs
> + is value passed and not associated with an option.
> +
> + @return: A tuple of the selected commandline options and commandline args.
> + @rtype: Tuple
> +
> + @param version: The version of the this script.
> + @type version: String
> + """
> + cmdParser = OptionParserExtended(version)
> + cmdParser.add_option("-d", "--debug",
> + action="store_true",
> + dest="enableDebugLogging",
> + help="enables debug logging",
> + default=False)
> + cmdParser.add_option("-q", "--quiet",
> + action="store_true",
> + dest="disableLoggingToConsole",
> + help="disables logging to console",
> + default=False)
> + cmdParser.add_option("-l", "--list",
> + action="store_true",
> + dest="listTraceEvents",
> + help="lists the enabled state and filters for the GFS2 trace events",
> + default=False)
> + cmdParser.add_option("-E", "--enable_all_trace_events",
> + action="store_true",
> + dest="enableAllTraceEvents",
> + help="enables all trace_events for GFS2",
> + default=False)
> + cmdParser.add_option("-e", "--enable_trace_event",
> + action="extend",
> + dest="enableTraceEventsList",
> + help="selected trace_events that will be enabled for GFS2",
> + type="string",
> + metavar="<trace event name>",
> + default=[])
> + cmdParser.add_option("-N", "--disable_all_trace_events",
> + action="store_true",
> + dest="disableAllTraceEvents",
> + help="disables all trace_events for GFS2",
> + default=False)
> + cmdParser.add_option("-n", "--disable_trace_event",
> + action="extend",
> + dest="disableTraceEventsList",
> + help="selected trace_events that will be disabled for GFS2",
> + type="string",
> + metavar="<trace event name>",
> + default=[])
> + cmdParser.add_option("-c", "--capture",
> + action="store",
> + dest="pathToOutputFilename",
> + help="enables capturing of trace events and will save the data to a file",
> + type="string",
> + metavar="<output filename>",
> + default="")
> +
> + (cmdLineOpts, cmdLineArgs) = cmdParser.parse_args()
> + return (cmdLineOpts, cmdLineArgs)
> +
> +# ##############################################################################
> +# OptParse classes for commandline options
> +# ##############################################################################
> +class OptionParserExtended(OptionParser):
> + """
> + This is the class that gets the command line options the end user
> + selects.
> + """
> + def __init__(self, version) :
> + """
> + @param version: The version of the this script.
> + @type version: String
> + """
> + self.__commandName = os.path.basename(sys.argv[0])
> + versionMessage = "%s %s\n" %(self.__commandName, version)
> +
> + commandDescription ="%s can enable trace events, disable trace events, and capture data from GFS2 trace events.\n"%(self.__commandName)
> + OptionParser.__init__(self, option_class=ExtendOption,
> + version=versionMessage,
> + description=commandDescription)
> +
> + def print_help(self):
> + """
> + Print examples at the bottom of the help message.
> + """
> + exampleMessage = "\nExamples:\n"
> + exampleMessage += "To list the enable status and filter for each trace event.\n"
> + exampleMessage += "# %s -l\n\n" %(self.__commandName)
> + exampleMessage += "To disable all trace events.\n"
> + exampleMessage += "# %s -N\n\n" %(self.__commandName)
> + exampleMessage += "To enable all trace events.\n"
> + exampleMessage += "# %s -E\n\n" %(self.__commandName)
> + exampleMessage += "To disable all trace events and then enable a couple trace events.\n"
> + exampleMessage += "# %s -N -e gfs2_demote_rq,gfs2_glock_state_change,gfs2_promote\n\n" %(self.__commandName)
> + exampleMessage += "To capture all the trace events and write to the file /tmp/gfs2_trace.log.\n"
> + exampleMessage += "# %s -c /tmp/gfs2_trace.log\n\n" %(self.__commandName)
> + exampleMessage += "To disable all trace events and then enable a couple trace events and capture the output to a file.\n"
> + exampleMessage += "# %s -N -e gfs2_demote_rq,gfs2_glock_state_change,gfs2_promote -c /tmp/gfs2_trace.log\n" %(self.__commandName)
> + self.print_version()
> + OptionParser.print_help(self)
> + print exampleMessage
> +
> +class ExtendOption (Option):
> + """
> + Allow to specify comma delimited list of entries for arrays
> + and dictionaries.
> + """
> + ACTIONS = Option.ACTIONS + ("extend",)
> + STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
> + TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
> +
> + def take_action(self, action, dest, opt, value, values, parser):
> + """
> + This function is a wrapper to take certain options passed on command
> + prompt and wrap them into an Array.
> +
> + @param action: The type of action that will be taken. For example:
> + "store_true", "store_false", "extend".
> + @type action: String
> + @param dest: The name of the variable that will be used to store the
> + option.
> + @type dest: String/Boolean/Array
> + @param opt: The option string that triggered the action.
> + @type opt: String
> + @param value: The value of opt(option) if it takes a
> + value, if not then None.
> + @type value:
> + @param values: All the opt(options) in a dictionary.
> + @type values: Dictionary
> + @param parser: The option parser that was orginally called.
> + @type parser: OptionParser
> + """
> + if (action == "extend") :
> + valueList=[]
> + try:
> + for v in value.split(","):
> + # Need to add code for dealing with paths if there is option for paths.
> + valueList.append(v)
> + except:
> + pass
> + else:
> + values.ensure_value(dest, []).extend(valueList)
> + else:
> + Option.take_action(self, action, dest, opt, value, values, parser)
> +
> +# ###############################################################################
> +# Main Function
> +# ###############################################################################
> +if __name__ == "__main__":
> + try:
> + # #######################################################################
> + # Get the options from the commandline.
> + # #######################################################################
> + (cmdLineOpts, cmdLineArgs) = __getOptions(VERSION_NUMBER)
> +
> + # #######################################################################
> + # Setup the logger
> + # #######################################################################
> + # Create the logger
> + logLevel = logging.INFO
> + logger = logging.getLogger(MAIN_LOGGER_NAME)
> + logger.setLevel(logLevel)
> + # Create a new status function and level.
> + logging.STATUS = logging.INFO + 2
> + logging.addLevelName(logging.STATUS, "STATUS")
> + # Create a function for the STATUS_LEVEL since not defined by python. This
> + # means you can call it like the other predefined message
> + # functions. Example: logging.getLogger("loggerName").status(message)
> + setattr(logger, "status", lambda *args: logger.log(logging.STATUS, *args))
> + streamHandler = logging.StreamHandler()
> + streamHandler.setFormatter(logging.Formatter("%(levelname)s %(message)s"))
> + logger.addHandler(streamHandler)
> +
> + # Set options on logger for debugging or no logging.
> + if (cmdLineOpts.disableLoggingToConsole):
> + logging.disable(logging.CRITICAL)
> + elif (cmdLineOpts.enableDebugLogging) :
> + logging.getLogger(MAIN_LOGGER_NAME).setLevel(logging.DEBUG)
> + message = "Debugging has been enabled."
> + logging.getLogger(MAIN_LOGGER_NAME).debug(message)
> +
> + # #######################################################################
> + # Check to see if pid file exists and error if it does.
> + # #######################################################################
> + if (os.path.exists(PATH_TO_PID_FILENAME)):
> + message = "The PID file %s already exists and this script cannot run till it does not exist." %(PATH_TO_PID_FILENAME)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + message = "Verify that there are no other existing processes running. If there are running processes those need to be stopped first and the file removed."
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + exitScript(removePidFile=False, errorCode=1)
> + else:
> + message = "Creating the pid file: %s" %(PATH_TO_PID_FILENAME)
> + logging.getLogger(MAIN_LOGGER_NAME).debug(message)
> + # Creata the pid file so we dont have more than 1 process of this
> + # script running.
> + FileUtils.writeToFile(PATH_TO_PID_FILENAME, str(os.getpid()), createFile=True)
> +
> + # #######################################################################
> + # Check to see if there any GFS2 filesystems mounted, if not then exit.
> + # #######################################################################
> + if (not len(getMountedGFS2Filesystems()) > 0):
> + message = "There was no GFS2 file-systems mounted."
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + exitScript(errorCode=1)
> +
> + # #######################################################################
> + # Check to see if the debug directory is mounted. If not then
> + # log an error.
> + # #######################################################################
> + if(mountFilesystem("debugfs", "none", PATH_TO_DEBUG_DIR)):
> + message = "The debug filesystem %s is mounted." %(PATH_TO_DEBUG_DIR)
> + logging.getLogger(MAIN_LOGGER_NAME).debug(message)
> + else:
> + message = "There was a problem mounting the debug filesystem: %s" %(PATH_TO_DEBUG_DIR)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + message = "The debug filesystem is required to be mounted for this script to run."
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + exitScript(errorCode=1)
> +
> + # #######################################################################
> + # List of the enable state and filters for each trace event
> + # #######################################################################
> + traceEvents = TraceEvents(PATH_TO_GFS2_TRACE_EVENTS_DIR)
> + listOfTraceEventNames = traceEvents.getTraceEventNames()
> +
> + if (cmdLineOpts.listTraceEvents):
> + listOfTraceEventNames.sort()
> + maxTraceEventNameSize = len(max(listOfTraceEventNames, key=len))
> + traceEventsString = ""
> + for traceEventName in listOfTraceEventNames:
> + traceEvent = traceEvents.getTraceEvent(traceEventName)
> + if (not traceEventName == None):
> + traceEventEnableStatus = "UNKNOWN"
> + if (traceEvent.getEnable() == "0"):
> + traceEventEnableStatus = "DISABLED"
> + elif (traceEvent.getEnable() == "1"):
> + traceEventEnableStatus = "ENABLED"
> + whitespaces = ""
> + for i in range(0, (maxTraceEventNameSize - len(traceEventName))):
> + whitespaces += " "
> + traceEventsString += "%s %s%s\n" %(traceEventName, whitespaces, traceEventEnableStatus)
> + # Disable logging to console except for debug when we print into information to console.
> + logging.disable(logging.CRITICAL)
> + if (len(traceEventsString) > 0):
> + print "trace event name trace event status"
> + print "---------------- ------------------"
> + print traceEventsString.rstrip()
> + exitScript()
> + # #######################################################################
> + # Enable or Disable Trace Events
> + # #######################################################################
> + if (cmdLineOpts.disableAllTraceEvents):
> + message = "Disabling all trace events."
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + for traceEventName in listOfTraceEventNames:
> + traceEvent = traceEvents.getTraceEvent(traceEventName)
> + if (not traceEvent == None):
> + traceEvent.setEventEnable("0")
> + elif (cmdLineOpts.enableAllTraceEvents):
> + message = "Enabling all trace events."
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + for traceEventName in listOfTraceEventNames:
> + traceEvent = traceEvents.getTraceEvent(traceEventName)
> + if (not traceEvent == None):
> + traceEvent.setEventEnable("1")
> +
> + if (len(cmdLineOpts.disableTraceEventsList) > 0):
> + for traceEventName in cmdLineOpts.disableTraceEventsList:
> + traceEvent = traceEvents.getTraceEvent(traceEventName)
> + if (not traceEvent == None):
> + message = "Disabling the selected trace event: %s" %(traceEvent.getName())
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + traceEvent.setEventEnable("0")
> + if (len(cmdLineOpts.enableTraceEventsList) > 0):
> + for traceEventName in cmdLineOpts.enableTraceEventsList:
> + traceEvent = traceEvents.getTraceEvent(traceEventName)
> + if (not traceEvent == None):
> + message = "Enabling the selected trace event: %s" %(traceEvent.getName())
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + traceEvent.setEventEnable("1")
> +
> + # #######################################################################
> + # Capture the data generate by the trace events.
> + # #######################################################################
> + if (len(cmdLineOpts.pathToOutputFilename) > 0):
> + # Read from tracing pipe and write the output to a file.
> + message = "The capturing of the trace events that were enabled to a file will be started by reading the the trace pipe: %s." %(PATH_TO_TRACE_PIPE)
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + message = "Leave this script running until you have capture all the data, then hit control-c to exit."
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + try:
> + fout = open(cmdLineOpts.pathToOutputFilename, "w")
> + for line in fileinput.input(PATH_TO_TRACE_PIPE):
> + fout.write(line)
> + fout.close()
> + message = "The data was written to this file: %s" %(cmdLineOpts.pathToOutputFilename)
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + except KeyboardInterrupt:
> + message = "A control-c was detected and the capturing of trace events data will stop."
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + fout.close()
> + message = "The data was written to this file: %s" %(cmdLineOpts.pathToOutputFilename)
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + except UnicodeEncodeError, e:
> + message = "There was a unicode encode error writing to the file: %s." %(cmdLineOpts.pathToOutputFilename)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + except IOError:
> + message = "There was an error writing to the file: %s." %(cmdLineOpts.pathToOutputFilename)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + message = "The capturing of the trace event data has completed."
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + # Compress the file so that it will have a smaller file size.
> + pathToTarFilename = "%s.tar.bz2" %(os.path.splitext(cmdLineOpts.pathToOutputFilename)[0])
> + message = "Creating a compressed archvied file: %s" %(pathToTarFilename)
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + try:
> + tar = tarfile.open(pathToTarFilename, "w:bz2")
> + tar.add(cmdLineOpts.pathToOutputFilename, arcname=os.path.basename(cmdLineOpts.pathToOutputFilename))
> + tar.close()
> + message = "The compressed archvied file was created: %s" %(pathToTarFilename)
> + logging.getLogger(MAIN_LOGGER_NAME).info(message)
> + except tarfile.TarError:
> + message = "There was an error creating the tarfile: %s." %(pathToTarFilename)
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + except KeyboardInterrupt:
> + print ""
> + message = "This script will exit since control-c was executed by end user."
> + logging.getLogger(MAIN_LOGGER_NAME).error(message)
> + exitScript()
> + # #######################################################################
> + # Exit the application with zero exit code since we cleanly exited.
> + # #######################################################################
> + exitScript()
>
More information about the Cluster-devel
mailing list