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