Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800// SPDX-License-Identifier: GPL-2.0#include <errno.h>#include <inttypes.h>#include <limits.h>#include "string2.h"#include <sys/param.h>#include <sys/types.h>#include <byteswap.h>#include <unistd.h>#include <regex.h>#include <stdio.h>#include <stdlib.h>#include <linux/compiler.h>#include <linux/list.h>#include <linux/kernel.h>#include <linux/bitops.h>#include <linux/string.h>#include <linux/stringify.h>#include <linux/zalloc.h>#include <sys/stat.h>#include <sys/utsname.h>#include <linux/time64.h>#include <dirent.h>#ifdef HAVE_LIBBPF_SUPPORT#include <bpf/libbpf.h>#endif#include <perf/cpumap.h>#include <tools/libc_compat.h> // reallocarray
#include "dso.h"#include "evlist.h"#include "evsel.h"#include "util/evsel_fprintf.h"#include "header.h"#include "memswap.h"#include "trace-event.h"#include "session.h"#include "symbol.h"#include "debug.h"#include "cpumap.h"#include "pmu.h"#include "pmus.h"#include "vdso.h"#include "strbuf.h"#include "build-id.h"#include "data.h"#include <api/fs/fs.h>#include <api/io_dir.h>#include "asm/bug.h"#include "tool.h"#include "../perf.h"#include "time-utils.h"#include "units.h"#include "util/util.h" // perf_exe()#include "cputopo.h"#include "bpf-event.h"#include "bpf-utils.h"#include "clockid.h"#include "cacheline.h"
#include <linux/ctype.h>#include <internal/lib.h>
#ifdef HAVE_LIBTRACEEVENT#include <event-parse.h>#endif
/* * nr_ids * sizeof(struct perf_sample_id) must not overflow * size_t on 32-bit; the struct is ~104 bytes (32-bit) or * ~184 bytes (64-bit), so 1<<24 (16M) keeps the product * under 2 GB on 32-bit. * * This is a per-attribute cap only — the total across all * attributes is not capped because legitimate high-core-count * workloads (e.g. 5000 tracepoints × 4096 CPUs) can exceed * a single-attribute limit. */#define MAX_IDS_PER_ATTR (1 << 24)/* * Cap nr_attrs to prevent resource exhaustion from crafted * files. 65536 is well beyond any real workload (perf stat * typically uses < 100 events) but prevents u64-to-int * truncation on the attr count. */#define MAX_NR_ATTRS (1 << 16)#define MAX_BPF_DATA_LEN (256 * 1024 * 1024)#define MAX_BPF_PROGS 131072#define MAX_CACHE_ENTRIES 32768#define MAX_GROUP_DESC 32768#define MAX_NUMA_NODES 4096#define MAX_PMU_CAPS 512#define MAX_PMU_MAPPINGS 4096#define MAX_SCHED_DOMAINS 64
/* * magic2 = "PERFILE2" * must be a numerical value to let the endianness * determine the memory layout. That way we are able * to detect endianness when reading the perf.data file * back. * * we check for legacy (PERFFILE) format. */static const char *__perf_magic1 = "PERFFILE";static const u64 __perf_magic2 = 0x32454c4946524550ULL;static const u64 __perf_magic2_sw = 0x50455246494c4532ULL;
#define PERF_MAGIC __perf_magic2#define DNAME_LEN 16
const char perf_version_string[] = PERF_VERSION;
struct perf_file_attr { struct perf_event_attr attr; struct perf_file_section ids;};
void perf_header__set_feat(struct perf_header *header, int feat){ __set_bit(feat, header->adds_features);}
void perf_header__clear_feat(struct perf_header *header, int feat){ __clear_bit(feat, header->adds_features);}
bool perf_header__has_feat(const struct perf_header *header, int feat){ return test_bit(feat, header->adds_features);}
static int __do_write_fd(struct feat_fd *ff, const void *buf, size_t size){ ssize_t ret = writen(ff->fd, buf, size);
if (ret != (ssize_t)size) return ret < 0 ? (int)ret : -1; return 0;}
static int __do_write_buf(struct feat_fd *ff, const void *buf, size_t size){ /* struct perf_event_header::size is u16 */ const size_t max_size = 0xffff - sizeof(struct perf_event_header); size_t new_size = ff->size; void *addr;
if (size + ff->offset > max_size) return -E2BIG;
while (size > (new_size - ff->offset)) new_size <<= 1; new_size = min(max_size, new_size);
if (ff->size < new_size) { addr = realloc(ff->buf, new_size); if (!addr) return -ENOMEM; ff->buf = addr; ff->size = new_size; }
memcpy(ff->buf + ff->offset, buf, size); ff->offset += size;
return 0;}
/* Return: 0 if succeeded, -ERR if failed. */int do_write(struct feat_fd *ff, const void *buf, size_t size){ if (!ff->buf) return __do_write_fd(ff, buf, size); return __do_write_buf(ff, buf, size);}
/* Return: 0 if succeeded, -ERR if failed. */static int do_write_bitmap(struct feat_fd *ff, unsigned long *set, u64 size){ size_t byte_size = BITS_TO_LONGS(size) * sizeof(unsigned long); int i, ret;
ret = do_write(ff, &size, sizeof(size)); if (ret < 0) return ret;
/* * The on-disk format uses u64 elements, but the in-memory bitmap * uses unsigned long, which is only 4 bytes on 32-bit architectures. * Copy with bounded size so the last element doesn't read past the * bitmap allocation when BITS_TO_LONGS(size) is odd. */ for (i = 0; (u64) i < BITS_TO_U64(size); i++) { u64 val = 0; size_t off = i * sizeof(val);
memcpy(&val, (char *)set + off, min(sizeof(val), byte_size - off)); ret = do_write(ff, &val, sizeof(val)); if (ret < 0) return ret; }
return 0;}
/* Return: 0 if succeeded, -ERR if failed. */int write_padded(struct feat_fd *ff, const void *bf, size_t count, size_t count_aligned){ static const char zero_buf[NAME_ALIGN]; int err = do_write(ff, bf, count);
if (!err) err = do_write(ff, zero_buf, count_aligned - count);
return err;}
#define string_size(str) \ (PERF_ALIGN((strlen(str) + 1), NAME_ALIGN) + sizeof(u32))
/* Return: 0 if succeeded, -ERR if failed. */static int do_write_string(struct feat_fd *ff, const char *str){ u32 len, olen; int ret;
olen = strlen(str) + 1; len = PERF_ALIGN(olen, NAME_ALIGN);
/* write len, incl. \0 */ ret = do_write(ff, &len, sizeof(len)); if (ret < 0) return ret;
return write_padded(ff, str, olen, len);}
static int __do_read_fd(struct feat_fd *ff, void *addr, ssize_t size){ ssize_t ret = readn(ff->fd, addr, size);
if (ret != size) return ret < 0 ? (int)ret : -1; ff->offset += size; return 0;}
static int __do_read_buf(struct feat_fd *ff, void *addr, ssize_t size){ memcpy(addr, ff->buf + ff->offset, size); ff->offset += size;
return 0;}
static int __do_read(struct feat_fd *ff, void *addr, ssize_t size){ /* * Reject negative sizes, which on 32-bit can occur when a * u32 >= 0x80000000 is passed as ssize_t. The cast to * ssize_t is safe because perf_header__process_sections() * validates that each section fits within the file size * before any feature callback reaches here, and only * feature sections (metadata like build IDs, topology, etc.) * use this path — these cannot legitimately approach 2GB. */ if (size < 0 || size > (ssize_t)ff->size - ff->offset) return -1;
if (!ff->buf) return __do_read_fd(ff, addr, size); return __do_read_buf(ff, addr, size);}
static int do_read_u32(struct feat_fd *ff, u32 *addr){ int ret;
ret = __do_read(ff, addr, sizeof(*addr)); if (ret) return ret;
if (ff->ph->needs_swap) *addr = bswap_32(*addr); return 0;}
static int do_read_u64(struct feat_fd *ff, u64 *addr){ int ret;
ret = __do_read(ff, addr, sizeof(*addr)); if (ret) return ret;
if (ff->ph->needs_swap) *addr = bswap_64(*addr); return 0;}
static char *do_read_string(struct feat_fd *ff){ u32 len; char *buf;
if (do_read_u32(ff, &len)) return NULL;
/* At least the null terminator. */ if (len < 1 || len > ff->size - ff->offset) { pr_debug("do_read_string: invalid length %u (remaining %zu)\n", len, (size_t)(ff->size - ff->offset)); return NULL; }
buf = malloc(len); if (!buf) return NULL;
if (!__do_read(ff, buf, len)) { /* * do_write_string() writes len including the null * terminator, padded to NAME_ALIGN. Ensure the * string is always null-terminated even if the file * data has been tampered with. */ buf[len - 1] = '\0'; return buf; }
free(buf); return NULL;}
/* Return: 0 if succeeded, -ERR if failed. */static int do_read_bitmap(struct feat_fd *ff, unsigned long **pset, u64 *psize){ unsigned long *set; u64 size, *p; int i, ret;
ret = do_read_u64(ff, &size); if (ret) return ret;
/* Bitmap APIs use int for nbits; reject u64 values that truncate. */ if (size > INT_MAX || BITS_TO_U64(size) > (ff->size - ff->offset) / sizeof(u64)) { pr_debug("do_read_bitmap: size %" PRIu64 " exceeds section bounds\n", size); return -1; }
/* * bitmap_zalloc() allocates in unsigned long units, which are only * 4 bytes on 32-bit architectures. The read loop below casts the * buffer to u64 * and writes 8-byte elements, so allocate in u64 * units to ensure the buffer is large enough. */ set = calloc(BITS_TO_U64(size), sizeof(u64)); if (!set) return -ENOMEM;
p = (u64 *) set;
for (i = 0; (u64) i < BITS_TO_U64(size); i++) { ret = do_read_u64(ff, p + i); if (ret < 0) { free(set); return ret; } }
*pset = set; *psize = size; return 0;}
static int write_tracing_data(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ if (WARN(ff->buf, "Error: calling %s in pipe-mode.\n", __func__)) return -1;
#ifdef HAVE_LIBTRACEEVENT return read_tracing_data(ff->fd, &evlist->core.entries);#else pr_err("ERROR: Trying to write tracing data without libtraceevent support.\n"); return -1;#endif}
static int write_build_id(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct perf_session *session; int err;
session = container_of(ff->ph, struct perf_session, header);
if (!perf_session__read_build_ids(session, true)) return -1;
if (WARN(ff->buf, "Error: calling %s in pipe-mode.\n", __func__)) return -1;
err = perf_session__write_buildid_table(session, ff); if (err < 0) { pr_debug("failed to write buildid table\n"); return err; }
return 0;}
static int write_hostname(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct utsname uts; int ret;
ret = uname(&uts); if (ret < 0) return -1;
return do_write_string(ff, uts.nodename);}
static int write_osrelease(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct utsname uts; const char *release = NULL;
if (evlist->session) release = perf_env__os_release(perf_session__env(evlist->session));
if (!release) { int ret = uname(&uts);
if (ret < 0) return -1; release = uts.release; } return do_write_string(ff, release);}
static int write_arch(struct feat_fd *ff, struct evlist *evlist){ struct utsname uts; const char *arch = NULL;
if (evlist->session) arch = perf_env__arch(perf_session__env(evlist->session));
if (!arch) { int ret = uname(&uts);
if (ret < 0) return -1; arch = uts.machine; } return do_write_string(ff, arch);}
static int write_e_machine(struct feat_fd *ff, struct evlist *evlist){ /* e_machine expanded from 16 to 32-bits for alignment. */ uint32_t e_flags; uint32_t e_machine = perf_session__e_machine(evlist->session, &e_flags); int ret;
ret = do_write(ff, &e_machine, sizeof(e_machine)); if (ret) return ret;
return do_write(ff, &e_flags, sizeof(e_flags));}
static int write_version(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ return do_write_string(ff, perf_version_string);}
static int __write_cpudesc(struct feat_fd *ff, const char *cpuinfo_proc){ FILE *file; char *buf = NULL; char *s, *p; const char *search = cpuinfo_proc; size_t len = 0; int ret = -1;
if (!search) return -1;
file = fopen("/proc/cpuinfo", "r"); if (!file) return -1;
while (getline(&buf, &len, file) > 0) { ret = strncmp(buf, search, strlen(search)); if (!ret) break; }
if (ret) { ret = -1; goto done; }
s = buf;
p = strchr(buf, ':'); if (p && *(p+1) == ' ' && *(p+2)) s = p + 2; p = strchr(s, '\n'); if (p) *p = '\0';
/* squash extra space characters (branding string) */ p = s; while (*p) { if (isspace(*p)) { char *r = p + 1; char *q = skip_spaces(r); *p = ' '; if (q != (p+1)) while ((*r++ = *q++)); } p++; } ret = do_write_string(ff, s);done: free(buf); fclose(file); return ret;}
static int write_cpudesc(struct feat_fd *ff, struct evlist *evlist __maybe_unused){#if defined(__powerpc__) || defined(__hppa__) || defined(__sparc__)#define CPUINFO_PROC { "cpu", }#elif defined(__s390__)#define CPUINFO_PROC { "vendor_id", }#elif defined(__sh__)#define CPUINFO_PROC { "cpu type", }#elif defined(__alpha__) || defined(__mips__)#define CPUINFO_PROC { "cpu model", }#elif defined(__arm__)#define CPUINFO_PROC { "model name", "Processor", }#elif defined(__arc__)#define CPUINFO_PROC { "Processor", }#elif defined(__xtensa__)#define CPUINFO_PROC { "core ID", }#elif defined(__loongarch__)#define CPUINFO_PROC { "Model Name", }#else#define CPUINFO_PROC { "model name", }#endif const char *cpuinfo_procs[] = CPUINFO_PROC;#undef CPUINFO_PROC unsigned int i;
for (i = 0; i < ARRAY_SIZE(cpuinfo_procs); i++) { int ret; ret = __write_cpudesc(ff, cpuinfo_procs[i]); if (ret >= 0) return ret; } return -1;}
static int write_nrcpus(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ long nr; u32 nrc, nra; int ret;
nrc = cpu__max_present_cpu().cpu;
nr = sysconf(_SC_NPROCESSORS_ONLN); if (nr < 0) return -1;
nra = (u32)(nr & UINT_MAX);
ret = do_write(ff, &nrc, sizeof(nrc)); if (ret < 0) return ret;
return do_write(ff, &nra, sizeof(nra));}
static int write_event_desc(struct feat_fd *ff, struct evlist *evlist){ struct evsel *evsel; u32 nre, nri, sz; int ret;
nre = evlist->core.nr_entries;
/* * write number of events */ ret = do_write(ff, &nre, sizeof(nre)); if (ret < 0) return ret;
/* * size of perf_event_attr struct */ sz = (u32)sizeof(evsel->core.attr); ret = do_write(ff, &sz, sizeof(sz)); if (ret < 0) return ret;
evlist__for_each_entry(evlist, evsel) { ret = do_write(ff, &evsel->core.attr, sz); if (ret < 0) return ret; /* * write number of unique id per event * there is one id per instance of an event * * copy into an nri to be independent of the * type of ids, */ nri = evsel->core.ids; ret = do_write(ff, &nri, sizeof(nri)); if (ret < 0) return ret;
/* * write event string as passed on cmdline */ ret = do_write_string(ff, evsel__name(evsel)); if (ret < 0) return ret; /* * write unique ids for this event */ ret = do_write(ff, evsel->core.id, evsel->core.ids * sizeof(u64)); if (ret < 0) return ret; } return 0;}
static int write_cmdline(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct perf_env *env = &ff->ph->env; char pbuf[MAXPATHLEN], *buf; int i, ret, n;
/* actual path to perf binary */ buf = perf_exe(pbuf, MAXPATHLEN);
/* account for binary path */ n = env->nr_cmdline + 1;
ret = do_write(ff, &n, sizeof(n)); if (ret < 0) return ret;
ret = do_write_string(ff, buf); if (ret < 0) return ret;
for (i = 0 ; i < env->nr_cmdline; i++) { ret = do_write_string(ff, env->cmdline_argv[i]); if (ret < 0) return ret; } return 0;}
static int write_cpu_topology(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct perf_env *env = &ff->ph->env; struct cpu_topology *tp; u32 i; int ret, j;
tp = cpu_topology__new(); if (!tp) return -1;
ret = do_write(ff, &tp->package_cpus_lists, sizeof(tp->package_cpus_lists)); if (ret < 0) goto done;
for (i = 0; i < tp->package_cpus_lists; i++) { ret = do_write_string(ff, tp->package_cpus_list[i]); if (ret < 0) goto done; } ret = do_write(ff, &tp->core_cpus_lists, sizeof(tp->core_cpus_lists)); if (ret < 0) goto done;
for (i = 0; i < tp->core_cpus_lists; i++) { ret = do_write_string(ff, tp->core_cpus_list[i]); if (ret < 0) break; }
ret = perf_env__read_cpu_topology_map(env); if (ret < 0) goto done;
for (j = 0; j < env->nr_cpus_avail; j++) { ret = do_write(ff, &env->cpu[j].core_id, sizeof(env->cpu[j].core_id)); if (ret < 0) return ret; ret = do_write(ff, &env->cpu[j].socket_id, sizeof(env->cpu[j].socket_id)); if (ret < 0) return ret; }
if (!tp->die_cpus_lists) goto done;
ret = do_write(ff, &tp->die_cpus_lists, sizeof(tp->die_cpus_lists)); if (ret < 0) goto done;
for (i = 0; i < tp->die_cpus_lists; i++) { ret = do_write_string(ff, tp->die_cpus_list[i]); if (ret < 0) goto done; }
for (j = 0; j < env->nr_cpus_avail; j++) { ret = do_write(ff, &env->cpu[j].die_id, sizeof(env->cpu[j].die_id)); if (ret < 0) return ret; }
done: cpu_topology__delete(tp); return ret;}
static int write_total_mem(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ char *buf = NULL; FILE *fp; size_t len = 0; int ret = -1, n; uint64_t mem;
fp = fopen("/proc/meminfo", "r"); if (!fp) return -1;
while (getline(&buf, &len, fp) > 0) { ret = strncmp(buf, "MemTotal:", 9); if (!ret) break; } if (!ret) { n = sscanf(buf, "%*s %"PRIu64, &mem); if (n == 1) ret = do_write(ff, &mem, sizeof(mem)); } else ret = -1; free(buf); fclose(fp); return ret;}
static int write_numa_topology(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct numa_topology *tp; int ret = -1; u32 i;
tp = numa_topology__new(); if (!tp) return -ENOMEM;
ret = do_write(ff, &tp->nr, sizeof(u32)); if (ret < 0) goto err;
for (i = 0; i < tp->nr; i++) { struct numa_topology_node *n = &tp->nodes[i];
ret = do_write(ff, &n->node, sizeof(u32)); if (ret < 0) goto err;
ret = do_write(ff, &n->mem_total, sizeof(u64)); if (ret) goto err;
ret = do_write(ff, &n->mem_free, sizeof(u64)); if (ret) goto err;
ret = do_write_string(ff, n->cpus); if (ret < 0) goto err; }
ret = 0;
err: numa_topology__delete(tp); return ret;}
/* * File format: * * struct pmu_mappings { * u32 pmu_num; * struct pmu_map { * u32 type; * char name[]; * }[pmu_num]; * }; */
static int write_pmu_mappings(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct perf_pmu *pmu = NULL; u32 pmu_num = 0; int ret;
/* * Do a first pass to count number of pmu to avoid lseek so this * works in pipe mode as well. */ while ((pmu = perf_pmus__scan(pmu))) pmu_num++;
ret = do_write(ff, &pmu_num, sizeof(pmu_num)); if (ret < 0) return ret;
while ((pmu = perf_pmus__scan(pmu))) { ret = do_write(ff, &pmu->type, sizeof(pmu->type)); if (ret < 0) return ret;
ret = do_write_string(ff, pmu->name); if (ret < 0) return ret; }
return 0;}
/* * File format: * * struct group_descs { * u32 nr_groups; * struct group_desc { * char name[]; * u32 leader_idx; * u32 nr_members; * }[nr_groups]; * }; */static int write_group_desc(struct feat_fd *ff, struct evlist *evlist){ u32 nr_groups = evlist__nr_groups(evlist); struct evsel *evsel; int ret;
ret = do_write(ff, &nr_groups, sizeof(nr_groups)); if (ret < 0) return ret;
evlist__for_each_entry(evlist, evsel) { if (evsel__is_group_leader(evsel) && evsel->core.nr_members > 1) { const char *name = evsel->group_name ?: "{anon_group}"; u32 leader_idx = evsel->core.idx; u32 nr_members = evsel->core.nr_members;
ret = do_write_string(ff, name); if (ret < 0) return ret;
ret = do_write(ff, &leader_idx, sizeof(leader_idx)); if (ret < 0) return ret;
ret = do_write(ff, &nr_members, sizeof(nr_members)); if (ret < 0) return ret; } } return 0;}
/* * Return the CPU id as a raw string. * * Each architecture should provide a more precise id string that * can be use to match the architecture's "mapfile". */char * __weak get_cpuid_str(struct perf_cpu cpu __maybe_unused){ return NULL;}
char *get_cpuid_allow_env_override(struct perf_cpu cpu){ char *cpuid; static bool printed;
cpuid = getenv("PERF_CPUID"); if (cpuid) cpuid = strdup(cpuid); if (!cpuid) cpuid = get_cpuid_str(cpu); if (!cpuid) return NULL;
if (!printed) { pr_debug("Using CPUID %s\n", cpuid); printed = true; } return cpuid;}
/* Return zero when the cpuid from the mapfile.csv matches the * cpuid string generated on this platform. * Otherwise return non-zero. */int __weak strcmp_cpuid_str(const char *mapcpuid, const char *cpuid){ regex_t re; regmatch_t pmatch[1]; int match;
if (regcomp(&re, mapcpuid, REG_EXTENDED) != 0) { /* Warn unable to generate match particular string. */ pr_info("Invalid regular expression %s\n", mapcpuid); return 1; }
match = !regexec(&re, cpuid, 1, pmatch, 0); regfree(&re); if (match) { size_t match_len = (pmatch[0].rm_eo - pmatch[0].rm_so);
/* Verify the entire string matched. */ if (match_len == strlen(cpuid)) return 0; } return 1;}
/* * default get_cpuid(): nothing gets recorded * actual implementation must be in arch/$(SRCARCH)/util/header.c */int __weak get_cpuid(char *buffer __maybe_unused, size_t sz __maybe_unused, struct perf_cpu cpu __maybe_unused){ return ENOSYS; /* Not implemented */}
static int write_cpuid(struct feat_fd *ff, struct evlist *evlist){ struct perf_cpu cpu = perf_cpu_map__min(evlist->core.all_cpus); char buffer[64]; int ret;
ret = get_cpuid(buffer, sizeof(buffer), cpu); if (ret) return -1;
return do_write_string(ff, buffer);}
static int write_branch_stack(struct feat_fd *ff __maybe_unused, struct evlist *evlist __maybe_unused){ return 0;}
static int write_auxtrace(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct perf_session *session; int err;
if (WARN(ff->buf, "Error: calling %s in pipe-mode.\n", __func__)) return -1;
session = container_of(ff->ph, struct perf_session, header);
err = auxtrace_index__write(ff->fd, &session->auxtrace_index); if (err < 0) pr_err("Failed to write auxtrace index\n"); return err;}
static int write_clockid(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ return do_write(ff, &ff->ph->env.clock.clockid_res_ns, sizeof(ff->ph->env.clock.clockid_res_ns));}
static int write_clock_data(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ u64 *data64; u32 data32; int ret;
/* version */ data32 = 1;
ret = do_write(ff, &data32, sizeof(data32)); if (ret < 0) return ret;
/* clockid */ data32 = ff->ph->env.clock.clockid;
ret = do_write(ff, &data32, sizeof(data32)); if (ret < 0) return ret;
/* TOD ref time */ data64 = &ff->ph->env.clock.tod_ns;
ret = do_write(ff, data64, sizeof(*data64)); if (ret < 0) return ret;
/* clockid ref time */ data64 = &ff->ph->env.clock.clockid_ns;
return do_write(ff, data64, sizeof(*data64));}
static int write_hybrid_topology(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct hybrid_topology *tp; int ret; u32 i;
tp = hybrid_topology__new(); if (!tp) return -ENOENT;
ret = do_write(ff, &tp->nr, sizeof(u32)); if (ret < 0) goto err;
for (i = 0; i < tp->nr; i++) { struct hybrid_topology_node *n = &tp->nodes[i];
ret = do_write_string(ff, n->pmu_name); if (ret < 0) goto err;
ret = do_write_string(ff, n->cpus); if (ret < 0) goto err; }
ret = 0;
err: hybrid_topology__delete(tp); return ret;}
static int write_dir_format(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct perf_session *session; struct perf_data *data;
session = container_of(ff->ph, struct perf_session, header); data = session->data;
if (WARN_ON(!perf_data__is_dir(data))) return -1;
return do_write(ff, &data->dir.version, sizeof(data->dir.version));}
static int write_bpf_prog_info(struct feat_fd *ff __maybe_unused, struct evlist *evlist __maybe_unused){#ifdef HAVE_LIBBPF_SUPPORT struct perf_env *env = &ff->ph->env; struct rb_root *root; struct rb_node *next; int ret = 0;
down_read(&env->bpf_progs.lock);
ret = do_write(ff, &env->bpf_progs.infos_cnt, sizeof(env->bpf_progs.infos_cnt)); if (ret < 0 || env->bpf_progs.infos_cnt == 0) goto out;
root = &env->bpf_progs.infos; next = rb_first(root); while (next) { struct bpf_prog_info_node *node; size_t len;
node = rb_entry(next, struct bpf_prog_info_node, rb_node); next = rb_next(&node->rb_node); len = sizeof(struct perf_bpil) + node->info_linear->data_len;
/* before writing to file, translate address to offset */ bpil_addr_to_offs(node->info_linear); ret = do_write(ff, node->info_linear, len); /* * translate back to address even when do_write() fails, * so that this function never changes the data. */ bpil_offs_to_addr(node->info_linear); if (ret < 0) goto out; }out: up_read(&env->bpf_progs.lock); return ret;#else pr_err("ERROR: Trying to write bpf_prog_info without libbpf support.\n"); return -1;#endif // HAVE_LIBBPF_SUPPORT}
static int write_bpf_btf(struct feat_fd *ff __maybe_unused, struct evlist *evlist __maybe_unused){#ifdef HAVE_LIBBPF_SUPPORT struct perf_env *env = &ff->ph->env; struct rb_root *root; struct rb_node *next; int ret = 0;
down_read(&env->bpf_progs.lock);
ret = do_write(ff, &env->bpf_progs.btfs_cnt, sizeof(env->bpf_progs.btfs_cnt));
if (ret < 0 || env->bpf_progs.btfs_cnt == 0) goto out;
root = &env->bpf_progs.btfs; next = rb_first(root); while (next) { struct btf_node *node;
node = rb_entry(next, struct btf_node, rb_node); next = rb_next(&node->rb_node); ret = do_write(ff, &node->id, sizeof(u32) * 2 + node->data_size); if (ret < 0) goto out; }out: up_read(&env->bpf_progs.lock); return ret;#else pr_err("ERROR: Trying to write btf data without libbpf support.\n"); return -1;#endif // HAVE_LIBBPF_SUPPORT}
static int cpu_cache_level__sort(const void *a, const void *b){ struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a; struct cpu_cache_level *cache_b = (struct cpu_cache_level *)b;
return cache_a->level - cache_b->level;}
static bool cpu_cache_level__cmp(struct cpu_cache_level *a, struct cpu_cache_level *b){ if (a->level != b->level) return false;
if (a->line_size != b->line_size) return false;
if (a->sets != b->sets) return false;
if (a->ways != b->ways) return false;
if (strcmp(a->type, b->type)) return false;
if (strcmp(a->size, b->size)) return false;
if (strcmp(a->map, b->map)) return false;
return true;}
static int cpu_cache_level__read(struct cpu_cache_level *cache, u32 cpu, u16 level){ char path[PATH_MAX], file[PATH_MAX]; struct stat st; size_t len;
scnprintf(path, PATH_MAX, "devices/system/cpu/cpu%d/cache/index%d/", cpu, level); scnprintf(file, PATH_MAX, "%s/%s", sysfs__mountpoint(), path);
if (stat(file, &st)) return 1;
scnprintf(file, PATH_MAX, "%s/level", path); if (sysfs__read_int(file, (int *) &cache->level)) return -1;
scnprintf(file, PATH_MAX, "%s/coherency_line_size", path); if (sysfs__read_int(file, (int *) &cache->line_size)) return -1;
scnprintf(file, PATH_MAX, "%s/number_of_sets", path); if (sysfs__read_int(file, (int *) &cache->sets)) return -1;
scnprintf(file, PATH_MAX, "%s/ways_of_associativity", path); if (sysfs__read_int(file, (int *) &cache->ways)) return -1;
scnprintf(file, PATH_MAX, "%s/type", path); if (sysfs__read_str(file, &cache->type, &len)) return -1;
cache->type[len] = 0; cache->type = strim(cache->type);
scnprintf(file, PATH_MAX, "%s/size", path); if (sysfs__read_str(file, &cache->size, &len)) { zfree(&cache->type); return -1; }
cache->size[len] = 0; cache->size = strim(cache->size);
scnprintf(file, PATH_MAX, "%s/shared_cpu_list", path); if (sysfs__read_str(file, &cache->map, &len)) { zfree(&cache->size); zfree(&cache->type); return -1; }
cache->map[len] = 0; cache->map = strim(cache->map); return 0;}
static void cpu_cache_level__fprintf(FILE *out, struct cpu_cache_level *c){ fprintf(out, "L%d %-15s %8s [%s]\n", c->level, c->type, c->size, c->map);}
/* * Build caches levels for a particular CPU from the data in * /sys/devices/system/cpu/cpu<cpu>/cache/ * The cache level data is stored in caches[] from index at * *cntp. */int build_caches_for_cpu(u32 cpu, struct cpu_cache_level caches[], u32 *cntp){ u16 level;
for (level = 0; level < MAX_CACHE_LVL; level++) { struct cpu_cache_level c; int err; u32 i;
err = cpu_cache_level__read(&c, cpu, level); if (err < 0) return err;
if (err == 1) break;
for (i = 0; i < *cntp; i++) { if (cpu_cache_level__cmp(&c, &caches[i])) break; }
if (i == *cntp) { caches[*cntp] = c; *cntp = *cntp + 1; } else cpu_cache_level__free(&c); }
return 0;}
static int build_caches(struct cpu_cache_level caches[], u32 *cntp){ u32 nr, cpu, cnt = 0;
nr = cpu__max_cpu().cpu;
for (cpu = 0; cpu < nr; cpu++) { int ret = build_caches_for_cpu(cpu, caches, &cnt);
if (ret) return ret; } *cntp = cnt; return 0;}
static int write_cache(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ u32 max_caches = cpu__max_cpu().cpu * MAX_CACHE_LVL; struct cpu_cache_level caches[max_caches]; u32 cnt = 0, i, version = 1; int ret;
ret = build_caches(caches, &cnt); if (ret) goto out;
qsort(&caches, cnt, sizeof(struct cpu_cache_level), cpu_cache_level__sort);
ret = do_write(ff, &version, sizeof(u32)); if (ret < 0) goto out;
ret = do_write(ff, &cnt, sizeof(u32)); if (ret < 0) goto out;
for (i = 0; i < cnt; i++) { struct cpu_cache_level *c = &caches[i];
#define _W(v) \ ret = do_write(ff, &c->v, sizeof(u32)); \ if (ret < 0) \ goto out;
_W(level) _W(line_size) _W(sets) _W(ways) #undef _W
#define _W(v) \ ret = do_write_string(ff, (const char *) c->v); \ if (ret < 0) \ goto out;
_W(type) _W(size) _W(map) #undef _W }
out: for (i = 0; i < cnt; i++) cpu_cache_level__free(&caches[i]); return ret;}
static int write_cln_size(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ int cln_size = cacheline_size();
if (!cln_size) cln_size = DEFAULT_CACHELINE_SIZE;
ff->ph->env.cln_size = cln_size;
return do_write(ff, &cln_size, sizeof(cln_size));}
static int write_stat(struct feat_fd *ff __maybe_unused, struct evlist *evlist __maybe_unused){ return 0;}
static int write_sample_time(struct feat_fd *ff, struct evlist *evlist){ int ret;
ret = do_write(ff, &evlist->first_sample_time, sizeof(evlist->first_sample_time)); if (ret < 0) return ret;
return do_write(ff, &evlist->last_sample_time, sizeof(evlist->last_sample_time));}
static int memory_node__read(struct memory_node *n, unsigned long idx){ unsigned int phys, size = 0; char path[PATH_MAX]; struct io_dirent64 *ent; struct io_dir dir;
#define for_each_memory(mem, dir) \ while ((ent = io_dir__readdir(&dir)) != NULL) \ if (strcmp(ent->d_name, ".") && \ strcmp(ent->d_name, "..") && \ sscanf(ent->d_name, "memory%u", &mem) == 1)
scnprintf(path, PATH_MAX, "%s/devices/system/node/node%lu", sysfs__mountpoint(), idx);
io_dir__init(&dir, open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); if (dir.dirfd < 0) { pr_warning("failed: can't open memory sysfs data '%s'\n", path); return -1; }
for_each_memory(phys, dir) { size = max(phys, size); }
size++;
n->set = bitmap_zalloc(size); if (!n->set) { close(dir.dirfd); return -ENOMEM; }
n->node = idx; n->size = size;
io_dir__rewinddir(&dir);
for_each_memory(phys, dir) { __set_bit(phys, n->set); }
close(dir.dirfd); return 0;}
static void memory_node__delete_nodes(struct memory_node *nodesp, u64 cnt){ for (u64 i = 0; i < cnt; i++) bitmap_free(nodesp[i].set);
free(nodesp);}
static int memory_node__sort(const void *a, const void *b){ const struct memory_node *na = a; const struct memory_node *nb = b;
return na->node - nb->node;}
static int build_mem_topology(struct memory_node **nodesp, u64 *cntp){ char path[PATH_MAX]; struct io_dirent64 *ent; struct io_dir dir; int ret = 0; size_t cnt = 0, size = 0; struct memory_node *nodes = NULL;
scnprintf(path, PATH_MAX, "%s/devices/system/node/", sysfs__mountpoint());
io_dir__init(&dir, open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); if (dir.dirfd < 0) { pr_debug2("%s: couldn't read %s, does this arch have topology information?\n", __func__, path); return -1; }
while (!ret && (ent = io_dir__readdir(&dir))) { unsigned int idx; int r;
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) continue;
r = sscanf(ent->d_name, "node%u", &idx); if (r != 1) continue;
if (cnt >= size) { struct memory_node *new_nodes = reallocarray(nodes, cnt + 4, sizeof(*nodes));
if (!new_nodes) { pr_err("Failed to write MEM_TOPOLOGY, size %zd nodes\n", size); ret = -ENOMEM; goto out; } nodes = new_nodes; size += 4; } ret = memory_node__read(&nodes[cnt], idx); if (!ret) cnt += 1; }out: close(dir.dirfd); if (!ret) { *cntp = cnt; *nodesp = nodes; qsort(nodes, cnt, sizeof(nodes[0]), memory_node__sort); } else memory_node__delete_nodes(nodes, cnt);
return ret;}
/* * The MEM_TOPOLOGY holds physical memory map for every * node in system. The format of data is as follows: * * 0 - version | for future changes * 8 - block_size_bytes | /sys/devices/system/memory/block_size_bytes * 16 - count | number of nodes * * For each node we store map of physical indexes for * each node: * * 32 - node id | node index * 40 - size | size of bitmap * 48 - bitmap | bitmap of memory indexes that belongs to node */static int write_mem_topology(struct feat_fd *ff __maybe_unused, struct evlist *evlist __maybe_unused){ struct memory_node *nodes = NULL; u64 bsize, version = 1, i, nr = 0; int ret;
ret = sysfs__read_xll("devices/system/memory/block_size_bytes", (unsigned long long *) &bsize); if (ret) return ret;
ret = build_mem_topology(&nodes, &nr); if (ret) return ret;
ret = do_write(ff, &version, sizeof(version)); if (ret < 0) goto out;
ret = do_write(ff, &bsize, sizeof(bsize)); if (ret < 0) goto out;
ret = do_write(ff, &nr, sizeof(nr)); if (ret < 0) goto out;
for (i = 0; i < nr; i++) { struct memory_node *n = &nodes[i];
#define _W(v) \ ret = do_write(ff, &n->v, sizeof(n->v)); \ if (ret < 0) \ goto out;
_W(node) _W(size)
#undef _W
ret = do_write_bitmap(ff, n->set, n->size); if (ret < 0) goto out; }
out: memory_node__delete_nodes(nodes, nr); return ret;}
static int write_compressed(struct feat_fd *ff __maybe_unused, struct evlist *evlist __maybe_unused){ int ret;
ret = do_write(ff, &(ff->ph->env.comp_ver), sizeof(ff->ph->env.comp_ver)); if (ret) return ret;
ret = do_write(ff, &(ff->ph->env.comp_type), sizeof(ff->ph->env.comp_type)); if (ret) return ret;
ret = do_write(ff, &(ff->ph->env.comp_level), sizeof(ff->ph->env.comp_level)); if (ret) return ret;
ret = do_write(ff, &(ff->ph->env.comp_ratio), sizeof(ff->ph->env.comp_ratio)); if (ret) return ret;
return do_write(ff, &(ff->ph->env.comp_mmap_len), sizeof(ff->ph->env.comp_mmap_len));}
static int __write_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu, bool write_pmu){ struct perf_pmu_caps *caps = NULL; int ret;
ret = do_write(ff, &pmu->nr_caps, sizeof(pmu->nr_caps)); if (ret < 0) return ret;
list_for_each_entry(caps, &pmu->caps, list) { ret = do_write_string(ff, caps->name); if (ret < 0) return ret;
ret = do_write_string(ff, caps->value); if (ret < 0) return ret; }
if (write_pmu) { ret = do_write_string(ff, pmu->name); if (ret < 0) return ret; }
return ret;}
static int write_cpu_pmu_caps(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct perf_pmu *cpu_pmu = perf_pmus__find_core_pmu(); int ret;
if (!cpu_pmu) return -ENOENT;
ret = perf_pmu__caps_parse(cpu_pmu); if (ret < 0) return ret;
return __write_pmu_caps(ff, cpu_pmu, false);}
static int write_pmu_caps(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ struct perf_pmu *pmu = NULL; int nr_pmu = 0; int ret;
while ((pmu = perf_pmus__scan(pmu))) { if (!strcmp(pmu->name, "cpu")) { /* * The "cpu" PMU is special and covered by * HEADER_CPU_PMU_CAPS. Note, core PMUs are * counted/written here for ARM, s390 and Intel hybrid. */ continue; } if (perf_pmu__caps_parse(pmu) <= 0) continue; nr_pmu++; }
ret = do_write(ff, &nr_pmu, sizeof(nr_pmu)); if (ret < 0) return ret;
if (!nr_pmu) return 0;
/* * Note older perf tools assume core PMUs come first, this is a property * of perf_pmus__scan. */ pmu = NULL; while ((pmu = perf_pmus__scan(pmu))) { if (!strcmp(pmu->name, "cpu")) { /* Skip as above. */ continue; } if (perf_pmu__caps_parse(pmu) <= 0) continue; ret = __write_pmu_caps(ff, pmu, true); if (ret < 0) return ret; } return 0;}
struct cpu_domain_map **build_cpu_domain_map(u32 *schedstat_version, u32 *max_sched_domains, u32 nr){ char dname[DNAME_LEN], cpumask[MAX_NR_CPUS]; struct domain_info *domain_info; struct cpu_domain_map **cd_map; char cpulist[MAX_NR_CPUS]; char *line = NULL; u32 cpu, domain; u32 dcount = 0; size_t len; FILE *fp;
fp = fopen("/proc/schedstat", "r"); if (!fp) { pr_err("Failed to open /proc/schedstat\n"); return NULL; }
cd_map = zalloc(sizeof(*cd_map) * nr); if (!cd_map) goto out;
while (getline(&line, &len, fp) > 0) { int retval;
if (strncmp(line, "version", 7) == 0) { retval = sscanf(line, "version %d\n", schedstat_version); if (retval != 1) continue;
} else if (strncmp(line, "cpu", 3) == 0) { retval = sscanf(line, "cpu%u %*s", &cpu); if (retval == 1) { cd_map[cpu] = zalloc(sizeof(*cd_map[cpu])); if (!cd_map[cpu]) goto out_free_line; cd_map[cpu]->cpu = cpu; } else continue;
dcount = 0; } else if (strncmp(line, "domain", 6) == 0) { struct domain_info **temp_domains;
dcount++; temp_domains = realloc(cd_map[cpu]->domains, dcount * sizeof(domain_info)); if (!temp_domains) goto out_free_line; else cd_map[cpu]->domains = temp_domains;
domain_info = zalloc(sizeof(*domain_info)); if (!domain_info) goto out_free_line;
cd_map[cpu]->domains[dcount - 1] = domain_info;
if (*schedstat_version >= 17) { retval = sscanf(line, "domain%u %s %s %*s", &domain, dname, cpumask); if (retval != 3) continue;
domain_info->dname = strdup(dname); if (!domain_info->dname) goto out_free_line; } else { retval = sscanf(line, "domain%u %s %*s", &domain, cpumask); if (retval != 2) continue; }
domain_info->domain = domain; if (domain > *max_sched_domains) *max_sched_domains = domain;
domain_info->cpumask = strdup(cpumask); if (!domain_info->cpumask) goto out_free_line;
cpumask_to_cpulist(cpumask, cpulist); domain_info->cpulist = strdup(cpulist); if (!domain_info->cpulist) goto out_free_line;
cd_map[cpu]->nr_domains = dcount; } }
out_free_line: free(line);out: fclose(fp); return cd_map;}
static int write_cpu_domain_info(struct feat_fd *ff, struct evlist *evlist __maybe_unused){ u32 max_sched_domains = 0, schedstat_version = 0; struct cpu_domain_map **cd_map; u32 i, j, nr, ret;
nr = cpu__max_present_cpu().cpu;
cd_map = build_cpu_domain_map(&schedstat_version, &max_sched_domains, nr); if (!cd_map) return -1;
ret = do_write(ff, &schedstat_version, sizeof(u32)); if (ret < 0) goto out;
max_sched_domains += 1; ret = do_write(ff, &max_sched_domains, sizeof(u32)); if (ret < 0) goto out;
for (i = 0; i < nr; i++) { if (!cd_map[i]) continue;
ret = do_write(ff, &cd_map[i]->cpu, sizeof(u32)); if (ret < 0) goto out;
ret = do_write(ff, &cd_map[i]->nr_domains, sizeof(u32)); if (ret < 0) goto out;
for (j = 0; j < cd_map[i]->nr_domains; j++) { ret = do_write(ff, &cd_map[i]->domains[j]->domain, sizeof(u32)); if (ret < 0) goto out; if (schedstat_version >= 17) { ret = do_write_string(ff, cd_map[i]->domains[j]->dname); if (ret < 0) goto out; }
ret = do_write_string(ff, cd_map[i]->domains[j]->cpumask); if (ret < 0) goto out;
ret = do_write_string(ff, cd_map[i]->domains[j]->cpulist); if (ret < 0) goto out; } }
out: free_cpu_domain_info(cd_map, schedstat_version, nr); return ret;}
static void print_hostname(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);}
static void print_osrelease(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# os release : %s\n", ff->ph->env.os_release);}
static void print_arch(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# arch : %s\n", ff->ph->env.arch);}
static void print_e_machine(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# e_machine : %u\n", ff->ph->env.e_machine); fprintf(fp, "# e_flags : %u\n", ff->ph->env.e_flags);}
static void print_cpudesc(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# cpudesc : %s\n", ff->ph->env.cpu_desc);}
static void print_nrcpus(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# nrcpus online : %u\n", ff->ph->env.nr_cpus_online); fprintf(fp, "# nrcpus avail : %u\n", ff->ph->env.nr_cpus_avail);}
static void print_version(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# perf version : %s\n", ff->ph->env.version);}
static void print_cmdline(struct feat_fd *ff, FILE *fp){ int nr, i;
nr = ff->ph->env.nr_cmdline;
fprintf(fp, "# cmdline : ");
for (i = 0; i < nr; i++) { char *argv_i = strdup(ff->ph->env.cmdline_argv[i]); if (!argv_i) { fprintf(fp, "%s ", ff->ph->env.cmdline_argv[i]); } else { char *mem = argv_i; do { char *quote = strchr(argv_i, '\''); if (!quote) break; *quote++ = '\0'; fprintf(fp, "%s\\\'", argv_i); argv_i = quote; } while (1); fprintf(fp, "%s ", argv_i); free(mem); } } fputc('\n', fp);}
static void print_cpu_topology(struct feat_fd *ff, FILE *fp){ struct perf_header *ph = ff->ph; int cpu_nr = ph->env.nr_cpus_avail; int nr, i; char *str;
nr = ph->env.nr_sibling_cores; str = ph->env.sibling_cores;
for (i = 0; i < nr; i++) { fprintf(fp, "# sibling sockets : %s\n", str); str += strlen(str) + 1; }
if (ph->env.nr_sibling_dies) { nr = ph->env.nr_sibling_dies; str = ph->env.sibling_dies;
for (i = 0; i < nr; i++) { fprintf(fp, "# sibling dies : %s\n", str); str += strlen(str) + 1; } }
nr = ph->env.nr_sibling_threads; str = ph->env.sibling_threads;
for (i = 0; i < nr; i++) { fprintf(fp, "# sibling threads : %s\n", str); str += strlen(str) + 1; }
if (ph->env.nr_sibling_dies) { if (ph->env.cpu != NULL) { for (i = 0; i < cpu_nr; i++) fprintf(fp, "# CPU %d: Core ID %d, " "Die ID %d, Socket ID %d\n", i, ph->env.cpu[i].core_id, ph->env.cpu[i].die_id, ph->env.cpu[i].socket_id); } else fprintf(fp, "# Core ID, Die ID and Socket ID " "information is not available\n"); } else { if (ph->env.cpu != NULL) { for (i = 0; i < cpu_nr; i++) fprintf(fp, "# CPU %d: Core ID %d, " "Socket ID %d\n", i, ph->env.cpu[i].core_id, ph->env.cpu[i].socket_id); } else fprintf(fp, "# Core ID and Socket ID " "information is not available\n"); }}
static void print_clockid(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# clockid frequency: %"PRIu64" MHz\n", ff->ph->env.clock.clockid_res_ns * 1000);}
static void print_clock_data(struct feat_fd *ff, FILE *fp){ struct timespec clockid_ns; char tstr[64], date[64]; struct timeval tod_ns; clockid_t clockid; struct tm ltime; u64 ref;
if (!ff->ph->env.clock.enabled) { fprintf(fp, "# reference time disabled\n"); return; }
/* Compute TOD time. */ ref = ff->ph->env.clock.tod_ns; tod_ns.tv_sec = ref / NSEC_PER_SEC; ref -= tod_ns.tv_sec * NSEC_PER_SEC; tod_ns.tv_usec = ref / NSEC_PER_USEC;
/* Compute clockid time. */ ref = ff->ph->env.clock.clockid_ns; clockid_ns.tv_sec = ref / NSEC_PER_SEC; ref -= clockid_ns.tv_sec * NSEC_PER_SEC; clockid_ns.tv_nsec = ref;
clockid = ff->ph->env.clock.clockid;
if (localtime_r(&tod_ns.tv_sec, <ime) == NULL) snprintf(tstr, sizeof(tstr), "<error>"); else { strftime(date, sizeof(date), "%F %T", <ime); scnprintf(tstr, sizeof(tstr), "%s.%06d", date, (int) tod_ns.tv_usec); }
fprintf(fp, "# clockid: %s (%u)\n", clockid_name(clockid), clockid); fprintf(fp, "# reference time: %s = %ld.%06d (TOD) = %ld.%09ld (%s)\n", tstr, (long) tod_ns.tv_sec, (int) tod_ns.tv_usec, (long) clockid_ns.tv_sec, clockid_ns.tv_nsec, clockid_name(clockid));}
static void print_hybrid_topology(struct feat_fd *ff, FILE *fp){ int i; struct hybrid_node *n;
fprintf(fp, "# hybrid cpu system:\n"); for (i = 0; i < ff->ph->env.nr_hybrid_nodes; i++) { n = &ff->ph->env.hybrid_nodes[i]; fprintf(fp, "# %s cpu list : %s\n", n->pmu_name, n->cpus); }}
static void print_dir_format(struct feat_fd *ff, FILE *fp){ struct perf_session *session; struct perf_data *data;
session = container_of(ff->ph, struct perf_session, header); data = session->data;
fprintf(fp, "# directory data version : %"PRIu64"\n", data->dir.version);}
static void print_bpf_prog_info(struct feat_fd *ff __maybe_unused, FILE *fp){#ifdef HAVE_LIBBPF_SUPPORT struct perf_env *env = &ff->ph->env; struct rb_root *root; struct rb_node *next;
down_read(&env->bpf_progs.lock);
root = &env->bpf_progs.infos; next = rb_first(root);
if (!next) fprintf(fp, "# bpf_prog_info empty\n");
while (next) { struct bpf_prog_info_node *node;
node = rb_entry(next, struct bpf_prog_info_node, rb_node); next = rb_next(&node->rb_node);
__bpf_event__print_bpf_prog_info(node->info_linear, env, fp); }
up_read(&env->bpf_progs.lock);#else fprintf(fp, "# bpf_prog_info missing, no libbpf support\n");#endif // HAVE_LIBBPF_SUPPORT}
static void print_bpf_btf(struct feat_fd *ff __maybe_unused, FILE *fp){#ifdef HAVE_LIBBPF_SUPPORT struct perf_env *env = &ff->ph->env; struct rb_root *root; struct rb_node *next;
down_read(&env->bpf_progs.lock);
root = &env->bpf_progs.btfs; next = rb_first(root);
if (!next) printf("# btf info empty\n");
while (next) { struct btf_node *node;
node = rb_entry(next, struct btf_node, rb_node); next = rb_next(&node->rb_node); fprintf(fp, "# btf info of id %u\n", node->id); }
up_read(&env->bpf_progs.lock);#else fprintf(fp, "# bpf btf data missing, no libbpf support\n");#endif // HAVE_LIBBPF_SUPPORT}
static void free_event_desc(struct evsel *events){ struct evsel *evsel;
if (!events) return;
for (evsel = events; evsel->core.attr.size; evsel++) { zfree(&evsel->name); zfree(&evsel->core.id); }
free(events);}
static bool perf_attr_check(struct perf_event_attr *attr){ if (attr->__reserved_1 || attr->__reserved_2 || attr->__reserved_3) { pr_warning("Reserved bits are set unexpectedly. " "Please update perf tool.\n"); return false; }
if (attr->sample_type & ~(PERF_SAMPLE_MAX-1)) { pr_warning("Unknown sample type (0x%llx) is detected. " "Please update perf tool.\n", attr->sample_type); return false; }
if (attr->read_format & ~(PERF_FORMAT_MAX-1)) { pr_warning("Unknown read format (0x%llx) is detected. " "Please update perf tool.\n", attr->read_format); return false; }
if ((attr->sample_type & PERF_SAMPLE_BRANCH_STACK) && (attr->branch_sample_type & ~(PERF_SAMPLE_BRANCH_MAX-1))) { pr_warning("Unknown branch sample type (0x%llx) is detected. " "Please update perf tool.\n", attr->branch_sample_type);
return false; }
return true;}
static struct evsel *read_event_desc(struct feat_fd *ff){ struct evsel *evsel, *events = NULL; u64 *id; void *buf = NULL; u32 nre, sz, nr, i, j; size_t msz;
/* number of events */ if (do_read_u32(ff, &nre)) goto error;
/* Size of each of the nre attributes. */ if (do_read_u32(ff, &sz)) goto error;
/* * Require at least one event with an attr no smaller than the * first published struct, and reject sz values where * sz + sizeof(u32) would overflow size_t (possible on 32-bit) * or nre == UINT32_MAX where nre + 1 wraps to 0 in the calloc. * * The minimum section footprint per event is sz bytes for the * attr plus a u32 for the id count, check that nre events fit. */ if (!nre || sz < PERF_ATTR_SIZE_VER0 || sz > ff->size || (size_t)sz > SIZE_MAX - sizeof(u32) || nre == UINT32_MAX || nre > (ff->size - ff->offset) / (sz + sizeof(u32))) { pr_err("Invalid HEADER_EVENT_DESC: nre=%u sz=%u (min %d)\n", nre, sz, PERF_ATTR_SIZE_VER0); goto error; }
/* buffer to hold on file attr struct */ buf = malloc(sz); if (!buf) goto error;
/* the last event terminates with evsel->core.attr.size == 0: */ events = calloc(nre + 1, sizeof(*events)); if (!events) goto error;
msz = sizeof(evsel->core.attr); if (sz < msz) msz = sz;
for (i = 0, evsel = events; i < nre; evsel++, i++) { struct perf_event_attr *attr = buf; u32 attr_size;
evsel->core.idx = i;
/* * must read entire on-file attr struct to * sync up with layout. */ if (__do_read(ff, buf, sz)) goto error;
/* Reject before attr_swap to prevent OOB via bswap_safe() */ attr_size = ff->ph->needs_swap ? bswap_32(attr->size) : attr->size; /* ABI0: size == 0 means the producer didn't set it */ if (!attr_size) { attr_size = PERF_ATTR_SIZE_VER0; /* * Write back so free_event_desc() doesn't * treat this event as the end-of-array sentinel * (it iterates while attr.size != 0). * * Only for native — the swap path must NOT * write native-endian VER0 here because * perf_event__attr_swap() would re-swap it * to 0x40000000, defeating bswap_safe() bounds. * perf_event__attr_swap() has its own ABI0 * fallback that sets VER0 after swapping. */ if (!ff->ph->needs_swap) attr->size = attr_size; } if (attr_size < PERF_ATTR_SIZE_VER0 || attr_size > sz) { pr_err("Event %d attr.size (%u) invalid (min: %d, max: %u)\n", i, attr_size, PERF_ATTR_SIZE_VER0, sz); goto error; }
if (ff->ph->needs_swap) perf_event__attr_swap(buf);
memcpy(&evsel->core.attr, buf, msz);
if (!perf_attr_check(&evsel->core.attr)) goto error;
if (do_read_u32(ff, &nr)) goto error;
if (ff->ph->needs_swap) evsel->needs_swap = true;
evsel->name = do_read_string(ff); if (!evsel->name) goto error;
if (!nr) continue;
/* Prevent oversized allocation from crafted nr */ if (nr > (ff->size - ff->offset) / sizeof(*id)) { pr_err("Event %d: id count %u exceeds remaining section\n", i, nr); goto error; }
id = calloc(nr, sizeof(*id)); if (!id) goto error; evsel->core.ids = nr; evsel->core.id = id;
for (j = 0 ; j < nr; j++) { if (do_read_u64(ff, id)) goto error; id++; } }out: free(buf); return events;error: free_event_desc(events); events = NULL; goto out;}
static int __desc_attr__fprintf(FILE *fp, const char *name, const char *val, void *priv __maybe_unused){ return fprintf(fp, ", %s = %s", name, val);}
static void print_event_desc(struct feat_fd *ff, FILE *fp){ struct evsel *evsel, *events; u32 j; u64 *id;
if (ff->events) events = ff->events; else events = read_event_desc(ff);
if (!events) { fprintf(fp, "# event desc: not available or unable to read\n"); return; }
for (evsel = events; evsel->core.attr.size; evsel++) { fprintf(fp, "# event : name = %s, ", evsel->name);
if (evsel->core.ids) { fprintf(fp, ", id = {"); for (j = 0, id = evsel->core.id; j < evsel->core.ids; j++, id++) { if (j) fputc(',', fp); fprintf(fp, " %"PRIu64, *id); } fprintf(fp, " }"); }
perf_event_attr__fprintf(fp, &evsel->core.attr, __desc_attr__fprintf, NULL);
fputc('\n', fp); }
free_event_desc(events); ff->events = NULL;}
static void print_total_mem(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# total memory : %llu kB\n", ff->ph->env.total_mem);}
static void print_numa_topology(struct feat_fd *ff, FILE *fp){ int i; struct numa_node *n;
for (i = 0; i < ff->ph->env.nr_numa_nodes; i++) { n = &ff->ph->env.numa_nodes[i];
fprintf(fp, "# node%u meminfo : total = %"PRIu64" kB," " free = %"PRIu64" kB\n", n->node, n->mem_total, n->mem_free);
fprintf(fp, "# node%u cpu list : ", n->node); cpu_map__fprintf(n->map, fp); }}
static void print_cpuid(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# cpuid : %s\n", ff->ph->env.cpuid);}
static void print_branch_stack(struct feat_fd *ff __maybe_unused, FILE *fp){ fprintf(fp, "# contains samples with branch stack\n");}
static void print_auxtrace(struct feat_fd *ff __maybe_unused, FILE *fp){ fprintf(fp, "# contains AUX area data (e.g. instruction trace)\n");}
static void print_stat(struct feat_fd *ff __maybe_unused, FILE *fp){ fprintf(fp, "# contains stat data\n");}
static void print_cache(struct feat_fd *ff, FILE *fp __maybe_unused){ int i;
fprintf(fp, "# CPU cache info:\n"); for (i = 0; i < ff->ph->env.caches_cnt; i++) { fprintf(fp, "# "); cpu_cache_level__fprintf(fp, &ff->ph->env.caches[i]); }}
static void print_cln_size(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# cacheline size: %u\n", ff->ph->env.cln_size);}
static void print_compressed(struct feat_fd *ff, FILE *fp){ fprintf(fp, "# compressed : %s, level = %d, ratio = %d\n", ff->ph->env.comp_type == PERF_COMP_ZSTD ? "Zstd" : "Unknown", ff->ph->env.comp_level, ff->ph->env.comp_ratio);}
static void __print_pmu_caps(FILE *fp, int nr_caps, char **caps, char *pmu_name){ const char *delimiter = ""; int i;
if (!nr_caps) { fprintf(fp, "# %s pmu capabilities: not available\n", pmu_name); return; }
fprintf(fp, "# %s pmu capabilities: ", pmu_name); for (i = 0; i < nr_caps; i++) { fprintf(fp, "%s%s", delimiter, caps[i]); delimiter = ", "; }
fprintf(fp, "\n");}
static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp){ __print_pmu_caps(fp, ff->ph->env.nr_cpu_pmu_caps, ff->ph->env.cpu_pmu_caps, (char *)"cpu");}
static void print_pmu_caps(struct feat_fd *ff, FILE *fp){ struct perf_env *env = &ff->ph->env; uint16_t e_machine = perf_env__e_machine(env, /*e_flags=*/NULL);
for (int i = 0; i < env->nr_pmus_with_caps; i++) { struct pmu_caps *pmu_caps = &env->pmu_caps[i];
__print_pmu_caps(fp, pmu_caps->nr_caps, pmu_caps->caps, pmu_caps->pmu_name); }
if ((e_machine == EM_X86_64 || e_machine == EM_386) && perf_env__has_pmu_mapping(env, "ibs_op")) { char *max_precise = perf_env__find_pmu_cap(env, "cpu", "max_precise");
if (max_precise != NULL && atoi(max_precise) == 0) fprintf(fp, "# AMD systems uses ibs_op// PMU for some precise events, e.g.: cycles:p, see the 'perf list' man page for further details.\n"); }}
static void print_pmu_mappings(struct feat_fd *ff, FILE *fp){ struct perf_env *env = &ff->ph->env; const char *delimiter = "# pmu mappings: "; char *str, *tmp; u32 pmu_num; u32 type;
pmu_num = env->nr_pmu_mappings; if (!pmu_num) { fprintf(fp, "# pmu mappings: not available\n"); return; }
str = env->pmu_mappings;
while (pmu_num) { type = strtoul(str, &tmp, 0); if (*tmp != ':') goto error;
str = tmp + 1; fprintf(fp, "%s%s = %" PRIu32, delimiter, str, type);
delimiter = ", "; str += strlen(str) + 1; pmu_num--; }
fprintf(fp, "\n");
if (!pmu_num) return;error: fprintf(fp, "# pmu mappings: unable to read\n");}
static void print_group_desc(struct feat_fd *ff, FILE *fp){ struct perf_session *session; struct evsel *evsel; u32 nr = 0;
session = container_of(ff->ph, struct perf_session, header);
evlist__for_each_entry(session->evlist, evsel) { if (evsel__is_group_leader(evsel) && evsel->core.nr_members > 1) { fprintf(fp, "# group: %s{%s", evsel->group_name ?: "", evsel__name(evsel));
nr = evsel->core.nr_members - 1; } else if (nr) { fprintf(fp, ",%s", evsel__name(evsel));
if (--nr == 0) fprintf(fp, "}\n"); } }}
static void print_sample_time(struct feat_fd *ff, FILE *fp){ struct perf_session *session; char time_buf[32]; double d;
session = container_of(ff->ph, struct perf_session, header);
timestamp__scnprintf_usec(session->evlist->first_sample_time, time_buf, sizeof(time_buf)); fprintf(fp, "# time of first sample : %s\n", time_buf);
timestamp__scnprintf_usec(session->evlist->last_sample_time, time_buf, sizeof(time_buf)); fprintf(fp, "# time of last sample : %s\n", time_buf);
d = (double)(session->evlist->last_sample_time - session->evlist->first_sample_time) / NSEC_PER_MSEC;
fprintf(fp, "# sample duration : %10.3f ms\n", d);}
static void memory_node__fprintf(struct memory_node *n, unsigned long long bsize, FILE *fp){ char buf_map[100], buf_size[50]; unsigned long long size;
size = bsize * bitmap_weight(n->set, n->size); unit_number__scnprintf(buf_size, 50, size);
bitmap_scnprintf(n->set, n->size, buf_map, 100); fprintf(fp, "# %3" PRIu64 " [%s]: %s\n", n->node, buf_size, buf_map);}
static void print_mem_topology(struct feat_fd *ff, FILE *fp){ struct perf_env *env = &ff->ph->env; struct memory_node *nodes; int i, nr;
nodes = env->memory_nodes; nr = env->nr_memory_nodes;
fprintf(fp, "# memory nodes (nr %d, block size 0x%llx):\n", nr, env->memory_bsize);
for (i = 0; i < nr; i++) { memory_node__fprintf(&nodes[i], env->memory_bsize, fp); }}
static void print_cpu_domain_info(struct feat_fd *ff, FILE *fp){ struct cpu_domain_map **cd_map = ff->ph->env.cpu_domain; u32 nr = ff->ph->env.nr_cpus_avail; struct domain_info *d_info; u32 i, j;
fprintf(fp, "# schedstat version : %u\n", ff->ph->env.schedstat_version); fprintf(fp, "# Maximum sched domains : %u\n", ff->ph->env.max_sched_domains);
for (i = 0; i < nr; i++) { if (!cd_map[i]) continue;
fprintf(fp, "# cpu : %u\n", cd_map[i]->cpu); fprintf(fp, "# nr_domains : %u\n", cd_map[i]->nr_domains);
for (j = 0; j < cd_map[i]->nr_domains; j++) { d_info = cd_map[i]->domains[j]; if (!d_info) continue;
fprintf(fp, "# Domain : %u\n", d_info->domain);
if (ff->ph->env.schedstat_version >= 17) fprintf(fp, "# Domain name : %s\n", d_info->dname);
fprintf(fp, "# Domain cpu map : %s\n", d_info->cpumask); fprintf(fp, "# Domain cpu list : %s\n", d_info->cpulist); } }}
static int __event_process_build_id(struct perf_record_header_build_id *bev, char *filename, struct perf_session *session){ int err = -1; struct machine *machine; u16 cpumode; struct dso *dso; enum dso_space_type dso_space;
machine = perf_session__findnew_machine(session, bev->pid); if (!machine) goto out;
cpumode = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
switch (cpumode) { case PERF_RECORD_MISC_KERNEL: dso_space = DSO_SPACE__KERNEL; break; case PERF_RECORD_MISC_GUEST_KERNEL: dso_space = DSO_SPACE__KERNEL_GUEST; break; case PERF_RECORD_MISC_USER: case PERF_RECORD_MISC_GUEST_USER: dso_space = DSO_SPACE__USER; break; default: goto out; }
dso = machine__findnew_dso(machine, filename); if (dso != NULL) { char sbuild_id[SBUILD_ID_SIZE]; struct build_id bid; size_t size = BUILD_ID_SIZE;
if (bev->header.misc & PERF_RECORD_MISC_BUILD_ID_SIZE) size = bev->size;
build_id__init(&bid, bev->data, size); dso__set_build_id(dso, &bid); dso__set_header_build_id(dso, true);
if (dso_space != DSO_SPACE__USER) { struct kmod_path m = { .name = NULL, };
if (!kmod_path__parse_name(&m, filename) && m.kmod) dso__set_module_info(dso, &m, machine);
dso__set_kernel(dso, dso_space); free(m.name); }
build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); pr_debug("build id event received for %s: %s [%zu]\n", dso__long_name(dso), sbuild_id, size); dso__put(dso); }
err = 0;out: return err;}
static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, int input, u64 offset, u64 size){ struct perf_session *session = container_of(header, struct perf_session, header); struct { struct perf_event_header header; u8 build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))]; char filename[0]; } old_bev; struct perf_record_header_build_id bev; char filename[PATH_MAX]; u64 limit;
/* Prevent offset + size from wrapping past ULLONG_MAX */ if (size > ULLONG_MAX - offset) return -1;
limit = offset + size;
while (offset < limit) { ssize_t len;
if (readn(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) return -1;
if (header->needs_swap) perf_event_header__bswap(&old_bev.header);
/* size == 0 loops forever; size > remaining reads past section */ if (old_bev.header.size == 0 || old_bev.header.size > limit - offset) return -1;
len = old_bev.header.size - sizeof(old_bev); if (len < 0 || len >= PATH_MAX) { pr_warning("invalid build_id filename length %zd\n", len); return -1; }
if (readn(input, filename, len) != len) return -1; /* * The file data may lack a null terminator, which could * indicate a corrupt or crafted perf.data file. Ensure * filename is always a valid C string before passing it * to functions like machine__findnew_dso(). */ filename[len] = '\0';
bev.header = old_bev.header;
/* * As the pid is the missing value, we need to fill * it properly. The header.misc value give us nice hint. */ bev.pid = HOST_KERNEL_ID; if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER || bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL) bev.pid = DEFAULT_GUEST_KERNEL_ID;
memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); __event_process_build_id(&bev, filename, session);
offset += bev.header.size; }
return 0;}
static int perf_header__read_build_ids(struct perf_header *header, int input, u64 offset, u64 size){ struct perf_session *session = container_of(header, struct perf_session, header); struct perf_record_header_build_id bev; char filename[PATH_MAX]; u64 limit, orig_offset = offset; int err = -1;
/* Prevent offset + size from wrapping past ULLONG_MAX */ if (size > ULLONG_MAX - offset) return -1;
limit = offset + size;
while (offset < limit) { ssize_t len;
if (readn(input, &bev, sizeof(bev)) != sizeof(bev)) goto out;
if (header->needs_swap) { perf_event_header__bswap(&bev.header); bev.pid = bswap_32(bev.pid); }
/* * size == 0 would loop forever (offset never advances); * size > remaining would read past the section boundary. */ if (bev.header.size == 0 || bev.header.size > limit - offset) goto out;
len = bev.header.size - sizeof(bev); if (len < 0 || len >= PATH_MAX) { pr_warning("invalid build_id filename length %zd\n", len); goto out; }
if (readn(input, filename, len) != len) goto out; /* * The file data may lack a null terminator, which could * indicate a corrupt or crafted perf.data file. Ensure * filename is always a valid C string before passing it * to functions like machine__findnew_dso(). */ filename[len] = '\0'; /* * The a1645ce1 changeset: * * "perf: 'perf kvm' tool for monitoring guest performance from host" * * Added a field to struct perf_record_header_build_id that broke the file * format. * * Since the kernel build-id is the first entry, process the * table using the old format if the well known * '[kernel.kallsyms]' string for the kernel build-id has the * first 4 characters chopped off (where the pid_t sits). */ /* Guard short filenames against memcmp reading past the buffer */ if (len >= (ssize_t)sizeof("nel.kallsyms]") - 1 && memcmp(filename, "nel.kallsyms]", sizeof("nel.kallsyms]") - 1) == 0) { if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) return -1; return perf_header__read_build_ids_abi_quirk(header, input, offset, size); }
__event_process_build_id(&bev, filename, session);
offset += bev.header.size; } err = 0;out: return err;}
/* Macro for features that simply need to read and store a string. */#define FEAT_PROCESS_STR_FUN(__feat, __feat_env) \static int process_##__feat(struct feat_fd *ff, void *data __maybe_unused) \{\ free(ff->ph->env.__feat_env); \ ff->ph->env.__feat_env = do_read_string(ff); \ return ff->ph->env.__feat_env ? 0 : -ENOMEM; \}
FEAT_PROCESS_STR_FUN(hostname, hostname);FEAT_PROCESS_STR_FUN(osrelease, os_release);FEAT_PROCESS_STR_FUN(version, version);FEAT_PROCESS_STR_FUN(cpudesc, cpu_desc);FEAT_PROCESS_STR_FUN(cpuid, cpuid);
static int process_arch(struct feat_fd *ff, void *data __maybe_unused){ free(ff->ph->env.arch); ff->ph->env.arch = do_read_string(ff); if (!ff->ph->env.arch) return -ENOMEM; return 0;}
static int process_e_machine(struct feat_fd *ff, void *data __maybe_unused){ int ret;
ret = do_read_u32(ff, &ff->ph->env.e_machine); if (ret) return ret;
return do_read_u32(ff, &ff->ph->env.e_flags);}
static int process_tracing_data(struct feat_fd *ff __maybe_unused, void *data __maybe_unused){#ifdef HAVE_LIBTRACEEVENT ssize_t ret = trace_report(ff->fd, data, false);
return ret < 0 ? -1 : 0;#else /* Not an error — the feature is simply unsupported in this build */ pr_debug("Tracing data present but libtraceevent not available, skipping.\n"); return 0;#endif}
static int process_build_id(struct feat_fd *ff, void *data __maybe_unused){ /* lseek fails in pipe mode — fall back to ff->offset */ off_t offset = lseek(ff->fd, 0, SEEK_CUR);
if (offset == (off_t)-1) offset = ff->offset;
if (perf_header__read_build_ids(ff->ph, ff->fd, offset, ff->size)) pr_debug("Failed to read buildids, continuing...\n"); return 0;}
static int process_nrcpus(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env; int ret; u32 nr_cpus_avail, nr_cpus_online;
ret = do_read_u32(ff, &nr_cpus_avail); if (ret) return ret;
ret = do_read_u32(ff, &nr_cpus_online); if (ret) return ret;
/* * Cap at 1M CPUs — generous for any real system but prevents * stack overflow from VLA allocations sized by nr_cpus_avail * (e.g. DECLARE_BITMAP in builtin-c2c.c node_entry()). */ if (nr_cpus_avail > (1U << 20)) { pr_err("Invalid HEADER_NRCPUS: nr_cpus_avail (%u) exceeds maximum (%u)\n", nr_cpus_avail, 1U << 20); return -1; }
if (nr_cpus_online > nr_cpus_avail) { pr_err("Invalid HEADER_NRCPUS: nr_cpus_online (%u) > nr_cpus_avail (%u)\n", nr_cpus_online, nr_cpus_avail); return -1; }
env->nr_cpus_avail = (int)nr_cpus_avail; env->nr_cpus_online = (int)nr_cpus_online; return 0;}
static int process_total_mem(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env; u64 total_mem; int ret;
ret = do_read_u64(ff, &total_mem); if (ret) return -1; env->total_mem = (unsigned long long)total_mem; return 0;}
static struct evsel *evlist__find_by_index(struct evlist *evlist, int idx){ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) { if (evsel->core.idx == idx) return evsel; }
return NULL;}
static void evlist__set_event_name(struct evlist *evlist, struct evsel *event){ struct evsel *evsel;
if (!event->name) return;
evsel = evlist__find_by_index(evlist, event->core.idx); if (!evsel) return;
if (evsel->name) return;
evsel->name = strdup(event->name);}
static intprocess_event_desc(struct feat_fd *ff, void *data __maybe_unused){ struct perf_session *session; struct evsel *evsel, *events = read_event_desc(ff);
if (!events) return 0;
session = container_of(ff->ph, struct perf_session, header);
if (session->data->is_pipe) { /* Save events for reading later by print_event_desc, * since they can't be read again in pipe mode. */ ff->events = events; }
for (evsel = events; evsel->core.attr.size; evsel++) evlist__set_event_name(session->evlist, evsel);
if (!session->data->is_pipe) free_event_desc(events);
return 0;}
/* * Some arbitrary max for the number of command line arguments, * Wildcards can expand and end up with tons of command line args. */#define MAX_CMDLINE_NR 1048576
static int process_cmdline(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env; char *str, *cmdline = NULL, **argv = NULL; u32 nr, i, len = 0;
if (do_read_u32(ff, &nr)) return -1;
if (nr > MAX_CMDLINE_NR) return -1;
env->nr_cmdline = nr;
cmdline = zalloc(ff->size + nr + 1); if (!cmdline) return -1;
argv = calloc(nr + 1, sizeof(char *)); if (!argv) goto error;
for (i = 0; i < nr; i++) { str = do_read_string(ff); if (!str) goto error;
argv[i] = cmdline + len; memcpy(argv[i], str, strlen(str) + 1); len += strlen(str) + 1; free(str); } env->cmdline = cmdline; env->cmdline_argv = (const char **) argv; return 0;
error: free(argv); free(cmdline); return -1;}
static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused){ u32 nr, i; char *str = NULL; struct strbuf sb; struct perf_env *env = &ff->ph->env; int cpu_nr = env->nr_cpus_avail; u64 size = 0;
if (cpu_nr == 0) { pr_err("Invalid HEADER_CPU_TOPOLOGY: missing HEADER_NRCPUS\n"); return -1; }
env->cpu = calloc(cpu_nr, sizeof(*env->cpu)); if (!env->cpu) return -1;
if (do_read_u32(ff, &nr)) goto free_cpu;
if (nr > (u32)cpu_nr) { pr_err("Invalid HEADER_CPU_TOPOLOGY: nr_sibling_cores (%u) > nr_cpus_avail (%d)\n", nr, cpu_nr); goto free_cpu; }
env->nr_sibling_cores = nr; size += sizeof(u32); if (strbuf_init(&sb, 128) < 0) goto free_cpu;
for (i = 0; i < nr; i++) { str = do_read_string(ff); if (!str) goto error;
/* include a NULL character at the end */ if (strbuf_add(&sb, str, strlen(str) + 1) < 0) goto error; size += string_size(str); zfree(&str); } env->sibling_cores = strbuf_detach(&sb, NULL);
if (do_read_u32(ff, &nr)) goto free_cpu;
if (nr > (u32)cpu_nr) { pr_err("Invalid HEADER_CPU_TOPOLOGY: nr_sibling_threads (%u) > nr_cpus_avail (%d)\n", nr, cpu_nr); goto free_cpu; }
env->nr_sibling_threads = nr; size += sizeof(u32);
for (i = 0; i < nr; i++) { str = do_read_string(ff); if (!str) goto error;
/* include a NULL character at the end */ if (strbuf_add(&sb, str, strlen(str) + 1) < 0) goto error; size += string_size(str); zfree(&str); } env->sibling_threads = strbuf_detach(&sb, NULL);
/* * The header may be from old perf, * which doesn't include core id and socket id information. */ if (ff->size <= size) { zfree(&env->cpu); return 0; }
for (i = 0; i < (u32)cpu_nr; i++) { if (do_read_u32(ff, &nr)) goto free_cpu;
env->cpu[i].core_id = nr; size += sizeof(u32);
if (do_read_u32(ff, &nr)) goto free_cpu;
env->cpu[i].socket_id = nr; size += sizeof(u32); }
/* * The header may be from old perf, * which doesn't include die information. */ if (ff->size <= size) return 0;
if (do_read_u32(ff, &nr)) goto free_cpu;
if (nr > (u32)cpu_nr) { pr_err("Invalid HEADER_CPU_TOPOLOGY: nr_sibling_dies (%u) > nr_cpus_avail (%d)\n", nr, cpu_nr); goto free_cpu; }
env->nr_sibling_dies = nr; size += sizeof(u32);
for (i = 0; i < nr; i++) { str = do_read_string(ff); if (!str) goto error;
/* include a NULL character at the end */ if (strbuf_add(&sb, str, strlen(str) + 1) < 0) goto error; size += string_size(str); zfree(&str); } env->sibling_dies = strbuf_detach(&sb, NULL);
for (i = 0; i < (u32)cpu_nr; i++) { if (do_read_u32(ff, &nr)) goto free_cpu;
env->cpu[i].die_id = nr; }
return 0;
error: strbuf_release(&sb); zfree(&str);free_cpu: zfree(&env->cpu); return -1;}
static int process_numa_topology(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env; struct numa_node *nodes, *n; u32 nr, i; char *str;
/* nr nodes */ if (do_read_u32(ff, &nr)) return -1;
if (nr > MAX_NUMA_NODES) { pr_err("Invalid HEADER_NUMA_TOPOLOGY: nr_nodes (%u) > %u\n", nr, MAX_NUMA_NODES); return -1; }
if (ff->size < sizeof(u32) + nr * (sizeof(u32) + 2 * sizeof(u64))) { pr_err("Invalid HEADER_NUMA_TOPOLOGY: section too small (%zu) for %u nodes\n", ff->size, nr); return -1; }
nodes = calloc(nr, sizeof(*nodes)); if (!nodes) return -ENOMEM;
for (i = 0; i < nr; i++) { n = &nodes[i];
/* node number */ if (do_read_u32(ff, &n->node)) goto error;
if (do_read_u64(ff, &n->mem_total)) goto error;
if (do_read_u64(ff, &n->mem_free)) goto error;
str = do_read_string(ff); if (!str) goto error;
n->map = perf_cpu_map__new(str); free(str); if (!n->map) goto error; } env->nr_numa_nodes = nr; env->numa_nodes = nodes; return 0;
error: free(nodes); return -1;}
static int process_pmu_mappings(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env; char *name; u32 pmu_num; u32 type; struct strbuf sb;
if (do_read_u32(ff, &pmu_num)) return -1;
if (!pmu_num) { pr_debug("pmu mappings not available\n"); return 0; }
if (pmu_num > MAX_PMU_MAPPINGS) { pr_err("Invalid HEADER_PMU_MAPPINGS: pmu_num (%u) > %u\n", pmu_num, MAX_PMU_MAPPINGS); return -1; }
if (ff->size < sizeof(u32) + pmu_num * 2 * sizeof(u32)) { pr_err("Invalid HEADER_PMU_MAPPINGS: section too small (%zu) for %u PMUs\n", ff->size, pmu_num); return -1; }
env->nr_pmu_mappings = pmu_num; if (strbuf_init(&sb, 128) < 0) return -1;
while (pmu_num) { if (do_read_u32(ff, &type)) goto error;
name = do_read_string(ff); if (!name) goto error;
if (strbuf_addf(&sb, "%u:%s", type, name) < 0) goto error; /* include a NULL character at the end */ if (strbuf_add(&sb, "", 1) < 0) goto error;
if (!strcmp(name, "msr")) env->msr_pmu_type = type;
free(name); pmu_num--; } /* AMD may set it by evlist__has_amd_ibs() from perf_session__new() */ free(env->pmu_mappings); env->pmu_mappings = strbuf_detach(&sb, NULL); return 0;
error: strbuf_release(&sb); return -1;}
static int process_group_desc(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env; size_t ret = -1; u32 i, nr, nr_groups; struct perf_session *session; struct evsel *evsel, *leader = NULL; struct group_desc { char *name; u32 leader_idx; u32 nr_members; } *desc;
if (do_read_u32(ff, &nr_groups)) return -1;
if (!nr_groups) { pr_debug("group desc not available\n"); return 0; }
if (nr_groups > MAX_GROUP_DESC) { pr_err("Invalid HEADER_GROUP_DESC: nr_groups (%u) > %u\n", nr_groups, MAX_GROUP_DESC); return -1; }
if (ff->size < sizeof(u32) + nr_groups * 3 * sizeof(u32)) { pr_err("Invalid HEADER_GROUP_DESC: section too small (%zu) for %u groups\n", ff->size, nr_groups); return -1; }
env->nr_groups = nr_groups;
desc = calloc(nr_groups, sizeof(*desc)); if (!desc) return -1;
for (i = 0; i < nr_groups; i++) { desc[i].name = do_read_string(ff); if (!desc[i].name) goto out_free;
if (do_read_u32(ff, &desc[i].leader_idx)) goto out_free;
if (do_read_u32(ff, &desc[i].nr_members)) goto out_free; }
/* * Rebuild group relationship based on the group_desc */ session = container_of(ff->ph, struct perf_session, header);
i = nr = 0; evlist__for_each_entry(session->evlist, evsel) { if (i < nr_groups && evsel->core.idx == (int) desc[i].leader_idx) { evsel__set_leader(evsel, evsel); /* {anon_group} is a dummy name */ if (strcmp(desc[i].name, "{anon_group}")) { evsel->group_name = desc[i].name; desc[i].name = NULL; } evsel->core.nr_members = desc[i].nr_members;
if (i >= nr_groups || nr > 0) { pr_debug("invalid group desc\n"); goto out_free; }
leader = evsel; nr = evsel->core.nr_members - 1; i++; } else if (nr) { /* This is a group member */ evsel__set_leader(evsel, leader);
nr--; } }
if (i != nr_groups || nr != 0) { pr_debug("invalid group desc\n"); goto out_free; }
ret = 0;out_free: for (i = 0; i < nr_groups; i++) zfree(&desc[i].name); free(desc);
return ret;}
static int process_auxtrace(struct feat_fd *ff, void *data __maybe_unused){ struct perf_session *session; int err;
session = container_of(ff->ph, struct perf_session, header);
err = auxtrace_index__process(ff->fd, ff->size, session, ff->ph->needs_swap); if (err < 0) pr_err("Failed to process auxtrace index\n"); return err;}
static int process_cache(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env; struct cpu_cache_level *caches; u32 cnt, i, version;
if (do_read_u32(ff, &version)) return -1;
if (version != 1) return -1;
if (do_read_u32(ff, &cnt)) return -1;
if (cnt > MAX_CACHE_ENTRIES) { pr_err("Invalid HEADER_CACHE: cnt (%u) > %u\n", cnt, MAX_CACHE_ENTRIES); return -1; }
if (ff->size < 2 * sizeof(u32) + cnt * 7 * sizeof(u32)) { pr_err("Invalid HEADER_CACHE: section too small (%zu) for %u entries\n", ff->size, cnt); return -1; }
caches = calloc(cnt, sizeof(*caches)); if (!caches) return -1;
for (i = 0; i < cnt; i++) { struct cpu_cache_level *c = &caches[i];
#define _R(v) \ if (do_read_u32(ff, &c->v)) \ goto out_free_caches; \
_R(level) _R(line_size) _R(sets) _R(ways) #undef _R
#define _R(v) \ c->v = do_read_string(ff); \ if (!c->v) \ goto out_free_caches; \
_R(type) _R(size) _R(map) #undef _R }
env->caches = caches; env->caches_cnt = cnt; return 0;out_free_caches: for (i = 0; i < cnt; i++) { free(caches[i].type); free(caches[i].size); free(caches[i].map); } free(caches); return -1;}
static int process_cln_size(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env;
if (do_read_u32(ff, &env->cln_size)) return -1;
return 0;}
static int process_sample_time(struct feat_fd *ff, void *data __maybe_unused){ struct perf_session *session; u64 first_sample_time, last_sample_time; int ret;
session = container_of(ff->ph, struct perf_session, header);
ret = do_read_u64(ff, &first_sample_time); if (ret) return -1;
ret = do_read_u64(ff, &last_sample_time); if (ret) return -1;
session->evlist->first_sample_time = first_sample_time; session->evlist->last_sample_time = last_sample_time; return 0;}
static int process_mem_topology(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env; struct memory_node *nodes; u64 version, i, nr, bsize; int ret = -1;
if (do_read_u64(ff, &version)) return -1;
if (version != 1) return -1;
if (do_read_u64(ff, &bsize)) return -1;
if (do_read_u64(ff, &nr)) return -1;
if (nr > MAX_NUMA_NODES) { pr_err("Invalid HEADER_MEM_TOPOLOGY: nr_nodes (%llu) > %u\n", (unsigned long long)nr, MAX_NUMA_NODES); return -1; }
/* Per node: node_id(u64) + mem_size(u64) + bitmap_nr_bits(u64) */ if (ff->size < 3 * sizeof(u64) + nr * 3 * sizeof(u64)) { pr_err("Invalid HEADER_MEM_TOPOLOGY: section too small (%zu) for %llu nodes\n", ff->size, (unsigned long long)nr); return -1; }
nodes = calloc(nr, sizeof(*nodes)); if (!nodes) return -1;
for (i = 0; i < nr; i++) { struct memory_node n;
#define _R(v) \ if (do_read_u64(ff, &n.v)) \ goto out; \
_R(node) _R(size)
#undef _R
if (do_read_bitmap(ff, &n.set, &n.size)) goto out;
nodes[i] = n; }
env->memory_bsize = bsize; env->memory_nodes = nodes; env->nr_memory_nodes = nr; ret = 0;
out: if (ret) memory_node__delete_nodes(nodes, nr); return ret;}
static int process_clockid(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env;
if (do_read_u64(ff, &env->clock.clockid_res_ns)) return -1;
return 0;}
static int process_clock_data(struct feat_fd *ff, void *_data __maybe_unused){ struct perf_env *env = &ff->ph->env; u32 data32; u64 data64;
/* version */ if (do_read_u32(ff, &data32)) return -1;
if (data32 != 1) return -1;
/* clockid */ if (do_read_u32(ff, &data32)) return -1;
env->clock.clockid = data32;
/* TOD ref time */ if (do_read_u64(ff, &data64)) return -1;
env->clock.tod_ns = data64;
/* clockid ref time */ if (do_read_u64(ff, &data64)) return -1;
env->clock.clockid_ns = data64; env->clock.enabled = true; return 0;}
static int process_hybrid_topology(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env; struct hybrid_node *nodes, *n; u32 nr, i;
/* nr nodes */ if (do_read_u32(ff, &nr)) return -1;
if (nr > MAX_PMU_MAPPINGS) { pr_err("Invalid HEADER_HYBRID_TOPOLOGY: nr_nodes (%u) > %u\n", nr, MAX_PMU_MAPPINGS); return -1; }
if (ff->size < sizeof(u32) + nr * 2 * sizeof(u32)) { pr_err("Invalid HEADER_HYBRID_TOPOLOGY: section too small (%zu) for %u nodes\n", ff->size, nr); return -1; }
nodes = calloc(nr, sizeof(*nodes)); if (!nodes) return -ENOMEM;
for (i = 0; i < nr; i++) { n = &nodes[i];
n->pmu_name = do_read_string(ff); if (!n->pmu_name) goto error;
n->cpus = do_read_string(ff); if (!n->cpus) goto error; }
env->nr_hybrid_nodes = nr; env->hybrid_nodes = nodes; return 0;
error: for (i = 0; i < nr; i++) { free(nodes[i].pmu_name); free(nodes[i].cpus); }
free(nodes); return -1;}
static int process_dir_format(struct feat_fd *ff, void *_data __maybe_unused){ struct perf_session *session; struct perf_data *data;
session = container_of(ff->ph, struct perf_session, header); data = session->data;
if (WARN_ON(!perf_data__is_dir(data))) return -1;
return do_read_u64(ff, &data->dir.version);}
static int process_bpf_prog_info(struct feat_fd *ff __maybe_unused, void *data __maybe_unused){#ifdef HAVE_LIBBPF_SUPPORT struct bpf_prog_info_node *info_node; struct perf_env *env = &ff->ph->env; struct perf_bpil *info_linear; u32 count, i; int err = -1;
if (ff->ph->needs_swap) { pr_warning("interpreting bpf_prog_info from systems with endianness is not yet supported\n"); return 0; }
if (do_read_u32(ff, &count)) return -1;
if (count > MAX_BPF_PROGS) { pr_err("Invalid HEADER_BPF_PROG_INFO: count (%u) > %u\n", count, MAX_BPF_PROGS); return -1; }
if (ff->size < sizeof(u32) + count * (2 * sizeof(u32) + sizeof(u64))) { pr_err("Invalid HEADER_BPF_PROG_INFO: section too small (%zu) for %u entries\n", ff->size, count); return -1; }
down_write(&env->bpf_progs.lock);
for (i = 0; i < count; ++i) { u32 info_len, data_len;
info_linear = NULL; info_node = NULL; if (do_read_u32(ff, &info_len)) goto out; if (do_read_u32(ff, &data_len)) goto out;
if (info_len > sizeof(struct bpf_prog_info)) { pr_warning("detected invalid bpf_prog_info\n"); goto out; }
if (data_len > MAX_BPF_DATA_LEN) { pr_warning("Invalid HEADER_BPF_PROG_INFO: data_len (%u) too large\n", data_len); goto out; }
info_linear = malloc(sizeof(struct perf_bpil) + data_len); if (!info_linear) goto out; info_linear->info_len = sizeof(struct bpf_prog_info); info_linear->data_len = data_len; if (do_read_u64(ff, (u64 *)(&info_linear->arrays))) goto out; if (__do_read(ff, &info_linear->info, info_len)) goto out; if (info_len < sizeof(struct bpf_prog_info)) memset(((void *)(&info_linear->info)) + info_len, 0, sizeof(struct bpf_prog_info) - info_len);
if (__do_read(ff, info_linear->data, data_len)) goto out;
info_node = malloc(sizeof(struct bpf_prog_info_node)); if (!info_node) goto out;
/* after reading from file, translate offset to address */ bpil_offs_to_addr(info_linear); info_node->info_linear = info_linear; info_node->metadata = NULL; if (!__perf_env__insert_bpf_prog_info(env, info_node)) { free(info_linear); free(info_node); } }
up_write(&env->bpf_progs.lock); return 0;out: free(info_linear); free(info_node); up_write(&env->bpf_progs.lock); return err;#else /* Not an error — the feature is simply unsupported in this build */ pr_debug("BPF prog info present but libbpf not available, skipping.\n"); return 0;#endif // HAVE_LIBBPF_SUPPORT}
static int process_bpf_btf(struct feat_fd *ff __maybe_unused, void *data __maybe_unused){#ifdef HAVE_LIBBPF_SUPPORT struct perf_env *env = &ff->ph->env; struct btf_node *node = NULL; u32 count, i; int err = -1;
if (ff->ph->needs_swap) { pr_warning("interpreting btf from systems with endianness is not yet supported\n"); return 0; }
if (do_read_u32(ff, &count)) return -1;
if (count > MAX_BPF_PROGS) { pr_err("bpf btf count %u too large (max %u)\n", count, MAX_BPF_PROGS); return -1; }
if (ff->size < sizeof(u32) + count * 2 * sizeof(u32)) { pr_err("Invalid HEADER_BPF_BTF: section too small (%zu) for %u entries\n", ff->size, count); return -1; }
down_write(&env->bpf_progs.lock);
for (i = 0; i < count; ++i) { u32 id, data_size;
if (do_read_u32(ff, &id)) goto out; if (do_read_u32(ff, &data_size)) goto out;
if (data_size > MAX_BPF_DATA_LEN) { pr_err("bpf btf data size %u too large (max %u)\n", data_size, MAX_BPF_DATA_LEN); goto out; }
node = malloc(sizeof(struct btf_node) + data_size); if (!node) goto out;
node->id = id; node->data_size = data_size;
if (__do_read(ff, node->data, data_size)) goto out;
if (!__perf_env__insert_btf(env, node)) free(node); node = NULL; }
err = 0;out: up_write(&env->bpf_progs.lock); free(node); return err;#else /* Not an error — the feature is simply unsupported in this build */ pr_debug("BTF data present but libbpf not available, skipping.\n"); return 0;#endif // HAVE_LIBBPF_SUPPORT}
static int process_compressed(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env;
if (do_read_u32(ff, &(env->comp_ver))) return -1;
if (do_read_u32(ff, &(env->comp_type))) return -1;
if (do_read_u32(ff, &(env->comp_level))) return -1;
if (do_read_u32(ff, &(env->comp_ratio))) return -1;
if (do_read_u32(ff, &(env->comp_mmap_len))) return -1;
/* * FIXME: perf.data should record the recording system's page * size — it affects mmap buffer alignment, sample addresses, * and data_page_size/code_page_size interpretation. Without * it we assume 4K (the smallest Linux page size) as a safe * minimum alignment for comp_mmap_len validation. * * No upper-bound cap: perf_session__process_compressed_event() * checks decomp_len + sizeof(struct decomp) against SIZE_MAX * before allocating, which handles 32-bit safety. */ if (env->comp_mmap_len < 4096 || env->comp_mmap_len % 4096) { pr_err("Invalid HEADER_COMPRESSED: comp_mmap_len (%u) must be a 4K-aligned value >= 4096\n", env->comp_mmap_len); return -1; }
return 0;}
static int __process_pmu_caps(struct feat_fd *ff, int *nr_caps, char ***caps, unsigned int *max_branches, unsigned int *br_cntr_nr, unsigned int *br_cntr_width){ char *name, *value, *ptr; u32 nr_pmu_caps, i;
*nr_caps = 0; *caps = NULL;
if (do_read_u32(ff, &nr_pmu_caps)) return -1;
if (!nr_pmu_caps) return 0;
if (nr_pmu_caps > MAX_PMU_CAPS) { pr_err("Invalid pmu caps: nr_pmu_caps (%u) > %u\n", nr_pmu_caps, MAX_PMU_CAPS); return -1; }
*caps = calloc(nr_pmu_caps, sizeof(char *)); if (!*caps) return -1;
for (i = 0; i < nr_pmu_caps; i++) { name = do_read_string(ff); if (!name) goto error;
value = do_read_string(ff); if (!value) goto free_name;
if (asprintf(&ptr, "%s=%s", name, value) < 0) goto free_value;
(*caps)[i] = ptr;
if (!strcmp(name, "branches")) *max_branches = atoi(value);
if (!strcmp(name, "branch_counter_nr")) *br_cntr_nr = atoi(value);
if (!strcmp(name, "branch_counter_width")) *br_cntr_width = atoi(value);
free(value); free(name); } *nr_caps = nr_pmu_caps; return 0;
free_value: free(value);free_name: free(name);error: for (; i > 0; i--) free((*caps)[i - 1]); free(*caps); *caps = NULL; *nr_caps = 0; return -1;}
static int process_cpu_pmu_caps(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env; int ret = __process_pmu_caps(ff, &env->nr_cpu_pmu_caps, &env->cpu_pmu_caps, &env->max_branches, &env->br_cntr_nr, &env->br_cntr_width);
if (!ret && !env->cpu_pmu_caps) pr_debug("cpu pmu capabilities not available\n"); return ret;}
static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused){ struct perf_env *env = &ff->ph->env; struct pmu_caps *pmu_caps; u32 nr_pmu, i; int ret; int j;
if (do_read_u32(ff, &nr_pmu)) return -1;
if (!nr_pmu) { pr_debug("pmu capabilities not available\n"); return 0; }
if (nr_pmu > MAX_PMU_MAPPINGS) { pr_err("Invalid HEADER_PMU_CAPS: nr_pmu (%u) > %u\n", nr_pmu, MAX_PMU_MAPPINGS); return -1; }
if (ff->size < sizeof(u32) + nr_pmu * sizeof(u32)) { pr_err("Invalid HEADER_PMU_CAPS: section too small (%zu) for %u PMUs\n", ff->size, nr_pmu); return -1; }
pmu_caps = calloc(nr_pmu, sizeof(*pmu_caps)); if (!pmu_caps) return -ENOMEM;
for (i = 0; i < nr_pmu; i++) { ret = __process_pmu_caps(ff, &pmu_caps[i].nr_caps, &pmu_caps[i].caps, &pmu_caps[i].max_branches, &pmu_caps[i].br_cntr_nr, &pmu_caps[i].br_cntr_width); if (ret) goto err;
pmu_caps[i].pmu_name = do_read_string(ff); if (!pmu_caps[i].pmu_name) { ret = -1; goto err; } if (!pmu_caps[i].nr_caps) { pr_debug("%s pmu capabilities not available\n", pmu_caps[i].pmu_name); } }
env->nr_pmus_with_caps = nr_pmu; env->pmu_caps = pmu_caps; return 0;
err: for (i = 0; i < nr_pmu; i++) { for (j = 0; j < pmu_caps[i].nr_caps; j++) free(pmu_caps[i].caps[j]); free(pmu_caps[i].caps); free(pmu_caps[i].pmu_name); }
free(pmu_caps); return ret;}
static int process_cpu_domain_info(struct feat_fd *ff, void *data __maybe_unused){ u32 schedstat_version, max_sched_domains, cpu, domain, nr_domains; struct perf_env *env = &ff->ph->env; char *dname, *cpumask, *cpulist; struct cpu_domain_map **cd_map; struct domain_info *d_info; u32 nra, nr, i, j; int ret;
nra = env->nr_cpus_avail; nr = env->nr_cpus_online;
if (nra == 0 || nr == 0) { pr_err("Invalid HEADER_CPU_DOMAIN_INFO: missing HEADER_NRCPUS\n"); return -1; }
if (ff->size < 2 * sizeof(u32) + nr * 2 * sizeof(u32)) { pr_err("Invalid HEADER_CPU_DOMAIN_INFO: section too small (%zu) for %u CPUs\n", (size_t)ff->size, nr); return -1; }
cd_map = calloc(nra, sizeof(*cd_map)); if (!cd_map) return -1;
env->cpu_domain = cd_map;
ret = do_read_u32(ff, &schedstat_version); if (ret) return ret;
env->schedstat_version = schedstat_version;
ret = do_read_u32(ff, &max_sched_domains); if (ret) return ret;
/* * Sanity check: real systems have at most ~10 sched domain levels * (SMT, CLS, MC, PKG + NUMA hops). Reject obviously bogus values * from malformed perf.data files before they cause excessive * allocation in the per-CPU loop. */ if (max_sched_domains > MAX_SCHED_DOMAINS) { pr_err("Invalid HEADER_CPU_DOMAIN_INFO: max_sched_domains %u > %u\n", max_sched_domains, MAX_SCHED_DOMAINS); return -1; }
env->max_sched_domains = max_sched_domains;
for (i = 0; i < nr; i++) { if (do_read_u32(ff, &cpu)) return -1;
if (cpu >= nra) { pr_err("Invalid HEADER_CPU_DOMAIN_INFO: cpu %d >= nr_cpus_avail (%d)\n", cpu, nra); return -1; }
if (cd_map[cpu]) { pr_err("Invalid HEADER_CPU_DOMAIN_INFO: duplicate cpu %u\n", cpu); return -1; }
cd_map[cpu] = zalloc(sizeof(*cd_map[cpu])); if (!cd_map[cpu]) return -1;
cd_map[cpu]->cpu = cpu;
if (do_read_u32(ff, &nr_domains)) return -1;
if (nr_domains > max_sched_domains) { pr_err("Invalid HEADER_CPU_DOMAIN_INFO: nr_domains %u > max_sched_domains (%u)\n", nr_domains, max_sched_domains); return -1; }
cd_map[cpu]->nr_domains = nr_domains;
cd_map[cpu]->domains = calloc(max_sched_domains, sizeof(*d_info)); if (!cd_map[cpu]->domains) return -1;
for (j = 0; j < nr_domains; j++) { if (do_read_u32(ff, &domain)) return -1;
if (domain >= max_sched_domains) { pr_err("Invalid HEADER_CPU_DOMAIN_INFO: domain %d >= max_sched_domains (%d)\n", domain, max_sched_domains); return -1; }
d_info = zalloc(sizeof(*d_info)); if (!d_info) return -1;
if (cd_map[cpu]->domains[domain]) { pr_err("Invalid HEADER_CPU_DOMAIN_INFO: duplicate domain %u for cpu %u\n", domain, cpu); free(d_info); return -1; }
cd_map[cpu]->domains[domain] = d_info; d_info->domain = domain;
if (schedstat_version >= 17) { dname = do_read_string(ff); if (!dname) return -1;
d_info->dname = dname; }
cpumask = do_read_string(ff); if (!cpumask) return -1;
d_info->cpumask = cpumask;
cpulist = do_read_string(ff); if (!cpulist) return -1;
d_info->cpulist = cpulist; } }
return ret;}
#define FEAT_OPR(n, func, __full_only) \ [HEADER_##n] = { \ .name = __stringify(n), \ .write = write_##func, \ .print = print_##func, \ .full_only = __full_only, \ .process = process_##func, \ .synthesize = true \ }
#define FEAT_OPN(n, func, __full_only) \ [HEADER_##n] = { \ .name = __stringify(n), \ .write = write_##func, \ .print = print_##func, \ .full_only = __full_only, \ .process = process_##func \ }
/* feature_ops not implemented: */#define print_tracing_data NULL#define print_build_id NULL
#define process_branch_stack NULL#define process_stat NULL
// Only used in util/synthetic-events.cconst struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE];
const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = { FEAT_OPN(TRACING_DATA, tracing_data, false), FEAT_OPN(BUILD_ID, build_id, false), FEAT_OPR(HOSTNAME, hostname, false), FEAT_OPR(OSRELEASE, osrelease, false), FEAT_OPR(VERSION, version, false), FEAT_OPR(ARCH, arch, false), FEAT_OPR(NRCPUS, nrcpus, false), FEAT_OPR(CPUDESC, cpudesc, false), FEAT_OPR(CPUID, cpuid, false), FEAT_OPR(TOTAL_MEM, total_mem, false), FEAT_OPR(EVENT_DESC, event_desc, false), FEAT_OPR(CMDLINE, cmdline, false), FEAT_OPR(CPU_TOPOLOGY, cpu_topology, true), FEAT_OPR(NUMA_TOPOLOGY, numa_topology, true), FEAT_OPN(BRANCH_STACK, branch_stack, false), FEAT_OPR(PMU_MAPPINGS, pmu_mappings, false), FEAT_OPR(GROUP_DESC, group_desc, false), FEAT_OPN(AUXTRACE, auxtrace, false), FEAT_OPN(STAT, stat, false), FEAT_OPN(CACHE, cache, true), FEAT_OPR(SAMPLE_TIME, sample_time, false), FEAT_OPR(MEM_TOPOLOGY, mem_topology, true), FEAT_OPR(CLOCKID, clockid, false), FEAT_OPN(DIR_FORMAT, dir_format, false), FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false), FEAT_OPR(BPF_BTF, bpf_btf, false), FEAT_OPR(COMPRESSED, compressed, false), FEAT_OPR(CPU_PMU_CAPS, cpu_pmu_caps, false), FEAT_OPR(CLOCK_DATA, clock_data, false), FEAT_OPN(HYBRID_TOPOLOGY, hybrid_topology, true), FEAT_OPR(PMU_CAPS, pmu_caps, false), FEAT_OPR(CPU_DOMAIN_INFO, cpu_domain_info, true), FEAT_OPR(E_MACHINE, e_machine, false), FEAT_OPR(CLN_SIZE, cln_size, false),};
struct header_print_data { FILE *fp; bool full; /* extended list of headers */};
const char *header_feat__name(unsigned int id){ if (id < HEADER_LAST_FEATURE) return feat_ops[id].name ?: "INVALID"; return "INVALID";}
static int perf_file_section__fprintf_info(struct perf_file_section *section, struct perf_header *ph, int feat, int fd, void *data){ struct header_print_data *hd = data; struct feat_fd ff;
if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { pr_debug("Failed to lseek to %" PRIu64 " offset for feature %s (%d), continuing...\n", section->offset, header_feat__name(feat), feat); return 0; } if (feat >= ph->last_feat) { pr_warning("unknown feature %d\n", feat); return 0; } if (!feat_ops[feat].print) return 0;
ff = (struct feat_fd) { .fd = fd, .ph = ph, .size = section->size, };
if (!feat_ops[feat].full_only || hd->full) feat_ops[feat].print(&ff, hd->fp); else fprintf(hd->fp, "# %s info available, use -I to display\n", feat_ops[feat].name);
return 0;}
int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full){ struct header_print_data hd; struct perf_header *header = &session->header; int fd = perf_data__fd(session->data); struct stat st; time_t stctime; int ret, bit;
hd.fp = fp; hd.full = full;
ret = fstat(fd, &st); if (ret == -1) return -1;
stctime = st.st_mtime; fprintf(fp, "# captured on : %s", ctime(&stctime));
fprintf(fp, "# header version : %u\n", header->version); fprintf(fp, "# data offset : %" PRIu64 "\n", header->data_offset); fprintf(fp, "# data size : %" PRIu64 "\n", header->data_size); fprintf(fp, "# feat offset : %" PRIu64 "\n", header->feat_offset);
perf_header__process_sections(header, fd, &hd, perf_file_section__fprintf_info);
if (session->data->is_pipe) return 0;
fprintf(fp, "# missing features: "); for_each_clear_bit(bit, header->adds_features, header->last_feat) { if (bit) fprintf(fp, "%s ", feat_ops[bit].name); }
fprintf(fp, "\n"); return 0;}
struct header_fw { struct feat_writer fw; struct feat_fd *ff;};
static int feat_writer_cb(struct feat_writer *fw, void *buf, size_t sz){ struct header_fw *h = container_of(fw, struct header_fw, fw);
return do_write(h->ff, buf, sz);}
static int do_write_feat(struct feat_fd *ff, int type, struct perf_file_section **p, struct evlist *evlist, struct feat_copier *fc){ int err; int ret = 0;
if (perf_header__has_feat(ff->ph, type)) { if (!feat_ops[type].write) return -1;
if (WARN(ff->buf, "Error: calling %s in pipe-mode.\n", __func__)) return -1;
(*p)->offset = lseek(ff->fd, 0, SEEK_CUR);
/* * Hook to let perf inject copy features sections from the input * file. */ if (fc && fc->copy) { struct header_fw h = { .fw.write = feat_writer_cb, .ff = ff, };
/* ->copy() returns 0 if the feature was not copied */ err = fc->copy(fc, type, &h.fw); } else { err = 0; } if (!err) err = feat_ops[type].write(ff, evlist); if (err < 0) { pr_debug("failed to write feature %s\n", feat_ops[type].name);
/* undo anything written */ lseek(ff->fd, (*p)->offset, SEEK_SET);
return -1; } (*p)->size = lseek(ff->fd, 0, SEEK_CUR) - (*p)->offset; (*p)++; } return ret;}
static int perf_header__adds_write(struct perf_header *header, struct evlist *evlist, int fd, struct feat_copier *fc){ int nr_sections; struct feat_fd ff = { .fd = fd, .ph = header, }; struct perf_file_section *feat_sec, *p; int sec_size; u64 sec_start; int feat; int err;
nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); if (!nr_sections) return 0;
feat_sec = p = calloc(nr_sections, sizeof(*feat_sec)); if (feat_sec == NULL) return -ENOMEM;
sec_size = sizeof(*feat_sec) * nr_sections;
sec_start = header->feat_offset; lseek(fd, sec_start + sec_size, SEEK_SET);
for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) { if (do_write_feat(&ff, feat, &p, evlist, fc)) perf_header__clear_feat(header, feat); }
lseek(fd, sec_start, SEEK_SET); /* * may write more than needed due to dropped feature, but * this is okay, reader will skip the missing entries */ err = do_write(&ff, feat_sec, sec_size); if (err < 0) pr_debug("failed to write feature section\n"); free(ff.buf); /* TODO: added to silence clang-tidy. */ free(feat_sec); return err;}
int perf_header__write_pipe(int fd){ struct perf_pipe_file_header f_header; struct feat_fd ff = { .fd = fd, }; int err;
f_header = (struct perf_pipe_file_header){ .magic = PERF_MAGIC, .size = sizeof(f_header), };
err = do_write(&ff, &f_header, sizeof(f_header)); if (err < 0) { pr_debug("failed to write perf pipe header\n"); return err; } free(ff.buf); return 0;}
static int perf_session__do_write_header(struct perf_session *session, struct evlist *evlist, int fd, bool at_exit, struct feat_copier *fc, bool write_attrs_after_data){ struct perf_file_header f_header; struct perf_header *header = &session->header; struct evsel *evsel; struct feat_fd ff = { .ph = header, .fd = fd, }; u64 attr_offset = sizeof(f_header), attr_size = 0; int err;
if (write_attrs_after_data && at_exit) { /* * Write features at the end of the file first so that * attributes may come after them. */ if (!header->data_offset && header->data_size) { pr_err("File contains data but offset unknown\n"); err = -1; goto err_out; } header->feat_offset = header->data_offset + header->data_size; err = perf_header__adds_write(header, evlist, fd, fc); if (err < 0) goto err_out; attr_offset = lseek(fd, 0, SEEK_CUR); } else { lseek(fd, attr_offset, SEEK_SET); }
evlist__for_each_entry(session->evlist, evsel) { evsel->id_offset = attr_offset; /* Avoid writing at the end of the file until the session is exiting. */ if (!write_attrs_after_data || at_exit) { err = do_write(&ff, evsel->core.id, evsel->core.ids * sizeof(u64)); if (err < 0) { pr_debug("failed to write perf header\n"); goto err_out; } } attr_offset += evsel->core.ids * sizeof(u64); }
evlist__for_each_entry(evlist, evsel) { if (evsel->core.attr.size < sizeof(evsel->core.attr)) { /* * We are likely in "perf inject" and have read * from an older file. Update attr size so that * reader gets the right offset to the ids. */ evsel->core.attr.size = sizeof(evsel->core.attr); } /* Avoid writing at the end of the file until the session is exiting. */ if (!write_attrs_after_data || at_exit) { struct perf_file_attr f_attr = { .attr = evsel->core.attr, .ids = { .offset = evsel->id_offset, .size = evsel->core.ids * sizeof(u64), } }; err = do_write(&ff, &f_attr, sizeof(f_attr)); if (err < 0) { pr_debug("failed to write perf header attribute\n"); goto err_out; } } attr_size += sizeof(struct perf_file_attr); }
if (!header->data_offset) { if (write_attrs_after_data) header->data_offset = sizeof(f_header); else header->data_offset = attr_offset + attr_size; } header->feat_offset = header->data_offset + header->data_size;
if (!write_attrs_after_data && at_exit) { /* Write features now feat_offset is known. */ err = perf_header__adds_write(header, evlist, fd, fc); if (err < 0) goto err_out; }
f_header = (struct perf_file_header){ .magic = PERF_MAGIC, .size = sizeof(f_header), .attr_size = sizeof(struct perf_file_attr), .attrs = { .offset = attr_offset, .size = attr_size, }, .data = { .offset = header->data_offset, .size = header->data_size, }, /* event_types is ignored, store zeros */ };
memcpy(&f_header.adds_features, &header->adds_features, sizeof(header->adds_features));
lseek(fd, 0, SEEK_SET); err = do_write(&ff, &f_header, sizeof(f_header)); if (err < 0) { pr_debug("failed to write perf header\n"); goto err_out; } else { lseek(fd, 0, SEEK_END); err = 0; }err_out: free(ff.buf); return err;}
int perf_session__write_header(struct perf_session *session, struct evlist *evlist, int fd, bool at_exit){ return perf_session__do_write_header(session, evlist, fd, at_exit, /*fc=*/NULL, /*write_attrs_after_data=*/false);}
size_t perf_session__data_offset(const struct evlist *evlist){ struct evsel *evsel; size_t data_offset;
data_offset = sizeof(struct perf_file_header); evlist__for_each_entry(evlist, evsel) { data_offset += evsel->core.ids * sizeof(u64); } data_offset += evlist->core.nr_entries * sizeof(struct perf_file_attr);
return data_offset;}
int perf_session__inject_header(struct perf_session *session, struct evlist *evlist, int fd, struct feat_copier *fc, bool write_attrs_after_data){ return perf_session__do_write_header(session, evlist, fd, true, fc, write_attrs_after_data);}
static int perf_header__getbuffer64(struct perf_header *header, int fd, void *buf, size_t size){ ssize_t n = readn(fd, buf, size);
if (n <= 0) { if (n == 0) errno = EIO; return -1; }
if (header->needs_swap) mem_bswap_64(buf, size);
return 0;}
int perf_header__process_sections(struct perf_header *header, int fd, void *data, int (*process)(struct perf_file_section *section, struct perf_header *ph, int feat, int fd, void *data)){ struct perf_file_section *feat_sec, *sec; int nr_sections; int sec_size; int feat; int err; struct stat st;
nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); if (!nr_sections) return 0;
feat_sec = sec = calloc(nr_sections, sizeof(*feat_sec)); if (!feat_sec) return -1;
sec_size = sizeof(*feat_sec) * nr_sections;
lseek(fd, header->feat_offset, SEEK_SET);
err = perf_header__getbuffer64(header, fd, feat_sec, sec_size); if (err < 0) goto out_free;
if (fstat(fd, &st) < 0) { pr_err("Failed to stat the perf data file\n"); err = -1; goto out_free; }
for_each_set_bit(feat, header->adds_features, header->last_feat) { /* * FIXME: block devices have st_size == 0, so we skip * bounds checking entirely. Historically perf never * prevented using a block device as input, but it * probably should — there's no valid use case for it * and it bypasses all file-size validation. */ if (S_ISREG(st.st_mode) && (sec->offset > (u64)st.st_size || sec->size > (u64)st.st_size - sec->offset)) { pr_err("Feature %s (%d) section extends past EOF (offset=%" PRIu64 ", size=%" PRIu64 ", file=%" PRIu64 ")\n", header_feat__name(feat), feat, sec->offset, sec->size, (u64)st.st_size); err = -1; goto out_free; } err = process(sec++, header, feat, fd, data); if (err < 0) goto out_free; } err = 0;out_free: free(feat_sec); return err;}
static const int attr_file_abi_sizes[] = { [0] = PERF_ATTR_SIZE_VER0, [1] = PERF_ATTR_SIZE_VER1, [2] = PERF_ATTR_SIZE_VER2, [3] = PERF_ATTR_SIZE_VER3, [4] = PERF_ATTR_SIZE_VER4, 0,};
/* * In the legacy file format, the magic number is not used to encode endianness. * hdr_sz was used to encode endianness. But given that hdr_sz can vary based * on ABI revisions, we need to try all combinations for all endianness to * detect the endianness. */static int try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph){ uint64_t ref_size, attr_size; int i;
for (i = 0 ; attr_file_abi_sizes[i]; i++) { ref_size = attr_file_abi_sizes[i] + sizeof(struct perf_file_section); if (hdr_sz != ref_size) { attr_size = bswap_64(hdr_sz); if (attr_size != ref_size) continue;
ph->needs_swap = true; } pr_debug("ABI%d perf.data file detected, need_swap=%d\n", i, ph->needs_swap); return 0; } /* could not determine endianness */ return -1;}
#define PERF_PIPE_HDR_VER0 16
static const size_t attr_pipe_abi_sizes[] = { [0] = PERF_PIPE_HDR_VER0, 0,};
/* * In the legacy pipe format, there is an implicit assumption that endianness * between host recording the samples, and host parsing the samples is the * same. This is not always the case given that the pipe output may always be * redirected into a file and analyzed on a different machine with possibly a * different endianness and perf_event ABI revisions in the perf tool itself. */static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph){ u64 attr_size; int i;
for (i = 0 ; attr_pipe_abi_sizes[i]; i++) { if (hdr_sz != attr_pipe_abi_sizes[i]) { attr_size = bswap_64(hdr_sz); if (attr_size != hdr_sz) continue;
ph->needs_swap = true; } pr_debug("Pipe ABI%d perf.data file detected\n", i); return 0; } return -1;}
bool is_perf_magic(u64 magic){ if (!memcmp(&magic, __perf_magic1, sizeof(magic)) || magic == __perf_magic2 || magic == __perf_magic2_sw) return true;
return false;}
static int check_magic_endian(u64 magic, uint64_t hdr_sz, bool is_pipe, struct perf_header *ph){ int ret;
/* check for legacy format */ ret = memcmp(&magic, __perf_magic1, sizeof(magic)); if (ret == 0) { ph->version = PERF_HEADER_VERSION_1; pr_debug("legacy perf.data format\n"); if (is_pipe) return try_all_pipe_abis(hdr_sz, ph);
return try_all_file_abis(hdr_sz, ph); } /* * the new magic number serves two purposes: * - unique number to identify actual perf.data files * - encode endianness of file */ ph->version = PERF_HEADER_VERSION_2;
/* check magic number with one endianness */ if (magic == __perf_magic2) return 0;
/* check magic number with opposite endianness */ if (magic != __perf_magic2_sw) return -1;
ph->needs_swap = true;
return 0;}
int perf_file_header__read(struct perf_file_header *header, struct perf_header *ph, int fd){ ssize_t ret;
lseek(fd, 0, SEEK_SET);
ret = readn(fd, header, sizeof(*header)); if (ret <= 0) return -1;
if (check_magic_endian(header->magic, header->attr_size, false, ph) < 0) { pr_debug("magic/endian check failed\n"); return -1; }
if (ph->needs_swap) { mem_bswap_64(header, offsetof(struct perf_file_header, adds_features)); }
if (header->size > header->attrs.offset) { pr_err("Perf file header corrupt: header overlaps attrs\n"); return -1; }
if (header->size > header->data.offset) { pr_err("Perf file header corrupt: header overlaps data\n"); return -1; }
if ((header->attrs.offset <= header->data.offset && header->attrs.offset + header->attrs.size > header->data.offset) || (header->attrs.offset > header->data.offset && header->data.offset + header->data.size > header->attrs.offset)) { pr_err("Perf file header corrupt: Attributes and data overlap\n"); return -1; }
if (header->size != sizeof(*header)) { /* Support the previous format */ if (header->size == offsetof(typeof(*header), adds_features)) bitmap_zero(header->adds_features, HEADER_FEAT_BITS); else return -1; } else if (ph->needs_swap) { /* * feature bitmap is declared as an array of unsigned longs -- * not good since its size can differ between the host that * generated the data file and the host analyzing the file. * * We need to handle endianness, but we don't know the size of * the unsigned long where the file was generated. Take a best * guess at determining it: try 64-bit swap first (ie., file * created on a 64-bit host), and check if the hostname feature * bit is set (this feature bit is forced on as of fbe96f2). * If the bit is not, undo the 64-bit swap and try a 32-bit * swap. If the hostname bit is still not set (e.g., older data * file), punt and fallback to the original behavior -- * clearing all feature bits and setting buildid. */ mem_bswap_64(&header->adds_features, BITS_TO_U64(HEADER_FEAT_BITS));
if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { /* unswap as u64 */ mem_bswap_64(&header->adds_features, BITS_TO_U64(HEADER_FEAT_BITS));
/* unswap as u32 */ mem_bswap_32(&header->adds_features, BITS_TO_U32(HEADER_FEAT_BITS)); }
if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { bitmap_zero(header->adds_features, HEADER_FEAT_BITS); __set_bit(HEADER_BUILD_ID, header->adds_features); } }
memcpy(&ph->adds_features, &header->adds_features, sizeof(ph->adds_features));
ph->data_offset = header->data.offset; ph->data_size = header->data.size; ph->feat_offset = header->data.offset + header->data.size; ph->last_feat = HEADER_LAST_FEATURE; return 0;}
static int perf_file_section__process(struct perf_file_section *section, struct perf_header *ph, int feat, int fd, void *data){ struct feat_fd fdd = { .fd = fd, .ph = ph, .size = section->size, .offset = 0, };
if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { pr_debug("Failed to lseek to %" PRIu64 " offset for feature %s (%d), continuing...\n", section->offset, header_feat__name(feat), feat); return 0; }
if (feat >= HEADER_LAST_FEATURE) { pr_debug("unknown feature %d, continuing...\n", feat); return 0; }
if (!feat_ops[feat].process) return 0;
return feat_ops[feat].process(&fdd, data);}
static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, struct perf_header *ph, struct perf_data *data){ ssize_t ret;
ret = perf_data__read(data, header, sizeof(*header)); if (ret <= 0) return -1;
if (check_magic_endian(header->magic, header->size, true, ph) < 0) { pr_debug("endian/magic failed\n"); return -1; }
if (ph->needs_swap) header->size = bswap_64(header->size);
/* The last feature is written out as a 0 sized event and will update this value. */ ph->last_feat = 0; return 0;}
static int perf_header__read_pipe(struct perf_session *session){ struct perf_header *header = &session->header; struct perf_pipe_file_header f_header;
if (perf_file_header__read_pipe(&f_header, header, session->data) < 0) { pr_debug("incompatible file format\n"); return -EINVAL; }
return f_header.size == sizeof(f_header) ? 0 : -1;}
static int read_attr(int fd, struct perf_header *ph, struct perf_file_attr *f_attr){ struct perf_event_attr *attr = &f_attr->attr; size_t sz, left; size_t our_sz = sizeof(f_attr->attr); ssize_t ret;
memset(f_attr, 0, sizeof(*f_attr));
/* read minimal guaranteed structure */ ret = readn(fd, attr, PERF_ATTR_SIZE_VER0); if (ret <= 0) { pr_debug("cannot read %d bytes of header attr\n", PERF_ATTR_SIZE_VER0); if (ret == 0) errno = EIO; return -1; }
/* on file perf_event_attr size */ sz = attr->size;
if (ph->needs_swap) sz = bswap_32(sz);
if (sz == 0) { /* assume ABI0 */ sz = PERF_ATTR_SIZE_VER0; } else if (sz < PERF_ATTR_SIZE_VER0) { pr_debug("bad attr size %zu, expected at least %d\n", sz, PERF_ATTR_SIZE_VER0); errno = EINVAL; return -1; } else if (sz > our_sz) { pr_debug("file uses a more recent and unsupported ABI" " (%zu bytes extra)\n", sz - our_sz); errno = EINVAL; return -1; } /* what we have not yet read and that we know about */ left = sz - PERF_ATTR_SIZE_VER0; if (left) { void *ptr = attr; ptr += PERF_ATTR_SIZE_VER0;
ret = readn(fd, ptr, left); if (ret <= 0) { if (ret == 0) errno = EIO; return -1; } } /* read perf_file_section, ids are read in caller */ ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids)); if (ret <= 0) { if (ret == 0) errno = EIO; return -1; }
return 0;}
#ifdef HAVE_LIBTRACEEVENTstatic int evsel__prepare_tracepoint_event(struct evsel *evsel, struct tep_handle *pevent){ struct tep_event *event; char bf[128];
/* already prepared */ if (evsel->tp_format) return 0;
if (pevent == NULL) { pr_debug("broken or missing trace data\n"); return -1; }
event = tep_find_event(pevent, evsel->core.attr.config); if (event == NULL) { pr_debug("cannot find event format for %d\n", (int)evsel->core.attr.config); return -1; }
if (!evsel->name) { snprintf(bf, sizeof(bf), "%s:%s", event->system, event->name); evsel->name = strdup(bf); if (evsel->name == NULL) return -1; }
evsel->tp_format = event; return 0;}
static int evlist__prepare_tracepoint_events(struct evlist *evlist, struct tep_handle *pevent){ struct evsel *pos;
evlist__for_each_entry(evlist, pos) { if (pos->core.attr.type == PERF_TYPE_TRACEPOINT && evsel__prepare_tracepoint_event(pos, pevent)) return -1; }
return 0;}#endif
int perf_session__read_header(struct perf_session *session){ struct perf_data *data = session->data; struct perf_header *header = &session->header; struct perf_file_header f_header; struct perf_file_attr f_attr; u64 f_id; struct stat input_stat; int nr_attrs, nr_ids, i, j, err = -ENOMEM; int fd = perf_data__fd(data);
session->evlist = evlist__new(); if (session->evlist == NULL) return -ENOMEM;
session->evlist->session = session; session->machines.host.env = &header->env;
/* * We can read 'pipe' data event from regular file, * check for the pipe header regardless of source. */ err = perf_header__read_pipe(session); if (!err || perf_data__is_pipe(data)) { data->is_pipe = true; return err; }
err = -ENOMEM; if (perf_file_header__read(&f_header, header, fd) < 0) return -EINVAL;
if (header->needs_swap && data->in_place_update) { pr_err("In-place update not supported when byte-swapping is required\n"); return -EINVAL; }
/* * Sanity check that perf.data was written cleanly; data size is * initialized to 0 and updated only if the on_exit function is run. * If data size is still 0 then the file contains only partial * information. Just warn user and process it as much as it can. */ if (f_header.data.size == 0) { pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" "Was the 'perf record' command properly terminated?\n", data->file.path); }
if (f_header.attr_size == 0) { pr_err("ERROR: The %s file's attr size field is 0 which is unexpected.\n" "Was the 'perf record' command properly terminated?\n", data->file.path); return -EINVAL; }
if (fstat(fd, &input_stat) < 0) return -errno;
/* Check before assigning to int to avoid u64-to-int truncation */ if (f_header.attrs.size / f_header.attr_size > MAX_NR_ATTRS) { pr_err("Too many attributes: %" PRIu64 " (max %d)\n", f_header.attrs.size / f_header.attr_size, MAX_NR_ATTRS); return -EINVAL; } nr_attrs = f_header.attrs.size / f_header.attr_size; lseek(fd, f_header.attrs.offset, SEEK_SET);
for (i = 0; i < nr_attrs; i++) { struct evsel *evsel; off_t tmp;
if (read_attr(fd, header, &f_attr) < 0) goto out_errno;
if (header->needs_swap) { f_attr.ids.size = bswap_64(f_attr.ids.size); f_attr.ids.offset = bswap_64(f_attr.ids.offset); perf_event__attr_swap(&f_attr.attr); }
/* * Validate ids section: must be aligned to u64, and * the count must fit in an int to avoid truncation in * nr_ids and size_t overflow in perf_evsel__alloc_id() * on 32-bit architectures. */ if (f_attr.ids.size % sizeof(u64)) { pr_err("Invalid ids section size %" PRIu64 " for attr %d, not aligned to u64\n", f_attr.ids.size, i); err = -EINVAL; goto out_delete_evlist; }
/* * Cap the ID count to avoid int truncation of nr_ids * on 64-bit and size_t overflow in the allocation * paths (nr_ids * sizeof(u64), nr_ids * * sizeof(struct perf_sample_id)) on 32-bit. */ if (f_attr.ids.size / sizeof(u64) > MAX_IDS_PER_ATTR) { pr_err("Invalid ids section size %" PRIu64 " for attr %d, too many IDs\n", f_attr.ids.size, i); err = -EINVAL; goto out_delete_evlist; }
/* * FIXME: see perf_header__process_sections() — block * devices bypass this check because st_size is 0. */ if (S_ISREG(input_stat.st_mode) && (f_attr.ids.offset > (u64)input_stat.st_size || f_attr.ids.size > (u64)input_stat.st_size - f_attr.ids.offset)) { pr_err("Invalid ids section for attr %d: offset=%" PRIu64 " size=%" PRIu64 " exceeds file size %" PRIu64 "\n", i, f_attr.ids.offset, f_attr.ids.size, (u64)input_stat.st_size); err = -EINVAL; goto out_delete_evlist; }
tmp = lseek(fd, 0, SEEK_CUR); evsel = evsel__new(&f_attr.attr);
if (evsel == NULL) goto out_delete_evlist;
evsel->needs_swap = header->needs_swap; /* * Do it before so that if perf_evsel__alloc_id fails, this * entry gets purged too at evlist__delete(). */ evlist__add(session->evlist, evsel);
nr_ids = f_attr.ids.size / sizeof(u64); /* * We don't have the cpu and thread maps on the header, so * for allocating the perf_sample_id table we fake 1 cpu and * hattr->ids threads. */ if (perf_evsel__alloc_id(&evsel->core, 1, nr_ids)) goto out_delete_evlist;
lseek(fd, f_attr.ids.offset, SEEK_SET);
for (j = 0; j < nr_ids; j++) { if (perf_header__getbuffer64(header, fd, &f_id, sizeof(f_id))) goto out_errno;
perf_evlist__id_add(&session->evlist->core, &evsel->core, 0, j, f_id); }
lseek(fd, tmp, SEEK_SET); }
/* * Skip feature section processing for truncated files * (data.size == 0 means recording was interrupted). The * section table is unreliable in that case, and the event * data can still be processed without the feature headers. * Clear the bitmap so has_feat() returns false and tools * use their "feature not present" fallbacks instead of * accessing uninitialized env fields. */ if (f_header.data.size == 0) { bitmap_zero(header->adds_features, HEADER_FEAT_BITS); } else {#ifdef HAVE_LIBTRACEEVENT err = perf_header__process_sections(header, fd, &session->tevent, perf_file_section__process); if (err < 0) goto out_delete_evlist;
if (evlist__prepare_tracepoint_events(session->evlist, session->tevent.pevent)) { err = -ENOMEM; goto out_delete_evlist; }#else err = perf_header__process_sections(header, fd, NULL, perf_file_section__process); if (err < 0) goto out_delete_evlist;#endif }
/* * Without nr_cpus_avail the sample CPU bounds check in * perf_session__deliver_event() is bypassed, allowing crafted * CPU IDs to reach downstream consumers that index fixed-size * arrays (timechart, kwork, sched — all sized MAX_NR_CPUS). * * This can happen with truncated files (interrupted recording * loses all feature sections), very old files that predate * HEADER_NRCPUS, or crafted files that omit it. Fall back to * MAX_NR_CPUS so the bounds check is still effective — any * CPU ID below that limit is safe for all downstream arrays. */ if (header->env.nr_cpus_avail == 0) { header->env.nr_cpus_avail = MAX_NR_CPUS; pr_warning("WARNING: perf.data is missing HEADER_NRCPUS, using MAX_NR_CPUS (%d) as CPU bound\n", MAX_NR_CPUS); }
return 0;out_errno: return -errno;
out_delete_evlist: evlist__delete(session->evlist); session->evlist = NULL; return err;}
int perf_event__process_feature(const struct perf_tool *tool __maybe_unused, struct perf_session *session, union perf_event *event){ struct feat_fd ff = { .fd = 0 }; struct perf_record_header_feature *fe = (struct perf_record_header_feature *)event; struct perf_header *header = &session->header; int type = fe->header.type; int feat = (int)fe->feat_id; int ret = 0; bool print = dump_trace; bool last_feature_mark = false;
if (type < 0 || type >= PERF_RECORD_HEADER_MAX) { pr_warning("invalid record type %d in pipe-mode\n", type); return 0; } if (feat == HEADER_RESERVED) { pr_warning("invalid reserved record type in pipe-mode\n"); return -1; } if (feat < 0 || feat == INT_MAX) { pr_warning("invalid value for feature type %x\n", feat); return -1; } if (feat >= header->last_feat) { if (event->header.size == sizeof(*fe)) { /* * Either an unexpected zero size feature or the * HEADER_LAST_FEATURE mark. */ if (feat > header->last_feat) header->last_feat = min(feat, HEADER_LAST_FEATURE); last_feature_mark = true; } else { /* * A feature but beyond what is known as in * bounds. Assume the last feature is 1 beyond this * feature. */ session->header.last_feat = min(feat + 1, HEADER_LAST_FEATURE); } } if (feat >= HEADER_LAST_FEATURE) { if (!last_feature_mark) { pr_warning("unknown feature %d for data file version (%s) in this version of perf (%s)\n", feat, header->env.version, perf_version_string); } return 0; } if (event->header.size < sizeof(*fe)) { pr_warning("feature header size too small\n"); return -1; } ff.buf = (void *)fe->data; ff.size = event->header.size - sizeof(*fe); ff.ph = header;
if (feat_ops[feat].process && feat_ops[feat].process(&ff, NULL)) { // Processing failed, ignore when this is the last feature mark. if (!last_feature_mark) ret = -1; goto out; }
if (session->tool->show_feat_hdr) { if (!feat_ops[feat].full_only || session->tool->show_feat_hdr >= SHOW_FEAT_HEADER_FULL_INFO) { print = true; } else { fprintf(stdout, "# %s info available, use -I to display\n", feat_ops[feat].name); } }
if (dump_trace) printf(", ");
if (print) { if (feat_ops[feat].print) feat_ops[feat].print(&ff, stdout); else printf("# %s", feat_ops[feat].name); }
out: free_event_desc(ff.events); return ret;}
size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp){ struct perf_record_event_update *ev = &event->event_update; struct perf_cpu_map *map; size_t ret;
ret = fprintf(fp, "\n... id: %" PRI_lu64 "\n", ev->id);
switch (ev->type) { case PERF_EVENT_UPDATE__SCALE: if (event->header.size < offsetof(struct perf_record_event_update, scale) + sizeof(ev->scale)) { ret += fprintf(fp, "... scale: (truncated)\n"); break; } ret += fprintf(fp, "... scale: %f\n", ev->scale.scale); break; case PERF_EVENT_UPDATE__UNIT: case PERF_EVENT_UPDATE__NAME: { size_t str_off = offsetof(struct perf_record_event_update, unit); size_t max_len = event->header.size > str_off ? event->header.size - str_off : 0;
if (max_len == 0 || strnlen(ev->unit, max_len) == max_len) { ret += fprintf(fp, "... %s: (unterminated)\n", ev->type == PERF_EVENT_UPDATE__UNIT ? "unit" : "name"); break; } ret += fprintf(fp, "... %s: %s\n", ev->type == PERF_EVENT_UPDATE__UNIT ? "unit" : "name", ev->unit); break; } case PERF_EVENT_UPDATE__CPUS: { size_t cpus_off = offsetof(struct perf_record_event_update, cpus); u32 cpus_payload;
if (event->header.size < cpus_off + sizeof(__u16) + sizeof(struct perf_record_range_cpu_map)) { ret += fprintf(fp, "... cpus: (truncated)\n"); break; }
/* * Validate nr against payload — this function may be * called from the stub handler (dump_trace path) which * bypasses perf_event__process_event_update() validation. */ cpus_payload = event->header.size - cpus_off; if (ev->cpus.cpus.type == PERF_CPU_MAP__CPUS) { if (cpus_payload < offsetof(struct perf_record_cpu_map_data, cpus_data.cpu) || ev->cpus.cpus.cpus_data.nr > (cpus_payload - offsetof(struct perf_record_cpu_map_data, cpus_data.cpu)) / sizeof(ev->cpus.cpus.cpus_data.cpu[0])) { ret += fprintf(fp, "... cpus: nr %u exceeds payload\n", ev->cpus.cpus.cpus_data.nr); break; } } else if (ev->cpus.cpus.type == PERF_CPU_MAP__MASK) { if (ev->cpus.cpus.mask32_data.long_size == 4) { if (cpus_payload < offsetof(struct perf_record_cpu_map_data, mask32_data.mask) || ev->cpus.cpus.mask32_data.nr > (cpus_payload - offsetof(struct perf_record_cpu_map_data, mask32_data.mask)) / sizeof(ev->cpus.cpus.mask32_data.mask[0])) { ret += fprintf(fp, "... cpus: mask nr %u exceeds payload\n", ev->cpus.cpus.mask32_data.nr); break; } } else if (ev->cpus.cpus.mask64_data.long_size == 8) { if (cpus_payload < offsetof(struct perf_record_cpu_map_data, mask64_data.mask) || ev->cpus.cpus.mask64_data.nr > (cpus_payload - offsetof(struct perf_record_cpu_map_data, mask64_data.mask)) / sizeof(ev->cpus.cpus.mask64_data.mask[0])) { ret += fprintf(fp, "... cpus: mask nr %u exceeds payload\n", ev->cpus.cpus.mask64_data.nr); break; } } }
ret += fprintf(fp, "... ");
map = cpu_map__new_data(&ev->cpus.cpus); if (map) { ret += cpu_map__fprintf(map, fp); perf_cpu_map__put(map); } else ret += fprintf(fp, "failed to get cpus\n"); break; } default: ret += fprintf(fp, "... unknown type\n"); break; }
return ret;}
size_t perf_event__fprintf_attr(union perf_event *event, FILE *fp){ return perf_event_attr__fprintf(fp, &event->attr.attr, __desc_attr__fprintf, NULL);}
int perf_event__process_attr(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct evlist **pevlist){ struct perf_event_attr attr; u32 i, n_ids, raw_attr_size; u64 *ids; size_t attr_size, copy_size; struct evsel *evsel; struct evlist *evlist = *pevlist;
/* * HEADER_ATTR event layout (pipe/inject mode): * * [header (8 bytes)] [attr (attr_size bytes)] [id0 id1 ... idN] * |<------------------ header.size --------------------------->| * * attr_size varies across perf versions: VER0 = 64 bytes, * current sizeof(struct perf_event_attr) = larger. A newer * producer may emit a larger attr than we understand. * * attr.size == 0 (ABI0) means the producer didn't set it * (e.g., bench/inject-buildid, older perf). Treat as VER0. * * Require 8-byte alignment so the u64 ID array is aligned * and attr.size fits cleanly within the payload. * * Read attr.size once — the event may be on a shared mmap * and re-reading could yield a different value. */ raw_attr_size = event->attr.attr.size; if (event->header.size < sizeof(event->header) + PERF_ATTR_SIZE_VER0 || (raw_attr_size && (raw_attr_size < PERF_ATTR_SIZE_VER0 || raw_attr_size % sizeof(u64) || raw_attr_size > event->header.size - sizeof(event->header)))) { pr_err("PERF_RECORD_HEADER_ATTR: invalid attr.size %u (event size %u, min %d)\n", raw_attr_size, event->header.size, PERF_ATTR_SIZE_VER0); return -EINVAL; }
if (dump_trace) perf_event__fprintf_attr(event, stdout);
if (evlist == NULL) { *pevlist = evlist = evlist__new(); if (evlist == NULL) return -ENOMEM; }
/* * attr_size = footprint of the attr in the event — determines * where the ID array starts. For ABI0, assume VER0 (64 bytes). * * copy_size = how much we copy into our local struct, capped at * sizeof(attr) so a newer producer's larger attr doesn't * overflow. Fields beyond copy_size are zeroed. * * Do NOT write attr_size back to the event — native-endian * files use MAP_SHARED (read-only), writing would SIGSEGV. * The swap path handles ABI0 in perf_event__attr_swap() * which writes to the writable MAP_PRIVATE copy instead. */ attr_size = raw_attr_size ?: PERF_ATTR_SIZE_VER0; copy_size = min(attr_size, sizeof(attr)); memcpy(&attr, &event->attr.attr, copy_size); if (copy_size < sizeof(attr)) memset((void *)&attr + copy_size, 0, sizeof(attr) - copy_size);
/* * Normalize ABI0: the swap path sets attr.size = VER0 on the * event, but the native path leaves it as 0. Set it on the * local copy so perf inject re-synthesizes with consistent * layout regardless of endianness. */ attr.size = attr_size;
evsel = evsel__new(&attr); if (evsel == NULL) return -ENOMEM;
evlist__add(evlist, evsel);
/* * IDs occupy the remainder after header + attr. Use attr_size * (not copy_size) — even if the producer's attr is larger than * our struct, the IDs start after attr_size bytes in the event. * Validation above guarantees attr_size <= payload size. */ n_ids = event->header.size - sizeof(event->header) - attr_size; n_ids = n_ids / sizeof(u64); /* * We don't have the cpu and thread maps on the header, so * for allocating the perf_sample_id table we fake 1 cpu and * hattr->ids threads. */ if (perf_evsel__alloc_id(&evsel->core, 1, n_ids)) return -ENOMEM;
/* * Locate IDs at attr_size bytes past the attr start in the * event. Cannot use perf_record_header_attr_id() — that * macro reads event->attr.attr.size, which is 0 for ABI0 * on the native-endian path (no swap handler to fix it up). */ ids = (void *)&event->attr.attr + attr_size; for (i = 0; i < n_ids; i++) { perf_evlist__id_add(&evlist->core, &evsel->core, 0, i, ids[i]); }
return 0;}
int perf_event__process_event_update(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct evlist **pevlist){ struct perf_record_event_update *ev = &event->event_update; struct evlist *evlist; struct evsel *evsel; struct perf_cpu_map *map;
/* * Validate payload before dump_trace or processing — both * paths access variant-specific fields without further checks. */ if (ev->type == PERF_EVENT_UPDATE__UNIT || ev->type == PERF_EVENT_UPDATE__NAME) { size_t str_off = offsetof(struct perf_record_event_update, unit); size_t max_len = event->header.size > str_off ? event->header.size - str_off : 0;
if (max_len == 0 || strnlen(ev->unit, max_len) == max_len) { pr_warning("WARNING: PERF_RECORD_EVENT_UPDATE: %s not null-terminated, skipping\n", ev->type == PERF_EVENT_UPDATE__UNIT ? "unit" : "name"); return 0; } } else if (ev->type == PERF_EVENT_UPDATE__SCALE) { if (event->header.size < offsetof(struct perf_record_event_update, scale) + sizeof(ev->scale)) { pr_warning("WARNING: PERF_RECORD_EVENT_UPDATE: SCALE payload too small, skipping\n"); return 0; } } else if (ev->type == PERF_EVENT_UPDATE__CPUS) { size_t cpus_off = offsetof(struct perf_record_event_update, cpus); size_t min_cpus = sizeof(__u16) + sizeof(struct perf_record_range_cpu_map); u32 cpus_payload;
if (event->header.size < cpus_off + min_cpus) { pr_warning("WARNING: PERF_RECORD_EVENT_UPDATE: CPUS payload too small, skipping\n"); return 0; }
/* * Validate per-variant nr against the remaining * payload on the native path — the swap path clamps * nr in perf_event__event_update_swap(), but native * events are read-only and cannot be clamped in place. * cpu_map__new_data() trusts nr for allocation and * iteration, so unchecked values cause OOB reads. */ cpus_payload = event->header.size - cpus_off; switch (ev->cpus.cpus.type) { case PERF_CPU_MAP__CPUS: if (ev->cpus.cpus.cpus_data.nr > (cpus_payload - offsetof(struct perf_record_cpu_map_data, cpus_data.cpu)) / sizeof(ev->cpus.cpus.cpus_data.cpu[0])) { pr_warning("WARNING: EVENT_UPDATE CPUS: nr %u exceeds payload, skipping\n", ev->cpus.cpus.cpus_data.nr); return 0; } break; case PERF_CPU_MAP__MASK: if (ev->cpus.cpus.mask32_data.long_size == 4) { if (cpus_payload < offsetof(struct perf_record_cpu_map_data, mask32_data.mask) || ev->cpus.cpus.mask32_data.nr > (cpus_payload - offsetof(struct perf_record_cpu_map_data, mask32_data.mask)) / sizeof(ev->cpus.cpus.mask32_data.mask[0])) { pr_warning("WARNING: EVENT_UPDATE MASK: nr %u exceeds payload, skipping\n", ev->cpus.cpus.mask32_data.nr); return 0; } } else if (ev->cpus.cpus.mask64_data.long_size == 8) { if (cpus_payload < offsetof(struct perf_record_cpu_map_data, mask64_data.mask) || ev->cpus.cpus.mask64_data.nr > (cpus_payload - offsetof(struct perf_record_cpu_map_data, mask64_data.mask)) / sizeof(ev->cpus.cpus.mask64_data.mask[0])) { pr_warning("WARNING: EVENT_UPDATE MASK: nr %u exceeds payload, skipping\n", ev->cpus.cpus.mask64_data.nr); return 0; } } break; default: break; } }
if (dump_trace) perf_event__fprintf_event_update(event, stdout);
if (!pevlist || *pevlist == NULL) return -EINVAL;
evlist = *pevlist;
evsel = evlist__id2evsel(evlist, ev->id); if (evsel == NULL) return -EINVAL;
switch (ev->type) { case PERF_EVENT_UPDATE__UNIT: free((char *)evsel->unit); evsel->unit = strdup(ev->unit); break; case PERF_EVENT_UPDATE__NAME: free(evsel->name); evsel->name = strdup(ev->name); break; case PERF_EVENT_UPDATE__SCALE: evsel->scale = ev->scale.scale; break; case PERF_EVENT_UPDATE__CPUS: map = cpu_map__new_data(&ev->cpus.cpus); if (map) { perf_cpu_map__put(evsel->core.pmu_cpus); evsel->core.pmu_cpus = map; } else pr_err("failed to get event_update cpus\n"); break; default: break; }
return 0;}
#ifdef HAVE_LIBTRACEEVENTint perf_event__process_tracing_data(const struct perf_tool *tool __maybe_unused, struct perf_session *session, union perf_event *event){ ssize_t size_read, padding, size = event->tracing_data.size; int fd = perf_data__fd(session->data); char buf[BUFSIZ];
/* * The pipe fd is already in proper place and in any case * we can't move it, and we'd screw the case where we read * 'pipe' data from regular file. The trace_report reads * data from 'fd' so we need to set it directly behind the * event, where the tracing data starts. */ if (!perf_data__is_pipe(session->data)) { off_t offset = lseek(fd, 0, SEEK_CUR);
/* setup for reading amidst mmap */ lseek(fd, offset + sizeof(struct perf_record_header_tracing_data), SEEK_SET); }
size_read = trace_report(fd, &session->tevent, session->trace_event_repipe); padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read;
if (readn(fd, buf, padding) < 0) { pr_err("%s: reading input file", __func__); return -1; } if (session->trace_event_repipe) { int retw = write(STDOUT_FILENO, buf, padding); if (retw <= 0 || retw != padding) { pr_err("%s: repiping tracing data padding", __func__); return -1; } }
if (size_read + padding != size) { pr_err("%s: tracing data size mismatch", __func__); return -1; }
evlist__prepare_tracepoint_events(session->evlist, session->tevent.pevent);
return size_read + padding;}#endif
int perf_event__process_build_id(const struct perf_tool *tool __maybe_unused, struct perf_session *session, union perf_event *event){ __event_process_build_id(&event->build_id, event->build_id.filename, session); return 0;}