xref: /xnu-8792.81.2/san/tools/kstksz (revision 19c3b8c28c31cb8130e034cfb5df6bf9ba342d90)
1*19c3b8c2SApple OSS Distributions#!/usr/local/bin/recon
2*19c3b8c2SApple OSS Distributionslocal CoreSymbolication = require 'CoreSymbolication'
3*19c3b8c2SApple OSS Distributionslocal argparse = require 'argparse'
4*19c3b8c2SApple OSS Distributionslocal kperf = require 'kperf'
5*19c3b8c2SApple OSS Distributionslocal ktrace = require 'ktrace'
6*19c3b8c2SApple OSS Distributionslocal strict = require 'strict'
7*19c3b8c2SApple OSS Distributions
8*19c3b8c2SApple OSS Distributions
9*19c3b8c2SApple OSS Distributionslocal ksancov = {}
10*19c3b8c2SApple OSS Distributions
11*19c3b8c2SApple OSS Distributions-- KCOV event
12*19c3b8c2SApple OSS Distributions
13*19c3b8c2SApple OSS Distributionsksancov.Event = {}
14*19c3b8c2SApple OSS Distributionsksancov.Event.__index = ksancov.Event
15*19c3b8c2SApple OSS Distributions
16*19c3b8c2SApple OSS Distributionsfunction ksancov.Event.new()
17*19c3b8c2SApple OSS Distributions	local instance = {
18*19c3b8c2SApple OSS Distributions		pid = nil,
19*19c3b8c2SApple OSS Distributions		procname = nil,
20*19c3b8c2SApple OSS Distributions		tid = nil,
21*19c3b8c2SApple OSS Distributions		stack_size = nil,
22*19c3b8c2SApple OSS Distributions		pc = nil,
23*19c3b8c2SApple OSS Distributions		duration = nil,
24*19c3b8c2SApple OSS Distributions		ustack = nil,
25*19c3b8c2SApple OSS Distributions		kstack = nil,
26*19c3b8c2SApple OSS Distributions	}
27*19c3b8c2SApple OSS Distributions
28*19c3b8c2SApple OSS Distributions	return setmetatable(instance, ksancov.Event)
29*19c3b8c2SApple OSS Distributionsend
30*19c3b8c2SApple OSS Distributions
31*19c3b8c2SApple OSS Distributionsfunction ksancov.Event:print()
32*19c3b8c2SApple OSS Distributions	print(string.format("%s(%d)\t%d\t at %x has stack size %d.", self.procname, self.pid, self.tid,
33*19c3b8c2SApple OSS Distributions		self.pc, self.stack_size))
34*19c3b8c2SApple OSS Distributionsend
35*19c3b8c2SApple OSS Distributions
36*19c3b8c2SApple OSS Distributions-- KSANCOV tracing session
37*19c3b8c2SApple OSS Distributions
38*19c3b8c2SApple OSS Distributionsksancov.Session = {}
39*19c3b8c2SApple OSS Distributionsksancov.Session.__index = ksancov.Session
40*19c3b8c2SApple OSS Distributions
41*19c3b8c2SApple OSS Distributionsfunction ksancov.Session.new(ktrace_session, flags)
42*19c3b8c2SApple OSS Distributions	assert(ktrace_session)
43*19c3b8c2SApple OSS Distributions
44*19c3b8c2SApple OSS Distributions	local instance = {
45*19c3b8c2SApple OSS Distributions		ktrace_session = ktrace_session,
46*19c3b8c2SApple OSS Distributions		kperf_session = kperf.Session.new(ktrace_session),
47*19c3b8c2SApple OSS Distributions		events_by_tid = {},
48*19c3b8c2SApple OSS Distributions		event_handlers = {},
49*19c3b8c2SApple OSS Distributions	}
50*19c3b8c2SApple OSS Distributions
51*19c3b8c2SApple OSS Distributions	local self = setmetatable(instance, ksancov.Session)
52*19c3b8c2SApple OSS Distributions	self:_register_kperf_callbacks(flags.ustack, flags.kstack)
53*19c3b8c2SApple OSS Distributions	self:_register_ktrace_callbacks(flags.proc)
54*19c3b8c2SApple OSS Distributions	return self
55*19c3b8c2SApple OSS Distributionsend
56*19c3b8c2SApple OSS Distributions
57*19c3b8c2SApple OSS Distributionsfunction ksancov.Session:start()
58*19c3b8c2SApple OSS Distributions	return self.ktrace_session:start()
59*19c3b8c2SApple OSS Distributionsend
60*19c3b8c2SApple OSS Distributions
61*19c3b8c2SApple OSS Distributionsfunction ksancov.Session:_register_kperf_callbacks(ustack, kstack)
62*19c3b8c2SApple OSS Distributions	local samplers = {}
63*19c3b8c2SApple OSS Distributions	if kstack then
64*19c3b8c2SApple OSS Distributions		table.insert(samplers, 'kstack')
65*19c3b8c2SApple OSS Distributions	end
66*19c3b8c2SApple OSS Distributions
67*19c3b8c2SApple OSS Distributions	if ustack then
68*19c3b8c2SApple OSS Distributions		table.insert(samplers, 'ustack')
69*19c3b8c2SApple OSS Distributions	end
70*19c3b8c2SApple OSS Distributions
71*19c3b8c2SApple OSS Distributions	-- D0x01ad0000 KCOV_THRESHOLD_ABOVE
72*19c3b8c2SApple OSS Distributions	if #samplers == 0 then
73*19c3b8c2SApple OSS Distributions		return
74*19c3b8c2SApple OSS Distributions	end
75*19c3b8c2SApple OSS Distributions
76*19c3b8c2SApple OSS Distributions	self.kperf_session:add_kdebug_sampler({'D0x01ad0000'}, samplers)
77*19c3b8c2SApple OSS Distributions
78*19c3b8c2SApple OSS Distributions	-- Collect userspace stacks
79*19c3b8c2SApple OSS Distributions	if ustack then
80*19c3b8c2SApple OSS Distributions		self.kperf_session:add_callback_sample({'ustack'}, function (sample)
81*19c3b8c2SApple OSS Distributions			local event = self.events_by_tid[sample.threadid]
82*19c3b8c2SApple OSS Distributions			if event then
83*19c3b8c2SApple OSS Distributions				event.ustack = sample.ustack
84*19c3b8c2SApple OSS Distributions			end
85*19c3b8c2SApple OSS Distributions		end)
86*19c3b8c2SApple OSS Distributions	end
87*19c3b8c2SApple OSS Distributions
88*19c3b8c2SApple OSS Distributions	-- Collect kernel stacks
89*19c3b8c2SApple OSS Distributions	if kstack then
90*19c3b8c2SApple OSS Distributions		self.kperf_session:add_callback_sample({'kstack'}, function (sample)
91*19c3b8c2SApple OSS Distributions			local event = self.events_by_tid[sample.threadid]
92*19c3b8c2SApple OSS Distributions			if event then
93*19c3b8c2SApple OSS Distributions				event.kstack = sample.kstack
94*19c3b8c2SApple OSS Distributions			end
95*19c3b8c2SApple OSS Distributions		end)
96*19c3b8c2SApple OSS Distributions	end
97*19c3b8c2SApple OSS Distributionsend
98*19c3b8c2SApple OSS Distributions
99*19c3b8c2SApple OSS Distributionsfunction ksancov.Session:_register_ktrace_callbacks(proc)
100*19c3b8c2SApple OSS Distributions	self.ktrace_session:add_callback('KCOV_STKSZ_THRESHOLD_ABOVE', function (trace_point)
101*19c3b8c2SApple OSS Distributions		self:_handle_threshold_above(ktrace.copy_event(trace_point))
102*19c3b8c2SApple OSS Distributions	end)
103*19c3b8c2SApple OSS Distributions
104*19c3b8c2SApple OSS Distributions	self.ktrace_session:add_callback('KCOV_STKSZ_THRESHOLD_BELOW', function (trace_point)
105*19c3b8c2SApple OSS Distributions		self:_handle_threshold_below(ktrace.copy_event(trace_point))
106*19c3b8c2SApple OSS Distributions	end)
107*19c3b8c2SApple OSS Distributions
108*19c3b8c2SApple OSS Distributions	if proc then
109*19c3b8c2SApple OSS Distributions		self.ktrace_session:filter_proc(proc)
110*19c3b8c2SApple OSS Distributions	end
111*19c3b8c2SApple OSS Distributionsend
112*19c3b8c2SApple OSS Distributions
113*19c3b8c2SApple OSS Distributionsfunction ksancov.Session:_handle_threshold_above(trace_point)
114*19c3b8c2SApple OSS Distributions	local event = ksancov.Event.new()
115*19c3b8c2SApple OSS Distributions
116*19c3b8c2SApple OSS Distributions	event.tid = trace_point["threadid"]
117*19c3b8c2SApple OSS Distributions	event.pid = self.ktrace_session:pid_for_threadid(event.tid) or "0"
118*19c3b8c2SApple OSS Distributions	event.pc = trace_point[1]
119*19c3b8c2SApple OSS Distributions	event.stack_size = trace_point[2]
120*19c3b8c2SApple OSS Distributions	event.procname = self.ktrace_session:procname_for_threadid(event.tid)
121*19c3b8c2SApple OSS Distributions
122*19c3b8c2SApple OSS Distributions	self.events_by_tid[event.tid] = event
123*19c3b8c2SApple OSS Distributionsend
124*19c3b8c2SApple OSS Distributions
125*19c3b8c2SApple OSS Distributionsfunction ksancov.Session:_handle_threshold_below(trace_point)
126*19c3b8c2SApple OSS Distributions	local event = self.events_by_tid[trace_point["threadid"]]
127*19c3b8c2SApple OSS Distributions
128*19c3b8c2SApple OSS Distributions	-- It is possible that we redord BELOW event as first. Ignore it if we haven't seen
129*19c3b8c2SApple OSS Distributions	-- the ABOVE event first.
130*19c3b8c2SApple OSS Distributions	if event then
131*19c3b8c2SApple OSS Distributions		self.events_by_tid[event.tid] = nil
132*19c3b8c2SApple OSS Distributions
133*19c3b8c2SApple OSS Distributions		for i, handler in pairs(self.event_handlers) do
134*19c3b8c2SApple OSS Distributions			handler(event)
135*19c3b8c2SApple OSS Distributions		end
136*19c3b8c2SApple OSS Distributions	end
137*19c3b8c2SApple OSS Distributionsend
138*19c3b8c2SApple OSS Distributions
139*19c3b8c2SApple OSS Distributionsfunction ksancov.Session:add_event_handler(handler)
140*19c3b8c2SApple OSS Distributions	table.insert(self.event_handlers, handler)
141*19c3b8c2SApple OSS Distributionsend
142*19c3b8c2SApple OSS Distributions
143*19c3b8c2SApple OSS Distributions-- Utility code
144*19c3b8c2SApple OSS Distributions
145*19c3b8c2SApple OSS Distributionslocal function parse_args()
146*19c3b8c2SApple OSS Distributions	local parser = argparse("ksancov", "Kernel stack size monitoring utility.")
147*19c3b8c2SApple OSS Distributions
148*19c3b8c2SApple OSS Distributions	parser:option {
149*19c3b8c2SApple OSS Distributions		name = "--codes-files",
150*19c3b8c2SApple OSS Distributions		description = "Import debugid-to-string mapping files",
151*19c3b8c2SApple OSS Distributions		args = "*",
152*19c3b8c2SApple OSS Distributions		count = "?",
153*19c3b8c2SApple OSS Distributions	}
154*19c3b8c2SApple OSS Distributions
155*19c3b8c2SApple OSS Distributions	parser:option {
156*19c3b8c2SApple OSS Distributions		name = "-f --file",
157*19c3b8c2SApple OSS Distributions		description = "artrace or ktrace file to read from",
158*19c3b8c2SApple OSS Distributions		args = 1,
159*19c3b8c2SApple OSS Distributions		count = "?",
160*19c3b8c2SApple OSS Distributions	}
161*19c3b8c2SApple OSS Distributions
162*19c3b8c2SApple OSS Distributions	parser:option {
163*19c3b8c2SApple OSS Distributions		name = "-p --proc",
164*19c3b8c2SApple OSS Distributions		description = "pid or process name to be recorded",
165*19c3b8c2SApple OSS Distributions		args = 1,
166*19c3b8c2SApple OSS Distributions		count = "?",
167*19c3b8c2SApple OSS Distributions	}
168*19c3b8c2SApple OSS Distributions
169*19c3b8c2SApple OSS Distributions	parser:flag  {
170*19c3b8c2SApple OSS Distributions		name = "-u --ustack",
171*19c3b8c2SApple OSS Distributions		description = "sample user space stacks",
172*19c3b8c2SApple OSS Distributions		count = "?",
173*19c3b8c2SApple OSS Distributions	}
174*19c3b8c2SApple OSS Distributions
175*19c3b8c2SApple OSS Distributions	parser:flag {
176*19c3b8c2SApple OSS Distributions		name = "-k --kstack",
177*19c3b8c2SApple OSS Distributions		description = "sample kernel space stacks",
178*19c3b8c2SApple OSS Distributions		count = "?",
179*19c3b8c2SApple OSS Distributions	}
180*19c3b8c2SApple OSS Distributions
181*19c3b8c2SApple OSS Distributions	return parser:parse(arg)
182*19c3b8c2SApple OSS Distributionsend
183*19c3b8c2SApple OSS Distributions
184*19c3b8c2SApple OSS Distributionslocal flags = parse_args()
185*19c3b8c2SApple OSS Distributionslocal ktrace_session = ktrace.Session.new(flags.file)
186*19c3b8c2SApple OSS Distributions
187*19c3b8c2SApple OSS Distributionsif flags.codes_files then
188*19c3b8c2SApple OSS Distributions	for _, file in pairs(flags.codes_files) do
189*19c3b8c2SApple OSS Distributions		ktrace_session:add_codes_file(file)
190*19c3b8c2SApple OSS Distributions	end
191*19c3b8c2SApple OSS Distributionsend
192*19c3b8c2SApple OSS Distributions
193*19c3b8c2SApple OSS Distributionslocal ksancov_session = ksancov.Session.new(ktrace_session, flags)
194*19c3b8c2SApple OSS Distributions
195*19c3b8c2SApple OSS Distributionsksancov_session:add_event_handler(function (event)
196*19c3b8c2SApple OSS Distributions	event:print()
197*19c3b8c2SApple OSS Distributions
198*19c3b8c2SApple OSS Distributions	local function symbolicate(symbolicator, frame)
199*19c3b8c2SApple OSS Distributions		local symbol = symbolicator:symbolicate(frame)
200*19c3b8c2SApple OSS Distributions
201*19c3b8c2SApple OSS Distributions		if symbol then
202*19c3b8c2SApple OSS Distributions			print(("\t%s  (in %s)"):format(symbol.name or "???", symbol.owner_name or "???"))
203*19c3b8c2SApple OSS Distributions		else
204*19c3b8c2SApple OSS Distributions			print(("\t0x%x  (in ???)").format(frame))
205*19c3b8c2SApple OSS Distributions		end
206*19c3b8c2SApple OSS Distributions	end
207*19c3b8c2SApple OSS Distributions
208*19c3b8c2SApple OSS Distributions	-- Symbolicate stacks
209*19c3b8c2SApple OSS Distributions	if event.ustack then
210*19c3b8c2SApple OSS Distributions		print('ustack:')
211*19c3b8c2SApple OSS Distributions
212*19c3b8c2SApple OSS Distributions		if flags.file then
213*19c3b8c2SApple OSS Distributions			-- When reading from a file, we can't use CoreSymbolication to symbolicate the stack frames as the processes are
214*19c3b8c2SApple OSS Distributions			-- not actually running, so use our ktrace session instead.
215*19c3b8c2SApple OSS Distributions			for _, frame in ipairs(event.ustack or {}) do
216*19c3b8c2SApple OSS Distributions				print(("    %s"):format(ktrace_session:symbolicate_with_pid(event.pid, frame)))
217*19c3b8c2SApple OSS Distributions			end
218*19c3b8c2SApple OSS Distributions		else
219*19c3b8c2SApple OSS Distributions			local symbolicator = CoreSymbolication.Symbolicator.new(event.pid)
220*19c3b8c2SApple OSS Distributions			if symbolicator then
221*19c3b8c2SApple OSS Distributions				for _, frame in ipairs(event.ustack or {}) do
222*19c3b8c2SApple OSS Distributions					symbolicate(symbolicator, frame)
223*19c3b8c2SApple OSS Distributions				end
224*19c3b8c2SApple OSS Distributions			end
225*19c3b8c2SApple OSS Distributions		end
226*19c3b8c2SApple OSS Distributions	end
227*19c3b8c2SApple OSS Distributions
228*19c3b8c2SApple OSS Distributions	if event.kstack then
229*19c3b8c2SApple OSS Distributions		print('kstack:')
230*19c3b8c2SApple OSS Distributions
231*19c3b8c2SApple OSS Distributions		if flags.file then
232*19c3b8c2SApple OSS Distributions			-- When reading from a file, we can't use CoreSymbolication to symbolicate the stack frames as the processes are
233*19c3b8c2SApple OSS Distributions			-- not actually running, so use our ktrace session instead.
234*19c3b8c2SApple OSS Distributions			for _, frame in ipairs(event.kstack or {}) do
235*19c3b8c2SApple OSS Distributions				print(("    %s"):format(ktrace_session:symbolicate_with_pid(event.pid, frame)))
236*19c3b8c2SApple OSS Distributions			end
237*19c3b8c2SApple OSS Distributions		else
238*19c3b8c2SApple OSS Distributions			local symbolicator = CoreSymbolication.Symbolicator.new(0)
239*19c3b8c2SApple OSS Distributions			if symbolicator then
240*19c3b8c2SApple OSS Distributions				for _, frame in ipairs(event.kstack or {}) do
241*19c3b8c2SApple OSS Distributions					symbolicate(symbolicator, frame)
242*19c3b8c2SApple OSS Distributions				end
243*19c3b8c2SApple OSS Distributions			end
244*19c3b8c2SApple OSS Distributions		end
245*19c3b8c2SApple OSS Distributions	end
246*19c3b8c2SApple OSS Distributionsend)
247*19c3b8c2SApple OSS Distributions
248*19c3b8c2SApple OSS Distributionsio.stdout:write("Started recording ...\n")
249*19c3b8c2SApple OSS Distributionslocal ok, err = ksancov_session:start()
250*19c3b8c2SApple OSS Distributionsif not ok then
251*19c3b8c2SApple OSS Distributions	io.stderr:write("Failed to start session: ", err, "\n")
252*19c3b8c2SApple OSS Distributions	os.exit(1)
253*19c3b8c2SApple OSS Distributionsend
254*19c3b8c2SApple OSS Distributions
255*19c3b8c2SApple OSS Distributionsio.stdout:write("DONE.\n")
256*19c3b8c2SApple OSS Distributions
257