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