analyze_suspend.py 170 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235
  1. #!/usr/bin/python
  2. #
  3. # Tool for analyzing suspend/resume timing
  4. # Copyright (c) 2013, Intel Corporation.
  5. #
  6. # This program is free software; you can redistribute it and/or modify it
  7. # under the terms and conditions of the GNU General Public License,
  8. # version 2, as published by the Free Software Foundation.
  9. #
  10. # This program is distributed in the hope it will be useful, but WITHOUT
  11. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13. # more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along with
  16. # this program; if not, write to the Free Software Foundation, Inc.,
  17. # 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  18. #
  19. # Authors:
  20. # Todd Brandt <todd.e.brandt@linux.intel.com>
  21. #
  22. # Links:
  23. # Home Page
  24. # https://01.org/suspendresume
  25. # Source repo
  26. # https://github.com/01org/suspendresume
  27. #
  28. # Description:
  29. # This tool is designed to assist kernel and OS developers in optimizing
  30. # their linux stack's suspend/resume time. Using a kernel image built
  31. # with a few extra options enabled, the tool will execute a suspend and
  32. # will capture dmesg and ftrace data until resume is complete. This data
  33. # is transformed into a device timeline and a callgraph to give a quick
  34. # and detailed view of which devices and callbacks are taking the most
  35. # time in suspend/resume. The output is a single html file which can be
  36. # viewed in firefox or chrome.
  37. #
  38. # The following kernel build options are required:
  39. # CONFIG_PM_DEBUG=y
  40. # CONFIG_PM_SLEEP_DEBUG=y
  41. # CONFIG_FTRACE=y
  42. # CONFIG_FUNCTION_TRACER=y
  43. # CONFIG_FUNCTION_GRAPH_TRACER=y
  44. # CONFIG_KPROBES=y
  45. # CONFIG_KPROBES_ON_FTRACE=y
  46. #
  47. # For kernel versions older than 3.15:
  48. # The following additional kernel parameters are required:
  49. # (e.g. in file /etc/default/grub)
  50. # GRUB_CMDLINE_LINUX_DEFAULT="... initcall_debug log_buf_len=16M ..."
  51. #
  52. # ----------------- LIBRARIES --------------------
  53. import sys
  54. import time
  55. import os
  56. import string
  57. import re
  58. import platform
  59. from datetime import datetime
  60. import struct
  61. import ConfigParser
  62. from threading import Thread
  63. from subprocess import call, Popen, PIPE
  64. # ----------------- CLASSES --------------------
  65. # Class: SystemValues
  66. # Description:
  67. # A global, single-instance container used to
  68. # store system values and test parameters
  69. class SystemValues:
  70. ansi = False
  71. version = '4.5'
  72. verbose = False
  73. addlogs = False
  74. mindevlen = 0.0
  75. mincglen = 0.0
  76. cgphase = ''
  77. cgtest = -1
  78. callloopmaxgap = 0.0001
  79. callloopmaxlen = 0.005
  80. srgap = 0
  81. cgexp = False
  82. outdir = ''
  83. testdir = '.'
  84. tpath = '/sys/kernel/debug/tracing/'
  85. fpdtpath = '/sys/firmware/acpi/tables/FPDT'
  86. epath = '/sys/kernel/debug/tracing/events/power/'
  87. traceevents = [
  88. 'suspend_resume',
  89. 'device_pm_callback_end',
  90. 'device_pm_callback_start'
  91. ]
  92. logmsg = ''
  93. testcommand = ''
  94. mempath = '/dev/mem'
  95. powerfile = '/sys/power/state'
  96. suspendmode = 'mem'
  97. hostname = 'localhost'
  98. prefix = 'test'
  99. teststamp = ''
  100. dmesgstart = 0.0
  101. dmesgfile = ''
  102. ftracefile = ''
  103. htmlfile = ''
  104. embedded = False
  105. rtcwake = False
  106. rtcwaketime = 10
  107. rtcpath = ''
  108. devicefilter = []
  109. stamp = 0
  110. execcount = 1
  111. x2delay = 0
  112. usecallgraph = False
  113. usetraceevents = False
  114. usetraceeventsonly = False
  115. usetracemarkers = True
  116. usekprobes = True
  117. usedevsrc = False
  118. useprocmon = False
  119. notestrun = False
  120. mixedphaseheight = True
  121. devprops = dict()
  122. predelay = 0
  123. postdelay = 0
  124. procexecfmt = 'ps - (?P<ps>.*)$'
  125. devpropfmt = '# Device Properties: .*'
  126. tracertypefmt = '# tracer: (?P<t>.*)'
  127. firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
  128. stampfmt = '# suspend-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\
  129. '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
  130. ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
  131. tracefuncs = {
  132. 'sys_sync': dict(),
  133. 'pm_prepare_console': dict(),
  134. 'pm_notifier_call_chain': dict(),
  135. 'freeze_processes': dict(),
  136. 'freeze_kernel_threads': dict(),
  137. 'pm_restrict_gfp_mask': dict(),
  138. 'acpi_suspend_begin': dict(),
  139. 'suspend_console': dict(),
  140. 'acpi_pm_prepare': dict(),
  141. 'syscore_suspend': dict(),
  142. 'arch_enable_nonboot_cpus_end': dict(),
  143. 'syscore_resume': dict(),
  144. 'acpi_pm_finish': dict(),
  145. 'resume_console': dict(),
  146. 'acpi_pm_end': dict(),
  147. 'pm_restore_gfp_mask': dict(),
  148. 'thaw_processes': dict(),
  149. 'pm_restore_console': dict(),
  150. 'CPU_OFF': {
  151. 'func':'_cpu_down',
  152. 'args_x86_64': {'cpu':'%di:s32'},
  153. 'format': 'CPU_OFF[{cpu}]'
  154. },
  155. 'CPU_ON': {
  156. 'func':'_cpu_up',
  157. 'args_x86_64': {'cpu':'%di:s32'},
  158. 'format': 'CPU_ON[{cpu}]'
  159. },
  160. }
  161. dev_tracefuncs = {
  162. # general wait/delay/sleep
  163. 'msleep': { 'args_x86_64': {'time':'%di:s32'}, 'ub': 1 },
  164. 'schedule_timeout_uninterruptible': { 'args_x86_64': {'timeout':'%di:s32'}, 'ub': 1 },
  165. 'schedule_timeout': { 'args_x86_64': {'timeout':'%di:s32'}, 'ub': 1 },
  166. 'udelay': { 'func':'__const_udelay', 'args_x86_64': {'loops':'%di:s32'}, 'ub': 1 },
  167. 'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
  168. 'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
  169. 'acpi_os_stall': {'ub': 1},
  170. # ACPI
  171. 'acpi_resume_power_resources': dict(),
  172. 'acpi_ps_parse_aml': dict(),
  173. # filesystem
  174. 'ext4_sync_fs': dict(),
  175. # 80211
  176. 'iwlagn_mac_start': dict(),
  177. 'iwlagn_alloc_bcast_station': dict(),
  178. 'iwl_trans_pcie_start_hw': dict(),
  179. 'iwl_trans_pcie_start_fw': dict(),
  180. 'iwl_run_init_ucode': dict(),
  181. 'iwl_load_ucode_wait_alive': dict(),
  182. 'iwl_alive_start': dict(),
  183. 'iwlagn_mac_stop': dict(),
  184. 'iwlagn_mac_suspend': dict(),
  185. 'iwlagn_mac_resume': dict(),
  186. 'iwlagn_mac_add_interface': dict(),
  187. 'iwlagn_mac_remove_interface': dict(),
  188. 'iwlagn_mac_change_interface': dict(),
  189. 'iwlagn_mac_config': dict(),
  190. 'iwlagn_configure_filter': dict(),
  191. 'iwlagn_mac_hw_scan': dict(),
  192. 'iwlagn_bss_info_changed': dict(),
  193. 'iwlagn_mac_channel_switch': dict(),
  194. 'iwlagn_mac_flush': dict(),
  195. # ATA
  196. 'ata_eh_recover': { 'args_x86_64': {'port':'+36(%di):s32'} },
  197. # i915
  198. 'i915_gem_resume': dict(),
  199. 'i915_restore_state': dict(),
  200. 'intel_opregion_setup': dict(),
  201. 'g4x_pre_enable_dp': dict(),
  202. 'vlv_pre_enable_dp': dict(),
  203. 'chv_pre_enable_dp': dict(),
  204. 'g4x_enable_dp': dict(),
  205. 'vlv_enable_dp': dict(),
  206. 'intel_hpd_init': dict(),
  207. 'intel_opregion_register': dict(),
  208. 'intel_dp_detect': dict(),
  209. 'intel_hdmi_detect': dict(),
  210. 'intel_opregion_init': dict(),
  211. 'intel_fbdev_set_suspend': dict(),
  212. }
  213. kprobes = dict()
  214. timeformat = '%.3f'
  215. def __init__(self):
  216. # if this is a phoronix test run, set some default options
  217. if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ):
  218. self.embedded = True
  219. self.addlogs = True
  220. self.htmlfile = os.environ['LOG_FILE']
  221. self.archargs = 'args_'+platform.machine()
  222. self.hostname = platform.node()
  223. if(self.hostname == ''):
  224. self.hostname = 'localhost'
  225. rtc = "rtc0"
  226. if os.path.exists('/dev/rtc'):
  227. rtc = os.readlink('/dev/rtc')
  228. rtc = '/sys/class/rtc/'+rtc
  229. if os.path.exists(rtc) and os.path.exists(rtc+'/date') and \
  230. os.path.exists(rtc+'/time') and os.path.exists(rtc+'/wakealarm'):
  231. self.rtcpath = rtc
  232. if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()):
  233. self.ansi = True
  234. def setPrecision(self, num):
  235. if num < 0 or num > 6:
  236. return
  237. self.timeformat = '%.{0}f'.format(num)
  238. def setOutputFolder(self, value):
  239. args = dict()
  240. n = datetime.now()
  241. args['date'] = n.strftime('%y%m%d')
  242. args['time'] = n.strftime('%H%M%S')
  243. args['hostname'] = self.hostname
  244. self.outdir = value.format(**args)
  245. def setOutputFile(self):
  246. if((self.htmlfile == '') and (self.dmesgfile != '')):
  247. m = re.match('(?P<name>.*)_dmesg\.txt$', self.dmesgfile)
  248. if(m):
  249. self.htmlfile = m.group('name')+'.html'
  250. if((self.htmlfile == '') and (self.ftracefile != '')):
  251. m = re.match('(?P<name>.*)_ftrace\.txt$', self.ftracefile)
  252. if(m):
  253. self.htmlfile = m.group('name')+'.html'
  254. if(self.htmlfile == ''):
  255. self.htmlfile = 'output.html'
  256. def initTestOutput(self, subdir, testpath=''):
  257. self.prefix = self.hostname
  258. v = open('/proc/version', 'r').read().strip()
  259. kver = string.split(v)[2]
  260. n = datetime.now()
  261. testtime = n.strftime('suspend-%m%d%y-%H%M%S')
  262. if not testpath:
  263. testpath = n.strftime('suspend-%y%m%d-%H%M%S')
  264. if(subdir != "."):
  265. self.testdir = subdir+"/"+testpath
  266. else:
  267. self.testdir = testpath
  268. self.teststamp = \
  269. '# '+testtime+' '+self.prefix+' '+self.suspendmode+' '+kver
  270. if(self.embedded):
  271. self.dmesgfile = \
  272. '/tmp/'+testtime+'_'+self.suspendmode+'_dmesg.txt'
  273. self.ftracefile = \
  274. '/tmp/'+testtime+'_'+self.suspendmode+'_ftrace.txt'
  275. return
  276. self.dmesgfile = \
  277. self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_dmesg.txt'
  278. self.ftracefile = \
  279. self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_ftrace.txt'
  280. self.htmlfile = \
  281. self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
  282. if not os.path.isdir(self.testdir):
  283. os.mkdir(self.testdir)
  284. def setDeviceFilter(self, value):
  285. self.devicefilter = []
  286. if value:
  287. value = value.split(',')
  288. for i in value:
  289. self.devicefilter.append(i.strip())
  290. def rtcWakeAlarmOn(self):
  291. call('echo 0 > '+self.rtcpath+'/wakealarm', shell=True)
  292. outD = open(self.rtcpath+'/date', 'r').read().strip()
  293. outT = open(self.rtcpath+'/time', 'r').read().strip()
  294. mD = re.match('^(?P<y>[0-9]*)-(?P<m>[0-9]*)-(?P<d>[0-9]*)', outD)
  295. mT = re.match('^(?P<h>[0-9]*):(?P<m>[0-9]*):(?P<s>[0-9]*)', outT)
  296. if(mD and mT):
  297. # get the current time from hardware
  298. utcoffset = int((datetime.now() - datetime.utcnow()).total_seconds())
  299. dt = datetime(\
  300. int(mD.group('y')), int(mD.group('m')), int(mD.group('d')),
  301. int(mT.group('h')), int(mT.group('m')), int(mT.group('s')))
  302. nowtime = int(dt.strftime('%s')) + utcoffset
  303. else:
  304. # if hardware time fails, use the software time
  305. nowtime = int(datetime.now().strftime('%s'))
  306. alarm = nowtime + self.rtcwaketime
  307. call('echo %d > %s/wakealarm' % (alarm, self.rtcpath), shell=True)
  308. def rtcWakeAlarmOff(self):
  309. call('echo 0 > %s/wakealarm' % self.rtcpath, shell=True)
  310. def initdmesg(self):
  311. # get the latest time stamp from the dmesg log
  312. fp = Popen('dmesg', stdout=PIPE).stdout
  313. ktime = '0'
  314. for line in fp:
  315. line = line.replace('\r\n', '')
  316. idx = line.find('[')
  317. if idx > 1:
  318. line = line[idx:]
  319. m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
  320. if(m):
  321. ktime = m.group('ktime')
  322. fp.close()
  323. self.dmesgstart = float(ktime)
  324. def getdmesg(self):
  325. # store all new dmesg lines since initdmesg was called
  326. fp = Popen('dmesg', stdout=PIPE).stdout
  327. op = open(self.dmesgfile, 'a')
  328. for line in fp:
  329. line = line.replace('\r\n', '')
  330. idx = line.find('[')
  331. if idx > 1:
  332. line = line[idx:]
  333. m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
  334. if(not m):
  335. continue
  336. ktime = float(m.group('ktime'))
  337. if ktime > self.dmesgstart:
  338. op.write(line)
  339. fp.close()
  340. op.close()
  341. def addFtraceFilterFunctions(self, file):
  342. fp = open(file)
  343. list = fp.read().split('\n')
  344. fp.close()
  345. for i in list:
  346. if len(i) < 2:
  347. continue
  348. self.tracefuncs[i] = dict()
  349. def getFtraceFilterFunctions(self, current):
  350. rootCheck(True)
  351. if not current:
  352. call('cat '+self.tpath+'available_filter_functions', shell=True)
  353. return
  354. fp = open(self.tpath+'available_filter_functions')
  355. master = fp.read().split('\n')
  356. fp.close()
  357. for i in self.tracefuncs:
  358. if 'func' in self.tracefuncs[i]:
  359. i = self.tracefuncs[i]['func']
  360. if i in master:
  361. print i
  362. else:
  363. print self.colorText(i)
  364. def setFtraceFilterFunctions(self, list):
  365. fp = open(self.tpath+'available_filter_functions')
  366. master = fp.read().split('\n')
  367. fp.close()
  368. flist = ''
  369. for i in list:
  370. if i not in master:
  371. continue
  372. if ' [' in i:
  373. flist += i.split(' ')[0]+'\n'
  374. else:
  375. flist += i+'\n'
  376. fp = open(self.tpath+'set_graph_function', 'w')
  377. fp.write(flist)
  378. fp.close()
  379. def basicKprobe(self, name):
  380. self.kprobes[name] = {'name': name,'func': name,'args': dict(),'format': name}
  381. def defaultKprobe(self, name, kdata):
  382. k = kdata
  383. for field in ['name', 'format', 'func']:
  384. if field not in k:
  385. k[field] = name
  386. if self.archargs in k:
  387. k['args'] = k[self.archargs]
  388. else:
  389. k['args'] = dict()
  390. k['format'] = name
  391. self.kprobes[name] = k
  392. def kprobeColor(self, name):
  393. if name not in self.kprobes or 'color' not in self.kprobes[name]:
  394. return ''
  395. return self.kprobes[name]['color']
  396. def kprobeDisplayName(self, name, dataraw):
  397. if name not in self.kprobes:
  398. self.basicKprobe(name)
  399. data = ''
  400. quote=0
  401. # first remvoe any spaces inside quotes, and the quotes
  402. for c in dataraw:
  403. if c == '"':
  404. quote = (quote + 1) % 2
  405. if quote and c == ' ':
  406. data += '_'
  407. elif c != '"':
  408. data += c
  409. fmt, args = self.kprobes[name]['format'], self.kprobes[name]['args']
  410. arglist = dict()
  411. # now process the args
  412. for arg in sorted(args):
  413. arglist[arg] = ''
  414. m = re.match('.* '+arg+'=(?P<arg>.*) ', data);
  415. if m:
  416. arglist[arg] = m.group('arg')
  417. else:
  418. m = re.match('.* '+arg+'=(?P<arg>.*)', data);
  419. if m:
  420. arglist[arg] = m.group('arg')
  421. out = fmt.format(**arglist)
  422. out = out.replace(' ', '_').replace('"', '')
  423. return out
  424. def kprobeText(self, kname, kprobe):
  425. name = fmt = func = kname
  426. args = dict()
  427. if 'name' in kprobe:
  428. name = kprobe['name']
  429. if 'format' in kprobe:
  430. fmt = kprobe['format']
  431. if 'func' in kprobe:
  432. func = kprobe['func']
  433. if self.archargs in kprobe:
  434. args = kprobe[self.archargs]
  435. if 'args' in kprobe:
  436. args = kprobe['args']
  437. if re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', func):
  438. doError('Kprobe "%s" has format info in the function name "%s"' % (name, func))
  439. for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', fmt):
  440. if arg not in args:
  441. doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
  442. val = 'p:%s_cal %s' % (name, func)
  443. for i in sorted(args):
  444. val += ' %s=%s' % (i, args[i])
  445. val += '\nr:%s_ret %s $retval\n' % (name, func)
  446. return val
  447. def addKprobes(self, output=False):
  448. if len(sysvals.kprobes) < 1:
  449. return
  450. if output:
  451. print(' kprobe functions in this kernel:')
  452. # first test each kprobe
  453. rejects = []
  454. # sort kprobes: trace, ub-dev, custom, dev
  455. kpl = [[], [], [], []]
  456. for name in sorted(self.kprobes):
  457. res = self.colorText('YES', 32)
  458. if not self.testKprobe(name, self.kprobes[name]):
  459. res = self.colorText('NO')
  460. rejects.append(name)
  461. else:
  462. if name in self.tracefuncs:
  463. kpl[0].append(name)
  464. elif name in self.dev_tracefuncs:
  465. if 'ub' in self.dev_tracefuncs[name]:
  466. kpl[1].append(name)
  467. else:
  468. kpl[3].append(name)
  469. else:
  470. kpl[2].append(name)
  471. if output:
  472. print(' %s: %s' % (name, res))
  473. kplist = kpl[0] + kpl[1] + kpl[2] + kpl[3]
  474. # remove all failed ones from the list
  475. for name in rejects:
  476. self.kprobes.pop(name)
  477. # set the kprobes all at once
  478. self.fsetVal('', 'kprobe_events')
  479. kprobeevents = ''
  480. for kp in kplist:
  481. kprobeevents += self.kprobeText(kp, self.kprobes[kp])
  482. self.fsetVal(kprobeevents, 'kprobe_events')
  483. # verify that the kprobes were set as ordered
  484. check = self.fgetVal('kprobe_events')
  485. linesout = len(kprobeevents.split('\n')) - 1
  486. linesack = len(check.split('\n')) - 1
  487. if output:
  488. res = '%d/%d' % (linesack, linesout)
  489. if linesack < linesout:
  490. res = self.colorText(res, 31)
  491. else:
  492. res = self.colorText(res, 32)
  493. print(' working kprobe functions enabled: %s' % res)
  494. self.fsetVal('1', 'events/kprobes/enable')
  495. def testKprobe(self, kname, kprobe):
  496. self.fsetVal('0', 'events/kprobes/enable')
  497. kprobeevents = self.kprobeText(kname, kprobe)
  498. if not kprobeevents:
  499. return False
  500. try:
  501. self.fsetVal(kprobeevents, 'kprobe_events')
  502. check = self.fgetVal('kprobe_events')
  503. except:
  504. return False
  505. linesout = len(kprobeevents.split('\n'))
  506. linesack = len(check.split('\n'))
  507. if linesack < linesout:
  508. return False
  509. return True
  510. def fsetVal(self, val, path, mode='w'):
  511. file = self.tpath+path
  512. if not os.path.exists(file):
  513. return False
  514. try:
  515. fp = open(file, mode, 0)
  516. fp.write(val)
  517. fp.flush()
  518. fp.close()
  519. except:
  520. pass
  521. return True
  522. def fgetVal(self, path):
  523. file = self.tpath+path
  524. res = ''
  525. if not os.path.exists(file):
  526. return res
  527. try:
  528. fp = open(file, 'r')
  529. res = fp.read()
  530. fp.close()
  531. except:
  532. pass
  533. return res
  534. def cleanupFtrace(self):
  535. if(self.usecallgraph or self.usetraceevents):
  536. self.fsetVal('0', 'events/kprobes/enable')
  537. self.fsetVal('', 'kprobe_events')
  538. def setupAllKprobes(self):
  539. for name in self.tracefuncs:
  540. self.defaultKprobe(name, self.tracefuncs[name])
  541. for name in self.dev_tracefuncs:
  542. self.defaultKprobe(name, self.dev_tracefuncs[name])
  543. def isCallgraphFunc(self, name):
  544. if len(self.tracefuncs) < 1 and self.suspendmode == 'command':
  545. return True
  546. for i in self.tracefuncs:
  547. if 'func' in self.tracefuncs[i]:
  548. f = self.tracefuncs[i]['func']
  549. else:
  550. f = i
  551. if name == f:
  552. return True
  553. return False
  554. def initFtrace(self, testing=False):
  555. print('INITIALIZING FTRACE...')
  556. # turn trace off
  557. self.fsetVal('0', 'tracing_on')
  558. self.cleanupFtrace()
  559. # set the trace clock to global
  560. self.fsetVal('global', 'trace_clock')
  561. # set trace buffer to a huge value
  562. self.fsetVal('nop', 'current_tracer')
  563. self.fsetVal('100000', 'buffer_size_kb')
  564. # go no further if this is just a status check
  565. if testing:
  566. return
  567. # initialize the callgraph trace
  568. if(self.usecallgraph):
  569. # set trace type
  570. self.fsetVal('function_graph', 'current_tracer')
  571. self.fsetVal('', 'set_ftrace_filter')
  572. # set trace format options
  573. self.fsetVal('print-parent', 'trace_options')
  574. self.fsetVal('funcgraph-abstime', 'trace_options')
  575. self.fsetVal('funcgraph-cpu', 'trace_options')
  576. self.fsetVal('funcgraph-duration', 'trace_options')
  577. self.fsetVal('funcgraph-proc', 'trace_options')
  578. self.fsetVal('funcgraph-tail', 'trace_options')
  579. self.fsetVal('nofuncgraph-overhead', 'trace_options')
  580. self.fsetVal('context-info', 'trace_options')
  581. self.fsetVal('graph-time', 'trace_options')
  582. self.fsetVal('0', 'max_graph_depth')
  583. cf = ['dpm_run_callback']
  584. if(self.usetraceeventsonly):
  585. cf += ['dpm_prepare', 'dpm_complete']
  586. for fn in self.tracefuncs:
  587. if 'func' in self.tracefuncs[fn]:
  588. cf.append(self.tracefuncs[fn]['func'])
  589. else:
  590. cf.append(fn)
  591. self.setFtraceFilterFunctions(cf)
  592. # initialize the kprobe trace
  593. elif self.usekprobes:
  594. for name in self.tracefuncs:
  595. self.defaultKprobe(name, self.tracefuncs[name])
  596. if self.usedevsrc:
  597. for name in self.dev_tracefuncs:
  598. self.defaultKprobe(name, self.dev_tracefuncs[name])
  599. print('INITIALIZING KPROBES...')
  600. self.addKprobes(self.verbose)
  601. if(self.usetraceevents):
  602. # turn trace events on
  603. events = iter(self.traceevents)
  604. for e in events:
  605. self.fsetVal('1', 'events/power/'+e+'/enable')
  606. # clear the trace buffer
  607. self.fsetVal('', 'trace')
  608. def verifyFtrace(self):
  609. # files needed for any trace data
  610. files = ['buffer_size_kb', 'current_tracer', 'trace', 'trace_clock',
  611. 'trace_marker', 'trace_options', 'tracing_on']
  612. # files needed for callgraph trace data
  613. tp = self.tpath
  614. if(self.usecallgraph):
  615. files += [
  616. 'available_filter_functions',
  617. 'set_ftrace_filter',
  618. 'set_graph_function'
  619. ]
  620. for f in files:
  621. if(os.path.exists(tp+f) == False):
  622. return False
  623. return True
  624. def verifyKprobes(self):
  625. # files needed for kprobes to work
  626. files = ['kprobe_events', 'events']
  627. tp = self.tpath
  628. for f in files:
  629. if(os.path.exists(tp+f) == False):
  630. return False
  631. return True
  632. def colorText(self, str, color=31):
  633. if not self.ansi:
  634. return str
  635. return '\x1B[%d;40m%s\x1B[m' % (color, str)
  636. sysvals = SystemValues()
  637. # Class: DevProps
  638. # Description:
  639. # Simple class which holds property values collected
  640. # for all the devices used in the timeline.
  641. class DevProps:
  642. syspath = ''
  643. altname = ''
  644. async = True
  645. xtraclass = ''
  646. xtrainfo = ''
  647. def out(self, dev):
  648. return '%s,%s,%d;' % (dev, self.altname, self.async)
  649. def debug(self, dev):
  650. print '%s:\n\taltname = %s\n\t async = %s' % (dev, self.altname, self.async)
  651. def altName(self, dev):
  652. if not self.altname or self.altname == dev:
  653. return dev
  654. return '%s [%s]' % (self.altname, dev)
  655. def xtraClass(self):
  656. if self.xtraclass:
  657. return ' '+self.xtraclass
  658. if not self.async:
  659. return ' sync'
  660. return ''
  661. def xtraInfo(self):
  662. if self.xtraclass:
  663. return ' '+self.xtraclass
  664. if self.async:
  665. return ' async_device'
  666. return ' sync_device'
  667. # Class: DeviceNode
  668. # Description:
  669. # A container used to create a device hierachy, with a single root node
  670. # and a tree of child nodes. Used by Data.deviceTopology()
  671. class DeviceNode:
  672. name = ''
  673. children = 0
  674. depth = 0
  675. def __init__(self, nodename, nodedepth):
  676. self.name = nodename
  677. self.children = []
  678. self.depth = nodedepth
  679. # Class: Data
  680. # Description:
  681. # The primary container for suspend/resume test data. There is one for
  682. # each test run. The data is organized into a cronological hierarchy:
  683. # Data.dmesg {
  684. # phases {
  685. # 10 sequential, non-overlapping phases of S/R
  686. # contents: times for phase start/end, order/color data for html
  687. # devlist {
  688. # device callback or action list for this phase
  689. # device {
  690. # a single device callback or generic action
  691. # contents: start/stop times, pid/cpu/driver info
  692. # parents/children, html id for timeline/callgraph
  693. # optionally includes an ftrace callgraph
  694. # optionally includes dev/ps data
  695. # }
  696. # }
  697. # }
  698. # }
  699. #
  700. class Data:
  701. dmesg = {} # root data structure
  702. phases = [] # ordered list of phases
  703. start = 0.0 # test start
  704. end = 0.0 # test end
  705. tSuspended = 0.0 # low-level suspend start
  706. tResumed = 0.0 # low-level resume start
  707. tKernSus = 0.0 # kernel level suspend start
  708. tKernRes = 0.0 # kernel level resume end
  709. tLow = 0.0 # time spent in low-level suspend (standby/freeze)
  710. fwValid = False # is firmware data available
  711. fwSuspend = 0 # time spent in firmware suspend
  712. fwResume = 0 # time spent in firmware resume
  713. dmesgtext = [] # dmesg text file in memory
  714. pstl = 0 # process timeline
  715. testnumber = 0
  716. idstr = ''
  717. html_device_id = 0
  718. stamp = 0
  719. outfile = ''
  720. devpids = []
  721. kerror = False
  722. def __init__(self, num):
  723. idchar = 'abcdefghij'
  724. self.pstl = dict()
  725. self.testnumber = num
  726. self.idstr = idchar[num]
  727. self.dmesgtext = []
  728. self.phases = []
  729. self.dmesg = { # fixed list of 10 phases
  730. 'suspend_prepare': {'list': dict(), 'start': -1.0, 'end': -1.0,
  731. 'row': 0, 'color': '#CCFFCC', 'order': 0},
  732. 'suspend': {'list': dict(), 'start': -1.0, 'end': -1.0,
  733. 'row': 0, 'color': '#88FF88', 'order': 1},
  734. 'suspend_late': {'list': dict(), 'start': -1.0, 'end': -1.0,
  735. 'row': 0, 'color': '#00AA00', 'order': 2},
  736. 'suspend_noirq': {'list': dict(), 'start': -1.0, 'end': -1.0,
  737. 'row': 0, 'color': '#008888', 'order': 3},
  738. 'suspend_machine': {'list': dict(), 'start': -1.0, 'end': -1.0,
  739. 'row': 0, 'color': '#0000FF', 'order': 4},
  740. 'resume_machine': {'list': dict(), 'start': -1.0, 'end': -1.0,
  741. 'row': 0, 'color': '#FF0000', 'order': 5},
  742. 'resume_noirq': {'list': dict(), 'start': -1.0, 'end': -1.0,
  743. 'row': 0, 'color': '#FF9900', 'order': 6},
  744. 'resume_early': {'list': dict(), 'start': -1.0, 'end': -1.0,
  745. 'row': 0, 'color': '#FFCC00', 'order': 7},
  746. 'resume': {'list': dict(), 'start': -1.0, 'end': -1.0,
  747. 'row': 0, 'color': '#FFFF88', 'order': 8},
  748. 'resume_complete': {'list': dict(), 'start': -1.0, 'end': -1.0,
  749. 'row': 0, 'color': '#FFFFCC', 'order': 9}
  750. }
  751. self.phases = self.sortedPhases()
  752. self.devicegroups = []
  753. for phase in self.phases:
  754. self.devicegroups.append([phase])
  755. self.errorinfo = {'suspend':[],'resume':[]}
  756. def extractErrorInfo(self, dmesg):
  757. error = ''
  758. tm = 0.0
  759. for i in range(len(dmesg)):
  760. if 'Call Trace:' in dmesg[i]:
  761. m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) .*', dmesg[i])
  762. if not m:
  763. continue
  764. tm = float(m.group('ktime'))
  765. if tm < self.start or tm > self.end:
  766. continue
  767. for j in range(i-10, i+1):
  768. error += dmesg[j]
  769. continue
  770. if error:
  771. m = re.match('[ \t]*\[ *[0-9\.]*\] \[\<[0-9a-fA-F]*\>\] .*', dmesg[i])
  772. if m:
  773. error += dmesg[i]
  774. else:
  775. if tm < self.tSuspended:
  776. dir = 'suspend'
  777. else:
  778. dir = 'resume'
  779. error = error.replace('<', '&lt').replace('>', '&gt')
  780. vprint('kernel error found in %s at %f' % (dir, tm))
  781. self.errorinfo[dir].append((tm, error))
  782. self.kerror = True
  783. error = ''
  784. def setStart(self, time):
  785. self.start = time
  786. def setEnd(self, time):
  787. self.end = time
  788. def isTraceEventOutsideDeviceCalls(self, pid, time):
  789. for phase in self.phases:
  790. list = self.dmesg[phase]['list']
  791. for dev in list:
  792. d = list[dev]
  793. if(d['pid'] == pid and time >= d['start'] and
  794. time < d['end']):
  795. return False
  796. return True
  797. def sourcePhase(self, start):
  798. for phase in self.phases:
  799. pend = self.dmesg[phase]['end']
  800. if start <= pend:
  801. return phase
  802. return 'resume_complete'
  803. def sourceDevice(self, phaselist, start, end, pid, type):
  804. tgtdev = ''
  805. for phase in phaselist:
  806. list = self.dmesg[phase]['list']
  807. for devname in list:
  808. dev = list[devname]
  809. # pid must match
  810. if dev['pid'] != pid:
  811. continue
  812. devS = dev['start']
  813. devE = dev['end']
  814. if type == 'device':
  815. # device target event is entirely inside the source boundary
  816. if(start < devS or start >= devE or end <= devS or end > devE):
  817. continue
  818. elif type == 'thread':
  819. # thread target event will expand the source boundary
  820. if start < devS:
  821. dev['start'] = start
  822. if end > devE:
  823. dev['end'] = end
  824. tgtdev = dev
  825. break
  826. return tgtdev
  827. def addDeviceFunctionCall(self, displayname, kprobename, proc, pid, start, end, cdata, rdata):
  828. # try to place the call in a device
  829. tgtdev = self.sourceDevice(self.phases, start, end, pid, 'device')
  830. # calls with device pids that occur outside device bounds are dropped
  831. # TODO: include these somehow
  832. if not tgtdev and pid in self.devpids:
  833. return False
  834. # try to place the call in a thread
  835. if not tgtdev:
  836. tgtdev = self.sourceDevice(self.phases, start, end, pid, 'thread')
  837. # create new thread blocks, expand as new calls are found
  838. if not tgtdev:
  839. if proc == '<...>':
  840. threadname = 'kthread-%d' % (pid)
  841. else:
  842. threadname = '%s-%d' % (proc, pid)
  843. tgtphase = self.sourcePhase(start)
  844. self.newAction(tgtphase, threadname, pid, '', start, end, '', ' kth', '')
  845. return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata)
  846. # this should not happen
  847. if not tgtdev:
  848. vprint('[%f - %f] %s-%d %s %s %s' % \
  849. (start, end, proc, pid, kprobename, cdata, rdata))
  850. return False
  851. # place the call data inside the src element of the tgtdev
  852. if('src' not in tgtdev):
  853. tgtdev['src'] = []
  854. dtf = sysvals.dev_tracefuncs
  855. ubiquitous = False
  856. if kprobename in dtf and 'ub' in dtf[kprobename]:
  857. ubiquitous = True
  858. title = cdata+' '+rdata
  859. mstr = '\(.*\) *(?P<args>.*) *\((?P<caller>.*)\+.* arg1=(?P<ret>.*)'
  860. m = re.match(mstr, title)
  861. if m:
  862. c = m.group('caller')
  863. a = m.group('args').strip()
  864. r = m.group('ret')
  865. if len(r) > 6:
  866. r = ''
  867. else:
  868. r = 'ret=%s ' % r
  869. if ubiquitous and c in dtf and 'ub' in dtf[c]:
  870. return False
  871. color = sysvals.kprobeColor(kprobename)
  872. e = DevFunction(displayname, a, c, r, start, end, ubiquitous, proc, pid, color)
  873. tgtdev['src'].append(e)
  874. return True
  875. def overflowDevices(self):
  876. # get a list of devices that extend beyond the end of this test run
  877. devlist = []
  878. for phase in self.phases:
  879. list = self.dmesg[phase]['list']
  880. for devname in list:
  881. dev = list[devname]
  882. if dev['end'] > self.end:
  883. devlist.append(dev)
  884. return devlist
  885. def mergeOverlapDevices(self, devlist):
  886. # merge any devices that overlap devlist
  887. for dev in devlist:
  888. devname = dev['name']
  889. for phase in self.phases:
  890. list = self.dmesg[phase]['list']
  891. if devname not in list:
  892. continue
  893. tdev = list[devname]
  894. o = min(dev['end'], tdev['end']) - max(dev['start'], tdev['start'])
  895. if o <= 0:
  896. continue
  897. dev['end'] = tdev['end']
  898. if 'src' not in dev or 'src' not in tdev:
  899. continue
  900. dev['src'] += tdev['src']
  901. del list[devname]
  902. def usurpTouchingThread(self, name, dev):
  903. # the caller test has priority of this thread, give it to him
  904. for phase in self.phases:
  905. list = self.dmesg[phase]['list']
  906. if name in list:
  907. tdev = list[name]
  908. if tdev['start'] - dev['end'] < 0.1:
  909. dev['end'] = tdev['end']
  910. if 'src' not in dev:
  911. dev['src'] = []
  912. if 'src' in tdev:
  913. dev['src'] += tdev['src']
  914. del list[name]
  915. break
  916. def stitchTouchingThreads(self, testlist):
  917. # merge any threads between tests that touch
  918. for phase in self.phases:
  919. list = self.dmesg[phase]['list']
  920. for devname in list:
  921. dev = list[devname]
  922. if 'htmlclass' not in dev or 'kth' not in dev['htmlclass']:
  923. continue
  924. for data in testlist:
  925. data.usurpTouchingThread(devname, dev)
  926. def optimizeDevSrc(self):
  927. # merge any src call loops to reduce timeline size
  928. for phase in self.phases:
  929. list = self.dmesg[phase]['list']
  930. for dev in list:
  931. if 'src' not in list[dev]:
  932. continue
  933. src = list[dev]['src']
  934. p = 0
  935. for e in sorted(src, key=lambda event: event.time):
  936. if not p or not e.repeat(p):
  937. p = e
  938. continue
  939. # e is another iteration of p, move it into p
  940. p.end = e.end
  941. p.length = p.end - p.time
  942. p.count += 1
  943. src.remove(e)
  944. def trimTimeVal(self, t, t0, dT, left):
  945. if left:
  946. if(t > t0):
  947. if(t - dT < t0):
  948. return t0
  949. return t - dT
  950. else:
  951. return t
  952. else:
  953. if(t < t0 + dT):
  954. if(t > t0):
  955. return t0 + dT
  956. return t + dT
  957. else:
  958. return t
  959. def trimTime(self, t0, dT, left):
  960. self.tSuspended = self.trimTimeVal(self.tSuspended, t0, dT, left)
  961. self.tResumed = self.trimTimeVal(self.tResumed, t0, dT, left)
  962. self.start = self.trimTimeVal(self.start, t0, dT, left)
  963. self.tKernSus = self.trimTimeVal(self.tKernSus, t0, dT, left)
  964. self.tKernRes = self.trimTimeVal(self.tKernRes, t0, dT, left)
  965. self.end = self.trimTimeVal(self.end, t0, dT, left)
  966. for phase in self.phases:
  967. p = self.dmesg[phase]
  968. p['start'] = self.trimTimeVal(p['start'], t0, dT, left)
  969. p['end'] = self.trimTimeVal(p['end'], t0, dT, left)
  970. list = p['list']
  971. for name in list:
  972. d = list[name]
  973. d['start'] = self.trimTimeVal(d['start'], t0, dT, left)
  974. d['end'] = self.trimTimeVal(d['end'], t0, dT, left)
  975. if('ftrace' in d):
  976. cg = d['ftrace']
  977. cg.start = self.trimTimeVal(cg.start, t0, dT, left)
  978. cg.end = self.trimTimeVal(cg.end, t0, dT, left)
  979. for line in cg.list:
  980. line.time = self.trimTimeVal(line.time, t0, dT, left)
  981. if('src' in d):
  982. for e in d['src']:
  983. e.time = self.trimTimeVal(e.time, t0, dT, left)
  984. def normalizeTime(self, tZero):
  985. # trim out any standby or freeze clock time
  986. if(self.tSuspended != self.tResumed):
  987. if(self.tResumed > tZero):
  988. self.trimTime(self.tSuspended, \
  989. self.tResumed-self.tSuspended, True)
  990. else:
  991. self.trimTime(self.tSuspended, \
  992. self.tResumed-self.tSuspended, False)
  993. def setPhase(self, phase, ktime, isbegin):
  994. if(isbegin):
  995. self.dmesg[phase]['start'] = ktime
  996. else:
  997. self.dmesg[phase]['end'] = ktime
  998. def dmesgSortVal(self, phase):
  999. return self.dmesg[phase]['order']
  1000. def sortedPhases(self):
  1001. return sorted(self.dmesg, key=self.dmesgSortVal)
  1002. def sortedDevices(self, phase):
  1003. list = self.dmesg[phase]['list']
  1004. slist = []
  1005. tmp = dict()
  1006. for devname in list:
  1007. dev = list[devname]
  1008. tmp[dev['start']] = devname
  1009. for t in sorted(tmp):
  1010. slist.append(tmp[t])
  1011. return slist
  1012. def fixupInitcalls(self, phase):
  1013. # if any calls never returned, clip them at system resume end
  1014. phaselist = self.dmesg[phase]['list']
  1015. for devname in phaselist:
  1016. dev = phaselist[devname]
  1017. if(dev['end'] < 0):
  1018. for p in self.phases:
  1019. if self.dmesg[p]['end'] > dev['start']:
  1020. dev['end'] = self.dmesg[p]['end']
  1021. break
  1022. vprint('%s (%s): callback didnt return' % (devname, phase))
  1023. def deviceFilter(self, devicefilter):
  1024. for phase in self.phases:
  1025. list = self.dmesg[phase]['list']
  1026. rmlist = []
  1027. for name in list:
  1028. keep = False
  1029. for filter in devicefilter:
  1030. if filter in name or \
  1031. ('drv' in list[name] and filter in list[name]['drv']):
  1032. keep = True
  1033. if not keep:
  1034. rmlist.append(name)
  1035. for name in rmlist:
  1036. del list[name]
  1037. def fixupInitcallsThatDidntReturn(self):
  1038. # if any calls never returned, clip them at system resume end
  1039. for phase in self.phases:
  1040. self.fixupInitcalls(phase)
  1041. def phaseOverlap(self, phases):
  1042. rmgroups = []
  1043. newgroup = []
  1044. for group in self.devicegroups:
  1045. for phase in phases:
  1046. if phase not in group:
  1047. continue
  1048. for p in group:
  1049. if p not in newgroup:
  1050. newgroup.append(p)
  1051. if group not in rmgroups:
  1052. rmgroups.append(group)
  1053. for group in rmgroups:
  1054. self.devicegroups.remove(group)
  1055. self.devicegroups.append(newgroup)
  1056. def newActionGlobal(self, name, start, end, pid=-1, color=''):
  1057. # which phase is this device callback or action in
  1058. targetphase = 'none'
  1059. htmlclass = ''
  1060. overlap = 0.0
  1061. phases = []
  1062. for phase in self.phases:
  1063. pstart = self.dmesg[phase]['start']
  1064. pend = self.dmesg[phase]['end']
  1065. # see if the action overlaps this phase
  1066. o = max(0, min(end, pend) - max(start, pstart))
  1067. if o > 0:
  1068. phases.append(phase)
  1069. # set the target phase to the one that overlaps most
  1070. if o > overlap:
  1071. if overlap > 0 and phase == 'post_resume':
  1072. continue
  1073. targetphase = phase
  1074. overlap = o
  1075. # if no target phase was found, pin it to the edge
  1076. if targetphase == 'none':
  1077. p0start = self.dmesg[self.phases[0]]['start']
  1078. if start <= p0start:
  1079. targetphase = self.phases[0]
  1080. else:
  1081. targetphase = self.phases[-1]
  1082. if pid == -2:
  1083. htmlclass = ' bg'
  1084. elif pid == -3:
  1085. htmlclass = ' ps'
  1086. if len(phases) > 1:
  1087. htmlclass = ' bg'
  1088. self.phaseOverlap(phases)
  1089. if targetphase in self.phases:
  1090. newname = self.newAction(targetphase, name, pid, '', start, end, '', htmlclass, color)
  1091. return (targetphase, newname)
  1092. return False
  1093. def newAction(self, phase, name, pid, parent, start, end, drv, htmlclass='', color=''):
  1094. # new device callback for a specific phase
  1095. self.html_device_id += 1
  1096. devid = '%s%d' % (self.idstr, self.html_device_id)
  1097. list = self.dmesg[phase]['list']
  1098. length = -1.0
  1099. if(start >= 0 and end >= 0):
  1100. length = end - start
  1101. if pid == -2:
  1102. i = 2
  1103. origname = name
  1104. while(name in list):
  1105. name = '%s[%d]' % (origname, i)
  1106. i += 1
  1107. list[name] = {'name': name, 'start': start, 'end': end, 'pid': pid,
  1108. 'par': parent, 'length': length, 'row': 0, 'id': devid, 'drv': drv }
  1109. if htmlclass:
  1110. list[name]['htmlclass'] = htmlclass
  1111. if color:
  1112. list[name]['color'] = color
  1113. return name
  1114. def deviceChildren(self, devname, phase):
  1115. devlist = []
  1116. list = self.dmesg[phase]['list']
  1117. for child in list:
  1118. if(list[child]['par'] == devname):
  1119. devlist.append(child)
  1120. return devlist
  1121. def printDetails(self):
  1122. vprint('Timeline Details:')
  1123. vprint(' test start: %f' % self.start)
  1124. vprint('kernel suspend start: %f' % self.tKernSus)
  1125. for phase in self.phases:
  1126. dc = len(self.dmesg[phase]['list'])
  1127. vprint(' %16s: %f - %f (%d devices)' % (phase, \
  1128. self.dmesg[phase]['start'], self.dmesg[phase]['end'], dc))
  1129. vprint(' kernel resume end: %f' % self.tKernRes)
  1130. vprint(' test end: %f' % self.end)
  1131. def deviceChildrenAllPhases(self, devname):
  1132. devlist = []
  1133. for phase in self.phases:
  1134. list = self.deviceChildren(devname, phase)
  1135. for dev in list:
  1136. if dev not in devlist:
  1137. devlist.append(dev)
  1138. return devlist
  1139. def masterTopology(self, name, list, depth):
  1140. node = DeviceNode(name, depth)
  1141. for cname in list:
  1142. # avoid recursions
  1143. if name == cname:
  1144. continue
  1145. clist = self.deviceChildrenAllPhases(cname)
  1146. cnode = self.masterTopology(cname, clist, depth+1)
  1147. node.children.append(cnode)
  1148. return node
  1149. def printTopology(self, node):
  1150. html = ''
  1151. if node.name:
  1152. info = ''
  1153. drv = ''
  1154. for phase in self.phases:
  1155. list = self.dmesg[phase]['list']
  1156. if node.name in list:
  1157. s = list[node.name]['start']
  1158. e = list[node.name]['end']
  1159. if list[node.name]['drv']:
  1160. drv = ' {'+list[node.name]['drv']+'}'
  1161. info += ('<li>%s: %.3fms</li>' % (phase, (e-s)*1000))
  1162. html += '<li><b>'+node.name+drv+'</b>'
  1163. if info:
  1164. html += '<ul>'+info+'</ul>'
  1165. html += '</li>'
  1166. if len(node.children) > 0:
  1167. html += '<ul>'
  1168. for cnode in node.children:
  1169. html += self.printTopology(cnode)
  1170. html += '</ul>'
  1171. return html
  1172. def rootDeviceList(self):
  1173. # list of devices graphed
  1174. real = []
  1175. for phase in self.dmesg:
  1176. list = self.dmesg[phase]['list']
  1177. for dev in list:
  1178. if list[dev]['pid'] >= 0 and dev not in real:
  1179. real.append(dev)
  1180. # list of top-most root devices
  1181. rootlist = []
  1182. for phase in self.dmesg:
  1183. list = self.dmesg[phase]['list']
  1184. for dev in list:
  1185. pdev = list[dev]['par']
  1186. pid = list[dev]['pid']
  1187. if(pid < 0 or re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
  1188. continue
  1189. if pdev and pdev not in real and pdev not in rootlist:
  1190. rootlist.append(pdev)
  1191. return rootlist
  1192. def deviceTopology(self):
  1193. rootlist = self.rootDeviceList()
  1194. master = self.masterTopology('', rootlist, 0)
  1195. return self.printTopology(master)
  1196. def selectTimelineDevices(self, widfmt, tTotal, mindevlen):
  1197. # only select devices that will actually show up in html
  1198. self.tdevlist = dict()
  1199. for phase in self.dmesg:
  1200. devlist = []
  1201. list = self.dmesg[phase]['list']
  1202. for dev in list:
  1203. length = (list[dev]['end'] - list[dev]['start']) * 1000
  1204. width = widfmt % (((list[dev]['end']-list[dev]['start'])*100)/tTotal)
  1205. if width != '0.000000' and length >= mindevlen:
  1206. devlist.append(dev)
  1207. self.tdevlist[phase] = devlist
  1208. def addHorizontalDivider(self, devname, devend):
  1209. phase = 'suspend_prepare'
  1210. self.newAction(phase, devname, -2, '', \
  1211. self.start, devend, '', ' sec', '')
  1212. if phase not in self.tdevlist:
  1213. self.tdevlist[phase] = []
  1214. self.tdevlist[phase].append(devname)
  1215. d = DevItem(0, phase, self.dmesg[phase]['list'][devname])
  1216. return d
  1217. def addProcessUsageEvent(self, name, times):
  1218. # get the start and end times for this process
  1219. maxC = 0
  1220. tlast = 0
  1221. start = -1
  1222. end = -1
  1223. for t in sorted(times):
  1224. if tlast == 0:
  1225. tlast = t
  1226. continue
  1227. if name in self.pstl[t]:
  1228. if start == -1 or tlast < start:
  1229. start = tlast
  1230. if end == -1 or t > end:
  1231. end = t
  1232. tlast = t
  1233. if start == -1 or end == -1:
  1234. return 0
  1235. # add a new action for this process and get the object
  1236. out = self.newActionGlobal(name, start, end, -3)
  1237. if not out:
  1238. return 0
  1239. phase, devname = out
  1240. dev = self.dmesg[phase]['list'][devname]
  1241. # get the cpu exec data
  1242. tlast = 0
  1243. clast = 0
  1244. cpuexec = dict()
  1245. for t in sorted(times):
  1246. if tlast == 0 or t <= start or t > end:
  1247. tlast = t
  1248. continue
  1249. list = self.pstl[t]
  1250. c = 0
  1251. if name in list:
  1252. c = list[name]
  1253. if c > maxC:
  1254. maxC = c
  1255. if c != clast:
  1256. key = (tlast, t)
  1257. cpuexec[key] = c
  1258. tlast = t
  1259. clast = c
  1260. dev['cpuexec'] = cpuexec
  1261. return maxC
  1262. def createProcessUsageEvents(self):
  1263. # get an array of process names
  1264. proclist = []
  1265. for t in self.pstl:
  1266. pslist = self.pstl[t]
  1267. for ps in pslist:
  1268. if ps not in proclist:
  1269. proclist.append(ps)
  1270. # get a list of data points for suspend and resume
  1271. tsus = []
  1272. tres = []
  1273. for t in sorted(self.pstl):
  1274. if t < self.tSuspended:
  1275. tsus.append(t)
  1276. else:
  1277. tres.append(t)
  1278. # process the events for suspend and resume
  1279. if len(proclist) > 0:
  1280. vprint('Process Execution:')
  1281. for ps in proclist:
  1282. c = self.addProcessUsageEvent(ps, tsus)
  1283. if c > 0:
  1284. vprint('%25s (sus): %d' % (ps, c))
  1285. c = self.addProcessUsageEvent(ps, tres)
  1286. if c > 0:
  1287. vprint('%25s (res): %d' % (ps, c))
  1288. # Class: DevFunction
  1289. # Description:
  1290. # A container for kprobe function data we want in the dev timeline
  1291. class DevFunction:
  1292. row = 0
  1293. count = 1
  1294. def __init__(self, name, args, caller, ret, start, end, u, proc, pid, color):
  1295. self.name = name
  1296. self.args = args
  1297. self.caller = caller
  1298. self.ret = ret
  1299. self.time = start
  1300. self.length = end - start
  1301. self.end = end
  1302. self.ubiquitous = u
  1303. self.proc = proc
  1304. self.pid = pid
  1305. self.color = color
  1306. def title(self):
  1307. cnt = ''
  1308. if self.count > 1:
  1309. cnt = '(x%d)' % self.count
  1310. l = '%0.3fms' % (self.length * 1000)
  1311. if self.ubiquitous:
  1312. title = '%s(%s)%s <- %s, %s(%s)' % \
  1313. (self.name, self.args, cnt, self.caller, self.ret, l)
  1314. else:
  1315. title = '%s(%s) %s%s(%s)' % (self.name, self.args, self.ret, cnt, l)
  1316. return title.replace('"', '')
  1317. def text(self):
  1318. if self.count > 1:
  1319. text = '%s(x%d)' % (self.name, self.count)
  1320. else:
  1321. text = self.name
  1322. return text
  1323. def repeat(self, tgt):
  1324. # is the tgt call just a repeat of this call (e.g. are we in a loop)
  1325. dt = self.time - tgt.end
  1326. # only combine calls if -all- attributes are identical
  1327. if tgt.caller == self.caller and \
  1328. tgt.name == self.name and tgt.args == self.args and \
  1329. tgt.proc == self.proc and tgt.pid == self.pid and \
  1330. tgt.ret == self.ret and dt >= 0 and \
  1331. dt <= sysvals.callloopmaxgap and \
  1332. self.length < sysvals.callloopmaxlen:
  1333. return True
  1334. return False
  1335. # Class: FTraceLine
  1336. # Description:
  1337. # A container for a single line of ftrace data. There are six basic types:
  1338. # callgraph line:
  1339. # call: " dpm_run_callback() {"
  1340. # return: " }"
  1341. # leaf: " dpm_run_callback();"
  1342. # trace event:
  1343. # tracing_mark_write: SUSPEND START or RESUME COMPLETE
  1344. # suspend_resume: phase or custom exec block data
  1345. # device_pm_callback: device callback info
  1346. class FTraceLine:
  1347. time = 0.0
  1348. length = 0.0
  1349. fcall = False
  1350. freturn = False
  1351. fevent = False
  1352. fkprobe = False
  1353. depth = 0
  1354. name = ''
  1355. type = ''
  1356. def __init__(self, t, m='', d=''):
  1357. self.time = float(t)
  1358. if not m and not d:
  1359. return
  1360. # is this a trace event
  1361. if(d == 'traceevent' or re.match('^ *\/\* *(?P<msg>.*) \*\/ *$', m)):
  1362. if(d == 'traceevent'):
  1363. # nop format trace event
  1364. msg = m
  1365. else:
  1366. # function_graph format trace event
  1367. em = re.match('^ *\/\* *(?P<msg>.*) \*\/ *$', m)
  1368. msg = em.group('msg')
  1369. emm = re.match('^(?P<call>.*?): (?P<msg>.*)', msg)
  1370. if(emm):
  1371. self.name = emm.group('msg')
  1372. self.type = emm.group('call')
  1373. else:
  1374. self.name = msg
  1375. km = re.match('^(?P<n>.*)_cal$', self.type)
  1376. if km:
  1377. self.fcall = True
  1378. self.fkprobe = True
  1379. self.type = km.group('n')
  1380. return
  1381. km = re.match('^(?P<n>.*)_ret$', self.type)
  1382. if km:
  1383. self.freturn = True
  1384. self.fkprobe = True
  1385. self.type = km.group('n')
  1386. return
  1387. self.fevent = True
  1388. return
  1389. # convert the duration to seconds
  1390. if(d):
  1391. self.length = float(d)/1000000
  1392. # the indentation determines the depth
  1393. match = re.match('^(?P<d> *)(?P<o>.*)$', m)
  1394. if(not match):
  1395. return
  1396. self.depth = self.getDepth(match.group('d'))
  1397. m = match.group('o')
  1398. # function return
  1399. if(m[0] == '}'):
  1400. self.freturn = True
  1401. if(len(m) > 1):
  1402. # includes comment with function name
  1403. match = re.match('^} *\/\* *(?P<n>.*) *\*\/$', m)
  1404. if(match):
  1405. self.name = match.group('n').strip()
  1406. # function call
  1407. else:
  1408. self.fcall = True
  1409. # function call with children
  1410. if(m[-1] == '{'):
  1411. match = re.match('^(?P<n>.*) *\(.*', m)
  1412. if(match):
  1413. self.name = match.group('n').strip()
  1414. # function call with no children (leaf)
  1415. elif(m[-1] == ';'):
  1416. self.freturn = True
  1417. match = re.match('^(?P<n>.*) *\(.*', m)
  1418. if(match):
  1419. self.name = match.group('n').strip()
  1420. # something else (possibly a trace marker)
  1421. else:
  1422. self.name = m
  1423. def getDepth(self, str):
  1424. return len(str)/2
  1425. def debugPrint(self, dev=''):
  1426. if(self.freturn and self.fcall):
  1427. print('%s -- %f (%02d): %s(); (%.3f us)' % (dev, self.time, \
  1428. self.depth, self.name, self.length*1000000))
  1429. elif(self.freturn):
  1430. print('%s -- %f (%02d): %s} (%.3f us)' % (dev, self.time, \
  1431. self.depth, self.name, self.length*1000000))
  1432. else:
  1433. print('%s -- %f (%02d): %s() { (%.3f us)' % (dev, self.time, \
  1434. self.depth, self.name, self.length*1000000))
  1435. def startMarker(self):
  1436. # Is this the starting line of a suspend?
  1437. if not self.fevent:
  1438. return False
  1439. if sysvals.usetracemarkers:
  1440. if(self.name == 'SUSPEND START'):
  1441. return True
  1442. return False
  1443. else:
  1444. if(self.type == 'suspend_resume' and
  1445. re.match('suspend_enter\[.*\] begin', self.name)):
  1446. return True
  1447. return False
  1448. def endMarker(self):
  1449. # Is this the ending line of a resume?
  1450. if not self.fevent:
  1451. return False
  1452. if sysvals.usetracemarkers:
  1453. if(self.name == 'RESUME COMPLETE'):
  1454. return True
  1455. return False
  1456. else:
  1457. if(self.type == 'suspend_resume' and
  1458. re.match('thaw_processes\[.*\] end', self.name)):
  1459. return True
  1460. return False
  1461. # Class: FTraceCallGraph
  1462. # Description:
  1463. # A container for the ftrace callgraph of a single recursive function.
  1464. # This can be a dpm_run_callback, dpm_prepare, or dpm_complete callgraph
  1465. # Each instance is tied to a single device in a single phase, and is
  1466. # comprised of an ordered list of FTraceLine objects
  1467. class FTraceCallGraph:
  1468. start = -1.0
  1469. end = -1.0
  1470. list = []
  1471. invalid = False
  1472. depth = 0
  1473. pid = 0
  1474. def __init__(self, pid):
  1475. self.start = -1.0
  1476. self.end = -1.0
  1477. self.list = []
  1478. self.depth = 0
  1479. self.pid = pid
  1480. def addLine(self, line, debug=False):
  1481. # if this is already invalid, just leave
  1482. if(self.invalid):
  1483. return False
  1484. # invalidate on too much data or bad depth
  1485. if(len(self.list) >= 1000000 or self.depth < 0):
  1486. self.invalidate(line)
  1487. return False
  1488. # compare current depth with this lines pre-call depth
  1489. prelinedep = line.depth
  1490. if(line.freturn and not line.fcall):
  1491. prelinedep += 1
  1492. last = 0
  1493. lasttime = line.time
  1494. virtualfname = 'execution_misalignment'
  1495. if len(self.list) > 0:
  1496. last = self.list[-1]
  1497. lasttime = last.time
  1498. # handle low misalignments by inserting returns
  1499. if prelinedep < self.depth:
  1500. if debug and last:
  1501. print '-------- task %d --------' % self.pid
  1502. last.debugPrint()
  1503. idx = 0
  1504. # add return calls to get the depth down
  1505. while prelinedep < self.depth:
  1506. if debug:
  1507. print 'MISALIGN LOW (add returns): C%d - eC%d' % (self.depth, prelinedep)
  1508. self.depth -= 1
  1509. if idx == 0 and last and last.fcall and not last.freturn:
  1510. # special case, turn last call into a leaf
  1511. last.depth = self.depth
  1512. last.freturn = True
  1513. last.length = line.time - last.time
  1514. if debug:
  1515. last.debugPrint()
  1516. else:
  1517. vline = FTraceLine(lasttime)
  1518. vline.depth = self.depth
  1519. vline.name = virtualfname
  1520. vline.freturn = True
  1521. self.list.append(vline)
  1522. if debug:
  1523. vline.debugPrint()
  1524. idx += 1
  1525. if debug:
  1526. line.debugPrint()
  1527. print ''
  1528. # handle high misalignments by inserting calls
  1529. elif prelinedep > self.depth:
  1530. if debug and last:
  1531. print '-------- task %d --------' % self.pid
  1532. last.debugPrint()
  1533. idx = 0
  1534. # add calls to get the depth up
  1535. while prelinedep > self.depth:
  1536. if debug:
  1537. print 'MISALIGN HIGH (add calls): C%d - eC%d' % (self.depth, prelinedep)
  1538. if idx == 0 and line.freturn and not line.fcall:
  1539. # special case, turn this return into a leaf
  1540. line.fcall = True
  1541. prelinedep -= 1
  1542. else:
  1543. vline = FTraceLine(lasttime)
  1544. vline.depth = self.depth
  1545. vline.name = virtualfname
  1546. vline.fcall = True
  1547. if debug:
  1548. vline.debugPrint()
  1549. self.list.append(vline)
  1550. self.depth += 1
  1551. if not last:
  1552. self.start = vline.time
  1553. idx += 1
  1554. if debug:
  1555. line.debugPrint()
  1556. print ''
  1557. # process the call and set the new depth
  1558. if(line.fcall and not line.freturn):
  1559. self.depth += 1
  1560. elif(line.freturn and not line.fcall):
  1561. self.depth -= 1
  1562. if len(self.list) < 1:
  1563. self.start = line.time
  1564. self.list.append(line)
  1565. if(line.depth == 0 and line.freturn):
  1566. if(self.start < 0):
  1567. self.start = line.time
  1568. self.end = line.time
  1569. if line.fcall:
  1570. self.end += line.length
  1571. if self.list[0].name == virtualfname:
  1572. self.invalid = True
  1573. return True
  1574. return False
  1575. def invalidate(self, line):
  1576. if(len(self.list) > 0):
  1577. first = self.list[0]
  1578. self.list = []
  1579. self.list.append(first)
  1580. self.invalid = True
  1581. id = 'task %s' % (self.pid)
  1582. window = '(%f - %f)' % (self.start, line.time)
  1583. if(self.depth < 0):
  1584. vprint('Too much data for '+id+\
  1585. ' (buffer overflow), ignoring this callback')
  1586. else:
  1587. vprint('Too much data for '+id+\
  1588. ' '+window+', ignoring this callback')
  1589. def slice(self, t0, tN):
  1590. minicg = FTraceCallGraph(0)
  1591. count = -1
  1592. firstdepth = 0
  1593. for l in self.list:
  1594. if(l.time < t0 or l.time > tN):
  1595. continue
  1596. if(count < 0):
  1597. if(not l.fcall or l.name == 'dev_driver_string'):
  1598. continue
  1599. firstdepth = l.depth
  1600. count = 0
  1601. l.depth -= firstdepth
  1602. minicg.addLine(l)
  1603. if((count == 0 and l.freturn and l.fcall) or
  1604. (count > 0 and l.depth <= 0)):
  1605. break
  1606. count += 1
  1607. return minicg
  1608. def repair(self, enddepth):
  1609. # bring the depth back to 0 with additional returns
  1610. fixed = False
  1611. last = self.list[-1]
  1612. for i in reversed(range(enddepth)):
  1613. t = FTraceLine(last.time)
  1614. t.depth = i
  1615. t.freturn = True
  1616. fixed = self.addLine(t)
  1617. if fixed:
  1618. self.end = last.time
  1619. return True
  1620. return False
  1621. def postProcess(self, debug=False):
  1622. stack = dict()
  1623. cnt = 0
  1624. for l in self.list:
  1625. if(l.fcall and not l.freturn):
  1626. stack[l.depth] = l
  1627. cnt += 1
  1628. elif(l.freturn and not l.fcall):
  1629. if(l.depth not in stack):
  1630. if debug:
  1631. print 'Post Process Error: Depth missing'
  1632. l.debugPrint()
  1633. return False
  1634. # transfer total time from return line to call line
  1635. stack[l.depth].length = l.length
  1636. stack.pop(l.depth)
  1637. l.length = 0
  1638. cnt -= 1
  1639. if(cnt == 0):
  1640. # trace caught the whole call tree
  1641. return True
  1642. elif(cnt < 0):
  1643. if debug:
  1644. print 'Post Process Error: Depth is less than 0'
  1645. return False
  1646. # trace ended before call tree finished
  1647. return self.repair(cnt)
  1648. def deviceMatch(self, pid, data):
  1649. found = False
  1650. # add the callgraph data to the device hierarchy
  1651. borderphase = {
  1652. 'dpm_prepare': 'suspend_prepare',
  1653. 'dpm_complete': 'resume_complete'
  1654. }
  1655. if(self.list[0].name in borderphase):
  1656. p = borderphase[self.list[0].name]
  1657. list = data.dmesg[p]['list']
  1658. for devname in list:
  1659. dev = list[devname]
  1660. if(pid == dev['pid'] and
  1661. self.start <= dev['start'] and
  1662. self.end >= dev['end']):
  1663. dev['ftrace'] = self.slice(dev['start'], dev['end'])
  1664. found = True
  1665. return found
  1666. for p in data.phases:
  1667. if(data.dmesg[p]['start'] <= self.start and
  1668. self.start <= data.dmesg[p]['end']):
  1669. list = data.dmesg[p]['list']
  1670. for devname in list:
  1671. dev = list[devname]
  1672. if(pid == dev['pid'] and
  1673. self.start <= dev['start'] and
  1674. self.end >= dev['end']):
  1675. dev['ftrace'] = self
  1676. found = True
  1677. break
  1678. break
  1679. return found
  1680. def newActionFromFunction(self, data):
  1681. name = self.list[0].name
  1682. if name in ['dpm_run_callback', 'dpm_prepare', 'dpm_complete']:
  1683. return
  1684. fs = self.start
  1685. fe = self.end
  1686. if fs < data.start or fe > data.end:
  1687. return
  1688. phase = ''
  1689. for p in data.phases:
  1690. if(data.dmesg[p]['start'] <= self.start and
  1691. self.start < data.dmesg[p]['end']):
  1692. phase = p
  1693. break
  1694. if not phase:
  1695. return
  1696. out = data.newActionGlobal(name, fs, fe, -2)
  1697. if out:
  1698. phase, myname = out
  1699. data.dmesg[phase]['list'][myname]['ftrace'] = self
  1700. def debugPrint(self):
  1701. print('[%f - %f] %s (%d)') % (self.start, self.end, self.list[0].name, self.pid)
  1702. for l in self.list:
  1703. if(l.freturn and l.fcall):
  1704. print('%f (%02d): %s(); (%.3f us)' % (l.time, \
  1705. l.depth, l.name, l.length*1000000))
  1706. elif(l.freturn):
  1707. print('%f (%02d): %s} (%.3f us)' % (l.time, \
  1708. l.depth, l.name, l.length*1000000))
  1709. else:
  1710. print('%f (%02d): %s() { (%.3f us)' % (l.time, \
  1711. l.depth, l.name, l.length*1000000))
  1712. print(' ')
  1713. class DevItem:
  1714. def __init__(self, test, phase, dev):
  1715. self.test = test
  1716. self.phase = phase
  1717. self.dev = dev
  1718. def isa(self, cls):
  1719. if 'htmlclass' in self.dev and cls in self.dev['htmlclass']:
  1720. return True
  1721. return False
  1722. # Class: Timeline
  1723. # Description:
  1724. # A container for a device timeline which calculates
  1725. # all the html properties to display it correctly
  1726. class Timeline:
  1727. html = {}
  1728. height = 0 # total timeline height
  1729. scaleH = 20 # timescale (top) row height
  1730. rowH = 30 # device row height
  1731. bodyH = 0 # body height
  1732. rows = 0 # total timeline rows
  1733. rowlines = dict()
  1734. rowheight = dict()
  1735. def __init__(self, rowheight, scaleheight):
  1736. self.rowH = rowheight
  1737. self.scaleH = scaleheight
  1738. self.html = {
  1739. 'header': '',
  1740. 'timeline': '',
  1741. 'legend': '',
  1742. }
  1743. # Function: getDeviceRows
  1744. # Description:
  1745. # determine how may rows the device funcs will take
  1746. # Arguments:
  1747. # rawlist: the list of devices/actions for a single phase
  1748. # Output:
  1749. # The total number of rows needed to display this phase of the timeline
  1750. def getDeviceRows(self, rawlist):
  1751. # clear all rows and set them to undefined
  1752. sortdict = dict()
  1753. for item in rawlist:
  1754. item.row = -1
  1755. sortdict[item] = item.length
  1756. sortlist = sorted(sortdict, key=sortdict.get, reverse=True)
  1757. remaining = len(sortlist)
  1758. rowdata = dict()
  1759. row = 1
  1760. # try to pack each row with as many ranges as possible
  1761. while(remaining > 0):
  1762. if(row not in rowdata):
  1763. rowdata[row] = []
  1764. for i in sortlist:
  1765. if(i.row >= 0):
  1766. continue
  1767. s = i.time
  1768. e = i.time + i.length
  1769. valid = True
  1770. for ritem in rowdata[row]:
  1771. rs = ritem.time
  1772. re = ritem.time + ritem.length
  1773. if(not (((s <= rs) and (e <= rs)) or
  1774. ((s >= re) and (e >= re)))):
  1775. valid = False
  1776. break
  1777. if(valid):
  1778. rowdata[row].append(i)
  1779. i.row = row
  1780. remaining -= 1
  1781. row += 1
  1782. return row
  1783. # Function: getPhaseRows
  1784. # Description:
  1785. # Organize the timeline entries into the smallest
  1786. # number of rows possible, with no entry overlapping
  1787. # Arguments:
  1788. # devlist: the list of devices/actions in a group of contiguous phases
  1789. # Output:
  1790. # The total number of rows needed to display this phase of the timeline
  1791. def getPhaseRows(self, devlist, row=0):
  1792. # clear all rows and set them to undefined
  1793. remaining = len(devlist)
  1794. rowdata = dict()
  1795. sortdict = dict()
  1796. myphases = []
  1797. # initialize all device rows to -1 and calculate devrows
  1798. for item in devlist:
  1799. dev = item.dev
  1800. tp = (item.test, item.phase)
  1801. if tp not in myphases:
  1802. myphases.append(tp)
  1803. dev['row'] = -1
  1804. # sort by length 1st, then name 2nd
  1805. sortdict[item] = (float(dev['end']) - float(dev['start']), item.dev['name'])
  1806. if 'src' in dev:
  1807. dev['devrows'] = self.getDeviceRows(dev['src'])
  1808. # sort the devlist by length so that large items graph on top
  1809. sortlist = sorted(sortdict, key=sortdict.get, reverse=True)
  1810. orderedlist = []
  1811. for item in sortlist:
  1812. if item.dev['pid'] == -2:
  1813. orderedlist.append(item)
  1814. for item in sortlist:
  1815. if item not in orderedlist:
  1816. orderedlist.append(item)
  1817. # try to pack each row with as many devices as possible
  1818. while(remaining > 0):
  1819. rowheight = 1
  1820. if(row not in rowdata):
  1821. rowdata[row] = []
  1822. for item in orderedlist:
  1823. dev = item.dev
  1824. if(dev['row'] < 0):
  1825. s = dev['start']
  1826. e = dev['end']
  1827. valid = True
  1828. for ritem in rowdata[row]:
  1829. rs = ritem.dev['start']
  1830. re = ritem.dev['end']
  1831. if(not (((s <= rs) and (e <= rs)) or
  1832. ((s >= re) and (e >= re)))):
  1833. valid = False
  1834. break
  1835. if(valid):
  1836. rowdata[row].append(item)
  1837. dev['row'] = row
  1838. remaining -= 1
  1839. if 'devrows' in dev and dev['devrows'] > rowheight:
  1840. rowheight = dev['devrows']
  1841. for t, p in myphases:
  1842. if t not in self.rowlines or t not in self.rowheight:
  1843. self.rowlines[t] = dict()
  1844. self.rowheight[t] = dict()
  1845. if p not in self.rowlines[t] or p not in self.rowheight[t]:
  1846. self.rowlines[t][p] = dict()
  1847. self.rowheight[t][p] = dict()
  1848. rh = self.rowH
  1849. # section headers should use a different row height
  1850. if len(rowdata[row]) == 1 and \
  1851. 'htmlclass' in rowdata[row][0].dev and \
  1852. 'sec' in rowdata[row][0].dev['htmlclass']:
  1853. rh = 15
  1854. self.rowlines[t][p][row] = rowheight
  1855. self.rowheight[t][p][row] = rowheight * rh
  1856. row += 1
  1857. if(row > self.rows):
  1858. self.rows = int(row)
  1859. return row
  1860. def phaseRowHeight(self, test, phase, row):
  1861. return self.rowheight[test][phase][row]
  1862. def phaseRowTop(self, test, phase, row):
  1863. top = 0
  1864. for i in sorted(self.rowheight[test][phase]):
  1865. if i >= row:
  1866. break
  1867. top += self.rowheight[test][phase][i]
  1868. return top
  1869. # Function: calcTotalRows
  1870. # Description:
  1871. # Calculate the heights and offsets for the header and rows
  1872. def calcTotalRows(self):
  1873. maxrows = 0
  1874. standardphases = []
  1875. for t in self.rowlines:
  1876. for p in self.rowlines[t]:
  1877. total = 0
  1878. for i in sorted(self.rowlines[t][p]):
  1879. total += self.rowlines[t][p][i]
  1880. if total > maxrows:
  1881. maxrows = total
  1882. if total == len(self.rowlines[t][p]):
  1883. standardphases.append((t, p))
  1884. self.height = self.scaleH + (maxrows*self.rowH)
  1885. self.bodyH = self.height - self.scaleH
  1886. # if there is 1 line per row, draw them the standard way
  1887. for t, p in standardphases:
  1888. for i in sorted(self.rowheight[t][p]):
  1889. self.rowheight[t][p][i] = self.bodyH/len(self.rowlines[t][p])
  1890. # Function: createTimeScale
  1891. # Description:
  1892. # Create the timescale for a timeline block
  1893. # Arguments:
  1894. # m0: start time (mode begin)
  1895. # mMax: end time (mode end)
  1896. # tTotal: total timeline time
  1897. # mode: suspend or resume
  1898. # Output:
  1899. # The html code needed to display the time scale
  1900. def createTimeScale(self, m0, mMax, tTotal, mode):
  1901. timescale = '<div class="t" style="right:{0}%">{1}</div>\n'
  1902. rline = '<div class="t" style="left:0;border-left:1px solid black;border-right:0;">Resume</div>\n'
  1903. output = '<div class="timescale">\n'
  1904. # set scale for timeline
  1905. mTotal = mMax - m0
  1906. tS = 0.1
  1907. if(tTotal <= 0):
  1908. return output+'</div>\n'
  1909. if(tTotal > 4):
  1910. tS = 1
  1911. divTotal = int(mTotal/tS) + 1
  1912. divEdge = (mTotal - tS*(divTotal-1))*100/mTotal
  1913. for i in range(divTotal):
  1914. htmlline = ''
  1915. if(mode == 'resume'):
  1916. pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal))
  1917. val = '%0.fms' % (float(i)*tS*1000)
  1918. htmlline = timescale.format(pos, val)
  1919. if(i == 0):
  1920. htmlline = rline
  1921. else:
  1922. pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal) - divEdge)
  1923. val = '%0.fms' % (float(i-divTotal+1)*tS*1000)
  1924. if(i == divTotal - 1):
  1925. val = 'Suspend'
  1926. htmlline = timescale.format(pos, val)
  1927. output += htmlline
  1928. output += '</div>\n'
  1929. return output
  1930. # Class: TestProps
  1931. # Description:
  1932. # A list of values describing the properties of these test runs
  1933. class TestProps:
  1934. stamp = ''
  1935. S0i3 = False
  1936. fwdata = []
  1937. ftrace_line_fmt_fg = \
  1938. '^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\
  1939. ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\
  1940. '[ +!#\*@$]*(?P<dur>[0-9\.]*) .*\| (?P<msg>.*)'
  1941. ftrace_line_fmt_nop = \
  1942. ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\[(?P<cpu>[0-9]*)\] *'+\
  1943. '(?P<flags>.{4}) *(?P<time>[0-9\.]*): *'+\
  1944. '(?P<msg>.*)'
  1945. ftrace_line_fmt = ftrace_line_fmt_nop
  1946. cgformat = False
  1947. data = 0
  1948. ktemp = dict()
  1949. def __init__(self):
  1950. self.ktemp = dict()
  1951. def setTracerType(self, tracer):
  1952. if(tracer == 'function_graph'):
  1953. self.cgformat = True
  1954. self.ftrace_line_fmt = self.ftrace_line_fmt_fg
  1955. elif(tracer == 'nop'):
  1956. self.ftrace_line_fmt = self.ftrace_line_fmt_nop
  1957. else:
  1958. doError('Invalid tracer format: [%s]' % tracer)
  1959. # Class: TestRun
  1960. # Description:
  1961. # A container for a suspend/resume test run. This is necessary as
  1962. # there could be more than one, and they need to be separate.
  1963. class TestRun:
  1964. ftemp = dict()
  1965. ttemp = dict()
  1966. data = 0
  1967. def __init__(self, dataobj):
  1968. self.data = dataobj
  1969. self.ftemp = dict()
  1970. self.ttemp = dict()
  1971. class ProcessMonitor:
  1972. proclist = dict()
  1973. running = False
  1974. def procstat(self):
  1975. c = ['cat /proc/[1-9]*/stat 2>/dev/null']
  1976. process = Popen(c, shell=True, stdout=PIPE)
  1977. running = dict()
  1978. for line in process.stdout:
  1979. data = line.split()
  1980. pid = data[0]
  1981. name = re.sub('[()]', '', data[1])
  1982. user = int(data[13])
  1983. kern = int(data[14])
  1984. kjiff = ujiff = 0
  1985. if pid not in self.proclist:
  1986. self.proclist[pid] = {'name' : name, 'user' : user, 'kern' : kern}
  1987. else:
  1988. val = self.proclist[pid]
  1989. ujiff = user - val['user']
  1990. kjiff = kern - val['kern']
  1991. val['user'] = user
  1992. val['kern'] = kern
  1993. if ujiff > 0 or kjiff > 0:
  1994. running[pid] = ujiff + kjiff
  1995. result = process.wait()
  1996. out = ''
  1997. for pid in running:
  1998. jiffies = running[pid]
  1999. val = self.proclist[pid]
  2000. if out:
  2001. out += ','
  2002. out += '%s-%s %d' % (val['name'], pid, jiffies)
  2003. return 'ps - '+out
  2004. def processMonitor(self, tid):
  2005. while self.running:
  2006. out = self.procstat()
  2007. if out:
  2008. sysvals.fsetVal(out, 'trace_marker')
  2009. def start(self):
  2010. self.thread = Thread(target=self.processMonitor, args=(0,))
  2011. self.running = True
  2012. self.thread.start()
  2013. def stop(self):
  2014. self.running = False
  2015. # ----------------- FUNCTIONS --------------------
  2016. # Function: vprint
  2017. # Description:
  2018. # verbose print (prints only with -verbose option)
  2019. # Arguments:
  2020. # msg: the debug/log message to print
  2021. def vprint(msg):
  2022. sysvals.logmsg += msg+'\n'
  2023. if(sysvals.verbose):
  2024. print(msg)
  2025. # Function: parseStamp
  2026. # Description:
  2027. # Pull in the stamp comment line from the data file(s),
  2028. # create the stamp, and add it to the global sysvals object
  2029. # Arguments:
  2030. # m: the valid re.match output for the stamp line
  2031. def parseStamp(line, data):
  2032. m = re.match(sysvals.stampfmt, line)
  2033. data.stamp = {'time': '', 'host': '', 'mode': ''}
  2034. dt = datetime(int(m.group('y'))+2000, int(m.group('m')),
  2035. int(m.group('d')), int(m.group('H')), int(m.group('M')),
  2036. int(m.group('S')))
  2037. data.stamp['time'] = dt.strftime('%B %d %Y, %I:%M:%S %p')
  2038. data.stamp['host'] = m.group('host')
  2039. data.stamp['mode'] = m.group('mode')
  2040. data.stamp['kernel'] = m.group('kernel')
  2041. sysvals.hostname = data.stamp['host']
  2042. sysvals.suspendmode = data.stamp['mode']
  2043. if sysvals.suspendmode == 'command' and sysvals.ftracefile != '':
  2044. modes = ['on', 'freeze', 'standby', 'mem']
  2045. out = Popen(['grep', 'suspend_enter', sysvals.ftracefile],
  2046. stderr=PIPE, stdout=PIPE).stdout.read()
  2047. m = re.match('.* suspend_enter\[(?P<mode>.*)\]', out)
  2048. if m and m.group('mode') in ['1', '2', '3']:
  2049. sysvals.suspendmode = modes[int(m.group('mode'))]
  2050. data.stamp['mode'] = sysvals.suspendmode
  2051. if not sysvals.stamp:
  2052. sysvals.stamp = data.stamp
  2053. # Function: diffStamp
  2054. # Description:
  2055. # compare the host, kernel, and mode fields in 3 stamps
  2056. # Arguments:
  2057. # stamp1: string array with mode, kernel, and host
  2058. # stamp2: string array with mode, kernel, and host
  2059. # Return:
  2060. # True if stamps differ, False if they're the same
  2061. def diffStamp(stamp1, stamp2):
  2062. if 'host' in stamp1 and 'host' in stamp2:
  2063. if stamp1['host'] != stamp2['host']:
  2064. return True
  2065. if 'kernel' in stamp1 and 'kernel' in stamp2:
  2066. if stamp1['kernel'] != stamp2['kernel']:
  2067. return True
  2068. if 'mode' in stamp1 and 'mode' in stamp2:
  2069. if stamp1['mode'] != stamp2['mode']:
  2070. return True
  2071. return False
  2072. # Function: doesTraceLogHaveTraceEvents
  2073. # Description:
  2074. # Quickly determine if the ftrace log has some or all of the trace events
  2075. # required for primary parsing. Set the usetraceevents and/or
  2076. # usetraceeventsonly flags in the global sysvals object
  2077. def doesTraceLogHaveTraceEvents():
  2078. # check for kprobes
  2079. sysvals.usekprobes = False
  2080. out = call('grep -q "_cal: (" '+sysvals.ftracefile, shell=True)
  2081. if(out == 0):
  2082. sysvals.usekprobes = True
  2083. # check for callgraph data on trace event blocks
  2084. out = call('grep -q "_cpu_down()" '+sysvals.ftracefile, shell=True)
  2085. if(out == 0):
  2086. sysvals.usekprobes = True
  2087. out = Popen(['head', '-1', sysvals.ftracefile],
  2088. stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
  2089. m = re.match(sysvals.stampfmt, out)
  2090. if m and m.group('mode') == 'command':
  2091. sysvals.usetraceeventsonly = True
  2092. sysvals.usetraceevents = True
  2093. return
  2094. # figure out what level of trace events are supported
  2095. sysvals.usetraceeventsonly = True
  2096. sysvals.usetraceevents = False
  2097. for e in sysvals.traceevents:
  2098. out = call('grep -q "'+e+': " '+sysvals.ftracefile, shell=True)
  2099. if(out != 0):
  2100. sysvals.usetraceeventsonly = False
  2101. if(e == 'suspend_resume' and out == 0):
  2102. sysvals.usetraceevents = True
  2103. # determine is this log is properly formatted
  2104. for e in ['SUSPEND START', 'RESUME COMPLETE']:
  2105. out = call('grep -q "'+e+'" '+sysvals.ftracefile, shell=True)
  2106. if(out != 0):
  2107. sysvals.usetracemarkers = False
  2108. # Function: appendIncompleteTraceLog
  2109. # Description:
  2110. # [deprecated for kernel 3.15 or newer]
  2111. # Legacy support of ftrace outputs that lack the device_pm_callback
  2112. # and/or suspend_resume trace events. The primary data should be
  2113. # taken from dmesg, and this ftrace is used only for callgraph data
  2114. # or custom actions in the timeline. The data is appended to the Data
  2115. # objects provided.
  2116. # Arguments:
  2117. # testruns: the array of Data objects obtained from parseKernelLog
  2118. def appendIncompleteTraceLog(testruns):
  2119. # create TestRun vessels for ftrace parsing
  2120. testcnt = len(testruns)
  2121. testidx = 0
  2122. testrun = []
  2123. for data in testruns:
  2124. testrun.append(TestRun(data))
  2125. # extract the callgraph and traceevent data
  2126. vprint('Analyzing the ftrace data...')
  2127. tp = TestProps()
  2128. tf = open(sysvals.ftracefile, 'r')
  2129. data = 0
  2130. for line in tf:
  2131. # remove any latent carriage returns
  2132. line = line.replace('\r\n', '')
  2133. # grab the time stamp
  2134. m = re.match(sysvals.stampfmt, line)
  2135. if(m):
  2136. tp.stamp = line
  2137. continue
  2138. # determine the trace data type (required for further parsing)
  2139. m = re.match(sysvals.tracertypefmt, line)
  2140. if(m):
  2141. tp.setTracerType(m.group('t'))
  2142. continue
  2143. # device properties line
  2144. if(re.match(sysvals.devpropfmt, line)):
  2145. devProps(line)
  2146. continue
  2147. # parse only valid lines, if this is not one move on
  2148. m = re.match(tp.ftrace_line_fmt, line)
  2149. if(not m):
  2150. continue
  2151. # gather the basic message data from the line
  2152. m_time = m.group('time')
  2153. m_pid = m.group('pid')
  2154. m_msg = m.group('msg')
  2155. if(tp.cgformat):
  2156. m_param3 = m.group('dur')
  2157. else:
  2158. m_param3 = 'traceevent'
  2159. if(m_time and m_pid and m_msg):
  2160. t = FTraceLine(m_time, m_msg, m_param3)
  2161. pid = int(m_pid)
  2162. else:
  2163. continue
  2164. # the line should be a call, return, or event
  2165. if(not t.fcall and not t.freturn and not t.fevent):
  2166. continue
  2167. # look for the suspend start marker
  2168. if(t.startMarker()):
  2169. data = testrun[testidx].data
  2170. parseStamp(tp.stamp, data)
  2171. data.setStart(t.time)
  2172. continue
  2173. if(not data):
  2174. continue
  2175. # find the end of resume
  2176. if(t.endMarker()):
  2177. data.setEnd(t.time)
  2178. testidx += 1
  2179. if(testidx >= testcnt):
  2180. break
  2181. continue
  2182. # trace event processing
  2183. if(t.fevent):
  2184. # general trace events have two types, begin and end
  2185. if(re.match('(?P<name>.*) begin$', t.name)):
  2186. isbegin = True
  2187. elif(re.match('(?P<name>.*) end$', t.name)):
  2188. isbegin = False
  2189. else:
  2190. continue
  2191. m = re.match('(?P<name>.*)\[(?P<val>[0-9]*)\] .*', t.name)
  2192. if(m):
  2193. val = m.group('val')
  2194. if val == '0':
  2195. name = m.group('name')
  2196. else:
  2197. name = m.group('name')+'['+val+']'
  2198. else:
  2199. m = re.match('(?P<name>.*) .*', t.name)
  2200. name = m.group('name')
  2201. # special processing for trace events
  2202. if re.match('dpm_prepare\[.*', name):
  2203. continue
  2204. elif re.match('machine_suspend.*', name):
  2205. continue
  2206. elif re.match('suspend_enter\[.*', name):
  2207. if(not isbegin):
  2208. data.dmesg['suspend_prepare']['end'] = t.time
  2209. continue
  2210. elif re.match('dpm_suspend\[.*', name):
  2211. if(not isbegin):
  2212. data.dmesg['suspend']['end'] = t.time
  2213. continue
  2214. elif re.match('dpm_suspend_late\[.*', name):
  2215. if(isbegin):
  2216. data.dmesg['suspend_late']['start'] = t.time
  2217. else:
  2218. data.dmesg['suspend_late']['end'] = t.time
  2219. continue
  2220. elif re.match('dpm_suspend_noirq\[.*', name):
  2221. if(isbegin):
  2222. data.dmesg['suspend_noirq']['start'] = t.time
  2223. else:
  2224. data.dmesg['suspend_noirq']['end'] = t.time
  2225. continue
  2226. elif re.match('dpm_resume_noirq\[.*', name):
  2227. if(isbegin):
  2228. data.dmesg['resume_machine']['end'] = t.time
  2229. data.dmesg['resume_noirq']['start'] = t.time
  2230. else:
  2231. data.dmesg['resume_noirq']['end'] = t.time
  2232. continue
  2233. elif re.match('dpm_resume_early\[.*', name):
  2234. if(isbegin):
  2235. data.dmesg['resume_early']['start'] = t.time
  2236. else:
  2237. data.dmesg['resume_early']['end'] = t.time
  2238. continue
  2239. elif re.match('dpm_resume\[.*', name):
  2240. if(isbegin):
  2241. data.dmesg['resume']['start'] = t.time
  2242. else:
  2243. data.dmesg['resume']['end'] = t.time
  2244. continue
  2245. elif re.match('dpm_complete\[.*', name):
  2246. if(isbegin):
  2247. data.dmesg['resume_complete']['start'] = t.time
  2248. else:
  2249. data.dmesg['resume_complete']['end'] = t.time
  2250. continue
  2251. # skip trace events inside devices calls
  2252. if(not data.isTraceEventOutsideDeviceCalls(pid, t.time)):
  2253. continue
  2254. # global events (outside device calls) are simply graphed
  2255. if(isbegin):
  2256. # store each trace event in ttemp
  2257. if(name not in testrun[testidx].ttemp):
  2258. testrun[testidx].ttemp[name] = []
  2259. testrun[testidx].ttemp[name].append(\
  2260. {'begin': t.time, 'end': t.time})
  2261. else:
  2262. # finish off matching trace event in ttemp
  2263. if(name in testrun[testidx].ttemp):
  2264. testrun[testidx].ttemp[name][-1]['end'] = t.time
  2265. # call/return processing
  2266. elif sysvals.usecallgraph:
  2267. # create a callgraph object for the data
  2268. if(pid not in testrun[testidx].ftemp):
  2269. testrun[testidx].ftemp[pid] = []
  2270. testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid))
  2271. # when the call is finished, see which device matches it
  2272. cg = testrun[testidx].ftemp[pid][-1]
  2273. if(cg.addLine(t)):
  2274. testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid))
  2275. tf.close()
  2276. for test in testrun:
  2277. # add the traceevent data to the device hierarchy
  2278. if(sysvals.usetraceevents):
  2279. for name in test.ttemp:
  2280. for event in test.ttemp[name]:
  2281. test.data.newActionGlobal(name, event['begin'], event['end'])
  2282. # add the callgraph data to the device hierarchy
  2283. for pid in test.ftemp:
  2284. for cg in test.ftemp[pid]:
  2285. if len(cg.list) < 1 or cg.invalid:
  2286. continue
  2287. if(not cg.postProcess()):
  2288. id = 'task %s cpu %s' % (pid, m.group('cpu'))
  2289. vprint('Sanity check failed for '+\
  2290. id+', ignoring this callback')
  2291. continue
  2292. callstart = cg.start
  2293. callend = cg.end
  2294. for p in test.data.phases:
  2295. if(test.data.dmesg[p]['start'] <= callstart and
  2296. callstart <= test.data.dmesg[p]['end']):
  2297. list = test.data.dmesg[p]['list']
  2298. for devname in list:
  2299. dev = list[devname]
  2300. if(pid == dev['pid'] and
  2301. callstart <= dev['start'] and
  2302. callend >= dev['end']):
  2303. dev['ftrace'] = cg
  2304. break
  2305. test.data.printDetails()
  2306. # Function: parseTraceLog
  2307. # Description:
  2308. # Analyze an ftrace log output file generated from this app during
  2309. # the execution phase. Used when the ftrace log is the primary data source
  2310. # and includes the suspend_resume and device_pm_callback trace events
  2311. # The ftrace filename is taken from sysvals
  2312. # Output:
  2313. # An array of Data objects
  2314. def parseTraceLog():
  2315. vprint('Analyzing the ftrace data...')
  2316. if(os.path.exists(sysvals.ftracefile) == False):
  2317. doError('%s does not exist' % sysvals.ftracefile)
  2318. sysvals.setupAllKprobes()
  2319. tracewatch = []
  2320. if sysvals.usekprobes:
  2321. tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend',
  2322. 'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON', 'CPU_OFF']
  2323. # extract the callgraph and traceevent data
  2324. tp = TestProps()
  2325. testruns = []
  2326. testdata = []
  2327. testrun = 0
  2328. data = 0
  2329. tf = open(sysvals.ftracefile, 'r')
  2330. phase = 'suspend_prepare'
  2331. for line in tf:
  2332. # remove any latent carriage returns
  2333. line = line.replace('\r\n', '')
  2334. # stamp line: each stamp means a new test run
  2335. m = re.match(sysvals.stampfmt, line)
  2336. if(m):
  2337. tp.stamp = line
  2338. continue
  2339. # firmware line: pull out any firmware data
  2340. m = re.match(sysvals.firmwarefmt, line)
  2341. if(m):
  2342. tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
  2343. continue
  2344. # tracer type line: determine the trace data type
  2345. m = re.match(sysvals.tracertypefmt, line)
  2346. if(m):
  2347. tp.setTracerType(m.group('t'))
  2348. continue
  2349. # device properties line
  2350. if(re.match(sysvals.devpropfmt, line)):
  2351. devProps(line)
  2352. continue
  2353. # ignore all other commented lines
  2354. if line[0] == '#':
  2355. continue
  2356. # ftrace line: parse only valid lines
  2357. m = re.match(tp.ftrace_line_fmt, line)
  2358. if(not m):
  2359. continue
  2360. # gather the basic message data from the line
  2361. m_time = m.group('time')
  2362. m_proc = m.group('proc')
  2363. m_pid = m.group('pid')
  2364. m_msg = m.group('msg')
  2365. if(tp.cgformat):
  2366. m_param3 = m.group('dur')
  2367. else:
  2368. m_param3 = 'traceevent'
  2369. if(m_time and m_pid and m_msg):
  2370. t = FTraceLine(m_time, m_msg, m_param3)
  2371. pid = int(m_pid)
  2372. else:
  2373. continue
  2374. # the line should be a call, return, or event
  2375. if(not t.fcall and not t.freturn and not t.fevent):
  2376. continue
  2377. # find the start of suspend
  2378. if(t.startMarker()):
  2379. phase = 'suspend_prepare'
  2380. data = Data(len(testdata))
  2381. testdata.append(data)
  2382. testrun = TestRun(data)
  2383. testruns.append(testrun)
  2384. parseStamp(tp.stamp, data)
  2385. data.setStart(t.time)
  2386. data.tKernSus = t.time
  2387. continue
  2388. if(not data):
  2389. continue
  2390. # process cpu exec line
  2391. if t.type == 'tracing_mark_write':
  2392. m = re.match(sysvals.procexecfmt, t.name)
  2393. if(m):
  2394. proclist = dict()
  2395. for ps in m.group('ps').split(','):
  2396. val = ps.split()
  2397. if not val:
  2398. continue
  2399. name = val[0].replace('--', '-')
  2400. proclist[name] = int(val[1])
  2401. data.pstl[t.time] = proclist
  2402. continue
  2403. # find the end of resume
  2404. if(t.endMarker()):
  2405. data.setEnd(t.time)
  2406. if data.tKernRes == 0.0:
  2407. data.tKernRes = t.time
  2408. if data.dmesg['resume_complete']['end'] < 0:
  2409. data.dmesg['resume_complete']['end'] = t.time
  2410. if sysvals.suspendmode == 'mem' and len(tp.fwdata) > data.testnumber:
  2411. data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
  2412. if(data.tSuspended != 0 and data.tResumed != 0 and \
  2413. (data.fwSuspend > 0 or data.fwResume > 0)):
  2414. data.fwValid = True
  2415. if(not sysvals.usetracemarkers):
  2416. # no trace markers? then quit and be sure to finish recording
  2417. # the event we used to trigger resume end
  2418. if(len(testrun.ttemp['thaw_processes']) > 0):
  2419. # if an entry exists, assume this is its end
  2420. testrun.ttemp['thaw_processes'][-1]['end'] = t.time
  2421. break
  2422. continue
  2423. # trace event processing
  2424. if(t.fevent):
  2425. if(phase == 'post_resume'):
  2426. data.setEnd(t.time)
  2427. if(t.type == 'suspend_resume'):
  2428. # suspend_resume trace events have two types, begin and end
  2429. if(re.match('(?P<name>.*) begin$', t.name)):
  2430. isbegin = True
  2431. elif(re.match('(?P<name>.*) end$', t.name)):
  2432. isbegin = False
  2433. else:
  2434. continue
  2435. m = re.match('(?P<name>.*)\[(?P<val>[0-9]*)\] .*', t.name)
  2436. if(m):
  2437. val = m.group('val')
  2438. if val == '0':
  2439. name = m.group('name')
  2440. else:
  2441. name = m.group('name')+'['+val+']'
  2442. else:
  2443. m = re.match('(?P<name>.*) .*', t.name)
  2444. name = m.group('name')
  2445. # ignore these events
  2446. if(name.split('[')[0] in tracewatch):
  2447. continue
  2448. # -- phase changes --
  2449. # start of kernel suspend
  2450. if(re.match('suspend_enter\[.*', t.name)):
  2451. if(isbegin):
  2452. data.dmesg[phase]['start'] = t.time
  2453. data.tKernSus = t.time
  2454. continue
  2455. # suspend_prepare start
  2456. elif(re.match('dpm_prepare\[.*', t.name)):
  2457. phase = 'suspend_prepare'
  2458. if(not isbegin):
  2459. data.dmesg[phase]['end'] = t.time
  2460. continue
  2461. # suspend start
  2462. elif(re.match('dpm_suspend\[.*', t.name)):
  2463. phase = 'suspend'
  2464. data.setPhase(phase, t.time, isbegin)
  2465. continue
  2466. # suspend_late start
  2467. elif(re.match('dpm_suspend_late\[.*', t.name)):
  2468. phase = 'suspend_late'
  2469. data.setPhase(phase, t.time, isbegin)
  2470. continue
  2471. # suspend_noirq start
  2472. elif(re.match('dpm_suspend_noirq\[.*', t.name)):
  2473. phase = 'suspend_noirq'
  2474. data.setPhase(phase, t.time, isbegin)
  2475. if(not isbegin):
  2476. phase = 'suspend_machine'
  2477. data.dmesg[phase]['start'] = t.time
  2478. continue
  2479. # suspend_machine/resume_machine
  2480. elif(re.match('machine_suspend\[.*', t.name)):
  2481. if(isbegin):
  2482. phase = 'suspend_machine'
  2483. data.dmesg[phase]['end'] = t.time
  2484. data.tSuspended = t.time
  2485. else:
  2486. if(sysvals.suspendmode in ['mem', 'disk'] and not tp.S0i3):
  2487. data.dmesg['suspend_machine']['end'] = t.time
  2488. data.tSuspended = t.time
  2489. phase = 'resume_machine'
  2490. data.dmesg[phase]['start'] = t.time
  2491. data.tResumed = t.time
  2492. data.tLow = data.tResumed - data.tSuspended
  2493. continue
  2494. # acpi_suspend
  2495. elif(re.match('acpi_suspend\[.*', t.name)):
  2496. # acpi_suspend[0] S0i3
  2497. if(re.match('acpi_suspend\[0\] begin', t.name)):
  2498. if(sysvals.suspendmode == 'mem'):
  2499. tp.S0i3 = True
  2500. data.dmesg['suspend_machine']['end'] = t.time
  2501. data.tSuspended = t.time
  2502. continue
  2503. # resume_noirq start
  2504. elif(re.match('dpm_resume_noirq\[.*', t.name)):
  2505. phase = 'resume_noirq'
  2506. data.setPhase(phase, t.time, isbegin)
  2507. if(isbegin):
  2508. data.dmesg['resume_machine']['end'] = t.time
  2509. continue
  2510. # resume_early start
  2511. elif(re.match('dpm_resume_early\[.*', t.name)):
  2512. phase = 'resume_early'
  2513. data.setPhase(phase, t.time, isbegin)
  2514. continue
  2515. # resume start
  2516. elif(re.match('dpm_resume\[.*', t.name)):
  2517. phase = 'resume'
  2518. data.setPhase(phase, t.time, isbegin)
  2519. continue
  2520. # resume complete start
  2521. elif(re.match('dpm_complete\[.*', t.name)):
  2522. phase = 'resume_complete'
  2523. if(isbegin):
  2524. data.dmesg[phase]['start'] = t.time
  2525. continue
  2526. # skip trace events inside devices calls
  2527. if(not data.isTraceEventOutsideDeviceCalls(pid, t.time)):
  2528. continue
  2529. # global events (outside device calls) are graphed
  2530. if(name not in testrun.ttemp):
  2531. testrun.ttemp[name] = []
  2532. if(isbegin):
  2533. # create a new list entry
  2534. testrun.ttemp[name].append(\
  2535. {'begin': t.time, 'end': t.time, 'pid': pid})
  2536. else:
  2537. if(len(testrun.ttemp[name]) > 0):
  2538. # if an entry exists, assume this is its end
  2539. testrun.ttemp[name][-1]['end'] = t.time
  2540. elif(phase == 'post_resume'):
  2541. # post resume events can just have ends
  2542. testrun.ttemp[name].append({
  2543. 'begin': data.dmesg[phase]['start'],
  2544. 'end': t.time})
  2545. # device callback start
  2546. elif(t.type == 'device_pm_callback_start'):
  2547. m = re.match('(?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*',\
  2548. t.name);
  2549. if(not m):
  2550. continue
  2551. drv = m.group('drv')
  2552. n = m.group('d')
  2553. p = m.group('p')
  2554. if(n and p):
  2555. data.newAction(phase, n, pid, p, t.time, -1, drv)
  2556. if pid not in data.devpids:
  2557. data.devpids.append(pid)
  2558. # device callback finish
  2559. elif(t.type == 'device_pm_callback_end'):
  2560. m = re.match('(?P<drv>.*) (?P<d>.*), err.*', t.name);
  2561. if(not m):
  2562. continue
  2563. n = m.group('d')
  2564. list = data.dmesg[phase]['list']
  2565. if(n in list):
  2566. dev = list[n]
  2567. dev['length'] = t.time - dev['start']
  2568. dev['end'] = t.time
  2569. # kprobe event processing
  2570. elif(t.fkprobe):
  2571. kprobename = t.type
  2572. kprobedata = t.name
  2573. key = (kprobename, pid)
  2574. # displayname is generated from kprobe data
  2575. displayname = ''
  2576. if(t.fcall):
  2577. displayname = sysvals.kprobeDisplayName(kprobename, kprobedata)
  2578. if not displayname:
  2579. continue
  2580. if(key not in tp.ktemp):
  2581. tp.ktemp[key] = []
  2582. tp.ktemp[key].append({
  2583. 'pid': pid,
  2584. 'begin': t.time,
  2585. 'end': t.time,
  2586. 'name': displayname,
  2587. 'cdata': kprobedata,
  2588. 'proc': m_proc,
  2589. })
  2590. elif(t.freturn):
  2591. if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
  2592. continue
  2593. e = tp.ktemp[key][-1]
  2594. if e['begin'] < 0.0 or t.time - e['begin'] < 0.000001:
  2595. tp.ktemp[key].pop()
  2596. else:
  2597. e['end'] = t.time
  2598. e['rdata'] = kprobedata
  2599. # end of kernel resume
  2600. if(kprobename == 'pm_notifier_call_chain' or \
  2601. kprobename == 'pm_restore_console'):
  2602. data.dmesg[phase]['end'] = t.time
  2603. data.tKernRes = t.time
  2604. # callgraph processing
  2605. elif sysvals.usecallgraph:
  2606. # create a callgraph object for the data
  2607. key = (m_proc, pid)
  2608. if(key not in testrun.ftemp):
  2609. testrun.ftemp[key] = []
  2610. testrun.ftemp[key].append(FTraceCallGraph(pid))
  2611. # when the call is finished, see which device matches it
  2612. cg = testrun.ftemp[key][-1]
  2613. if(cg.addLine(t)):
  2614. testrun.ftemp[key].append(FTraceCallGraph(pid))
  2615. tf.close()
  2616. if sysvals.suspendmode == 'command':
  2617. for test in testruns:
  2618. for p in test.data.phases:
  2619. if p == 'suspend_prepare':
  2620. test.data.dmesg[p]['start'] = test.data.start
  2621. test.data.dmesg[p]['end'] = test.data.end
  2622. else:
  2623. test.data.dmesg[p]['start'] = test.data.end
  2624. test.data.dmesg[p]['end'] = test.data.end
  2625. test.data.tSuspended = test.data.end
  2626. test.data.tResumed = test.data.end
  2627. test.data.tLow = 0
  2628. test.data.fwValid = False
  2629. # dev source and procmon events can be unreadable with mixed phase height
  2630. if sysvals.usedevsrc or sysvals.useprocmon:
  2631. sysvals.mixedphaseheight = False
  2632. for i in range(len(testruns)):
  2633. test = testruns[i]
  2634. data = test.data
  2635. # find the total time range for this test (begin, end)
  2636. tlb, tle = data.start, data.end
  2637. if i < len(testruns) - 1:
  2638. tle = testruns[i+1].data.start
  2639. # add the process usage data to the timeline
  2640. if sysvals.useprocmon:
  2641. data.createProcessUsageEvents()
  2642. # add the traceevent data to the device hierarchy
  2643. if(sysvals.usetraceevents):
  2644. # add actual trace funcs
  2645. for name in test.ttemp:
  2646. for event in test.ttemp[name]:
  2647. data.newActionGlobal(name, event['begin'], event['end'], event['pid'])
  2648. # add the kprobe based virtual tracefuncs as actual devices
  2649. for key in tp.ktemp:
  2650. name, pid = key
  2651. if name not in sysvals.tracefuncs:
  2652. continue
  2653. for e in tp.ktemp[key]:
  2654. kb, ke = e['begin'], e['end']
  2655. if kb == ke or tlb > kb or tle <= kb:
  2656. continue
  2657. color = sysvals.kprobeColor(name)
  2658. data.newActionGlobal(e['name'], kb, ke, pid, color)
  2659. # add config base kprobes and dev kprobes
  2660. if sysvals.usedevsrc:
  2661. for key in tp.ktemp:
  2662. name, pid = key
  2663. if name in sysvals.tracefuncs or name not in sysvals.dev_tracefuncs:
  2664. continue
  2665. for e in tp.ktemp[key]:
  2666. kb, ke = e['begin'], e['end']
  2667. if kb == ke or tlb > kb or tle <= kb:
  2668. continue
  2669. data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
  2670. ke, e['cdata'], e['rdata'])
  2671. if sysvals.usecallgraph:
  2672. # add the callgraph data to the device hierarchy
  2673. sortlist = dict()
  2674. for key in test.ftemp:
  2675. proc, pid = key
  2676. for cg in test.ftemp[key]:
  2677. if len(cg.list) < 1 or cg.invalid:
  2678. continue
  2679. if(not cg.postProcess()):
  2680. id = 'task %s' % (pid)
  2681. vprint('Sanity check failed for '+\
  2682. id+', ignoring this callback')
  2683. continue
  2684. # match cg data to devices
  2685. if sysvals.suspendmode == 'command' or not cg.deviceMatch(pid, data):
  2686. sortkey = '%f%f%d' % (cg.start, cg.end, pid)
  2687. sortlist[sortkey] = cg
  2688. # create blocks for orphan cg data
  2689. for sortkey in sorted(sortlist):
  2690. cg = sortlist[sortkey]
  2691. name = cg.list[0].name
  2692. if sysvals.isCallgraphFunc(name):
  2693. vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name))
  2694. cg.newActionFromFunction(data)
  2695. if sysvals.suspendmode == 'command':
  2696. for data in testdata:
  2697. data.printDetails()
  2698. return testdata
  2699. # fill in any missing phases
  2700. for data in testdata:
  2701. lp = data.phases[0]
  2702. for p in data.phases:
  2703. if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0):
  2704. vprint('WARNING: phase "%s" is missing!' % p)
  2705. if(data.dmesg[p]['start'] < 0):
  2706. data.dmesg[p]['start'] = data.dmesg[lp]['end']
  2707. if(p == 'resume_machine'):
  2708. data.tSuspended = data.dmesg[lp]['end']
  2709. data.tResumed = data.dmesg[lp]['end']
  2710. data.tLow = 0
  2711. if(data.dmesg[p]['end'] < 0):
  2712. data.dmesg[p]['end'] = data.dmesg[p]['start']
  2713. if(p != lp and not ('machine' in p and 'machine' in lp)):
  2714. data.dmesg[lp]['end'] = data.dmesg[p]['start']
  2715. lp = p
  2716. if(len(sysvals.devicefilter) > 0):
  2717. data.deviceFilter(sysvals.devicefilter)
  2718. data.fixupInitcallsThatDidntReturn()
  2719. if sysvals.usedevsrc:
  2720. data.optimizeDevSrc()
  2721. data.printDetails()
  2722. # x2: merge any overlapping devices between test runs
  2723. if sysvals.usedevsrc and len(testdata) > 1:
  2724. tc = len(testdata)
  2725. for i in range(tc - 1):
  2726. devlist = testdata[i].overflowDevices()
  2727. for j in range(i + 1, tc):
  2728. testdata[j].mergeOverlapDevices(devlist)
  2729. testdata[0].stitchTouchingThreads(testdata[1:])
  2730. return testdata
  2731. # Function: loadKernelLog
  2732. # Description:
  2733. # [deprecated for kernel 3.15.0 or newer]
  2734. # load the dmesg file into memory and fix up any ordering issues
  2735. # The dmesg filename is taken from sysvals
  2736. # Output:
  2737. # An array of empty Data objects with only their dmesgtext attributes set
  2738. def loadKernelLog(justtext=False):
  2739. vprint('Analyzing the dmesg data...')
  2740. if(os.path.exists(sysvals.dmesgfile) == False):
  2741. doError('%s does not exist' % sysvals.dmesgfile)
  2742. if justtext:
  2743. dmesgtext = []
  2744. # there can be multiple test runs in a single file
  2745. tp = TestProps()
  2746. tp.stamp = datetime.now().strftime('# suspend-%m%d%y-%H%M%S localhost mem unknown')
  2747. testruns = []
  2748. data = 0
  2749. lf = open(sysvals.dmesgfile, 'r')
  2750. for line in lf:
  2751. line = line.replace('\r\n', '')
  2752. idx = line.find('[')
  2753. if idx > 1:
  2754. line = line[idx:]
  2755. m = re.match(sysvals.stampfmt, line)
  2756. if(m):
  2757. tp.stamp = line
  2758. continue
  2759. m = re.match(sysvals.firmwarefmt, line)
  2760. if(m):
  2761. tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
  2762. continue
  2763. m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
  2764. if(not m):
  2765. continue
  2766. msg = m.group("msg")
  2767. if justtext:
  2768. dmesgtext.append(line)
  2769. continue
  2770. if(re.match('PM: Syncing filesystems.*', msg)):
  2771. if(data):
  2772. testruns.append(data)
  2773. data = Data(len(testruns))
  2774. parseStamp(tp.stamp, data)
  2775. if len(tp.fwdata) > data.testnumber:
  2776. data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
  2777. if(data.fwSuspend > 0 or data.fwResume > 0):
  2778. data.fwValid = True
  2779. if(not data):
  2780. continue
  2781. m = re.match('.* *(?P<k>[0-9]\.[0-9]{2}\.[0-9]-.*) .*', msg)
  2782. if(m):
  2783. sysvals.stamp['kernel'] = m.group('k')
  2784. m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
  2785. if(m):
  2786. sysvals.stamp['mode'] = sysvals.suspendmode = m.group('m')
  2787. data.dmesgtext.append(line)
  2788. lf.close()
  2789. if justtext:
  2790. return dmesgtext
  2791. if data:
  2792. testruns.append(data)
  2793. if len(testruns) < 1:
  2794. doError(' dmesg log has no suspend/resume data: %s' \
  2795. % sysvals.dmesgfile)
  2796. # fix lines with same timestamp/function with the call and return swapped
  2797. for data in testruns:
  2798. last = ''
  2799. for line in data.dmesgtext:
  2800. mc = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) calling '+\
  2801. '(?P<f>.*)\+ @ .*, parent: .*', line)
  2802. mr = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) call '+\
  2803. '(?P<f>.*)\+ returned .* after (?P<dt>.*) usecs', last)
  2804. if(mc and mr and (mc.group('t') == mr.group('t')) and
  2805. (mc.group('f') == mr.group('f'))):
  2806. i = data.dmesgtext.index(last)
  2807. j = data.dmesgtext.index(line)
  2808. data.dmesgtext[i] = line
  2809. data.dmesgtext[j] = last
  2810. last = line
  2811. return testruns
  2812. # Function: parseKernelLog
  2813. # Description:
  2814. # [deprecated for kernel 3.15.0 or newer]
  2815. # Analyse a dmesg log output file generated from this app during
  2816. # the execution phase. Create a set of device structures in memory
  2817. # for subsequent formatting in the html output file
  2818. # This call is only for legacy support on kernels where the ftrace
  2819. # data lacks the suspend_resume or device_pm_callbacks trace events.
  2820. # Arguments:
  2821. # data: an empty Data object (with dmesgtext) obtained from loadKernelLog
  2822. # Output:
  2823. # The filled Data object
  2824. def parseKernelLog(data):
  2825. phase = 'suspend_runtime'
  2826. if(data.fwValid):
  2827. vprint('Firmware Suspend = %u ns, Firmware Resume = %u ns' % \
  2828. (data.fwSuspend, data.fwResume))
  2829. # dmesg phase match table
  2830. dm = {
  2831. 'suspend_prepare': 'PM: Syncing filesystems.*',
  2832. 'suspend': 'PM: Entering [a-z]* sleep.*',
  2833. 'suspend_late': 'PM: suspend of devices complete after.*',
  2834. 'suspend_noirq': 'PM: late suspend of devices complete after.*',
  2835. 'suspend_machine': 'PM: noirq suspend of devices complete after.*',
  2836. 'resume_machine': 'ACPI: Low-level resume complete.*',
  2837. 'resume_noirq': 'ACPI: Waking up from system sleep state.*',
  2838. 'resume_early': 'PM: noirq resume of devices complete after.*',
  2839. 'resume': 'PM: early resume of devices complete after.*',
  2840. 'resume_complete': 'PM: resume of devices complete after.*',
  2841. 'post_resume': '.*Restarting tasks \.\.\..*',
  2842. }
  2843. if(sysvals.suspendmode == 'standby'):
  2844. dm['resume_machine'] = 'PM: Restoring platform NVS memory'
  2845. elif(sysvals.suspendmode == 'disk'):
  2846. dm['suspend_late'] = 'PM: freeze of devices complete after.*'
  2847. dm['suspend_noirq'] = 'PM: late freeze of devices complete after.*'
  2848. dm['suspend_machine'] = 'PM: noirq freeze of devices complete after.*'
  2849. dm['resume_machine'] = 'PM: Restoring platform NVS memory'
  2850. dm['resume_early'] = 'PM: noirq restore of devices complete after.*'
  2851. dm['resume'] = 'PM: early restore of devices complete after.*'
  2852. dm['resume_complete'] = 'PM: restore of devices complete after.*'
  2853. elif(sysvals.suspendmode == 'freeze'):
  2854. dm['resume_machine'] = 'ACPI: resume from mwait'
  2855. # action table (expected events that occur and show up in dmesg)
  2856. at = {
  2857. 'sync_filesystems': {
  2858. 'smsg': 'PM: Syncing filesystems.*',
  2859. 'emsg': 'PM: Preparing system for mem sleep.*' },
  2860. 'freeze_user_processes': {
  2861. 'smsg': 'Freezing user space processes .*',
  2862. 'emsg': 'Freezing remaining freezable tasks.*' },
  2863. 'freeze_tasks': {
  2864. 'smsg': 'Freezing remaining freezable tasks.*',
  2865. 'emsg': 'PM: Entering (?P<mode>[a-z,A-Z]*) sleep.*' },
  2866. 'ACPI prepare': {
  2867. 'smsg': 'ACPI: Preparing to enter system sleep state.*',
  2868. 'emsg': 'PM: Saving platform NVS memory.*' },
  2869. 'PM vns': {
  2870. 'smsg': 'PM: Saving platform NVS memory.*',
  2871. 'emsg': 'Disabling non-boot CPUs .*' },
  2872. }
  2873. t0 = -1.0
  2874. cpu_start = -1.0
  2875. prevktime = -1.0
  2876. actions = dict()
  2877. for line in data.dmesgtext:
  2878. # parse each dmesg line into the time and message
  2879. m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
  2880. if(m):
  2881. val = m.group('ktime')
  2882. try:
  2883. ktime = float(val)
  2884. except:
  2885. continue
  2886. msg = m.group('msg')
  2887. # initialize data start to first line time
  2888. if t0 < 0:
  2889. data.setStart(ktime)
  2890. t0 = ktime
  2891. else:
  2892. continue
  2893. # hack for determining resume_machine end for freeze
  2894. if(not sysvals.usetraceevents and sysvals.suspendmode == 'freeze' \
  2895. and phase == 'resume_machine' and \
  2896. re.match('calling (?P<f>.*)\+ @ .*, parent: .*', msg)):
  2897. data.dmesg['resume_machine']['end'] = ktime
  2898. phase = 'resume_noirq'
  2899. data.dmesg[phase]['start'] = ktime
  2900. # suspend start
  2901. if(re.match(dm['suspend_prepare'], msg)):
  2902. phase = 'suspend_prepare'
  2903. data.dmesg[phase]['start'] = ktime
  2904. data.setStart(ktime)
  2905. data.tKernSus = ktime
  2906. # suspend start
  2907. elif(re.match(dm['suspend'], msg)):
  2908. data.dmesg['suspend_prepare']['end'] = ktime
  2909. phase = 'suspend'
  2910. data.dmesg[phase]['start'] = ktime
  2911. # suspend_late start
  2912. elif(re.match(dm['suspend_late'], msg)):
  2913. data.dmesg['suspend']['end'] = ktime
  2914. phase = 'suspend_late'
  2915. data.dmesg[phase]['start'] = ktime
  2916. # suspend_noirq start
  2917. elif(re.match(dm['suspend_noirq'], msg)):
  2918. data.dmesg['suspend_late']['end'] = ktime
  2919. phase = 'suspend_noirq'
  2920. data.dmesg[phase]['start'] = ktime
  2921. # suspend_machine start
  2922. elif(re.match(dm['suspend_machine'], msg)):
  2923. data.dmesg['suspend_noirq']['end'] = ktime
  2924. phase = 'suspend_machine'
  2925. data.dmesg[phase]['start'] = ktime
  2926. # resume_machine start
  2927. elif(re.match(dm['resume_machine'], msg)):
  2928. if(sysvals.suspendmode in ['freeze', 'standby']):
  2929. data.tSuspended = prevktime
  2930. data.dmesg['suspend_machine']['end'] = prevktime
  2931. else:
  2932. data.tSuspended = ktime
  2933. data.dmesg['suspend_machine']['end'] = ktime
  2934. phase = 'resume_machine'
  2935. data.tResumed = ktime
  2936. data.tLow = data.tResumed - data.tSuspended
  2937. data.dmesg[phase]['start'] = ktime
  2938. # resume_noirq start
  2939. elif(re.match(dm['resume_noirq'], msg)):
  2940. data.dmesg['resume_machine']['end'] = ktime
  2941. phase = 'resume_noirq'
  2942. data.dmesg[phase]['start'] = ktime
  2943. # resume_early start
  2944. elif(re.match(dm['resume_early'], msg)):
  2945. data.dmesg['resume_noirq']['end'] = ktime
  2946. phase = 'resume_early'
  2947. data.dmesg[phase]['start'] = ktime
  2948. # resume start
  2949. elif(re.match(dm['resume'], msg)):
  2950. data.dmesg['resume_early']['end'] = ktime
  2951. phase = 'resume'
  2952. data.dmesg[phase]['start'] = ktime
  2953. # resume complete start
  2954. elif(re.match(dm['resume_complete'], msg)):
  2955. data.dmesg['resume']['end'] = ktime
  2956. phase = 'resume_complete'
  2957. data.dmesg[phase]['start'] = ktime
  2958. # post resume start
  2959. elif(re.match(dm['post_resume'], msg)):
  2960. data.dmesg['resume_complete']['end'] = ktime
  2961. data.setEnd(ktime)
  2962. data.tKernRes = ktime
  2963. break
  2964. # -- device callbacks --
  2965. if(phase in data.phases):
  2966. # device init call
  2967. if(re.match('calling (?P<f>.*)\+ @ .*, parent: .*', msg)):
  2968. sm = re.match('calling (?P<f>.*)\+ @ '+\
  2969. '(?P<n>.*), parent: (?P<p>.*)', msg);
  2970. f = sm.group('f')
  2971. n = sm.group('n')
  2972. p = sm.group('p')
  2973. if(f and n and p):
  2974. data.newAction(phase, f, int(n), p, ktime, -1, '')
  2975. # device init return
  2976. elif(re.match('call (?P<f>.*)\+ returned .* after '+\
  2977. '(?P<t>.*) usecs', msg)):
  2978. sm = re.match('call (?P<f>.*)\+ returned .* after '+\
  2979. '(?P<t>.*) usecs(?P<a>.*)', msg);
  2980. f = sm.group('f')
  2981. t = sm.group('t')
  2982. list = data.dmesg[phase]['list']
  2983. if(f in list):
  2984. dev = list[f]
  2985. dev['length'] = int(t)
  2986. dev['end'] = ktime
  2987. # if trace events are not available, these are better than nothing
  2988. if(not sysvals.usetraceevents):
  2989. # look for known actions
  2990. for a in at:
  2991. if(re.match(at[a]['smsg'], msg)):
  2992. if(a not in actions):
  2993. actions[a] = []
  2994. actions[a].append({'begin': ktime, 'end': ktime})
  2995. if(re.match(at[a]['emsg'], msg)):
  2996. if(a in actions):
  2997. actions[a][-1]['end'] = ktime
  2998. # now look for CPU on/off events
  2999. if(re.match('Disabling non-boot CPUs .*', msg)):
  3000. # start of first cpu suspend
  3001. cpu_start = ktime
  3002. elif(re.match('Enabling non-boot CPUs .*', msg)):
  3003. # start of first cpu resume
  3004. cpu_start = ktime
  3005. elif(re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg)):
  3006. # end of a cpu suspend, start of the next
  3007. m = re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg)
  3008. cpu = 'CPU'+m.group('cpu')
  3009. if(cpu not in actions):
  3010. actions[cpu] = []
  3011. actions[cpu].append({'begin': cpu_start, 'end': ktime})
  3012. cpu_start = ktime
  3013. elif(re.match('CPU(?P<cpu>[0-9]*) is up', msg)):
  3014. # end of a cpu resume, start of the next
  3015. m = re.match('CPU(?P<cpu>[0-9]*) is up', msg)
  3016. cpu = 'CPU'+m.group('cpu')
  3017. if(cpu not in actions):
  3018. actions[cpu] = []
  3019. actions[cpu].append({'begin': cpu_start, 'end': ktime})
  3020. cpu_start = ktime
  3021. prevktime = ktime
  3022. # fill in any missing phases
  3023. lp = data.phases[0]
  3024. for p in data.phases:
  3025. if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0):
  3026. print('WARNING: phase "%s" is missing, something went wrong!' % p)
  3027. print(' In %s, this dmesg line denotes the start of %s:' % \
  3028. (sysvals.suspendmode, p))
  3029. print(' "%s"' % dm[p])
  3030. if(data.dmesg[p]['start'] < 0):
  3031. data.dmesg[p]['start'] = data.dmesg[lp]['end']
  3032. if(p == 'resume_machine'):
  3033. data.tSuspended = data.dmesg[lp]['end']
  3034. data.tResumed = data.dmesg[lp]['end']
  3035. data.tLow = 0
  3036. if(data.dmesg[p]['end'] < 0):
  3037. data.dmesg[p]['end'] = data.dmesg[p]['start']
  3038. lp = p
  3039. # fill in any actions we've found
  3040. for name in actions:
  3041. for event in actions[name]:
  3042. data.newActionGlobal(name, event['begin'], event['end'])
  3043. data.printDetails()
  3044. if(len(sysvals.devicefilter) > 0):
  3045. data.deviceFilter(sysvals.devicefilter)
  3046. data.fixupInitcallsThatDidntReturn()
  3047. return True
  3048. # Function: createHTMLSummarySimple
  3049. # Description:
  3050. # Create summary html file for a series of tests
  3051. # Arguments:
  3052. # testruns: array of Data objects from parseTraceLog
  3053. def createHTMLSummarySimple(testruns, htmlfile):
  3054. # print out the basic summary of all the tests
  3055. hf = open(htmlfile, 'w')
  3056. # write the html header first (html head, css code, up to body start)
  3057. html = '<!DOCTYPE html>\n<html>\n<head>\n\
  3058. <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
  3059. <title>AnalyzeSuspend Summary</title>\n\
  3060. <style type=\'text/css\'>\n\
  3061. body {overflow-y: scroll;}\n\
  3062. .stamp {width: 100%;text-align:center;background-color:#495E09;line-height:30px;color:white;font: 25px Arial;}\n\
  3063. table {width:100%;border-collapse: collapse;}\n\
  3064. .summary {font: 22px Arial;border:1px solid;}\n\
  3065. th {border: 1px solid black;background-color:#A7C942;color:white;}\n\
  3066. td {text-align: center;}\n\
  3067. tr.alt td {background-color:#EAF2D3;}\n\
  3068. tr.avg td {background-color:#BDE34C;}\n\
  3069. a:link {color: #90B521;}\n\
  3070. a:visited {color: #495E09;}\n\
  3071. a:hover {color: #B1DF28;}\n\
  3072. a:active {color: #FFFFFF;}\n\
  3073. </style>\n</head>\n<body>\n'
  3074. # group test header
  3075. count = len(testruns)
  3076. headline_stamp = '<div class="stamp">{0} {1} {2} {3} ({4} tests)</div>\n'
  3077. html += headline_stamp.format(sysvals.stamp['host'],
  3078. sysvals.stamp['kernel'], sysvals.stamp['mode'],
  3079. sysvals.stamp['time'], count)
  3080. # check to see if all the tests have the same value
  3081. stampcolumns = False
  3082. for data in testruns:
  3083. if diffStamp(sysvals.stamp, data.stamp):
  3084. stampcolumns = True
  3085. break
  3086. th = '\t<th>{0}</th>\n'
  3087. td = '\t<td>{0}</td>\n'
  3088. tdlink = '\t<td><a href="{0}">Click Here</a></td>\n'
  3089. # table header
  3090. html += '<table class="summary">\n<tr>\n'
  3091. html += th.format("Test #")
  3092. if stampcolumns:
  3093. html += th.format("Hostname")
  3094. html += th.format("Kernel Version")
  3095. html += th.format("Suspend Mode")
  3096. html += th.format("Test Time")
  3097. html += th.format("Suspend Time")
  3098. html += th.format("Resume Time")
  3099. html += th.format("Detail")
  3100. html += '</tr>\n'
  3101. # test data, 1 row per test
  3102. sTimeAvg = 0.0
  3103. rTimeAvg = 0.0
  3104. num = 1
  3105. for data in testruns:
  3106. # data.end is the end of post_resume
  3107. resumeEnd = data.dmesg['resume_complete']['end']
  3108. if num % 2 == 1:
  3109. html += '<tr class="alt">\n'
  3110. else:
  3111. html += '<tr>\n'
  3112. # test num
  3113. html += td.format("test %d" % num)
  3114. num += 1
  3115. if stampcolumns:
  3116. # host name
  3117. val = "unknown"
  3118. if('host' in data.stamp):
  3119. val = data.stamp['host']
  3120. html += td.format(val)
  3121. # host kernel
  3122. val = "unknown"
  3123. if('kernel' in data.stamp):
  3124. val = data.stamp['kernel']
  3125. html += td.format(val)
  3126. # suspend mode
  3127. val = "unknown"
  3128. if('mode' in data.stamp):
  3129. val = data.stamp['mode']
  3130. html += td.format(val)
  3131. # test time
  3132. val = "unknown"
  3133. if('time' in data.stamp):
  3134. val = data.stamp['time']
  3135. html += td.format(val)
  3136. # suspend time
  3137. sTime = (data.tSuspended - data.start)*1000
  3138. sTimeAvg += sTime
  3139. html += td.format("%3.3f ms" % sTime)
  3140. # resume time
  3141. rTime = (resumeEnd - data.tResumed)*1000
  3142. rTimeAvg += rTime
  3143. html += td.format("%3.3f ms" % rTime)
  3144. # link to the output html
  3145. html += tdlink.format(data.outfile)
  3146. html += '</tr>\n'
  3147. # last line: test average
  3148. if(count > 0):
  3149. sTimeAvg /= count
  3150. rTimeAvg /= count
  3151. html += '<tr class="avg">\n'
  3152. html += td.format('Average') # name
  3153. if stampcolumns:
  3154. html += td.format('') # host
  3155. html += td.format('') # kernel
  3156. html += td.format('') # mode
  3157. html += td.format('') # time
  3158. html += td.format("%3.3f ms" % sTimeAvg) # suspend time
  3159. html += td.format("%3.3f ms" % rTimeAvg) # resume time
  3160. html += td.format('') # output link
  3161. html += '</tr>\n'
  3162. # flush the data to file
  3163. hf.write(html+'</table>\n')
  3164. hf.write('</body>\n</html>\n')
  3165. hf.close()
  3166. def htmlTitle():
  3167. modename = {
  3168. 'freeze': 'Freeze (S0)',
  3169. 'standby': 'Standby (S1)',
  3170. 'mem': 'Suspend (S3)',
  3171. 'disk': 'Hibernate (S4)'
  3172. }
  3173. kernel = sysvals.stamp['kernel']
  3174. host = sysvals.hostname[0].upper()+sysvals.hostname[1:]
  3175. mode = sysvals.suspendmode
  3176. if sysvals.suspendmode in modename:
  3177. mode = modename[sysvals.suspendmode]
  3178. return host+' '+mode+' '+kernel
  3179. def ordinal(value):
  3180. suffix = 'th'
  3181. if value < 10 or value > 19:
  3182. if value % 10 == 1:
  3183. suffix = 'st'
  3184. elif value % 10 == 2:
  3185. suffix = 'nd'
  3186. elif value % 10 == 3:
  3187. suffix = 'rd'
  3188. return '%d%s' % (value, suffix)
  3189. # Function: createHTML
  3190. # Description:
  3191. # Create the output html file from the resident test data
  3192. # Arguments:
  3193. # testruns: array of Data objects from parseKernelLog or parseTraceLog
  3194. # Output:
  3195. # True if the html file was created, false if it failed
  3196. def createHTML(testruns):
  3197. if len(testruns) < 1:
  3198. print('ERROR: Not enough test data to build a timeline')
  3199. return
  3200. kerror = False
  3201. for data in testruns:
  3202. if data.kerror:
  3203. kerror = True
  3204. data.normalizeTime(testruns[-1].tSuspended)
  3205. x2changes = ['', 'absolute']
  3206. if len(testruns) > 1:
  3207. x2changes = ['1', 'relative']
  3208. # html function templates
  3209. headline_version = '<div class="version"><a href="https://01.org/suspendresume">AnalyzeSuspend v%s</a></div>' % sysvals.version
  3210. headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n'
  3211. html_devlist1 = '<button id="devlist1" class="devlist" style="float:left;">Device Detail%s</button>' % x2changes[0]
  3212. html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
  3213. html_devlist2 = '<button id="devlist2" class="devlist" style="float:right;">Device Detail2</button>\n'
  3214. html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n'
  3215. html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n'
  3216. html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n'
  3217. html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR&rarr;</div>\n'
  3218. html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n'
  3219. html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n'
  3220. html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background-color:{4}">{5}</div>\n'
  3221. html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n'
  3222. html_legend = '<div id="p{3}" class="square" style="left:{0}%;background-color:{1}">&nbsp;{2}</div>\n'
  3223. html_timetotal = '<table class="time1">\n<tr>'\
  3224. '<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\
  3225. '<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\
  3226. '</tr>\n</table>\n'
  3227. html_timetotal2 = '<table class="time1">\n<tr>'\
  3228. '<td class="green" title="{4}">{3} Suspend Time: <b>{0} ms</b></td>'\
  3229. '<td class="gray" title="time spent in low-power mode with clock running">'+sysvals.suspendmode+' time: <b>{1} ms</b></td>'\
  3230. '<td class="yellow" title="{5}">{3} Resume Time: <b>{2} ms</b></td>'\
  3231. '</tr>\n</table>\n'
  3232. html_timetotal3 = '<table class="time1">\n<tr>'\
  3233. '<td class="green">Execution Time: <b>{0} ms</b></td>'\
  3234. '<td class="yellow">Command: <b>{1}</b></td>'\
  3235. '</tr>\n</table>\n'
  3236. html_timegroups = '<table class="time2">\n<tr>'\
  3237. '<td class="green" title="time from kernel enter_state({5}) to firmware mode [kernel time only]">{4}Kernel Suspend: {0} ms</td>'\
  3238. '<td class="purple">{4}Firmware Suspend: {1} ms</td>'\
  3239. '<td class="purple">{4}Firmware Resume: {2} ms</td>'\
  3240. '<td class="yellow" title="time from firmware mode to return from kernel enter_state({5}) [kernel time only]">{4}Kernel Resume: {3} ms</td>'\
  3241. '</tr>\n</table>\n'
  3242. # html format variables
  3243. hoverZ = 'z-index:8;'
  3244. if sysvals.usedevsrc:
  3245. hoverZ = ''
  3246. scaleH = 20
  3247. scaleTH = 20
  3248. if kerror:
  3249. scaleH = 40
  3250. scaleTH = 60
  3251. # device timeline
  3252. vprint('Creating Device Timeline...')
  3253. devtl = Timeline(30, scaleH)
  3254. # Generate the header for this timeline
  3255. for data in testruns:
  3256. tTotal = data.end - data.start
  3257. sktime = (data.dmesg['suspend_machine']['end'] - \
  3258. data.tKernSus) * 1000
  3259. rktime = (data.dmesg['resume_complete']['end'] - \
  3260. data.dmesg['resume_machine']['start']) * 1000
  3261. if(tTotal == 0):
  3262. print('ERROR: No timeline data')
  3263. sys.exit()
  3264. if(data.tLow > 0):
  3265. low_time = '%.0f'%(data.tLow*1000)
  3266. if sysvals.suspendmode == 'command':
  3267. run_time = '%.0f'%((data.end-data.start)*1000)
  3268. if sysvals.testcommand:
  3269. testdesc = sysvals.testcommand
  3270. else:
  3271. testdesc = 'unknown'
  3272. if(len(testruns) > 1):
  3273. testdesc = ordinal(data.testnumber+1)+' '+testdesc
  3274. thtml = html_timetotal3.format(run_time, testdesc)
  3275. devtl.html['header'] += thtml
  3276. elif data.fwValid:
  3277. suspend_time = '%.0f'%(sktime + (data.fwSuspend/1000000.0))
  3278. resume_time = '%.0f'%(rktime + (data.fwResume/1000000.0))
  3279. testdesc1 = 'Total'
  3280. testdesc2 = ''
  3281. stitle = 'time from kernel enter_state(%s) to low-power mode [kernel & firmware time]' % sysvals.suspendmode
  3282. rtitle = 'time from low-power mode to return from kernel enter_state(%s) [firmware & kernel time]' % sysvals.suspendmode
  3283. if(len(testruns) > 1):
  3284. testdesc1 = testdesc2 = ordinal(data.testnumber+1)
  3285. testdesc2 += ' '
  3286. if(data.tLow == 0):
  3287. thtml = html_timetotal.format(suspend_time, \
  3288. resume_time, testdesc1, stitle, rtitle)
  3289. else:
  3290. thtml = html_timetotal2.format(suspend_time, low_time, \
  3291. resume_time, testdesc1, stitle, rtitle)
  3292. devtl.html['header'] += thtml
  3293. sftime = '%.3f'%(data.fwSuspend / 1000000.0)
  3294. rftime = '%.3f'%(data.fwResume / 1000000.0)
  3295. devtl.html['header'] += html_timegroups.format('%.3f'%sktime, \
  3296. sftime, rftime, '%.3f'%rktime, testdesc2, sysvals.suspendmode)
  3297. else:
  3298. suspend_time = '%.3f' % sktime
  3299. resume_time = '%.3f' % rktime
  3300. testdesc = 'Kernel'
  3301. stitle = 'time from kernel enter_state(%s) to firmware mode [kernel time only]' % sysvals.suspendmode
  3302. rtitle = 'time from firmware mode to return from kernel enter_state(%s) [kernel time only]' % sysvals.suspendmode
  3303. if(len(testruns) > 1):
  3304. testdesc = ordinal(data.testnumber+1)+' '+testdesc
  3305. if(data.tLow == 0):
  3306. thtml = html_timetotal.format(suspend_time, \
  3307. resume_time, testdesc, stitle, rtitle)
  3308. else:
  3309. thtml = html_timetotal2.format(suspend_time, low_time, \
  3310. resume_time, testdesc, stitle, rtitle)
  3311. devtl.html['header'] += thtml
  3312. # time scale for potentially multiple datasets
  3313. t0 = testruns[0].start
  3314. tMax = testruns[-1].end
  3315. tTotal = tMax - t0
  3316. # determine the maximum number of rows we need to draw
  3317. fulllist = []
  3318. threadlist = []
  3319. pscnt = 0
  3320. devcnt = 0
  3321. for data in testruns:
  3322. data.selectTimelineDevices('%f', tTotal, sysvals.mindevlen)
  3323. for group in data.devicegroups:
  3324. devlist = []
  3325. for phase in group:
  3326. for devname in data.tdevlist[phase]:
  3327. d = DevItem(data.testnumber, phase, data.dmesg[phase]['list'][devname])
  3328. devlist.append(d)
  3329. if d.isa('kth'):
  3330. threadlist.append(d)
  3331. else:
  3332. if d.isa('ps'):
  3333. pscnt += 1
  3334. else:
  3335. devcnt += 1
  3336. fulllist.append(d)
  3337. if sysvals.mixedphaseheight:
  3338. devtl.getPhaseRows(devlist)
  3339. if not sysvals.mixedphaseheight:
  3340. if len(threadlist) > 0 and len(fulllist) > 0:
  3341. if pscnt > 0 and devcnt > 0:
  3342. msg = 'user processes & device pm callbacks'
  3343. elif pscnt > 0:
  3344. msg = 'user processes'
  3345. else:
  3346. msg = 'device pm callbacks'
  3347. d = testruns[0].addHorizontalDivider(msg, testruns[-1].end)
  3348. fulllist.insert(0, d)
  3349. devtl.getPhaseRows(fulllist)
  3350. if len(threadlist) > 0:
  3351. d = testruns[0].addHorizontalDivider('asynchronous kernel threads', testruns[-1].end)
  3352. threadlist.insert(0, d)
  3353. devtl.getPhaseRows(threadlist, devtl.rows)
  3354. devtl.calcTotalRows()
  3355. # create bounding box, add buttons
  3356. if sysvals.suspendmode != 'command':
  3357. devtl.html['timeline'] += html_devlist1
  3358. if len(testruns) > 1:
  3359. devtl.html['timeline'] += html_devlist2
  3360. devtl.html['timeline'] += html_zoombox
  3361. devtl.html['timeline'] += html_timeline.format('dmesg', devtl.height)
  3362. # draw the full timeline
  3363. phases = {'suspend':[],'resume':[]}
  3364. for phase in data.dmesg:
  3365. if 'resume' in phase:
  3366. phases['resume'].append(phase)
  3367. else:
  3368. phases['suspend'].append(phase)
  3369. # draw each test run chronologically
  3370. for data in testruns:
  3371. # now draw the actual timeline blocks
  3372. for dir in phases:
  3373. # draw suspend and resume blocks separately
  3374. bname = '%s%d' % (dir[0], data.testnumber)
  3375. if dir == 'suspend':
  3376. m0 = testruns[data.testnumber].start
  3377. mMax = testruns[data.testnumber].tSuspended
  3378. mTotal = mMax - m0
  3379. left = '%f' % (((m0-t0)*100.0)/tTotal)
  3380. else:
  3381. m0 = testruns[data.testnumber].tSuspended
  3382. mMax = testruns[data.testnumber].end
  3383. # in an x2 run, remove any gap between blocks
  3384. if len(testruns) > 1 and data.testnumber == 0:
  3385. mMax = testruns[1].start
  3386. mTotal = mMax - m0
  3387. left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal)
  3388. # if a timeline block is 0 length, skip altogether
  3389. if mTotal == 0:
  3390. continue
  3391. width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal)
  3392. devtl.html['timeline'] += html_tblock.format(bname, left, width, devtl.scaleH)
  3393. for b in sorted(phases[dir]):
  3394. # draw the phase color background
  3395. phase = data.dmesg[b]
  3396. length = phase['end']-phase['start']
  3397. left = '%f' % (((phase['start']-m0)*100.0)/mTotal)
  3398. width = '%f' % ((length*100.0)/mTotal)
  3399. devtl.html['timeline'] += html_phase.format(left, width, \
  3400. '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
  3401. data.dmesg[b]['color'], '')
  3402. for e in data.errorinfo[dir]:
  3403. # draw red lines for any kernel errors found
  3404. t, err = e
  3405. right = '%f' % (((mMax-t)*100.0)/mTotal)
  3406. devtl.html['timeline'] += html_error.format(right, err)
  3407. for b in sorted(phases[dir]):
  3408. # draw the devices for this phase
  3409. phaselist = data.dmesg[b]['list']
  3410. for d in data.tdevlist[b]:
  3411. name = d
  3412. drv = ''
  3413. dev = phaselist[d]
  3414. xtraclass = ''
  3415. xtrainfo = ''
  3416. xtrastyle = ''
  3417. if 'htmlclass' in dev:
  3418. xtraclass = dev['htmlclass']
  3419. if 'color' in dev:
  3420. xtrastyle = 'background-color:%s;' % dev['color']
  3421. if(d in sysvals.devprops):
  3422. name = sysvals.devprops[d].altName(d)
  3423. xtraclass = sysvals.devprops[d].xtraClass()
  3424. xtrainfo = sysvals.devprops[d].xtraInfo()
  3425. elif xtraclass == ' kth':
  3426. xtrainfo = ' kernel_thread'
  3427. if('drv' in dev and dev['drv']):
  3428. drv = ' {%s}' % dev['drv']
  3429. rowheight = devtl.phaseRowHeight(data.testnumber, b, dev['row'])
  3430. rowtop = devtl.phaseRowTop(data.testnumber, b, dev['row'])
  3431. top = '%.3f' % (rowtop + devtl.scaleH)
  3432. left = '%f' % (((dev['start']-m0)*100)/mTotal)
  3433. width = '%f' % (((dev['end']-dev['start'])*100)/mTotal)
  3434. length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
  3435. title = name+drv+xtrainfo+length
  3436. if sysvals.suspendmode == 'command':
  3437. title += sysvals.testcommand
  3438. elif xtraclass == ' ps':
  3439. if 'suspend' in b:
  3440. title += 'pre_suspend_process'
  3441. else:
  3442. title += 'post_resume_process'
  3443. else:
  3444. title += b
  3445. devtl.html['timeline'] += html_device.format(dev['id'], \
  3446. title, left, top, '%.3f'%rowheight, width, \
  3447. d+drv, xtraclass, xtrastyle)
  3448. if('cpuexec' in dev):
  3449. for t in sorted(dev['cpuexec']):
  3450. start, end = t
  3451. j = float(dev['cpuexec'][t]) / 5
  3452. if j > 1.0:
  3453. j = 1.0
  3454. height = '%.3f' % (rowheight/3)
  3455. top = '%.3f' % (rowtop + devtl.scaleH + 2*rowheight/3)
  3456. left = '%f' % (((start-m0)*100)/mTotal)
  3457. width = '%f' % ((end-start)*100/mTotal)
  3458. color = 'rgba(255, 0, 0, %f)' % j
  3459. devtl.html['timeline'] += \
  3460. html_cpuexec.format(left, top, height, width, color)
  3461. if('src' not in dev):
  3462. continue
  3463. # draw any trace events for this device
  3464. for e in dev['src']:
  3465. height = '%.3f' % devtl.rowH
  3466. top = '%.3f' % (rowtop + devtl.scaleH + (e.row*devtl.rowH))
  3467. left = '%f' % (((e.time-m0)*100)/mTotal)
  3468. width = '%f' % (e.length*100/mTotal)
  3469. xtrastyle = ''
  3470. if e.color:
  3471. xtrastyle = 'background:%s;' % e.color
  3472. devtl.html['timeline'] += \
  3473. html_traceevent.format(e.title(), \
  3474. left, top, height, width, e.text(), '', xtrastyle)
  3475. # draw the time scale, try to make the number of labels readable
  3476. devtl.html['timeline'] += devtl.createTimeScale(m0, mMax, tTotal, dir)
  3477. devtl.html['timeline'] += '</div>\n'
  3478. # timeline is finished
  3479. devtl.html['timeline'] += '</div>\n</div>\n'
  3480. # draw a legend which describes the phases by color
  3481. if sysvals.suspendmode != 'command':
  3482. data = testruns[-1]
  3483. devtl.html['legend'] = '<div class="legend">\n'
  3484. pdelta = 100.0/len(data.phases)
  3485. pmargin = pdelta / 4.0
  3486. for phase in data.phases:
  3487. tmp = phase.split('_')
  3488. id = tmp[0][0]
  3489. if(len(tmp) > 1):
  3490. id += tmp[1][0]
  3491. order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin)
  3492. name = string.replace(phase, '_', ' &nbsp;')
  3493. devtl.html['legend'] += html_legend.format(order, \
  3494. data.dmesg[phase]['color'], name, id)
  3495. devtl.html['legend'] += '</div>\n'
  3496. hf = open(sysvals.htmlfile, 'w')
  3497. if not sysvals.cgexp:
  3498. cgchk = 'checked'
  3499. cgnchk = 'not(:checked)'
  3500. else:
  3501. cgchk = 'not(:checked)'
  3502. cgnchk = 'checked'
  3503. # write the html header first (html head, css code, up to body start)
  3504. html_header = '<!DOCTYPE html>\n<html>\n<head>\n\
  3505. <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
  3506. <title>'+htmlTitle()+'</title>\n\
  3507. <style type=\'text/css\'>\n\
  3508. body {overflow-y:scroll;}\n\
  3509. .stamp {width:100%;text-align:center;background-color:gray;line-height:30px;color:white;font:25px Arial;}\n\
  3510. .callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\
  3511. .callgraph article * {padding-left:28px;}\n\
  3512. h1 {color:black;font:bold 30px Times;}\n\
  3513. t0 {color:black;font:bold 30px Times;}\n\
  3514. t1 {color:black;font:30px Times;}\n\
  3515. t2 {color:black;font:25px Times;}\n\
  3516. t3 {color:black;font:20px Times;white-space:nowrap;}\n\
  3517. t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\
  3518. cS {font:bold 13px Times;}\n\
  3519. table {width:100%;}\n\
  3520. .gray {background-color:rgba(80,80,80,0.1);}\n\
  3521. .green {background-color:rgba(204,255,204,0.4);}\n\
  3522. .purple {background-color:rgba(128,0,128,0.2);}\n\
  3523. .yellow {background-color:rgba(255,255,204,0.4);}\n\
  3524. .time1 {font:22px Arial;border:1px solid;}\n\
  3525. .time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\
  3526. td {text-align:center;}\n\
  3527. r {color:#500000;font:15px Tahoma;}\n\
  3528. n {color:#505050;font:15px Tahoma;}\n\
  3529. .tdhl {color:red;}\n\
  3530. .hide {display:none;}\n\
  3531. .pf {display:none;}\n\
  3532. .pf:'+cgchk+' + label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
  3533. .pf:'+cgnchk+' ~ label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
  3534. .pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\
  3535. .zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\
  3536. .timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\
  3537. .thread {position:absolute;height:0%;overflow:hidden;z-index:7;line-height:30px;font-size:14px;border:1px solid;text-align:center;white-space:nowrap;}\n\
  3538. .thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\
  3539. .thread:hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
  3540. .thread.sec,.thread.sec:hover {background-color:black;border:0;color:white;line-height:15px;font-size:10px;}\n\
  3541. .hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
  3542. .hover.sync {background-color:white;}\n\
  3543. .hover.bg,.hover.kth,.hover.sync,.hover.ps {background-color:white;}\n\
  3544. .jiffie {position:absolute;pointer-events: none;z-index:8;}\n\
  3545. .traceevent {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\
  3546. .traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\
  3547. .phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\
  3548. .phaselet {position:absolute;overflow:hidden;border:0px;text-align:center;height:100px;font-size:24px;}\n\
  3549. .t {position:absolute;line-height:'+('%d'%scaleTH)+'px;pointer-events:none;top:0;height:100%;border-right:1px solid black;z-index:6;}\n\
  3550. .err {position:absolute;top:0%;height:100%;border-right:3px solid red;color:red;font:bold 14px Times;line-height:18px;}\n\
  3551. .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\
  3552. .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\
  3553. button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\
  3554. .logbtn {position:relative;float:right;height:25px;width:50px;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\
  3555. .devlist {position:'+x2changes[1]+';width:190px;}\n\
  3556. a:link {color:white;text-decoration:none;}\n\
  3557. a:visited {color:white;}\n\
  3558. a:hover {color:white;}\n\
  3559. a:active {color:white;}\n\
  3560. .version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\
  3561. #devicedetail {height:100px;box-shadow:5px 5px 20px black;}\n\
  3562. .tblock {position:absolute;height:100%;background-color:#ddd;}\n\
  3563. .tback {position:absolute;width:100%;background:linear-gradient(#ccc, #ddd);}\n\
  3564. .bg {z-index:1;}\n\
  3565. </style>\n</head>\n<body>\n'
  3566. # no header or css if its embedded
  3567. if(sysvals.embedded):
  3568. hf.write('pass True tSus %.3f tRes %.3f tLow %.3f fwvalid %s tSus %.3f tRes %.3f\n' %
  3569. (data.tSuspended-data.start, data.end-data.tSuspended, data.tLow, data.fwValid, \
  3570. data.fwSuspend/1000000, data.fwResume/1000000))
  3571. else:
  3572. hf.write(html_header)
  3573. # write the test title and general info header
  3574. if(sysvals.stamp['time'] != ""):
  3575. hf.write(headline_version)
  3576. if sysvals.logmsg:
  3577. hf.write('<button id="showtest" class="logbtn">log</button>')
  3578. if sysvals.addlogs and sysvals.dmesgfile:
  3579. hf.write('<button id="showdmesg" class="logbtn">dmesg</button>')
  3580. if sysvals.addlogs and sysvals.ftracefile:
  3581. hf.write('<button id="showftrace" class="logbtn">ftrace</button>')
  3582. hf.write(headline_stamp.format(sysvals.stamp['host'],
  3583. sysvals.stamp['kernel'], sysvals.stamp['mode'], \
  3584. sysvals.stamp['time']))
  3585. # write the device timeline
  3586. hf.write(devtl.html['header'])
  3587. hf.write(devtl.html['timeline'])
  3588. hf.write(devtl.html['legend'])
  3589. hf.write('<div id="devicedetailtitle"></div>\n')
  3590. hf.write('<div id="devicedetail" style="display:none;">\n')
  3591. # draw the colored boxes for the device detail section
  3592. for data in testruns:
  3593. hf.write('<div id="devicedetail%d">\n' % data.testnumber)
  3594. pscolor = 'linear-gradient(to top left, #ccc, #eee)'
  3595. hf.write(html_phaselet.format('pre_suspend_process', \
  3596. '0', '0', pscolor))
  3597. for b in data.phases:
  3598. phase = data.dmesg[b]
  3599. length = phase['end']-phase['start']
  3600. left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal)
  3601. width = '%.3f' % ((length*100.0)/tTotal)
  3602. hf.write(html_phaselet.format(b, left, width, \
  3603. data.dmesg[b]['color']))
  3604. hf.write(html_phaselet.format('post_resume_process', \
  3605. '0', '0', pscolor))
  3606. if sysvals.suspendmode == 'command':
  3607. hf.write(html_phaselet.format('cmdexec', '0', '0', pscolor))
  3608. hf.write('</div>\n')
  3609. hf.write('</div>\n')
  3610. # write the ftrace data (callgraph)
  3611. if sysvals.cgtest >= 0 and len(testruns) > sysvals.cgtest:
  3612. data = testruns[sysvals.cgtest]
  3613. else:
  3614. data = testruns[-1]
  3615. if(sysvals.usecallgraph and not sysvals.embedded):
  3616. hf.write('<section id="callgraphs" class="callgraph">\n')
  3617. # write out the ftrace data converted to html
  3618. html_func_top = '<article id="{0}" class="atop" style="background-color:{1}">\n<input type="checkbox" class="pf" id="f{2}" checked/><label for="f{2}">{3} {4}</label>\n'
  3619. html_func_start = '<article>\n<input type="checkbox" class="pf" id="f{0}" checked/><label for="f{0}">{1} {2}</label>\n'
  3620. html_func_end = '</article>\n'
  3621. html_func_leaf = '<article>{0} {1}</article>\n'
  3622. num = 0
  3623. for p in data.phases:
  3624. if sysvals.cgphase and p != sysvals.cgphase:
  3625. continue
  3626. list = data.dmesg[p]['list']
  3627. for devname in data.sortedDevices(p):
  3628. if('ftrace' not in list[devname]):
  3629. continue
  3630. devid = list[devname]['id']
  3631. cg = list[devname]['ftrace']
  3632. clen = (cg.end - cg.start) * 1000
  3633. if clen < sysvals.mincglen:
  3634. continue
  3635. fmt = '<r>(%.3f ms @ '+sysvals.timeformat+' to '+sysvals.timeformat+')</r>'
  3636. flen = fmt % (clen, cg.start, cg.end)
  3637. name = devname
  3638. if(devname in sysvals.devprops):
  3639. name = sysvals.devprops[devname].altName(devname)
  3640. if sysvals.suspendmode == 'command':
  3641. ftitle = name
  3642. else:
  3643. ftitle = name+' '+p
  3644. hf.write(html_func_top.format(devid, data.dmesg[p]['color'], \
  3645. num, ftitle, flen))
  3646. num += 1
  3647. for line in cg.list:
  3648. if(line.length < 0.000000001):
  3649. flen = ''
  3650. else:
  3651. fmt = '<n>(%.3f ms @ '+sysvals.timeformat+')</n>'
  3652. flen = fmt % (line.length*1000, line.time)
  3653. if(line.freturn and line.fcall):
  3654. hf.write(html_func_leaf.format(line.name, flen))
  3655. elif(line.freturn):
  3656. hf.write(html_func_end)
  3657. else:
  3658. hf.write(html_func_start.format(num, line.name, flen))
  3659. num += 1
  3660. hf.write(html_func_end)
  3661. hf.write('\n\n </section>\n')
  3662. # add the test log as a hidden div
  3663. if sysvals.logmsg:
  3664. hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n')
  3665. # add the dmesg log as a hidden div
  3666. if sysvals.addlogs and sysvals.dmesgfile:
  3667. hf.write('<div id="dmesglog" style="display:none;">\n')
  3668. lf = open(sysvals.dmesgfile, 'r')
  3669. for line in lf:
  3670. line = line.replace('<', '&lt').replace('>', '&gt')
  3671. hf.write(line)
  3672. lf.close()
  3673. hf.write('</div>\n')
  3674. # add the ftrace log as a hidden div
  3675. if sysvals.addlogs and sysvals.ftracefile:
  3676. hf.write('<div id="ftracelog" style="display:none;">\n')
  3677. lf = open(sysvals.ftracefile, 'r')
  3678. for line in lf:
  3679. hf.write(line)
  3680. lf.close()
  3681. hf.write('</div>\n')
  3682. if(not sysvals.embedded):
  3683. # write the footer and close
  3684. addScriptCode(hf, testruns)
  3685. hf.write('</body>\n</html>\n')
  3686. else:
  3687. # embedded out will be loaded in a page, skip the js
  3688. t0 = (testruns[0].start - testruns[-1].tSuspended) * 1000
  3689. tMax = (testruns[-1].end - testruns[-1].tSuspended) * 1000
  3690. # add js code in a div entry for later evaluation
  3691. detail = 'var bounds = [%f,%f];\n' % (t0, tMax)
  3692. detail += 'var devtable = [\n'
  3693. for data in testruns:
  3694. topo = data.deviceTopology()
  3695. detail += '\t"%s",\n' % (topo)
  3696. detail += '];\n'
  3697. hf.write('<div id=customcode style=display:none>\n'+detail+'</div>\n')
  3698. hf.close()
  3699. return True
  3700. # Function: addScriptCode
  3701. # Description:
  3702. # Adds the javascript code to the output html
  3703. # Arguments:
  3704. # hf: the open html file pointer
  3705. # testruns: array of Data objects from parseKernelLog or parseTraceLog
  3706. def addScriptCode(hf, testruns):
  3707. t0 = testruns[0].start * 1000
  3708. tMax = testruns[-1].end * 1000
  3709. # create an array in javascript memory with the device details
  3710. detail = ' var devtable = [];\n'
  3711. for data in testruns:
  3712. topo = data.deviceTopology()
  3713. detail += ' devtable[%d] = "%s";\n' % (data.testnumber, topo)
  3714. detail += ' var bounds = [%f,%f];\n' % (t0, tMax)
  3715. # add the code which will manipulate the data in the browser
  3716. script_code = \
  3717. '<script type="text/javascript">\n'+detail+\
  3718. ' var resolution = -1;\n'\
  3719. ' var dragval = [0, 0];\n'\
  3720. ' function redrawTimescale(t0, tMax, tS) {\n'\
  3721. ' var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;"><cS>&larr;R</cS></div>\';\n'\
  3722. ' var tTotal = tMax - t0;\n'\
  3723. ' var list = document.getElementsByClassName("tblock");\n'\
  3724. ' for (var i = 0; i < list.length; i++) {\n'\
  3725. ' var timescale = list[i].getElementsByClassName("timescale")[0];\n'\
  3726. ' var m0 = t0 + (tTotal*parseFloat(list[i].style.left)/100);\n'\
  3727. ' var mTotal = tTotal*parseFloat(list[i].style.width)/100;\n'\
  3728. ' var mMax = m0 + mTotal;\n'\
  3729. ' var html = "";\n'\
  3730. ' var divTotal = Math.floor(mTotal/tS) + 1;\n'\
  3731. ' if(divTotal > 1000) continue;\n'\
  3732. ' var divEdge = (mTotal - tS*(divTotal-1))*100/mTotal;\n'\
  3733. ' var pos = 0.0, val = 0.0;\n'\
  3734. ' for (var j = 0; j < divTotal; j++) {\n'\
  3735. ' var htmlline = "";\n'\
  3736. ' if(list[i].id[5] == "r") {\n'\
  3737. ' pos = 100 - (((j)*tS*100)/mTotal);\n'\
  3738. ' val = (j)*tS;\n'\
  3739. ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
  3740. ' if(j == 0)\n'\
  3741. ' htmlline = rline;\n'\
  3742. ' } else {\n'\
  3743. ' pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\
  3744. ' val = (j-divTotal+1)*tS;\n'\
  3745. ' if(j == divTotal - 1)\n'\
  3746. ' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S&rarr;</cS></div>\';\n'\
  3747. ' else\n'\
  3748. ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
  3749. ' }\n'\
  3750. ' html += htmlline;\n'\
  3751. ' }\n'\
  3752. ' timescale.innerHTML = html;\n'\
  3753. ' }\n'\
  3754. ' }\n'\
  3755. ' function zoomTimeline() {\n'\
  3756. ' var dmesg = document.getElementById("dmesg");\n'\
  3757. ' var zoombox = document.getElementById("dmesgzoombox");\n'\
  3758. ' var left = zoombox.scrollLeft;\n'\
  3759. ' var val = parseFloat(dmesg.style.width);\n'\
  3760. ' var newval = 100;\n'\
  3761. ' var sh = window.outerWidth / 2;\n'\
  3762. ' if(this.id == "zoomin") {\n'\
  3763. ' newval = val * 1.2;\n'\
  3764. ' if(newval > 910034) newval = 910034;\n'\
  3765. ' dmesg.style.width = newval+"%";\n'\
  3766. ' zoombox.scrollLeft = ((left + sh) * newval / val) - sh;\n'\
  3767. ' } else if (this.id == "zoomout") {\n'\
  3768. ' newval = val / 1.2;\n'\
  3769. ' if(newval < 100) newval = 100;\n'\
  3770. ' dmesg.style.width = newval+"%";\n'\
  3771. ' zoombox.scrollLeft = ((left + sh) * newval / val) - sh;\n'\
  3772. ' } else {\n'\
  3773. ' zoombox.scrollLeft = 0;\n'\
  3774. ' dmesg.style.width = "100%";\n'\
  3775. ' }\n'\
  3776. ' var tS = [10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1];\n'\
  3777. ' var t0 = bounds[0];\n'\
  3778. ' var tMax = bounds[1];\n'\
  3779. ' var tTotal = tMax - t0;\n'\
  3780. ' var wTotal = tTotal * 100.0 / newval;\n'\
  3781. ' var idx = 7*window.innerWidth/1100;\n'\
  3782. ' for(var i = 0; (i < tS.length)&&((wTotal / tS[i]) < idx); i++);\n'\
  3783. ' if(i >= tS.length) i = tS.length - 1;\n'\
  3784. ' if(tS[i] == resolution) return;\n'\
  3785. ' resolution = tS[i];\n'\
  3786. ' redrawTimescale(t0, tMax, tS[i]);\n'\
  3787. ' }\n'\
  3788. ' function deviceName(title) {\n'\
  3789. ' var name = title.slice(0, title.indexOf(" ("));\n'\
  3790. ' return name;\n'\
  3791. ' }\n'\
  3792. ' function deviceHover() {\n'\
  3793. ' var name = deviceName(this.title);\n'\
  3794. ' var dmesg = document.getElementById("dmesg");\n'\
  3795. ' var dev = dmesg.getElementsByClassName("thread");\n'\
  3796. ' var cpu = -1;\n'\
  3797. ' if(name.match("CPU_ON\[[0-9]*\]"))\n'\
  3798. ' cpu = parseInt(name.slice(7));\n'\
  3799. ' else if(name.match("CPU_OFF\[[0-9]*\]"))\n'\
  3800. ' cpu = parseInt(name.slice(8));\n'\
  3801. ' for (var i = 0; i < dev.length; i++) {\n'\
  3802. ' dname = deviceName(dev[i].title);\n'\
  3803. ' var cname = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
  3804. ' if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
  3805. ' (name == dname))\n'\
  3806. ' {\n'\
  3807. ' dev[i].className = "hover "+cname;\n'\
  3808. ' } else {\n'\
  3809. ' dev[i].className = cname;\n'\
  3810. ' }\n'\
  3811. ' }\n'\
  3812. ' }\n'\
  3813. ' function deviceUnhover() {\n'\
  3814. ' var dmesg = document.getElementById("dmesg");\n'\
  3815. ' var dev = dmesg.getElementsByClassName("thread");\n'\
  3816. ' for (var i = 0; i < dev.length; i++) {\n'\
  3817. ' dev[i].className = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
  3818. ' }\n'\
  3819. ' }\n'\
  3820. ' function deviceTitle(title, total, cpu) {\n'\
  3821. ' var prefix = "Total";\n'\
  3822. ' if(total.length > 3) {\n'\
  3823. ' prefix = "Average";\n'\
  3824. ' total[1] = (total[1]+total[3])/2;\n'\
  3825. ' total[2] = (total[2]+total[4])/2;\n'\
  3826. ' }\n'\
  3827. ' var devtitle = document.getElementById("devicedetailtitle");\n'\
  3828. ' var name = deviceName(title);\n'\
  3829. ' if(cpu >= 0) name = "CPU"+cpu;\n'\
  3830. ' var driver = "";\n'\
  3831. ' var tS = "<t2>(</t2>";\n'\
  3832. ' var tR = "<t2>)</t2>";\n'\
  3833. ' if(total[1] > 0)\n'\
  3834. ' tS = "<t2>("+prefix+" Suspend:</t2><t0> "+total[1].toFixed(3)+" ms</t0> ";\n'\
  3835. ' if(total[2] > 0)\n'\
  3836. ' tR = " <t2>"+prefix+" Resume:</t2><t0> "+total[2].toFixed(3)+" ms<t2>)</t2></t0>";\n'\
  3837. ' var s = title.indexOf("{");\n'\
  3838. ' var e = title.indexOf("}");\n'\
  3839. ' if((s >= 0) && (e >= 0))\n'\
  3840. ' driver = title.slice(s+1, e) + " <t1>@</t1> ";\n'\
  3841. ' if(total[1] > 0 && total[2] > 0)\n'\
  3842. ' devtitle.innerHTML = "<t0>"+driver+name+"</t0> "+tS+tR;\n'\
  3843. ' else\n'\
  3844. ' devtitle.innerHTML = "<t0>"+title+"</t0>";\n'\
  3845. ' return name;\n'\
  3846. ' }\n'\
  3847. ' function deviceDetail() {\n'\
  3848. ' var devinfo = document.getElementById("devicedetail");\n'\
  3849. ' devinfo.style.display = "block";\n'\
  3850. ' var name = deviceName(this.title);\n'\
  3851. ' var cpu = -1;\n'\
  3852. ' if(name.match("CPU_ON\[[0-9]*\]"))\n'\
  3853. ' cpu = parseInt(name.slice(7));\n'\
  3854. ' else if(name.match("CPU_OFF\[[0-9]*\]"))\n'\
  3855. ' cpu = parseInt(name.slice(8));\n'\
  3856. ' var dmesg = document.getElementById("dmesg");\n'\
  3857. ' var dev = dmesg.getElementsByClassName("thread");\n'\
  3858. ' var idlist = [];\n'\
  3859. ' var pdata = [[]];\n'\
  3860. ' if(document.getElementById("devicedetail1"))\n'\
  3861. ' pdata = [[], []];\n'\
  3862. ' var pd = pdata[0];\n'\
  3863. ' var total = [0.0, 0.0, 0.0];\n'\
  3864. ' for (var i = 0; i < dev.length; i++) {\n'\
  3865. ' dname = deviceName(dev[i].title);\n'\
  3866. ' if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
  3867. ' (name == dname))\n'\
  3868. ' {\n'\
  3869. ' idlist[idlist.length] = dev[i].id;\n'\
  3870. ' var tidx = 1;\n'\
  3871. ' if(dev[i].id[0] == "a") {\n'\
  3872. ' pd = pdata[0];\n'\
  3873. ' } else {\n'\
  3874. ' if(pdata.length == 1) pdata[1] = [];\n'\
  3875. ' if(total.length == 3) total[3]=total[4]=0.0;\n'\
  3876. ' pd = pdata[1];\n'\
  3877. ' tidx = 3;\n'\
  3878. ' }\n'\
  3879. ' var info = dev[i].title.split(" ");\n'\
  3880. ' var pname = info[info.length-1];\n'\
  3881. ' pd[pname] = parseFloat(info[info.length-3].slice(1));\n'\
  3882. ' total[0] += pd[pname];\n'\
  3883. ' if(pname.indexOf("suspend") >= 0)\n'\
  3884. ' total[tidx] += pd[pname];\n'\
  3885. ' else\n'\
  3886. ' total[tidx+1] += pd[pname];\n'\
  3887. ' }\n'\
  3888. ' }\n'\
  3889. ' var devname = deviceTitle(this.title, total, cpu);\n'\
  3890. ' var left = 0.0;\n'\
  3891. ' for (var t = 0; t < pdata.length; t++) {\n'\
  3892. ' pd = pdata[t];\n'\
  3893. ' devinfo = document.getElementById("devicedetail"+t);\n'\
  3894. ' var phases = devinfo.getElementsByClassName("phaselet");\n'\
  3895. ' for (var i = 0; i < phases.length; i++) {\n'\
  3896. ' if(phases[i].id in pd) {\n'\
  3897. ' var w = 100.0*pd[phases[i].id]/total[0];\n'\
  3898. ' var fs = 32;\n'\
  3899. ' if(w < 8) fs = 4*w | 0;\n'\
  3900. ' var fs2 = fs*3/4;\n'\
  3901. ' phases[i].style.width = w+"%";\n'\
  3902. ' phases[i].style.left = left+"%";\n'\
  3903. ' phases[i].title = phases[i].id+" "+pd[phases[i].id]+" ms";\n'\
  3904. ' left += w;\n'\
  3905. ' var time = "<t4 style=\\"font-size:"+fs+"px\\">"+pd[phases[i].id]+" ms<br></t4>";\n'\
  3906. ' var pname = "<t3 style=\\"font-size:"+fs2+"px\\">"+phases[i].id.replace(new RegExp("_", "g"), " ")+"</t3>";\n'\
  3907. ' phases[i].innerHTML = time+pname;\n'\
  3908. ' } else {\n'\
  3909. ' phases[i].style.width = "0%";\n'\
  3910. ' phases[i].style.left = left+"%";\n'\
  3911. ' }\n'\
  3912. ' }\n'\
  3913. ' }\n'\
  3914. ' var cglist = document.getElementById("callgraphs");\n'\
  3915. ' if(!cglist) return;\n'\
  3916. ' var cg = cglist.getElementsByClassName("atop");\n'\
  3917. ' if(cg.length < 10) return;\n'\
  3918. ' for (var i = 0; i < cg.length; i++) {\n'\
  3919. ' if(idlist.indexOf(cg[i].id) >= 0) {\n'\
  3920. ' cg[i].style.display = "block";\n'\
  3921. ' } else {\n'\
  3922. ' cg[i].style.display = "none";\n'\
  3923. ' }\n'\
  3924. ' }\n'\
  3925. ' }\n'\
  3926. ' function devListWindow(e) {\n'\
  3927. ' var win = window.open();\n'\
  3928. ' var html = "<title>"+e.target.innerHTML+"</title>"+\n'\
  3929. ' "<style type=\\"text/css\\">"+\n'\
  3930. ' " ul {list-style-type:circle;padding-left:10px;margin-left:10px;}"+\n'\
  3931. ' "</style>"\n'\
  3932. ' var dt = devtable[0];\n'\
  3933. ' if(e.target.id != "devlist1")\n'\
  3934. ' dt = devtable[1];\n'\
  3935. ' win.document.write(html+dt);\n'\
  3936. ' }\n'\
  3937. ' function errWindow() {\n'\
  3938. ' var text = this.id;\n'\
  3939. ' var win = window.open();\n'\
  3940. ' win.document.write("<pre>"+text+"</pre>");\n'\
  3941. ' win.document.close();\n'\
  3942. ' }\n'\
  3943. ' function logWindow(e) {\n'\
  3944. ' var name = e.target.id.slice(4);\n'\
  3945. ' var win = window.open();\n'\
  3946. ' var log = document.getElementById(name+"log");\n'\
  3947. ' var title = "<title>"+document.title.split(" ")[0]+" "+name+" log</title>";\n'\
  3948. ' win.document.write(title+"<pre>"+log.innerHTML+"</pre>");\n'\
  3949. ' win.document.close();\n'\
  3950. ' }\n'\
  3951. ' function onClickPhase(e) {\n'\
  3952. ' }\n'\
  3953. ' function onMouseDown(e) {\n'\
  3954. ' dragval[0] = e.clientX;\n'\
  3955. ' dragval[1] = document.getElementById("dmesgzoombox").scrollLeft;\n'\
  3956. ' document.onmousemove = onMouseMove;\n'\
  3957. ' }\n'\
  3958. ' function onMouseMove(e) {\n'\
  3959. ' var zoombox = document.getElementById("dmesgzoombox");\n'\
  3960. ' zoombox.scrollLeft = dragval[1] + dragval[0] - e.clientX;\n'\
  3961. ' }\n'\
  3962. ' function onMouseUp(e) {\n'\
  3963. ' document.onmousemove = null;\n'\
  3964. ' }\n'\
  3965. ' function onKeyPress(e) {\n'\
  3966. ' var c = e.charCode;\n'\
  3967. ' if(c != 42 && c != 43 && c != 45) return;\n'\
  3968. ' var click = document.createEvent("Events");\n'\
  3969. ' click.initEvent("click", true, false);\n'\
  3970. ' if(c == 43) \n'\
  3971. ' document.getElementById("zoomin").dispatchEvent(click);\n'\
  3972. ' else if(c == 45)\n'\
  3973. ' document.getElementById("zoomout").dispatchEvent(click);\n'\
  3974. ' else if(c == 42)\n'\
  3975. ' document.getElementById("zoomdef").dispatchEvent(click);\n'\
  3976. ' }\n'\
  3977. ' window.addEventListener("resize", function () {zoomTimeline();});\n'\
  3978. ' window.addEventListener("load", function () {\n'\
  3979. ' var dmesg = document.getElementById("dmesg");\n'\
  3980. ' dmesg.style.width = "100%"\n'\
  3981. ' dmesg.onmousedown = onMouseDown;\n'\
  3982. ' document.onmouseup = onMouseUp;\n'\
  3983. ' document.onkeypress = onKeyPress;\n'\
  3984. ' document.getElementById("zoomin").onclick = zoomTimeline;\n'\
  3985. ' document.getElementById("zoomout").onclick = zoomTimeline;\n'\
  3986. ' document.getElementById("zoomdef").onclick = zoomTimeline;\n'\
  3987. ' var list = document.getElementsByClassName("square");\n'\
  3988. ' for (var i = 0; i < list.length; i++)\n'\
  3989. ' list[i].onclick = onClickPhase;\n'\
  3990. ' var list = document.getElementsByClassName("err");\n'\
  3991. ' for (var i = 0; i < list.length; i++)\n'\
  3992. ' list[i].onclick = errWindow;\n'\
  3993. ' var list = document.getElementsByClassName("logbtn");\n'\
  3994. ' for (var i = 0; i < list.length; i++)\n'\
  3995. ' list[i].onclick = logWindow;\n'\
  3996. ' list = document.getElementsByClassName("devlist");\n'\
  3997. ' for (var i = 0; i < list.length; i++)\n'\
  3998. ' list[i].onclick = devListWindow;\n'\
  3999. ' var dev = dmesg.getElementsByClassName("thread");\n'\
  4000. ' for (var i = 0; i < dev.length; i++) {\n'\
  4001. ' dev[i].onclick = deviceDetail;\n'\
  4002. ' dev[i].onmouseover = deviceHover;\n'\
  4003. ' dev[i].onmouseout = deviceUnhover;\n'\
  4004. ' }\n'\
  4005. ' zoomTimeline();\n'\
  4006. ' });\n'\
  4007. '</script>\n'
  4008. hf.write(script_code);
  4009. # Function: executeSuspend
  4010. # Description:
  4011. # Execute system suspend through the sysfs interface, then copy the output
  4012. # dmesg and ftrace files to the test output directory.
  4013. def executeSuspend():
  4014. pm = ProcessMonitor()
  4015. tp = sysvals.tpath
  4016. fwdata = []
  4017. # mark the start point in the kernel ring buffer just as we start
  4018. sysvals.initdmesg()
  4019. # start ftrace
  4020. if(sysvals.usecallgraph or sysvals.usetraceevents):
  4021. print('START TRACING')
  4022. sysvals.fsetVal('1', 'tracing_on')
  4023. if sysvals.useprocmon:
  4024. pm.start()
  4025. # execute however many s/r runs requested
  4026. for count in range(1,sysvals.execcount+1):
  4027. # x2delay in between test runs
  4028. if(count > 1 and sysvals.x2delay > 0):
  4029. sysvals.fsetVal('WAIT %d' % sysvals.x2delay, 'trace_marker')
  4030. time.sleep(sysvals.x2delay/1000.0)
  4031. sysvals.fsetVal('WAIT END', 'trace_marker')
  4032. # start message
  4033. if sysvals.testcommand != '':
  4034. print('COMMAND START')
  4035. else:
  4036. if(sysvals.rtcwake):
  4037. print('SUSPEND START')
  4038. else:
  4039. print('SUSPEND START (press a key to resume)')
  4040. # set rtcwake
  4041. if(sysvals.rtcwake):
  4042. print('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
  4043. sysvals.rtcWakeAlarmOn()
  4044. # start of suspend trace marker
  4045. if(sysvals.usecallgraph or sysvals.usetraceevents):
  4046. sysvals.fsetVal('SUSPEND START', 'trace_marker')
  4047. # predelay delay
  4048. if(count == 1 and sysvals.predelay > 0):
  4049. sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker')
  4050. time.sleep(sysvals.predelay/1000.0)
  4051. sysvals.fsetVal('WAIT END', 'trace_marker')
  4052. # initiate suspend or command
  4053. if sysvals.testcommand != '':
  4054. call(sysvals.testcommand+' 2>&1', shell=True);
  4055. else:
  4056. pf = open(sysvals.powerfile, 'w')
  4057. pf.write(sysvals.suspendmode)
  4058. # execution will pause here
  4059. try:
  4060. pf.close()
  4061. except:
  4062. pass
  4063. if(sysvals.rtcwake):
  4064. sysvals.rtcWakeAlarmOff()
  4065. # postdelay delay
  4066. if(count == sysvals.execcount and sysvals.postdelay > 0):
  4067. sysvals.fsetVal('WAIT %d' % sysvals.postdelay, 'trace_marker')
  4068. time.sleep(sysvals.postdelay/1000.0)
  4069. sysvals.fsetVal('WAIT END', 'trace_marker')
  4070. # return from suspend
  4071. print('RESUME COMPLETE')
  4072. if(sysvals.usecallgraph or sysvals.usetraceevents):
  4073. sysvals.fsetVal('RESUME COMPLETE', 'trace_marker')
  4074. if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
  4075. fwdata.append(getFPDT(False))
  4076. # stop ftrace
  4077. if(sysvals.usecallgraph or sysvals.usetraceevents):
  4078. if sysvals.useprocmon:
  4079. pm.stop()
  4080. sysvals.fsetVal('0', 'tracing_on')
  4081. print('CAPTURING TRACE')
  4082. writeDatafileHeader(sysvals.ftracefile, fwdata)
  4083. call('cat '+tp+'trace >> '+sysvals.ftracefile, shell=True)
  4084. sysvals.fsetVal('', 'trace')
  4085. devProps()
  4086. # grab a copy of the dmesg output
  4087. print('CAPTURING DMESG')
  4088. writeDatafileHeader(sysvals.dmesgfile, fwdata)
  4089. sysvals.getdmesg()
  4090. def writeDatafileHeader(filename, fwdata):
  4091. fp = open(filename, 'a')
  4092. fp.write(sysvals.teststamp+'\n')
  4093. if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
  4094. for fw in fwdata:
  4095. if(fw):
  4096. fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
  4097. fp.close()
  4098. # Function: setUSBDevicesAuto
  4099. # Description:
  4100. # Set the autosuspend control parameter of all USB devices to auto
  4101. # This can be dangerous, so use at your own risk, most devices are set
  4102. # to always-on since the kernel cant determine if the device can
  4103. # properly autosuspend
  4104. def setUSBDevicesAuto():
  4105. rootCheck(True)
  4106. for dirname, dirnames, filenames in os.walk('/sys/devices'):
  4107. if(re.match('.*/usb[0-9]*.*', dirname) and
  4108. 'idVendor' in filenames and 'idProduct' in filenames):
  4109. call('echo auto > %s/power/control' % dirname, shell=True)
  4110. name = dirname.split('/')[-1]
  4111. desc = Popen(['cat', '%s/product' % dirname],
  4112. stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
  4113. ctrl = Popen(['cat', '%s/power/control' % dirname],
  4114. stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
  4115. print('control is %s for %6s: %s' % (ctrl, name, desc))
  4116. # Function: yesno
  4117. # Description:
  4118. # Print out an equivalent Y or N for a set of known parameter values
  4119. # Output:
  4120. # 'Y', 'N', or ' ' if the value is unknown
  4121. def yesno(val):
  4122. yesvals = ['auto', 'enabled', 'active', '1']
  4123. novals = ['on', 'disabled', 'suspended', 'forbidden', 'unsupported']
  4124. if val in yesvals:
  4125. return 'Y'
  4126. elif val in novals:
  4127. return 'N'
  4128. return ' '
  4129. # Function: ms2nice
  4130. # Description:
  4131. # Print out a very concise time string in minutes and seconds
  4132. # Output:
  4133. # The time string, e.g. "1901m16s"
  4134. def ms2nice(val):
  4135. ms = 0
  4136. try:
  4137. ms = int(val)
  4138. except:
  4139. return 0.0
  4140. m = ms / 60000
  4141. s = (ms / 1000) - (m * 60)
  4142. return '%3dm%2ds' % (m, s)
  4143. # Function: detectUSB
  4144. # Description:
  4145. # Detect all the USB hosts and devices currently connected and add
  4146. # a list of USB device names to sysvals for better timeline readability
  4147. def detectUSB():
  4148. field = {'idVendor':'', 'idProduct':'', 'product':'', 'speed':''}
  4149. power = {'async':'', 'autosuspend':'', 'autosuspend_delay_ms':'',
  4150. 'control':'', 'persist':'', 'runtime_enabled':'',
  4151. 'runtime_status':'', 'runtime_usage':'',
  4152. 'runtime_active_time':'',
  4153. 'runtime_suspended_time':'',
  4154. 'active_duration':'',
  4155. 'connected_duration':''}
  4156. print('LEGEND')
  4157. print('---------------------------------------------------------------------------------------------')
  4158. print(' A = async/sync PM queue Y/N D = autosuspend delay (seconds)')
  4159. print(' S = autosuspend Y/N rACTIVE = runtime active (min/sec)')
  4160. print(' P = persist across suspend Y/N rSUSPEN = runtime suspend (min/sec)')
  4161. print(' E = runtime suspend enabled/forbidden Y/N ACTIVE = active duration (min/sec)')
  4162. print(' R = runtime status active/suspended Y/N CONNECT = connected duration (min/sec)')
  4163. print(' U = runtime usage count')
  4164. print('---------------------------------------------------------------------------------------------')
  4165. print(' NAME ID DESCRIPTION SPEED A S P E R U D rACTIVE rSUSPEN ACTIVE CONNECT')
  4166. print('---------------------------------------------------------------------------------------------')
  4167. for dirname, dirnames, filenames in os.walk('/sys/devices'):
  4168. if(re.match('.*/usb[0-9]*.*', dirname) and
  4169. 'idVendor' in filenames and 'idProduct' in filenames):
  4170. for i in field:
  4171. field[i] = Popen(['cat', '%s/%s' % (dirname, i)],
  4172. stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
  4173. name = dirname.split('/')[-1]
  4174. for i in power:
  4175. power[i] = Popen(['cat', '%s/power/%s' % (dirname, i)],
  4176. stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
  4177. if(re.match('usb[0-9]*', name)):
  4178. first = '%-8s' % name
  4179. else:
  4180. first = '%8s' % name
  4181. print('%s [%s:%s] %-20s %-4s %1s %1s %1s %1s %1s %1s %1s %s %s %s %s' % \
  4182. (first, field['idVendor'], field['idProduct'], \
  4183. field['product'][0:20], field['speed'], \
  4184. yesno(power['async']), \
  4185. yesno(power['control']), \
  4186. yesno(power['persist']), \
  4187. yesno(power['runtime_enabled']), \
  4188. yesno(power['runtime_status']), \
  4189. power['runtime_usage'], \
  4190. power['autosuspend'], \
  4191. ms2nice(power['runtime_active_time']), \
  4192. ms2nice(power['runtime_suspended_time']), \
  4193. ms2nice(power['active_duration']), \
  4194. ms2nice(power['connected_duration'])))
  4195. # Function: devProps
  4196. # Description:
  4197. # Retrieve a list of properties for all devices in the trace log
  4198. def devProps(data=0):
  4199. props = dict()
  4200. if data:
  4201. idx = data.index(': ') + 2
  4202. if idx >= len(data):
  4203. return
  4204. devlist = data[idx:].split(';')
  4205. for dev in devlist:
  4206. f = dev.split(',')
  4207. if len(f) < 3:
  4208. continue
  4209. dev = f[0]
  4210. props[dev] = DevProps()
  4211. props[dev].altname = f[1]
  4212. if int(f[2]):
  4213. props[dev].async = True
  4214. else:
  4215. props[dev].async = False
  4216. sysvals.devprops = props
  4217. if sysvals.suspendmode == 'command' and 'testcommandstring' in props:
  4218. sysvals.testcommand = props['testcommandstring'].altname
  4219. return
  4220. if(os.path.exists(sysvals.ftracefile) == False):
  4221. doError('%s does not exist' % sysvals.ftracefile)
  4222. # first get the list of devices we need properties for
  4223. msghead = 'Additional data added by AnalyzeSuspend'
  4224. alreadystamped = False
  4225. tp = TestProps()
  4226. tf = open(sysvals.ftracefile, 'r')
  4227. for line in tf:
  4228. if msghead in line:
  4229. alreadystamped = True
  4230. continue
  4231. # determine the trace data type (required for further parsing)
  4232. m = re.match(sysvals.tracertypefmt, line)
  4233. if(m):
  4234. tp.setTracerType(m.group('t'))
  4235. continue
  4236. # parse only valid lines, if this is not one move on
  4237. m = re.match(tp.ftrace_line_fmt, line)
  4238. if(not m or 'device_pm_callback_start' not in line):
  4239. continue
  4240. m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
  4241. if(not m):
  4242. continue
  4243. dev = m.group('d')
  4244. if dev not in props:
  4245. props[dev] = DevProps()
  4246. tf.close()
  4247. if not alreadystamped and sysvals.suspendmode == 'command':
  4248. out = '#\n# '+msghead+'\n# Device Properties: '
  4249. out += 'testcommandstring,%s,0;' % (sysvals.testcommand)
  4250. with open(sysvals.ftracefile, 'a') as fp:
  4251. fp.write(out+'\n')
  4252. sysvals.devprops = props
  4253. return
  4254. # now get the syspath for each of our target devices
  4255. for dirname, dirnames, filenames in os.walk('/sys/devices'):
  4256. if(re.match('.*/power', dirname) and 'async' in filenames):
  4257. dev = dirname.split('/')[-2]
  4258. if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
  4259. props[dev].syspath = dirname[:-6]
  4260. # now fill in the properties for our target devices
  4261. for dev in props:
  4262. dirname = props[dev].syspath
  4263. if not dirname or not os.path.exists(dirname):
  4264. continue
  4265. with open(dirname+'/power/async') as fp:
  4266. text = fp.read()
  4267. props[dev].async = False
  4268. if 'enabled' in text:
  4269. props[dev].async = True
  4270. fields = os.listdir(dirname)
  4271. if 'product' in fields:
  4272. with open(dirname+'/product') as fp:
  4273. props[dev].altname = fp.read()
  4274. elif 'name' in fields:
  4275. with open(dirname+'/name') as fp:
  4276. props[dev].altname = fp.read()
  4277. elif 'model' in fields:
  4278. with open(dirname+'/model') as fp:
  4279. props[dev].altname = fp.read()
  4280. elif 'description' in fields:
  4281. with open(dirname+'/description') as fp:
  4282. props[dev].altname = fp.read()
  4283. elif 'id' in fields:
  4284. with open(dirname+'/id') as fp:
  4285. props[dev].altname = fp.read()
  4286. elif 'idVendor' in fields and 'idProduct' in fields:
  4287. idv, idp = '', ''
  4288. with open(dirname+'/idVendor') as fp:
  4289. idv = fp.read().strip()
  4290. with open(dirname+'/idProduct') as fp:
  4291. idp = fp.read().strip()
  4292. props[dev].altname = '%s:%s' % (idv, idp)
  4293. if props[dev].altname:
  4294. out = props[dev].altname.strip().replace('\n', ' ')
  4295. out = out.replace(',', ' ')
  4296. out = out.replace(';', ' ')
  4297. props[dev].altname = out
  4298. # and now write the data to the ftrace file
  4299. if not alreadystamped:
  4300. out = '#\n# '+msghead+'\n# Device Properties: '
  4301. for dev in sorted(props):
  4302. out += props[dev].out(dev)
  4303. with open(sysvals.ftracefile, 'a') as fp:
  4304. fp.write(out+'\n')
  4305. sysvals.devprops = props
  4306. # Function: getModes
  4307. # Description:
  4308. # Determine the supported power modes on this system
  4309. # Output:
  4310. # A string list of the available modes
  4311. def getModes():
  4312. modes = ''
  4313. if(os.path.exists(sysvals.powerfile)):
  4314. fp = open(sysvals.powerfile, 'r')
  4315. modes = string.split(fp.read())
  4316. fp.close()
  4317. return modes
  4318. # Function: getFPDT
  4319. # Description:
  4320. # Read the acpi bios tables and pull out FPDT, the firmware data
  4321. # Arguments:
  4322. # output: True to output the info to stdout, False otherwise
  4323. def getFPDT(output):
  4324. rectype = {}
  4325. rectype[0] = 'Firmware Basic Boot Performance Record'
  4326. rectype[1] = 'S3 Performance Table Record'
  4327. prectype = {}
  4328. prectype[0] = 'Basic S3 Resume Performance Record'
  4329. prectype[1] = 'Basic S3 Suspend Performance Record'
  4330. rootCheck(True)
  4331. if(not os.path.exists(sysvals.fpdtpath)):
  4332. if(output):
  4333. doError('file does not exist: %s' % sysvals.fpdtpath)
  4334. return False
  4335. if(not os.access(sysvals.fpdtpath, os.R_OK)):
  4336. if(output):
  4337. doError('file is not readable: %s' % sysvals.fpdtpath)
  4338. return False
  4339. if(not os.path.exists(sysvals.mempath)):
  4340. if(output):
  4341. doError('file does not exist: %s' % sysvals.mempath)
  4342. return False
  4343. if(not os.access(sysvals.mempath, os.R_OK)):
  4344. if(output):
  4345. doError('file is not readable: %s' % sysvals.mempath)
  4346. return False
  4347. fp = open(sysvals.fpdtpath, 'rb')
  4348. buf = fp.read()
  4349. fp.close()
  4350. if(len(buf) < 36):
  4351. if(output):
  4352. doError('Invalid FPDT table data, should '+\
  4353. 'be at least 36 bytes')
  4354. return False
  4355. table = struct.unpack('4sIBB6s8sI4sI', buf[0:36])
  4356. if(output):
  4357. print('')
  4358. print('Firmware Performance Data Table (%s)' % table[0])
  4359. print(' Signature : %s' % table[0])
  4360. print(' Table Length : %u' % table[1])
  4361. print(' Revision : %u' % table[2])
  4362. print(' Checksum : 0x%x' % table[3])
  4363. print(' OEM ID : %s' % table[4])
  4364. print(' OEM Table ID : %s' % table[5])
  4365. print(' OEM Revision : %u' % table[6])
  4366. print(' Creator ID : %s' % table[7])
  4367. print(' Creator Revision : 0x%x' % table[8])
  4368. print('')
  4369. if(table[0] != 'FPDT'):
  4370. if(output):
  4371. doError('Invalid FPDT table')
  4372. return False
  4373. if(len(buf) <= 36):
  4374. return False
  4375. i = 0
  4376. fwData = [0, 0]
  4377. records = buf[36:]
  4378. fp = open(sysvals.mempath, 'rb')
  4379. while(i < len(records)):
  4380. header = struct.unpack('HBB', records[i:i+4])
  4381. if(header[0] not in rectype):
  4382. i += header[1]
  4383. continue
  4384. if(header[1] != 16):
  4385. i += header[1]
  4386. continue
  4387. addr = struct.unpack('Q', records[i+8:i+16])[0]
  4388. try:
  4389. fp.seek(addr)
  4390. first = fp.read(8)
  4391. except:
  4392. if(output):
  4393. print('Bad address 0x%x in %s' % (addr, sysvals.mempath))
  4394. return [0, 0]
  4395. rechead = struct.unpack('4sI', first)
  4396. recdata = fp.read(rechead[1]-8)
  4397. if(rechead[0] == 'FBPT'):
  4398. record = struct.unpack('HBBIQQQQQ', recdata)
  4399. if(output):
  4400. print('%s (%s)' % (rectype[header[0]], rechead[0]))
  4401. print(' Reset END : %u ns' % record[4])
  4402. print(' OS Loader LoadImage Start : %u ns' % record[5])
  4403. print(' OS Loader StartImage Start : %u ns' % record[6])
  4404. print(' ExitBootServices Entry : %u ns' % record[7])
  4405. print(' ExitBootServices Exit : %u ns' % record[8])
  4406. elif(rechead[0] == 'S3PT'):
  4407. if(output):
  4408. print('%s (%s)' % (rectype[header[0]], rechead[0]))
  4409. j = 0
  4410. while(j < len(recdata)):
  4411. prechead = struct.unpack('HBB', recdata[j:j+4])
  4412. if(prechead[0] not in prectype):
  4413. continue
  4414. if(prechead[0] == 0):
  4415. record = struct.unpack('IIQQ', recdata[j:j+prechead[1]])
  4416. fwData[1] = record[2]
  4417. if(output):
  4418. print(' %s' % prectype[prechead[0]])
  4419. print(' Resume Count : %u' % \
  4420. record[1])
  4421. print(' FullResume : %u ns' % \
  4422. record[2])
  4423. print(' AverageResume : %u ns' % \
  4424. record[3])
  4425. elif(prechead[0] == 1):
  4426. record = struct.unpack('QQ', recdata[j+4:j+prechead[1]])
  4427. fwData[0] = record[1] - record[0]
  4428. if(output):
  4429. print(' %s' % prectype[prechead[0]])
  4430. print(' SuspendStart : %u ns' % \
  4431. record[0])
  4432. print(' SuspendEnd : %u ns' % \
  4433. record[1])
  4434. print(' SuspendTime : %u ns' % \
  4435. fwData[0])
  4436. j += prechead[1]
  4437. if(output):
  4438. print('')
  4439. i += header[1]
  4440. fp.close()
  4441. return fwData
  4442. # Function: statusCheck
  4443. # Description:
  4444. # Verify that the requested command and options will work, and
  4445. # print the results to the terminal
  4446. # Output:
  4447. # True if the test will work, False if not
  4448. def statusCheck(probecheck=False):
  4449. status = True
  4450. print('Checking this system (%s)...' % platform.node())
  4451. # check we have root access
  4452. res = sysvals.colorText('NO (No features of this tool will work!)')
  4453. if(rootCheck(False)):
  4454. res = 'YES'
  4455. print(' have root access: %s' % res)
  4456. if(res != 'YES'):
  4457. print(' Try running this script with sudo')
  4458. return False
  4459. # check sysfs is mounted
  4460. res = sysvals.colorText('NO (No features of this tool will work!)')
  4461. if(os.path.exists(sysvals.powerfile)):
  4462. res = 'YES'
  4463. print(' is sysfs mounted: %s' % res)
  4464. if(res != 'YES'):
  4465. return False
  4466. # check target mode is a valid mode
  4467. if sysvals.suspendmode != 'command':
  4468. res = sysvals.colorText('NO')
  4469. modes = getModes()
  4470. if(sysvals.suspendmode in modes):
  4471. res = 'YES'
  4472. else:
  4473. status = False
  4474. print(' is "%s" a valid power mode: %s' % (sysvals.suspendmode, res))
  4475. if(res == 'NO'):
  4476. print(' valid power modes are: %s' % modes)
  4477. print(' please choose one with -m')
  4478. # check if ftrace is available
  4479. res = sysvals.colorText('NO')
  4480. ftgood = sysvals.verifyFtrace()
  4481. if(ftgood):
  4482. res = 'YES'
  4483. elif(sysvals.usecallgraph):
  4484. status = False
  4485. print(' is ftrace supported: %s' % res)
  4486. # check if kprobes are available
  4487. res = sysvals.colorText('NO')
  4488. sysvals.usekprobes = sysvals.verifyKprobes()
  4489. if(sysvals.usekprobes):
  4490. res = 'YES'
  4491. else:
  4492. sysvals.usedevsrc = False
  4493. print(' are kprobes supported: %s' % res)
  4494. # what data source are we using
  4495. res = 'DMESG'
  4496. if(ftgood):
  4497. sysvals.usetraceeventsonly = True
  4498. sysvals.usetraceevents = False
  4499. for e in sysvals.traceevents:
  4500. check = False
  4501. if(os.path.exists(sysvals.epath+e)):
  4502. check = True
  4503. if(not check):
  4504. sysvals.usetraceeventsonly = False
  4505. if(e == 'suspend_resume' and check):
  4506. sysvals.usetraceevents = True
  4507. if(sysvals.usetraceevents and sysvals.usetraceeventsonly):
  4508. res = 'FTRACE (all trace events found)'
  4509. elif(sysvals.usetraceevents):
  4510. res = 'DMESG and FTRACE (suspend_resume trace event found)'
  4511. print(' timeline data source: %s' % res)
  4512. # check if rtcwake
  4513. res = sysvals.colorText('NO')
  4514. if(sysvals.rtcpath != ''):
  4515. res = 'YES'
  4516. elif(sysvals.rtcwake):
  4517. status = False
  4518. print(' is rtcwake supported: %s' % res)
  4519. if not probecheck:
  4520. return status
  4521. # verify kprobes
  4522. if sysvals.usekprobes:
  4523. for name in sysvals.tracefuncs:
  4524. sysvals.defaultKprobe(name, sysvals.tracefuncs[name])
  4525. if sysvals.usedevsrc:
  4526. for name in sysvals.dev_tracefuncs:
  4527. sysvals.defaultKprobe(name, sysvals.dev_tracefuncs[name])
  4528. sysvals.addKprobes(True)
  4529. return status
  4530. # Function: doError
  4531. # Description:
  4532. # generic error function for catastrphic failures
  4533. # Arguments:
  4534. # msg: the error message to print
  4535. # help: True if printHelp should be called after, False otherwise
  4536. def doError(msg, help=False):
  4537. if(help == True):
  4538. printHelp()
  4539. print('ERROR: %s\n') % msg
  4540. sys.exit()
  4541. # Function: rootCheck
  4542. # Description:
  4543. # quick check to see if we have root access
  4544. def rootCheck(fatal):
  4545. if(os.access(sysvals.powerfile, os.W_OK)):
  4546. return True
  4547. if fatal:
  4548. doError('This command must be run as root')
  4549. return False
  4550. # Function: getArgInt
  4551. # Description:
  4552. # pull out an integer argument from the command line with checks
  4553. def getArgInt(name, args, min, max, main=True):
  4554. if main:
  4555. try:
  4556. arg = args.next()
  4557. except:
  4558. doError(name+': no argument supplied', True)
  4559. else:
  4560. arg = args
  4561. try:
  4562. val = int(arg)
  4563. except:
  4564. doError(name+': non-integer value given', True)
  4565. if(val < min or val > max):
  4566. doError(name+': value should be between %d and %d' % (min, max), True)
  4567. return val
  4568. # Function: getArgFloat
  4569. # Description:
  4570. # pull out a float argument from the command line with checks
  4571. def getArgFloat(name, args, min, max, main=True):
  4572. if main:
  4573. try:
  4574. arg = args.next()
  4575. except:
  4576. doError(name+': no argument supplied', True)
  4577. else:
  4578. arg = args
  4579. try:
  4580. val = float(arg)
  4581. except:
  4582. doError(name+': non-numerical value given', True)
  4583. if(val < min or val > max):
  4584. doError(name+': value should be between %f and %f' % (min, max), True)
  4585. return val
  4586. def processData():
  4587. print('PROCESSING DATA')
  4588. if(sysvals.usetraceeventsonly):
  4589. testruns = parseTraceLog()
  4590. if sysvals.dmesgfile:
  4591. dmesgtext = loadKernelLog(True)
  4592. for data in testruns:
  4593. data.extractErrorInfo(dmesgtext)
  4594. else:
  4595. testruns = loadKernelLog()
  4596. for data in testruns:
  4597. parseKernelLog(data)
  4598. if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
  4599. appendIncompleteTraceLog(testruns)
  4600. createHTML(testruns)
  4601. # Function: rerunTest
  4602. # Description:
  4603. # generate an output from an existing set of ftrace/dmesg logs
  4604. def rerunTest():
  4605. if sysvals.ftracefile:
  4606. doesTraceLogHaveTraceEvents()
  4607. if not sysvals.dmesgfile and not sysvals.usetraceeventsonly:
  4608. doError('recreating this html output requires a dmesg file')
  4609. sysvals.setOutputFile()
  4610. vprint('Output file: %s' % sysvals.htmlfile)
  4611. if(os.path.exists(sysvals.htmlfile) and not os.access(sysvals.htmlfile, os.W_OK)):
  4612. doError('missing permission to write to %s' % sysvals.htmlfile)
  4613. processData()
  4614. # Function: runTest
  4615. # Description:
  4616. # execute a suspend/resume, gather the logs, and generate the output
  4617. def runTest(subdir, testpath=''):
  4618. # prepare for the test
  4619. sysvals.initFtrace()
  4620. sysvals.initTestOutput(subdir, testpath)
  4621. vprint('Output files:\n\t%s\n\t%s\n\t%s' % \
  4622. (sysvals.dmesgfile, sysvals.ftracefile, sysvals.htmlfile))
  4623. # execute the test
  4624. executeSuspend()
  4625. sysvals.cleanupFtrace()
  4626. processData()
  4627. # if running as root, change output dir owner to sudo_user
  4628. if os.path.isdir(sysvals.testdir) and os.getuid() == 0 and \
  4629. 'SUDO_USER' in os.environ:
  4630. cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
  4631. call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True)
  4632. # Function: runSummary
  4633. # Description:
  4634. # create a summary of tests in a sub-directory
  4635. def runSummary(subdir, output):
  4636. # get a list of ftrace output files
  4637. files = []
  4638. for dirname, dirnames, filenames in os.walk(subdir):
  4639. for filename in filenames:
  4640. if(re.match('.*_ftrace.txt', filename)):
  4641. files.append("%s/%s" % (dirname, filename))
  4642. # process the files in order and get an array of data objects
  4643. testruns = []
  4644. for file in sorted(files):
  4645. if output:
  4646. print("Test found in %s" % os.path.dirname(file))
  4647. sysvals.ftracefile = file
  4648. sysvals.dmesgfile = file.replace('_ftrace.txt', '_dmesg.txt')
  4649. doesTraceLogHaveTraceEvents()
  4650. sysvals.usecallgraph = False
  4651. if not sysvals.usetraceeventsonly:
  4652. if(not os.path.exists(sysvals.dmesgfile)):
  4653. print("Skipping %s: not a valid test input" % file)
  4654. continue
  4655. else:
  4656. if output:
  4657. f = os.path.basename(sysvals.ftracefile)
  4658. d = os.path.basename(sysvals.dmesgfile)
  4659. print("\tInput files: %s and %s" % (f, d))
  4660. testdata = loadKernelLog()
  4661. data = testdata[0]
  4662. parseKernelLog(data)
  4663. testdata = [data]
  4664. appendIncompleteTraceLog(testdata)
  4665. else:
  4666. if output:
  4667. print("\tInput file: %s" % os.path.basename(sysvals.ftracefile))
  4668. testdata = parseTraceLog()
  4669. data = testdata[0]
  4670. data.normalizeTime(data.tSuspended)
  4671. link = file.replace(subdir+'/', '').replace('_ftrace.txt', '.html')
  4672. data.outfile = link
  4673. testruns.append(data)
  4674. createHTMLSummarySimple(testruns, subdir+'/summary.html')
  4675. # Function: checkArgBool
  4676. # Description:
  4677. # check if a boolean string value is true or false
  4678. def checkArgBool(value):
  4679. yes = ['1', 'true', 'yes', 'on']
  4680. if value.lower() in yes:
  4681. return True
  4682. return False
  4683. # Function: configFromFile
  4684. # Description:
  4685. # Configure the script via the info in a config file
  4686. def configFromFile(file):
  4687. Config = ConfigParser.ConfigParser()
  4688. Config.read(file)
  4689. sections = Config.sections()
  4690. overridekprobes = False
  4691. overridedevkprobes = False
  4692. if 'Settings' in sections:
  4693. for opt in Config.options('Settings'):
  4694. value = Config.get('Settings', opt).lower()
  4695. if(opt.lower() == 'verbose'):
  4696. sysvals.verbose = checkArgBool(value)
  4697. elif(opt.lower() == 'addlogs'):
  4698. sysvals.addlogs = checkArgBool(value)
  4699. elif(opt.lower() == 'dev'):
  4700. sysvals.usedevsrc = checkArgBool(value)
  4701. elif(opt.lower() == 'proc'):
  4702. sysvals.useprocmon = checkArgBool(value)
  4703. elif(opt.lower() == 'x2'):
  4704. if checkArgBool(value):
  4705. sysvals.execcount = 2
  4706. elif(opt.lower() == 'callgraph'):
  4707. sysvals.usecallgraph = checkArgBool(value)
  4708. elif(opt.lower() == 'override-timeline-functions'):
  4709. overridekprobes = checkArgBool(value)
  4710. elif(opt.lower() == 'override-dev-timeline-functions'):
  4711. overridedevkprobes = checkArgBool(value)
  4712. elif(opt.lower() == 'devicefilter'):
  4713. sysvals.setDeviceFilter(value)
  4714. elif(opt.lower() == 'expandcg'):
  4715. sysvals.cgexp = checkArgBool(value)
  4716. elif(opt.lower() == 'srgap'):
  4717. if checkArgBool(value):
  4718. sysvals.srgap = 5
  4719. elif(opt.lower() == 'mode'):
  4720. sysvals.suspendmode = value
  4721. elif(opt.lower() == 'command'):
  4722. sysvals.testcommand = value
  4723. elif(opt.lower() == 'x2delay'):
  4724. sysvals.x2delay = getArgInt('-x2delay', value, 0, 60000, False)
  4725. elif(opt.lower() == 'predelay'):
  4726. sysvals.predelay = getArgInt('-predelay', value, 0, 60000, False)
  4727. elif(opt.lower() == 'postdelay'):
  4728. sysvals.postdelay = getArgInt('-postdelay', value, 0, 60000, False)
  4729. elif(opt.lower() == 'rtcwake'):
  4730. sysvals.rtcwake = True
  4731. sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False)
  4732. elif(opt.lower() == 'timeprec'):
  4733. sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False))
  4734. elif(opt.lower() == 'mindev'):
  4735. sysvals.mindevlen = getArgFloat('-mindev', value, 0.0, 10000.0, False)
  4736. elif(opt.lower() == 'callloop-maxgap'):
  4737. sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', value, 0.0, 1.0, False)
  4738. elif(opt.lower() == 'callloop-maxlen'):
  4739. sysvals.callloopmaxgap = getArgFloat('-callloop-maxlen', value, 0.0, 1.0, False)
  4740. elif(opt.lower() == 'mincg'):
  4741. sysvals.mincglen = getArgFloat('-mincg', value, 0.0, 10000.0, False)
  4742. elif(opt.lower() == 'output-dir'):
  4743. sysvals.setOutputFolder(value)
  4744. if sysvals.suspendmode == 'command' and not sysvals.testcommand:
  4745. doError('No command supplied for mode "command"')
  4746. # compatibility errors
  4747. if sysvals.usedevsrc and sysvals.usecallgraph:
  4748. doError('-dev is not compatible with -f')
  4749. if sysvals.usecallgraph and sysvals.useprocmon:
  4750. doError('-proc is not compatible with -f')
  4751. if overridekprobes:
  4752. sysvals.tracefuncs = dict()
  4753. if overridedevkprobes:
  4754. sysvals.dev_tracefuncs = dict()
  4755. kprobes = dict()
  4756. kprobesec = 'dev_timeline_functions_'+platform.machine()
  4757. if kprobesec in sections:
  4758. for name in Config.options(kprobesec):
  4759. text = Config.get(kprobesec, name)
  4760. kprobes[name] = (text, True)
  4761. kprobesec = 'timeline_functions_'+platform.machine()
  4762. if kprobesec in sections:
  4763. for name in Config.options(kprobesec):
  4764. if name in kprobes:
  4765. doError('Duplicate timeline function found "%s"' % (name))
  4766. text = Config.get(kprobesec, name)
  4767. kprobes[name] = (text, False)
  4768. for name in kprobes:
  4769. function = name
  4770. format = name
  4771. color = ''
  4772. args = dict()
  4773. text, dev = kprobes[name]
  4774. data = text.split()
  4775. i = 0
  4776. for val in data:
  4777. # bracketted strings are special formatting, read them separately
  4778. if val[0] == '[' and val[-1] == ']':
  4779. for prop in val[1:-1].split(','):
  4780. p = prop.split('=')
  4781. if p[0] == 'color':
  4782. try:
  4783. color = int(p[1], 16)
  4784. color = '#'+p[1]
  4785. except:
  4786. color = p[1]
  4787. continue
  4788. # first real arg should be the format string
  4789. if i == 0:
  4790. format = val
  4791. # all other args are actual function args
  4792. else:
  4793. d = val.split('=')
  4794. args[d[0]] = d[1]
  4795. i += 1
  4796. if not function or not format:
  4797. doError('Invalid kprobe: %s' % name)
  4798. for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', format):
  4799. if arg not in args:
  4800. doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
  4801. if (dev and name in sysvals.dev_tracefuncs) or (not dev and name in sysvals.tracefuncs):
  4802. doError('Duplicate timeline function found "%s"' % (name))
  4803. kp = {
  4804. 'name': name,
  4805. 'func': function,
  4806. 'format': format,
  4807. sysvals.archargs: args
  4808. }
  4809. if color:
  4810. kp['color'] = color
  4811. if dev:
  4812. sysvals.dev_tracefuncs[name] = kp
  4813. else:
  4814. sysvals.tracefuncs[name] = kp
  4815. # Function: printHelp
  4816. # Description:
  4817. # print out the help text
  4818. def printHelp():
  4819. modes = getModes()
  4820. print('')
  4821. print('AnalyzeSuspend v%s' % sysvals.version)
  4822. print('Usage: sudo analyze_suspend.py <options>')
  4823. print('')
  4824. print('Description:')
  4825. print(' This tool is designed to assist kernel and OS developers in optimizing')
  4826. print(' their linux stack\'s suspend/resume time. Using a kernel image built')
  4827. print(' with a few extra options enabled, the tool will execute a suspend and')
  4828. print(' capture dmesg and ftrace data until resume is complete. This data is')
  4829. print(' transformed into a device timeline and an optional callgraph to give')
  4830. print(' a detailed view of which devices/subsystems are taking the most')
  4831. print(' time in suspend/resume.')
  4832. print('')
  4833. print(' Generates output files in subdirectory: suspend-mmddyy-HHMMSS')
  4834. print(' HTML output: <hostname>_<mode>.html')
  4835. print(' raw dmesg output: <hostname>_<mode>_dmesg.txt')
  4836. print(' raw ftrace output: <hostname>_<mode>_ftrace.txt')
  4837. print('')
  4838. print('Options:')
  4839. print(' [general]')
  4840. print(' -h Print this help text')
  4841. print(' -v Print the current tool version')
  4842. print(' -config fn Pull arguments and config options from file fn')
  4843. print(' -verbose Print extra information during execution and analysis')
  4844. print(' -status Test to see if the system is enabled to run this tool')
  4845. print(' -modes List available suspend modes')
  4846. print(' -m mode Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode)
  4847. print(' -o subdir Override the output subdirectory')
  4848. print(' -rtcwake t Use rtcwake to autoresume after <t> seconds (default: disabled)')
  4849. print(' -addlogs Add the dmesg and ftrace logs to the html output')
  4850. print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)')
  4851. print(' [advanced]')
  4852. print(' -cmd {s} Run the timeline over a custom command, e.g. "sync -d"')
  4853. print(' -proc Add usermode process info into the timeline (default: disabled)')
  4854. print(' -dev Add kernel function calls and threads to the timeline (default: disabled)')
  4855. print(' -x2 Run two suspend/resumes back to back (default: disabled)')
  4856. print(' -x2delay t Include t ms delay between multiple test runs (default: 0 ms)')
  4857. print(' -predelay t Include t ms delay before 1st suspend (default: 0 ms)')
  4858. print(' -postdelay t Include t ms delay after last resume (default: 0 ms)')
  4859. print(' -mindev ms Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)')
  4860. print(' -multi n d Execute <n> consecutive tests at <d> seconds intervals. The outputs will')
  4861. print(' be created in a new subdirectory with a summary page.')
  4862. print(' [debug]')
  4863. print(' -f Use ftrace to create device callgraphs (default: disabled)')
  4864. print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)')
  4865. print(' -flist Print the list of functions currently being captured in ftrace')
  4866. print(' -flistall Print all functions capable of being captured in ftrace')
  4867. print(' -fadd file Add functions to be graphed in the timeline from a list in a text file')
  4868. print(' -filter "d1,d2,..." Filter out all but this comma-delimited list of device names')
  4869. print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)')
  4870. print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)')
  4871. print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)')
  4872. print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)')
  4873. print(' [utilities]')
  4874. print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table')
  4875. print(' -usbtopo Print out the current USB topology with power info')
  4876. print(' -usbauto Enable autosuspend for all connected USB devices')
  4877. print(' [re-analyze data from previous runs]')
  4878. print(' -ftrace ftracefile Create HTML output using ftrace input')
  4879. print(' -dmesg dmesgfile Create HTML output using dmesg (not needed for kernel >= 3.15)')
  4880. print(' -summary directory Create a summary of all test in this dir')
  4881. print('')
  4882. return True
  4883. # ----------------- MAIN --------------------
  4884. # exec start (skipped if script is loaded as library)
  4885. if __name__ == '__main__':
  4886. cmd = ''
  4887. cmdarg = ''
  4888. multitest = {'run': False, 'count': 0, 'delay': 0}
  4889. simplecmds = ['-modes', '-fpdt', '-flist', '-flistall', '-usbtopo', '-usbauto', '-status']
  4890. # loop through the command line arguments
  4891. args = iter(sys.argv[1:])
  4892. for arg in args:
  4893. if(arg == '-m'):
  4894. try:
  4895. val = args.next()
  4896. except:
  4897. doError('No mode supplied', True)
  4898. if val == 'command' and not sysvals.testcommand:
  4899. doError('No command supplied for mode "command"', True)
  4900. sysvals.suspendmode = val
  4901. elif(arg in simplecmds):
  4902. cmd = arg[1:]
  4903. elif(arg == '-h'):
  4904. printHelp()
  4905. sys.exit()
  4906. elif(arg == '-v'):
  4907. print("Version %s" % sysvals.version)
  4908. sys.exit()
  4909. elif(arg == '-x2'):
  4910. sysvals.execcount = 2
  4911. elif(arg == '-x2delay'):
  4912. sysvals.x2delay = getArgInt('-x2delay', args, 0, 60000)
  4913. elif(arg == '-predelay'):
  4914. sysvals.predelay = getArgInt('-predelay', args, 0, 60000)
  4915. elif(arg == '-postdelay'):
  4916. sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000)
  4917. elif(arg == '-f'):
  4918. sysvals.usecallgraph = True
  4919. elif(arg == '-addlogs'):
  4920. sysvals.addlogs = True
  4921. elif(arg == '-verbose'):
  4922. sysvals.verbose = True
  4923. elif(arg == '-proc'):
  4924. sysvals.useprocmon = True
  4925. elif(arg == '-dev'):
  4926. sysvals.usedevsrc = True
  4927. elif(arg == '-rtcwake'):
  4928. sysvals.rtcwake = True
  4929. sysvals.rtcwaketime = getArgInt('-rtcwake', args, 0, 3600)
  4930. elif(arg == '-timeprec'):
  4931. sysvals.setPrecision(getArgInt('-timeprec', args, 0, 6))
  4932. elif(arg == '-mindev'):
  4933. sysvals.mindevlen = getArgFloat('-mindev', args, 0.0, 10000.0)
  4934. elif(arg == '-mincg'):
  4935. sysvals.mincglen = getArgFloat('-mincg', args, 0.0, 10000.0)
  4936. elif(arg == '-cgtest'):
  4937. sysvals.cgtest = getArgInt('-cgtest', args, 0, 1)
  4938. elif(arg == '-cgphase'):
  4939. try:
  4940. val = args.next()
  4941. except:
  4942. doError('No phase name supplied', True)
  4943. d = Data(0)
  4944. if val not in d.phases:
  4945. doError('Invalid phase, valid phaess are %s' % d.phases, True)
  4946. sysvals.cgphase = val
  4947. elif(arg == '-callloop-maxgap'):
  4948. sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', args, 0.0, 1.0)
  4949. elif(arg == '-callloop-maxlen'):
  4950. sysvals.callloopmaxlen = getArgFloat('-callloop-maxlen', args, 0.0, 1.0)
  4951. elif(arg == '-cmd'):
  4952. try:
  4953. val = args.next()
  4954. except:
  4955. doError('No command string supplied', True)
  4956. sysvals.testcommand = val
  4957. sysvals.suspendmode = 'command'
  4958. elif(arg == '-expandcg'):
  4959. sysvals.cgexp = True
  4960. elif(arg == '-srgap'):
  4961. sysvals.srgap = 5
  4962. elif(arg == '-multi'):
  4963. multitest['run'] = True
  4964. multitest['count'] = getArgInt('-multi n (exec count)', args, 2, 1000000)
  4965. multitest['delay'] = getArgInt('-multi d (delay between tests)', args, 0, 3600)
  4966. elif(arg == '-o'):
  4967. try:
  4968. val = args.next()
  4969. except:
  4970. doError('No subdirectory name supplied', True)
  4971. sysvals.setOutputFolder(val)
  4972. elif(arg == '-config'):
  4973. try:
  4974. val = args.next()
  4975. except:
  4976. doError('No text file supplied', True)
  4977. if(os.path.exists(val) == False):
  4978. doError('%s does not exist' % val)
  4979. configFromFile(val)
  4980. elif(arg == '-fadd'):
  4981. try:
  4982. val = args.next()
  4983. except:
  4984. doError('No text file supplied', True)
  4985. if(os.path.exists(val) == False):
  4986. doError('%s does not exist' % val)
  4987. sysvals.addFtraceFilterFunctions(val)
  4988. elif(arg == '-dmesg'):
  4989. try:
  4990. val = args.next()
  4991. except:
  4992. doError('No dmesg file supplied', True)
  4993. sysvals.notestrun = True
  4994. sysvals.dmesgfile = val
  4995. if(os.path.exists(sysvals.dmesgfile) == False):
  4996. doError('%s does not exist' % sysvals.dmesgfile)
  4997. elif(arg == '-ftrace'):
  4998. try:
  4999. val = args.next()
  5000. except:
  5001. doError('No ftrace file supplied', True)
  5002. sysvals.notestrun = True
  5003. sysvals.ftracefile = val
  5004. if(os.path.exists(sysvals.ftracefile) == False):
  5005. doError('%s does not exist' % sysvals.ftracefile)
  5006. elif(arg == '-summary'):
  5007. try:
  5008. val = args.next()
  5009. except:
  5010. doError('No directory supplied', True)
  5011. cmd = 'summary'
  5012. cmdarg = val
  5013. sysvals.notestrun = True
  5014. if(os.path.isdir(val) == False):
  5015. doError('%s is not accesible' % val)
  5016. elif(arg == '-filter'):
  5017. try:
  5018. val = args.next()
  5019. except:
  5020. doError('No devnames supplied', True)
  5021. sysvals.setDeviceFilter(val)
  5022. else:
  5023. doError('Invalid argument: '+arg, True)
  5024. # compatibility errors
  5025. if(sysvals.usecallgraph and sysvals.usedevsrc):
  5026. doError('-dev is not compatible with -f')
  5027. if(sysvals.usecallgraph and sysvals.useprocmon):
  5028. doError('-proc is not compatible with -f')
  5029. # callgraph size cannot exceed device size
  5030. if sysvals.mincglen < sysvals.mindevlen:
  5031. sysvals.mincglen = sysvals.mindevlen
  5032. # just run a utility command and exit
  5033. if(cmd != ''):
  5034. if(cmd == 'status'):
  5035. statusCheck(True)
  5036. elif(cmd == 'fpdt'):
  5037. getFPDT(True)
  5038. elif(cmd == 'usbtopo'):
  5039. detectUSB()
  5040. elif(cmd == 'modes'):
  5041. print getModes()
  5042. elif(cmd == 'flist'):
  5043. sysvals.getFtraceFilterFunctions(True)
  5044. elif(cmd == 'flistall'):
  5045. sysvals.getFtraceFilterFunctions(False)
  5046. elif(cmd == 'usbauto'):
  5047. setUSBDevicesAuto()
  5048. elif(cmd == 'summary'):
  5049. print("Generating a summary of folder \"%s\"" % cmdarg)
  5050. runSummary(cmdarg, True)
  5051. sys.exit()
  5052. # if instructed, re-analyze existing data files
  5053. if(sysvals.notestrun):
  5054. rerunTest()
  5055. sys.exit()
  5056. # verify that we can run a test
  5057. if(not statusCheck()):
  5058. print('Check FAILED, aborting the test run!')
  5059. sys.exit()
  5060. if multitest['run']:
  5061. # run multiple tests in a separate subdirectory
  5062. s = 'x%d' % multitest['count']
  5063. if not sysvals.outdir:
  5064. sysvals.outdir = datetime.now().strftime('suspend-'+s+'-%m%d%y-%H%M%S')
  5065. if not os.path.isdir(sysvals.outdir):
  5066. os.mkdir(sysvals.outdir)
  5067. for i in range(multitest['count']):
  5068. if(i != 0):
  5069. print('Waiting %d seconds...' % (multitest['delay']))
  5070. time.sleep(multitest['delay'])
  5071. print('TEST (%d/%d) START' % (i+1, multitest['count']))
  5072. runTest(sysvals.outdir)
  5073. print('TEST (%d/%d) COMPLETE' % (i+1, multitest['count']))
  5074. runSummary(sysvals.outdir, False)
  5075. else:
  5076. # run the test in the current directory
  5077. runTest('.', sysvals.outdir)