Back to home page

OSCL-LXR

 
 

    


0001 # report time spent in compaction
0002 # Licensed under the terms of the GNU GPL License version 2
0003 
0004 # testing:
0005 # 'echo 1 > /proc/sys/vm/compact_memory' to force compaction of all zones
0006 
0007 import os
0008 import sys
0009 import re
0010 
0011 import signal
0012 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
0013 
0014 usage = "usage: perf script report compaction-times.py -- [-h] [-u] [-p|-pv] [-t | [-m] [-fs] [-ms]] [pid|pid-range|comm-regex]\n"
0015 
0016 class popt:
0017     DISP_DFL = 0
0018     DISP_PROC = 1
0019     DISP_PROC_VERBOSE=2
0020 
0021 class topt:
0022     DISP_TIME = 0
0023     DISP_MIG = 1
0024     DISP_ISOLFREE = 2
0025     DISP_ISOLMIG = 4
0026     DISP_ALL = 7
0027 
0028 class comm_filter:
0029     def __init__(self, re):
0030         self.re = re
0031 
0032     def filter(self, pid, comm):
0033         m = self.re.search(comm)
0034         return m == None or m.group() == ""
0035 
0036 class pid_filter:
0037     def __init__(self, low, high):
0038         self.low = (0 if low == "" else int(low))
0039         self.high = (0 if high == "" else int(high))
0040 
0041     def filter(self, pid, comm):
0042         return not (pid >= self.low and (self.high == 0 or pid <= self.high))
0043 
0044 def set_type(t):
0045     global opt_disp
0046     opt_disp = (t if opt_disp == topt.DISP_ALL else opt_disp|t)
0047 
0048 def ns(sec, nsec):
0049     return (sec * 1000000000) + nsec
0050 
0051 def time(ns):
0052     return "%dns" % ns if opt_ns else "%dus" % (round(ns, -3) / 1000)
0053 
0054 class pair:
0055     def __init__(self, aval, bval, alabel = None, blabel = None):
0056         self.alabel = alabel
0057         self.blabel = blabel
0058         self.aval = aval
0059         self.bval = bval
0060 
0061     def __add__(self, rhs):
0062         self.aval += rhs.aval
0063         self.bval += rhs.bval
0064         return self
0065 
0066     def __str__(self):
0067         return "%s=%d %s=%d" % (self.alabel, self.aval, self.blabel, self.bval)
0068 
0069 class cnode:
0070     def __init__(self, ns):
0071         self.ns = ns
0072         self.migrated = pair(0, 0, "moved", "failed")
0073         self.fscan = pair(0,0, "scanned", "isolated")
0074         self.mscan = pair(0,0, "scanned", "isolated")
0075 
0076     def __add__(self, rhs):
0077         self.ns += rhs.ns
0078         self.migrated += rhs.migrated
0079         self.fscan += rhs.fscan
0080         self.mscan += rhs.mscan
0081         return self
0082 
0083     def __str__(self):
0084         prev = 0
0085         s = "%s " % time(self.ns)
0086         if (opt_disp & topt.DISP_MIG):
0087             s += "migration: %s" % self.migrated
0088             prev = 1
0089         if (opt_disp & topt.DISP_ISOLFREE):
0090             s += "%sfree_scanner: %s" % (" " if prev else "", self.fscan)
0091             prev = 1
0092         if (opt_disp & topt.DISP_ISOLMIG):
0093             s += "%smigration_scanner: %s" % (" " if prev else "", self.mscan)
0094         return s
0095 
0096     def complete(self, secs, nsecs):
0097         self.ns = ns(secs, nsecs) - self.ns
0098 
0099     def increment(self, migrated, fscan, mscan):
0100         if (migrated != None):
0101             self.migrated += migrated
0102         if (fscan != None):
0103             self.fscan += fscan
0104         if (mscan != None):
0105             self.mscan += mscan
0106 
0107 
0108 class chead:
0109     heads = {}
0110     val = cnode(0);
0111     fobj = None
0112 
0113     @classmethod
0114     def add_filter(cls, filter):
0115         cls.fobj = filter
0116 
0117     @classmethod
0118     def create_pending(cls, pid, comm, start_secs, start_nsecs):
0119         filtered = 0
0120         try:
0121             head = cls.heads[pid]
0122             filtered = head.is_filtered()
0123         except KeyError:
0124             if cls.fobj != None:
0125                 filtered = cls.fobj.filter(pid, comm)
0126             head = cls.heads[pid] = chead(comm, pid, filtered)
0127 
0128         if not filtered:
0129             head.mark_pending(start_secs, start_nsecs)
0130 
0131     @classmethod
0132     def increment_pending(cls, pid, migrated, fscan, mscan):
0133         head = cls.heads[pid]
0134         if not head.is_filtered():
0135             if head.is_pending():
0136                 head.do_increment(migrated, fscan, mscan)
0137             else:
0138                 sys.stderr.write("missing start compaction event for pid %d\n" % pid)
0139 
0140     @classmethod
0141     def complete_pending(cls, pid, secs, nsecs):
0142         head = cls.heads[pid]
0143         if not head.is_filtered():
0144             if head.is_pending():
0145                 head.make_complete(secs, nsecs)
0146             else:
0147                 sys.stderr.write("missing start compaction event for pid %d\n" % pid)
0148 
0149     @classmethod
0150     def gen(cls):
0151         if opt_proc != popt.DISP_DFL:
0152             for i in cls.heads:
0153                 yield cls.heads[i]
0154 
0155     @classmethod
0156     def str(cls):
0157         return cls.val
0158 
0159     def __init__(self, comm, pid, filtered):
0160         self.comm = comm
0161         self.pid = pid
0162         self.val = cnode(0)
0163         self.pending = None
0164         self.filtered = filtered
0165         self.list = []
0166 
0167     def __add__(self, rhs):
0168         self.ns += rhs.ns
0169         self.val += rhs.val
0170         return self
0171 
0172     def mark_pending(self, secs, nsecs):
0173         self.pending = cnode(ns(secs, nsecs))
0174 
0175     def do_increment(self, migrated, fscan, mscan):
0176         self.pending.increment(migrated, fscan, mscan)
0177 
0178     def make_complete(self, secs, nsecs):
0179         self.pending.complete(secs, nsecs)
0180         chead.val += self.pending
0181 
0182         if opt_proc != popt.DISP_DFL:
0183             self.val += self.pending
0184 
0185             if opt_proc == popt.DISP_PROC_VERBOSE:
0186                 self.list.append(self.pending)
0187         self.pending = None
0188 
0189     def enumerate(self):
0190         if opt_proc == popt.DISP_PROC_VERBOSE and not self.is_filtered():
0191             for i, pelem in enumerate(self.list):
0192                 sys.stdout.write("%d[%s].%d: %s\n" % (self.pid, self.comm, i+1, pelem))
0193 
0194     def is_pending(self):
0195         return self.pending != None
0196 
0197     def is_filtered(self):
0198         return self.filtered
0199 
0200     def display(self):
0201         if not self.is_filtered():
0202             sys.stdout.write("%d[%s]: %s\n" % (self.pid, self.comm, self.val))
0203 
0204 
0205 def trace_end():
0206     sys.stdout.write("total: %s\n" % chead.str())
0207     for i in chead.gen():
0208         i.display(),
0209         i.enumerate()
0210 
0211 def compaction__mm_compaction_migratepages(event_name, context, common_cpu,
0212     common_secs, common_nsecs, common_pid, common_comm,
0213     common_callchain, nr_migrated, nr_failed):
0214 
0215     chead.increment_pending(common_pid,
0216         pair(nr_migrated, nr_failed), None, None)
0217 
0218 def compaction__mm_compaction_isolate_freepages(event_name, context, common_cpu,
0219     common_secs, common_nsecs, common_pid, common_comm,
0220     common_callchain, start_pfn, end_pfn, nr_scanned, nr_taken):
0221 
0222     chead.increment_pending(common_pid,
0223         None, pair(nr_scanned, nr_taken), None)
0224 
0225 def compaction__mm_compaction_isolate_migratepages(event_name, context, common_cpu,
0226     common_secs, common_nsecs, common_pid, common_comm,
0227     common_callchain, start_pfn, end_pfn, nr_scanned, nr_taken):
0228 
0229     chead.increment_pending(common_pid,
0230         None, None, pair(nr_scanned, nr_taken))
0231 
0232 def compaction__mm_compaction_end(event_name, context, common_cpu,
0233     common_secs, common_nsecs, common_pid, common_comm,
0234     common_callchain, zone_start, migrate_start, free_start, zone_end,
0235     sync, status):
0236 
0237     chead.complete_pending(common_pid, common_secs, common_nsecs)
0238 
0239 def compaction__mm_compaction_begin(event_name, context, common_cpu,
0240     common_secs, common_nsecs, common_pid, common_comm,
0241     common_callchain, zone_start, migrate_start, free_start, zone_end,
0242     sync):
0243 
0244     chead.create_pending(common_pid, common_comm, common_secs, common_nsecs)
0245 
0246 def pr_help():
0247     global usage
0248 
0249     sys.stdout.write(usage)
0250     sys.stdout.write("\n")
0251     sys.stdout.write("-h    display this help\n")
0252     sys.stdout.write("-p    display by process\n")
0253     sys.stdout.write("-pv   display by process (verbose)\n")
0254     sys.stdout.write("-t    display stall times only\n")
0255     sys.stdout.write("-m    display stats for migration\n")
0256     sys.stdout.write("-fs   display stats for free scanner\n")
0257     sys.stdout.write("-ms   display stats for migration scanner\n")
0258     sys.stdout.write("-u    display results in microseconds (default nanoseconds)\n")
0259 
0260 
0261 comm_re = None
0262 pid_re = None
0263 pid_regex = "^(\d*)-(\d*)$|^(\d*)$"
0264 
0265 opt_proc = popt.DISP_DFL
0266 opt_disp = topt.DISP_ALL
0267 
0268 opt_ns = True
0269 
0270 argc = len(sys.argv) - 1
0271 if argc >= 1:
0272     pid_re = re.compile(pid_regex)
0273 
0274     for i, opt in enumerate(sys.argv[1:]):
0275         if opt[0] == "-":
0276             if opt == "-h":
0277                 pr_help()
0278                 exit(0);
0279             elif opt == "-p":
0280                 opt_proc = popt.DISP_PROC
0281             elif opt == "-pv":
0282                 opt_proc = popt.DISP_PROC_VERBOSE
0283             elif opt == '-u':
0284                 opt_ns = False
0285             elif opt == "-t":
0286                 set_type(topt.DISP_TIME)
0287             elif opt == "-m":
0288                 set_type(topt.DISP_MIG)
0289             elif opt == "-fs":
0290                 set_type(topt.DISP_ISOLFREE)
0291             elif opt == "-ms":
0292                 set_type(topt.DISP_ISOLMIG)
0293             else:
0294                 sys.exit(usage)
0295 
0296         elif i == argc - 1:
0297             m = pid_re.search(opt)
0298             if m != None and m.group() != "":
0299                 if m.group(3) != None:
0300                     f = pid_filter(m.group(3), m.group(3))
0301                 else:
0302                     f = pid_filter(m.group(1), m.group(2))
0303             else:
0304                 try:
0305                     comm_re=re.compile(opt)
0306                 except:
0307                     sys.stderr.write("invalid regex '%s'" % opt)
0308                     sys.exit(usage)
0309                 f = comm_filter(comm_re)
0310 
0311             chead.add_filter(f)