pytimechart-1.0.0.rc1/0000755000175100017510000000000011646055745014133 5ustar pierre00000000000000pytimechart-1.0.0.rc1/PKG-INFO0000644000175100017510000000371511646055745015236 0ustar pierre00000000000000Metadata-Version: 1.0 Name: pytimechart Version: 1.0.0.rc1 Summary: Fast graphical exploration and visualisation for linux kernel traces Home-page: http://gitorious.org/pytimechart Author: Pierre Tardy Author-email: tardyp@gmail.com License: BSD Download-URL: http://gitorious.org/pytimechart Description: pyTimechart is aimed at helping kernel developer to browse into large scale traces. Based on very powerful and efficient plotting library *Chaco*, pytimechart UI feels very smooth. Based on the python language, its plugin based architecture allow developer to very quickly implement parsing and representation of new trace-event function-trace or trace_printk Kernel traces are parsed as trace-events, and organised into processes, each process is displayed in its own line. The x-axis representing the time, process is represented as intervals when it is scheduled in the system. pyTimechart processes are not only *process* in the common unix sense, it can be any group of activities in the system. Thus pytimechart display activities of: * cpuidle states * cpufreq states * runtime_pm * irqs * tasklets * works * timers * kernel threads * user process * whatever there is a plugin for pyTimechart also represent graphically the wakeup events between two process. Keywords: gui,ftrace,perf,trace-event Platform: Windows Platform: Linux Platform: Mac OS-X Platform: Unix Platform: Solaris Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Intended Audience :: Developers pytimechart-1.0.0.rc1/timechart/0000755000175100017510000000000011646055745016113 5ustar pierre00000000000000pytimechart-1.0.0.rc1/timechart/backends/0000755000175100017510000000000011646055745017665 5ustar pierre00000000000000pytimechart-1.0.0.rc1/timechart/backends/__init__.py0000644000175100017510000000012311567523405021765 0ustar pierre00000000000000try: __import__('pkg_resources').declare_namespace(__name__) except: pass pytimechart-1.0.0.rc1/timechart/backends/ftrace.py0000644000175100017510000001457511631370253021503 0ustar pierre00000000000000import re import sys,os from timechart.plugin import get_plugins_additional_ftrace_parsers from enthought.pyface.api import ProgressDialog # take the TP_printk from the /include/trace/events dir # syntax is event_name, printk, printk_args... events_desc = [ ('sched_switch', 'task %s:%d [%d] (%s) ==> %s:%d [%d]', 'prev_comm', 'prev_pid', 'prev_prio','prev_state' , 'next_comm', 'next_pid', 'next_prio'), ('sched_switch', 'task %s:%d [%d] ==> %s:%d [%d]', 'prev_comm', 'prev_pid', 'prev_prio' , 'next_comm', 'next_pid', 'next_prio'), ('sched_switch', 'prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s ==> next_comm=%s next_pid=%d next_prio=%d', 'prev_comm', 'prev_pid', 'prev_prio' , 'prev_state', 'next_comm', 'next_pid', 'next_prio'), ('sched_wakeup','task %s:%d [%d] success=%d [%d]','comm', 'pid', 'prio', 'success', 'cpu'), ('sched_wakeup','task %s:%d [%d] success=%d','comm', 'pid', 'prio', 'success'), ('sched_wakeup','comm=%s pid=%d prio=%d success=%d target_cpu=%d','comm', 'pid', 'prio', 'success', 'cpu'), ] events_desc += get_plugins_additional_ftrace_parsers() # pre process our descriptions to transform it into re events_re = {} num_func = 0 for event in events_desc: name = event[0] printk = event[1] args = event[2:] args_l = [] for i in '()[]': printk = printk.replace(i,'\\'+i) # we replace %d %s by the equivalent regular expression, and keep the type in memory for later i = 0 func = "def my_dispatch_func%d(event,group):\n"%(num_func) for arg in args: idx = printk.index('%') format = printk[idx+1] if format=='d': filt=int regex="([-0-9]+)" func+=" event['%s'] = int(group[%d])\n"%(arg,i) elif format=='s': filt=str regex="(.*)" func+=" event['%s'] = group[%d]\n"%(arg,i) printk = printk.replace("%"+format,regex,1) args_l.append((arg,filt)) i+=1 if not events_re.has_key(name): events_re[name] = [] exec func events_re[name].append((name,re.compile(printk),eval("my_dispatch_func%d"%num_func))) num_func+=1 # event class passed to callback, this is more convenient than passing a dictionary class Event: def __init__(self,event): self.__dict__=event def __repr__(self): ret = "" for k in self.__dict__: ret += "%s: %s, "%(k,str(self.__dict__[k])) return ret # seamlessly open gziped of raw text files def ftrace_open(filename): if filename.endswith(".gz"): import gzip return gzip.open(filename,"r") elif filename.endswith(".lzma"): try: import lzma except: raise Exception("lzma module could not be imported. Please install python-lzma to seamlessly open lzma compressed file: http://pypi.python.org/pypi/pyliblzma") return lzma.LZMAFile(filename,"r") else: return open(filename,"r") #@profile def parse_ftrace(filename,callback): fid = ftrace_open(filename) progress = ProgressDialog(title="ftrace", message="loading %s..."%(os.path.basename(filename)), max=100, show_time=True, can_cancel=True) progress.open() try: fid.seek(0,2) except ValueError: # gzip do not support seek end # do we uncompress everything. :-/ # parsing is already far slower than uncompressing. while fid.read(1024): pass totsize = fid.tell() fid.seek(0,0) last_percent = 0 # the base regular expressions event_re = re.compile( r'\s*(.+)-([0-9]+)\s+\[([0-9]+)\]\s+([0-9.]+): ([^:]*): (.*)') function_re = re.compile( r'\s*(.+)-([0-9]+)\s+\[([0-9]+)\]\s+([0-9.]+): (.*) <-(.*)') last_timestamp = 0 linenumber = 0 for line in fid: percent = int(fid.tell()*100./totsize) if percent != last_percent: last_percent = percent (cont, skip) = progress.update(percent) if not cont or skip: break linenumber+=1 line = line.rstrip() res = event_re.match(line) if res: groups = res.groups() event_name = groups[4] event = { 'linenumber': linenumber, 'common_comm' : groups[0], 'common_pid' : int(groups[1]), 'common_cpu' : int(groups[2]), 'timestamp' : int(float(groups[3])*1000000), 'event' : event_name, 'event_arg' : groups[5] } last_timestamp = event['timestamp'] to_match = event['event_arg'] try: for name,regex,func in events_re[event_name]: res = regex.search(to_match) if res: func(event,res.groups()) except KeyError: pass callback(Event(event)) continue res = function_re.match(line) if res: event = { 'linenumber': linenumber, 'common_comm' : res.group(1), 'common_pid' : int(res.group(2)), 'common_cpu' : int(res.group(3)), 'timestamp' : int(float(res.group(4))*1000000), 'event':'function', 'callee' : res.group(5), 'caller' : res.group(6) } callback(Event(event)) continue fid.close() def get_partial_text(fn,start,end): text = "" fid = ftrace_open(fn) linenumber = 0 for line in fid: linenumber+=1 if linenumber >= start and linenumber <= end: text+=line return text def load_ftrace(fn): from timechart.model import tcProject proj = tcProject() proj.filename = fn proj.start_parsing(get_partial_text) parse_ftrace(fn,proj.handle_trace_event) proj.finish_parsing() return proj def detect_ftrace(fn): if fn.endswith(".txt"): return load_ftrace if fn.endswith(".txt.gz"): return load_ftrace if fn.endswith(".txt.lzma"): return load_ftrace return None #### TEST ###################################################################### if __name__ == "__main__": def callback(event): #print event.__dict__ pass parse_ftrace(sys.argv[1],callback) #### EOF ###################################################################### pytimechart-1.0.0.rc1/timechart/backends/dummy.py0000644000175100017510000000045111631370253021356 0ustar pierre00000000000000def get_partial_text(fn,start,end): return "" def load_dummy(fn): from timechart.model import tcProject proj = tcProject() proj.filename = fn proj.start_parsing(get_partial_text) proj.finish_parsing() return proj def detect_dummy(fn): #todo return load_dummy pytimechart-1.0.0.rc1/timechart/backends/perf.py0000644000175100017510000000234611567523405021173 0ustar pierre00000000000000import os from timechart.model import tcProject from timechart.window import tcWindow class Event(): def __init__(self,name,kw): self.__dict__=kw self.event = name self.timestamp = self.common_s*1000000+self.common_ns/1000 self.linenumber = 0 def get_partial_text(fn,start,end): return "text trace unsupported with perf backend" def trace_begin(): global proj proj = tcProject() proj.start_parsing(get_partial_text) def trace_end(): proj.finish_parsing() # Create and open the main window. window = tcWindow(project = proj) window.configure_traits() def trace_unhandled(event_name, context, field_dict): event_name = event_name[event_name.find("__")+2:] proj.handle_trace_event(Event(event_name,field_dict)) def load_perf(filename): dotpy = __file__ # perf python wants a .py file and not .pyc... if dotpy.endswith("pyc"): dotpy = dotpy[:-1] perf = "perf" if "PERF" in os.environ: perf = os.environ["PERF"] os.execlp(perf, perf, "trace", "-i", filename, "-s", dotpy) return None def detect_perf(filename): name, ext = os.path.splitext(os.path.basename(filename)) if ext == ".data": return load_perf return None pytimechart-1.0.0.rc1/timechart/backends/trace_cmd.py0000644000175100017510000000530711631370253022151 0ustar pierre00000000000000import sys,os additional_event_field = [ ('softirq_entry', 'name'), ] def get_softirq_entry_name(event): softirq_list = ["HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL", "TASKLET", "SCHED", "HRTIMER", "RCU"] return softirq_list[event.vec] class TraceCmdEventWrapper: def __init__(self,event): self.tracecmd_event = event self.event = str(event.name) self.linenumber = 0 self.common_cpu = int(event.cpu) self.common_comm = str(event.comm) self.common_pid = int(event.pid) self.timestamp = event.ts/1000 def __getattr__(self,name): try: f = self.tracecmd_event[name] except : attr = self.get_additional_event_field(name) if attr: return attr raise AttributeError(name+ " not in "+str( self.tracecmd_event.keys())) try: return long(f) except : return str(f) def get_additional_event_field(self, name): for field in additional_event_field: event = field[0] attr = field[1] if ((self.event==event) & (name==attr)): func = eval("get_"+event+"_"+attr) return func(self) def parse_tracecmd(filename,callback): try: import tracecmd except ImportError: raise Exception("please compile python support in trace-cmd and add trace-cmd directory into your PYTHONPATH environment variable") t = tracecmd.Trace(str(filename)) # the higher level assumes events are already system sorted, but tracecmd sort them by cpus. # so we have to manually sort them. cpu_event_list_not_empty = t.cpus events = [ t.read_event(cpu) for cpu in xrange(t.cpus)] availble_cpu = range(0, t.cpus) while cpu_event_list_not_empty > 0: ts = 0xFFFFFFFFFFFFFFFF if len(availble_cpu): first_cpu = availble_cpu[0] else: break for cpu in availble_cpu: if events[cpu].ts < ts: first_cpu = cpu ts = events[cpu].ts callback(TraceCmdEventWrapper(events[first_cpu])) events[first_cpu] = t.read_event(first_cpu) if events[first_cpu] == None: cpu_event_list_not_empty -= 1 availble_cpu.remove(first_cpu) def get_partial_text(fn,start,end): text = "" return text def load_tracecmd(fn): from timechart.model import tcProject proj = tcProject() proj.filename = fn proj.start_parsing(get_partial_text) parse_tracecmd(fn,proj.handle_trace_event) proj.finish_parsing() return proj def detect_tracecmd(fn): if fn.endswith(".dat"): return load_tracecmd return None pytimechart-1.0.0.rc1/timechart/images/0000755000175100017510000000000011646055745017360 5ustar pierre00000000000000pytimechart-1.0.0.rc1/timechart/images/hide_others.png0000644000175100017510000000201311567523405022352 0ustar pierre00000000000000PNG  IHDRw=sRGBbKGD pHYs B(xtIME 1cIDATHToU}e]7Zf7u#07JyH/ /C/~RpƨYh@[, ukt"d&D?KNr<'w~= '/so8Ck儻c~_66% G%S{[`l[8xW4V//khWMw֭\xӪOGz $#:tzDY*y{@ h\aO5.E&v[JrwkbcWkv#=3_NG"75zC[m-B%?"f<Z_lі}Fe\``H00b1bhLP|^J2?'yI;^~ĠA[t0+U*@p$`c/nvB𛠠xR Qkϕ <4?7w۝(fd̶rE^/<ܵ1JŬ'9Y4l~dRq5[Q Y=G"CR%W D$z]۶o;ޢg`dn03P,T>ƎB @"";|ss.v]̂p8,Ƶ}wM,|XX$[CUO<^BPsѹ/1V9m``+VL&Ms9GqHNs螞|Wօ@ pM}\ƲKq" J7XvvJ!:'U5]NGFFz2٩jD"jrZ: 6:^uR.?kqr+cIENDB`pytimechart-1.0.0.rc1/timechart/images/view_properties.png0000644000175100017510000000262411567523405023313 0ustar pierre00000000000000PNG  IHDRw=sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATHUMhSY6$ Q(:Ttm0q% .jڅ̦02؍.Du]iQ00 Emc̋21yԺ.|\b411qE)5h!@1ι`ll,B9WR'O˲jܻwo8LJ6,9s%qeYm?&LZZI۶[58NK4' ?2)%^~Bw0"$8fֺۣhTb1Q133Rx< T*aff|ާ.QJRّ!Ĝ٢ŋHRhooGSS8 ˗/.֘-J)BpPkBA{tvvh hadKKKRf{_m۷h1Ʋ,G޾}^zU:o>;1p] T*ٳgb(ΝꜬ /re)wϟn޼y(ݏF|DZAC"91(10@M܄1R(|{9uW:;ڼY ӽ$J@ჴB)=RJ[[[}B߾R#` OMM cH&1v"J)d^\\֭9ɤ3fb8~yƍ'ǎkRJAJ )M-F pA)Ç.J}.\0>p1.ajc BXFF}eAJ)1Ô1vqA>OSJE`PmR J)  p>O{rR)xɧqŋc\.͛_۩S199;::~m[Tiaa ѣ-Tݻ߿'###Ǐ2300 ӧ;sܛG@֤ @̓W~//_Y?G1PsE|R]O7+`IENDB`pytimechart-1.0.0.rc1/timechart/images/invert.png0000644000175100017510000000211111567523405021363 0ustar pierre00000000000000PNG  IHDRw=sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATHO#eǿ̴S uR`d큂'R7$LLh01X1?5\lDM )`=̆B-i:N;;B['>yg}511oc 14>Bȧm?uimPr^꺾nvF4Uh+y @ww7(ˈD"e$ѓ5Ms:p/̓_ !EZbkk \LdYF<JjBC@$y+CCCf(ɤH$Jz}}}|0>dY54J ٶ J)FFFч-\i>1t]ULD(^sss>_ #4͆kgvCQJ199C(ʴ(( OyEeY|>u=`< 8m<χ@]zV޵X,cAwJY[=|>WQmhg^\,ywq$I/ڐnBkjM뺸a;~Q]hCgcH˲@REQ BZ$ LQJ7yggwΐ ,TLӜbx;8V0`Y띝g^W WDZapppv??::y1\.Q_,VWW5UU7׿5tBtCG*:OR#cccߤ'x$(k ⁰InR-Ewlp{9s"s~ f/=y JVYXm"Ш S"m;A1UeKL==Gɉ3MD#T@CIXw)X(^1<7G5G'Ro^5ꍏB̡/;Ui'I~<:::q}\ ׉|S_D65f )_]0XMߨ^Rbtrb\.^xjٹ>ǥ+ٛ)@~3IKK˽EQ!/)UXPyydl,i9En4$!$k;::V |_uDQ`SSѭ[}JTLlnn>|eO@>S=BKn>IENDB`pytimechart-1.0.0.rc1/timechart/images/toggle_auto_zoom_y.png0000644000175100017510000000066111567523405023771 0ustar pierre00000000000000PNG  IHDRw=sRGBbKGD pHYs  tIMEtEXtCommentCreated with GIMPW IDATHӽJAERD`#X؋6b)| B |FF0*"fa3!b,wsanw0YXr;X"6q]UGQ12+9E*Tw+8GxOQ Y.jxq3XC5 \6Zڢ&R&UA-{,I9\b%@i Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. pytimechart-1.0.0.rc1/timechart/images/trace_text.png0000644000175100017510000000162511567523405022227 0ustar pierre00000000000000PNG  IHDRw=sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATHKcWzIDŽdJSXU N3UJ)(.:P_ S 7Au q#Ɖss*fI /y9Bk :B BhkLMP(d2 ?{{{~]TlשTcc+%rrx\ASjCeh[rL   B)d+; p˲k/*VWz}5zպg_oDͯ@)bPl]fprrŸ.?8 ol\.(O>m. TxRJBX XT*pp{F21H)%j5BLd`owoh$p||̣G, ˲dT*(gg;®-RMTb5mmmH))JՕR-J$y Bx<Lt777FMZd/_\.NOORrssCT"pttؘ pIXXXx0 N333CWWW5mTNc6HXdvv O 4eYLOOsqqA `OnZ~853M9:::&chhhBB݃e~~`0RϗT^ Aǽzt RJ j3!D;ь 4PI$흝yۿ{8/IENDB`pytimechart-1.0.0.rc1/timechart/images/toggle_cpufreq.png0000644000175100017510000000070711567523405023073 0ustar pierre00000000000000PNG  IHDRw=sRGBbKGD pHYs  tIME$otEXtCommentCreated with GIMPW"IDATH?J@OŪ X.b'Xn '~#O_IENDB`pytimechart-1.0.0.rc1/timechart/images/zoom.png0000644000175100017510000000253011567523405021045 0ustar pierre00000000000000PNG  IHDRw=sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATH[hWƿs9{.lC@!At541XD%"/Y,X⃴Vj"!حHݬ-1\23҇@K80 7gc:_f&aBQkC1Suuu3wޕ ɰ(!Zke)JϛWG9_xw˖-O3͞mۥ\.'o޼Yw!{rxx+:::c>@k~̙40@*z_TOOL&s@عs;ZOR멀zԩ+} @(;wnܶE!VRX,9xQ,PJ},˿v1q sssmV(!c1jkBA!۶9n֭5+0t:xqիD(70)s||rsnwyeBUvVXBJ98Pikk@ ͛m۶}@ xݻg_ť,o=^Raqknnw:;;Bݺuuqq1===MR X+W_7o~16 @k BDe۶d{bҌOyئMT*BeYd2L"P!$QMJ1&\ p-TSJ 7=zt1QBh4ړL&Pf`lvm; J)Ä4Lh"Ƙ8~,KKKp88\,_vBggZ8{,Gu&xim˲lƘZ1aƘCBb1'tcSSSSuݨzɓ'f@1sS4TJ)S6ƔRаq+ J"kU@WJض]RKJKzsR)ZrgnϞ=eD>y7KrA۶ysnI)} ei 9羔w]׿}zQ.woC塡Ȉ]WWgH$Bc\.k!RJ=va~~!DsڪErֵϾߖHIENDB`pytimechart-1.0.0.rc1/timechart/images/toggle_cpuidle.png0000644000175100017510000000045511567523405023053 0ustar pierre00000000000000PNG  IHDRw=sRGBbKGD pHYs  tIME mtEXtCommentCreated with GIMPWIDATH10  Z^p  BK{?;rƸb]BM'\|PJ4T谟eJy]W`aaU !o $W33G8877{\.ޜ:,q7UU}?-eY^:9!Bon۶}X,~ݛ8;;{V/3/gFxGXnu]h@@UɤrnTװ{S1<{K/:8~2sBr\J{A"a4M˲QdK[%XaǤ;?|u~~>yʱ$eV*ZpfD"G<~EQ0"/-B vF#!`a SMZEq3cc#LJ<G\LD^PJ[k"ŋRtCxj,ˏL19(o-ohv}kM] 8JylAB!h6h6>lo[?55F?PݕK9LdlzckQ!&T&!eYLLL`߾4ž]\\'˲?(rXRmC]FQip]|%L*?_jp]7<==aHQ+N\/+|~g@̙3{EQ(h4~Hy(y>l/eL&Ѩ7J8/-//v@x\~NI*BZ{; i,Mplv}GZ4" %1)J(ZaͶ^u]XeA- =YӴ qiou( K.K#1eY~v6un_3LXIb<2v {,jt:."}LW ' єc;l'BNzIENDB`pytimechart-1.0.0.rc1/timechart/images/clear.png0000644000175100017510000000113611567523405021150 0ustar pierre00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8͓ϋqƟ gxu4W{O^A :,"`5 ţ[@{H `A]cv6 m^z{>}eDˈTIv'UW@4pz/MwKnIUJr bCDG=y],cێ pȲD" d2 rp8 Fk10ۡP>4>,@UUuN0k[y~،F7fٗz;g}| IrA~%(G8n^@|8Ζ('LSm fDf_eYMv 8,вC]O5M&0LK҇T*eg2a$*m-_u!6Fqk0|\Y,Kt: [ fY?ܶ,,z}IENDB`pytimechart-1.0.0.rc1/timechart/images/toggle_wakes.png0000644000175100017510000000110511567523405022531 0ustar pierre00000000000000PNG  IHDRw=sRGBbKGD pHYs  tIME [n*tEXtCommentCreated with GIMPWIDATH+a?IA9(+iE.=(`rZ"ցb"ٚw*So<;33< 9/ts2 ` p>sF]S d>#f#208a iƍ L?A|tgɞ"7bNg\͇>*HZH 1v^ ^ϣ D @L;tZn7r#M@5pT….;ssu@~߭R.vQ5"[ Qʳb8vz>J q)q^[P)@fxҺ53 `_'juiRq3TN8fp"U6p0iOJg| NQRIENDB`pytimechart-1.0.0.rc1/timechart/images/show.png0000644000175100017510000000214511567523405021043 0ustar pierre00000000000000PNG  IHDRw=sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATH[L\U9s0\Sֶ҂ 6M^V &5V/AL&>/&Fc6I 0e}CD;YI:w;kxὭ:_*WZwLڧ::Έ5h3C5l]_qR汌\Ϛ"2 zme*gFbHf ZGLp62gj;F^@T&#_;{kjL T B;zdYWkq70:}dq2zx׾팈"`~KiwXܻ%ZO~/Zׇu+B%ҿ`~%?r%R ں ?qϔz@'cpmDz7FbB[ėj-#h;geZ/߽öL W.0.-*,."(t IJ.308 7VWRl:6UI)Ԯʻ$<Wc E2Gk ;*G!?spg_$M>8_C<9 b(-,YgA+ O$ңW>H1$\)$ @j ,l8Bp!J)6656}P068tnƁעD$ca0 9==-"G`>kܓ1l0m䍩Աo FD2Gbbl1q)ַ֚fKH3\eVm(kR^Zz-!-IENDB`pytimechart-1.0.0.rc1/timechart/images/select_all.png0000644000175100017510000000143011567523405022166 0ustar pierre00000000000000PNG  IHDRw=sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATHOA?lv5AaÁH7w9xUO?Hr711HKp4F~l- -ݙ]h}&;|޼Eĥ SJ|UJ=9 LMMuuTU-"޷X@Ur;&)qlԻBc5"bwS2Ķ#́qH&eYJ'tCEcQ75"v{c}|FXzr:??6T.)01~SiiXh4F4 o@d(ŏMwM0+Uh1Rh[Dӻ>;V.%nyôm lno7V{]c9&L| u}8qYݘp_R*s _D첵ˈIENDB`pytimechart-1.0.0.rc1/timechart/images/toggle_autohide.png0000644000175100017510000000060711567523405023227 0ustar pierre00000000000000PNG  IHDRw=sRGBbKGD pHYs  tIMEtEXtCommentCreated with GIMPWIDATH=N@+tԄ"W⟜ )K: f#Yk' f<;o{WTق3^e0 & F(p.ꇹ$!!.pBƠjYǠ'-.b#7Ėz3xwb K *eSbM~^{h[3L>:ic]Dz̢Kr}p_EW jfxgtmRIENDB`pytimechart-1.0.0.rc1/timechart/images/unzoom.png0000644000175100017510000000247611567523405021421 0ustar pierre00000000000000PNG  IHDRw=sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATHKhTWq獙LC@t Sc\.B[Zઋ2"+m@. BpBZj"!ةH%y{ϣ\m ?8p8p1U_o1ϳJҢj!Ƙ٦7nȗ#lnnh44NZcZ+\.9 ;v<<۶kRI^zΝ;oB~q]ӗtww7|T(~5`n߾Ϝ9Zt֭ffp"ۻ'{.ٳgPk}8LGSN{P(}osMٶ,8ip8\M&K=888J1Zk4%1q*ƘNB@Ƙ4c_{ b66ܹ3!eJiɓ'/B cEZxϩRNp7 `Ʋ,W)bŋ;ci)F΁M;::B_oڵB@ -7oڗ.]:=??/ꛓ6l6{qڨy:t臞H$BǓ׮]X^^#Jd2˗ow16OR @k BtU۶әL RMj@҆:9۶mh*e2D>N/h uR,XcLS]N)%P._{----*,L&FX?V,"mRa)eF !1u@uO&?>z(Fju2No>ٳg9nim˲lƘZ1QƘCIB1'J]]]Ƶ? > @o8&kRHgcJ)L$K' !Jh4B1433s:P(J)] ۶kҪRjE)9^Z/pΟ*R+,]422JRضmy[RJ+(0}eYZkHι/]_~s~YǎSVGFFvSSb 1S׵BI)%OMLLyٳg !{ֳI{{;RdnU[-@IENDB`pytimechart-1.0.0.rc1/timechart/images/gtkrc0000644000175100017510000000052111567523405020406 0ustar pierre00000000000000# minimal style that allows pytimechart to looks at least ok on # every themes of ubuntu style "default" { text[NORMAL] = "#323232" bg[NORMAL] = "#D9D4CC" } style "menubar" = "default" { engine "murrine" { textstyle = 0 } } class "GtkMenuBar" style:highest "default" widget_class "*.*" style "default" pytimechart-1.0.0.rc1/timechart/plot.py0000644000175100017510000004711511646002236017436 0ustar pierre00000000000000from enthought.chaco.api import ArrayDataSource, DataRange1D, LinearMapper,BarPlot, LinePlot, \ ScatterPlot, PlotAxis, PlotGrid,OverlayPlotContainer, VPlotContainer,add_default_axes, \ add_default_grids,VPlotContainer from enthought.chaco.tools.api import PanTool, ZoomTool,RangeSelection,RangeSelectionOverlay from enthought.chaco.api import create_line_plot from enthought.traits.ui.api import View,Item,VGroup,HGroup from enthought.traits.api import HasTraits,DelegatesTo,Trait from enthought.traits.api import Float, Instance, Int,Bool,Str,Unicode,Enum,Button from enthought.chaco.api import AbstractOverlay, BaseXYPlot from enthought.chaco.label import Label from enthought.kiva.traits.kiva_font_trait import KivaFont from enthought.enable.api import black_color_trait, KeySpec from model import tcProject from colors import get_aggcolor_by_id,get_color_id import tools from numpy import linspace,arange,amin,amax from math import log from numpy import array, ndarray,argmax,searchsorted,mean from numpy import array, compress, column_stack, invert, isnan, transpose, zeros,ones from enthought.traits.api import List from enthought.enable.colors import ColorTrait from enthought.pyface.timer import timer process_colors=[0x000000,0x555555,0xffff88,0x55ffff,0xAD2D2D, 0xeeeeee,0xeeaaaa,0xaaaaee,0xee0000] class TimeChartOptions(HasTraits): remove_pids_not_on_screen = Bool(True) show_wake_events = Bool(False) show_p_states = Bool(True) show_c_states = Bool(True) auto_zoom_y = Bool(True) use_overview = Bool(True) proj = tcProject def connect(self,plot): self.auto_zoom_timer = timer.Timer(300,self._auto_zoom_y_delayed) self.auto_zoom_timer.Stop() self.plot = plot def _minimum_time_filter_changed(self): self.plot.invalidate() def _remove_pids_not_on_screen_changed(self): self.plot.invalidate() def _show_wake_events_changed(self): self.plot.invalidate() def _show_p_states_changed(self): self.plot.invalidate() def _show_c_states_changed(self): self.plot.invalidate() def _use_overview_changed(self): self.plot.invalidate() def _auto_zoom_y_changed(self,val): self.plot.auto_zoom_y() self.auto_zoom_timer.Stop() def _auto_zoom_y_delayed(self): self.plot.auto_zoom_y() self.auto_zoom_timer.Stop() def _on_toggle_autohide(self, value): self.remove_pids_not_on_screen = value def _on_toggle_wakes(self, value): self.show_wake_events = value def _on_toggle_cpuidle(self, value): self.show_c_states = value def _on_toggle_cpufreq(self, value): self.show_p_states = value def _on_toggle_auto_zoom_y(self, value): self.auto_zoom_y = value def _on_toggle_overview(self, value): self.use_overview = value class TextView(HasTraits): text = Str save = Button() def __init__(self,text,title): self.text = text self.trait_view().title = title traits_view = View( Item('text',style="custom",show_label=False), HGroup( Item('save'), show_labels = False), resizable = True, width = 1024, height = 600, ) def _save_changed(self): from window import save_dialog fn = save_dialog() if fn: try: f = open(fn,"w") f.write(self.text) f.close() except: print "unable to write file..." class RangeSelectionTools(HasTraits): time = Str start = 0 end = 0 def connect(self,plot): self.plot = plot plot.range_selection.on_trait_change(self._selection_update_handler, "selection") self._timer = timer.Timer(100,self._selection_updated_delayed) self._timer.Stop() def _selection_update_handler(self,value): if value is not None : self.start, self.end = amin(value), amax(value) time = self.end-self.start self.time = "%d.%03d %03ds"%(time/1000000,(time/1000)%1000,time%1000) self.plot.immediate_invalidate() self._timer.Stop() self._timer.Start() else: self.start = 0 self.end = 0 def _on_zoom(self): if self.end != self.start: self.plot.index_range.high = self.end self.plot.index_range.low = self.start self.plot.range_selection.deselect() self.plot.invalidate_draw() self.plot.request_redraw() def _on_unzoom(self): self.plot.index_range.high = self.plot.highest_i self.plot.index_range.low = self.plot.lowest_i self.plot.invalidate_draw() self.plot.request_redraw() def _on_trace_text(self): if self.end != self.start: text = self.plot.proj.get_selection_text(self.start,self.end) text_view = TextView(text,"%s:[%d:%d]"%(self.plot.proj.filename,self.start,self.end)) text_view.edit_traits() def _selection_updated_delayed(self): self.plot.proj.process_stats(self.start,self.end) self._timer.Stop() class tcPlot(BarPlot): """custom plot to draw the timechart probably not very 'chacotic' We draw the chart as a whole """ # The text of the axis title. title = Trait('', Str, Unicode) #May want to add PlotLabel option # The font of the title. title_font = KivaFont('modern 9') # The font of the title. title_font_large = KivaFont('modern 15') # The font of the title. title_font_huge = KivaFont('modern 20') # The spacing between the axis line and the title title_spacing = Trait('auto', 'auto', Float) # The color of the title. title_color = ColorTrait("black") not_on_screen = List on_screen = List options = TimeChartOptions() range_tools = RangeSelectionTools() redraw_timer = None def invalidate(self): self.invalidate_draw() self.request_redraw() def immediate_invalidate(self): self.invalidate_draw() self.request_redraw_delayed() def request_redraw_delayed(self): self.redraw_timer.Stop() BarPlot.request_redraw(self) def request_redraw(self): if self.redraw_timer == None: self.redraw_timer = timer.Timer(30,self.request_redraw_delayed) self.redraw_timer.Start() def auto_zoom_y(self): if self.value_range.high != self.max_y+1 or self.value_range.low != self.min_y: self.value_range.high = self.max_y+1 self.value_range.low = self.min_y self.invalidate_draw() self.request_redraw() def _gather_timechart_points(self,start_ts,end_ts,y,step): low_i = searchsorted(end_ts,self.index_mapper.range.low) high_i = searchsorted(start_ts,self.index_mapper.range.high) if low_i==high_i: return array([]) start_ts = start_ts[low_i:high_i] end_ts = end_ts[low_i:high_i] points = column_stack((start_ts,end_ts, zeros(high_i-low_i)+(y+step), ones(high_i-low_i)+(y-step),array(range(low_i,high_i)))) return points def _draw_label(self,gc,label,text,x,y): label.text = text l_w,l_h = label.get_width_height(gc) offset = array((x,y-l_h/2)) gc.translate_ctm(*offset) label.draw(gc) gc.translate_ctm(*(-offset)) return l_w,l_h def _draw_timechart(self,gc,tc,label,base_y): bar_middle_y = self.first_bar_y+(base_y+.5)*self.bar_height points = self._gather_timechart_points(tc.start_ts,tc.end_ts,base_y,.2) overview = None if self.options.use_overview: if points.size > 500: overview = tc.get_overview_ts(self.overview_threshold) points = self._gather_timechart_points(overview[0],overview[1],base_y,.2) if self.options.remove_pids_not_on_screen and points.size == 0: return 0 if bar_middle_y+self.bar_height < self.y or bar_middle_y-self.bar_height>self.y+self.height: return 1 #quickly decide we are not on the screen self._draw_bg(gc,base_y,tc.bg_color) # we are too short in height, dont display all the labels if self.last_label >= bar_middle_y: # draw label l_w,l_h = self._draw_label(gc,label,tc.name,self.x,bar_middle_y) self.last_label = bar_middle_y-8 else: l_w,l_h = 0,0 if points.size != 0: # draw the middle line from end of label to end of screen if l_w != 0: # we did not draw label because too short on space gc.set_alpha(0.2) gc.move_to(self.x+l_w,bar_middle_y) gc.line_to(self.x+self.width,bar_middle_y) gc.draw_path() gc.set_alpha(0.5) # map the bars start and stop locations into screen space lower_left_pts = self.map_screen(points[:,(0,2)]) upper_right_pts = self.map_screen(points[:,(1,3)]) bounds = upper_right_pts - lower_left_pts if overview: # critical path, we only draw unicolor rects #calculate the mean color #print points.size gc.set_fill_color(get_aggcolor_by_id(get_color_id("overview"))) gc.set_alpha(.9) rects=column_stack((lower_left_pts, bounds)) gc.rects(rects) gc.draw_path() else: # lets display them more nicely rects=column_stack((lower_left_pts, bounds,points[:,(4)])) last_t = -1 gc.save_state() for x,y,sx,sy,i in rects: t = tc.types[i] if last_t != t: # only draw when we change color. agg will then simplify the path # note that a path only can only have one color in agg. gc.draw_path() gc.set_fill_color(get_aggcolor_by_id(int(t))) last_t = t gc.rect(x,y,sx,sy) # draw last path gc.draw_path() if tc.has_comments: for x,y,sx,sy,i in rects: if sx<8: # not worth calculatig text size continue label.text = tc.get_comment(i) l_w,l_h = label.get_width_height(gc) if l_w < sx: offset = array((x,y+self.bar_height*.6/2-l_h/2)) gc.translate_ctm(*offset) label.draw(gc) gc.translate_ctm(*(-offset)) if tc.max_latency > 0: # emphase events where max_latency is reached ts = tc.max_latency_ts if ts.size>0: points = self._gather_timechart_points(ts,ts,base_y,0) if points.size>0: # map the bars start and stop locations into screen space gc.set_alpha(1) lower_left_pts = self.map_screen(points[:,(0,2)]) upper_right_pts = self.map_screen(points[:,(1,3)]) bounds = upper_right_pts - lower_left_pts rects=column_stack((lower_left_pts, bounds)) gc.rects(rects) gc.draw_path() return 1 def _draw_freqchart(self,gc,tc,label,y): self._draw_bg(gc,y,tc.bg_color) low_i = searchsorted(tc.start_ts,self.index_mapper.range.low) high_i = searchsorted(tc.start_ts,self.index_mapper.range.high) if low_i>0: low_i -=1 if high_i=high_i-1: return array([]) start_ts = tc.start_ts[low_i:high_i-1] end_ts = tc.start_ts[low_i+1:high_i] values = (tc.types[low_i:high_i-1]/(float(tc.max_types)))+y starts = column_stack((start_ts,values)) ends = column_stack((end_ts,values)) starts = self.map_screen(starts) ends = self.map_screen(ends) gc.begin_path() gc.line_set(starts, ends) gc.stroke_path() for i in xrange(len(starts)): x1,y1 = starts[i] x2,y2 = ends[i] sx = x2-x1 if sx >8: label.text = str(tc.types[low_i+i]) l_w,l_h = label.get_width_height(gc) if l_w < sx: if x1<0:x1=0 offset = array((x1,y1)) gc.translate_ctm(*offset) label.draw(gc) gc.translate_ctm(*(-offset)) def _draw_wake_ups(self,gc,processes_y): low_i = searchsorted(self.proj.wake_events['time'],self.index_mapper.range.low) high_i = searchsorted(self.proj.wake_events['time'],self.index_mapper.range.high) gc.set_stroke_color((0,0,0,.6)) for i in xrange(low_i,high_i): waker,wakee,ts = self.proj.wake_events[i] if processes_y.has_key(wakee) and processes_y.has_key(waker): y1 = processes_y[wakee] y2 = processes_y[waker] x,y = self.map_screen(array((ts,y1))) gc.move_to(x,y) y2 = processes_y[waker] x,y = self.map_screen(array((ts,y2))) gc.line_to(x,y) x,y = self.map_screen(array((ts,(y1+y2)/2))) if y1 > y2: y+=5 dy=-5 else: y-=5 dy=+5 gc.move_to(x,y) gc.line_to(x-3,y+dy) gc.move_to(x,y) gc.line_to(x+3,y+dy) gc.draw_path() def _draw_bg(self,gc,y,color): gc.set_alpha(1) gc.set_line_width(0) gc.set_fill_color(color) this_bar_y = self.map_screen(array((0,y)))[1] gc.rect(self.x, this_bar_y, self.width, self.bar_height) gc.draw_path() gc.set_line_width(self.line_width) gc.set_alpha(0.5) def _draw_plot(self, gc, view_bounds=None, mode="normal"): gc.save_state() gc.clip_to_rect(self.x, self.y, self.width, self.height) gc.set_antialias(1) gc.set_stroke_color(self.line_color_) gc.set_line_width(self.line_width) self.first_bar_y = self.map_screen(array((0,0)))[1] self.last_label = self.height+self.y self.bar_height = self.map_screen(array((0,1)))[1]-self.first_bar_y self.max_y = y = self.proj.num_cpu*2+self.proj.num_process-1 if self.bar_height>15: font = self.title_font_large else: font = self.title_font label = Label(text="", font=font, color=self.title_color, rotate_angle=0) # we unmap four pixels on screen, and find the nearest greater power of two # this by rounding the log2, and then exponentiate again # as the overview data is cached, this avoids using too much memory four_pixels = self.index_mapper.map_data(array((0,4))) self.overview_threshold = 1< high: low=0 high=1 # we have the same x_mapper/range for each plots index_range = DataRange1D(low=low, high=high) index_mapper = LinearMapper(range=index_range,domain_limit=(low,high)) value_range = DataRange1D(low=0, high=project.num_cpu*2+project.num_process) value_mapper = LinearMapper(range=value_range,domain_limit=(0,project.num_cpu*2+project.num_process)) index = ArrayDataSource(array((low,high)), sort_order="ascending") plot = tcPlot(index=index, proj=project, bgcolor="white",padding=(0,0,0,40), use_backbuffer = True, fill_padding = True, value_mapper = value_mapper, index_mapper=index_mapper, line_color="black", render_style='hold', line_width=1) plot.lowest_i = low plot.highest_i = high project.on_trait_change(plot.invalidate, "plot_redraw") project.on_trait_change(plot.invalidate, "selected") max_process = 50 if value_range.high>max_process: value_range.low = value_range.high-max_process # Attach some tools plot.tools.append(tools.myPanTool(plot,drag_button='left')) zoom = tools.myZoomTool(component=plot, tool_mode="range", always_on=True,axis="index",drag_button=None) plot.tools.append(zoom) plot.range_selection = tools.myRangeSelection(plot,resize_margin=3) plot.tools.append(plot.range_selection) plot.overlays.append(RangeSelectionOverlay(component=plot,axis="index",use_backbuffer=True)) axe = PlotAxis(orientation='bottom',title='time',mapper=index_mapper,component=plot) plot.underlays.append(axe) plot.options.connect(plot) plot.range_tools.connect(plot) return plot pytimechart-1.0.0.rc1/timechart/window.py0000644000175100017510000001240311646044755017773 0ustar pierre00000000000000import sys,os from enthought.traits.api import HasTraits,Str,Button from enthought.traits.ui.api import InstanceEditor,Item,View,HSplit,VSplit,Handler, StatusItem from enthought.traits.ui.menu import Action, MenuBar, ToolBar, Menu, Separator from model import tcProject from plot import tcPlot, create_timechart_container from enthought.enable.component_editor import ComponentEditor from actions import _create_toolbar_actions, _create_menubar_actions __doc__ = """http://packages.python.org/pytimechart/""" __version__="1.0.0.rc1" def browse_doc(): from enthought.etsconfig.api import ETSConfig if ETSConfig.toolkit == 'wx': try: from wx import LaunchDefaultBrowser LaunchDefaultBrowser(__doc__) except: print "failure to launch browser" class aboutBox(HasTraits): program = Str("pytimechart: linux traces exploration and visualization") author = Str("Pierre Tardy ") version = Str(__version__) doc = Button(__doc__) traits_view = View( Item("program", show_label=False, style="readonly"), Item("author" , style="readonly"), Item("version", style="readonly"), Item("doc"), width=500, title="about" ) def _doc_changed(self,ign): browse_doc() class tcActionHandler(Handler): handler_list = [] actions = {} def chooseAction(self, UIInfo,name): window = UIInfo.ui.context['object'] handler_list = [window, window.project, window.plot, window.plot.options, window.plot.range_tools] for i in handler_list: fn = getattr(i, name, None) if fn is not None: if name.startswith("_on_toggle"): fn(getattr(UIInfo,name[len("_on_"):].replace("_"," ")).checked) else: fn() class tcWindow(HasTraits): project = tcProject plot = tcPlot def __init__(self,project): self.project = project self.plot = create_timechart_container(project) self.plot_range_tools = self.plot.range_tools self.plot_range_tools.on_trait_change(self._selection_time_changed, "time") self.trait_view().title = self.get_title() def get_title(self): if self.project.filename == "dummy": return "PyTimechart: Please Open a File" return "PyTimechart:"+self.project.filename # Create an action that exits the application. status = Str("Welcome to PyTimechart") traits_view = View( HSplit( VSplit( Item('project', show_label = False, editor=InstanceEditor(view = 'process_view'), style='custom',width=150), # Item('plot_range_tools', show_label = False, editor=InstanceEditor(view = 'selection_view'), style='custom',width=150,height=100) ), Item('plot', show_label = False, editor = ComponentEditor()), ), toolbar = ToolBar(*_create_toolbar_actions(), image_size = ( 24, 24 ), show_tool_names = False), menubar = MenuBar(*_create_menubar_actions()), statusbar = [StatusItem(name='status'),], resizable = True, width = 1280, height = 1024, handler = tcActionHandler() ) def _on_open_trace_file(self): if open_file(None) and self.project.filename=="dummy": self._ui.dispose() def _on_view_properties(self): self.plot.options.edit_traits() def _on_exit(self,n=None): self.close() sys.exit(0) def close(self,n=None): pass def _on_about(self): aboutBox().edit_traits() def _on_doc(self): browse_doc() def _selection_time_changed(self): self.status = "selection time:%s"%(self.plot_range_tools.time) prof = 0 import wx def open_dialog(): dlg = wx.FileDialog(None, "Choose a file", "", "", "*.txt;*.gz;*.lzma;*.dat", wx.OPEN) rv = None if dlg.ShowModal() == wx.ID_OK: filename=dlg.GetFilename() dirname=dlg.GetDirectory() rv = os.path.join(dirname, filename) dlg.Destroy() return rv def save_dialog(): dlg = wx.FileDialog(None, "Save file...", "", "", "*.txt", wx.SAVE) rv = None if dlg.ShowModal() == wx.ID_OK: filename=dlg.GetFilename() dirname=dlg.GetDirectory() rv = os.path.join(dirname, filename) dlg.Destroy() return rv def open_file(fn=None): from backends.perf import detect_perf from backends.ftrace import detect_ftrace from backends.dummy import detect_dummy from backends.trace_cmd import detect_tracecmd if fn == None: fn = open_dialog() if fn == None: return 0 parser = None for func in detect_ftrace, detect_perf, detect_tracecmd, detect_dummy: parser = func(fn) if parser: break if prof: import cProfile cProfile.run('proj = parser(fn)','timechart_load.prof') elif fn: proj = parser(fn) if proj: # Create and open the main window. window = tcWindow(project = proj) window._ui = window.edit_traits() # Traits has the bad habbit of autoselecting the first row in the table_editor. Workaround this. proj.selected = [] return 1 return 0 pytimechart-1.0.0.rc1/timechart/plugins/0000755000175100017510000000000011646055745017574 5ustar pierre00000000000000pytimechart-1.0.0.rc1/timechart/plugins/irq.py0000644000175100017510000000701011631370253020723 0ustar pierre00000000000000from timechart.plugin import * from timechart import colors from timechart.model import tcProcess class irq(plugin): additional_colors = """ """ additional_ftrace_parsers = [ ('softirq_entry','softirq=%d action=%s','vec','name'), ('softirq_exit','softirq=%d action=%s','vec','name'), ('softirq_entry','vec=%d [action=%s]','vec','name'), ('softirq_exit','vec=%d [action=%s]','vec','name'), ('softirq_raise','vec=%d [action=%s]','vec','name'), ('irq_handler_entry', 'irq=%d handler=%s','irq','name'), ('irq_handler_entry', 'irq=%d name=%s','irq','name'), ('irq_handler_exit', 'irq=%d return=%s','irq','ret'), ('irq_handler_exit', 'irq=%d ret=%s','irq','ret'), ('workqueue_execution','thread=%s func=%s\\+%s/%s','thread','func','func_offset','func_size'), ('workqueue_execution','thread=%s func=%s','thread','func'), ('workqueue_execution_end','thread=%s func=%s','thread','func'), ('tasklet_action','%s: %s','state', 'func'), ] additional_process_types = { "irq":(tcProcess, IRQ_CLASS), "softirq":(tcProcess, IRQ_CLASS), "work":(tcProcess, WORK_CLASS), } @staticmethod def do_event_irq_handler_entry(self,event,soft=""): process = self.generic_find_process(0,"%sirq%d:%s"%(soft,event.irq,event.name),soft+"irq") self.last_irq[(event.irq,soft)] = process self.generic_process_start(process,event) @staticmethod def do_event_irq_handler_exit(self,event,soft=""): try: process = self.last_irq[(event.irq,soft)] except KeyError: print "error did not find last irq" print self.last_irq.keys(),(event.irq,soft) return self.generic_process_end(process,event) try: if event.ret=="unhandled": process['types'][-1]=4 except: pass @staticmethod def do_event_softirq_entry(self,event): event.irq = event.vec return irq.do_event_irq_handler_entry(self,event,"soft") @staticmethod def do_event_softirq_exit(self,event): event.irq = event.vec return irq.do_event_irq_handler_exit(self,event,"soft") @staticmethod def do_event_workqueue_execution(self,event): process = self.generic_find_process(0,"work:%s:%s"%(event.thread,event.func),"work") if len(process['start_ts'])>len(process['end_ts']): process['end_ts'].append(process['start_ts'][-1]) self.generic_process_start(process,event,False) @staticmethod def do_event_workqueue_execution_end(self,event): process = self.generic_find_process(0,"work:%s:%s"%(event.thread,event.func),"work") self.generic_process_end(process,event,False) @staticmethod def do_event_tasklet_action(self,event): process = self.generic_find_process(0,"tasklet:%s"%(event.func),"work") if event.state=="tasklet_enter": self.generic_process_start(process,event) else: self.generic_process_end(process,event) @staticmethod def do_event_softirq_raise(self,event): p_stack = self.cur_process[event.common_cpu] softirqname = "softirq:%d:%s"%(event.vec,event.name) if p_stack: p = p_stack[-1] self.wake_events.append(((p['comm'],p['pid']),(softirqname,0),event.timestamp)) else: p = self.generic_find_process(0,softirqname+" raise","softirq") self.generic_process_single_event(p,event) plugin_register(irq) pytimechart-1.0.0.rc1/timechart/plugins/spi.py0000644000175100017510000000172511631370253020732 0ustar pierre00000000000000from timechart.plugin import * from timechart import colors from timechart.model import tcProcess # to use with start_spi.sh last_spi = [] class spi(plugin): additional_colors = """ spi_bg #80ff80 """ additional_ftrace_parsers = [ ] additional_process_types = { "spi":(tcProcess, MISC_TRACES_CLASS), } @staticmethod def do_function_spi_sync(proj,event): global last_spi process = proj.generic_find_process(0,"spi:%s"%(event.caller),"spi") last_spi.append(process) proj.generic_process_start(process,event,False) @staticmethod def do_function_spi_complete(proj,event): global last_spi if len(last_spi): process = last_spi.pop(0) proj.generic_process_end(process,event,False) @staticmethod def do_function_spi_async(proj,event): if event.caller != 'spi_sync': spi.do_function_spi_sync(proj,event) plugin_register(spi) pytimechart-1.0.0.rc1/timechart/plugins/cpuidle.py0000644000175100017510000001007711601347446021571 0ustar pierre00000000000000from timechart.plugin import * from timechart import colors from timechart.model import tcProcess, _pretty_time from enthought.traits.api import Bool c_state_table = ["C0","C1","C2","C4","C6","S0i1","S0i3"] #by default it is hidden... class tcCpuIdle(tcProcess): show = Bool(False) def _get_name(self): return "%s (%s)"%(self.comm, _pretty_time(self.total_time)) class cpu_idle(plugin): additional_colors = """ C0 #000000 C1 #bbbbff C2 #7777ff C3 #5555ff C4 #3333ff C5 #1111ff C6 #0000ff S0i3 #0011ff S0i1 #0022ff cpuidle_bg #ffdddd cpufreq_bg #ffddee """ additional_ftrace_parsers = [ ('power_start', 'type=%d state=%d', 'type','state'), ('power_frequency', 'type=%d state=%d', 'type','state'), #('power_end', 'nothing interesting to parse'), ('cpu_idle', 'state=%d cpu_id=%d', 'state', 'cpuid'), ('cpu_frequency', 'state=%d cpu_id=%d', 'state', 'cpuid'), ] additional_process_types = { "cpuidle":(tcCpuIdle,POWER_CLASS), "cpufreq":(tcCpuIdle,POWER_CLASS), } @staticmethod def start_cpu_idle(self, event): try: tc = self.tmp_c_states[event.cpuid] except: self.ensure_cpu_allocated(event.cpuid) tc = self.tmp_c_states[event.cpuid] if len(tc['start_ts'])>len(tc['end_ts']): tc['end_ts'].append(event.timestamp) self.missed_power_end +=1 if self.missed_power_end < 10: print "warning: missed cpu_idle end" if self.missed_power_end == 10: print "warning: missed cpu_idle end: wont warn anymore!" name = c_state_table[int(event.state)] tc['start_ts'].append(event.timestamp) tc['types'].append(colors.get_color_id(name)) process = self.generic_find_process(0,"cpu%d/%s"%(event.cpuid,name),"cpuidle") self.generic_process_start(process,event, build_p_stack=False) @staticmethod def stop_cpu_idle(self, event): try: tc = self.tmp_c_states[event.cpuid] except: self.ensure_cpu_allocated(event.cpuid) tc = self.tmp_c_states[event.cpuid] if len(tc['start_ts'])>len(tc['end_ts']): name = colors.get_colorname_by_id(tc['types'][-1]) tc['end_ts'].append(event.timestamp) process = self.generic_find_process(0,"cpu%d/%s"%(event.cpuid,name),"cpuidle") self.generic_process_end(process,event, build_p_stack=False) # stable event support @staticmethod def do_event_cpu_idle(self,event): if event.state != 4294967295 : cpu_idle.start_cpu_idle(self, event) else : cpu_idle.stop_cpu_idle(self, event) # legacy event support @staticmethod def do_event_power_start(self,event): event.cpuid = event.common_cpu if event.type==1:# c_state cpu_idle.start_cpu_idle(self, event) @staticmethod def do_event_power_end(self,event): event.cpuid = event.common_cpu cpu_idle.stop_cpu_idle(self, event) @staticmethod def do_all_events(self,event): event.cpuid = event.common_cpu cpu_idle.stop_cpu_idle(self, event) @staticmethod def do_event_cpu_frequency(self,event): self.ensure_cpu_allocated(event.common_cpu) tc = self.tmp_p_states[event.cpuid] if len(tc['types']) > 0: name = tc['types'][-1] process = self.generic_find_process(0,"cpu%d/freq:%s"%(event.cpuid,name),"cpufreq") self.generic_process_end(process,event, build_p_stack=False) tc['start_ts'].append(event.timestamp) tc['types'].append(event.state) name = event.state process = self.generic_find_process(0,"cpu%d/freq:%s"%(event.cpuid,name),"cpufreq") self.generic_process_start(process,event, build_p_stack=False) @staticmethod def do_event_power_frequency(self,event): if event.type==2:# p_state event.cpuid = event.common_cpu cpu_idle.do_event_cpu_frequency(self, event) plugin_register(cpu_idle) pytimechart-1.0.0.rc1/timechart/plugins/timers.py0000644000175100017510000000611211567523405021444 0ustar pierre00000000000000from timechart.plugin import * from timechart import colors from timechart.model import tcProcess class timer(plugin): additional_colors = """ timer_bg #e5bebe timer #ee0000 """ additional_ftrace_parsers = [ ("timer_expire_entry","timer=%s function=%s now=%d","timer","function","now"), ("timer_expire_exit","timer=%s","timer"), ("hrtimer_expire_entry","timer=%s function=%s now=%d","timer","function","now"), ("hrtimer_expire_exit","timer=%s","timer"), ("hrtimer_cancel","timer=%s","timer"), ("hrtimer_start","hrtimer=%s function=%s expires=%d softexpires=%d","timer","function","expire","timeout"), ("itimer_expire","which=%d pid=%d now=%d","which","pid","now"), ("smp_apic_timer_interrupt","%s %s","state","func"), ] additional_process_types = { "timer":(tcProcess, IRQ_CLASS), } timers_dict = {} jitter = 0 @staticmethod def do_event_timer_expire_entry(proj,event): process = proj.generic_find_process(0,"timer[%d]:%s"%(event.common_cpu,event.function),"timer") timer.timers_dict[event.timer] = process proj.generic_process_start(process,event,False) if event.event.startswith("hr"): event.now = event.now/1000 timer.jitter = event.now - event.timestamp @staticmethod def do_event_timer_expire_exit(proj,event): if timer.timers_dict.has_key(event.timer): process = timer.timers_dict[event.timer] proj.generic_process_end(process,event,False) do_event_hrtimer_expire_entry = do_event_timer_expire_entry do_event_hrtimer_expire_exit = do_event_timer_expire_exit @staticmethod def do_event_hrtimer_cancel(proj,event): if timer.timers_dict.has_key(event.timer): process = timer.timers_dict[event.timer] process = proj.generic_find_process(0,"%s:cancel"%(process["comm"]),"timer") proj.generic_process_start(process,event,False) proj.generic_process_end(process,event,False) @staticmethod def do_event_hrtimer_start(proj,event): if timer.timers_dict.has_key(event.timer): process = timer.timers_dict[event.timer] process = proj.generic_find_process(0,"%s:start"%(process["comm"]),"timer") proj.generic_process_start(process,event,False) proj.generic_process_end(process,event,False) @staticmethod def do_event_itimer_expire(proj,event): process = proj.generic_find_process(0,"itimer:%d %d"%(event.which,event.pid),"timer") proj.generic_process_start(process,event,False) proj.generic_process_end(process,event,False) @staticmethod def do_event_smp_apic_timer_interrupt(proj,event): if not event.__dict__.has_key("func"): return process = proj.generic_find_process(0,"apictimer[%d]:%s"%(event.common_cpu,event.func),"timer") if event.state == "start": proj.generic_process_start(process,event,False) else: proj.generic_process_end(process,event,False) plugin_register(timer) pytimechart-1.0.0.rc1/timechart/plugins/sched.py0000644000175100017510000000273011567523405021231 0ustar pierre00000000000000from timechart.plugin import * from timechart import colors from timechart.model import tcProcess class sched(plugin): additional_colors = """ """ additional_ftrace_parsers = [ ] additional_process_types = { "kernel_process":(tcProcess, KERNEL_CLASS), "user_process":(tcProcess, USER_CLASS) } @staticmethod def do_event_sched_switch(self,event): # @todo differenciate between kernel and user process prev = self.generic_find_process(event.prev_pid,event.prev_comm,"user_process",event.timestamp-100000000) next = self.generic_find_process(event.next_pid,event.next_comm,"user_process",event.timestamp-100000000) self.generic_process_end(prev,event) if event.__dict__.has_key('prev_state') and event.prev_state == 'R':# mark prev to be waiting for cpu prev['start_ts'].append(event.timestamp) prev['types'].append(colors.get_color_id("waiting_for_cpu")) prev['cpus'].append(event.common_cpu) self.generic_process_start(next,event) @staticmethod def do_event_sched_wakeup(self,event): p_stack = self.cur_process[event.common_cpu] if p_stack: p = p_stack[-1] self.wake_events.append(((p['comm'],p['pid']),(event.comm,event.pid),event.timestamp)) else: self.wake_events.append(((event.common_comm,event.common_pid),(event.comm,event.pid),event.timestamp)) plugin_register(sched) pytimechart-1.0.0.rc1/timechart/plugins/__init__.py0000644000175100017510000000012211567523405021673 0ustar pierre00000000000000try: __import__('pkg_resources').declare_namespace(__name__) except: pass pytimechart-1.0.0.rc1/timechart/plugins/runtime_pm.py0000644000175100017510000000412011567523405022315 0ustar pierre00000000000000from timechart.plugin import * from timechart import colors from timechart.model import tcProcess class tcRuntimePM(tcProcess): def _get_name(self): return "%s"%(self.comm) def get_comment(self,i): return colors.get_colorname_by_id(self.types[i])[len("rpm_"):] class runtime_pm(plugin): additional_colors = """ runtime_pm_bg #e5bebe rpm_usage=-1 #ff0000 rpm_usage=0 #eeeeee rpm_usage=1 #FA8072 rpm_usage=2 #FFA500 rpm_usage=3 #FF8C00 rpm_usage=4 #FF7F50 rpm_usage=5 #FF6347 rpm_usage=6 #FF4500 rpm_suspended #eeeeee rpm_suspending #eeaaaa rpm_resuming #aaaaee rpm_active #ee0000 """ additional_ftrace_parsers = [ ('runtime_pm_status', 'driver=%s dev=%s status=%s', 'driver','dev','status'), ('runtime_pm_usage', 'driver=%s dev=%s usage=%d', 'driver','dev','usage'), ] additional_process_types = {"runtime_pm":(tcRuntimePM,POWER_CLASS)} @staticmethod def do_event_runtime_pm_status(proj,event): if proj.first_ts == 0: proj.first_ts = event.timestamp-1 p = proj.generic_find_process(0,"runtime_pm:%s %s"%(event.driver,event.dev),"runtime_pm") if len(p['start_ts'])>len(p['end_ts']): p['end_ts'].append(event.timestamp) if event.status!="SUSPENDED": p['start_ts'].append(int(event.timestamp)) p['types'].append(colors.get_color_id("rpm_%s"%(event.status.lower()))) p['cpus'].append(event.common_cpu) @staticmethod def do_event_runtime_pm_usage(proj, event): p = proj.generic_find_process(0,"runtime_pm_usage:%s %s"%(event.driver,event.dev),"runtime_pm") if len(p['start_ts'])>len(p['end_ts']): p['end_ts'].append(event.timestamp) if event.usage!=0: p['start_ts'].append(int(event.timestamp)) usagecolor = event.usage if usagecolor<0: usagecolor = -1 if usagecolor>6: usagecolor = 6 p['types'].append(colors.get_color_id("rpm_usage=%d"%(usagecolor))) p['cpus'].append(event.common_cpu) plugin_register(runtime_pm) pytimechart-1.0.0.rc1/timechart/plugins/wake_lock.py0000644000175100017510000000212311567523405022076 0ustar pierre00000000000000from timechart.plugin import * from timechart import colors from timechart.model import tcProcess class wake_lock(plugin): additional_colors = """ wakelock_bg #D6F09D """ additional_ftrace_parsers = [ ('wakelock_lock', 'name=%s type=%d', 'name', 'type'), ('wakelock_unlock', 'name=%s', 'name'), ] additional_process_types = { "wakelock":(tcProcess, POWER_CLASS), } @staticmethod def do_event_wakelock_lock(proj,event): process = proj.generic_find_process(0,"wakelock:%s"%(event.name),"wakelock") proj.generic_process_start(process,event,False) proj.wake_events.append(((event.common_comm,event.common_pid),(process['comm'],process['pid']),event.timestamp)) @staticmethod def do_event_wakelock_unlock(proj,event): process = proj.generic_find_process(0,"wakelock:%s"%(event.name),"wakelock") proj.generic_process_end(process,event,False) proj.wake_events.append(((event.common_comm,event.common_pid),(process['comm'],process['pid']),event.timestamp)) plugin_register(wake_lock) pytimechart-1.0.0.rc1/timechart/plugins/menu_select.py0000644000175100017510000000412111567523405022442 0ustar pierre00000000000000from timechart.plugin import * from timechart import colors from timechart.model import tcProcess class menu_select(plugin): additional_colors = """ menu_select_bg #e5bebe menu_select #ee0000 """ additional_ftrace_parsers = [ ( 'menu_select', 'expected:%d predicted %d state:%s %d','expected','predicted','next_state','num_state'), ] additional_process_types = { "menu_select":(tcProcess, POWER_CLASS), } @staticmethod def do_event_menu_select(proj,event): try: a= event.predicted except AttributeError: return found = 0 i = 0 while not found: p = proj.generic_find_process(0,"menu_select_cpu%d_%d_predicted"%(event.common_cpu,i),"menu_select") p2 = proj.generic_find_process(0,"menu_select_cpu%d_%d_expected"%(event.common_cpu,i),"menu_select") if len(p['end_ts'])>0 and len(p2['end_ts'])>0 and (p['end_ts'][-1]>event.timestamp or p2['end_ts'][-1]>event.timestamp): i+=1 continue found = 1 p['start_ts'].append(int(event.timestamp)) p['end_ts'].append(int(event.timestamp+event.predicted)) p['types'].append(colors.get_color_id("menu_select")) p['cpus'].append(event.common_cpu) p = p2 p['start_ts'].append(int(event.timestamp)) p['end_ts'].append(int(event.timestamp+event.expected)) p['types'].append(colors.get_color_id("menu_select")) p['cpus'].append(event.common_cpu) plugin_register(menu_select) menu_select_patch=""" diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 1b12870..4b18893 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -292,6 +292,7 @@ static int menu_select(struct cpuidle_device *dev) data->exit_us = s->exit_latency; data->last_state_idx = i; } + trace_printk("expected:%u predicted %llu state:%s %d\n",data->expected_us, data->predicted_us, dev->states[data->last_state_idx].name, dev->state_count); return data->last_state_idx; } """ pytimechart-1.0.0.rc1/timechart/__init__.py0000755000175100017510000000012311567523405020216 0ustar pierre00000000000000try: __import__('pkg_resources').declare_namespace(__name__) except: pass pytimechart-1.0.0.rc1/timechart/py2exe_wximports.py0000644000175100017510000000225011567523405022027 0ustar pierre00000000000000# for py2exe. try: import enthought.pyface.ui.wx.about_dialog import enthought.pyface.ui.wx.application_window import enthought.pyface.ui.wx.clipboard import enthought.pyface.ui.wx.confirmation_dialog import enthought.pyface.ui.wx.dialog import enthought.pyface.ui.wx.directory_dialog import enthought.pyface.ui.wx.file_dialog import enthought.pyface.ui.wx.gui import enthought.pyface.ui.wx.heading_text import enthought.pyface.ui.wx.image_cache import enthought.pyface.ui.wx.image_resource import enthought.pyface.ui.wx.__init__ import enthought.pyface.ui.wx.init import enthought.pyface.ui.wx.ipython_widget import enthought.pyface.ui.wx.message_dialog import enthought.pyface.ui.wx.progress_dialog import enthought.pyface.ui.wx.python_editor import enthought.pyface.ui.wx.python_shell import enthought.pyface.ui.wx.resource_manager import enthought.pyface.ui.wx.splash_screen import enthought.pyface.ui.wx.split_widget import enthought.pyface.ui.wx.system_metrics import enthought.pyface.ui.wx.widget import enthought.pyface.ui.wx.window import enthought.pyface.ui.wx.window except: pass pytimechart-1.0.0.rc1/timechart/tools.py0000644000175100017510000000426411567523405017626 0ustar pierre00000000000000from enthought.chaco.tools.api import PanTool, ZoomTool, RangeSelection, PanTool class myZoomTool(ZoomTool): """ a zoom tool which change y range only when control is pressed it also hande some page up page down to zoom via keyboard """ def normal_mouse_wheel(self, event): if event.control_down: self.tool_mode = "box" else: self.tool_mode = "range" super(myZoomTool, self).normal_mouse_wheel(event) # restore default zoom mode if event.control_down: self.tool_mode = "range" def normal_key_pressed(self, event): super(myZoomTool, self).normal_key_pressed(event) print event class fake_event: pass my_fake_event = fake_event() c = self.component my_fake_event.x = event.x#(c.x+c.x2)/2 my_fake_event.y = event.x#(c.y+c.y2)/2 my_fake_event.control_down = event.control_down my_fake_event.mouse_wheel = 0 if event.character == 'Page Up': my_fake_event.mouse_wheel = 1 if event.character == 'Page Down': my_fake_event.mouse_wheel = -1 if event.shift_down: my_fake_event.mouse_wheel*=10 if event.alt_down: my_fake_event.mouse_wheel*=2 if my_fake_event.mouse_wheel: self.normal_mouse_wheel(my_fake_event) # left down conflicts with the panning tool # just overide and disable change state to moving # change the moving binding to middle click class myRangeSelection(RangeSelection): def selected_left_down(self, event): RangeSelection.selected_left_down(self,event) if self.event_state == "moving": self.event_state = "selected" def selected_middle_down(self, event): RangeSelection.selected_left_down(self,event) def moving_middle_up(self, event): RangeSelection.moving_left_up(self,event) def selecting_middle_up(self, event): RangeSelection.selected_left_up(self,event) # immediatly refresh the plot for better fluidity class myPanTool(PanTool): def panning_mouse_move(self,event): PanTool.panning_mouse_move(self,event) self.component.immediate_invalidate() pytimechart-1.0.0.rc1/timechart/colors.py0000644000175100017510000000475211646053135017765 0ustar pierre00000000000000from enthought.traits.api import Color from enthought.enable.colors import ColorTrait from enthought.kiva.agg import Rgba # hint: use gcolor2 to pick up colors # if you omit several color, it will automatically gradiant... _tc_colors_txt = """ idle_bg #ffdddd irq_bg #f5ffe1 softirq_bg work_bg function_bg #80ff80 event_bg kernel_process_bg #F0F5A3 user_process_bg #E1DFFF selected_bg #ACD7E6 idle #000000 waiting_for_cpu #ffff88 running #555555 overview #333333 shown_process #111111 hidden_process #777777 """ _tc_colors_by_name = {} _tc_colorname_by_id = [] _tc_colors_by_id = [] _tc_aggcolors_by_id = [] def _to_traits(color): r = int(color[1:3],16)/256. g = int(color[3:5],16)/256. b = int(color[5:7],16)/256. return r,g,b def add_color(colorname, color): _tc_colors_by_name[colorname] = (color,len(_tc_colors_by_id)) _tc_colorname_by_id.append(colorname) _tc_colors_by_id.append(color) _tc_aggcolors_by_id.append(Rgba(_to_traits(color))) def parse_colors(color_text): # first parse the syntax sugared color definition table and change it to dictionary pending_colors = [] last_color=0,0,0 for line in color_text.split("\n"): line = line.split() if len(line)==2: colorname, color = line if pending_colors: r1,g1,b1 = last_color r2,g2,b2 = _to_traits(color) n = len(pending_colors)+2 for i in xrange(1,n-1): r = r1+(r2-r1)*i/n g = g1+(g2-g1)*i/n b = b1+(b2-b1)*i/n add_color(pending_colors[i-1], "#%02X%02X%02X"%(255*r,255*g,255*b)) pending_colors = [] add_color(colorname,color) last_color = _to_traits(color) elif len(line)==1: pending_colors.append(line[0]) parse_colors(_tc_colors_txt) def get_colorname_by_id(i): return _tc_colorname_by_id[i] def get_color_by_name(name): return _tc_colors_by_name[name][0] def get_color_id(name): return _tc_colors_by_name[name][1] def get_traits_color_by_name(name): return _to_traits(_tc_colors_by_name[name][0]) def get_color_by_id(i): return _tc_colors_by_id[i] def get_traits_color_by_id(i): return _to_traits(_tc_colors_by_id[i]) def get_aggcolor_by_id(i): return _tc_aggcolors_by_id[i] if __name__ == '__main__': print get_traits_color_by_name("running"),get_traits_color_by_id(get_color_id("running")) pytimechart-1.0.0.rc1/timechart/timechart.py0000755000175100017510000000641111646055677020456 0ustar pierre00000000000000#!/usr/bin/python #------------------------------------------------------------------------------ import sys try: from enthought.etsconfig.api import ETSConfig except: print >>sys.stderr, "did you install python-chaco?" print >>sys.stderr, "maybe you did install chaco>=4, then you will need to install the package etsproxy" print >>sys.stderr, "sudo easy_install etsproxy" sys.exit(1) # select the toolkit we want to use # WX is more stable for now #ETSConfig.toolkit = 'qt4' ETSConfig.toolkit = 'wx' # workaround bad bg color in ubuntu, with Ambiance theme # wxgtk (or traitsGUI, I dont know) looks like using the menu's bgcolor # for all custom widgets bg colors. :-( if ETSConfig.toolkit == 'wx': import wx, os if "gtk2" in wx.PlatformInfo: from gtk import rc_parse, MenuBar m = MenuBar() if m.rc_get_style().bg[0].red_float < 0.5: # only customize dark bg rc_parse(os.path.join(os.path.dirname(__file__),"images/gtkrc")) m.destroy() # workaround bug in kiva's font manager that fails to find a correct default font on linux if os.name=="posix": import warnings def devnull(*args): pass warnings.showwarning = devnull from enthought.kiva.fonttools.font_manager import fontManager, FontProperties try: font = FontProperties() font.set_name("DejaVu Sans") fontManager.defaultFont = fontManager.findfont(font) fontManager.warnings = None except: # this code will throw exception on ETS4, which has actually fixed fontmanager pass from enthought.pyface.api import GUI from window import open_file def main(): import optparse parser = optparse.OptionParser(usage="""\ %prog [options] [trace.txt|trace.txt.gz|trace.txt.lzma|trace.dat] pytimechart - Fast graphical exploration and visualisation for linux kernel traces.""") parser.add_option("-p", "--prof", dest="prof", action="store_true", help="activate profiling", default=False) (options, args) = parser.parse_args() # Create the GUI (this does NOT start the GUI event loop). gui = GUI() if len(args) == 0: args.append("dummy") for fn in args: if not open_file(fn): sys.exit(0) if options.prof: import cProfile dict = {"gui":gui} cProfile.runctx('gui.start_event_loop()',dict,dict,'timechart.prof') else: gui.start_event_loop() # used for profiling, and regression tests def just_open(): import optparse parser = optparse.OptionParser(usage="""\ %prog [options] [trace.txt|trace.txt.gz|trace.txt.lzma|trace.dat] pytimechart_parse_test - just test parsing backend...""") parser.add_option("-p", "--prof", dest="prof", action="store_true", help="activate profiling", default=False) (options, args) = parser.parse_args() if len(args) > 0: fn = args[0] else: return if options.prof: import cProfile dict = {"open_file":open_file,"fn":fn} cProfile.runctx('open_file(fn)',dict,dict,'timechart.prof') else: open_file(fn) if __name__ == '__main__': main() import py2exe_wximports ##### EOF ##################################################################### pytimechart-1.0.0.rc1/timechart/actions.py0000644000175100017510000001307611631370253020120 0ustar pierre00000000000000from enthought.traits.ui.menu import Action, Menu, Separator from enthought.traits.ui.api import Handler from enthought.pyface.image_resource import ImageResource actions_doc = None def _buildAction(desc): global actions_doc from window import tcActionHandler if len(desc) == 0: return Separator() exec("tcActionHandler.%s = lambda self,i:self.chooseAction(i,'_on_%s')"%(desc["name"],desc["name"])) style = desc["name"].startswith("toggle") and "toggle" or "push" default = False if "default" in desc: default = desc["default"] desc["tooltip"] = desc["tooltip"].strip() action = Action(name=desc["name"].replace("_"," "), action=desc["name"], tooltip=desc["tooltip"], image=ImageResource(desc["name"]), style=style, checked=default) tcActionHandler.actions[desc["name"]] = action if not actions_doc is None: actions_doc += "\n**%s**:\n"%(desc["name"].replace("_"," ").strip()) actions_doc += "\n.. image:: images/%s.png\n\n"%(desc["name"]) actions_doc += desc["tooltip"] +"\n" return action # this is a bit ugly, but still better than duplicating the documentation in .rst and tooltips def _create_toolbar_actions(): actions = ( {}, {"name": "invert","tooltip":"""Invert processes show/hide value. This is useful, when you are fully zoomed, and you want to see if you are not missing some valuable info in the hidden processes"""}, {"name": "select_all","tooltip":"""Select/Unselect all process visible in the process list Thus processes that are filtered are always unselected by this command"""}, {}, {"name": "show","tooltip":"""Show selected processes in the timechart"""}, {"name": "hide","tooltip":"""Hide selected processes in the timechart"""}, {}, {"name": "hide_others","tooltip": """Hide process that are not shown at current zoom window All processes that *are not* currently visible in the timechart will be hidden This is useful when you zoom at a particular activity, and you want to unzoom without being noised by other activities.""" }, {"name": "hide_onscreen","tooltip": """Hide process that are shown at current zoom window All processes that *are* currently visible in the timechart will be hidden This is useful when you zoom at a particular noise activity, and you want to unzoom without being annoyed by this activity.""" }, {}, {"name": "toggle_autohide","tooltip":"""*autoHide* processes that do not have any events in the current zooming window If this option is disabled, and a process does not have any activity in the current view, this will show an empty line, and eat vertical space for not much meaning. This is why it is recommanded to leave this setting on. """, "default":True}, {"name": "toggle_auto_zoom_y","tooltip":"""Automatically set the y scale to fit the number of process shown This make sure all the process that are not hidden fit the window vertically. Disable this feature if you want to manually zoom though the y-axis with the *CTRL+mouse wheel* command. """, "default":True}, {}, {"name": "toggle_wakes","tooltip":"""Show/Hide the wake_events. Wake events are generated by the "sched_wakeup" trace_event. wake events are represented by a row connecting a process to another. Reason of wakes can be for example: * kernel driver waking up process because IO is available. * user thread releasing mutex on which another thread was waiting for. Disabled by default because this slows down a lot graphics, and adds a lot of noise when fully unzoomed""" }, {"name": "toggle_cpufreq","tooltip":'Show/Hide the cpufreq representation.', "default":True}, {"name": "toggle_cpuidle","tooltip":'Show/Hide the cpuidle representation.', "default":True}, # only for debugging overview # {"name": "toggle_overview","tooltip":'This will accelerate plotting by merging contiguous events when zoomed out.', "default":True}, {}, {"name": "trace_text","tooltip":"""Shows the text trace of the selection Sometimes, looking at the textual trace is more precise than just looking at the timechart. Moreover, all the informations of the trace is not represented by the timechart. You can also save some part of a trace to another file with this option """ }, {"name": "zoom","tooltip":"""Zoom so that the selection fits the window"""}, {"name": "unzoom","tooltip":'Unzoom to show the whole trace'}, ) ret = [] for i in actions: ret.append(_buildAction(i)) return tuple(ret) def _create_menubar_actions(): desc = (('&File', ( {"name": "open_trace_file","tooltip":'open new file into pytimechart'}, {"name": "exit","tooltip":'exit pytimechart'})), ('&Help', ( {"name": "about","tooltip":'about'},{"name": "doc","tooltip":'doc'}))) ret = [] for menu in desc: actions = [] for action in menu[1]: actions.append(_buildAction(action)) ret.append(Menu(*tuple(actions), name = menu[0])) return tuple(ret) if __name__ == '__main__': actions_doc = "" _create_toolbar_actions() wholedoc = open("docs/sources/userguide.rst").read() start = ".. start_automatically_generated_from_tooltips\n" end = ".. end_automatically_generated_from_tooltips\n" wholedoc = wholedoc[0:wholedoc.find(start)+len(start)] + actions_doc + wholedoc[wholedoc.find(end)-1:] f = open("docs/sources/userguide.rst","w") f.write(wholedoc) f.close() pytimechart-1.0.0.rc1/timechart/model.py0000644000175100017510000004717511645777530017605 0ustar pierre00000000000000# timechart project # the timechart model with all loading facilities from numpy import amin, amax, arange, searchsorted, sin, pi, linspace import numpy as np import traceback import re from enthought.traits.api import HasTraits, Instance, Str, Float,Delegate,\ DelegatesTo, Int, Long, Enum, Color, List, Bool, CArray, Property, cached_property, String, Button, Dict from enthought.traits.ui.api import Group, HGroup, Item, View, spring, Handler,VGroup,TableEditor from enthought.enable.colors import ColorTrait from enthought.pyface.image_resource import ImageResource from enthought.pyface.api import ProgressDialog from process_table import process_table_editor import colors import numpy import sys def _pretty_time(time): if time > 1000000: time = time/1000000. return "%.1f s"%(time) if time > 1000: time = time/1000. return "%.1f ms"%(time) return "%.1f us"%(time) class tcGeneric(HasTraits): name = String start_ts = CArray end_ts = CArray types = CArray has_comments = Bool(True) total_time = Property(Int) max_types = Property(Int) max_latency = Property(Int) max_latency_ts = Property(CArray) overview_ts_cache = Dict({}) @cached_property def _get_total_time(self): return sum(self.end_ts-self.start_ts) @cached_property def _get_max_types(self): return amax(self.types) @cached_property def _get_max_latency(self): return -1 def get_partial_tables(self,start,end): low_i = searchsorted(self.end_ts,start) high_i = searchsorted(self.start_ts,end) ends = self.end_ts[low_i:high_i].copy() starts = self.start_ts[low_i:high_i].copy() if len(starts)==0: return np.array([]),np.array([]),[] # take care of activities crossing the selection if starts[0]end: ends[-1] = end types = self.types[low_i:high_i] return starts,ends,types def get_overview_ts(self, threshold): """merge events so that there never are two events in the same "threshold" microsecond """ if threshold in self.overview_ts_cache: return self.overview_ts_cache[threshold] # we recursively use the lower threshold caches # this allows to pre-compute the whole cache more efficiently if threshold > 4: origin_start_ts, origin_end_ts = self.get_overview_ts(threshold/2) else: origin_start_ts, origin_end_ts = self.start_ts, self.end_ts # only calculate overview if it worth. if len(origin_start_ts) < 500: overview = (origin_start_ts, origin_end_ts) self.overview_ts_cache[threshold] = overview return overview # assume at least one event start_ts = [] end_ts = [] # start is the first start of the merge list start = origin_start_ts[0] i = 1 while i < len(origin_start_ts): if origin_start_ts[i] > origin_start_ts[i-1] + threshold: start_ts.append(start) end_ts.append(origin_end_ts[i-1]) start = origin_start_ts[i] i += 1 start_ts.append(start) end_ts.append(origin_end_ts[i-1]) overview = (numpy.array(start_ts), numpy.array(end_ts)) self.overview_ts_cache[threshold] = overview return overview # UI traits default_bg_color = Property(ColorTrait) bg_color = Property(ColorTrait) @cached_property def _get_bg_color(self): return colors.get_traits_color_by_name("idle_bg") class tcIdleState(tcGeneric): def get_comment(self,i): return colors.get_colorname_by_id(self.types[i]) class tcFrequencyState(tcGeneric): def get_comment(self,i): return "%d"%(self.types[i]) class tcProcess(tcGeneric): name = Property(String) # overide TimeChart # start_ts=CArray # inherited from TimeChart # end_ts=CArray # inherited from TimeChart # values = CArray # inherited from TimeChart pid = Long ppid = Long selection_time = Long(0) selection_pc = Float(0) comm = String cpus = CArray comments = [] has_comments = Bool(True) show = Bool(True) process_type = String project = None @cached_property def _get_name(self): return "%s:%d (%s)"%(self.comm,self.pid, _pretty_time(self.total_time)) def get_comment(self,i): if len(self.comments)>i: return "%s"%(self.comments[int(i)]) elif len(self.cpus)>i: return "%d"%(self.cpus[i]) else: return "" @cached_property def _get_max_latency(self): if self.pid==0 and self.comm.startswith("irq"): return 1000 @cached_property def _get_max_latency_ts(self): if self.max_latency > 0: indices = np.nonzero((self.end_ts - self.start_ts) > self.max_latency)[0] return np.array(sorted(map(lambda i:self.start_ts[i], indices))) return [] @cached_property def _get_default_bg_color(self): if self.max_latency >0 and max(self.end_ts - self.start_ts)>self.max_latency: return (1,.1,.1,1) return colors.get_traits_color_by_name(self.process_type+"_bg") def _get_bg_color(self): if self.project != None and self in self.project.selected: return colors.get_traits_color_by_name("selected_bg") return self.default_bg_color class tcProject(HasTraits): c_states = List(tcGeneric) p_states = List(tcGeneric) processes = List(tcProcess) selected = List(tcProcess) filtered_processes = List(tcProcess) remove_filter = Button(image=ImageResource("clear.png"),width_padding=0,height_padding=0,style='toolbar') minimum_time_filter = Enum((0,1000,10000,50000,100000,500000,1000000,5000000,1000000,5000000,10000000,50000000)) minimum_events_filter = Enum((0,2,4,8,10,20,40,100,1000,10000,100000,1000000)) plot_redraw = Long() filter = Str("") filter_invalid = Property(depends_on="filter") filename = Str("") power_event = CArray num_cpu = Property(Int,depends_on='c_states') num_process = Property(Int,depends_on='process') traits_view = View( VGroup( HGroup( Item('filter',invalid="filter_invalid",width=1, tooltip='filter the process list using a regular expression,\nallowing you to quickly find a process'), Item('remove_filter', show_label=False, style='custom', tooltip='clear the filter') ), HGroup( Item('minimum_time_filter',width=1,label='dur', tooltip='filter the process list with minimum duration process is scheduled'), Item('minimum_events_filter',width=1,label='num', tooltip='filter the process list with minimum number of events process is generating'), ) ), Item( 'filtered_processes', show_label = False, height=40, editor = process_table_editor ) ) first_ts = 0 def _get_filter_invalid(self): try: r = re.compile(self.filter) except: return True return False def _remove_filter_changed(self): self.filter="" def _filter_changed(self): try: r = re.compile(self.filter) except: r = None filtered_processes =self.processes if self.minimum_events_filter: filtered_processes = filter(lambda p:self.minimum_events_filter < len(p.start_ts), filtered_processes) if self.minimum_time_filter: filtered_processes = filter(lambda p:self.minimum_time_filter < p.total_time, filtered_processes) if r: filtered_processes = filter(lambda p:r.search(p.comm), filtered_processes) self.filtered_processes = filtered_processes _minimum_time_filter_changed = _filter_changed _minimum_events_filter_changed = _filter_changed def _processes_changed(self): self._filter_changed() def _on_show(self): for i in self.selected: i.show = True self.plot_redraw +=1 def _on_hide(self): for i in self.selected: i.show = False self.plot_redraw +=1 def _on_select_all(self): if self.selected == self.filtered_processes: self.selected = [] else: self.selected = self.filtered_processes self.plot_redraw +=1 def _on_invert(self): for i in self.filtered_processes: i.show = not i.show self.plot_redraw +=1 @cached_property def _get_num_cpu(self): return len(self.c_states) def _get_num_process(self): return len(self.processes) def process_list_selected(self, selection): print selection ######### stats part ########## def process_stats(self,start,end): fact = 100./(end-start) for tc in self.processes: starts,ends,types = tc.get_partial_tables(start,end) inds = np.where(types==colors.get_color_id("running")) tot = sum(ends[inds]-starts[inds]) tc.selection_time = int(tot) tc.selection_pc = tot*fact def get_selection_text(self,start,end): low_line = -1 high_line = -1 low_i = searchsorted(self.timestamps,start) high_i = searchsorted(self.timestamps,end) low_line = self.linenumbers[low_i] high_line = self.linenumbers[high_i] return self.get_partial_text(self.filename, low_line, high_line) ######### generic parsing part ########## def generic_find_process(self,pid,comm,ptype,same_pid_match_timestamp=0): if self.tmp_process.has_key((pid,comm)): return self.tmp_process[(pid,comm)] # else try to find if there has been a process with same pid recently, and different name if same_pid_match_timestamp != 0 and comm != "swapper": for k, p in self.tmp_process.items(): if k[0] == pid: if len(p['start_ts'])>0 and p['start_ts'][-1] > same_pid_match_timestamp: p['comm'] = comm self.tmp_process[(pid,comm)] = p del self.tmp_process[k] return p tmp = {'type':ptype,'comm':comm,'pid':pid,'start_ts':[],'end_ts':[],'types':[],'cpus':[],'comments':[]} if not (pid==0 and comm =="swapper"): self.tmp_process[(pid,comm)] = tmp return tmp def generic_process_start(self,process,event, build_p_stack=True): if process['comm']=='swapper' and process['pid']==0: return # ignore swapper event if len(process['start_ts'])>len(process['end_ts']): process['end_ts'].append(event.timestamp) if self.first_ts == 0: self.first_ts = event.timestamp self.cur_process_by_pid[process['pid']] = process if build_p_stack : p_stack = self.cur_process[event.common_cpu] if p_stack: p = p_stack[-1] if len(p['start_ts'])>len(p['end_ts']): p['end_ts'].append(event.timestamp) # mark old process to wait for cpu p['start_ts'].append(int(event.timestamp)) p['types'].append(colors.get_color_id("waiting_for_cpu")) p['cpus'].append(event.common_cpu) p_stack.append(process) else: self.cur_process[event.common_cpu] = [process] # mark process to use cpu process['start_ts'].append(event.timestamp) process['types'].append(colors.get_color_id("running")) process['cpus'].append(event.common_cpu) def generic_process_end(self,process,event, build_p_stack=True): if process['comm']=='swapper' and process['pid']==0: return # ignore swapper event if len(process['start_ts'])>len(process['end_ts']): process['end_ts'].append(event.timestamp) if build_p_stack : p_stack = self.cur_process[event.common_cpu] if p_stack: p = p_stack.pop() if p['pid'] != process['pid']: print "warning: process premption stack following failure on CPU",event.common_cpu, p['comm'],p['pid'],process['comm'],process['pid'],map(lambda a:"%s:%d"%(a['comm'],a['pid']),p_stack),event.linenumber p_stack = [] if p_stack: p = p_stack[-1] if len(p['start_ts'])>len(p['end_ts']): p['end_ts'].append(event.timestamp) # mark old process to run on cpu p['start_ts'].append(event.timestamp) p['types'].append(colors.get_color_id("running")) p['cpus'].append(event.common_cpu) def generic_process_single_event(self,process,event): if len(process['start_ts'])>len(process['end_ts']): process['end_ts'].append(event.timestamp) # mark process to use cpu process['start_ts'].append(event.timestamp) process['types'].append(colors.get_color_id("running")) process['cpus'].append(event.common_cpu) process['end_ts'].append(event.timestamp) def do_function_default(self,event): process = self.generic_find_process(0,"kernel function:%s"%(event.callee),"function") self.generic_process_single_event(process,event) def do_event_default(self,event): event.name = event.event.split(":")[0] process = self.generic_find_process(0,"event:%s"%(event.name),"event") self.generic_process_single_event(process,event) process['comments'].append(event.event) def start_parsing(self, get_partial_text): # we build our data into python data formats, who are resizeable # once everything is parsed, we will transform it into numpy array, for fast access self.tmp_c_states = [] self.tmp_p_states = [] self.tmp_process = {} self.timestamps = [] self.linenumbers = [] self.cur_process_by_pid = {} self.wake_events = [] self.cur_process = [None]*20 self.last_irq={} self.last_spi=[] self.missed_power_end = 0 self.get_partial_text = get_partial_text self.methods = {} import plugin colors.parse_colors(plugin.get_plugins_additional_colors()) plugin.get_plugins_methods(self.methods) self.process_types = { "function":(tcProcess, plugin.MISC_TRACES_CLASS), "event":(tcProcess, plugin.MISC_TRACES_CLASS)} self.process_types.update(plugin.get_plugins_additional_process_types()) def finish_parsing(self): #put generated data in unresizable numpy format c_states = [] i=0 for tc in self.tmp_c_states: t = tcIdleState(name='cpu%d'%(i)) while len(tc['start_ts'])>len(tc['end_ts']): tc['end_ts'].append(tc['start_ts'][-1]) t.start_ts = numpy.array(tc['start_ts']) t.end_ts = numpy.array(tc['end_ts']) t.types = numpy.array(tc['types']) c_states.append(t) i+=1 self.c_states=c_states i=0 p_states = [] for tc in self.tmp_p_states: t = tcFrequencyState(name='cpu%d'%(i)) t.start_ts = numpy.array(tc['start_ts']) t.end_ts = numpy.array(tc['end_ts']) t.types = numpy.array(tc['types']) i+=1 p_states.append(t) self.wake_events = numpy.array(self.wake_events,dtype=[('waker',tuple),('wakee',tuple),('time','uint64')]) self.p_states=p_states processes = [] last_ts = 0 for pid,comm in self.tmp_process: tc = self.tmp_process[pid,comm] if len(tc['end_ts'])>0 and last_ts < tc['end_ts'][-1]: last_ts = tc['end_ts'][-1] if len(self.tmp_process) >0: progress = ProgressDialog(title="precomputing data", message="precomputing overview data...", max=len(self.tmp_process), show_time=False, can_cancel=False) progress.open() i = 0 for pid,comm in self.tmp_process: tc = self.tmp_process[pid,comm] if self.process_types.has_key(tc['type']): klass, order = self.process_types[tc['type']] t = klass(pid=pid,comm=tc['comm'],project=self) else: t = tcProcess(pid=pid,comm=comm,project=self) while len(tc['start_ts'])>len(tc['end_ts']): tc['end_ts'].append(last_ts) t.start_ts = numpy.array(tc['start_ts']) t.end_ts = numpy.array(tc['end_ts']) t.types = numpy.array(tc['types']) t.cpus = numpy.array(tc['cpus']) t.comments = tc['comments'] #numpy.array(tc['comments']) t.process_type = tc["type"] # precompute 16 levels of overview cache t.get_overview_ts(1<<16) processes.append(t) progress.update(i) i += 1 if len(self.tmp_process) > 0: progress.close() self.tmp_process = [] def cmp_process(x,y): # sort process by type, pid, comm def type_index(t): try: return self.process_types[t][1] except ValueError: return len(order)+1 c = cmp(type_index(x.process_type),type_index(y.process_type)) if c != 0: return c c = cmp(x.pid,y.pid) if c != 0: return c c = cmp(x.comm,y.comm) return c processes.sort(cmp_process) self.processes = processes self.p_states=p_states self.tmp_c_states = [] self.tmp_p_states = [] self.tmp_process = {} def ensure_cpu_allocated(self,cpu): # ensure we have enough per_cpu p/c_states timecharts while len(self.tmp_c_states)<=cpu: self.tmp_c_states.append({'start_ts':[],'end_ts':[],'types':[]}) while len(self.tmp_p_states)<=cpu: self.tmp_p_states.append({'start_ts':[],'end_ts':[],'types':[]}) def run_callbacks(self, callback, event): if callback in self.methods: for m in self.methods[callback]: try: m(self,event) except AttributeError: if not hasattr(m,"num_exc"): m.num_exc = 0 m.num_exc += 1 if m.num_exc <10: print "bug in ", m, "still continue.." traceback.print_exc() print event if m.num_exc == 10: print m, "is too buggy, disabling, please report bug!" self.methods[callback].remove(m) if len(self.methods[callback])==0: del self.methods[callback] return True return False def handle_trace_event(self,event): self.linenumbers.append(event.linenumber) self.timestamps.append(event.timestamp) if event.event=='function': callback = "do_function_"+event.callee self.run_callbacks("do_all_functions", event) else: callback = "do_event_"+event.event self.run_callbacks("do_all_events", event) if not self.run_callbacks(callback, event): if event.event=='function': self.do_function_default(event) else: self.do_event_default(event) pytimechart-1.0.0.rc1/timechart/process_table.py0000644000175100017510000000255511567523405021314 0ustar pierre00000000000000#import timechart.colors as colors import colors from enthought.traits.ui.table_column import ObjectColumn, ExpressionColumn from enthought.traits.ui.api import TableEditor # we subclass ObjectColumn to be able to change the text color depending of whether the Process is shown class coloredObjectColumn(ObjectColumn): def get_text_color(self,i): if i.show: return colors.get_color_by_name("shown_process") else: return colors.get_color_by_name("hidden_process") def get_cell_color(self,i): return colors.get_color_by_name(i.process_type+"_bg") # The definition of the process TableEditor: process_table_editor = TableEditor( columns = [ coloredObjectColumn( name = 'comm', width = 0.45 ,editable=False), coloredObjectColumn( name = 'pid', width = 0.10 ,editable=False), coloredObjectColumn( name = 'selection_time',label="stime", width = 0.20 ,editable=False), ExpressionColumn( label = 'stime%', width = 0.20, expression = "'%.2f' % (object.selection_pc)" ) ], deletable = False, editable = False, sort_model = False, auto_size = False, orientation = 'vertical', show_toolbar = False, selection_mode = 'rows', selected = "selected" ) pytimechart-1.0.0.rc1/timechart/plugin.py0000644000175100017510000000233511567523405017761 0ustar pierre00000000000000POWER_CLASS=0 IRQ_CLASS=1 WORK_CLASS=2 MISC_TRACES_CLASS=3 KERNEL_CLASS=4 USER_CLASS=5 plugin_list = [] class plugin: additional_colors = "" additional_ftrace_parsers = [] additional_process_types = [] def plugin_register(plugin_class): plugin_list.append(plugin_class) def get_plugins_methods(methods): for p in plugin_list: for name in dir(p): method = getattr(p, name) if callable(method): if not name in methods: methods[name] = [] methods[name].append(method) def get_plugins_additional_process_types(): s = {} for p in plugin_list: s.update(p.additional_process_types) return s def get_plugins_additional_colors(): s = "" for p in plugin_list: s += p.additional_colors return s def get_plugins_additional_ftrace_parsers(): s = [] for p in plugin_list: s += p.additional_ftrace_parsers return s import plugins import os for f in os.listdir(os.path.abspath(plugins.__path__[0])): module_name, ext = os.path.splitext(f) if (not module_name.startswith(".")) and ext == '.py' and module_name != "__init__": module = __import__("timechart.plugins."+module_name) pytimechart-1.0.0.rc1/setup.py0000755000175100017510000000563611646045040015645 0ustar pierre00000000000000#!/usr/bin/env python # Installs pyTimechart using setuptools # Run: # python setup.py install # to install the package from the source archive. import os, sys from setuptools import setup, find_packages # get version from source code version = [ (line.split('=')[1]).strip().strip('"').strip("'") for line in open(os.path.join('timechart', 'window.py')) if line.startswith( '__version__' ) ][0] # get descriptions from documentation DOCLINES = {"":[]} current_part = "" for line in open(os.path.join('docs',os.path.join('sources', 'index.rst'))): if line.startswith(".. DESC"): current_part = line[7:].strip() DOCLINES[current_part] = [] else: DOCLINES[current_part].append(line.strip()) if __name__ == "__main__": # docs are only supposed to be generated by a few, so dont make it a hard dependancy if "build_sphinx" in sys.argv or "upload_sphinx" in sys.argv: extraArguments = {'setup_requires' : 'sphinx-pypi-upload>=0.2'} else: extraArguments = {} ### Now the actual set up call setup ( name = DOCLINES["title"][1], classifiers = [ c.strip() for c in """\ License :: OSI Approved :: BSD License Programming Language :: Python Topic :: Software Development :: Libraries :: Python Modules Operating System :: Microsoft :: Windows Operating System :: OS Independent Operating System :: POSIX Operating System :: Unix Intended Audience :: Developers """.splitlines() if len(c.strip()) > 0], keywords = 'gui,ftrace,perf,trace-event', version = version, url = "http://gitorious.org/pytimechart", download_url = "http://gitorious.org/pytimechart", description = DOCLINES["shortdesc"][1], long_description = '\n'.join(DOCLINES["longdesc"][1:]), author = "Pierre Tardy", author_email = "tardyp@gmail.com", install_requires = [ 'Chaco >= 3.0', # you should install that via distro rather than pypi.. # 'pyliblzma >= 0.5' # not really mandatory ], license = "BSD", platforms = ["Windows", "Linux", "Mac OS-X", # actually did not manage to make it work on osx because of Traits.. "Unix", "Solaris"], namespace_packages = [ 'timechart', 'timechart.plugins', 'timechart.backends', ], packages = find_packages(exclude = [ 'examples', ]), package_data = { '': ['images/*'], }, include_package_data = True, options = { 'sdist':{ 'force_manifest':1, 'formats':['gztar','zip'],}, }, zip_safe=False, entry_points = { 'gui_scripts': [ 'pytimechart=timechart.timechart:main', ], }, **extraArguments ) pytimechart-1.0.0.rc1/pytimechart-record0000644000175100017510000000217011567526761017666 0ustar pierre00000000000000#!/bin/sh if [ `whoami` != root ] then echo you need to run this script as root exit 1 fi mount -t debugfs none /sys/kernel/debug 2>/dev/null cd /sys/kernel/debug/tracing start() { echo 50000 > buffer_size_kb echo nop > current_tracer # exemple for also tracing all function starting with hsi: # echo function > current_tracer # echo hsi* >> set_ftrace_filter echo > set_event ( while read i do echo $i >> set_event done ) <trace echo 1 >tracing_enabled } stop() { echo >set_event echo 0 >tracing_enabled output=~/trace`date +%y-%m-%d-%H-%M-%S`.txt.lzma cat trace | lzma > $output echo trace written to $output } COMMAND="$1" case $COMMAND in start|stop) $COMMAND ;; *) echo "usage: $0 [start|stop]" ;; esac pytimechart-1.0.0.rc1/pytimechart.desktop0000644000175100017510000000032511567523405020052 0ustar pierre00000000000000[Desktop Entry] Name=pytimechart GenericName=pytimchart trace analyser Comment=Viewer for linux kernel's ftrace/perf/trace-event data Exec=pytimechart Icon= Terminal=false Type=Application Categories=Development; pytimechart-1.0.0.rc1/MANIFEST.in0000644000175100017510000000027711631370253015663 0ustar pierre00000000000000include MANIFEST.in include license.txt include readme.txt include setup.py include *.desktop include timechart/images/* include pytimechart-record include docs/sources/* include docs/man1/* pytimechart-1.0.0.rc1/license.txt0000644000175100017510000000335311567523405016315 0ustar pierre00000000000000THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY SITUATION ENDANGERING HUMAN LIFE OR PROPERTY. RunSnakeRun, a simple wxPython GUI viewer for Python profile logs Copyright (c) 2005-2011, The Contributors (Listed Below) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The names of The Contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The Contributors: Mike C. Fletcher Marius Gedminas pytimechart-1.0.0.rc1/pytimechart.egg-info/0000755000175100017510000000000011646055745020156 5ustar pierre00000000000000pytimechart-1.0.0.rc1/pytimechart.egg-info/PKG-INFO0000644000175100017510000000371511646055745021261 0ustar pierre00000000000000Metadata-Version: 1.0 Name: pytimechart Version: 1.0.0.rc1 Summary: Fast graphical exploration and visualisation for linux kernel traces Home-page: http://gitorious.org/pytimechart Author: Pierre Tardy Author-email: tardyp@gmail.com License: BSD Download-URL: http://gitorious.org/pytimechart Description: pyTimechart is aimed at helping kernel developer to browse into large scale traces. Based on very powerful and efficient plotting library *Chaco*, pytimechart UI feels very smooth. Based on the python language, its plugin based architecture allow developer to very quickly implement parsing and representation of new trace-event function-trace or trace_printk Kernel traces are parsed as trace-events, and organised into processes, each process is displayed in its own line. The x-axis representing the time, process is represented as intervals when it is scheduled in the system. pyTimechart processes are not only *process* in the common unix sense, it can be any group of activities in the system. Thus pytimechart display activities of: * cpuidle states * cpufreq states * runtime_pm * irqs * tasklets * works * timers * kernel threads * user process * whatever there is a plugin for pyTimechart also represent graphically the wakeup events between two process. Keywords: gui,ftrace,perf,trace-event Platform: Windows Platform: Linux Platform: Mac OS-X Platform: Unix Platform: Solaris Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Intended Audience :: Developers pytimechart-1.0.0.rc1/pytimechart.egg-info/top_level.txt0000644000175100017510000000001211646055745022701 0ustar pierre00000000000000timechart pytimechart-1.0.0.rc1/pytimechart.egg-info/requires.txt0000644000175100017510000000001411646055745022551 0ustar pierre00000000000000Chaco >= 3.0pytimechart-1.0.0.rc1/pytimechart.egg-info/entry_points.txt0000644000175100017510000000006611646055745023456 0ustar pierre00000000000000[gui_scripts] pytimechart = timechart.timechart:main pytimechart-1.0.0.rc1/pytimechart.egg-info/dependency_links.txt0000644000175100017510000000000111646055745024224 0ustar pierre00000000000000 pytimechart-1.0.0.rc1/pytimechart.egg-info/SOURCES.txt0000644000175100017510000000331711646055745022046 0ustar pierre00000000000000MANIFEST.in license.txt pytimechart-record pytimechart.desktop readme.txt setup.cfg setup.py docs/man1/pytimechart.1 docs/sources/Makefile docs/sources/conf.py docs/sources/devguide.rst docs/sources/index.rst docs/sources/userguide.rst pytimechart.egg-info/PKG-INFO pytimechart.egg-info/SOURCES.txt pytimechart.egg-info/dependency_links.txt pytimechart.egg-info/entry_points.txt pytimechart.egg-info/namespace_packages.txt pytimechart.egg-info/not-zip-safe pytimechart.egg-info/requires.txt pytimechart.egg-info/top_level.txt timechart/__init__.py timechart/actions.py timechart/colors.py timechart/model.py timechart/plot.py timechart/plugin.py timechart/process_table.py timechart/py2exe_wximports.py timechart/timechart.py timechart/tools.py timechart/window.py timechart/backends/__init__.py timechart/backends/dummy.py timechart/backends/ftrace.py timechart/backends/perf.py timechart/backends/trace_cmd.py timechart/images/clear.png timechart/images/gtkrc timechart/images/hide.png timechart/images/hide_onscreen.png timechart/images/hide_others.png timechart/images/images_license.txt timechart/images/invert.png timechart/images/select_all.png timechart/images/show.png timechart/images/toggle_auto_zoom_y.png timechart/images/toggle_autohide.png timechart/images/toggle_cpufreq.png timechart/images/toggle_cpuidle.png timechart/images/toggle_wakes.png timechart/images/trace_text.png timechart/images/unzoom.png timechart/images/view_properties.png timechart/images/zoom.png timechart/plugins/__init__.py timechart/plugins/cpuidle.py timechart/plugins/irq.py timechart/plugins/menu_select.py timechart/plugins/runtime_pm.py timechart/plugins/sched.py timechart/plugins/spi.py timechart/plugins/timers.py timechart/plugins/wake_lock.pypytimechart-1.0.0.rc1/pytimechart.egg-info/namespace_packages.txt0000644000175100017510000000005711646055745024513 0ustar pierre00000000000000timechart timechart.plugins timechart.backends pytimechart-1.0.0.rc1/pytimechart.egg-info/not-zip-safe0000644000175100017510000000000111646041225022370 0ustar pierre00000000000000 pytimechart-1.0.0.rc1/readme.txt0000644000175100017510000000225711646036064016130 0ustar pierre00000000000000PyTimechart, a tool to display ftrace (or perf) traces This is a reimplemetation of some of the main features of timechart from Arjan Van de Ven. pyTimechart is aimed at helping kernel developer to browse into large scale traces. Based on very powerful and efficient plotting library Chaco, pytimechart UI feels very smooth. Based on the python language, its plugin based architecture allow developer to very quickly implement parsing and representation of new trace-event function-trace or trace_printk Kernel traces are parsed as trace-events, and organised into processes, each process is displayed in its own line. The x-axis representing the time, process is represented as intervals when it is scheduled in the system. pyTimechart processes are not only process in the common unix sense, it can be any group of activities in the system. Thus pytimechart display activities of: cpuidle states cpufreq states runtime_pm irqs tasklets works timers kernel threads user process whatever there is a plugin for pyTimechart also represent graphically the wakeup events between two process. See online Documentation for user and installation guide at: http://packages.python.org/pytimechart/ pytimechart-1.0.0.rc1/docs/0000755000175100017510000000000011646055745015063 5ustar pierre00000000000000pytimechart-1.0.0.rc1/docs/sources/0000755000175100017510000000000011646055745016546 5ustar pierre00000000000000pytimechart-1.0.0.rc1/docs/sources/Makefile0000644000175100017510000000607411567523405020210 0ustar pierre00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = .. # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyTimechart.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyTimechart.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." pytimechart-1.0.0.rc1/docs/sources/devguide.rst0000644000175100017510000000021411567523405021064 0ustar pierre00000000000000pyTimechart Developer Guide =========================== This section is here to explain how to extend pyTimechart to parse your own traces pytimechart-1.0.0.rc1/docs/sources/userguide.rst0000644000175100017510000003065111646051202021261 0ustar pierre00000000000000pyTimechart User Guide ********************** Installation ============ pytimechart is based on the chaco graphic library, who is itself based on a lot of python technology. So its better to be helped by distros. Easiest installation varies between OS Ubuntu, and debian based linux distros -------------------------------------- apt-get and easy_install are your friends:: sudo apt-get install python-chaco python-setuptools python-lzma lzma sudo easy_install pytimechart Please note that chaco4.0 introduced incompatible namespace: http://blog.enthought.com/open-source/ets-4-0-released/ This leads to following error when trying to launch pytimechart:: ImportError: No module named enthought.etsconfig.api To restore original namespace, that pytimechart uses, you will need to install another package:: sudo easy_install etsproxy Fedora based linux distros -------------------------- yum and easy_install are your friends:: sudo yum install python-chaco python-setuptools sudo easy_install pytimechart Windows ------- Windows is not as easy, but pythonxy still helps a lot. Easiest way is to install chaco is to use pythonxy python distribution. * http://www.pythonxy.com **You need to make sure that "ETS" and "wxpython" are selected for installation**. They are not selected in default install. Then, you can use easy_install to install pytimechart. In the command line window:: easy_install pytimechart Alternatively, you can download the sources of pytimechart, and double click on the timechart.py script at root of tarball * http://gitorious.org/pytimechart/pytimechart/archive-tarball/master OSX --- You can use EPD to run pytimechart on OSX. Please note, that I have issues to make it run on latest version of EPD/pytimechart. * http://www.enthought.com/products/epd.php Recording a trace ================= Pytimechart is only a trace visualizer, it does not actually make the trace. You have to generate it with other programs. Pytimechart supports parser for 3 kind of traces available on linux. `ftrace`_, `perf`_ and `trace-cmd`_. Problem with perf and trace-cmd, is that their python binding are not currently packaged by distributions. You'll have to install them from source, and making sure you compile the python bindings. (usually build if you have python-dev package) Because of this, its currently recommended to use ftrace with pytimechart. A simple script is provided with the source of pytimechart: **pytimechart-record** .. _ftrace: http://lxr.linux.no/linux+v2.6.39/Documentation/trace/ftrace.txt .. _perf: http://lxr.linux.no/linux+v2.6.39/tools/perf/Documentation/perf-record.txt .. _trace-cmd: http://lwn.net/Articles/341902/ * http://gitorious.org/pytimechart/pytimechart/blobs/raw/master/pytimechart-record Basic usage of this script is (as root):: # pytimechart-record start # ... run your workload here # pytimechart-record stop trace written to ~/trace11-06-11-20-13-26.txt.lzma The script will produce you a timestamped .txt.lzma file that you can feed directly to pytimechart:: pytimechart ~/trace11-06-11-20-13-26.txt.lzma unlike perf and trace-cmd, this script write the data to filesystem at the "stop". During the workload, trace is kept in memory. I never needed any workload that would need more than 50MB of trace buffer. You can modify the script as you wish, but dont worry about removing some tracepoints. They are really very low overhead, and can be filtered out by pytimechart easily if they are too noisy. Every trace type can make sense at some point. Its better to have it in. User Interface ============== The UI is divided into 3 parts 1. the timechart plot 2. the process list 3. the toolbar the timechart plot ------------------- What do I see? ^^^^^^^^^^^^^^^ This is the main part of timechart. It consist of * One cpuidle line per CPU (aka C-states) .. todoimage images/cpuidle_sample.png * The color of the line is darker when the c-state is deeper * No drawing means the cpu is running at that time. * The name of the idle state is drawn inside the reactangle, if there is enough space (i.e. if you zoom enough) * Those can be hidden via following toolbar button .. image:: images/toggle_cpuidle.png :width: 16 * one P-states line per CPU (aka P-states) .. todoimage images/cpufreq_sample.png * frequency value is displayed as a usual line plot * Those can be hidden via following toolbar button .. image:: images/toggle_cpufreq.png :width: 16 * one line per visible process, .. todoimage images/cpufreq_sample.png * identifier of the process is displayed at the left of the line. It contains the "comm" of the process , its pid, and the total time of running. * A grey rectangle means that the process is running. Processor on which it is running is displayed inside the rectangle (provided you zoom enough, and there is actually enough space in the rectangle to display the number) * A yellow rectangle means the process is in running state, but another process has the cpu. This can happen when a process is scheduled out by the scheduler, or when its interrupted by an irq or other defered task (e.g. workqueue, tasklets). Following example shows nautilus being interrupted by softirq1 (timers) .. image:: images/process_preempt.png :scale: 50 * background color of the line is showing you the type of the process (e.g. irq, workqueue, timer, runtime_pm, process, etc.) How do I navigate? ^^^^^^^^^^^^^^^^^^ 1. The mouse pyTimechart is designed to be used with a 3 button mouse (with a scroll-wheel) * **Left button**: Pan into the plot * **Scroll wheel**: Zoom in, Zoom out. Normally, scroll wheel only zooms on the time axis. You can use the Ctrl modifier key to zoom on time axis **and** on the y axis. *By default, pyTimechart will hide the process that are not active in the current zoom window, so that you can concentrate on important processes.* * **Right button**: Select a part of the chart, This allow you to measure time, to zoom on a specific part, calculate statistics, or extract textual trace of a smaller period of time. See the selection section for more information * **Middle button**: when selection is active, this allows you to move the selection. (when clicked in middle of selection), extend/reduce selection (when clicked in edges of selection), or completly deselect (when clicked outside selection) 2. The keyboard * **PageUp/PageDown**: For people using timechart in planes its not always handy to use scrollwheel of the touchpad. ;-) The PageUp/PageDown keys are mapped to zoom up/zoom down. * **Esc**: This resets the zoom level to show the whole trace, the process list ---------------- The process list is very important for best use of pyTimechart. Usually, the trace will contain far too much information, The first thing to do is to hide a bunch of process, show only the process or event that highlight your problem, zoom on it, and show again processes that are active around this time. The process is a simple table containing the list of all process available in your trace. The table shows 4 columns * **Comm**: The 'comm' of the process, or if its not a process, its name. * **pid**: The pid of the process, or 0 if its not a real linux process (irq, timer, etc). * **stime**: The time where the process is active in the current selection. * **stime%**: The pourcent time where the process is active in the current selection. (time of process active * 100 / total time of selection) You can sort the table by clicking on column title with **CTRL** key down, Ctrl-clicking again will restore the original "sorted by types" order. The process list background color correspond to the type of process (irq, workqueue, user process, etc) When you select some process in the process list. They will be highlighted in blue in the timechart as well. In order to easily find your processes, a filter as been implemented. * **filter by name** (filter): Enter here a regular expression matching the process you want to see. process list is updated as you type * **filter by process duration** (dur): This popup menu allow you to only show process that have minimum activity duration in the process list. * **filter by process number of activities** (num): This popup menu allow you to only show process that have minimum activity occurences in the process list. This is useful to look processes that does not stay long but generate lot of wakes. the toolbar ----------- The toolbar tools all have tooltips that user can see with mouseover on the tools. Following is the detailed description of each action. .. start_automatically_generated_from_tooltips **invert**: .. image:: images/invert.png Invert processes show/hide value. This is useful, when you are fully zoomed, and you want to see if you are not missing some valuable info in the hidden processes **select all**: .. image:: images/select_all.png Select/Unselect all process visible in the process list Thus processes that are filtered are always unselected by this command **show**: .. image:: images/show.png Show selected processes in the timechart **hide**: .. image:: images/hide.png Hide selected processes in the timechart **hide others**: .. image:: images/hide_others.png Hide process that are not shown at current zoom window All processes that *are not* currently visible in the timechart will be hidden This is usefull when you zoom at a particular activity, and you want to unzoom without being noised by other activities. **hide onscreen**: .. image:: images/hide_onscreen.png Hide process that are shown at current zoom window All processes that *are* currently visible in the timechart will be hidden This is usefull when you zoom at a particular noise activity, and you want to unzoom without being annoyed by this activity. **toggle autohide**: .. image:: images/toggle_autohide.png *autoHide* processes that do not have any events in the current zooming window If this option is disabled, and a process does not have any activity in the current view, this will show an empty line, and eat vertical space for not much meaning. This is why it is recommanded to leave this setting on. **toggle auto zoom y**: .. image:: images/toggle_auto_zoom_y.png Automatically set the y scale to fit the number of process shown This make sure all the process that are not hidden fit the window vertically. Disable this feature if you want to manually zoom though the y-axis with the *CTRL+mouse wheel* command. **toggle wakes**: .. image:: images/toggle_wakes.png Show/Hide the wake_events. Wake events are generated by the "sched_wakeup" trace_event. wake events are represented by a row connecting a process to another. Reason of wakes can be for example: * kernel driver waking up process because IO is available. * user thread releasing mutex on which another thread was waiting for. Disabled by default because this slows down a lot graphics, and adds a lot of noise when fully unzoomed **toggle cpufreq**: .. image:: images/toggle_cpufreq.png Show/Hide the cpufreq representation. **toggle cpuidle**: .. image:: images/toggle_cpuidle.png Show/Hide the cpuidle representation. **trace text**: .. image:: images/trace_text.png Shows the text trace of the selection Sometimes, looking at the textual trace is more precise than just looking at the timechart. Moreover, all the informations of the trace is not represented by the timechart. You can also save some part of a trace to another file with this option **zoom**: .. image:: images/zoom.png Zoom so that the selection fits the window **unzoom**: .. image:: images/unzoom.png Unzoom to show the whole trace .. end_automatically_generated_from_tooltips the selection ------------- The selection is a very important UI element of pytimechart. .. image:: images/selection_illustration.png :scale: 50 1. You can start selecting part of the chart by click-and-drag with the **right mouse button**. 2. While selecting, you can see the duration of the selection in the status area, in the bottom of the window. This is very useful to measure the duration of an activity, frequency of a wake, etc. 3. The process list is also automatically updated with activity statistics over the time interval of the selection. 4. You can also use the toolbar action to zoom on the selection, or extract the textual trace delimited by the selection. pytimechart-1.0.0.rc1/docs/sources/conf.py0000644000175100017510000001427311567523405020047 0ustar pierre00000000000000# -*- coding: utf-8 -*- # # pyTimechart documentation build configuration file, created by # sphinx-quickstart on Thu May 19 19:44:44 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'pyTimechart' copyright = u'2011, Pierre Tardy' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.0' # The full version, including alpha/beta/rc tags. release = '1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'pyTimechartdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'pyTimechart.tex', u'pyTimechart Documentation', u'Pierre Tardy', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True pytimechart-1.0.0.rc1/docs/sources/index.rst0000644000175100017510000000230311606050210020357 0ustar pierre00000000000000.. DESCtitle pytimechart =========== .. DESCshortdesc Fast graphical exploration and visualisation for linux kernel traces .. image:: images/pytimechart-overview.png .. toctree:: :maxdepth: 4 userguide devguide .. DESClongdesc pyTimechart is aimed at helping kernel developer to browse into large scale traces. Based on very powerful and efficient plotting library *Chaco*, pytimechart UI feels very smooth. Based on the python language, its plugin based architecture allow developer to very quickly implement parsing and representation of new trace-event function-trace or trace_printk Kernel traces are parsed as trace-events, and organised into processes, each process is displayed in its own line. The x-axis representing the time, process is represented as intervals when it is scheduled in the system. pyTimechart processes are not only *process* in the common unix sense, it can be any group of activities in the system. Thus pytimechart display activities of: * cpuidle states * cpufreq states * runtime_pm * irqs * tasklets * works * timers * kernel threads * user process * whatever there is a plugin for pyTimechart also represent graphically the wakeup events between two process. pytimechart-1.0.0.rc1/docs/man1/0000755000175100017510000000000011646055745015717 5ustar pierre00000000000000pytimechart-1.0.0.rc1/docs/man1/pytimechart.10000644000175100017510000000057411606050210020312 0ustar pierre00000000000000.TH "pytimechart" "1" .SH NAME pytimechart \- Fast graphical exploration and visualisation for linux kernel traces .SH Usage .sp pytimechart [trace.txt|trace.txt.gz|trace.txt.lzma|trace.dat] .sp pyTimechart is aimed at helping kernel developer to browse into large scale traces. .sp See <\fI\%http://packages.python.org/pytimechart/\fP> for more information and documentation . pytimechart-1.0.0.rc1/setup.cfg0000644000175100017510000000025411646055745015755 0ustar pierre00000000000000[build_sphinx] source-dir = docs/sources build-dir = docs all_files = 1 [upload_sphinx] upload-dir = docs/html [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0