[llvm] [llvm-profgen][SPGO] Support profiles with multiple concurrent processes (PR #169353)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Nov 24 09:03:49 PST 2025
https://github.com/Heath123 updated https://github.com/llvm/llvm-project/pull/169353
>From 7c1e0811cb15bd4be1e9c0200d44f3075fe1ef3a Mon Sep 17 00:00:00 2001
From: Heath Mitchell <heath.mitchell at sony.com>
Date: Mon, 24 Nov 2025 15:27:09 +0000
Subject: [PATCH 1/2] [llvm-profgen][SPGO] Support profiles with multiple
concurrent processes
---
.../Inputs/multi-process-cs-probe.perfbin | Bin 0 -> 17704 bytes
.../Inputs/multi-process-cs-probe.perfscript | 30 +++
.../Inputs/multi-process-nocs-noprobe.perfbin | Bin 0 -> 17424 bytes
.../multi-process-nocs-noprobe.perfscript | 12 ++
.../Inputs/multi-process-warning.perfscript | 12 ++
.../tools/llvm-profgen/multi-pid-filter.test | 39 ++++
.../llvm-profgen/multi-process-cs-probe.test | 46 +++++
.../multi-process-nocs-noprobe.test | 40 ++++
.../llvm-profgen/multi-process-warning.test | 10 +
llvm/tools/llvm-profgen/PerfReader.cpp | 172 ++++++++++++++----
llvm/tools/llvm-profgen/PerfReader.h | 33 ++--
llvm/tools/llvm-profgen/ProfiledBinary.h | 28 ++-
llvm/tools/llvm-profgen/llvm-profgen.cpp | 17 +-
13 files changed, 374 insertions(+), 65 deletions(-)
create mode 100755 llvm/test/tools/llvm-profgen/Inputs/multi-process-cs-probe.perfbin
create mode 100644 llvm/test/tools/llvm-profgen/Inputs/multi-process-cs-probe.perfscript
create mode 100755 llvm/test/tools/llvm-profgen/Inputs/multi-process-nocs-noprobe.perfbin
create mode 100644 llvm/test/tools/llvm-profgen/Inputs/multi-process-nocs-noprobe.perfscript
create mode 100644 llvm/test/tools/llvm-profgen/Inputs/multi-process-warning.perfscript
create mode 100644 llvm/test/tools/llvm-profgen/multi-pid-filter.test
create mode 100644 llvm/test/tools/llvm-profgen/multi-process-cs-probe.test
create mode 100644 llvm/test/tools/llvm-profgen/multi-process-nocs-noprobe.test
create mode 100644 llvm/test/tools/llvm-profgen/multi-process-warning.test
diff --git a/llvm/test/tools/llvm-profgen/Inputs/multi-process-cs-probe.perfbin b/llvm/test/tools/llvm-profgen/Inputs/multi-process-cs-probe.perfbin
new file mode 100755
index 0000000000000000000000000000000000000000..6e98e86d236a442ff8d978ebe811ffa4f5ec0f7f
GIT binary patch
literal 17704
zcmeHP4QyN06~52*bDX-3-8O9lWz;}JTSlBXHKEY7b(*?ype at j*rRArOXZt0w=D*p`
zkRa&1X0S%3TRX8IrVSWqteUzBG1w5BwjefO2mzhe35jV62u48+ihwOEQ16`g&T*Va
zvLYr;+VpO9-?{fY=ezg3^X~h(@xA?0bkjzMBneJVu~uL$;x~~9DF#<l0YpTs5_90V
zOq?&=P<I)asgIZfv}StIF;#j9cY;J-6BeeCXPK~I$sVFa-b`s+HZ?2-od at y^71bGM
z*-U=2WW3s6fO at pZMskd0Ee~TOg2Y=pRr;yFCX&p<D)Riq^AnHdgVY~Ojwi+h^D2s`
zRR%zZDPSChu<~{i&nh>NJxlBZ)3(?2<Y(#cO5(W!Ml- at HJ55-y<o>P#o)J&dpHA>)
z-b($=bR7i8pCpA>w5C(>j&N%_*^*9WOG7O~t2$ab!ogxL*lu)<eqkRtrh2w)7ml(R
zLyY-YZiI|sMg6&t44<5xpZNIo&Wn3~)~vqt&_Gt_Hjslhn9!bG!aA0*5417Q|I1LK
zu?+I4_FTpQW;h^k-ZZ*yzyj0Y$_#jR27Er?=T6g(P!xz at LMiHML02+rDho(rNLBh%
zSv8%ySre$ncy@@MO}*>8mG)qJ at S<vYWmqV^eVdh}R?r4gMO`cOZSGFzvRa=SPixTD
zKqi-^))muqT0 at MWWc)J+xCC|dm&Zrx%v3!_{X)VXKJ+aJvjd)Rl<5BB`z0{f#61>#
zoEr2*NKbUGkF7HSyoPv9I6Ym1*IxG*YH-tYlks8=j^l^Zcnyx@#ZGnv><HKqup?kc
zz>a_&0XqVA1g49?d%m+z#zx-q#P-XNw+a!vZA^DmUWkqS*7Jm6Ub*NQfGf?<!qK-R
z0;L~AssA9ih5XRpdPk4$j*Y$&8+rA_m3`6BSm^h$18c5=@7&6QYr*{e{-AHkR}2c^
zcL5SRAb$hJB at _Ar=z1-dq3im}xNk`r4WA$h_9?^uqBx2br(&ZgVvoPQF826 at GbTM5
zJ9<iQga+nN1D?uwzi-KgNj~~lUegR}QTqJ$*vOhUabg`CeNCSo+rP#Q`0*8$O67PG
z4o}J-gHpO4`mXkm{5Ng_CHb=*sIjM_6X;{?QSqv`55L-DmqaIgBMYJQhN0LWorsN$
zNwLxB#2bGCGB#<RKl5=85PMYmZ2Xb8{7*$c!k$KNorsN=PK?|-;qcvY2DZ6BdLp(z
z;_>}jj7)@lx8Ypicn`QRgGoD?;Q8;5egqfZe?4CL>bR(m1OV-1N5GDN9RWK6b_DDQ
z*b%TJU`N1?fE at ul0{^oSz~959#m?3Edl&vTb=HYWB at OaIkavRY1lfo6cPo`6Ak!e9
z1$hMITOjc_PE69xTZJ^_mln^i^Bjc9E#mmw{rEeT%K2!pq0ab)Y$+7OkPicH01f_)
z4NaH%yf=BuV%?dmFIc(M_&E{z*FioAcKq8LRE;)ukVD{?@f%%Nga39%_Z)W-Iwt*{
zAiwi(^bdpnQP6831 at j#@m<=FiCp!Xm1ndad5wIg*N5GDN9RWK6b_DDQ{2xYu*E90^
zMP8d|Ov2!NXY)DJ`o=j_Z=yw|_?x4VXg%a&s`8xeOs<n2=Y*J8|G}wB4(s>QB1c}k
zxYLCwuXz>XY0}R!VX=>-mt+&k1teJzO&q87k#(d9P)F>?7~!xO)JWXkhZWO501eYq
zR6jxf!Oy0K{~R}%RvPtj(*Kj{><2afUl+W7e5Q_hooY{a_oad5+vBCIUJ8W5uqL;y
zC0sJ9?R(n8!M5-Uf(02R(rR`faHCczrgGUpXjL#2Tp3s%GC83YVoUzdyXDaPS7#PI
z`$L at 7%jsM$AM)LB-3~|N_kJ=3Z9jSJ!0R2!Z at zT6f5rXF;V%;|TsX{Ny^zWa9pzch
za-(OjaFu1bJj-2ftlul@$}+bngOSTo_JEOLF_*jlgyJyzA|&IgO5$@=0?;8|gCpYR
zGGJ+kQ#hRxuKRX54h_`_*C-T0>%c!iNIP7j&gq=gdQ7eC!apxaJ7nR#4GL~xdaK3!
zY~lSrprXz#yF7=WFw3#na~H^Zsin?S=c&IB%RfSkIc`|LJXdONo-aF_3yb`V7B|mb
zJkMQR1Rew>EJm7~&6j#td)L9u==2&8xEdA?!h2znwmj8z*%`g_(cCRhv8r1toXu;!
zUbw3~3q|jIvt97S2NiH;0<sLDF-}dAK5UpHBuIclhQbnD3?Y_4c0$=FpY2?az}lm~
z*%m)%&ljW*@{W~_*Y52FOnUkD>+gBxo|hka?%0*Tcr5z$yRSm9Irq#j-?{IW!_V)|
zT)cI6b|KtF^^P`LbKNM<pLMw`yZ6dx-7U+P$)SGv!kx1Gm at K)yyJT_i>C_FgJiFxi
z?#twb{qi|S<oZq_yPhhScew}ThHI>f=S_uljoaJlaKp)c%Hf_Ysp()sv<~JnT5A>#
zh1N_dt*2V at g<L`_7F%?!s0-sBFpllQSR9|q>LMjd*<xxSt0e=l(tQ9Hxt9R$2rIf!
z3j+x}+$a)*YQb2oj at N-B6Q&5|N$MmYBzYQdT1&-3Ybu*amy%j5>~v5%!nEySAX{pU
zr}Uzr)6$3YS}`~%ih2 at UYeE0AQ0;_kCqu at x3^J~<*$jCb<Xw<=LykjELM}koA>Rsl
zFXXR6o;l&za*1~<RttPiOQ5-h9;^W}z)?D#{n>y!M1#oFdyLFQfbe at Q_)5b0y~y^)
z&=qG0f5b%4KTNoLLxR70^1c8Agu^Y6v0r}AGQJNAk^pb%y~sG;^3(Z0(|P^WXlI`A
zpTjLo%IVtqm0`a?Sl<V~GiY;#D=^8&(u<HKv8ej~WbNBfn8%eG>4ks<YU*6<G;nyU
zMi?>WGQj<!sTxn#1_7Vv at Cp9TU_6XCUPl_)_z1>yDYav5zX$MuVKMcv1Ms<yCc*DL
z)+P`KkC;TSaZv!=57)gZ!j`6t0FM4dX#5y|>ju?LWY6P-StDf6cP2hbG3_I~&2nB}
z104Odw)0)U0|s;I;bFkfoeCMX=K-JRfakhpoQrLVf*uOy;9;Z2Q;Mz*2mp$DslOkd
zVw1aX6g{IPaQ{saER#89Af1b=X(g%W3PnXN4T(f9lTT~9mJGH{Z-G01Qi at tAsKbhu
z)eFO-zo2F`C0WX3hJjL3Q@~Q6VoHx6McKHuYjaeIZrPx~uAdFNwsdXo?FKSzpHiYR
z(#AGy6-v*hE7o^yQm)vzaa*)c>FZj*DGChhrlbh(Jv!ZfBQ2 at wsxZ!h9@`KGfXppO
z(D2m#NJ_GpQwG&+689?gUIA9{yi`g>Eh*sS;h at BeMdBLIfAfW)D6q?k&knagO}nRQ
z>PD*SDd6 at gBXaQkEe>aNH4aiQm~xOy at Wj>%c at fO!bS(&v_aMBN@>)S3u7TpE6l{D;
zB?;<U-`j!%!!RFIi-RJV9L_=)rqm0jhCTtOR20w^w6ux at 5yZ<Xg88%_9LPaI*M{JT
z!vc(QMu3CbAPt1UBv83#Qq2)zGPn%ws+m**I?d_WGPr~nDhS~fK^T)67;4aEQ7gfq
zg;R~gfYXX#Ukd?Ai<br<c>OsptMPb2yOC?K2~guQM3ReY61_CZXaZNXT8E;N>+dgW
z at b>)A6V9vP7KV3;nVRUm$CBq&q&951jPg9lbAiuf!8psP*BXK at YC0p(i$PU5p%
zT4Rever__1 at 6B31zqeTi*pQ@^e>LELhq)B{&+|o=+&}(?iOK4}3K-56na}e{mIsjp
z34O#kGaueqMoPgEPnr2VKV^yU^V)=M)TS>&1?P&)AEy&!$@6UXpY2%g0Y1(>xz6)X
zmTlCT)qnQk02HuG<_G9=jpY~_a{t!z|2F9HoLFCdkI?&s=l#^oC$9fLEc^iRSW=bg
z*5(DymTLQNBLYkImmA<Z%Lgp{2=Q4Svs^H%KSwNl{w`t3e;48Yt>gD0;#a?!0%S?j
zYR`3+j}f1*AAhH@#4qsL#5~5Hh60`<^CJOM!}5=2wR+^ZG4KZ{G(e`?e)78&KlLG5
z?YYkKFJR+?%zXY%_IyhIo501f$9(>N<$dzJA0Ed)ChnK7;|~DC^JhNq#~o`$Yay1K
zqIzUL%lAN2%jdr*9Vb3NC%6ITvHZxw7ZI{-GbQ^;Xw`qNJD_uHi~Z;CK|k at iAGSwd
zYf~Knh>>|!#r&;R#4IqMrJwi~GZCi-yDSRkF?=5J%M6gbpZNHE$An`Hla(JLzNwu&
zo+LirpS8)#Uo!*$fso1CS4)Pnm4EpReERPI1YY0TWaVE?d~5tKSZT8Es3k+0`7Gna
zx0s2a6W`h{^VmL1e5>7GiGKqdl4L&1J;b+|iH%_siC7fOV;JlF>|qS$KH?A2z-|T%
opOHMC`M$#WK97AUP9~ZFTA$z6{&8A09sk%RW>beO1WQ%?3r1lTApigX
literal 0
HcmV?d00001
diff --git a/llvm/test/tools/llvm-profgen/Inputs/multi-process-cs-probe.perfscript b/llvm/test/tools/llvm-profgen/Inputs/multi-process-cs-probe.perfscript
new file mode 100644
index 0000000000000..1e23ed3f81fde
--- /dev/null
+++ b/llvm/test/tools/llvm-profgen/Inputs/multi-process-cs-probe.perfscript
@@ -0,0 +1,30 @@
+; Load first process
+ 262511 PERF_RECORD_MMAP2 262511/262511: [0x61f3acfa8000(0x1000) @ 0x1000 103:02 13931513 3957944494]: r-xp /home/multi-process-cs-probe.perfbin
+
+; Samples from first process
+ 262511
+ 61f3acfa8155
+ 61f3acfa81e9
+ 78d7ee62a1ca
+ 78d7ee62a28b
+ 61f3acfa8065
+ 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/-
+
+; Load second process
+ 262512 PERF_RECORD_MMAP2 262512/262512: [0x5b3c4c631000(0x1000) @ 0x1000 103:02 13931513 3957944494]: r-xp /home/multi-process-cs-probe.perfbin
+
+; Samples from both processes
+ 262512
+ 5b3c4c631195
+ 5b3c4c63120c
+ 73bf70e2a1ca
+ 73bf70e2a28b
+ 5b3c4c631065
+ 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/5/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/4/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/5/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/5/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/5/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/5/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/5/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/- 0x5b3c4c631190/0x5b3c4c631195/P/-/-/1/UNCOND/- 0x5b3c4c6311a1/0x5b3c4c63117c/P/-/-/1/UNCOND/-
+ 262511
+ 61f3acfa813c
+ 61f3acfa81e9
+ 78d7ee62a1ca
+ 78d7ee62a28b
+ 61f3acfa8065
+ 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/4/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/- 0x61f3acfa8161/0x61f3acfa813c/P/-/-/1/UNCOND/- 0x61f3acfa8150/0x61f3acfa8155/P/-/-/1/UNCOND/-
diff --git a/llvm/test/tools/llvm-profgen/Inputs/multi-process-nocs-noprobe.perfbin b/llvm/test/tools/llvm-profgen/Inputs/multi-process-nocs-noprobe.perfbin
new file mode 100755
index 0000000000000000000000000000000000000000..b7859ec8d679442adf150a78e7b19c073e2958b9
GIT binary patch
literal 17424
zcmeHOdu$xV8K1rLo-c9iJ3BAuQF4X25Q_JF&S64ul5?C()->S}oHRg_?VfM%e9}GK
zyS>ClKvPU;tdurE2ujuRl1fn(wX{$b(H0re0!4^N1y%m43<W9B6hfiYEs*Q??S9|o
zy!Zg2s#2>RYxXzuo9{O>`|aHBj(6tk at vWOZk|a2I#d?9cSkPu-RPg*-N&v*f8qo~L
z4zW`BAm3ndc6rPOD7C{e&rI|a?*&9%J0 at nq<u)xCxrd0Ts~4tZyM$5Dd7#cJqCE4A
z9n?=o=Bw?6s6_`ZppG%t>d-erA>I0!7^L>v30a3()CEZwBpu at e)E*=I6Mcel4f)fF
zAt=KTvW`MHb-PIC#7$(+h;3lF;x#k*S=zgdbPXY^8sWrVn-+}R-sPaP{7L1X1bmsd
zQ+xHUgJAy?l6yrsn at RLU!`WnKHj^)pc8;#;>FkLrrGnCJHI4nkHgHUBxnzg%>=%>B
zu^!V4Afj8be95CfE&TB5)?>eU_lC`P-ah)5hEvTCavi8a8w_aAUBW!3u?@7b&;J{c
zqOlD4g7Iw5BI+Da*EUO8KX9R0c&!efufxv={>)kG5t;@b9MVdrUNp6wp2-6<G^%T<
zOkU4st}z4_qd$AZmaPLD`?YSRTRFd)UL6(Mz~DA5X%vlgreqq$!EOE7Lf#nE6Ilc5
zO6LlBs$H{9XEj6*O4gq}z$M6Ie|dbAmuBXm*UuyFp(D3}n>}!cqd>nu{=I}+ZQk$T
zr>H`IjLJ#Q`N<77f!7eP35Ta at _}c6Kd<}2++*TZ`;c@(Mn5yA%ytu*jfa?L*1Fi>L
z54aw1J>Yu4^}uW%cqeeiztr&)e)W+2SXc=4z at +J^Jg<)b*8jL=UOE41per5Ez%j5Q
z2B{xIDs`CaLVe_~0~0UoQ72we$6q~p*<d^}8Tq|>^SaC7J-4#(YA}B{r36;oXi0#-
z0~qyY`4)r=r_F`X^m<G~({n0Qfff7F at Nq)0&sg^7ClFShQYTKTkG;7;eQesRN>8XS
zoH7?c1<h1}zcQ5ytk`r~kNw-ft^<li`HMT$@pW(D#9Ezr&77wmTIU1)_^L{!ay$u#
zC*%*IP}&V`SKCMZ>-(UP`m+P1>XY$l?4$ayc(rK|-`dp+<I{ohMUZ;kDmWCMR>vnL
zbs|3f`X51zRodszdYl8O4@;j-KYk+kWc&kcY2vzRb)tN7{JLpR;9E<u&O`B&>Y<oF
z at TeG{jsy<iT;TW?=t3QnZZg61KNSA}E_`q|Uis>{sE!06-Qaq_^?>UE*8{ExTo1S&
za6RC9!1aLZ0oMcnvmU_jVbU`1Mfkl7zfCPaS*c_J&jY*_uorL;^KVxwM**{d&j20;
zJOPN`I59}qY!}jKP+B&x(SH~wx5(qS`>D4ol`o*drbg=v*-A)8A>I$XMYIGrx3pgz
zXxiuBFE%W_=-kySt<Q<5{|dyzV8`FyAZyiW0**qztZ#IEEy0^S{ms4-G)(2U0)F>n
z${&LAhoRg66s-4r)vf?JH at F^fJ>Yu4^?>UE*8{ExTo1S&a6RC9;Q!DAyq=NQFY?+%
zYZ3<MJCDzq);Bg&zMU48;x|Vt(0a&al;t_wQqEI3&IvJa`QJ}f3Yfo}7CG|T#a#`^
z@|ss6o}%)(HZ2YkHW9WHE+k|inK(}CBO9q8L=AC2CW(i|pjP1e-mlpG1JW`*MfsD|
zKlp5F^`HF)(@HBpMdkmbJokgD|E~kDAFtOiuT$O9- at i6=&W=PmZ<a%mD6Glt>Wr4H
zZ1?ruQKc)oifBPbLs>na4(&CHrA#3oimXv0%IeTL5nB^kCGLkNWg%JKlf>sJ1*Av3
z21n%W<+jpJuW0Z}xW2l<b7Ztp$P<u+QV&k_rJW68j at Ns-bgo@0H{dj0+9`|X1Ca26
zvZ<OJnJ1cm0IX>A$qoJ^keKUP=D!1Qj?~%cZ}iW(7t=pNi)J6J6K<6{I_AsXj^g6r
z;$<DJ%i4UU#n1yq!ep%7+p)IkqNWWn=)FxA1zn8_5Ag%A7+RibdiIim`DpHwXIM3^
z6yA>YO-(R*{forFe7j!gNdPj?nKvQI;2P`HB<cN at W+6cU79u28;GzTT|0rHa2jnxn
zUq)il%J&|O3~c=K&tClSz7t3CukHa(dikc^cfE4g%fEQ`rOST#Nc=l at rjYC?JpHS;
z?!ET@=l0|-*uE#f2uAB1PZzCiUMRQD{gUkU-5 at W$Q}$jghg0&oyJYVpvgB*JQWp1o
zeDU4#yt)3X<yPOt@`9AS;;7u%E6cvLJnyQHa?(}4re2Q^PU%w)CetXH%8&>T7ji~8
z4~Jqno86o1G?HnfWF^CB5;hBktex*1DHc*`BQLUr!brqAb_;7gdM0m*j40<znRMPr
zhG21d8rGGUf$oWFrqGM&Aw29AL&JK}S|E;>gq;*6XN?e=gbxsYmM(_NrD8afAIg at K
zMi_P+XgyKdevr<W!-<Sp6664LY{V!j!=hv+p}(Ej={#h6;o8a&F`Nw%*SaJi4nxdC
zEJ54{aSY-&A>vQy7Kpb){656`0moj0c)Q}X!26{HCD+moE<^@6if6Mw19*>U5hL_G
zA#)KT{%!}qns|ObvHbufB(X#UV>W~R!+^UeB>3+mug|wgI9v-6+vVpS^9Laz3Fs|7
zpP0v6!fgGocV0iS>S+_f&vOl at akhGXZP_mr&inH3ELp2)2%Xkr>KH^xEUrGsxD at Xc
zZJepWCU8ioCeO)Ui-)IMj2Rox20kd-tN!Ft1^6~kK=AXP`6%*u9cj4ZEgHjGs>fOX
z7T`mc#mvJ_;9EWIf}cNJI)ps*h(+``6-D5KaNXNuY-wW*c<fJ%#*g(sXDHf7_B>8l
zHBR>YbHzI?hJ(a+InL|bz+*q1^?VQbkfoe?cnJ72XHr(~IpEtoaNl=~b8*E`(Tpes
zxC7~ljArU-0Yu3xr&4ggJG~1=GjrMy?t>|TWwM~9vxS77)skkRSkm<Js2D2bMzV%!
zB$ck&EpP`;M$?N$eM~d*W^qiUih9n_lI2`(3?wx<4J^$WrgZz!w9VW5w#BvhC7U$Z
zeY5GxOZv7A^n)0-F==s?O4Uu<g|=nur5pRUYL{-_d_{av8|>S-H4X~r=Aanw0s5qU
zKSokFbzz+Y-OIrZK-pV?pyHYPg0y6*pbhK!B<>j+xD>45eyNp9MpD4Z!$C`wN~E<O
z3ieY%(_ps|pB-*Pnssl{%neP|Q^0LZmgnI9TN=xmdIHca+BnQ9xMLf|5uxM at rlG*?
zU4iG+h*31hYN$jx0~^pXNuv5T4s_zcu*`?`(y&mHV|i%8Mzd&_(5u*tj0R;zBdeo8
zD0o?gGLkiwbO92kF$zZ<7GPAc9IP0_G!TZ9AmyAbwMT at l;55{$=Q2akXu-sqp-Xt7
z6mYLlU`*y<r~zjr%4zUTs=#SIktiB_IR|^~3{HciIH at PG6Sf#l;DS~2kkksPRLOvc
z<9{IF`~`lZ at T{;yJ3ZGJmlMx`wzc6b$TUJ^{Tk9UhH8x=ZM_!Pa6OQ&)$bxb<H{Oa
zO!9q^X?!l$>iN0M7-B<0ryk!U--9^|_n+rwjNCqcE5zXJzYZME$ym?xHpas!f`ENQ
z-?1K^NLI+e5l at -*JP%~V=XGsx`u}TCh;uU5Ptgf7 at _d&2&vuO0gC6I2oacESV;42<
z>_7MEW=LR at tPj!a7~>=va{JEn$2T!NCoZo(C+K;>^J}W+BiH{Q4t<DpjFe@$vwDHE
zrP}tpNWjSb<q9~@c%MTbBR%6wjtl1O&rye--#Zxj{}0^0bNv3C^woEw5LpsB?K#i*
z2<iFy at p~9!FIC6DI?g}k(8oe{3FC`wNXUL;`VWw3fk;35)9<B<ZQ-=%Jma6hEKsB8
z_hA30)V~2*9DCe8zn}7cbl!K4;~xXJ%h&N9;P4W&p7*^@hS6Gxb8M&{S<m<ml+^0^
z|2D at -&-V$gfOU)?IP at Y$mR&Y-KZ&jOpYtAQ9P8r#^ZQ<q^xO{HV_$1SBM``ud9{f3
z+pCP7U_E1y^bRwTpbGK#Q5#st-10j5mWXX}kPQi)`g7~(ZzKKRK0)6 at dgu8c?6!sX
z)X{T4`s%d5YPBtN*2_Asb{pxPc2|<V%!Y)lXWUJChnaYm^f5;P>zKwo-#eH?3`XtT
vC=IN$Aln9!#}kihoKN%EvlaI7-~~21 at 1M^0aoRGQe)om8e#9YgWW~P$_{!)h
literal 0
HcmV?d00001
diff --git a/llvm/test/tools/llvm-profgen/Inputs/multi-process-nocs-noprobe.perfscript b/llvm/test/tools/llvm-profgen/Inputs/multi-process-nocs-noprobe.perfscript
new file mode 100644
index 0000000000000..2ec2975cf07af
--- /dev/null
+++ b/llvm/test/tools/llvm-profgen/Inputs/multi-process-nocs-noprobe.perfscript
@@ -0,0 +1,12 @@
+; Load first process
+ 149754 PERF_RECORD_MMAP2 149754/149754: [0x5ffeb3637000(0x1000) @ 0x1000 103:02 16291238 2733047042]: r-xp /home/multi-process-nocs-noprobe.perfbin
+
+; Samples from first process
+ 149754 5ffeb3637150 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/-
+
+; Load second process
+ 149755 PERF_RECORD_MMAP2 149755/149755: [0x5caa46c41000(0x1000) @ 0x1000 103:02 16291238 2733047042]: r-xp /home/multi-process-nocs-noprobe.perfbin
+
+; Samples from both processes
+ 149755 5caa46c41190 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/-
+ 149754 5ffeb3637161 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/-
diff --git a/llvm/test/tools/llvm-profgen/Inputs/multi-process-warning.perfscript b/llvm/test/tools/llvm-profgen/Inputs/multi-process-warning.perfscript
new file mode 100644
index 0000000000000..bfca95dd26307
--- /dev/null
+++ b/llvm/test/tools/llvm-profgen/Inputs/multi-process-warning.perfscript
@@ -0,0 +1,12 @@
+; Load first process
+PERF_RECORD_MMAP2 149754/149754: [0x5ffeb3637000(0x1000) @ 0x1000 103:02 16291238 2733047042]: r-xp /home/multi-process-nocs-noprobe.perfbin
+
+; Samples from first process
+ 5ffeb3637150 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/-
+
+; Load second process
+PERF_RECORD_MMAP2 149755/149755: [0x5caa46c41000(0x1000) @ 0x1000 103:02 16291238 2733047042]: r-xp /home/multi-process-nocs-noprobe.perfbin
+
+; Samples from both processes
+ 5caa46c41190 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/- 0x5caa46c41190/0x5caa46c41195/P/-/-/1/UNCOND/- 0x5caa46c411a1/0x5caa46c4117c/P/-/-/1/UNCOND/-
+ 5ffeb3637161 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/- 0x5ffeb3637161/0x5ffeb363713c/P/-/-/1/UNCOND/- 0x5ffeb3637150/0x5ffeb3637155/P/-/-/1/UNCOND/-
diff --git a/llvm/test/tools/llvm-profgen/multi-pid-filter.test b/llvm/test/tools/llvm-profgen/multi-pid-filter.test
new file mode 100644
index 0000000000000..0802a6b545555
--- /dev/null
+++ b/llvm/test/tools/llvm-profgen/multi-pid-filter.test
@@ -0,0 +1,39 @@
+; Test that the --pid argument works, including when passing multiple PIDs
+
+; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/multi-process-nocs-noprobe.perfscript --binary=%S/Inputs/multi-process-nocs-noprobe.perfbin --output=%t --profile-summary-cold-count=0 --multi-process-profile --pid=149754
+; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-FIRST-ONLY
+
+CHECK-FIRST-ONLY-NOT: loop2
+
+CHECK-FIRST-ONLY: loop1:1302:0
+CHECK-FIRST-ONLY-NEXT: 0: 0
+CHECK-FIRST-ONLY-NEXT: 1: 31
+CHECK-FIRST-ONLY-NEXT: 2: 0
+
+CHECK-FIRST-ONLY-NOT: loop2
+
+; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/multi-process-nocs-noprobe.perfscript --binary=%S/Inputs/multi-process-nocs-noprobe.perfbin --output=%t --profile-summary-cold-count=0 --multi-process-profile --pid=149755
+; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-SECOND-ONLY
+
+CHECK-SECOND-ONLY-NOT: loop1
+
+CHECK-SECOND-ONLY:loop2:655:0
+CHECK-SECOND-ONLY-NEXT: 0: 0
+CHECK-SECOND-ONLY-NEXT: 1: 16
+CHECK-SECOND-ONLY-NEXT: 2: 0
+
+CHECK-SECOND-ONLY-NOT: loop1
+
+; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/multi-process-nocs-noprobe.perfscript --binary=%S/Inputs/multi-process-nocs-noprobe.perfbin --output=%t --profile-summary-cold-count=0 --multi-process-profile --pid=149754,149755
+; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-BOTH
+
+CHECK-BOTH: loop1:1302:0
+CHECK-BOTH-NEXT: 0: 0
+CHECK-BOTH-NEXT: 1: 31
+CHECK-BOTH-NEXT: 2: 0
+CHECK-BOTH-NEXT:loop2:655:0
+CHECK-BOTH-NEXT: 0: 0
+CHECK-BOTH-NEXT: 1: 16
+CHECK-BOTH-NEXT: 2: 0
+
+; original code can be found in multi-process-nocs-noprobe.test
diff --git a/llvm/test/tools/llvm-profgen/multi-process-cs-probe.test b/llvm/test/tools/llvm-profgen/multi-process-cs-probe.test
new file mode 100644
index 0000000000000..adc28040349ff
--- /dev/null
+++ b/llvm/test/tools/llvm-profgen/multi-process-cs-probe.test
@@ -0,0 +1,46 @@
+; Check that we can handle profiles with multiple processes, using pseudo probes
+; and a context-senstive profile
+
+; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/multi-process-cs-probe.perfscript --binary=%S/Inputs/multi-process-cs-probe.perfbin --output=%t --profile-summary-cold-count=0 --multi-process-profile
+; RUN: FileCheck %s --input-file %t
+
+CHECK: loop1:93:0
+CHECK-NEXT: 1: 0
+CHECK-NEXT: 2: 31
+CHECK-NEXT: 3: 31
+CHECK-NEXT: 4: 31
+CHECK-NEXT: 5: 0
+CHECK-NEXT: !CFGChecksum: 88680961901
+CHECK-NEXT:loop2:47:0
+CHECK-NEXT: 1: 0
+CHECK-NEXT: 2: 16
+CHECK-NEXT: 3: 16
+CHECK-NEXT: 4: 15
+CHECK-NEXT: 5: 0
+CHECK-NEXT: !CFGChecksum: 88680961901
+
+; original code:
+; clang -g -fpseudo-probe-for-profiling -fno-omit-frame-pointer test.c -o multi-process-cs-probe.perfbin
+#include <stdint.h>
+
+// Loop for a while so we see samples in the function (compiled at -O0)
+void loop1() {
+ for (uint64_t i = 0; i < 10000000000; i++) {}
+}
+
+// Slightly modified to make sure there isn't any kind of linker merging
+void loop2() {
+ for (uint64_t i = 1; i < 10000000001; i++) {}
+}
+
+int main(int argc, char *argv[]) {
+ // Use CLI argument to choose which loop to run, so we can distinguish which
+ // process samples were collected from
+
+ if (argc >= 2 && argv[1][0] == '1') {
+ loop1();
+ }
+ if (argc >= 2 && argv[1][0] == '2') {
+ loop2();
+ }
+}
diff --git a/llvm/test/tools/llvm-profgen/multi-process-nocs-noprobe.test b/llvm/test/tools/llvm-profgen/multi-process-nocs-noprobe.test
new file mode 100644
index 0000000000000..3886f116798cb
--- /dev/null
+++ b/llvm/test/tools/llvm-profgen/multi-process-nocs-noprobe.test
@@ -0,0 +1,40 @@
+; Check that we can handle profiles with multiple processes, without
+; pseudo probes or a context-senstive profile
+
+; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/multi-process-nocs-noprobe.perfscript --binary=%S/Inputs/multi-process-nocs-noprobe.perfbin --output=%t --profile-summary-cold-count=0 --multi-process-profile
+; RUN: FileCheck %s --input-file %t
+
+CHECK: loop1:1302:0
+CHECK-NEXT: 0: 0
+CHECK-NEXT: 1: 31
+CHECK-NEXT: 2: 0
+CHECK-NEXT:loop2:655:0
+CHECK-NEXT: 0: 0
+CHECK-NEXT: 1: 16
+CHECK-NEXT: 2: 0
+
+; original code:
+; clang -g test.c -o multi-process.perfbin
+#include <stdint.h>
+
+// Loop for a while so we see samples in the function (compiled at -O0)
+void loop1() {
+ for (uint64_t i = 0; i < 10000000000; i++) {}
+}
+
+// Slightly modified to make sure there isn't any kind of linker merging
+void loop2() {
+ for (uint64_t i = 1; i < 10000000001; i++) {}
+}
+
+int main(int argc, char *argv[]) {
+ // Use CLI argument to choose which loop to run, so we can distinguish which
+ // process samples were collected from
+
+ if (argc >= 2 && argv[1][0] == '1') {
+ loop1();
+ }
+ if (argc >= 2 && argv[1][0] == '2') {
+ loop2();
+ }
+}
diff --git a/llvm/test/tools/llvm-profgen/multi-process-warning.test b/llvm/test/tools/llvm-profgen/multi-process-warning.test
new file mode 100644
index 0000000000000..26858d583c48b
--- /dev/null
+++ b/llvm/test/tools/llvm-profgen/multi-process-warning.test
@@ -0,0 +1,10 @@
+; Check that we get a warning when processing a multi-process profile without
+; the relevant flag
+RUN: llvm-profgen --format=text --perfscript=%S/Inputs/multi-process-warning.perfscript --binary=%S/Inputs/multi-process-nocs-noprobe.perfbin --output=%t --profile-summary-cold-count=0 2>&1 | FileCheck %s
+CHECK: warning: Binary previously loaded in process ID 149754 at 0x5ffeb3637000 was reloaded in process ID 149755 at 0x5caa46c41000. If profiling multiple processes running concurrently, use --multi-process-profile to prevent loss of samples.
+
+; Make sure we don't get the warning if using the multi process flag
+; Just test that there's no warning, other tests will check the validity of the
+; output
+RUN: llvm-profgen --format=text --perfscript=%S/Inputs/multi-process-nocs-noprobe.perfscript --binary=%S/Inputs/multi-process-nocs-noprobe.perfbin --output=%t --profile-summary-cold-count=0 --multi-process-profile 2>&1 | FileCheck %s --check-prefix=CHECK-NOWARN
+CHECK-NOWARN-NOT: warning: Binary previously loaded
diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index 183b248a72320..1ea3f89f8c078 100644
--- a/llvm/tools/llvm-profgen/PerfReader.cpp
+++ b/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -60,6 +60,13 @@ static cl::opt<int> CSProfMaxUnsymbolizedCtxDepth(
"means no depth limit."),
cl::cat(ProfGenCategory));
+static cl::opt<bool> MultiProcessProfile(
+ "multi-process-profile",
+ cl::desc("Handle multiple instances of the binary loaded in different "
+ "processes as if they came from one process, effectively summing "
+ "their samples. perf script input must include the PID field."),
+ cl::cat(ProfGenCategory));
+
namespace sampleprof {
void VirtualUnwinder::unwindCall(UnwindState &State) {
@@ -345,7 +352,7 @@ bool VirtualUnwinder::unwind(const PerfSample *Sample, uint64_t Repeat) {
std::unique_ptr<PerfReaderBase>
PerfReaderBase::create(ProfiledBinary *Binary, PerfInputFile &PerfInput,
- std::optional<int32_t> PIDFilter) {
+ SmallSet<int32_t, 16> &PIDFilter) {
std::unique_ptr<PerfReaderBase> PerfReader;
if (PerfInput.Format == PerfFormat::UnsymbolizedProfile) {
@@ -378,7 +385,7 @@ PerfReaderBase::create(ProfiledBinary *Binary, PerfInputFile &PerfInput,
}
Error PerfReaderBase::parseDataAccessPerfTraces(
- StringRef DataAccessPerfTraceFile, std::optional<int32_t> PIDFilter) {
+ StringRef DataAccessPerfTraceFile, SmallSet<int32_t, 16> &PIDFilter) {
// A perf_record_sample line is like
// . 1282514022939813 0x87b0 [0x60]: PERF_RECORD_SAMPLE(IP, 0x4002):
// 3446532/3446532: 0x2608a2 period: 233 addr: 0x3b3fb0
@@ -423,7 +430,7 @@ Error PerfReaderBase::parseDataAccessPerfTraces(
"Failed to parse PID from perf trace line: " + Line,
inconvertibleErrorCode());
- if (PIDFilter.has_value() && *PIDFilter != PID) {
+ if (!PIDFilter.empty() && !PIDFilter.contains(PID)) {
continue;
}
@@ -440,8 +447,10 @@ Error PerfReaderBase::parseDataAccessPerfTraces(
if (DataSymbol.starts_with("_ZTV")) {
uint64_t IP = 0;
Fields[2].getAsInteger(16, IP);
- Counter.recordDataAccessCount(Binary->canonicalizeVirtualAddress(IP),
- DataSymbol, 1);
+ // If no PID is in the profile's samples, we treat everything as PID 0
+ int32_t LookupPID = MultiProcessProfile ? PID : 0;
+ Counter.recordDataAccessCount(
+ Binary->canonicalizeVirtualAddress(LookupPID, IP), DataSymbol, 1);
}
}
}
@@ -451,7 +460,7 @@ Error PerfReaderBase::parseDataAccessPerfTraces(
PerfInputFile
PerfScriptReader::convertPerfDataToTrace(ProfiledBinary *Binary, bool SkipPID,
PerfInputFile &File,
- std::optional<int32_t> PIDFilter) {
+ SmallSet<int32_t, 16> &PIDFilter) {
StringRef PerfData = File.InputFile;
// Run perf script to retrieve PIDs matching binary we're interested in.
auto PerfExecutable = sys::Process::FindInEnvPath("PATH", "perf");
@@ -484,7 +493,7 @@ PerfScriptReader::convertPerfDataToTrace(ProfiledBinary *Binary, bool SkipPID,
if (isMMapEvent(TraceIt.getCurrentLine()) &&
extractMMapEventForBinary(Binary, TraceIt.getCurrentLine(), MMap)) {
auto It = PIDSet.emplace(MMap.PID);
- if (It.second && (!PIDFilter || MMap.PID == *PIDFilter)) {
+ if (It.second && (PIDFilter.empty() || PIDFilter.contains(MMap.PID))) {
if (!PIDs.empty()) {
PIDs.append(",");
}
@@ -505,7 +514,11 @@ PerfScriptReader::convertPerfDataToTrace(ProfiledBinary *Binary, bool SkipPID,
ScriptSampleArgs.push_back("script");
ScriptSampleArgs.push_back("--show-mmap-events");
ScriptSampleArgs.push_back("-F");
- ScriptSampleArgs.push_back("ip,brstack");
+ if (MultiProcessProfile) {
+ ScriptSampleArgs.push_back("pid,ip,brstack");
+ } else {
+ ScriptSampleArgs.push_back("ip,brstack");
+ }
ScriptSampleArgs.push_back("-i");
ScriptSampleArgs.push_back(PerfData);
if (!PIDs.empty()) {
@@ -541,22 +554,40 @@ void PerfScriptReader::updateBinaryAddress(const MMapEvent &Event) {
return;
// Drop the event if process does not match pid filter
- if (PIDFilter && Event.PID != *PIDFilter)
+ if (!PIDFilter.empty() && !PIDFilter.contains(Event.PID))
return;
+ // If no PID is in the profile's samples, we treat everything as PID 0
+ int32_t LookupPID = MultiProcessProfile ? Event.PID : 0;
+
// Drop the event if its image is loaded at the same address
- if (Event.Address == Binary->getBaseAddress()) {
+ if (Event.Address == Binary->getPIDBaseAddress(LookupPID)) {
Binary->setIsLoadedByMMap(true);
return;
}
+ std::optional<int32_t> LastSeenPID = Binary->getLastSeenPID();
+ if (!MultiProcessProfile && LastSeenPID.has_value() &&
+ LastSeenPID.value() != Event.PID) {
+ WithColor::warning() << "Binary previously loaded in process ID "
+ << LastSeenPID.value() << " at "
+ << format("0x%" PRIx64,
+ Binary->getPIDBaseAddress(LookupPID))
+ << " was reloaded in process ID " << Event.PID
+ << " at " << format("0x%" PRIx64, Event.Address)
+ << ". If profiling multiple processes running "
+ << "concurrently, use --multi-process-profile to "
+ << "prevent loss of samples.\n";
+ }
+ Binary->setLastSeenPID(Event.PID);
+
if (IsKernel || Event.Offset == Binary->getTextSegmentOffset()) {
// A binary image could be unloaded and then reloaded at different
// place, so update binary load address.
// Only update for the first executable segment and assume all other
// segments are loaded at consecutive memory addresses, which is the case on
// X64.
- Binary->setBaseAddress(Event.Address);
+ Binary->setPIDBaseAddress(LookupPID, Event.Address);
Binary->setIsLoadedByMMap(true);
} else {
// Verify segments are loaded consecutively.
@@ -567,7 +598,7 @@ void PerfScriptReader::updateBinaryAddress(const MMapEvent &Event) {
auto I = std::distance(Offsets.begin(), It);
const auto &PreferredAddrs = Binary->getPreferredTextSegmentAddresses();
if (PreferredAddrs[I] - Binary->getPreferredBaseAddress() !=
- Event.Address - Binary->getBaseAddress())
+ Event.Address - Binary->getPIDBaseAddress(LookupPID))
exitWithError("Executable segments not loaded consecutively");
} else {
if (It == Offsets.begin())
@@ -577,7 +608,8 @@ void PerfScriptReader::updateBinaryAddress(const MMapEvent &Event) {
// via multiple mmap calls with consecutive memory addresses.
--It;
assert(*It < Event.Offset);
- if (Event.Offset - *It != Event.Address - Binary->getBaseAddress())
+ if (Event.Offset - *It !=
+ Event.Address - Binary->getPIDBaseAddress(LookupPID))
exitWithError("Segment not loaded by consecutive mmaps");
}
}
@@ -654,14 +686,15 @@ void HybridPerfReader::unwindSamples() {
"frame to match.");
}
-bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
+bool PerfScriptReader::extractLBRStack(std::optional<int32_t> PIDIfKnown,
+ TraceStream &TraceIt,
SmallVectorImpl<LBREntry> &LBRStack) {
// The raw format of LBR stack is like:
// 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
// ... 0x4005c8/0x4005dc/P/-/-/0
// It's in FIFO order and separated by whitespace.
SmallVector<StringRef, 32> Records;
- TraceIt.getCurrentLine().rtrim().split(Records, " ", -1, false);
+ TraceIt.getCurrentLine().trim().split(Records, " ", -1, false);
auto WarnInvalidLBR = [](TraceStream &TraceIt) {
WithColor::warning() << "Invalid address in LBR record at line "
<< TraceIt.getLineNumber() << ": "
@@ -670,14 +703,37 @@ bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
// Skip the leading instruction pointer.
size_t Index = 0;
+ // Default to PID 0 if not provided
+ int32_t PID = 0;
uint64_t LeadingAddr;
+
+ if (Records.size() < (MultiProcessProfile ? 2 : 1)) {
+ WarnInvalidLBR(TraceIt);
+ TraceIt.advance();
+ return false;
+ }
+
+ if (MultiProcessProfile) {
+ // If we read the PID elsewhere, use that instead of trying to read it here
+ if (PIDIfKnown.has_value()) {
+ PID = PIDIfKnown.value();
+ } else {
+ if (Records[Index].getAsInteger(10, PID)) {
+ WarnInvalidLBR(TraceIt);
+ TraceIt.advance();
+ return false;
+ }
+ Index++;
+ }
+ }
+
if (!Records.empty() && !Records[0].contains('/')) {
- if (Records[0].getAsInteger(16, LeadingAddr)) {
+ if (Records[Index].getAsInteger(16, LeadingAddr)) {
WarnInvalidLBR(TraceIt);
TraceIt.advance();
return false;
}
- Index = 1;
+ Index++;
}
// Now extract LBR samples - note that we do not reverse the
@@ -701,8 +757,8 @@ bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
}
// Canonicalize to use preferred load address as base address.
- Src = Binary->canonicalizeVirtualAddress(Src);
- Dst = Binary->canonicalizeVirtualAddress(Dst);
+ Src = Binary->canonicalizeVirtualAddress(PID, Src);
+ Dst = Binary->canonicalizeVirtualAddress(PID, Dst);
bool SrcIsInternal = Binary->addressIsCode(Src);
bool DstIsInternal = Binary->addressIsCode(Dst);
if (!SrcIsInternal)
@@ -719,7 +775,7 @@ bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
return !LBRStack.empty();
}
-bool PerfScriptReader::extractCallstack(TraceStream &TraceIt,
+bool PerfScriptReader::extractCallstack(int32_t PID, TraceStream &TraceIt,
SmallVectorImpl<uint64_t> &CallStack) {
// The raw format of call stack is like:
// 4005dc # leaf frame
@@ -739,7 +795,7 @@ bool PerfScriptReader::extractCallstack(TraceStream &TraceIt,
}
TraceIt.advance();
- FrameAddr = Binary->canonicalizeVirtualAddress(FrameAddr);
+ FrameAddr = Binary->canonicalizeVirtualAddress(PID, FrameAddr);
// Currently intermixed frame from different binaries is not supported.
if (!Binary->addressIsCode(FrameAddr)) {
if (CallStack.empty())
@@ -818,8 +874,24 @@ void HybridPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
#ifndef NDEBUG
Sample->Linenum = TraceIt.getLineNumber();
#endif
+ // Default to PID 0
+ int32_t PID = 0;
+ if (MultiProcessProfile) {
+ // Parse the PID at the beginning of the sample
+ StringRef FirstLine = TraceIt.getCurrentLine();
+ if (FirstLine.trim().getAsInteger(10, PID)) {
+ // If there's an error reading the PID, treat this line as not part of the
+ // sample
+ // While this should usually not happen in a real perf script, this allows
+ // us to use comments and empty lines in test cases
+ TraceIt.advance();
+ return;
+ }
+ TraceIt.advance();
+ }
+
// Parsing call stack and populate into PerfSample.CallStack
- if (!extractCallstack(TraceIt, Sample->CallStack)) {
+ if (!extractCallstack(PID, TraceIt, Sample->CallStack)) {
// Skip the next LBR line matched current call stack
if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().starts_with(" 0x"))
TraceIt.advance();
@@ -830,7 +902,7 @@ void HybridPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().starts_with(" 0x")) {
// Parsing LBR stack and populate into PerfSample.LBRStack
- if (extractLBRStack(TraceIt, Sample->LBRStack)) {
+ if (extractLBRStack(PID, TraceIt, Sample->LBRStack)) {
if (IgnoreStackSamples) {
Sample->CallStack.clear();
} else {
@@ -1022,7 +1094,7 @@ void PerfScriptReader::computeCounterFromLBR(const PerfSample *Sample,
void LBRPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
std::shared_ptr<PerfSample> Sample = std::make_shared<PerfSample>();
// Parsing LBR stack and populate into PerfSample.LBRStack
- if (extractLBRStack(TraceIt, Sample->LBRStack)) {
+ if (extractLBRStack(std::nullopt, TraceIt, Sample->LBRStack)) {
warnIfMissingMMap();
// Record LBR only samples by aggregation
AggregatedSamples[Hashable<PerfSample>(Sample)] += Count;
@@ -1065,15 +1137,17 @@ bool PerfScriptReader::extractMMapEventForBinary(ProfiledBinary *Binary,
// Parse a MMap2 line like:
// PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0
// 08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so
+ // It may be prefixed by a PID
constexpr static const char *const MMap2Pattern =
- "PERF_RECORD_MMAP2 (-?[0-9]+)/[0-9]+: "
+ " ?-?[0-9]* ?PERF_RECORD_MMAP2 (-?[0-9]+)/[0-9]+: "
"\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ "
"(0x[a-f0-9]+|0) .*\\]: ([-a-z]+) (.*)";
// Parse a MMap line like
// PERF_RECORD_MMAP -1/0: [0xffffffff81e00000(0x3e8fa000) @ \
// 0xffffffff81e00000]: x [kernel.kallsyms]_text
+ // It may be prefixed by a PID
constexpr static const char *const MMapPattern =
- "PERF_RECORD_MMAP (-?[0-9]+)/[0-9]+: "
+ " ?-?[0-9]* ?PERF_RECORD_MMAP (-?[0-9]+)/[0-9]+: "
"\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ "
"(0x[a-f0-9]+|0)\\]: ([-a-z]+) (.*)";
// Field 0 - whole line
@@ -1152,15 +1226,34 @@ void PerfScriptReader::parseAndAggregateTrace() {
// A LBR sample is like:
// 40062f 0x5c6313f/0x5c63170/P/-/-/0 0x5c630e7/0x5c63130/P/-/-/0 ...
// A heuristic for fast detection by checking whether a
-// leading " 0x" and the '/' exist.
+// leading "0x" and the '/' exist.
bool PerfScriptReader::isLBRSample(StringRef Line) {
- // Skip the leading instruction pointer
+ // Skip the leading instruction pointer, and maybe PID
+
SmallVector<StringRef, 32> Records;
- Line.trim().split(Records, " ", 2, false);
- if (Records.size() < 2)
- return false;
- if (Records[1].starts_with("0x") && Records[1].contains('/'))
+
+ // Trim off the PID (and maybe part/all of the IP)
+ StringRef Trimmed = Line.ltrim().ltrim("0123456789").trim();
+
+ // Split by spaces, handling duplicate whitespace using ltrim
+ auto Temp = Trimmed.split(' ');
+ StringRef FirstPart = Temp.first.ltrim();
+ StringRef Next = Temp.second.ltrim();
+
+ // If we trimmed off the IP, or it's a hybrid profile, there could be a record
+ // at the start
+ // The 0 at the start of the 0x may have been removed
+ if ((FirstPart.starts_with("x") || FirstPart.starts_with("0x")) && FirstPart.contains('/'))
+ return true;
+
+ // Get the next part split by whitespace
+ Temp = Next.split(' ');
+ StringRef SecondPart = Temp.first.ltrim();
+
+ // Otherwise it should be the second entry
+ if (SecondPart.starts_with("0x") && SecondPart.contains('/'))
return true;
+
return false;
}
@@ -1169,15 +1262,24 @@ bool PerfScriptReader::isMMapEvent(StringRef Line) {
if (Line.empty() || Line.size() < 50)
return false;
- if (std::isdigit(Line[0]))
- return false;
+ if (MultiProcessProfile) {
+ // Trim off the first numeric field, which is the PID.
+ // Make sure not to trim other numeric fields.
+ auto Trimmed = Line.ltrim().ltrim("0123456789").ltrim();
+
+ if (Trimmed.empty() || std::isdigit(Trimmed[0]))
+ return false;
+ } else {
+ if (std::isdigit(Line[0]))
+ return false;
+ }
// PERF_RECORD_MMAP2 or PERF_RECORD_MMAP does not appear at the beginning of
// the line for ` perf script --show-mmap-events -i ...`
return Line.contains("PERF_RECORD_MMAP");
}
-// The raw hybird sample is like
+// The raw hybrid sample is like
// e.g.
// 4005dc # call stack leaf
// 400634
diff --git a/llvm/tools/llvm-profgen/PerfReader.h b/llvm/tools/llvm-profgen/PerfReader.h
index 6e233d17f8e62..91eec4405c48c 100644
--- a/llvm/tools/llvm-profgen/PerfReader.h
+++ b/llvm/tools/llvm-profgen/PerfReader.h
@@ -10,6 +10,7 @@
#define LLVM_TOOLS_LLVM_PROFGEN_PERFREADER_H
#include "ErrorHandling.h"
#include "ProfiledBinary.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
@@ -567,23 +568,19 @@ class VirtualUnwinder {
class PerfReaderBase {
public:
PerfReaderBase(ProfiledBinary *B, StringRef PerfTrace)
- : Binary(B), PerfTraceFile(PerfTrace) {
- // Initialize the base address to preferred address.
- Binary->setBaseAddress(Binary->getPreferredBaseAddress());
- };
+ : Binary(B), PerfTraceFile(PerfTrace) {};
virtual ~PerfReaderBase() = default;
static std::unique_ptr<PerfReaderBase>
create(ProfiledBinary *Binary, PerfInputFile &PerfInput,
- std::optional<int32_t> PIDFilter);
+ SmallSet<int32_t, 16> &PIDFilter);
// Entry of the reader to parse multiple perf traces
virtual void parsePerfTraces() = 0;
// Parse the <ip, vtable-data-symbol> from the data access perf trace file,
// and accumulate the data access count for each <ip, data-symbol> pair.
- Error
- parseDataAccessPerfTraces(StringRef DataAccessPerfFile,
- std::optional<int32_t> PIDFilter = std::nullopt);
+ Error parseDataAccessPerfTraces(StringRef DataAccessPerfFile,
+ SmallSet<int32_t, 16> &PIDFilter);
const ContextSampleCounterMap &getSampleCounters() const {
return SampleCounters;
@@ -606,8 +603,8 @@ class PerfReaderBase {
class PerfScriptReader : public PerfReaderBase {
public:
PerfScriptReader(ProfiledBinary *B, StringRef PerfTrace,
- std::optional<int32_t> PID)
- : PerfReaderBase(B, PerfTrace), PIDFilter(PID) {};
+ SmallSet<int32_t, 16> &PIDFilter)
+ : PerfReaderBase(B, PerfTrace), PIDFilter(PIDFilter) {};
// Entry of the reader to parse multiple perf traces
void parsePerfTraces() override;
@@ -622,7 +619,7 @@ class PerfScriptReader : public PerfReaderBase {
// Generate perf script from perf data
static PerfInputFile convertPerfDataToTrace(ProfiledBinary *Binary,
bool SkipPID, PerfInputFile &File,
- std::optional<int32_t> PIDFilter);
+ SmallSet<int32_t, 16> &PIDFilter);
// Extract perf script type by peaking at the input
static PerfContent checkPerfScriptType(StringRef FileName);
@@ -651,10 +648,10 @@ class PerfScriptReader : public PerfReaderBase {
// Warn if range is invalid.
void warnInvalidRange();
// Extract call stack from the perf trace lines
- bool extractCallstack(TraceStream &TraceIt,
+ bool extractCallstack(int32_t PID, TraceStream &TraceIt,
SmallVectorImpl<uint64_t> &CallStack);
// Extract LBR stack from one perf trace line
- bool extractLBRStack(TraceStream &TraceIt,
+ bool extractLBRStack(std::optional<int32_t> PIDIfKnown, TraceStream &TraceIt,
SmallVectorImpl<LBREntry> &LBRStack);
uint64_t parseAggregatedCount(TraceStream &TraceIt);
// Parse one sample from multiple perf lines, override this for different
@@ -675,7 +672,7 @@ class PerfScriptReader : public PerfReaderBase {
// Keep track of all invalid return addresses
std::set<uint64_t> InvalidReturnAddresses;
// PID for the process of interest
- std::optional<int32_t> PIDFilter;
+ SmallSet<int32_t, 16> PIDFilter;
};
/*
@@ -687,8 +684,8 @@ class PerfScriptReader : public PerfReaderBase {
class LBRPerfReader : public PerfScriptReader {
public:
LBRPerfReader(ProfiledBinary *Binary, StringRef PerfTrace,
- std::optional<int32_t> PID)
- : PerfScriptReader(Binary, PerfTrace, PID) {};
+ SmallSet<int32_t, 16> &PIDFilter)
+ : PerfScriptReader(Binary, PerfTrace, PIDFilter) {};
// Parse the LBR only sample.
void parseSample(TraceStream &TraceIt, uint64_t Count) override;
};
@@ -705,8 +702,8 @@ class LBRPerfReader : public PerfScriptReader {
class HybridPerfReader : public PerfScriptReader {
public:
HybridPerfReader(ProfiledBinary *Binary, StringRef PerfTrace,
- std::optional<int32_t> PID)
- : PerfScriptReader(Binary, PerfTrace, PID) {};
+ SmallSet<int32_t, 16> &PIDFilter)
+ : PerfScriptReader(Binary, PerfTrace, PIDFilter) {};
// Parse the hybrid sample including the call and LBR line
void parseSample(TraceStream &TraceIt, uint64_t Count) override;
void generateUnsymbolizedProfile() override;
diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.h b/llvm/tools/llvm-profgen/ProfiledBinary.h
index 5a814b7dbd52d..f42fb4239ef6c 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.h
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.h
@@ -198,7 +198,13 @@ class ProfiledBinary {
// Options used to configure the symbolizer
symbolize::LLVMSymbolizer::Options SymbolizerOpts;
// The runtime base address that the first executable segment is loaded at.
- uint64_t BaseAddress = 0;
+ // The binary may be loaded at different addresses in different processes,
+ // so we use a map to store base address by PID.
+ // If the profile doesn't contain PID info we use PID 0.
+ std::unordered_map<int32_t, uint64_t> BaseAddressByPID;
+ // The last PID we saw the binary loaded into. Used to warn the user if a
+ // binary is loaded in multiple processes without --multi-process-profile.
+ std::optional<int32_t> LastSeenPID = std::nullopt;
// The runtime base address that the first loadabe segment is loaded at.
uint64_t FirstLoadableAddress = 0;
// The preferred load address of each executable segment.
@@ -396,19 +402,31 @@ class ProfiledBinary {
StringRef getPath() const { return Path; }
StringRef getName() const { return llvm::sys::path::filename(Path); }
- uint64_t getBaseAddress() const { return BaseAddress; }
- void setBaseAddress(uint64_t Address) { BaseAddress = Address; }
+ uint64_t getPIDBaseAddress(int32_t PID) const {
+ auto Pos = BaseAddressByPID.find(PID);
+ if (Pos == BaseAddressByPID.end()) {
+ // Use preferred address as the default base address.
+ return getPreferredBaseAddress();
+ }
+
+ return Pos->second;
+ }
+ void setPIDBaseAddress(int32_t PID, uint64_t Address) {
+ BaseAddressByPID[PID] = Address;
+ }
bool isCOFF() const { return IsCOFF; }
// Canonicalize to use preferred load address as base address.
- uint64_t canonicalizeVirtualAddress(uint64_t Address) {
- return Address - BaseAddress + getPreferredBaseAddress();
+ uint64_t canonicalizeVirtualAddress(int64_t PID, uint64_t Address) {
+ return Address - getPIDBaseAddress(PID) + getPreferredBaseAddress();
}
// Return the preferred load address for the first executable segment.
uint64_t getPreferredBaseAddress() const {
return PreferredTextSegmentAddresses[0];
}
+ std::optional<int32_t> getLastSeenPID() { return LastSeenPID; }
+ void setLastSeenPID(std::optional<int32_t> PID) { LastSeenPID = PID; }
// Return the preferred load address for the first loadable segment.
uint64_t getFirstLoadableAddress() const { return FirstLoadableAddress; }
// Return the file offset for the first executable segment.
diff --git a/llvm/tools/llvm-profgen/llvm-profgen.cpp b/llvm/tools/llvm-profgen/llvm-profgen.cpp
index 8d2ed2850e38a..1165338783b63 100644
--- a/llvm/tools/llvm-profgen/llvm-profgen.cpp
+++ b/llvm/tools/llvm-profgen/llvm-profgen.cpp
@@ -62,10 +62,10 @@ static cl::opt<std::string>
cl::desc("Path of profiled executable binary."),
cl::cat(ProfGenCategory));
-static cl::opt<uint32_t>
- ProcessId("pid", cl::value_desc("process Id"), cl::init(0),
- cl::desc("Process Id for the profiled executable binary."),
- cl::cat(ProfGenCategory));
+static cl::list<int32_t> ProcessIds(
+ "pid", cl::CommaSeparated, cl::value_desc("process ID 1,process ID 2,..."),
+ cl::desc("Comma-separated process IDs for the profiled executable binary."),
+ cl::cat(ProfGenCategory));
static cl::opt<std::string> DebugBinPath(
"debug-binary", cl::value_desc("debug-binary"),
@@ -177,9 +177,12 @@ int main(int argc, const char *argv[]) {
Generator->generateProfile();
Generator->write();
} else {
- std::optional<uint32_t> PIDFilter;
- if (ProcessId.getNumOccurrences())
- PIDFilter = ProcessId;
+ SmallSet<int32_t, 16> PIDFilter;
+
+ for (auto &PID : ProcessIds) {
+ PIDFilter.insert(PID);
+ }
+
PerfInputFile PerfFile = getPerfInputFile();
std::unique_ptr<PerfReaderBase> Reader =
PerfReaderBase::create(Binary.get(), PerfFile, PIDFilter);
>From 9e5043a551f63744342d31b3b0004c69aca3e7fb Mon Sep 17 00:00:00 2001
From: Heath Mitchell <heath.mitchell at sony.com>
Date: Mon, 24 Nov 2025 17:01:56 +0000
Subject: [PATCH 2/2] Fix formatting issues
---
llvm/tools/llvm-profgen/PerfReader.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index 1ea3f89f8c078..a652008b857c1 100644
--- a/llvm/tools/llvm-profgen/PerfReader.cpp
+++ b/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -1234,7 +1234,7 @@ bool PerfScriptReader::isLBRSample(StringRef Line) {
// Trim off the PID (and maybe part/all of the IP)
StringRef Trimmed = Line.ltrim().ltrim("0123456789").trim();
-
+
// Split by spaces, handling duplicate whitespace using ltrim
auto Temp = Trimmed.split(' ');
StringRef FirstPart = Temp.first.ltrim();
@@ -1243,7 +1243,8 @@ bool PerfScriptReader::isLBRSample(StringRef Line) {
// If we trimmed off the IP, or it's a hybrid profile, there could be a record
// at the start
// The 0 at the start of the 0x may have been removed
- if ((FirstPart.starts_with("x") || FirstPart.starts_with("0x")) && FirstPart.contains('/'))
+ if ((FirstPart.starts_with("x") || FirstPart.starts_with("0x")) &&
+ FirstPart.contains('/'))
return true;
// Get the next part split by whitespace
More information about the llvm-commits
mailing list