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