184e5d89aSTony Jones# report time spent in compaction
284e5d89aSTony Jones# Licensed under the terms of the GNU GPL License version 2
384e5d89aSTony Jones
484e5d89aSTony Jones# testing:
584e5d89aSTony Jones# 'echo 1 > /proc/sys/vm/compact_memory' to force compaction of all zones
684e5d89aSTony Jones
784e5d89aSTony Jonesimport os
884e5d89aSTony Jonesimport sys
984e5d89aSTony Jonesimport re
1084e5d89aSTony Jones
1184e5d89aSTony Jonesimport signal
1284e5d89aSTony Jonessignal.signal(signal.SIGPIPE, signal.SIG_DFL)
1384e5d89aSTony Jones
1484e5d89aSTony Jonesusage = "usage: perf script report compaction-times.py -- [-h] [-u] [-p|-pv] [-t | [-m] [-fs] [-ms]] [pid|pid-range|comm-regex]\n"
1584e5d89aSTony Jones
1684e5d89aSTony Jonesclass popt:
1784e5d89aSTony Jones	DISP_DFL = 0
1884e5d89aSTony Jones	DISP_PROC = 1
1984e5d89aSTony Jones	DISP_PROC_VERBOSE=2
2084e5d89aSTony Jones
2184e5d89aSTony Jonesclass topt:
2284e5d89aSTony Jones	DISP_TIME = 0
2384e5d89aSTony Jones	DISP_MIG = 1
2484e5d89aSTony Jones	DISP_ISOLFREE = 2
2584e5d89aSTony Jones	DISP_ISOLMIG = 4
2684e5d89aSTony Jones	DISP_ALL = 7
2784e5d89aSTony Jones
2884e5d89aSTony Jonesclass comm_filter:
2984e5d89aSTony Jones	def __init__(self, re):
3084e5d89aSTony Jones		self.re = re
3184e5d89aSTony Jones
3284e5d89aSTony Jones	def filter(self, pid, comm):
3384e5d89aSTony Jones		m = self.re.search(comm)
3484e5d89aSTony Jones		return m == None or m.group() == ""
3584e5d89aSTony Jones
3684e5d89aSTony Jonesclass pid_filter:
3784e5d89aSTony Jones	def __init__(self, low, high):
3884e5d89aSTony Jones		self.low = (0 if low == "" else int(low))
3984e5d89aSTony Jones		self.high = (0 if high == "" else int(high))
4084e5d89aSTony Jones
4184e5d89aSTony Jones	def filter(self, pid, comm):
4284e5d89aSTony Jones		return not (pid >= self.low and (self.high == 0 or pid <= self.high))
4384e5d89aSTony Jones
4484e5d89aSTony Jonesdef set_type(t):
4584e5d89aSTony Jones	global opt_disp
4684e5d89aSTony Jones	opt_disp = (t if opt_disp == topt.DISP_ALL else opt_disp|t)
4784e5d89aSTony Jones
4884e5d89aSTony Jonesdef ns(sec, nsec):
4984e5d89aSTony Jones	return (sec * 1000000000) + nsec
5084e5d89aSTony Jones
5184e5d89aSTony Jonesdef time(ns):
5284e5d89aSTony Jones	return "%dns" % ns if opt_ns else "%dus" % (round(ns, -3) / 1000)
5384e5d89aSTony Jones
5484e5d89aSTony Jonesclass pair:
5584e5d89aSTony Jones	def __init__(self, aval, bval, alabel = None, blabel = None):
5684e5d89aSTony Jones		self.alabel = alabel
5784e5d89aSTony Jones		self.blabel = blabel
5884e5d89aSTony Jones		self.aval = aval
5984e5d89aSTony Jones		self.bval = bval
6084e5d89aSTony Jones
6184e5d89aSTony Jones	def __add__(self, rhs):
6284e5d89aSTony Jones		self.aval += rhs.aval
6384e5d89aSTony Jones		self.bval += rhs.bval
6484e5d89aSTony Jones		return self
6584e5d89aSTony Jones
6684e5d89aSTony Jones	def __str__(self):
6784e5d89aSTony Jones		return "%s=%d %s=%d" % (self.alabel, self.aval, self.blabel, self.bval)
6884e5d89aSTony Jones
6984e5d89aSTony Jonesclass cnode:
7084e5d89aSTony Jones	def __init__(self, ns):
7184e5d89aSTony Jones		self.ns = ns
7284e5d89aSTony Jones		self.migrated = pair(0, 0, "moved", "failed")
7384e5d89aSTony Jones		self.fscan = pair(0,0, "scanned", "isolated")
7484e5d89aSTony Jones		self.mscan = pair(0,0, "scanned", "isolated")
7584e5d89aSTony Jones
7684e5d89aSTony Jones	def __add__(self, rhs):
7784e5d89aSTony Jones		self.ns += rhs.ns
7884e5d89aSTony Jones		self.migrated += rhs.migrated
7984e5d89aSTony Jones		self.fscan += rhs.fscan
8084e5d89aSTony Jones		self.mscan += rhs.mscan
8184e5d89aSTony Jones		return self
8284e5d89aSTony Jones
8384e5d89aSTony Jones	def __str__(self):
8484e5d89aSTony Jones		prev = 0
8584e5d89aSTony Jones		s = "%s " % time(self.ns)
8684e5d89aSTony Jones		if (opt_disp & topt.DISP_MIG):
8784e5d89aSTony Jones			s += "migration: %s" % self.migrated
8884e5d89aSTony Jones			prev = 1
8984e5d89aSTony Jones		if (opt_disp & topt.DISP_ISOLFREE):
9084e5d89aSTony Jones			s += "%sfree_scanner: %s" % (" " if prev else "", self.fscan)
9184e5d89aSTony Jones			prev = 1
9284e5d89aSTony Jones		if (opt_disp & topt.DISP_ISOLMIG):
9384e5d89aSTony Jones			s += "%smigration_scanner: %s" % (" " if prev else "", self.mscan)
9484e5d89aSTony Jones		return s
9584e5d89aSTony Jones
9684e5d89aSTony Jones	def complete(self, secs, nsecs):
9784e5d89aSTony Jones		self.ns = ns(secs, nsecs) - self.ns
9884e5d89aSTony Jones
9984e5d89aSTony Jones	def increment(self, migrated, fscan, mscan):
10084e5d89aSTony Jones		if (migrated != None):
10184e5d89aSTony Jones			self.migrated += migrated
10284e5d89aSTony Jones		if (fscan != None):
10384e5d89aSTony Jones			self.fscan += fscan
10484e5d89aSTony Jones		if (mscan != None):
10584e5d89aSTony Jones			self.mscan += mscan
10684e5d89aSTony Jones
10784e5d89aSTony Jones
10884e5d89aSTony Jonesclass chead:
10984e5d89aSTony Jones	heads = {}
11084e5d89aSTony Jones	val = cnode(0);
11184e5d89aSTony Jones	fobj = None
11284e5d89aSTony Jones
11384e5d89aSTony Jones	@classmethod
11484e5d89aSTony Jones	def add_filter(cls, filter):
11584e5d89aSTony Jones		cls.fobj = filter
11684e5d89aSTony Jones
11784e5d89aSTony Jones	@classmethod
11884e5d89aSTony Jones	def create_pending(cls, pid, comm, start_secs, start_nsecs):
11984e5d89aSTony Jones		filtered = 0
12084e5d89aSTony Jones		try:
12184e5d89aSTony Jones			head = cls.heads[pid]
12284e5d89aSTony Jones			filtered = head.is_filtered()
12384e5d89aSTony Jones		except KeyError:
12484e5d89aSTony Jones			if cls.fobj != None:
12584e5d89aSTony Jones				filtered = cls.fobj.filter(pid, comm)
12684e5d89aSTony Jones			head = cls.heads[pid] = chead(comm, pid, filtered)
12784e5d89aSTony Jones
12884e5d89aSTony Jones		if not filtered:
12984e5d89aSTony Jones			head.mark_pending(start_secs, start_nsecs)
13084e5d89aSTony Jones
13184e5d89aSTony Jones	@classmethod
13284e5d89aSTony Jones	def increment_pending(cls, pid, migrated, fscan, mscan):
13384e5d89aSTony Jones		head = cls.heads[pid]
13484e5d89aSTony Jones		if not head.is_filtered():
13584e5d89aSTony Jones			if head.is_pending():
13684e5d89aSTony Jones				head.do_increment(migrated, fscan, mscan)
13784e5d89aSTony Jones			else:
13884e5d89aSTony Jones				sys.stderr.write("missing start compaction event for pid %d\n" % pid)
13984e5d89aSTony Jones
14084e5d89aSTony Jones	@classmethod
14184e5d89aSTony Jones	def complete_pending(cls, pid, secs, nsecs):
14284e5d89aSTony Jones		head = cls.heads[pid]
14384e5d89aSTony Jones		if not head.is_filtered():
14484e5d89aSTony Jones			if head.is_pending():
14584e5d89aSTony Jones				head.make_complete(secs, nsecs)
14684e5d89aSTony Jones			else:
14784e5d89aSTony Jones				sys.stderr.write("missing start compaction event for pid %d\n" % pid)
14884e5d89aSTony Jones
14984e5d89aSTony Jones	@classmethod
15084e5d89aSTony Jones	def gen(cls):
15184e5d89aSTony Jones		if opt_proc != popt.DISP_DFL:
15284e5d89aSTony Jones			for i in cls.heads:
15384e5d89aSTony Jones				yield cls.heads[i]
15484e5d89aSTony Jones
15584e5d89aSTony Jones	@classmethod
15684e5d89aSTony Jones	def str(cls):
15784e5d89aSTony Jones		return cls.val
15884e5d89aSTony Jones
15984e5d89aSTony Jones	def __init__(self, comm, pid, filtered):
16084e5d89aSTony Jones		self.comm = comm
16184e5d89aSTony Jones		self.pid = pid
16284e5d89aSTony Jones		self.val = cnode(0)
16384e5d89aSTony Jones		self.pending = None
16484e5d89aSTony Jones		self.filtered = filtered
16584e5d89aSTony Jones		self.list = []
16684e5d89aSTony Jones
16784e5d89aSTony Jones	def __add__(self, rhs):
16884e5d89aSTony Jones		self.ns += rhs.ns
16984e5d89aSTony Jones		self.val += rhs.val
17084e5d89aSTony Jones		return self
17184e5d89aSTony Jones
17284e5d89aSTony Jones	def mark_pending(self, secs, nsecs):
17384e5d89aSTony Jones		self.pending = cnode(ns(secs, nsecs))
17484e5d89aSTony Jones
17584e5d89aSTony Jones	def do_increment(self, migrated, fscan, mscan):
17684e5d89aSTony Jones		self.pending.increment(migrated, fscan, mscan)
17784e5d89aSTony Jones
17884e5d89aSTony Jones	def make_complete(self, secs, nsecs):
17984e5d89aSTony Jones		self.pending.complete(secs, nsecs)
18084e5d89aSTony Jones		chead.val += self.pending
18184e5d89aSTony Jones
18284e5d89aSTony Jones		if opt_proc != popt.DISP_DFL:
18384e5d89aSTony Jones			self.val += self.pending
18484e5d89aSTony Jones
18584e5d89aSTony Jones			if opt_proc == popt.DISP_PROC_VERBOSE:
18684e5d89aSTony Jones				self.list.append(self.pending)
18784e5d89aSTony Jones		self.pending = None
18884e5d89aSTony Jones
18984e5d89aSTony Jones	def enumerate(self):
19084e5d89aSTony Jones		if opt_proc == popt.DISP_PROC_VERBOSE and not self.is_filtered():
19184e5d89aSTony Jones			for i, pelem in enumerate(self.list):
19284e5d89aSTony Jones				sys.stdout.write("%d[%s].%d: %s\n" % (self.pid, self.comm, i+1, pelem))
19384e5d89aSTony Jones
19484e5d89aSTony Jones	def is_pending(self):
19584e5d89aSTony Jones		return self.pending != None
19684e5d89aSTony Jones
19784e5d89aSTony Jones	def is_filtered(self):
19884e5d89aSTony Jones		return self.filtered
19984e5d89aSTony Jones
20084e5d89aSTony Jones	def display(self):
20184e5d89aSTony Jones		if not self.is_filtered():
20284e5d89aSTony Jones			sys.stdout.write("%d[%s]: %s\n" % (self.pid, self.comm, self.val))
20384e5d89aSTony Jones
20484e5d89aSTony Jones
20584e5d89aSTony Jonesdef trace_end():
20684e5d89aSTony Jones	sys.stdout.write("total: %s\n" % chead.str())
20784e5d89aSTony Jones	for i in chead.gen():
20884e5d89aSTony Jones		i.display(),
20984e5d89aSTony Jones		i.enumerate()
21084e5d89aSTony Jones
21184e5d89aSTony Jonesdef compaction__mm_compaction_migratepages(event_name, context, common_cpu,
21284e5d89aSTony Jones	common_secs, common_nsecs, common_pid, common_comm,
21384e5d89aSTony Jones	common_callchain, nr_migrated, nr_failed):
21484e5d89aSTony Jones
21584e5d89aSTony Jones	chead.increment_pending(common_pid,
21684e5d89aSTony Jones		pair(nr_migrated, nr_failed), None, None)
21784e5d89aSTony Jones
21884e5d89aSTony Jonesdef compaction__mm_compaction_isolate_freepages(event_name, context, common_cpu,
21984e5d89aSTony Jones	common_secs, common_nsecs, common_pid, common_comm,
22084e5d89aSTony Jones	common_callchain, start_pfn, end_pfn, nr_scanned, nr_taken):
22184e5d89aSTony Jones
22284e5d89aSTony Jones	chead.increment_pending(common_pid,
22384e5d89aSTony Jones		None, pair(nr_scanned, nr_taken), None)
22484e5d89aSTony Jones
22584e5d89aSTony Jonesdef compaction__mm_compaction_isolate_migratepages(event_name, context, common_cpu,
22684e5d89aSTony Jones	common_secs, common_nsecs, common_pid, common_comm,
22784e5d89aSTony Jones	common_callchain, start_pfn, end_pfn, nr_scanned, nr_taken):
22884e5d89aSTony Jones
22984e5d89aSTony Jones	chead.increment_pending(common_pid,
23084e5d89aSTony Jones		None, None, pair(nr_scanned, nr_taken))
23184e5d89aSTony Jones
23284e5d89aSTony Jonesdef compaction__mm_compaction_end(event_name, context, common_cpu,
23384e5d89aSTony Jones	common_secs, common_nsecs, common_pid, common_comm,
23484e5d89aSTony Jones	common_callchain, zone_start, migrate_start, free_start, zone_end,
23584e5d89aSTony Jones	sync, status):
23684e5d89aSTony Jones
23784e5d89aSTony Jones	chead.complete_pending(common_pid, common_secs, common_nsecs)
23884e5d89aSTony Jones
23984e5d89aSTony Jonesdef compaction__mm_compaction_begin(event_name, context, common_cpu,
24084e5d89aSTony Jones	common_secs, common_nsecs, common_pid, common_comm,
24184e5d89aSTony Jones	common_callchain, zone_start, migrate_start, free_start, zone_end,
24284e5d89aSTony Jones	sync):
24384e5d89aSTony Jones
24484e5d89aSTony Jones	chead.create_pending(common_pid, common_comm, common_secs, common_nsecs)
24584e5d89aSTony Jones
24684e5d89aSTony Jonesdef pr_help():
24784e5d89aSTony Jones	global usage
24884e5d89aSTony Jones
24984e5d89aSTony Jones	sys.stdout.write(usage)
25084e5d89aSTony Jones	sys.stdout.write("\n")
25184e5d89aSTony Jones	sys.stdout.write("-h	display this help\n")
25284e5d89aSTony Jones	sys.stdout.write("-p	display by process\n")
25384e5d89aSTony Jones	sys.stdout.write("-pv	display by process (verbose)\n")
25484e5d89aSTony Jones	sys.stdout.write("-t	display stall times only\n")
25584e5d89aSTony Jones	sys.stdout.write("-m	display stats for migration\n")
25684e5d89aSTony Jones	sys.stdout.write("-fs	display stats for free scanner\n")
25784e5d89aSTony Jones	sys.stdout.write("-ms	display stats for migration scanner\n")
25884e5d89aSTony Jones	sys.stdout.write("-u	display results in microseconds (default nanoseconds)\n")
25984e5d89aSTony Jones
26084e5d89aSTony Jones
26184e5d89aSTony Jonescomm_re = None
26284e5d89aSTony Jonespid_re = None
263*280b4e4aSBenjamin Graypid_regex = r"^(\d*)-(\d*)$|^(\d*)$"
26484e5d89aSTony Jones
26584e5d89aSTony Jonesopt_proc = popt.DISP_DFL
26684e5d89aSTony Jonesopt_disp = topt.DISP_ALL
26784e5d89aSTony Jones
26884e5d89aSTony Jonesopt_ns = True
26984e5d89aSTony Jones
27084e5d89aSTony Jonesargc = len(sys.argv) - 1
27184e5d89aSTony Jonesif argc >= 1:
27284e5d89aSTony Jones	pid_re = re.compile(pid_regex)
27384e5d89aSTony Jones
27484e5d89aSTony Jones	for i, opt in enumerate(sys.argv[1:]):
27584e5d89aSTony Jones		if opt[0] == "-":
27684e5d89aSTony Jones			if opt == "-h":
27784e5d89aSTony Jones				pr_help()
27884e5d89aSTony Jones				exit(0);
27984e5d89aSTony Jones			elif opt == "-p":
28084e5d89aSTony Jones				opt_proc = popt.DISP_PROC
28184e5d89aSTony Jones			elif opt == "-pv":
28284e5d89aSTony Jones				opt_proc = popt.DISP_PROC_VERBOSE
28384e5d89aSTony Jones			elif opt == '-u':
28484e5d89aSTony Jones				opt_ns = False
28584e5d89aSTony Jones			elif opt == "-t":
28684e5d89aSTony Jones				set_type(topt.DISP_TIME)
28784e5d89aSTony Jones			elif opt == "-m":
28884e5d89aSTony Jones				set_type(topt.DISP_MIG)
28984e5d89aSTony Jones			elif opt == "-fs":
29084e5d89aSTony Jones				set_type(topt.DISP_ISOLFREE)
29184e5d89aSTony Jones			elif opt == "-ms":
29284e5d89aSTony Jones				set_type(topt.DISP_ISOLMIG)
29384e5d89aSTony Jones			else:
29484e5d89aSTony Jones				sys.exit(usage)
29584e5d89aSTony Jones
29684e5d89aSTony Jones		elif i == argc - 1:
29784e5d89aSTony Jones			m = pid_re.search(opt)
29884e5d89aSTony Jones			if m != None and m.group() != "":
29984e5d89aSTony Jones				if m.group(3) != None:
30084e5d89aSTony Jones					f = pid_filter(m.group(3), m.group(3))
30184e5d89aSTony Jones				else:
30284e5d89aSTony Jones					f = pid_filter(m.group(1), m.group(2))
30384e5d89aSTony Jones			else:
30484e5d89aSTony Jones				try:
30584e5d89aSTony Jones					comm_re=re.compile(opt)
30684e5d89aSTony Jones				except:
30784e5d89aSTony Jones					sys.stderr.write("invalid regex '%s'" % opt)
30884e5d89aSTony Jones					sys.exit(usage)
30984e5d89aSTony Jones				f = comm_filter(comm_re)
31084e5d89aSTony Jones
31184e5d89aSTony Jones			chead.add_filter(f)
312