1*4882a593Smuzhiyun# report time spent in compaction 2*4882a593Smuzhiyun# Licensed under the terms of the GNU GPL License version 2 3*4882a593Smuzhiyun 4*4882a593Smuzhiyun# testing: 5*4882a593Smuzhiyun# 'echo 1 > /proc/sys/vm/compact_memory' to force compaction of all zones 6*4882a593Smuzhiyun 7*4882a593Smuzhiyunimport os 8*4882a593Smuzhiyunimport sys 9*4882a593Smuzhiyunimport re 10*4882a593Smuzhiyun 11*4882a593Smuzhiyunimport signal 12*4882a593Smuzhiyunsignal.signal(signal.SIGPIPE, signal.SIG_DFL) 13*4882a593Smuzhiyun 14*4882a593Smuzhiyunusage = "usage: perf script report compaction-times.py -- [-h] [-u] [-p|-pv] [-t | [-m] [-fs] [-ms]] [pid|pid-range|comm-regex]\n" 15*4882a593Smuzhiyun 16*4882a593Smuzhiyunclass popt: 17*4882a593Smuzhiyun DISP_DFL = 0 18*4882a593Smuzhiyun DISP_PROC = 1 19*4882a593Smuzhiyun DISP_PROC_VERBOSE=2 20*4882a593Smuzhiyun 21*4882a593Smuzhiyunclass topt: 22*4882a593Smuzhiyun DISP_TIME = 0 23*4882a593Smuzhiyun DISP_MIG = 1 24*4882a593Smuzhiyun DISP_ISOLFREE = 2 25*4882a593Smuzhiyun DISP_ISOLMIG = 4 26*4882a593Smuzhiyun DISP_ALL = 7 27*4882a593Smuzhiyun 28*4882a593Smuzhiyunclass comm_filter: 29*4882a593Smuzhiyun def __init__(self, re): 30*4882a593Smuzhiyun self.re = re 31*4882a593Smuzhiyun 32*4882a593Smuzhiyun def filter(self, pid, comm): 33*4882a593Smuzhiyun m = self.re.search(comm) 34*4882a593Smuzhiyun return m == None or m.group() == "" 35*4882a593Smuzhiyun 36*4882a593Smuzhiyunclass pid_filter: 37*4882a593Smuzhiyun def __init__(self, low, high): 38*4882a593Smuzhiyun self.low = (0 if low == "" else int(low)) 39*4882a593Smuzhiyun self.high = (0 if high == "" else int(high)) 40*4882a593Smuzhiyun 41*4882a593Smuzhiyun def filter(self, pid, comm): 42*4882a593Smuzhiyun return not (pid >= self.low and (self.high == 0 or pid <= self.high)) 43*4882a593Smuzhiyun 44*4882a593Smuzhiyundef set_type(t): 45*4882a593Smuzhiyun global opt_disp 46*4882a593Smuzhiyun opt_disp = (t if opt_disp == topt.DISP_ALL else opt_disp|t) 47*4882a593Smuzhiyun 48*4882a593Smuzhiyundef ns(sec, nsec): 49*4882a593Smuzhiyun return (sec * 1000000000) + nsec 50*4882a593Smuzhiyun 51*4882a593Smuzhiyundef time(ns): 52*4882a593Smuzhiyun return "%dns" % ns if opt_ns else "%dus" % (round(ns, -3) / 1000) 53*4882a593Smuzhiyun 54*4882a593Smuzhiyunclass pair: 55*4882a593Smuzhiyun def __init__(self, aval, bval, alabel = None, blabel = None): 56*4882a593Smuzhiyun self.alabel = alabel 57*4882a593Smuzhiyun self.blabel = blabel 58*4882a593Smuzhiyun self.aval = aval 59*4882a593Smuzhiyun self.bval = bval 60*4882a593Smuzhiyun 61*4882a593Smuzhiyun def __add__(self, rhs): 62*4882a593Smuzhiyun self.aval += rhs.aval 63*4882a593Smuzhiyun self.bval += rhs.bval 64*4882a593Smuzhiyun return self 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun def __str__(self): 67*4882a593Smuzhiyun return "%s=%d %s=%d" % (self.alabel, self.aval, self.blabel, self.bval) 68*4882a593Smuzhiyun 69*4882a593Smuzhiyunclass cnode: 70*4882a593Smuzhiyun def __init__(self, ns): 71*4882a593Smuzhiyun self.ns = ns 72*4882a593Smuzhiyun self.migrated = pair(0, 0, "moved", "failed") 73*4882a593Smuzhiyun self.fscan = pair(0,0, "scanned", "isolated") 74*4882a593Smuzhiyun self.mscan = pair(0,0, "scanned", "isolated") 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun def __add__(self, rhs): 77*4882a593Smuzhiyun self.ns += rhs.ns 78*4882a593Smuzhiyun self.migrated += rhs.migrated 79*4882a593Smuzhiyun self.fscan += rhs.fscan 80*4882a593Smuzhiyun self.mscan += rhs.mscan 81*4882a593Smuzhiyun return self 82*4882a593Smuzhiyun 83*4882a593Smuzhiyun def __str__(self): 84*4882a593Smuzhiyun prev = 0 85*4882a593Smuzhiyun s = "%s " % time(self.ns) 86*4882a593Smuzhiyun if (opt_disp & topt.DISP_MIG): 87*4882a593Smuzhiyun s += "migration: %s" % self.migrated 88*4882a593Smuzhiyun prev = 1 89*4882a593Smuzhiyun if (opt_disp & topt.DISP_ISOLFREE): 90*4882a593Smuzhiyun s += "%sfree_scanner: %s" % (" " if prev else "", self.fscan) 91*4882a593Smuzhiyun prev = 1 92*4882a593Smuzhiyun if (opt_disp & topt.DISP_ISOLMIG): 93*4882a593Smuzhiyun s += "%smigration_scanner: %s" % (" " if prev else "", self.mscan) 94*4882a593Smuzhiyun return s 95*4882a593Smuzhiyun 96*4882a593Smuzhiyun def complete(self, secs, nsecs): 97*4882a593Smuzhiyun self.ns = ns(secs, nsecs) - self.ns 98*4882a593Smuzhiyun 99*4882a593Smuzhiyun def increment(self, migrated, fscan, mscan): 100*4882a593Smuzhiyun if (migrated != None): 101*4882a593Smuzhiyun self.migrated += migrated 102*4882a593Smuzhiyun if (fscan != None): 103*4882a593Smuzhiyun self.fscan += fscan 104*4882a593Smuzhiyun if (mscan != None): 105*4882a593Smuzhiyun self.mscan += mscan 106*4882a593Smuzhiyun 107*4882a593Smuzhiyun 108*4882a593Smuzhiyunclass chead: 109*4882a593Smuzhiyun heads = {} 110*4882a593Smuzhiyun val = cnode(0); 111*4882a593Smuzhiyun fobj = None 112*4882a593Smuzhiyun 113*4882a593Smuzhiyun @classmethod 114*4882a593Smuzhiyun def add_filter(cls, filter): 115*4882a593Smuzhiyun cls.fobj = filter 116*4882a593Smuzhiyun 117*4882a593Smuzhiyun @classmethod 118*4882a593Smuzhiyun def create_pending(cls, pid, comm, start_secs, start_nsecs): 119*4882a593Smuzhiyun filtered = 0 120*4882a593Smuzhiyun try: 121*4882a593Smuzhiyun head = cls.heads[pid] 122*4882a593Smuzhiyun filtered = head.is_filtered() 123*4882a593Smuzhiyun except KeyError: 124*4882a593Smuzhiyun if cls.fobj != None: 125*4882a593Smuzhiyun filtered = cls.fobj.filter(pid, comm) 126*4882a593Smuzhiyun head = cls.heads[pid] = chead(comm, pid, filtered) 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun if not filtered: 129*4882a593Smuzhiyun head.mark_pending(start_secs, start_nsecs) 130*4882a593Smuzhiyun 131*4882a593Smuzhiyun @classmethod 132*4882a593Smuzhiyun def increment_pending(cls, pid, migrated, fscan, mscan): 133*4882a593Smuzhiyun head = cls.heads[pid] 134*4882a593Smuzhiyun if not head.is_filtered(): 135*4882a593Smuzhiyun if head.is_pending(): 136*4882a593Smuzhiyun head.do_increment(migrated, fscan, mscan) 137*4882a593Smuzhiyun else: 138*4882a593Smuzhiyun sys.stderr.write("missing start compaction event for pid %d\n" % pid) 139*4882a593Smuzhiyun 140*4882a593Smuzhiyun @classmethod 141*4882a593Smuzhiyun def complete_pending(cls, pid, secs, nsecs): 142*4882a593Smuzhiyun head = cls.heads[pid] 143*4882a593Smuzhiyun if not head.is_filtered(): 144*4882a593Smuzhiyun if head.is_pending(): 145*4882a593Smuzhiyun head.make_complete(secs, nsecs) 146*4882a593Smuzhiyun else: 147*4882a593Smuzhiyun sys.stderr.write("missing start compaction event for pid %d\n" % pid) 148*4882a593Smuzhiyun 149*4882a593Smuzhiyun @classmethod 150*4882a593Smuzhiyun def gen(cls): 151*4882a593Smuzhiyun if opt_proc != popt.DISP_DFL: 152*4882a593Smuzhiyun for i in cls.heads: 153*4882a593Smuzhiyun yield cls.heads[i] 154*4882a593Smuzhiyun 155*4882a593Smuzhiyun @classmethod 156*4882a593Smuzhiyun def str(cls): 157*4882a593Smuzhiyun return cls.val 158*4882a593Smuzhiyun 159*4882a593Smuzhiyun def __init__(self, comm, pid, filtered): 160*4882a593Smuzhiyun self.comm = comm 161*4882a593Smuzhiyun self.pid = pid 162*4882a593Smuzhiyun self.val = cnode(0) 163*4882a593Smuzhiyun self.pending = None 164*4882a593Smuzhiyun self.filtered = filtered 165*4882a593Smuzhiyun self.list = [] 166*4882a593Smuzhiyun 167*4882a593Smuzhiyun def __add__(self, rhs): 168*4882a593Smuzhiyun self.ns += rhs.ns 169*4882a593Smuzhiyun self.val += rhs.val 170*4882a593Smuzhiyun return self 171*4882a593Smuzhiyun 172*4882a593Smuzhiyun def mark_pending(self, secs, nsecs): 173*4882a593Smuzhiyun self.pending = cnode(ns(secs, nsecs)) 174*4882a593Smuzhiyun 175*4882a593Smuzhiyun def do_increment(self, migrated, fscan, mscan): 176*4882a593Smuzhiyun self.pending.increment(migrated, fscan, mscan) 177*4882a593Smuzhiyun 178*4882a593Smuzhiyun def make_complete(self, secs, nsecs): 179*4882a593Smuzhiyun self.pending.complete(secs, nsecs) 180*4882a593Smuzhiyun chead.val += self.pending 181*4882a593Smuzhiyun 182*4882a593Smuzhiyun if opt_proc != popt.DISP_DFL: 183*4882a593Smuzhiyun self.val += self.pending 184*4882a593Smuzhiyun 185*4882a593Smuzhiyun if opt_proc == popt.DISP_PROC_VERBOSE: 186*4882a593Smuzhiyun self.list.append(self.pending) 187*4882a593Smuzhiyun self.pending = None 188*4882a593Smuzhiyun 189*4882a593Smuzhiyun def enumerate(self): 190*4882a593Smuzhiyun if opt_proc == popt.DISP_PROC_VERBOSE and not self.is_filtered(): 191*4882a593Smuzhiyun for i, pelem in enumerate(self.list): 192*4882a593Smuzhiyun sys.stdout.write("%d[%s].%d: %s\n" % (self.pid, self.comm, i+1, pelem)) 193*4882a593Smuzhiyun 194*4882a593Smuzhiyun def is_pending(self): 195*4882a593Smuzhiyun return self.pending != None 196*4882a593Smuzhiyun 197*4882a593Smuzhiyun def is_filtered(self): 198*4882a593Smuzhiyun return self.filtered 199*4882a593Smuzhiyun 200*4882a593Smuzhiyun def display(self): 201*4882a593Smuzhiyun if not self.is_filtered(): 202*4882a593Smuzhiyun sys.stdout.write("%d[%s]: %s\n" % (self.pid, self.comm, self.val)) 203*4882a593Smuzhiyun 204*4882a593Smuzhiyun 205*4882a593Smuzhiyundef trace_end(): 206*4882a593Smuzhiyun sys.stdout.write("total: %s\n" % chead.str()) 207*4882a593Smuzhiyun for i in chead.gen(): 208*4882a593Smuzhiyun i.display(), 209*4882a593Smuzhiyun i.enumerate() 210*4882a593Smuzhiyun 211*4882a593Smuzhiyundef compaction__mm_compaction_migratepages(event_name, context, common_cpu, 212*4882a593Smuzhiyun common_secs, common_nsecs, common_pid, common_comm, 213*4882a593Smuzhiyun common_callchain, nr_migrated, nr_failed): 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun chead.increment_pending(common_pid, 216*4882a593Smuzhiyun pair(nr_migrated, nr_failed), None, None) 217*4882a593Smuzhiyun 218*4882a593Smuzhiyundef compaction__mm_compaction_isolate_freepages(event_name, context, common_cpu, 219*4882a593Smuzhiyun common_secs, common_nsecs, common_pid, common_comm, 220*4882a593Smuzhiyun common_callchain, start_pfn, end_pfn, nr_scanned, nr_taken): 221*4882a593Smuzhiyun 222*4882a593Smuzhiyun chead.increment_pending(common_pid, 223*4882a593Smuzhiyun None, pair(nr_scanned, nr_taken), None) 224*4882a593Smuzhiyun 225*4882a593Smuzhiyundef compaction__mm_compaction_isolate_migratepages(event_name, context, common_cpu, 226*4882a593Smuzhiyun common_secs, common_nsecs, common_pid, common_comm, 227*4882a593Smuzhiyun common_callchain, start_pfn, end_pfn, nr_scanned, nr_taken): 228*4882a593Smuzhiyun 229*4882a593Smuzhiyun chead.increment_pending(common_pid, 230*4882a593Smuzhiyun None, None, pair(nr_scanned, nr_taken)) 231*4882a593Smuzhiyun 232*4882a593Smuzhiyundef compaction__mm_compaction_end(event_name, context, common_cpu, 233*4882a593Smuzhiyun common_secs, common_nsecs, common_pid, common_comm, 234*4882a593Smuzhiyun common_callchain, zone_start, migrate_start, free_start, zone_end, 235*4882a593Smuzhiyun sync, status): 236*4882a593Smuzhiyun 237*4882a593Smuzhiyun chead.complete_pending(common_pid, common_secs, common_nsecs) 238*4882a593Smuzhiyun 239*4882a593Smuzhiyundef compaction__mm_compaction_begin(event_name, context, common_cpu, 240*4882a593Smuzhiyun common_secs, common_nsecs, common_pid, common_comm, 241*4882a593Smuzhiyun common_callchain, zone_start, migrate_start, free_start, zone_end, 242*4882a593Smuzhiyun sync): 243*4882a593Smuzhiyun 244*4882a593Smuzhiyun chead.create_pending(common_pid, common_comm, common_secs, common_nsecs) 245*4882a593Smuzhiyun 246*4882a593Smuzhiyundef pr_help(): 247*4882a593Smuzhiyun global usage 248*4882a593Smuzhiyun 249*4882a593Smuzhiyun sys.stdout.write(usage) 250*4882a593Smuzhiyun sys.stdout.write("\n") 251*4882a593Smuzhiyun sys.stdout.write("-h display this help\n") 252*4882a593Smuzhiyun sys.stdout.write("-p display by process\n") 253*4882a593Smuzhiyun sys.stdout.write("-pv display by process (verbose)\n") 254*4882a593Smuzhiyun sys.stdout.write("-t display stall times only\n") 255*4882a593Smuzhiyun sys.stdout.write("-m display stats for migration\n") 256*4882a593Smuzhiyun sys.stdout.write("-fs display stats for free scanner\n") 257*4882a593Smuzhiyun sys.stdout.write("-ms display stats for migration scanner\n") 258*4882a593Smuzhiyun sys.stdout.write("-u display results in microseconds (default nanoseconds)\n") 259*4882a593Smuzhiyun 260*4882a593Smuzhiyun 261*4882a593Smuzhiyuncomm_re = None 262*4882a593Smuzhiyunpid_re = None 263*4882a593Smuzhiyunpid_regex = "^(\d*)-(\d*)$|^(\d*)$" 264*4882a593Smuzhiyun 265*4882a593Smuzhiyunopt_proc = popt.DISP_DFL 266*4882a593Smuzhiyunopt_disp = topt.DISP_ALL 267*4882a593Smuzhiyun 268*4882a593Smuzhiyunopt_ns = True 269*4882a593Smuzhiyun 270*4882a593Smuzhiyunargc = len(sys.argv) - 1 271*4882a593Smuzhiyunif argc >= 1: 272*4882a593Smuzhiyun pid_re = re.compile(pid_regex) 273*4882a593Smuzhiyun 274*4882a593Smuzhiyun for i, opt in enumerate(sys.argv[1:]): 275*4882a593Smuzhiyun if opt[0] == "-": 276*4882a593Smuzhiyun if opt == "-h": 277*4882a593Smuzhiyun pr_help() 278*4882a593Smuzhiyun exit(0); 279*4882a593Smuzhiyun elif opt == "-p": 280*4882a593Smuzhiyun opt_proc = popt.DISP_PROC 281*4882a593Smuzhiyun elif opt == "-pv": 282*4882a593Smuzhiyun opt_proc = popt.DISP_PROC_VERBOSE 283*4882a593Smuzhiyun elif opt == '-u': 284*4882a593Smuzhiyun opt_ns = False 285*4882a593Smuzhiyun elif opt == "-t": 286*4882a593Smuzhiyun set_type(topt.DISP_TIME) 287*4882a593Smuzhiyun elif opt == "-m": 288*4882a593Smuzhiyun set_type(topt.DISP_MIG) 289*4882a593Smuzhiyun elif opt == "-fs": 290*4882a593Smuzhiyun set_type(topt.DISP_ISOLFREE) 291*4882a593Smuzhiyun elif opt == "-ms": 292*4882a593Smuzhiyun set_type(topt.DISP_ISOLMIG) 293*4882a593Smuzhiyun else: 294*4882a593Smuzhiyun sys.exit(usage) 295*4882a593Smuzhiyun 296*4882a593Smuzhiyun elif i == argc - 1: 297*4882a593Smuzhiyun m = pid_re.search(opt) 298*4882a593Smuzhiyun if m != None and m.group() != "": 299*4882a593Smuzhiyun if m.group(3) != None: 300*4882a593Smuzhiyun f = pid_filter(m.group(3), m.group(3)) 301*4882a593Smuzhiyun else: 302*4882a593Smuzhiyun f = pid_filter(m.group(1), m.group(2)) 303*4882a593Smuzhiyun else: 304*4882a593Smuzhiyun try: 305*4882a593Smuzhiyun comm_re=re.compile(opt) 306*4882a593Smuzhiyun except: 307*4882a593Smuzhiyun sys.stderr.write("invalid regex '%s'" % opt) 308*4882a593Smuzhiyun sys.exit(usage) 309*4882a593Smuzhiyun f = comm_filter(comm_re) 310*4882a593Smuzhiyun 311*4882a593Smuzhiyun chead.add_filter(f) 312