[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&951jPg9lb&#3Aiuf!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