analyze_suspend.py 170 KB

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