#!/usr/bin/python # # Copyright (C) 2016 Kyle Walker # # This copyrighted material is made available to anyone wishing to use, # modify, copy, or redistribute it subject to the terms and conditions # of the GNU General Public License, either version 2 of the License, or # (at your option) any later version # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Description: # When an strace is needing to be run in the background for an extended # period of time, the resulting files can become unnecessarily large. # This utility allows a individual file size limit and number of files to # to be specified. The remainder of the arguments are then passed # immediately to the strace utility in the traditional manner. # # Author: Kyle Walker # # ChangeLog: # * Wed Apr 13 - Kyle Walker # Initial release. # # Example of usage: # # ~]$ stracelogger.py --name test --num 5 --size 100M -- -p 1 -ttfv & # ~]$ ls -l # total 0 # -rw-rw-r--. 1 kwalker kwalker 0 Apr 13 08:32 test-4-13-2016-08:32:24 # from __future__ import print_function import os import sys import time import argparse import subprocess # Global tracker of files used class files: def __init__(self): self.filelist = [] self.openfile = self.newfile() self.lastprint = time.time() def newfile(self): filename = args.name + "-" + time.strftime("%m-%d-%Y-%I:%M:%S") f = open(filename, "w") # Initialize to zero since this avoids having to stat the file outside of the write operation self.cachetell = 0 self.filelist.append(f.name) print("\tOpened: %s" %(f.name)) return f def deletefile(self): print("\tDeleting %s" %(self.filelist[0])) os.remove(self.filelist[0]) self.filelist.pop(0) def cleanup(self): if args.debug: if int(time.time() - self.lastprint) > 1: print("\t\tDEBUG: File: %s\tWrote: %fMib (%db)\tLimit: %db" %(self.openfile.name, btomib(self.cachetell), self.cachetell, mibtob(args.size))) self.lastprint = time.time() if self.cachetell > mibtob( args.size ): if args.debug: print("\t%s - Over size limit of %sMiB - " %(self.openfile.name, args.size), end="") self.openfile = self.newfile() # Check to see if we are over the passed number of overall files if len(self.filelist) >= (args.num + 1): if args.debug: print("\t\tNumber of open files %d exceeds the limit of %d." %(len(self.filelist), args.num)) self.deletefile() def write(self, buf): self.openfile.write(buf) self.cachetell = self.openfile.tell() parser = argparse.ArgumentParser(description='Captures a rolling strace limited by defined values.') parser.add_argument('-d', '--debug', action="count", help='Debugging output. Add additional flags to increase verbosity.') parser.add_argument('--name', required=True, help='The name to prepend to each strace output file.') parser.add_argument('--num', type=int, default=5, help='The number of files to write prior to cleaning up the initial file.') parser.add_argument('--size', type=int, default=100, help='Size limit imposed to any individual strace file (In MiB).') parser.add_argument('-- ', help='Arguments for the strace utility. Note, the "--" delimiter must be used to note the start of strace arguments.') args, unknown = parser.parse_known_args() def btomib(val): return ( val / 1024 / 1024 ) def mibtob(val): return ( val * 1024 * 1024 ) # Check the strace arguments passed. If an incompatible argument is included, the # function returns true def incompatiblearg(straceargs): if "-o" in straceargs: print("-o in the passed strace arguments. Please rerun the command without this flag.") return True if "ff" in straceargs: print("-ff in the passed strace arguments. Please rerun the command without this flag.") return True return None def parsestraceargs(): ignoredargs = ["-d", "--debug", "--name", "--num", "--size"] retstr = "" strargs = None for entry in sys.argv: if "--" in entry and entry not in ignoredargs: strargs = True continue else: if strargs: retstr = "%s %s" %(retstr, entry) if args.debug: print("DEBUG - Adding - %s" %(entry)) if args.debug: retstr = ' '.join(retstr.split()) print("DEBUG - Arguments - '%s'" %(retstr)) # Put the strace arguments into the args member just to simplify things args.straceargs = retstr.split() return incompatiblearg(retstr) def main(): print("Starting:\n\tstrace %s" %(args.straceargs)) try: strace = subprocess.Popen(["strace"] + args.straceargs, stderr=subprocess.PIPE) except OSError as e: if e.errno == os.errno.ENOENT: print("Unable to find the strace utility") # Initialize the tracker variables exit = None buf = "" f = files() # Primary work cycle while not exit: buf = strace.stderr.readline() if args.debug > 3: print("Raw: '%s'" %(buf), end="") f.cleanup() f.write(buf) if not buf: exit = True if __name__ == "__main__": if not parsestraceargs(): main()