#!/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 os, sys, re from collections import defaultdict from optparse import OptionParser class FilterTrace: def __init__(self): # 2 pass parsing self.compiled_regexp = [] # regexp for 1st pass self.compiled_regexp2 = [] # regexp for 2nd pass self.final_text = [] # contains all lines before they are written to file self.previous_line = {} # index in self.final_text of previous status change per # tid + timestamp. So we can compute easily duration of # state for 1 tid # thread information self.tid_names = {} # process name per tid self.tid_states = defaultdict(int) # state per tid 0:RUNNING 1:QUEUED 2:SLEEPING # 4:QUEUED or SLEEPING # error checking per tid self.t1_errors = defaultdict(int) self.t2_errors = defaultdict(int) self.t3_errors = defaultdict(int) # context switch info self.prev_tids = defaultdict(int) # previous tid per cpu when tracing new switch # Time (relative and absolute) self.last_timestamp_found = 0 self.last_timestamp = 0 self.reference_timestamp = 0 # time offset used in target script self.convert = 1 / 1000.0 #1.0 / 1 * 1000 # 32kHz -> ms conversion REGEXP_GLOSSARY = [ ## irqs # -0 [001] 294.876831: irq_handler_entry: irq=30 name=arch_timer ['^.*\[(?P\d+)\]\s+(?P[\w+\.]+)\s+(?P\d+\.\d+):\s+' \ 'irq_handler_entry:\s+irq=(?P\d+)\s+name=(?P[\w \.-_]+).*$', self.callback_irq_start_ftrace ], ['^.*\[(?P\d+)\]\s+(?P\d+\.\d+):\s+irq_handler_entry:' \ '\s+irq=(?P\d+)\s+name=(?P[\w \.-_]+).*$', self.callback_irq_start_ftrace ], # kworker/u:0-5 [000] 294.876862: irq_handler_exit: irq=30 ret=handled ['^.*\[(?P\d+)\]\s+(?P[\w+\.]+)\s+(?P\d+\.\d+):' \ '\s+irq_handler_exit:\s+irq=(?P\d+)\s+ret=.*$', self.callback_irq_stop_ftrace ], ['^.*\[(?P\d+)\]\s+(?P\d+\.\d+):\s+irq_handler_exit:\s+' \ 'irq=(?P\d+)\s+ret=.*$', self.callback_irq_stop_ftrace ], ## scheduler #sh-2505 [000] 271.147491: sched_switch: prev_comm=sh prev_pid=2505 prev_prio=120 # prev_state=S ==> next_comm=swapper next_pid=0 next_prio=120 ['^.*\[(?P\d+)\]\s+(?P[\w+\.]+)\s+(?P\d+\.\d+):\s+' \ 'sched_switch:\s+.*==>\s+next_comm=(?P[\w \.#-_/]+)\s+' \ 'next_pid=(?P\d+)\s+next_prio.*$', self.callback_context_switch_ftrace ], ['^.*\[(?P\d+)\]\s+(?P\d+\.\d+):\s+sched_switch:\s+.*==>\s+' \ 'next_comm=(?P[\w \.#-_/]+)\s+next_pid=(?P\d+)\s+' \ 'next_prio.*$', self.callback_context_switch_ftrace ], ['^TIMESTAMP.*$', None], ['^.*$', self.callback_no_rule] ] REGEXP_GLOSSARY2 = [ ['^Ref timestamp:\s*(?P\d+)$', self.callback_reference_timestamp], ['^.*psel.*$', None], ['^(?P\d+)\s+(?P\d+)\s+(?P[\w \.#-_/]+):(?P-?\d+)' \ '\s+(?P\w+).*$', self.callback_context_switch2 ], ['^Last timestamp:\s*(?P\d+).*$', self.callback_end2], ['^.*$', self.callback_no_rule2] ] # Compile all regexp for regexp_string, regexp_rules in REGEXP_GLOSSARY: self.compiled_regexp.append([re.compile(regexp_string), regexp_rules]) for regexp_string, regexp_rules in REGEXP_GLOSSARY2: self.compiled_regexp2.append([re.compile(regexp_string), regexp_rules]) def filter_line(self, line): # Parse all regexps for regexp, regexp_rules in self.compiled_regexp: # Test if regexp matches res = regexp.search(line) # if regexp match if res: # Execute regexp_rules if regexp_rules: regexp_rules(res, line) # Only one regexp per line break def filter_line2(self): # Parse self.final_text to add duration of state per tid # Memorize index and timestamp of previous state change per tid and compute duration of # previous state when new state change is seen # For last state change, duration will be from state change until "Last timestamp" for line_index in range(0, len(self.final_text)): line = self.final_text[line_index] for regexp, regexp_rules in self.compiled_regexp2: # Test if regexp matches res = regexp.search(line) # if regexp match if res: # Execute regexp_rules if regexp_rules: regexp_rules(res, line_index) # Only one regexp per line break def callback_context_switch_post(self, timestamp, cpu_id, next_tid, name, prev_tid, prev_task_name, prev_state): self.last_timestamp = timestamp if (prev_task_name != ""): self.tid_names[prev_tid] = prev_task_name self.prev_tids[cpu_id] = prev_tid # short format requires storage of previous running thread per core if (cpu_id in self.prev_tids): prev_tid = self.prev_tids[cpu_id] if (prev_tid in self.tid_states): if (self.tid_states[prev_tid] > 0): # goes to queue/sleep but was not running # choice is to do as if running and we keep simply trace of that self.t1_errors[prev_tid] += 1 if (prev_task_name != "") and (prev_state != ""): if (res.group('prev_state') == 'R'): self.tid_states[prev_tid] = 1 # queued new_state = "Q" else: self.tid_states[prev_tid] = 2 # sleep new_state = "S" else: self.tid_states[prev_tid] = 4 # Queued or sleep new_state = "X" # new state of previous thread if previous thread not seen for first time self.final_text.append("%-10d %s %-16s:%-5s %s" % (self.last_timestamp, cpu_id, self.tid_names[prev_tid], prev_tid, new_state)) # handle "next task" if (next_tid == 0): next_tid = -cpu_id if (name == "swapper"): name += str(cpu_id) self.tid_names[next_tid] = name if (next_tid not in self.tid_states): # consider was queued first time we see it to trigger no transition error self.tid_states[next_tid] = 1 if (self.tid_states[next_tid] != 1): # process lifecycle: process must be queued to run # goes running but was not queued (missing wakeup ?) self.t2_errors[next_tid] += 1 self.tid_states[next_tid] = 0 # running self.final_text.append("%-10s %s %-16s:%-5s R" % (self.last_timestamp, cpu_id, self.tid_names[next_tid], next_tid)) self.prev_tids[cpu_id] = next_tid def callback_context_switch_ftrace(self, res, line): timestamp = int(float(res.group('timestamp')) * 1000.0) if self.reference_timestamp == 0: self.reference_timestamp = timestamp timestamp = timestamp - self.reference_timestamp cpu_id = int(res.group('cpu_id')) name = res.group('next_task_name').rstrip(' ') tid = int(res.group('next_tid')) self.callback_context_switch_post(timestamp, cpu_id, tid, name, 0, "", "" ) def callback_irq_start_ftrace(self, res, line): timestamp = int(float(res.group('timestamp')) * 1000.0) if self.reference_timestamp == 0: self.reference_timestamp = timestamp timestamp = timestamp - self.reference_timestamp cpu_id = int(res.group('cpu_id')) irq_id = int(res.group('irq_id')) irq_name = res.group('irq_name') self.final_text.append("%d %d irqin %d unknown_func %s 0x0" % (timestamp, cpu_id, irq_id, irq_name)) def callback_irq_stop_ftrace(self, res, line): timestamp = int(float(res.group('timestamp')) * 1000.0) if self.reference_timestamp == 0: self.reference_timestamp = timestamp timestamp = timestamp - self.reference_timestamp cpu_id = int(res.group('cpu_id')) irq_id = int(res.group('irq_id')) self.final_text.append("%d %d irqout %d" % (timestamp, cpu_id, irq_id)) def callback_no_rule(self, res, line): self.final_text.append(line.rstrip('\n')) return def callback_end(self, res, line): if res != None: self.final_text.append(line.rstrip('\n')) else: self.final_text.append("Last timestamp: %d" % self.last_timestamp) self.last_timestamp_found = 1 print("\nT1: goes to queued/sleep but was not running") keys = self.t1_errors.keys() keys.sort() for key in keys: print("%s: %s %d") % (self.tid_names[key], key, self.t1_errors[key]) print("\nT2: goes running but was not queued (missing wakeup ?)") keys = self.t2_errors.keys() keys.sort() for key in keys: print("%s: %s %d") % (self.tid_names[key], key, self.t2_errors[key]) print("\nT3: wakes up but was not sleeping") keys = self.t3_errors.keys() keys.sort() for key in keys: print("%s: %s %d") % (self.tid_names[key], key, self.t3_errors[key]) def callback_context_switch2(self, res, line_index): tid = int(res.group('tid')) self.last_timestamp = int(res.group('timestamp')) # per tid, go back to previous line, compute stamp current - previous stamp and add # to previous line if (tid in self.previous_line): duration = self.last_timestamp - self.previous_line[tid][1] self.final_text[self.previous_line[tid][0]] = \ self.final_text[self.previous_line[tid][0]] + \ " %-10d (%-10d %.3f ms)" % (duration, self.previous_line[tid][1] + self.reference_timestamp, self.previous_line[tid][1] * self.convert) self.previous_line[tid] = [line_index, self.last_timestamp] def callback_reference_timestamp(self, res, line_index): self.reference_timestamp = int(res.group('ref_time')) def callback_end2(self, res, line_index): # add diff from last timestamp to current time to all tids if res != None: self.last_timestamp = int(res.group('last_timestamp')) # add timing to end of file for last switch of all threads for tid in self.previous_line: last_duration = self.last_timestamp - self.previous_line[tid][1] self.final_text[self.previous_line[tid][0]] = \ self.final_text[self.previous_line[tid][0]] + \ " %-10d (%-10d %.3f ms)" % (last_duration, self.previous_line[tid][1] + self.reference_timestamp, self.previous_line[tid][1] * self.convert) # dump everything print "\n%-9s %s %-15s:%-4s STA %-9s (%-10s)" % ("TIMESTAMP", "CPU", "NAME", "PID", "DURATION", "ABS_TIME tick/REL_TIME ms") for i in range(0, len(self.final_text)): print self.final_text[i] def callback_no_rule2(self, res, line_index): return global filterMaster filterMaster = FilterTrace() def Main(argv): usage = "Usage: %prog FILENAME [options]" parser = OptionParser(usage = usage) (options, args) = parser.parse_args() if len(args) != 1: print "Usage: type python .py -h" return # First pass, fill each array index with 1 thread + its new status + timestamp # (basically 1 context switch will give 2 indexes, 1 wakeup 1 index) # only missing info is duration of state file = open(args[0], 'r') string = file.readline() while string: filterMaster.filter_line(string) string = file.readline() if filterMaster.last_timestamp_found == 0: filterMaster.callback_end(None, 0) # 2nd pass, add duration of state of tid filterMaster.filter_line2() if __name__=="__main__": Main(sys.argv[1:])