diff --git a/source/tools/detect/iofsstat/iofsstat.py b/source/tools/detect/iofsstat/iofsstat.py index 841aeb83c52a531b703d2a94726f9e256eb52c09..9bdfd702c545587f9891cdafb39f0ea8b553df7b 100755 --- a/source/tools/detect/iofsstat/iofsstat.py +++ b/source/tools/detect/iofsstat/iofsstat.py @@ -61,6 +61,7 @@ def fixComm(comm, pid): def getFullNameFromProcPid(pid, ino): try: piddir = "/proc/"+str(pid) + dockerRootPath = '' with open(piddir+"/cgroup") as f: #... #cpuset,cpu,cpuacct:/docker/e2afa607d8f13e5b1f89d38ee86d86.... @@ -79,14 +80,17 @@ def getFullNameFromProcPid(pid, ino): path = os.readlink(piddir+"/fd/"+f) if '/dev/' in path or '/proc/' in path or '/sys/' in path: continue - dockerFullPath = dockerRootPath+path + if os.path.isfile(path) and os.stat(path).st_ino == int(ino): if len(dockerRootPath) > 0: return path+"[containterId:%s]" % cid return path - if os.path.isfile(dockerFullPath) and \ - os.stat(dockerFullPath).st_ino == int(ino): - return dockerFullPath+"[containterId:%s]" % cid + + if dockerRootPath != '': + dockerFullPath = dockerRootPath+path + if os.path.isfile(dockerFullPath) and \ + os.stat(dockerFullPath).st_ino == int(ino): + return dockerFullPath+"[containterId:%s]" % cid except (IOError, EOFError) as e: continue @@ -95,31 +99,45 @@ def getFullNameFromProcPid(pid, ino): return "-" def getMntPath(mntfname, fsmountInfo): - if mntfname.isspace() or len(mntfname) is 0: - return fsmountInfo.split('\n')[0].split()[1] + if mntfname.isspace() or len(mntfname) == 0: + return fsmountInfo[0].split()[1] try: - for l in fsmountInfo.split('\n'): + for l in fsmountInfo: if l.find(mntfname)>-1: return l.split()[1] except IndexError: - return fsmountInfo.split('\n')[0].split()[1] + return fsmountInfo[0].split()[1] def getFullName(fileInfoDict): filename = getFullNameFromProcPid(fileInfoDict['pid'], fileInfoDict['ino']) - if filename is '-': + if filename == '-': mntfname=fileInfoDict['mntfname'] fsmountInfo=fileInfoDict['fsmountinfo'] bfname=fileInfoDict['bfname'] - dfname=fileInfoDict['dfname'] - ddfname=fileInfoDict['ddfname'] - mntdir=getMntPath(mntfname,fsmountInfo) - if bfname is dfname or dfname.isspace(): - filename=mntdir+'/.../'+bfname + d1fname=fileInfoDict['d1fname'] + d2fname=fileInfoDict['d2fname'] + d3fname=fileInfoDict['d3fname'] + if len(fsmountInfo) != 0: + mntdir=getMntPath(mntfname,fsmountInfo) + else: + mntdir = '-' + + fileSuffix = '' + if d1fname == '/': + fileSuffix = '/'+bfname else: - if ddfname is dfname or ddfname.isspace(): - filename=mntdir+'/.../'+dfname+'/'+bfname + if d2fname == '/': + fileSuffix = '/'+d1fname+'/'+bfname else: - filename=mntdir+'/.../'+ddfname+'/'+dfname+'/'+bfname + if d3fname == '/': + fileSuffix = '/'+d2fname+'/'+d1fname+'/'+bfname + else: + fileSuffix = '/.../'+d3fname+d2fname+'/'+d1fname+'/'+bfname + if mntdir == '/': + mntdir = '' + if mntdir is None: + mntdir = '-' + filename = mntdir+fileSuffix return filename def echoFile(filename, txt): @@ -143,56 +161,91 @@ def supportKprobe(name): class diskstatClass(object): def __init__(self, devname): self.devname = devname - self.field = {\ - '1':[0,0], '3':[0,0], '4':[0,0],\ - '5':[0,0], '7':[0,0], '8':[0,0],\ - '10':[0,0], '11':[0,0]} + self.fieldDicts = OrderedDict() + self.diskInfoDicts = {} + self.f = open("/proc/diskstats") + + def getDevNameByDevt(self, devt): + return self.diskInfoDicts[str(devt)] def start(self): - with open("/sys/class/block/"+self.devname+"/stat") as f: - stat = f.read().split() - for idx,value in self.field.items(): - value[0] = long(stat[int(idx) - 1]) + fieldDicts = self.fieldDicts + diskInfoDicts = self.diskInfoDicts + self.f.seek(0) + for stat in self.f.readlines(): + stat = stat.split() + if self.devname is not None and self.devname not in stat[2] and \ + stat[2] not in self.devname: + continue + + field = {\ + '1':[0,0], '3':[0,0], '4':[0,0],\ + '5':[0,0], '7':[0,0], '8':[0,0],\ + '10':[0,0], '11':[0,0]} + for idx,value in field.items(): + value[0] = long(stat[int(idx)+2]) + if stat[2] not in fieldDicts.keys(): + fieldDicts.setdefault(stat[2], field) + diskInfoDicts.setdefault(str((int(stat[0])<<20)+int(stat[1])), stat[2]) + else: + fieldDicts[stat[2]].update(field) def stop(self): - with open("/sys/class/block/"+self.devname+"/stat") as f: - stat = f.read().split() - for idx,value in self.field.items(): - value[1] = long(stat[int(idx) - 1]) + fieldDicts = self.fieldDicts + self.f.seek(0) + for stat in self.f.readlines(): + stat = stat.split() + if self.devname is not None and self.devname not in stat[2] and \ + stat[2] not in self.devname: + continue + for idx,value in fieldDicts[stat[2]].items(): + value[1] = long(stat[int(idx)+2]) def show(self, secs): + fieldDicts = self.fieldDicts + + prefix = self.devname if self.devname is not None else 'disk' print('%-20s%-8s%-8s%-12s%-12s%-8s%-8s%-8s%-8s' %\ - ((self.devname+"-stat:"),"r_iops","w_iops","r_bps",\ - "w_bps","wait","r_wait","w_wait","util%")) - r_iops = round((self.field['1'][1]-self.field['1'][0]) / secs, 1) - w_iops = round((self.field['5'][1]-self.field['5'][0]) / secs, 1) - r_bps = (self.field['3'][1]-self.field['3'][0]) / secs * 512 - w_bps = (self.field['7'][1]-self.field['7'][0]) / secs * 512 - r_ticks = self.field['4'][1]-self.field['4'][0] - w_ticks = self.field['8'][1]-self.field['8'][0] - wait = round((r_ticks+w_ticks)/(r_iops+w_iops), 2) if (r_iops+w_iops) else 0 - r_wait = round(r_ticks / r_iops, 2) if r_iops else 0 - w_wait = round(w_ticks / w_iops, 2) if w_iops else 0 - util = round((self.field['10'][1]-self.field['10'][0])*100.0/(secs*1000),2) - util = util if util <= 100 else 100.0 - print('%-20s%-8s%-8s%-12s%-12s%-8s%-8s%-8s%-8s\n' %\ - (' ',str(r_iops),str(w_iops),humConvert(r_bps, True),\ - humConvert(w_bps, True),str(wait),str(r_wait),str(w_wait),\ - str(util))) + (("device-stat:"),"r_iops","w_iops","r_bps",\ + "w_bps","wait","r_wait","w_wait","util%")) + for devname,field in fieldDicts.items(): + if self.devname is not None and devname != self.devname: + continue + r_iops = round((field['1'][1]-field['1'][0]) / secs, 1) + w_iops = round((field['5'][1]-field['5'][0]) / secs, 1) + r_bps = (field['3'][1]-field['3'][0]) / secs * 512 + w_bps = (field['7'][1]-field['7'][0]) / secs * 512 + r_ticks = field['4'][1]-field['4'][0] + w_ticks = field['8'][1]-field['8'][0] + wait = round((r_ticks+w_ticks)/(r_iops+w_iops), 2) if (r_iops+w_iops) else 0 + r_wait = round(r_ticks / r_iops, 2) if r_iops else 0 + w_wait = round(w_ticks / w_iops, 2) if w_iops else 0 + util = round((field['10'][1]-field['10'][0])*100.0/(secs*1000),2) + util = util if util <= 100 else 100.0 + print('%-20s%-8s%-8s%-12s%-12s%-8s%-8s%-8s%-8s' %\ + (devname,str(r_iops),str(w_iops),humConvert(r_bps, True),\ + humConvert(w_bps, True),str(wait),str(r_wait),str(w_wait),\ + str(util))) + print("") + + def clear(self): + self.f.close() class iostatClass(diskstatClass): def __init__(self, devname, pid): super(iostatClass, self).__init__(devname) self.pid = pid self.devname = devname - self.devt = getDevt(self.devname) + self.devt = getDevt(self.devname) if devname is not None else -1 self.tracingDir="/sys/kernel/debug/tracing/instances/iofsstat" self.blkTraceDir=self.tracingDir+"/events/block" - def config(self, devt): + def config(self): + devt = self.devt if not os.path.exists(self.tracingDir): os.mkdir(self.tracingDir) - echoFile(self.blkTraceDir+"/block_getrq/filter", "dev=="+str(devt)) + if devt > 0: + echoFile(self.blkTraceDir+"/block_getrq/filter", "dev=="+str(devt)) echoFile(self.blkTraceDir+"/block_getrq/enable", "1") def start(self): @@ -206,7 +259,9 @@ class iostatClass(diskstatClass): def clear(self): echoFile(self.blkTraceDir+"/block_getrq/enable", "0") - echoFile(self.blkTraceDir+"/block_getrq/filter", "0") + if self.devt > 0: + echoFile(self.blkTraceDir+"/block_getrq/filter", "0") + super(iostatClass, self).clear() def show(self, secs): stat = {} @@ -220,13 +275,18 @@ class iostatClass(diskstatClass): pid = matchObj.group(1).rsplit('-', 1)[1].strip() if self.pid is not None and pid != self.pid: continue + devinfo = oneIO[-6-comm.count(' ')].split(',') + dev = ((int(devinfo[0]) << 20) + int(devinfo[1])) + if self.devt > 0 and self.devt != dev: + continue iotype = oneIO[-5-comm.count(' ')] sectors = oneIO[-2-comm.count(' ')] if bool(stat.has_key(comm)) != True: stat.setdefault(comm, \ {"pid":pid, "iops_rd":0,\ "iops_wr":0, "bps_rd":0,\ - "bps_wr":0, "flushIO":0}) + "bps_wr":0, "flushIO":0,\ + "device":super(iostatClass, self).getDevNameByDevt(dev)}) if 'R' in iotype: stat[comm]["iops_rd"] += 1 stat[comm]["bps_rd"] += (int(sectors) * 512) @@ -237,8 +297,8 @@ class iostatClass(diskstatClass): stat[comm]["flushIO"] += 1 super(iostatClass, self).show(secs) - print('%-20s%-8s%-12s%-16s%-12s%s' %\ - ("comm","pid","iops_rd","bps_rd","iops_wr","bps_wr")) + print('%-20s%-8s%-12s%-16s%-12s%-12s%s' %\ + ("comm","pid","iops_rd","bps_rd","iops_wr","bps_wr","device")) if stat: stat = OrderedDict(sorted(stat.items(),\ key=lambda e:(e[1]["iops_rd"] + e[1]["iops_wr"]),\ @@ -250,25 +310,24 @@ class iostatClass(diskstatClass): item["iops_wr"] /= secs item["bps_rd"] = humConvert(item["bps_rd"]/secs, True) item["bps_wr"] = humConvert(item["bps_wr"]/secs, True) - print('%-20s%-8s%-12s%-16s%-12s%s' %\ + print('%-20s%-8s%-12s%-16s%-12s%-12s%s' %\ (key,str(item["pid"]),str(item["iops_rd"]),\ - item["bps_rd"],str(item["iops_wr"]),item["bps_wr"])) + item["bps_rd"],str(item["iops_wr"]),item["bps_wr"],item["device"])) def entry(self, secs): + loop = True if secs > 0 else False global global_stop - devt = self.devt - if devt == 0: - print("invalid devname '{}'".format(self.devname)) - return - self.config(devt) + self.config() while global_stop != True: + secs = secs if loop == True else 1 print(time.strftime('%Y/%m/%d %H:%M:%S', time.localtime())) self.start() time.sleep(float(secs)) self.stop() self.show(secs) - #time.sleep(float(secs)) print("") + if loop == False: + break self.clear() class fsstatClass(diskstatClass): @@ -277,11 +336,13 @@ class fsstatClass(diskstatClass): self.expression = [] self.pid = pid self.devname = devname - self.devt = getDevt(self.devname) + self.devt = getDevt(self.devname) if devname is not None else -1 tracingBaseDir = "/sys/kernel/debug/tracing" self.kprobeEvent = tracingBaseDir+"/kprobe_events" self.tracingDir = tracingBaseDir+'/instances/iofsstat' self.kprobeDir = self.tracingDir+"/events/kprobes" + self.kprobe = [] + self.inputExp = [] version = execCmd('uname -r').split('.') offflip = '0x0' if int(version[0]) > 3 or \ @@ -303,50 +364,66 @@ class fsstatClass(diskstatClass): # more details see surftrace project: https://github.com/aliyun/surftrace kprobeArgs = 'dev=+0x10(+0x28(+0x20(%s))):u32 '\ 'inode_num=+0x40(+0x20(%s)):u64 len=%s:u64 '\ - 'mntfname=+0x38(+0x0(+0x10(%s))):string '\ - 'bfname=+0x38(+0x18(%s)):string '\ - 'dfname=+0x38(+0x18(+0x18(%s))):string '\ - 'ddfname=+0x38(+0x18(+0x18(+0x18(%s)))):string ' % \ - (argv0,argv0,argv1,argv0,argv0,argv0,argv0) - self.fsmountInfo = execCmd("grep /dev/"+self.devname+" /proc/mounts -wr") - fstype = self.fsmountInfo.split('\n')[0].split()[2] - kprobe = fstype+"_file_write_iter" - if supportKprobe(kprobe): - writeKprobe = 'p '+kprobe+' '+kprobeArgs - elif supportKprobe(fstype+"_file_write"): - kprobe = fstype+"_file_write" - writeKprobe = 'p '+kprobe+' '+kprobeArgs + 'mntfname=+0x0(+0x28(+0x0(+0x10(%s)))):string '\ + 'bfname=+0x0(+0x28(+0x18(%s))):string '\ + 'd1fname=+0x0(+0x28(+0x18(+0x18(%s)))):string '\ + 'd2fname=+0x0(+0x28(+0x18(+0x18(+0x18(%s))))):string '\ + 'd3fname=+0x0(+0x28(+0x18(+0x18(+0x18(+0x18(%s)))))):string ' % \ + (argv0,argv0,argv1,argv0,argv0,argv0,argv0,argv0) + devList = [] + if self.devname is not None: + devList.append('/dev/'+self.devname) else: - print("not available write kprobe") - sys.exit(0) - self.writeKprobe = kprobe - - kprobe = fstype+"_file_read_iter" - if supportKprobe(kprobe): - readKprobe = 'p '+kprobe+' '+kprobeArgs - elif supportKprobe(fstype+"_file_read"): - kprobe = fstype+"_file_read" - readKprobe = 'p '+kprobe+' '+kprobeArgs - elif supportKprobe("generic_file_aio_read"): - kprobe = "generic_file_aio_read" - readKprobe = 'p '+kprobe+' '+kprobeArgs - else: - print("not available read kprobe") - sys.exit(0) - self.readKprobe = kprobe + sysfsBlockDirList = os.listdir("/sys/block") + for dev in sysfsBlockDirList: + devList.append('/dev/'+dev) + with open("/proc/mounts") as f: + self.fsmountInfo = list(filter(lambda x: any(e in x for e in devList), f.readlines())) + + for entry in self.fsmountInfo: + fstype = entry.split()[2] + kprobe = fstype+"_file_write_iter" + if kprobe in self.kprobe: + continue + if supportKprobe(kprobe): + writeKprobe = 'p '+kprobe+' '+kprobeArgs + elif supportKprobe(fstype+"_file_write"): + kprobe = fstype+"_file_write" + writeKprobe = 'p '+kprobe+' '+kprobeArgs + else: + print("not available write kprobe") + sys.exit(0) + self.kprobe.append(kprobe) + self.inputExp.append(writeKprobe) + + kprobe = fstype+"_file_read_iter" + if supportKprobe(kprobe): + readKprobe = 'p '+kprobe+' '+kprobeArgs + elif supportKprobe(fstype+"_file_read"): + kprobe = fstype+"_file_read" + readKprobe = 'p '+kprobe+' '+kprobeArgs + elif supportKprobe("generic_file_aio_read"): + kprobe = "generic_file_aio_read" + readKprobe = 'p '+kprobe+' '+kprobeArgs + else: + print("not available read kprobe") + sys.exit(0) + self.kprobe.append(kprobe) + self.inputExp.append(readKprobe) - self.inputExp=[writeKprobe, readKprobe] - self.expression=self.inputExp + self.expression=self.inputExp self.outlogFormatBase = 10 - def config(self, devt): + def config(self): + devt = self.devt if not os.path.exists(self.tracingDir): os.mkdir(self.tracingDir) for exp in self.expression: echoFileAppend(self.kprobeEvent, exp) probe='p_'+exp.split()[1]+'_0' - filterKprobe=self.kprobeDir+"/"+probe+"/filter" - echoFile(filterKprobe, "dev=="+str(devt)) + if devt > 0: + filterKprobe=self.kprobeDir+"/"+probe+"/filter" + echoFile(filterKprobe, "dev=="+str(devt)) enableKprobe=self.kprobeDir+"/"+probe+"/enable" echoFile(enableKprobe, "1") fmt=execCmd("grep print "+self.kprobeDir+"/"+probe+"/format") @@ -368,41 +445,62 @@ class fsstatClass(diskstatClass): probe='p_'+exp.split()[1]+'_0' enableKprobe=self.kprobeDir+"/"+probe+"/enable" echoFile(enableKprobe, "0") - filterKprobe=self.kprobeDir+"/"+probe+"/filter" - echoFile(filterKprobe, "0") + if self.devt > 0: + filterKprobe=self.kprobeDir+"/"+probe+"/filter" + echoFile(filterKprobe, "0") echoFileAppend(self.kprobeEvent, '-:%s'%probe) + super(fsstatClass, self).clear() def show(self, secs): stat = {} with open(self.tracingDir+"/trace") as f: - traceText = list(filter(lambda x: \ - any(e in x for e in [self.writeKprobe,self.readKprobe]),\ + traceText = list(filter(\ + lambda x: any(e in x for e in self.kprobe),\ f.readlines())) - #pool-1-thread-2-5029 [002] .... 5293018.252338: p_ext4_file_write_iter_0: (ext4_file_write_iter+0x0/0x6d0 [ext4]) dev=265289729 inode_num=530392 len=38 + + #pool-1-thread-2-5029 [002] .... 5293018.252338: p_ext4_file_write_iter_0:\ + # (ext4_file_write_iter+0x0/0x6d0 [ext4]) dev=265289729 inode_num=530392 len=38 #... for entry in traceText: matchObj = re.match(r'(.*) \[([^\[\]]*)\] (.*) dev=(.*) '+\ 'inode_num=(.*) len=(.*) mntfname=(.*) '+\ - 'bfname=(.*) dfname=(.*) ddfname=(.*)\n', entry) - commInfo = matchObj.group(1).rsplit('-', 1) + 'bfname=(.*) d1fname=(.*) d2fname=(.*) d3fname=(.*)\n', entry) + #print(entry) + if matchObj is not None: + commInfo = matchObj.group(1).rsplit('-', 1) + else: + continue pid = commInfo[1].strip() if self.pid is not None and pid != self.pid: continue + dev = int(matchObj.group(4),self.outlogFormatBase) + if self.devt > 0 and self.devt != dev: + continue ino = int(matchObj.group(5),self.outlogFormatBase) if bool(stat.has_key(ino)) != True: comm = fixComm(commInfo[0].lstrip(), pid) if '..' in comm: continue - fileInfoDict = {'mntfname':matchObj.group(7).strip("\""),\ + device = super(fsstatClass, self).getDevNameByDevt(dev) + fsmountinfo = [] + for f in self.fsmountInfo: + if ('/dev/'+device) in f: + fsmountinfo.append(f) + + fileInfoDict = {\ + 'device':device,\ + 'mntfname':matchObj.group(7).strip("\""),\ 'bfname':matchObj.group(8).strip("\""),\ - 'dfname':matchObj.group(9).strip("\""), - 'ddfname':matchObj.group(10).strip("\""),\ - 'fsmountinfo':self.fsmountInfo,\ + 'd1fname':matchObj.group(9).strip("\""), + 'd2fname':matchObj.group(10).strip("\""),\ + 'd3fname':matchObj.group(11).strip("\""),\ + 'fsmountinfo':fsmountinfo,\ 'ino':ino,'pid':pid} stat.setdefault(ino,\ {"comm":comm,"tgid":getTgid(pid),"pid":pid,\ "cnt_wr":0,"bw_wr":0,"cnt_rd":0,"bw_rd":0,\ - "file":getFullName(fileInfoDict)}) + "device":device,\ + "file":getFullName(fileInfoDict)}) size = int(matchObj.group(6),self.outlogFormatBase) if 'write' in entry: stat[ino]["cnt_wr"] += 1 @@ -412,9 +510,9 @@ class fsstatClass(diskstatClass): stat[ino]["bw_rd"] += int(size) super(fsstatClass, self).show(secs) - print("%-20s%-8s%-8s%-8s%-12s%-8s%-12s%-12s%s"\ + print("%-20s%-8s%-8s%-8s%-12s%-8s%-12s%-12s%-12s%s"\ %("comm","tgid","pid","cnt_rd","bw_rd",\ - "cnt_wr","bw_wr","inode","filepath")) + "cnt_wr","bw_wr","inode","device","filepath")) if stat: stat = OrderedDict(sorted(stat.items(),\ key=lambda e:(e[1]["bw_wr"]+e[1]["bw_rd"]),reverse=True)) @@ -425,26 +523,25 @@ class fsstatClass(diskstatClass): item["bw_wr"]=humConvert(item["bw_wr"]/secs,True) item["cnt_rd"]/=secs item["bw_rd"]=humConvert(item["bw_rd"]/secs,True) - print("%-20s%-8s%-8s%-8d%-12s%-8d%-12s%-12s%s"\ + print("%-20s%-8s%-8s%-8d%-12s%-8d%-12s%-12s%-12s%s"\ %(item["comm"],item["tgid"],item["pid"],\ item["cnt_rd"],item["bw_rd"],item["cnt_wr"],\ - item["bw_wr"],key,item["file"])) + item["bw_wr"],key,item["device"],item["file"])) def entry(self, secs): + loop = True if secs > 0 else False global global_stop - devt = self.devt - if devt == 0: - print("invalid devname '{}'".format(self.devname)) - return - self.config(devt) + self.config() while global_stop != True: + secs = secs if loop == True else 1 print(time.strftime('%Y/%m/%d %H:%M:%S', time.localtime())) self.start() time.sleep(float(secs)) self.stop() self.show(secs) - #time.sleep(float(secs)) print("") + if loop == False: + break self.clear() def main(): @@ -452,31 +549,38 @@ def main(): print "This program must be run as root. Aborting." sys.exit(0) examples = """e.g. - ./iofsstat.py -d vda 1 + ./iofsstat.py -i 1 + Report IO statistic for all disk per 1secs + ./iofsstat.py -d vda -i 1 Report IO statistic for vda per 1secs - ./iofsstat.py -d vda1 --fs 1 + ./iofsstat.py --fs -i 1 + Report fs IO statistic for all partitions per 1secs + ./iofsstat.py -d vda1 --fs -i 1 Report fs IO statistic for vda1 per 1secs """ parser = argparse.ArgumentParser( description="Report IO statistic for partitions.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) - parser.add_argument('-d','--device', help='Specify the disk name.') + parser.add_argument('-T','--Timeout', help='Specify the timeout for program exit(secs).') + parser.add_argument('-i','--interval', help='Specify refresh interval(secs).') + parser.add_argument('-d','--device', help='Specify the disk or partition name.') parser.add_argument('-p','--pid', help='Specify the process id.') parser.add_argument('-f','--fs', action='store_true',\ help='Report filesystem statistic for partitions.') - parser.add_argument('interval', help='Specify refresh interval(secs).') args = parser.parse_args() - if args.interval is None or int(args.interval) == 0: - secs = 2 - else: - secs = int(args.interval) + secs = int(args.interval) if args.interval is not None else 0 devname = args.device pid = int(args.pid) if args.pid else None signal.signal(signal.SIGINT, signal_exit_handler) signal.signal(signal.SIGHUP, signal_exit_handler) signal.signal(signal.SIGTERM, signal_exit_handler) + if args.Timeout is not None: + timeoutSec = args.Timeout if args.Timeout > 0 else 10 + signal.signal(signal.SIGALRM, signal_exit_handler) + signal.alarm(int(timeoutSec)) + secs = 1 if args.fs: fsstatClass(devname, pid).entry(secs) else: