#!/usr/bin/python # # Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import sys,re,os import datetime class ContextSwitch(): def __init__(self, context): self.context = context class Interrupt(): def __init__(self, irq): self.irq = irq class Sleep(): def __init__(self, duration, irq): self.duration = duration self.irq = irq class TaskState(): SWITCH = 0 RESET_BIT = 1 SET_BIT = 2 def __init__(self, task, switch, bits): self.task = task self.switch = switch self.bits = bits class PacketCmd(): def __init__(self, task, command_ptr): self.task = task self.command_ptr = command_ptr class Kevent(): def __init__(self, event_ptr): self.event_ptr = event_ptr class Event(): TYPE_ERROR = -1 TYPE_CONTEXTSWITCH = 1 TYPE_INTERRUPT = 2 TYPE_SLEEP = 3 TYPE_WAKEUP = 4 TYPE_TASK_STATE_CHANGE = 5 TYPE_COMMAND_PACKET = 6 TYPE_KEVENT = 7 def __init__(self, time, event_type, data): self.time = time self.etype = event_type self.data = data class EventType(): PLATFORM_INFO = 255 CONTEXTSWITCH = 1 INTERRUPT = 2 SLEEP = 3 TASK_STATE_CHANGE = 4 COMMAND_PACKET = 5 KEVENT = 6 class Params(): TICKS_PER_MSEC = 0 def getData(monitorFile): global symbols MO_STBIT0 = 0x20000000 MO_STBIT1 = 0x30000000 MO_EVENT = 0x40000000 MO_MASK = 0xFFFFFFF EVT_MASK = 0xFFFFFFC eventList = [] count = 0 with open(monitorFile, 'rb') as f: while (1): c = f.read(1) count += 1 if (len(c) < 1): return eventList code = int(c.encode('hex'), 16) if (code > 0): #print "Count={:x}".format(count-1) header = c cc = f.read(3) if (len(cc) < 3): return eventList header += cc count += 3 evt_type = int(header[0:2][::-1].encode('hex'), 16) size = int(header[2:4][::-1].encode('hex'), 16) #print "Event {}({})".format(evt_type, size) chunk = f.read(size*4) if (len(chunk) < size*4): return eventList count += size*4 if (evt_type == EventType.PLATFORM_INFO): Params.TICKS_PER_MSEC = \ int(chunk[0:4][::-1].encode('hex'), 16) / 1000.0 elif (evt_type == EventType.CONTEXTSWITCH): time = int(chunk[0:4][::-1].encode('hex'), 16) context = int(chunk[4:8][::-1].encode('hex'), 16) eventList.append(Event(time, Event.TYPE_CONTEXTSWITCH, ContextSwitch(context))) elif (evt_type == EventType.INTERRUPT): time = int(chunk[0:4][::-1].encode('hex'), 16) irq = int(chunk[4:8][::-1].encode('hex'), 16) eventList.append(Event(time, Event.TYPE_INTERRUPT, Interrupt(irq))) elif (evt_type == EventType.SLEEP): time = int(chunk[0:4][::-1].encode('hex'), 16) duration = int(chunk[4:8][::-1].encode('hex'), 16) cause = int(chunk[8:12][::-1].encode('hex'), 16) eventList.append(Event(time, Event.TYPE_SLEEP, Sleep(duration, cause))) eventList.append(Event(time, Event.TYPE_WAKEUP, Sleep(duration, cause))) elif (evt_type == EventType.TASK_STATE_CHANGE): time = int(chunk[0:4][::-1].encode('hex'), 16) task = int(chunk[4:8][::-1].encode('hex'), 16) data = int(chunk[8:12][::-1].encode('hex'), 16) if data == 0: eventList.append(Event( time, Event.TYPE_TASK_STATE_CHANGE, TaskState(task, TaskState.SWITCH, 0))) elif (data & 0xF0000000) == MO_STBIT0: eventList.append(Event( time, Event.TYPE_TASK_STATE_CHANGE, TaskState(task, TaskState.RESET_BIT, data & MO_MASK ))) elif (data & 0xF0000000) == MO_STBIT1: eventList.append(Event( time, Event.TYPE_TASK_STATE_CHANGE, TaskState(task, TaskState.SET_BIT, data & MO_MASK ))) elif (evt_type == EventType.COMMAND_PACKET): time = int(chunk[0:4][::-1].encode('hex'), 16) task = int(chunk[4:8][::-1].encode('hex'), 16) cmd_ptr = int(chunk[8:12][::-1].encode('hex'), 16) eventList.append(Event(time, Event.TYPE_COMMAND_PACKET, PacketCmd(task, cmd_ptr))) elif (evt_type == EventType.KEVENT): time = int(chunk[0:4][::-1].encode('hex'), 16) event = int(chunk[4:8][::-1].encode('hex'), 16) eventList.append(Event(time, Event.TYPE_KEVENT, Kevent(event & EVT_MASK))) else: eventList.append(Event(0, Event.TYPE_ERROR, evt_type)) def findFollowingTask(events, i): i = i + 1 while (i < len(events)) and (events[i].etype != Event.TYPE_CONTEXTSWITCH): i = i + 1 if (i == len(events)): return -1 else: return events[i].data.context def decodeStateBits(flags): bitValue = [ "STOP", "TERM", "SUSP", "BLCK", "GDBSTOP", "PRIO" , "NA" , "NA", "NA", "NA", "NA", "TIME", "DRIV", "RESV", "EVNT", "ENQU", "DEQU", "SEND", "RECV", "SEMA", "LIST", "LOCK", "ALLOC", "GTBL", "RESV", "RESV", "RECVDATA", "SENDDATA" ] s = '' for x in range(0, len(bitValue) - 1): if (flags & (1 << x)): s += bitValue[x] + " " return s def display(events): global ftrace_format global isr current_task = -1 for i in range(0, len(events) -1): evt = events[i] if ftrace_format == 0: if (evt.etype == Event.TYPE_CONTEXTSWITCH): fTask = findFollowingTask(events, i) if (fTask >= 0): print "{:12} : {:>16} ---> {:<16}".format( formatTime(evt.time), getTask(evt.data.context), getTask(fTask)) elif (evt.etype == Event.TYPE_INTERRUPT): print "{:12} : IRQ{} handler={}".format( formatTime(evt.time), evt.data.irq, getIsr(evt.data.irq)) elif (evt.etype == Event.TYPE_SLEEP): print "{:12} : SLEPT {} OS ticks".format( formatTime(evt.time), evt.data.duration) elif (evt.etype == Event.TYPE_WAKEUP): print "{:12} : WAKEUP IRQ{}".format( formatTime(evt.time), evt.data.irq) elif (evt.etype == Event.TYPE_TASK_STATE_CHANGE): if (evt.data.task == 0): task = "main_task" else: task = getSymbol(evt.data.task).replace('_k_task_obj_', '') if (evt.data.switch == TaskState.SWITCH): print "{:12} : " \ "Task switch to {}".format(formatTime(evt.time), task) elif (evt.data.switch == TaskState.SET_BIT): print "{:12} : " \ "Task bits set ({}) {}".format( formatTime(evt.time), task, decodeStateBits(evt.data.bits)) elif (evt.data.switch == TaskState.RESET_BIT): print "{:12} : " \ "Task bits reset ({}) {}".format( formatTime(evt.time), task, decodeStateBits(evt.data.bits)) elif (evt.etype == Event.TYPE_COMMAND_PACKET): if (evt.data.task == 0): task = "main_task" else: task = getSymbol(evt.data.task).replace('_k_task_obj_', '') print "{:12} : " \ "Command ({}) {}".format(formatTime(evt.time), task, getSymbol(evt.data.command_ptr)) elif (evt.etype == Event.TYPE_KEVENT): print "{:12} : " \ "Event {}".format( formatTime(evt.time), getSymbol(evt.data.event_ptr).replace( '_k_event_obj_','' ) ) else: print "ERROR type={:08x}".format(evt.data) else: if (evt.etype == Event.TYPE_CONTEXTSWITCH): ftask_id = findFollowingTask(events, i) if (ftask_id > 0): task_id = evt.data.context task_name = getTask(evt.data.context) if task_name == "main_task": task_id = 0 if task_name == "_k_server": task_id = 1 ftask_name = getTask(ftask_id) if ftask_name == "main_task": ftask_id = 0 if ftask_name == "_k_server": ftask_id = 1 print " {:>16}-{:<8} [000] .... {:12}: sched_switch:" \ " prev_comm={} prev_pid={} prev_prio=0" \ " prev_state=S ==> next_comm={} next_pid={}" \ " next_prio=0".format(task_name, task_id, formatTime(evt.time), task_name, task_id, ftask_name, ftask_id) current_task = evt.data.context elif (evt.etype == Event.TYPE_INTERRUPT): print " {:>16}-{:<8} [000] .... {:12}: irq_handler_entry: irq={}" \ " name={} handler={}".format(getTask(current_task), current_task, formatTime(evt.time), evt.data.irq, evt.data.irq, getIsr(evt.data.irq)) print " {:>16}-{:<8} [000] .... {:12}: irq_handler_exit: irq={}" \ " ret=handled".format(getTask(current_task), current_task, formatTime(evt.time), evt.data.irq) last_timestamp = 0.0 base_timestamp = 0.0 def formatTime(val): global last_timestamp global base_timestamp ticks_per_ms = Params.TICKS_PER_MSEC val_ms = base_timestamp + (val / ticks_per_ms) if val_ms < last_timestamp: val_ms = val_ms + (0xFFFFFFFF / ticks_per_ms) base_timestamp = base_timestamp + (0xFFFFFFFF / ticks_per_ms) last_timestamp = val_ms return "{:15.3f}".format(val_ms) def getSymbol(val): global symbols if symbols.has_key(val): return symbols[val] else: mem_key = 0 for key, value in symbols.iteritems(): if key < val: if key > mem_key: mem_key = key symbols[val] = symbols[mem_key] + "+{}".format(val - mem_key) return symbols[val] def getTask(val): return getSymbol(val).replace("_stack", "") def getIsr(val): global isr if (isr.has_key(val)): return isr[val].replace("$","").replace("_stub","") else: return "??{}".format(val) def loadSymbols(elfFile): os.system('readelf -s ' + elfFile + ' > symbols.txt') prog = re.compile("\s+\S+:\s+([a-fA-F0-9]+)\s+\d+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)") objList = {} with open('symbols.txt', 'r') as f: for line in f: match = prog.match(line) if match: address = int(match.group(1), 16) if not objList.has_key(address): objList[address] = match.group(2) else: s = match.group(2) # Prioritize tasks / event symbols when multiple symbols # on same address if (("_k_task_obj" in s) or ("_k_event_obj" in s) or ("_idt_base_address" in s)): objList[address] = s os.system('rm -rf symbols.txt') return objList def getIdtFunc(str1, str2): # Format: cc1d0800 008e1000 => 0x00101dcc # IDT encoding (see _IdtEntCreate function in zephyr) address = str2[6:8]+str2[4:6]+str1[2:4]+str1[0:2] if symbols.has_key(int(address, 16)): return symbols[int(address, 16)] else: return "??{}".format(address) def getSection(address, elfFile): os.system('readelf -S ' + elfFile + ' > sections.txt') prog = re.compile("\s+\[.+\]\s(\S+)\s+\S+\s+([0-9a-fA-F]+)\s+[0-9a-fA-F]+\s+([0-9a-fA-F]+)") with open("sections.txt", 'r') as f: for line in f: match = prog.match(line) if match: start = int(match.group(2), 16) end = start + int(match.group(3), 16) name = match.group(1) if (address >= start) and (address < end): os.system('rm -rf sections.txt') return name os.system('rm -rf sections.txt') return '' def getIsrTable(elfFile): # First get IDT table address '_idt_base_address' symbol idt_address = 0 for addr,sym in symbols.iteritems(): if sym == '_idt_base_address': idt_address = addr if idt_address == 0: print "IDT table address not found" return 0 sectionName = getSection(idt_address, elfFile) if sectionName == '': print "IDT section not found" return 0 os.system('readelf -x ' + sectionName + ' ' + elfFile + ' > ' + sectionName + '.txt') prog = re.compile( "\s+0x([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)" \ "\s+([0-9a-fA-F]+)\s+\S+") symbol_table = {} first = 0 with open(sectionName + '.txt', 'r') as f: for line in f: match = prog.match(line) if match: address = int(match.group(1), 16) if (first == 0): first = address symbol_table[address] = match.group(2) symbol_table[address+4] = match.group(3) symbol_table[address+8] = match.group(4) symbol_table[address+12] = match.group(5) address_end = address + 12 capture_on = 0 index = 0 isr = {} address = first while address < address_end: if not capture_on: if (address == idt_address): capture_on = 1 if (capture_on): isr[index] = getIdtFunc(symbol_table[address], symbol_table[address+4]) index += 1 isr[index] = getIdtFunc(symbol_table[address+8], symbol_table[address+12]) index += 1 if (index == 256): break address = address + 16 else: address = address + 4 os.system('rm -rf ' + sectionName + '.txt') return isr symbols = {} isr = {} prevTaskName = "" prevTaskId = -1 ftrace_format = 0 def Main(argv): global symbols, isr global ftrace_format dumpFile = "" elfFile = "" sys.argv.pop(0) iterator = sys.argv.__iter__() for arg in iterator: if arg == "--ftrace": ftrace_format = 1 elif arg == "-c": Params.TICKS_PER_MSEC = float(iterator.next()) / 1000.0 else: if not dumpFile: dumpFile = arg elif not elfFile: elfFile = arg if not elfFile: print "profile.py [DUMP FILE] [ELF FILE]" sys.exit(0) symbols = loadSymbols(elfFile) isr = getIsrTable(elfFile) eventList = getData(dumpFile) if (Params.TICKS_PER_MSEC == 0): print "Platform info not found ! Use -c option if decoding dump from JTAG" sys.exit(0) display(eventList) if __name__ == "__main__": Main(sys.argv[1:])