diff --git a/guider/guider.py b/guider/guider.py index 04fb880a..57666d03 100755 --- a/guider/guider.py +++ b/guider/guider.py @@ -7,7 +7,7 @@ __credits__ = "Peace Lee" __license__ = "GPLv2" __version__ = "3.9.8" -__revision__ = "240530" +__revision__ = "240531" __maintainer__ = "Peace Lee" __email__ = "iipeace5@gmail.com" __repository__ = "https://github.com/iipeace/guider" @@ -8805,10 +8805,11 @@ def printProgress(current=0, dest=0): @staticmethod def writeFlamegraph(path, samples, title, depth=20): - # flamegraph from https://github.com/rbspy/rbspy/tree/master/src/ui/flamegraph.rs # + # ref> https://github.com/rbspy/rbspy/tree/master/src/ui/flamegraph.rs # # fixed font size: 12, bar height: 15 # - barHeight = 14.4 - titleHeight = (title.count(" 0: + if SysMgr.funcDepth: ksize = len(self.nowCtx["kerStack"]) if ksize >= SysMgr.funcDepth: self.nowCtx["kerLastPos"] = self.nowCtx["kerStack"][ @@ -18775,9 +18776,8 @@ def parseEventInfo(self, tid, func, args, time, core): for event in self.customEventTable if event.endswith(func[:-1]) ]: - cond = self.customEventTable[func[:-1]] = None - else: - cond = self.customEventTable[func[:-1]] + self.customEventTable[func[:-1]] = None + cond = self.customEventTable[func[:-1]] # set event filter # customCnt = self.getCustomEventValue(args, cond) @@ -24562,7 +24562,7 @@ def printTrace(console=False): os.path.join( SysMgr.getTraceEventPath(), targets[0] if targets else "" ), - SysMgr.funcDepth if SysMgr.funcDepth > 0 else 1, + SysMgr.funcDepth if SysMgr.funcDepth else 1, ) sys.exit(0) @@ -26025,7 +26025,7 @@ def doLogTrace(msg=None, level=None): SysMgr.streamEnable = False SysMgr.printDirs( SysMgr.getTraceEventPath(), - SysMgr.funcDepth if SysMgr.funcDepth > 0 else 1, + SysMgr.funcDepth if SysMgr.funcDepth else 1, ) sys.exit(0) @@ -39749,6 +39749,7 @@ def _getDesc(s, t=0): - {3:1} {5:1} and ignore specific syscalls # {0:1} {1:1} -g a.out -q IGNSYSCALL:"mmap*", HIDEIGNSYSCALL # {0:1} {1:1} -g a.out -q IGNSYSCALL:"mmap*", IGNBTFILTER:"*printPeace*" -H + # {0:1} {1:1} -g a.out -q IGNSYSCALL:"*", IGNBTFILTER:"*printPeace*" -H - {4:1} {6:1} without data output for buffer contents # {0:1} {1:1} -I "ls -al" -t process_vm_readv -q ONLYADDR @@ -41296,10 +41297,6 @@ def _getDesc(s, t=0): # {0:1} {1:1} "*service" -q TARGETEXE # {0:1} {1:1} "*service" -q TARGETCMD - - {2:1} only for specific size of areas - # {0:1} {1:1} "a.out, java" -q CHUNKBT:1024000 - # {0:1} {1:1} "a.out, java" -q CHUNKLT:8096000 - - {2:1} with page contents # {0:1} {1:1} "a.out, java" -a # {0:1} {1:1} "a.out, java" -a -q BT:555 @@ -41317,6 +41314,14 @@ def _getDesc(s, t=0): - {2:1} and print duplicated stats for each memory segment # {0:1} {1:1} "a.out, java" -q PRINTDUPSTAT + - {2:1} only for specific size of areas + # {0:1} {1:1} "a.out, java" -q PRINTDUPSTAT, CHUNKBT:1024000 + # {0:1} {1:1} "a.out, java" -q PRINTDUPSTAT, CHUNKLT:8096000 + + - {2:1} only for specific percentage of duplicated chunks + # {0:1} {1:1} "a.out, java" -q PRINTDUPSTAT, PERBT:1024000 + # {0:1} {1:1} "a.out, java" -q PRINTDUPSTAT, PERLT:8096000 + - Check all duplicated page frames of all user processes # {0:1} {1:1} "*" -q ONLYUSER # {0:1} {1:1} "*" -q ONLYUSER, PERPROCINFO -a @@ -53671,7 +53676,7 @@ def checkCmdMode(): # MTRACE MODE # elif SysMgr.checkMode("mtrace"): SysMgr.sysEnable = True - SysMgr.parseSyscallOption("mmap, exit, exit_group") + SysMgr.parseSyscallOption("mmap, mremap, exit, exit_group") SysMgr.addEnvironVar("INCTGINFO") if not SysMgr.outPath: SysMgr.outPath = "." @@ -60711,30 +60716,34 @@ def doCheckDup(): if memFilter: memFilter = list(map(str.encode, memFilter)) + SysMgr.importPkgItems("ctypes", False) + # set condition variables # - condBig = ( - condLess - ) = condCallBig = condCallLess = condChunkBig = condChunkLess = 0 - if "BT" in SysMgr.environList: - condBig = UtilMgr.getEnvironNum("BT", default=0, isInt=True) - if "LT" in SysMgr.environList: - condLess = UtilMgr.getEnvironNum("LT", default=0, isInt=True) - if "INCALLBT" in SysMgr.environList: - condCallBig = UtilMgr.getEnvironNum( - "INCALLBT", default=0, isInt=True - ) - if "INCALLLT" in SysMgr.environList: - condCallLess = UtilMgr.getEnvironNum( - "INCALLLT", default=0, isInt=True - ) - if "CHUNKBT" in SysMgr.environList: - condChunkBig = UtilMgr.getEnvironNum( - "CHUNKBT", default=0, isInt=True - ) - if "CHUNKLT" in SysMgr.environList: - condChunkLess = UtilMgr.getEnvironNum( - "CHUNKLT", default=0, isInt=True - ) + condItems = ( + "BT", + "LT", + "INCALLBT", + "INCALLLT", + "CHUNKBT", + "CHUNKLT", + "PERBT", + "PERLT", + ) + condList = [0] * len(condItems) + for i, v in enumerate(condItems): + if not v in SysMgr.environList: + continue + condList[i] = UtilMgr.getEnvironNum(v, default=0, isInt=True) + ( + condBig, + condLess, + condCallBig, + condCallLess, + condChunkBig, + condChunkLess, + condPerBig, + condPerLess, + ) = condList def _checkInc(m, f): for fstr in f: @@ -61480,7 +61489,9 @@ def _isValidPage(pagemap, idx): # init stat variables # nrDupCnt = 0 + nrDupTotal = 0 dupMems = {} + cmds = [] # read current data # for which, length in chunkList: @@ -61525,6 +61536,7 @@ def _isValidPage(pagemap, idx): ) if printDupStat: nrDupCnt += 1 + nrDupTotal += len(frame) except SystemExit: sys.exit(0) except: @@ -61534,6 +61546,7 @@ def _isValidPage(pagemap, idx): mergeTable[frame] += 1 if printDupStat: nrDupCnt += 1 + nrDupTotal += len(frame) except SystemExit: sys.exit(0) except: @@ -61553,6 +61566,7 @@ def _isValidPage(pagemap, idx): ) if printDupStat: nrDupCnt += 1 + nrDupTotal += len(mem) except SystemExit: sys.exit(0) except: @@ -61562,6 +61576,7 @@ def _isValidPage(pagemap, idx): mergeTable[mem] += 1 if printDupStat: nrDupCnt += 1 + nrDupTotal += len(mem) except SystemExit: sys.exit(0) except: @@ -61570,39 +61585,50 @@ def _isValidPage(pagemap, idx): if printDupStat: dupMems[mem] = dupMems.get(mem, 0) + 1 - # execute commands # + # make commands # if SysMgr.customCmd: - cmds = [] # convert START and SIZE values # for item in SysMgr.customCmd: item = item.replace("START", str(which)) item = item.replace("SIZE", str(length)) cmds.append(item) - # execute remote commands # - dbgObj.executeCmd(cmds, force=True) - SysMgr.printPipe() + # check per filters # + nrChunks = sum(dupMems.values()) + nrDupPer = ( + long(nrDupCnt / float(nrChunks) * 100) if nrChunks else 0 + ) + if condPerBig and nrDupPer < condPerBig: + continue + elif condPerLess and nrDupPer > condPerLess: + continue # print dup stats # - if printDupStat and nrDupCnt: - nrChunks = sum(dupMems.values()) - dupTotal = convSize( - sum([len(i) * v for i, v in dupMems.items()]) - ) - dupPer = long(nrDupCnt / float(nrChunks) * 100) - SysMgr.printInfo( - "found a total of %s[%s](%s%%) duplicates for %s memory chunks from %s-%s(%s)" + if printDupStat and nrDupTotal: + dupPer = "%s%%" % nrDupPer + dupTotal = convSize(nrDupTotal) + if nrDupTotal > 1048576: + dupTotal = convColor(dupTotal, "RED") + SysMgr.printPipe( + ( + "\nfound a total of %s[%s](%s) duplicates " + "for %s memory chunks from %s-%s(%s)" + ) % ( convNum(nrDupCnt), dupTotal, - dupPer, + convColor(dupPer, "YELLOW"), convNum(nrChunks), hex(start), hex(end), - convNum(end - start), + convColor(convNum(end - start), "WARNING"), ) ) + # execute commands # + if cmds: + dbgObj.executeCmd(cmds, force=True) + UtilMgr.deleteProgress() # destroy debugger object # @@ -61613,8 +61639,10 @@ def _isValidPage(pagemap, idx): if printList: removeColor = UtilMgr.removeColor SysMgr.printPipe( - "{0:1} (NrProc: {1:1})\n{2:1}\n{3:>30} {4:>40} {5:1}\n{2:1}".format( - "\n[Process List]", + ( + "\n[Process List] (NrProc: {0:1})\n{1:1}\n" + "{2:>30} {3:>40} {4:1}\n{3:1}" + ).format( convNum(len(procUsageList)), twoLine, "Proc", @@ -63071,7 +63099,7 @@ def _doCommonJobs(pids, procList): Debugger.globalEvent = SysMgr.createShm() # check symbol requirement # - needSymbol = SysMgr.funcDepth > 0 or mode in ( + needSymbol = SysMgr.funcDepth or mode in ( "bind", "breakcall", "hook", @@ -72813,7 +72841,7 @@ def _startFuncGraph(self): SysMgr.streamEnable = False SysMgr.printDirs( SysMgr.getTraceEventPath(), - SysMgr.funcDepth if SysMgr.funcDepth > 0 else 1, + SysMgr.funcDepth if SysMgr.funcDepth else 1, ) sys.exit(0) @@ -92034,7 +92062,7 @@ def _finishPrint(self, needStop=False, term=False, flush=True): # handle rest jobs # if SysMgr.jsonEnable: continue - elif SysMgr.funcDepth > 0: + elif SysMgr.funcDepth: isBtPrinted = True ret = SysMgr.addPrint("%s\n" % oneLine) if not ret: @@ -93045,7 +93073,7 @@ def parseSyscallFilter(self): SysMgr.parseSyscallOption(syscallFilter, ignList, []) # get ignore list # - self.ignSyscallList = {x: 0 for x in ignList} + self.ignSyscallList = {x: 1 for x in ignList} self.hideIgnSyscall = "HIDEIGNSYSCALL" in SysMgr.environList def changeSyscall(self, rep=None): @@ -94494,7 +94522,7 @@ def getBpContext(self, sym, addr, args, isRetBp): diffstr = "%3.6f" % self.vdiff # build backtrace # - if SysMgr.funcDepth > 0: + if SysMgr.funcDepth: # add return address to backtrace # addBt = [addr] if isRetBp else [] @@ -94535,7 +94563,7 @@ def printBpContext(self, sym, addr, fname, checkArg, origPC): # top mode # if self.isRealtime: - if SysMgr.funcDepth > 0: + if SysMgr.funcDepth: backtrace = self.getBacktrace() else: backtrace = None @@ -94943,31 +94971,27 @@ def handleBp(self, printStat=False, checkArg=None): # update comm # if self.needUpdateComm(): - comm = SysMgr.getComm(self.pid, cache=True, save=True) - if self.comm != comm: - self.comm = comm - Debugger.updateCommFlag(False) + self.comm = SysMgr.getComm(self.pid, cache=True, save=True) + Debugger.updateCommFlag(False) - # check calls for memory map update # + # check memory calls # if sym.startswith("mmap"): self.needMapScan = True + # TODO: add mremap # elif sym.startswith("munmap"): unmapAddr = self.readArgs()[0] self.removeBpFileByAddr(unmapAddr) self.needMapScan = True - # check changing-comm calls # + # check comm calls # elif sym == "pthread_setname_np": Debugger.updateCommFlag() elif sym == "prctl": param = self.readArgs()[0] - if ( - param in ConfigMgr.PRCTL_TYPE - and ConfigMgr.PRCTL_TYPE[param] == "PR_SET_NAME" - ): + if ConfigMgr.PRCTL_TYPE.get(param) == "PR_SET_NAME": Debugger.updateCommFlag() # set print flag # - printStat = self.isBreakMode + printStat = self.mode == "break" # print context info # if ( @@ -94999,13 +95023,11 @@ def handleBp(self, printStat=False, checkArg=None): self.unlock(nrLock) return - # check reinstall flag # + # check reinstall or recursive type # reins = ret[2] - if not reins: - self.unlock(nrLock) - return - # check recursive call # - elif isRetBp and sym.endswith(Debugger.RETSTR) in self.entryTime: + if not reins or ( + isRetBp and sym.endswith(Debugger.RETSTR) in self.entryTime + ): self.unlock(nrLock) return @@ -95018,11 +95040,6 @@ def handleBp(self, printStat=False, checkArg=None): self.stop() else: ret = self.ptrace(self.singlestepCmd) - if ret != 0: - SysMgr.printWarn( - "failed to singlestep %s(%s) because %s" - % (self.comm, self.pid, self.errmsg) - ) # check process # ret = self.waitpid() @@ -95035,10 +95052,7 @@ def handleBp(self, printStat=False, checkArg=None): self.unlock(nrLock) # handle pycall # - if ( - self.mode == "pybreak" - and sym.rstrip(Debugger.RETSTR) == SysMgr.pyCallFunc - ): + if self.pyAddr and sym.startswith(SysMgr.pyCallFunc): self.handlePyTrap(sym, fname, addr) def handleTrapEvent(self, stat): @@ -95294,7 +95308,7 @@ def handleSignal(self, sig, warn=False, taskinfo=False): self.addTimelineInt(name, callString) # print backtrace # - if not self.isRealtime and SysMgr.funcDepth > 0: + if not self.isRealtime and SysMgr.funcDepth: # read registers for target # if not self.updateRegs(): sys.exit(-1) @@ -96006,7 +96020,7 @@ def handlePyTrap(self, sym, fname, addr): call = self.prevPySym self.prevPySym = None else: - if bt and SysMgr.funcDepth > 0: + if bt and SysMgr.funcDepth: if SysMgr.showAll: cont = False else: @@ -96227,10 +96241,7 @@ def handleUsercall(self, update=True): fname = offset = "??" # get backtrace # - if self.isRealtime and SysMgr.funcDepth > 0: - backtrace = self.getBacktrace(SysMgr.funcDepth) - else: - backtrace = None + backtrace = self.isRealtime and SysMgr.funcDepth # print unknown call address # if fname == "??": @@ -96514,12 +96525,25 @@ def addTimelineInt(self, sym, bts, gid=-1, sid=-1): } ) + def checkBtFilter(self): + if SysMgr.funcDepth and self.btIgnFilterList: + # get backtrace list # + backtrace = self.getBacktrace(limit=SysMgr.funcDepth, cur=True) + + # convert list to string # + bts = self.getBtStr(backtrace) + + # check backtrace filter # + return not UtilMgr.isValidStr(bts, self.btIgnFilterList) + else: + return True + def handleSyscallOutput(self, args, defer=False, ignored=False): # get diff time # diff = self.vdiff # get backtrace # - if SysMgr.funcDepth > 0: + if SysMgr.funcDepth: # get backtrace list # backtrace = self.getBacktrace(limit=SysMgr.funcDepth, cur=True) @@ -96805,25 +96829,9 @@ def handleSyscall(self): # enter # if self.status == "enter": # ignore syscall # - if self.ignSyscallList and nrSyscall in self.ignSyscallList: - ignored = True - - # get backtrace # - if SysMgr.funcDepth > 0 and self.btIgnFilterList: - # get backtrace list # - backtrace = self.getBacktrace( - limit=SysMgr.funcDepth, cur=True - ) - - # convert list to string # - bts = self.getBtStr(backtrace) - - # check backtrace filter # - if UtilMgr.isValidStr(bts, self.btIgnFilterList): - ignored = True - else: - ignored = False - + if self.ignSyscallList.get(nrSyscall): + # check bt filter # + ignored = not self.checkBtFilter() if ignored: self.changeSyscall() self.clearArgs() @@ -96838,6 +96846,11 @@ def handleSyscall(self): ) return + # check bt filter # + if not self.checkBtFilter(): + self.status = "skip" + return + # check filter # for target in self.filterList: if not UtilMgr.isValidStr(name, [target]): @@ -97004,21 +97017,27 @@ def handleSyscall(self): except: err = "" else: - # handle mmap tracing # + # handle mmap/mremap tracing # if self.isMtraceMode: # get args # args = self.readArgs(syscall=True) - # TODO: add mremap, munmap # + # TODO: add munmap # + + def _getDict(size, name): + return { + "size": size, + "time": self.current, + "stack": self.prevBtStr, + "type": name, + } # handle only MAP_ANON # if name == "mmap": if args[4] == -1: - self.mmapList[retval] = { - "size": args[1], - "time": self.current, - "stack": self.prevBtStr, - } + self.mmapList[retval] = _getDict(args[1], name) + elif name == "mremap": + self.mmapList[retval] = _getDict(args[2], name) # check exit condition for fail # if Debugger.envFlags["ONLYFAIL"]: @@ -97604,7 +97623,7 @@ def handoverNewTarget(self, fork=False): # check memory map # if not self.pmap: # load symbols # - if SysMgr.funcDepth > 0 or not self.mode in ( + if SysMgr.funcDepth or not self.mode in ( "syscall", "signal", "kernel", @@ -97801,7 +97820,7 @@ def restartTrace(self, initStatus=None): dobj.childNum = self.childNum # load symbols and inject breakpoints # - if SysMgr.funcDepth > 0 or not dobj.mode in ( + if SysMgr.funcDepth or not dobj.mode in ( "syscall", "signal", "kernel", @@ -98622,7 +98641,7 @@ def _monitorResource(signum, frame): signal.alarm(SysMgr.intervalEnable) # inst # - if SysMgr.checkMode("utrace") and SysMgr.funcDepth > 0: + if SysMgr.checkMode("utrace") and SysMgr.funcDepth: # set sampling rate for instruction # self.skipInst = SysMgr.funcDepth @@ -98651,7 +98670,7 @@ def _monitorResource(signum, frame): if self.mode in ("pycall", "pybreak"): self.initPyEnv() # load user symbols # - elif SysMgr.funcDepth > 0 or mode not in ( + elif SysMgr.funcDepth or mode not in ( "syscall", "signal", "kernel", @@ -120391,7 +120410,7 @@ def _drawAvgCpu(graphStats, xtype, pos, size): tick_params(direction="in") # update ymax # - if SysMgr.funcDepth > 0: + if SysMgr.funcDepth: ymax = SysMgr.funcDepth # set xticks attributes # @@ -120815,7 +120834,7 @@ def _findNthStr(s, x, n, i=0): # cut stack length # try: - if SysMgr.funcDepth > 0: + if SysMgr.funcDepth: nth = _findNthStr(stack, "\n", SysMgr.funcDepth) stack = stack[:nth] except: @@ -134436,7 +134455,7 @@ def _getStats(root, path, sub, dftDepth, isVer2): for dirpath, subdirs, subfiles in path: # check depth # - if SysMgr.funcDepth > 0: + if SysMgr.funcDepth: depth = dirpath.count("/") - dftDepth if depth >= SysMgr.funcDepth: del subdirs[:]