0001
0002
0003
0004
0005
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)