From 34ce8c6fcb660fb57efae6c39ce7f087fdefaca0 Mon Sep 17 00:00:00 2001 From: diego Date: Wed, 10 Jun 2026 15:11:55 -0300 Subject: [PATCH] chore(blazesym): bump to v0.2.4, drop pure-Go kallsyms fallback blazesym v0.2.4 (987d36c) no longer reads /proc/kcore for the KASLR offset unless a vmlinux DWARF resolver is present, so kernel symbolization works under lockdown=integrity without CAP_SYS_RAWIO. The pure-Go /proc/kallsyms fallback added in #25 (kallsyms.go + disk cache + boot-id EPERM marker + sticky ladder + the PERFAGENT_FORCE_KERNEL_FALLBACK env) is now redundant. Verified on a lockdown=integrity host (kcore 0400, no cap_sys_rawio, no /boot/vmlinux DWARF): blazesym resolves kernel frames with eperm=0, fallback_engaged=0, all frames named. SymbolizeKernel now calls blazesym directly and, on any error, preserves raw kernel addresses (rawKernelAddrFrames) so kernel context still survives into the pprof. Drops the now-meaningless KernelFallbackEngaged counter from stats + the /metrics endpoint; keeps KernelLockdownEPERM / KernelOtherErr for observability. - remove symbolize/{kallsyms,kallsyms_cache}.go and their tests - simplify symbolize/local_kernel.go (no fallback ladder/seam) - drop the forced-fallback integration test - regenerate eBPF bytecode (.o) --- bench/internal/schema/schema.go | 2 +- cpu/cpu_arm64_bpfel.o | Bin 45680 -> 45840 bytes cpu/cpu_x86_bpfel.o | Bin 45680 -> 45840 bytes offcpu/offcpu_arm64_bpfel.o | Bin 37104 -> 37136 bytes offcpu/offcpu_x86_bpfel.o | Bin 37104 -> 37136 bytes perfagent/metrics_endpoint.go | 6 +- perfagent/metrics_endpoint_test.go | 15 +- profile/offcpu_dwarf_arm64_bpfel.o | Bin 48000 -> 48288 bytes profile/offcpu_dwarf_x86_bpfel.o | Bin 55120 -> 55408 bytes profile/perf_arm64_bpfel.o | Bin 36984 -> 37152 bytes profile/perf_dwarf_arm64_bpfel.o | Bin 45880 -> 46152 bytes profile/perf_dwarf_x86_bpfel.o | Bin 53000 -> 53272 bytes profile/perf_x86_bpfel.o | Bin 36984 -> 37152 bytes symbolize/allocs_budget_test.go | 41 ---- symbolize/blazesym_eperm_marker_test.go | 78 ------- symbolize/kallsyms.go | 271 ---------------------- symbolize/kallsyms_bench_test.go | 108 --------- symbolize/kallsyms_cache.go | 293 ------------------------ symbolize/kallsyms_cache_test.go | 147 ------------ symbolize/kallsyms_parse_test.go | 85 ------- symbolize/kallsyms_test.go | 136 ----------- symbolize/local_kernel.go | 188 +++------------ symbolize/local_kernel_fallback_test.go | 142 ------------ symbolize/stats.go | 65 +++--- symbolize/stats_test.go | 88 +++---- test/integration_test.go | 61 ----- 26 files changed, 104 insertions(+), 1622 deletions(-) delete mode 100644 symbolize/blazesym_eperm_marker_test.go delete mode 100644 symbolize/kallsyms.go delete mode 100644 symbolize/kallsyms_bench_test.go delete mode 100644 symbolize/kallsyms_cache.go delete mode 100644 symbolize/kallsyms_cache_test.go delete mode 100644 symbolize/kallsyms_parse_test.go delete mode 100644 symbolize/kallsyms_test.go delete mode 100644 symbolize/local_kernel_fallback_test.go diff --git a/bench/internal/schema/schema.go b/bench/internal/schema/schema.go index a520143..3153a7a 100644 --- a/bench/internal/schema/schema.go +++ b/bench/internal/schema/schema.go @@ -60,7 +60,7 @@ type Run struct { // to the workload it's profiling. Above the budget = regression. // - KernelResolutionRate: fraction of kernel-side samples in // perf-agent #1's own pprof that resolved to a named symbol -// instead of "0x". A drop = blazesym + kallsyms fallback +// instead of "0x". A drop = blazesym kernel symbolization // broke (the original v1.2.0 lockdown class of bug). type SelfMetrics struct { WorkloadPID int `json:"workload_pid"` diff --git a/cpu/cpu_arm64_bpfel.o b/cpu/cpu_arm64_bpfel.o index 4097cc6178f3110f169c68fd772dfe0e81ebc1a8..a60f6534543731e7a522a2076f614ffc77520ec9 100644 GIT binary patch delta 1435 zcmZ9KYe-XJ7{||Z)O5C$Q8=3oq>X{aNZx2yl;y4 zt>S%s;WhWO;Wqo0J)VF8hZ?T3p*5L2`0?wyBDlqJ)*ngI1c3YZ@VyvxXf-#9d4yk$ zS218zqc=ALZs7G?Ej+>g+;i|09h)ui3{PxkFn~d$*7}(28zg*(@DSk{>D(2%*x@N3 z@ywRjnh;rij$_8fNzy7mj7Lod%n;Ski9u5)jNqtgBfP-hrXqNV1%gG>MfSbK4&l(! z55)8l4q{l;LKLl{N7F<672Lf=3$NHWasE7ru$er6vgSTne}|nm3-se>TL|7`n|&>G zvxxl{z!;8-nJntqlYm}9jjg8nJpYzi3+e!)q8cCW+7A<`FEqewmS5PI2ya-VsF26$ z67`B~AvSxm{1yq@2s^Q@!~kU&EHM~si9byE6ybKlR|H&8nx5A~RD|##;RULAD^cPn z1UyihZv8@(ULP-=BK{BIG%;S2g+ry?T{YKIE4hYdcN5p#SlKYg?BIC|V3p5{feYTGWzv6fq8YbC8XiSI ze8NTTZi$=QWma{#p`O)tj3?%#NVjO=T8^tQO5Y})OH<^mx$KqJHR?o8WcinpRT}5q z6;G6=1uXV|*YDtCgLATx(M2;W?q>5mS+kcVR+1oLT+zZktb-zNH6BMrb=bfBTpr_d|Ht&D{?@QpD=S^ArHUN(Ki6os0w-bjB9u@ rbd29|jiuhn1GtEeupQpu`LLim+$EpIarHPB7Uc4~Tj2*f?ymd`vEKjd delta 1424 zcmZ9~Ur1AN6bJB~>;Ks3Az1!lAg(Q2_Q%crWBx$`r;r&ab5dZY=0nx8a_)sQXPg33ciGqu6|-C@k?Bl z+u#{CD#YNxZpD6>#RP*c!JJTCa|L@PYfSfW?pS5tgzAiJkBCj`#)TS@2Viq zAT|(RB)&%6LVTClMm$73x$SWO#&f7Nh+zeb4C3@P()5$~#-6~Spb2;%u}FhM2BGv2 ziR8p-#Mv7BW)NCVljtn*ugJg-Wh57g0{JD9HxYLb_Y<#22iA^l=Q986Pmv})*?5`c ztJqm6j@=~5Ut*yazZ53KO0gb`WpQnt z$y`!V&bwUYrMbSej#pn>Zr<_v>I=oyW>W?8SapDo*5|5~6R;N>(T>O43cPA+ zv!kZH0IFC^`-f17XLs&JLf2B2sIne&Y!9fvvhNhqc2Zk-& tybWa|*SX&|HaLX{aNZx2yl;y4 zt>S%s;WhWO;Wqo0J)VF8hZ?T3p*5L2`0?wyBDlqJ)*ngI1c3YZ@VyvxXf-#9d4yk$ zS218zqc=ALZs7G?Ej+>g+;i|09h)ui3{PxkFn~d$*7}(28zg*(@DSk{>D(2%*x@N3 z@ywRjnh;rij$_8fNzy7mj7Lod%n;Ski9u5)jNqtgBfP-hrXqNV1%gG>MfSbK4&l(! z55)8l4q{l;LKLl{N7F<672Lf=3$NHWasE7ru$er6vgSTne}|nm3-se>TL|7`n|&>G zvxxl{z!;8-nJntqlYm}9jjg8nJpYzi3+e!)q8cCW+7A<`FEqewmS5PI2ya-VsF26$ z67`B~AvSxm{1yq@2s^Q@!~kU&EHM~si9byE6ybKlR|H&8nx5A~RD|##;RULAD^cPn z1UyihZv8@(ULP-=BK{BIG%;S2g+ry?T{YKIE4hYdcN5p#SlKYg?BIC|V3p5{feYTGWzv6fq8YbC8XiSI ze8NTTZi$=QWma{#p`O)tj3?%#NVjO=T8^tQO5Y})OH<^mx$KqJHR?o8WcinpRT}5q z6;G6=1uXV|*YDtCgLATx(M2;W?q>5mS+kcVR+1oLT+zZktb-zNH6BMrb=bfBTpr_d|Ht&D{?@QpD=S^ArHUN(Ki6os0w-bjB9u@ rbd29|jiuhn1GtEeupQpu`LLim+$EpIarHPB7Uc4~Tj2*f?ymd`vEKjd delta 1424 zcmZ9~Ur1AN6bJB~>;Ks3Az1!lAg(Q2_Q%crWBx$`r;r&ab5dZY=0nx8a_)sQXPg33ciGqu6|-C@k?Bl z+u#{CD#YNxZpD6>#RP*c!JJTCa|L@PYfSfW?pS5tgzAiJkBCj`#)TS@2Viq zAT|(RB)&%6LVTClMm$73x$SWO#&f7Nh+zeb4C3@P()5$~#-6~Spb2;%u}FhM2BGv2 ziR8p-#Mv7BW)NCVljtn*ugJg-Wh57g0{JD9HxYLb_Y<#22iA^l=Q986Pmv})*?5`c ztJqm6j@=~5Ut*yazZ53KO0gb`WpQnt z$y`!V&bwUYrMbSej#pn>Zr<_v>I=oyW>W?8SapDo*5|5~6R;N>(T>O43cPA+ zv!kZH0IFC^`-f17XLs&JLf2B2sIne&Y!9fvvhNhqc2Zk-& tybWa|*SX&|HaLbA9eG_T`LRp*2$|q1;9hUIjsr8Q!S4xwWHy(jX0-KMtuM<$baWv^LN}2?P&Y!*MIRT{IwgDFi}(D_|NorBdC!+PFAwvw z1>}@wgKs|#*nla+}Rq#Z_wtw?j5Bv%2OU0Q6k3GfmFwpXY# z3K+2qnj{%Y0hZQk@r+%t-XQVCtW5^xK)`1&YdCJXE4y z-GQgrT4{&#Y_Rehz!at&29~TkqS9X`^N)xhGt-_HfFZoFw*{tAT_Zu7S!?`x@Q@Ae zj{#P=1(Ux_Id}fwJ)w|O+?xFf*`+v$BW?-0Fyoej17v=Lc#?RQ_~)wJdtQ_LBk>aP zcVd+@_iUs>~_-FRs`>Sl-$)=qAY3bJ>Zo@X%1x_YYt8Zi^w2kUXITwd@tq8yJ~#6xm9q_In`&h8o6{0G?C%_{%^ delta 1019 zcmYk)T}V@57zgn495uDhiMq(?hDaM@n{$kdXf^`!HA|zzy$LzwwoJAvN zp8O|iXnriHmCn}Nhn1xkmQtV)52$<4pH7#>pY(Z|`ox^!LxOsZq_rhq7ojYJOud-dT1NdM!`Pfe!%!VMC=La)28AAA z#xg)7sIrV%2+aWa}{DP}JKjeS%}zIs&}e@8Gv9 z`f*l!%KVJ5?NA!d+7;Uvi*H74HmuH1a`duElGdv7b42XhX#!vEWpp3Aa@=aqT1}GV z7dV%5M(!eEcRS-V zEU%BdnC0cn$C-nx$7A!KWVzRguqTb$vDu@O;l!8dO&EJUx;>BCskY4c)23Mdk@-9G zGP*scjVZ2pm(GPZ9;4pEqI~AVF36=ueFckZnVVfGEH$c^Skx^(l_omT)#}HumZ~jH zH5Cnw_1?-x$=gs>|KH-sc{f4t>q-hFNYWr8J`?4OMPC~QPh3c$_oxmOiSfV=qCqTm oOj3^sT`3|uilxp2q##$zOP%${KHLK8v^?rTNGpo|F-w}|AJi7gy8r+H diff --git a/offcpu/offcpu_x86_bpfel.o b/offcpu/offcpu_x86_bpfel.o index c70de073b656cc59d0753ab73cbd91bf7a3aade7..ca7b5a94d8c7d3dbbedb240081ac99d65d2dfd71 100644 GIT binary patch delta 1083 zcmYk%T}V@57zgn4oVm$V^RBGunEs)|X`=Ho6dkK{t^>P&Y!*MIRT{+>$--#e075|NorBdA|+u@-QzO zL5`aiiXqH?6)*6ZFbL4aQrdZi*bCgs;``XdRxJ;RbK7em$~3xjO0kFZ-Pmd@5@nK1 z;GAI)MzGE(X(A+_C%%b8#t2N}PLm7nV1tQ?Q=}b6tGP&Zmn1g;8(nIwHw*9@1Ln6_ ztP?O~5mYfU6bH0btMR-=FpZJ;O4@pD=0d=yAL}@7rRDEq+_D20ezMfSZQNbz68l#= zj^n4Xwe&3i3UEfN#wmj!4w3Q!>a9WjMUq@4en|X?C9Q?4;1)|cM)Ji`(mcaXmkY*m z)^!h_V{^F$F0p~~9{@8Lw`y6e;K4hbj46_d6s9Sy@M>PiMl z6eE7)z(%hQzj_4K7ZQJW;G|a<{7WKnSMFD~5L<|y#D|G%iT%W##C^oktl3BJAN$5L z4JZ69zCdjY7x35l{u@uVoo0(|djUTLLs-(@ue`3{xERLUB`9O5_6~q;top**)$kIJ zbyxC29_#9^fb=QZ^8&_MxVI9Z9+Q!SunE`8&Vu)$%=NfMJSsaOfnm8ay(eYEKj+ZR A_5c6? delta 1020 zcmYk4T}YEr7{}k|Ra5JI!-6*52x((%bI!6YRAx#gRL+Y)ux`X6go4ccm|a+yvuNea z)L*7_xMdK(5tA4^d+k>LZrkgFQ=&3A>`+X7&jxv$OA3A!ok^XKadfjOm+EHtAz6ksMvpBxyC3pCe-1N)z~OE2sO|k?m4@Rx?SG zU*K%^X}ODV(xu6`#e%zxCt?iSD3#Pc2apQ?-nnohT|Zs27>)79YwI9q4NH;YUmL zrly*zhQ@l&(MHMBP+k9@#ldqfg3jBOKK}5Vp$`?!CHVPg)pF;0&++Qrl{o9BJ rvD`65JtB0anCK9eJNJ@;+(2IHyzk$`Eut=vM?DAyilg6TIbi++x@ODI diff --git a/perfagent/metrics_endpoint.go b/perfagent/metrics_endpoint.go index 155087d..73c394e 100644 --- a/perfagent/metrics_endpoint.go +++ b/perfagent/metrics_endpoint.go @@ -35,11 +35,9 @@ func metricsHandlerFor(getCounters func() symbolize.CountersSnapshot) http.Handl writeMetricLine(w, "perf_agent_symbolize_kernel_input_ips_total", "counter", "total kernel IPs handed into SymbolizeKernel", s.KernelInputIPs) writeMetricLine(w, "perf_agent_symbolize_kernel_batch_failures_total", - "counter", "batches where every symbolizer (blazesym + kallsyms) failed", s.KernelBatchFailures) - writeMetricLine(w, "perf_agent_symbolize_kernel_fallback_engaged", - "gauge", "1 when symbolizer switched to pure-Go kallsyms (lockdown-class hosts)", s.KernelFallbackEngaged) + "counter", "batches where blazesym kernel symbolization failed", s.KernelBatchFailures) writeMetricLine(w, "perf_agent_symbolize_kernel_raw_addr_frames_total", - "counter", "kernel IPs that fell to raw-hex synthesis (both symbolizers failed)", s.KernelRawAddrFrames) + "counter", "kernel IPs that fell to raw-hex synthesis (blazesym failed)", s.KernelRawAddrFrames) writeMetricLine(w, "perf_agent_symbolize_kernel_lockdown_eperm_total", "counter", "BLAZE_ERR_PERMISSION_DENIED events from blazesym (canonical lockdown signature)", s.KernelLockdownEPERM) writeMetricLine(w, "perf_agent_symbolize_kernel_other_err_total", diff --git a/perfagent/metrics_endpoint_test.go b/perfagent/metrics_endpoint_test.go index 0641519..721751b 100644 --- a/perfagent/metrics_endpoint_test.go +++ b/perfagent/metrics_endpoint_test.go @@ -17,13 +17,12 @@ import ( // dashboards downstream. func TestMetricsHandler_PrometheusFormat(t *testing.T) { snap := symbolize.CountersSnapshot{ - KernelBatches: 5, - KernelInputIPs: 42, - KernelBatchFailures: 1, - KernelFallbackEngaged: 1, - KernelRawAddrFrames: 3, - KernelLockdownEPERM: 7, - KernelOtherErr: 2, + KernelBatches: 5, + KernelInputIPs: 42, + KernelBatchFailures: 1, + KernelRawAddrFrames: 3, + KernelLockdownEPERM: 7, + KernelOtherErr: 2, } h := metricsHandlerFor(func() symbolize.CountersSnapshot { return snap }) @@ -44,8 +43,6 @@ func TestMetricsHandler_PrometheusFormat(t *testing.T) { "# HELP perf_agent_symbolize_kernel_batches_total", "# TYPE perf_agent_symbolize_kernel_batches_total counter", "perf_agent_symbolize_kernel_batches_total 5", - "# TYPE perf_agent_symbolize_kernel_fallback_engaged gauge", - "perf_agent_symbolize_kernel_fallback_engaged 1", "perf_agent_symbolize_kernel_lockdown_eperm_total 7", "perf_agent_symbolize_kernel_other_err_total 2", "perf_agent_symbolize_kernel_raw_addr_frames_total 3", diff --git a/profile/offcpu_dwarf_arm64_bpfel.o b/profile/offcpu_dwarf_arm64_bpfel.o index af5d633951037e8c24ad78297272f67d07f7d6fe..866f88f8d1188f71fe47397f4535ca63767ce6ac 100644 GIT binary patch delta 18370 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 diff --git a/profile/offcpu_dwarf_x86_bpfel.o b/profile/offcpu_dwarf_x86_bpfel.o index b2e3071a8d02cde140e67e79610cf15fee42d487..8eaf1211d01c2f6879131fc539b01b3cbaabc665 100644 GIT binary patch literal 55408 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 diff --git a/profile/perf_arm64_bpfel.o b/profile/perf_arm64_bpfel.o index 331c86cb9bcbb40c4ea0cd87cc538df28ee59068..7d35547f4b8312172fd8a56297b9e01f29f5869e 100644 GIT binary patch delta 2239 zcmZ9Me{54#6vyv*`vKirx52PKK*g5n*xI$-Wa~t1L1)lx3(JBo2I3~NXhK5AKv5Il zVA;|MTRTofMcFz~HGQ{fIWAO!IW0L(ve|$6_|c0J=$}Q z=XW6c=ACH*dTl@7un7C9Z(gOYz<(nqnyc>&%-mrFAZ zOa1^}cbTmtl5<4TC-9CkGkk_?$}Zq+kr91JX)h_*HG2f!I@^Dftd8vu6dRVdsBt0zYQ<9#PbVY8epl>Aq2T5O*v{{P3ne=g6 zU4iuYfCwgNUfW3vd&@KE;-Ljy00Ans05Zc&V1YIid03_6Q5c&P{2Vmfd%4o~3r%I29;y6>(JJ4JsKUlEDKe1ecUC zW}TodN#nT0>#2QS;!0vhTf=AkBIO&VX$1r$=NPSk;17sZ+xIjh$*>Ry1^-C_IdNbM zf_-jUvMa!fGm``K6X#w777#o|Y?!AN6#O-D-c4Ye(zq$bY9b&gj5zOLftFNoa*u$8 z1nVm@B3Pf%tYEu`@|nUcS;+!hIAVdxzO1?x)~5{P38#igf0=E!RE5GoX}4h zPe(;ZJf1g>r}1{S#`9@x%+!84a1SWM2XRSnH_iUd-YSUUOs^lBu%OQmPhtZ- zW7tnm56;l@J}e0Pp&1*({+u88^QswJk%>pbm4@k1661I|T%|;*&&Y$YfDMs*{{`p{ BSHA!N delta 2042 zcmZ9Me@t6d6vyv*{8$TA$Uyo7of{oGM@wI6!jDW=1`?;hLd2P|e`HaY=~%{KQ!tqY zoV9Zd3MX-ka~osEphIALn_~u{CdNPZ!&IZm5~IOHlewwkj~Qt)qvy7_>B(-=+wbSx zbIv{Yy!+nRxVAi|h1Y4bgCpw~%q9^Z3&D}wWh-rvAUR9v?l&Cp{3Ll);R@c+1K3e&%Akt0 zajdB;GWL5-neef3u=4B*%O@=2U3B|5z^C|%e;SUUy|xsN84b1nfPIiT9c6mR*jiT) znB&T^%ropz=yb|VJ{5hNP`pRj7tsO>F|Z|_%p*0sPV=dv_j5b=BM;i%)XuJ zolGBOdX(vLrY|xbVfyQY&WvyT1ODFVghjM8SsiznQ;ca-Ic{t!eju+rRimvOry2{( z*D{lv=_;n{nf7I4S#x1v2Q%$v+M1o(;SFa0cU8)Mh}lmtJ;n4q)89E#vDXv!#L0ie z>Sm|o4s*T7bb$|FYc6&;eW_?4(|#XbXfC!lGE*DVuNrMzu6ohaSBk&&2XT8}Rn~#- zm%Co*-rsfLMI+p|6>#^!+Zi2cLiFL^15T(w`&+Gn2yN!pg!qsw0Z9)k@;1;`L=h*k?g3Gu}jO zqCU!U<$49NO>r-=na-vwM#)h+j^cB~j_;|u1P6pP*vJr44l&}aMG$%Ei4CeebpBsK z)G8h!&i;cQh~foe^Afc~@i)Y|cPXFZMY+BV;<(~uKgZ%6NSr{PFrpli6)!0+)+vF| zgiaqsdEy2ke_%tyTXkwRQ*%8^H9>vLgA;rlKjY)P@fBTJ>XPtlpiv!S2Uw8=DMbnsEo{yT!79}QA;P;sV!6P@J5)y ISopF30mrsN`v3p{ diff --git a/profile/perf_dwarf_arm64_bpfel.o b/profile/perf_dwarf_arm64_bpfel.o index 3bd5db03d3a72925b914698fcb191ea1631a8ab3..bab0bc6f8a609aced413c8b1492f346daa24f819 100644 GIT binary patch delta 17834 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 diff --git a/profile/perf_dwarf_x86_bpfel.o b/profile/perf_dwarf_x86_bpfel.o index e3b527b73f6feffba62fe71c03c700faf26c373d..82b09e527812982ee2a7db87142420cc89e60a8c 100644 GIT binary patch literal 53272 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 BSHA!N delta 2042 zcmZ9Me@t6d6vyv*{8$TA$Uyo7of{oGM@wI6!jDW=1`?;hLd2P|e`HaY=~%{KQ!tqY zoV9Zd3MX-ka~osEphIALn_~u{CdNPZ!&IZm5~IOHlewwkj~Qt)qvy7_>B(-=+wbSx zbIv{Yy!+nRxVAi|h1Y4bgCpw~%q9^Z3&D}wWh-rvAUR9v?l&Cp{3Ll);R@c+1K3e&%Akt0 zajdB;GWL5-neef3u=4B*%O@=2U3B|5z^C|%e;SUUy|xsN84b1nfPIiT9c6mR*jiT) znB&T^%ropz=yb|VJ{5hNP`pRj7tsO>F|Z|_%p*0sPV=dv_j5b=BM;i%)XuJ zolGBOdX(vLrY|xbVfyQY&WvyT1ODFVghjM8SsiznQ;ca-Ic{t!eju+rRimvOry2{( z*D{lv=_;n{nf7I4S#x1v2Q%$v+M1o(;SFa0cU8)Mh}lmtJ;n4q)89E#vDXv!#L0ie z>Sm|o4s*T7bb$|FYc6&;eW_?4(|#XbXfC!lGE*DVuNrMzu6ohaSBk&&2XT8}Rn~#- zm%Co*-rsfLMI+p|6>#^!+Zi2cLiFL^15T(w`&+Gn2yN!pg!qsw0Z9)k@;1;`L=h*k?g3Gu}jO zqCU!U<$49NO>r-=na-vwM#)h+j^cB~j_;|u1P6pP*vJr44l&}aMG$%Ei4CeebpBsK z)G8h!&i;cQh~foe^Afc~@i)Y|cPXFZMY+BV;<(~uKgZ%6NSr{PFrpli6)!0+)+vF| zgiaqsdEy2ke_%tyTXkwRQ*%8^H9>vLgA;rlKjY)P@fBTJ>XPtlpiv!S2Uw8=DMbnsEo{yT!79}QA;P;sV!6P@J5)y ISopF30mrsN`v3p{ diff --git a/symbolize/allocs_budget_test.go b/symbolize/allocs_budget_test.go index fa65b28..9d1bb56 100644 --- a/symbolize/allocs_budget_test.go +++ b/symbolize/allocs_budget_test.go @@ -4,47 +4,6 @@ import ( "testing" ) -// TestAllocsBudget_ParseKallsymsLine asserts the per-line parser -// remains allocation-free. Catches PRs that accidentally -// reintroduce strings.Fields / strconv.ParseUint or other -// allocating helpers into the hot path (the regression that -// dogfood iter 5 surfaced via mallocgc / sweepone in user-side -// profiles). -// -// Budget: 0 allocs per call. Anything > 0 indicates a regression. -func TestAllocsBudget_ParseKallsymsLine(t *testing.T) { - const budget = 0 - line := []byte("ffffffffc0001234 T kvm_vcpu_ioctl [kvm]") - got := testing.AllocsPerRun(1000, func() { - _, _, _, _, _ = parseKallsymsLine(line) - }) - if got > float64(budget) { - t.Errorf("parseKallsymsLine allocs/op = %.2f, want <= %d", got, budget) - } -} - -// TestAllocsBudget_ResolveKernelIPs caps the per-Resolve-call -// allocation count. The current implementation allocates exactly -// one []Frame slice per call (the return value), so the budget -// is 1. Going above means someone added per-IP allocation — -// likely a [Name string conversion inside the hot loop instead -// of using pre-interned strings. -func TestAllocsBudget_ResolveKernelIPs(t *testing.T) { - const budget = 1 - k := &kallsymsSymbolizer{ - addrs: []uint64{0xffffffff80001000, 0xffffffff80002000, 0xffffffff80003000}, - names: []string{"sym_a", "sym_b", "sym_c"}, - modules: []string{"", "", ""}, - } - ips := []uint64{0xffffffff80001042, 0xffffffff80002042, 0xffffffff80003042} - got := testing.AllocsPerRun(1000, func() { - _ = k.Resolve(ips) - }) - if got > float64(budget) { - t.Errorf("Resolve allocs/op = %.2f, want <= %d", got, budget) - } -} - // TestAllocsBudget_LatencyHistRecord caps the per-Record cost // of the histogram on the hot path. Record runs under a mutex // but should never allocate — the ring buffer is fixed-size. diff --git a/symbolize/blazesym_eperm_marker_test.go b/symbolize/blazesym_eperm_marker_test.go deleted file mode 100644 index fbc2ea5..0000000 --- a/symbolize/blazesym_eperm_marker_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package symbolize - -import ( - "os" - "path/filepath" - "testing" -) - -// withMarkerCacheDir overrides the XDG_CACHE_HOME so the EPERM -// marker lands in a t.TempDir(). Avoids polluting the user's -// real ~/.cache. Also pins the boot_id reader. -func withMarkerCacheDir(t *testing.T, bootID [16]byte) func() { - t.Helper() - dir := t.TempDir() - prevXDG, hadPrev := os.LookupEnv("XDG_CACHE_HOME") - t.Setenv("XDG_CACHE_HOME", dir) - prevBoot := readBootIDFn - readBootIDFn = func() ([16]byte, error) { return bootID, nil } - return func() { - readBootIDFn = prevBoot - if hadPrev { - _ = os.Setenv("XDG_CACHE_HOME", prevXDG) - } else { - _ = os.Unsetenv("XDG_CACHE_HOME") - } - } -} - -// TestBlazesymEPERMMarker_Roundtrip asserts writing the marker -// then checking with the same boot_id reports it as present. -func TestBlazesymEPERMMarker_Roundtrip(t *testing.T) { - cleanup := withMarkerCacheDir(t, [16]byte{1, 2, 3, 4}) - defer cleanup() - - if blazesymEPERMMarkerExists() { - t.Fatalf("marker exists before write") - } - if err := writeBlazesymEPERMMarker(); err != nil { - t.Fatalf("writeBlazesymEPERMMarker: %v", err) - } - if !blazesymEPERMMarkerExists() { - t.Errorf("marker missing after write") - } -} - -// TestBlazesymEPERMMarker_BootIDScoped asserts the marker for one -// boot_id is invisible under a different boot_id. Critical: after -// a reboot, lockdown state may differ — must not assume EPERM -// persists. -func TestBlazesymEPERMMarker_BootIDScoped(t *testing.T) { - cleanup := withMarkerCacheDir(t, [16]byte{1, 2, 3, 4}) - defer cleanup() - if err := writeBlazesymEPERMMarker(); err != nil { - t.Fatalf("write: %v", err) - } - // Switch to a different boot_id; marker should appear absent. - readBootIDFn = func() ([16]byte, error) { return [16]byte{9, 9, 9, 9}, nil } - if blazesymEPERMMarkerExists() { - t.Errorf("marker visible under different boot_id") - } -} - -// TestBlazesymEPERMMarker_PathIncludesBootID is a structural -// check: the marker filename literally encodes the boot_id hex -// so multiple boots' markers can coexist without colliding. -func TestBlazesymEPERMMarker_PathIncludesBootID(t *testing.T) { - cleanup := withMarkerCacheDir(t, [16]byte{0xab, 0xcd}) - defer cleanup() - bootID, _ := readBootIDFn() - path := blazesymEPERMMarkerPath(bootID) - if !filepath.IsAbs(path) { - t.Errorf("marker path not absolute: %s", path) - } - // abcd0000...0000 hex form should appear in the filename. - if filepath.Base(path) == "" { - t.Fatalf("empty basename") - } -} diff --git a/symbolize/kallsyms.go b/symbolize/kallsyms.go deleted file mode 100644 index 9d1ff75..0000000 --- a/symbolize/kallsyms.go +++ /dev/null @@ -1,271 +0,0 @@ -package symbolize - -import ( - "bufio" - "fmt" - "os" - "sort" -) - -// parseKallsymsLine extracts (addr, type, name, module) from one -// /proc/kallsyms line without allocating. Returns ok=false on -// malformed lines. -// -// Format: "<16-hex-addr> [ \t]+[module]" -// Example: "ffffffff80c6a050 T __x64_sys_open" -// Example: "ffffffff8e2b0010 T kvm_init [kvm]" -// -// Name and module are slices into the input buffer; the caller is -// responsible for copying them out before the buffer is reused. -func parseKallsymsLine(line []byte) (addr uint64, typ byte, name, module []byte, ok bool) { - i := 0 - // hex addr — accept any run of hex digits, stop at first non-hex - for i < len(line) { - c := line[i] - v := uint64(0) - switch { - case c >= '0' && c <= '9': - v = uint64(c - '0') - case c >= 'a' && c <= 'f': - v = uint64(c-'a') + 10 - case c >= 'A' && c <= 'F': - v = uint64(c-'A') + 10 - default: - goto endAddr - } - addr = addr<<4 | v - i++ - } -endAddr: - if i == 0 { - return 0, 0, nil, nil, false - } - if i >= len(line) || line[i] != ' ' { - return 0, 0, nil, nil, false - } - i++ - if i >= len(line) { - return 0, 0, nil, nil, false - } - typ = line[i] - i++ - if i >= len(line) || line[i] != ' ' { - return 0, 0, nil, nil, false - } - i++ - nameStart := i - for i < len(line) && line[i] != ' ' && line[i] != '\t' { - i++ - } - name = line[nameStart:i] - // optional module: whitespace then "[modname]" - for i < len(line) && (line[i] == ' ' || line[i] == '\t') { - i++ - } - if i < len(line) { - module = line[i:] - } - return addr, typ, name, module, true -} - -// kallsymsSymbolizer resolves kernel addresses by binary search -// against a snapshot of /proc/kallsyms. Used as the lockdown-safe -// fallback when blazesym fails with permission-denied (e.g., on -// Secure-Boot hosts where blazesym's kernel source tries /proc/kcore -// and gets EACCES, even with vmlinux=""). -// -// Resolution is name + offset only — no inline expansion, no -// file:line. /proc/kallsyms doesn't carry DWARF, and that's -// acceptable: operators get readable flame graphs, which is the -// load-bearing property. -type kallsymsSymbolizer struct { - addrs []uint64 // sorted ascending; parallel to names/modules - names []string - modules []string // "" for vmlinux, "[xfs]" etc. for modules -} - -// newKallsymsSymbolizer materializes the /proc/kallsyms index, -// preferring the on-disk cache (keyed by kernel boot_id) over a -// fresh parse. The fresh-parse path is unchanged from iter 5 -// (allocation-free byte-level scan + module intern); the cache -// path skips it entirely when valid. -// -// Decision order: -// 1. Try loadCachedKallsyms. Hit → return; cache key (boot_id) -// guarantees correctness. -// 2. Miss / stale / corrupt → fall through to parseKallsymsFresh -// and best-effort write the cache for next time. -// 3. Fresh parse fails → return the parse error. -// -// On lockdown hosts where blazesym hits EPERM on /proc/kcore and -// every agent invocation otherwise pays the ~0.8s kallsyms parse, -// the cache drops first-symbol latency to single-digit -// milliseconds. On non-lockdown hosts the kallsymsSymbolizer -// path isn't engaged at all, so this code has zero cost there. -func newKallsymsSymbolizer() (*kallsymsSymbolizer, error) { - if s, err := loadCachedKallsyms(); err == nil { - return s, nil - } - s, err := parseKallsymsFresh() - if err != nil { - return nil, err - } - // Best-effort: cache-write failures don't affect this call. - _ = writeKallsymsCache(s) - return s, nil -} - -// parseKallsymsFresh is the slow path: read and parse -// /proc/kallsyms from scratch. Tuning history kept here because -// this is also the path that PRODUCES the cache contents — the -// disk cache speeds up subsequent invocations, but the first -// invocation on every boot still pays this cost. -// -// Tuned across two bench-self iterations: -// - iter 3: wrap the file in a 256 KiB bufio.Reader so each -// read() pulls many lines at once (the kernel synthesizes -// kallsyms via vsnprintf per read; small reads forced -// repeated trips through that path). -// - iter 5: byte-level allocation-free line parser, replacing -// strings.Fields + strconv.ParseUint. The previous version -// was the top allocation source in perf-agent's user-side -// pprof: 3M kallsyms lines × strings.Fields × ParseUint = -// ~9M+ allocations, triggering noticeable GC pressure -// (sweepone, tryDeferToSpanScan, mallocgc). -// -// One string allocation per KEPT symbol (the name; copies out of -// the read buffer so the buffer can be reused). Modules deduped -// via an intern map: a typical kernel has tens of modules but -// millions of module symbols. -func parseKallsymsFresh() (*kallsymsSymbolizer, error) { - f, err := os.Open("/proc/kallsyms") - if err != nil { - return nil, fmt.Errorf("kallsyms: open: %w", err) - } - defer func() { _ = f.Close() }() - - var ( - addrs []uint64 - names []string - modules []string - sawNZ bool - ) - moduleIntern := make(map[string]string, 64) - br := bufio.NewReaderSize(f, 256*1024) - sc := bufio.NewScanner(br) - // Token buffer: 4 KiB initial (typical line), 1 MiB max for - // pathologically long module-symbol names. - sc.Buffer(make([]byte, 0, 4096), 1<<20) - for sc.Scan() { - line := sc.Bytes() - addr, typ, nameBytes, modBytes, ok := parseKallsymsLine(line) - if !ok { - continue - } - // Type filter: only addressable code symbols. Matches the - // kinds the awk hack in resolve_user_addrs.py keeps for - // userspace; same logic applies to kernel symbols. - switch typ { - case 'T', 't', 'W', 'w', 'i': - default: - continue - } - if addr != 0 { - sawNZ = true - } - module := "" - if len(modBytes) > 0 { - s := string(modBytes) - if interned, ok := moduleIntern[s]; ok { - module = interned - } else { - moduleIntern[s] = s - module = s - } - } - addrs = append(addrs, addr) - names = append(names, string(nameBytes)) // one alloc per kept name - modules = append(modules, module) - } - if err := sc.Err(); err != nil { - return nil, fmt.Errorf("kallsyms: scan: %w", err) - } - if len(addrs) == 0 || !sawNZ { - return nil, ErrKernelSymbolsUnavailable - } - - // /proc/kallsyms is already sorted by address on every supported - // kernel, but the contract isn't formally documented anywhere we - // can lean on — sort defensively. - idx := make([]int, len(addrs)) - for i := range idx { - idx[i] = i - } - sort.Slice(idx, func(i, j int) bool { return addrs[idx[i]] < addrs[idx[j]] }) - sortedAddrs := make([]uint64, len(addrs)) - sortedNames := make([]string, len(addrs)) - sortedModules := make([]string, len(addrs)) - for i, j := range idx { - sortedAddrs[i] = addrs[j] - sortedNames[i] = names[j] - sortedModules[i] = modules[j] - } - return &kallsymsSymbolizer{ - addrs: sortedAddrs, - names: sortedNames, - modules: sortedModules, - }, nil -} - -// maxKallsymsOffset bounds how far past a symbol an IP may land -// before we treat the resolution as bogus. The awk hack uses 64 KiB -// for userspace; kernel functions tend to be smaller, but conservatively -// 64 KiB rejects only obvious mis-attributions (gaps between subsystem -// regions in vmlinux). -const maxKallsymsOffset = 0x10000 - -// Resolve returns one Frame per IP via at-or-below binary search. -// IPs that map to a symbol within maxKallsymsOffset get the symbol -// name + module + offset. IPs outside that window get a raw-hex Name -// and Reason=FailureUnknownAddress, matching the rawKernelAddrFrames -// posture so kernel context still survives into the pprof. -func (k *kallsymsSymbolizer) Resolve(ips []uint64) []Frame { - out := make([]Frame, len(ips)) - for i, ip := range ips { - // sort.Search returns the lowest j with addrs[j] > ip; - // the matching symbol is at j-1 (largest addr <= ip). - j := sort.Search(len(k.addrs), func(j int) bool { return k.addrs[j] > ip }) - if j == 0 { - out[i] = Frame{ - Address: ip, - Name: fmt.Sprintf("0x%x", ip), - Module: "[kernel.kallsyms]", - Reason: FailureUnknownAddress, - } - continue - } - symIdx := j - 1 - symAddr := k.addrs[symIdx] - offset := ip - symAddr - if offset > maxKallsymsOffset { - out[i] = Frame{ - Address: ip, - Name: fmt.Sprintf("0x%x", ip), - Module: "[kernel.kallsyms]", - Reason: FailureUnknownAddress, - } - continue - } - module := k.modules[symIdx] - if module == "" { - module = "[kernel.kallsyms]" - } - out[i] = Frame{ - Address: ip, - Name: k.names[symIdx], - Module: module, - Offset: offset, - } - } - return out -} diff --git a/symbolize/kallsyms_bench_test.go b/symbolize/kallsyms_bench_test.go deleted file mode 100644 index 43c2fab..0000000 --- a/symbolize/kallsyms_bench_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package symbolize - -import ( - "os" - "path/filepath" - "testing" -) - -// BenchmarkParseKallsymsFresh measures end-to-end /proc/kallsyms -// parse cost. This is the cold-cache path that every perf-agent -// invocation pays on lockdown hosts before iter 6's disk cache -// shipped; the benchmark exists to catch regressions in the -// allocation-free parser (iter 5) and the 256 KiB read buffer -// (iter 3). Skips on hosts where kallsyms is unreadable. -// -// Reference numbers on a Ryzen 9 7940HS / Fedora 44 kernel -// 7.0.8-200, ~225k T/t/W/w/i symbols after filtering: -// ~200 ms/op, 1 alloc-per-symbol (the Name string copy). -func BenchmarkParseKallsymsFresh(b *testing.B) { - if !kallsymsReadable() { - b.Skip("requires kptr_restrict=0") - } - b.ReportAllocs() - for b.Loop() { - s, err := parseKallsymsFresh() - if err != nil { - b.Fatalf("parseKallsymsFresh: %v", err) - } - if len(s.addrs) == 0 { - b.Fatalf("empty result") - } - } -} - -// BenchmarkLoadCachedKallsyms measures the warm-cache path — -// the disk format read + decode. Expected to be ~50x faster -// than BenchmarkParseKallsymsFresh; if the gap closes, the -// cache format or read path has regressed. -func BenchmarkLoadCachedKallsyms(b *testing.B) { - if !kallsymsReadable() { - b.Skip("requires kptr_restrict=0") - } - // Prime the cache once outside the timed loop. - tmpDir := b.TempDir() - cachePath := filepath.Join(tmpDir, "kallsyms.cache") - prevPath := cachePathFn - prevBoot := readBootIDFn - cachePathFn = func() string { return cachePath } - readBootIDFn = func() ([16]byte, error) { return [16]byte{1, 2, 3, 4}, nil } - defer func() { - cachePathFn = prevPath - readBootIDFn = prevBoot - }() - fresh, err := parseKallsymsFresh() - if err != nil { - b.Fatalf("parseKallsymsFresh: %v", err) - } - if err := writeKallsymsCache(fresh); err != nil { - b.Fatalf("writeKallsymsCache: %v", err) - } - if _, err := os.Stat(cachePath); err != nil { - b.Fatalf("cache not written: %v", err) - } - - b.ResetTimer() - b.ReportAllocs() - for b.Loop() { - s, err := loadCachedKallsyms() - if err != nil { - b.Fatalf("loadCachedKallsyms: %v", err) - } - if len(s.addrs) == 0 { - b.Fatalf("empty cache read") - } - } -} - -// BenchmarkResolveKernelIPs measures the per-IP resolve cost -// (binary search + frame construction) at production batch -// size. The path is hot — every BPF kernel sample goes through -// it on lockdown hosts. -func BenchmarkResolveKernelIPs(b *testing.B) { - if !kallsymsReadable() { - b.Skip("requires kptr_restrict=0") - } - k, err := parseKallsymsFresh() - if err != nil { - b.Fatalf("parseKallsymsFresh: %v", err) - } - // Probe addresses spread across the kallsyms range so binary - // search doesn't degenerate to one bucket. - probes := make([]uint64, 0, 32) - step := len(k.addrs) / 32 - if step == 0 { - step = 1 - } - for i := 0; i < len(k.addrs) && len(probes) < 32; i += step { - probes = append(probes, k.addrs[i]+1) // +1 to land inside the function - } - b.ResetTimer() - b.ReportAllocs() - for b.Loop() { - frames := k.Resolve(probes) - if len(frames) != len(probes) { - b.Fatalf("frame count mismatch") - } - } -} diff --git a/symbolize/kallsyms_cache.go b/symbolize/kallsyms_cache.go deleted file mode 100644 index 7c227f1..0000000 --- a/symbolize/kallsyms_cache.go +++ /dev/null @@ -1,293 +0,0 @@ -package symbolize - -import ( - "bufio" - "encoding/binary" - "encoding/hex" - "errors" - "fmt" - "io" - "os" - "path/filepath" - "strings" -) - -// Disk cache for the parsed /proc/kallsyms index. Motivated by -// bench-self iter 6: on lockdown hosts (Secure Boot, -// integrity-locked) every perf-agent invocation has to parse the -// 3M-line kallsyms file from scratch because blazesym's kernel -// source hits EPERM on /proc/kcore. The parse itself is fast -// (allocation-free per iter 5) but the kernel synthesizes the -// file via vsnprintf on each read syscall — that's the floor. -// -// Cache key: the kernel boot_id. Kernel symbol addresses change -// only across reboots (KASLR), so the boot_id is the right -// invalidation signal — drops the cache exactly when it would -// be wrong, never falsely keeps a stale copy. -// -// Format (little-endian): -// -// header: magic u32 | version u32 | boot_id [16]byte | n_syms u32 -// per-symbol: addr u64 | name_len u16 | module_len u16 | name | module -// -// One symbol record averages ~50 bytes; a typical kernel's filtered -// kallsyms (~1.5M kept after the T/t/W/w/i filter) lands at ~70 MiB -// on disk. Small enough to ship in ~/.cache without ceremony. -const ( - kallsymsCacheMagic uint32 = 0x4B414C53 // 'KALS' - kallsymsCacheVersion uint32 = 1 - kallsymsHeaderSize = 4 + 4 + 16 + 4 -) - -// errKallsymsCacheStale signals the cache was produced under a -// different kernel boot_id. Caller must reparse /proc/kallsyms. -var errKallsymsCacheStale = errors.New("kallsyms: cache stale (kernel rebooted)") - -// errKallsymsCacheCorrupt signals an unreadable or wrong-magic file. -// Non-fatal: caller falls back to a fresh parse and the next -// successful parse will overwrite the bad file. -var errKallsymsCacheCorrupt = errors.New("kallsyms: cache corrupt") - -// Indirection seams so tests can pin the cache path + boot_id. -// Production: cachePathFn = kallsymsDefaultCachePath, -// readBootIDFn = readBootID. -var ( - cachePathFn = kallsymsDefaultCachePath - readBootIDFn = readBootID -) - -// kallsymsDefaultCachePath honors $XDG_CACHE_HOME and falls back to -// ~/.cache; final fallback is /tmp so the cache works even for -// daemons with no $HOME set. -func kallsymsDefaultCachePath() string { - return filepath.Join(kallsymsCacheDir(), "kallsyms.cache") -} - -// kallsymsCacheDir returns the directory holding all per-boot -// cache artifacts (kallsyms parse + blazesym-EPERM marker). -func kallsymsCacheDir() string { - base := os.Getenv("XDG_CACHE_HOME") - if base == "" { - if home, err := os.UserHomeDir(); err == nil && home != "" { - base = filepath.Join(home, ".cache") - } else { - base = "/tmp" - } - } - return filepath.Join(base, "perf-agent") -} - -// blazesymEPERMMarkerPath returns the path of the -// "blazesym EPERM'd on this boot" marker. The boot_id is encoded -// in the filename so a reboot invalidates the signal — same -// rationale as the kallsyms cache key. -func blazesymEPERMMarkerPath(bootID [16]byte) string { - return filepath.Join(kallsymsCacheDir(), fmt.Sprintf("blazesym-eperm-%x", bootID)) -} - -// blazesymEPERMMarkerExists reports whether blazesym was observed -// to EPERM on this boot. Best-effort: any error is treated as -// "no marker", so a stat failure can't cause false fast-path -// engagement. -func blazesymEPERMMarkerExists() bool { - bootID, err := readBootIDFn() - if err != nil { - return false - } - _, err = os.Stat(blazesymEPERMMarkerPath(bootID)) - return err == nil -} - -// writeBlazesymEPERMMarker creates the marker file. Best-effort: -// failure is logged but never affects the current symbolize call. -func writeBlazesymEPERMMarker() error { - bootID, err := readBootIDFn() - if err != nil { - return err - } - path := blazesymEPERMMarkerPath(bootID) - if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { - return err - } - // Touch the file; content doesn't matter, only existence. - f, err := os.Create(path) - if err != nil { - return err - } - return f.Close() -} - -// readBootID parses /proc/sys/kernel/random/boot_id (a UUID like -// "12345678-1234-1234-1234-1234567890ab") into raw bytes. -func readBootID() ([16]byte, error) { - var out [16]byte - body, err := os.ReadFile("/proc/sys/kernel/random/boot_id") - if err != nil { - return out, err - } - text := strings.TrimSpace(string(body)) - text = strings.ReplaceAll(text, "-", "") - if len(text) != 32 { - return out, fmt.Errorf("kallsyms: unexpected boot_id length %d", len(text)) - } - b, err := hex.DecodeString(text) - if err != nil { - return out, fmt.Errorf("kallsyms: parse boot_id: %w", err) - } - copy(out[:], b) - return out, nil -} - -// loadCachedKallsyms tries to materialize a kallsymsSymbolizer from -// the on-disk cache. Returns: -// - (s, nil) on success -// - (nil, errKallsymsCacheStale) when boot_id changed -// - (nil, errKallsymsCacheCorrupt) on magic / size mismatch -// - (nil, fs error) on missing file / read failure -// -// The caller treats any error as "fall back to a fresh parse". -func loadCachedKallsyms() (*kallsymsSymbolizer, error) { - path := cachePathFn() - data, err := os.ReadFile(path) - if err != nil { - return nil, err - } - if len(data) < kallsymsHeaderSize { - return nil, errKallsymsCacheCorrupt - } - magic := binary.LittleEndian.Uint32(data[0:4]) - version := binary.LittleEndian.Uint32(data[4:8]) - if magic != kallsymsCacheMagic || version != kallsymsCacheVersion { - return nil, errKallsymsCacheCorrupt - } - var cachedBoot [16]byte - copy(cachedBoot[:], data[8:24]) - currentBoot, err := readBootIDFn() - if err != nil { - return nil, fmt.Errorf("kallsyms: read boot_id: %w", err) - } - if cachedBoot != currentBoot { - return nil, errKallsymsCacheStale - } - nSyms := int(binary.LittleEndian.Uint32(data[24:28])) - - addrs := make([]uint64, nSyms) - names := make([]string, nSyms) - modules := make([]string, nSyms) - modIntern := make(map[string]string, 64) - off := kallsymsHeaderSize - for i := range nSyms { - if off+12 > len(data) { - return nil, errKallsymsCacheCorrupt - } - addrs[i] = binary.LittleEndian.Uint64(data[off : off+8]) - nameLen := int(binary.LittleEndian.Uint16(data[off+8 : off+10])) - modLen := int(binary.LittleEndian.Uint16(data[off+10 : off+12])) - off += 12 - if off+nameLen+modLen > len(data) { - return nil, errKallsymsCacheCorrupt - } - names[i] = string(data[off : off+nameLen]) - off += nameLen - if modLen > 0 { - s := string(data[off : off+modLen]) - if interned, ok := modIntern[s]; ok { - modules[i] = interned - } else { - modIntern[s] = s - modules[i] = s - } - off += modLen - } - } - return &kallsymsSymbolizer{ - addrs: addrs, - names: names, - modules: modules, - }, nil -} - -// writeKallsymsCache serializes s to the cache path. Best-effort: -// errors are returned but callers MUST treat write failure as -// non-fatal (the next run just re-parses). Writes go to a temp -// file and rename for atomicity — partial writes never expose a -// corrupt file to concurrent readers. -func writeKallsymsCache(s *kallsymsSymbolizer) error { - bootID, err := readBootIDFn() - if err != nil { - return err - } - path := cachePathFn() - if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { - return err - } - tmp := path + ".tmp" - f, err := os.Create(tmp) - if err != nil { - return err - } - // On any error after this point: close and unlink the temp. - cleanup := func() { - _ = f.Close() - _ = os.Remove(tmp) - } - bw := bufio.NewWriterSize(f, 256*1024) - if err := writeKallsymsCacheTo(bw, s, bootID); err != nil { - cleanup() - return err - } - if err := bw.Flush(); err != nil { - cleanup() - return err - } - if err := f.Close(); err != nil { - _ = os.Remove(tmp) - return err - } - return os.Rename(tmp, path) -} - -// writeKallsymsCacheTo encodes the header + symbol stream onto w. -// Split out from writeKallsymsCache so tests can verify the -// format without touching disk. -func writeKallsymsCacheTo(w io.Writer, s *kallsymsSymbolizer, bootID [16]byte) error { - var hdr [kallsymsHeaderSize]byte - binary.LittleEndian.PutUint32(hdr[0:4], kallsymsCacheMagic) - binary.LittleEndian.PutUint32(hdr[4:8], kallsymsCacheVersion) - copy(hdr[8:24], bootID[:]) - binary.LittleEndian.PutUint32(hdr[24:28], uint32(len(s.addrs))) - if _, err := w.Write(hdr[:]); err != nil { - return err - } - var symHdr [12]byte - for i, addr := range s.addrs { - name := s.names[i] - mod := s.modules[i] - // Name / module lengths must fit in uint16. Truncate - // pathologically long names rather than fail the whole - // write (no real-world kernel symbol exceeds 65 KiB). - nameLen := len(name) - if nameLen > 0xFFFF { - nameLen = 0xFFFF - } - modLen := len(mod) - if modLen > 0xFFFF { - modLen = 0xFFFF - } - binary.LittleEndian.PutUint64(symHdr[0:8], addr) - binary.LittleEndian.PutUint16(symHdr[8:10], uint16(nameLen)) - binary.LittleEndian.PutUint16(symHdr[10:12], uint16(modLen)) - if _, err := w.Write(symHdr[:]); err != nil { - return err - } - if _, err := io.WriteString(w, name[:nameLen]); err != nil { - return err - } - if modLen > 0 { - if _, err := io.WriteString(w, mod[:modLen]); err != nil { - return err - } - } - } - return nil -} diff --git a/symbolize/kallsyms_cache_test.go b/symbolize/kallsyms_cache_test.go deleted file mode 100644 index 561f3b6..0000000 --- a/symbolize/kallsyms_cache_test.go +++ /dev/null @@ -1,147 +0,0 @@ -package symbolize - -import ( - "errors" - "os" - "path/filepath" - "testing" -) - -// withCacheDir overrides the cache path to a test-local temp dir -// and the boot-id reader to a deterministic value. Returns a -// cleanup func. -func withCacheDir(t *testing.T, bootID [16]byte) (string, func()) { - t.Helper() - dir := t.TempDir() - path := filepath.Join(dir, "kallsyms.cache") - prevPath := cachePathFn - prevBoot := readBootIDFn - cachePathFn = func() string { return path } - readBootIDFn = func() ([16]byte, error) { return bootID, nil } - return path, func() { - cachePathFn = prevPath - readBootIDFn = prevBoot - } -} - -// TestKallsymsCache_Roundtrip writes a synthetic symbolizer to the -// cache, then loads it back, and verifies the loaded copy resolves -// addresses to the same symbols. Sanity check for the binary -// format: addr → name/module pairs preserved exactly, including -// the module intern map's identity invariant (same module text -// resolves to the same Go string). -func TestKallsymsCache_Roundtrip(t *testing.T) { - _, cleanup := withCacheDir(t, [16]byte{1, 2, 3, 4}) - defer cleanup() - - want := &kallsymsSymbolizer{ - addrs: []uint64{0xffffffff80001000, 0xffffffff80002000, 0xffffffffc0001000}, - names: []string{"do_sys_openat2", "vfs_open", "kvm_vcpu_ioctl"}, - modules: []string{"", "", "[kvm]"}, - } - - if err := writeKallsymsCache(want); err != nil { - t.Fatalf("writeKallsymsCache: %v", err) - } - - got, err := loadCachedKallsyms() - if err != nil { - t.Fatalf("loadCachedKallsyms: %v", err) - } - if len(got.addrs) != len(want.addrs) { - t.Fatalf("addrs len = %d, want %d", len(got.addrs), len(want.addrs)) - } - for i := range want.addrs { - if got.addrs[i] != want.addrs[i] { - t.Errorf("addrs[%d] = %#x, want %#x", i, got.addrs[i], want.addrs[i]) - } - if got.names[i] != want.names[i] { - t.Errorf("names[%d] = %q, want %q", i, got.names[i], want.names[i]) - } - if got.modules[i] != want.modules[i] { - t.Errorf("modules[%d] = %q, want %q", i, got.modules[i], want.modules[i]) - } - } - - // Resolution still works: pick the kvm address + 0x42, expect - // the kvm module marker on the resolved frame. - frames := got.Resolve([]uint64{0xffffffffc0001042}) - if frames[0].Module != "[kvm]" { - t.Errorf("Module = %q, want [kvm]", frames[0].Module) - } -} - -// TestKallsymsCache_StaleBootID asserts a cache produced under one -// boot_id is rejected when the current boot_id has changed (reboot). -// Critical for correctness: kernel module addresses change on -// reboot and a stale cache would mis-attribute every kernel frame. -func TestKallsymsCache_StaleBootID(t *testing.T) { - path, cleanup := withCacheDir(t, [16]byte{1, 2, 3, 4}) - defer cleanup() - - if err := writeKallsymsCache(&kallsymsSymbolizer{ - addrs: []uint64{0xffffffff80001000}, - names: []string{"sym"}, - modules: []string{""}, - }); err != nil { - t.Fatalf("writeKallsymsCache: %v", err) - } - if _, err := os.Stat(path); err != nil { - t.Fatalf("cache not written: %v", err) - } - - // Simulate reboot: change the boot_id the loader sees. - readBootIDFn = func() ([16]byte, error) { return [16]byte{9, 9, 9, 9}, nil } - - _, err := loadCachedKallsyms() - if !errors.Is(err, errKallsymsCacheStale) { - t.Errorf("loadCachedKallsyms err = %v, want errKallsymsCacheStale", err) - } -} - -// TestKallsymsCache_Missing covers the "cold cache" case — first -// run on a host. Loader returns an error (not panic), caller falls -// back to a fresh parse. -func TestKallsymsCache_Missing(t *testing.T) { - _, cleanup := withCacheDir(t, [16]byte{1, 2, 3, 4}) - defer cleanup() - - if _, err := loadCachedKallsyms(); err == nil { - t.Fatalf("loadCachedKallsyms returned nil err on missing cache") - } -} - -// TestKallsymsCache_CorruptIsNonFatal asserts a corrupt file is -// detected (wrong magic) and reported as an error rather than -// causing a panic or hang. Hosts with partial writes from killed -// agents would otherwise re-hit the issue on every startup. -func TestKallsymsCache_CorruptIsNonFatal(t *testing.T) { - path, cleanup := withCacheDir(t, [16]byte{1, 2, 3, 4}) - defer cleanup() - if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { - t.Fatalf("mkdir: %v", err) - } - if err := os.WriteFile(path, []byte("garbage-not-a-cache"), 0o644); err != nil { - t.Fatalf("write garbage: %v", err) - } - if _, err := loadCachedKallsyms(); err == nil { - t.Errorf("loadCachedKallsyms returned nil on corrupt file") - } -} - -// TestReadBootIDLive: smoke test against the real -// /proc/sys/kernel/random/boot_id. Skips if unreadable (e.g., -// CI sandbox). -func TestReadBootIDLive(t *testing.T) { - if _, err := os.Stat("/proc/sys/kernel/random/boot_id"); err != nil { - t.Skip("no /proc/sys/kernel/random/boot_id on this host") - } - b, err := readBootID() - if err != nil { - t.Fatalf("readBootID: %v", err) - } - var zero [16]byte - if b == zero { - t.Errorf("boot_id read as all-zero — parser likely failed") - } -} diff --git a/symbolize/kallsyms_parse_test.go b/symbolize/kallsyms_parse_test.go deleted file mode 100644 index 03fdc0b..0000000 --- a/symbolize/kallsyms_parse_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package symbolize - -import ( - "bytes" - "testing" -) - -// TestParseKallsymsLine_Plain covers the no-module form. -func TestParseKallsymsLine_Plain(t *testing.T) { - line := []byte("ffffffff80c6a050 T __x64_sys_open") - addr, typ, name, module, ok := parseKallsymsLine(line) - if !ok { - t.Fatalf("parse failed") - } - if addr != 0xffffffff80c6a050 { - t.Errorf("addr = %#x", addr) - } - if typ != 'T' { - t.Errorf("typ = %q", typ) - } - if !bytes.Equal(name, []byte("__x64_sys_open")) { - t.Errorf("name = %q", name) - } - if len(module) != 0 { - t.Errorf("module = %q, want empty", module) - } -} - -// TestParseKallsymsLine_WithModule covers the "[modname]" suffix. -func TestParseKallsymsLine_WithModule(t *testing.T) { - line := []byte("ffffffffc0001000 t kvm_vcpu_ioctl [kvm]") - addr, typ, name, module, ok := parseKallsymsLine(line) - if !ok { - t.Fatalf("parse failed") - } - if addr != 0xffffffffc0001000 { - t.Errorf("addr = %#x", addr) - } - if typ != 't' { - t.Errorf("typ = %q", typ) - } - if !bytes.Equal(name, []byte("kvm_vcpu_ioctl")) { - t.Errorf("name = %q", name) - } - if !bytes.Equal(module, []byte("[kvm]")) { - t.Errorf("module = %q", module) - } -} - -// TestParseKallsymsLine_LongAddrAccepted: kallsyms emits 16-hex-digit -// uppercase too on some kernels; parser must not assume case. -func TestParseKallsymsLine_LongAddrAccepted(t *testing.T) { - line := []byte("FFFFFFFF80C6A050 T some_sym") - addr, _, _, _, ok := parseKallsymsLine(line) - if !ok { - t.Fatalf("parse failed") - } - if addr != 0xFFFFFFFF80C6A050 { - t.Errorf("addr = %#x", addr) - } -} - -// TestParseKallsymsLine_Empty / malformed return ok=false. -func TestParseKallsymsLine_Empty(t *testing.T) { - for _, in := range [][]byte{ - nil, - []byte(""), - []byte("not_a_hex_addr"), - []byte("ffffffff80c6a050"), // missing type + name - []byte("ffffffff80c6a050 T"), // missing name - } { - if _, _, _, _, ok := parseKallsymsLine(in); ok { - t.Errorf("parse %q unexpectedly succeeded", in) - } - } -} - -// BenchmarkParseKallsymsLine measures the per-line cost so future -// kallsyms-parser changes can show their improvement (or regression). -func BenchmarkParseKallsymsLine(b *testing.B) { - line := []byte("ffffffffc0001234 T kvm_vcpu_ioctl [kvm]") - for b.Loop() { - _, _, _, _, _ = parseKallsymsLine(line) - } -} diff --git a/symbolize/kallsyms_test.go b/symbolize/kallsyms_test.go deleted file mode 100644 index aed3ad8..0000000 --- a/symbolize/kallsyms_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package symbolize - -import ( - "strings" - "testing" -) - -// TestKallsymsSymbolizerResolveAtOrBelow asserts the binary search -// semantic: an IP that lands in the middle of a function resolves to -// the function's name with the right offset. -func TestKallsymsSymbolizerResolveAtOrBelow(t *testing.T) { - k := &kallsymsSymbolizer{ - addrs: []uint64{0xffffffff81000000, 0xffffffff81000100, 0xffffffff81000200}, - names: []string{"do_sys_openat2", "vfs_open", "tcp_sendmsg"}, - modules: []string{"", "", ""}, - } - frames := k.Resolve([]uint64{ - 0xffffffff81000000, // exact match → do_sys_openat2 - 0xffffffff8100007f, // 0x7f past do_sys_openat2 start → still in do_sys_openat2 - 0xffffffff81000180, // 0x80 past vfs_open start → vfs_open - }) - want := []struct { - name string - offset uint64 - }{ - {"do_sys_openat2", 0x0}, - {"do_sys_openat2", 0x7f}, - {"vfs_open", 0x80}, - } - for i, w := range want { - if frames[i].Name != w.name { - t.Errorf("frame[%d].Name = %q, want %q", i, frames[i].Name, w.name) - } - if frames[i].Offset != w.offset { - t.Errorf("frame[%d].Offset = %#x, want %#x", i, frames[i].Offset, w.offset) - } - if frames[i].Module != "[kernel.kallsyms]" { - t.Errorf("frame[%d].Module = %q, want [kernel.kallsyms]", i, frames[i].Module) - } - } -} - -// TestKallsymsSymbolizerModuleSymbols asserts that module symbols -// retain their module marker (e.g., "[kvm]") in Frame.Module. -func TestKallsymsSymbolizerModuleSymbols(t *testing.T) { - k := &kallsymsSymbolizer{ - addrs: []uint64{0xffffffffc0001000}, - names: []string{"kvm_vcpu_ioctl"}, - modules: []string{"[kvm]"}, - } - frames := k.Resolve([]uint64{0xffffffffc0001042}) - if frames[0].Name != "kvm_vcpu_ioctl" { - t.Errorf("Name = %q, want kvm_vcpu_ioctl", frames[0].Name) - } - if frames[0].Module != "[kvm]" { - t.Errorf("Module = %q, want [kvm]", frames[0].Module) - } - if frames[0].Offset != 0x42 { - t.Errorf("Offset = %#x, want 0x42", frames[0].Offset) - } -} - -// TestKallsymsSymbolizerRejectsWildOffset asserts that an IP that -// lands "obviously too far" past the closest symbol (> 64 KiB) is -// reported as unknown instead of being mis-attributed to a distant -// function. Matches the awk-hack guard rail. -func TestKallsymsSymbolizerRejectsWildOffset(t *testing.T) { - k := &kallsymsSymbolizer{ - addrs: []uint64{0xffffffff81000000}, - names: []string{"do_sys_openat2"}, - modules: []string{""}, - } - // 0x20000 (128 KiB) past the only known symbol → reject. - frames := k.Resolve([]uint64{0xffffffff81020000}) - if frames[0].Name == "do_sys_openat2" { - t.Errorf("wild offset attributed to do_sys_openat2; want raw-hex name") - } - if frames[0].Reason != FailureUnknownAddress { - t.Errorf("Reason = %v, want FailureUnknownAddress", frames[0].Reason) - } -} - -// TestKallsymsSymbolizerBelowFirstSymbol asserts that an IP below the -// lowest symbol address is reported as unknown rather than wrapping -// into the high end of the table. -func TestKallsymsSymbolizerBelowFirstSymbol(t *testing.T) { - k := &kallsymsSymbolizer{ - addrs: []uint64{0xffffffff81000000}, - names: []string{"do_sys_openat2"}, - modules: []string{""}, - } - frames := k.Resolve([]uint64{0xffffffff80ffffff}) - if frames[0].Reason != FailureUnknownAddress { - t.Errorf("Reason = %v, want FailureUnknownAddress (IP below first symbol)", frames[0].Reason) - } -} - -// TestNewKallsymsSymbolizerLive asserts that on a host with -// kptr_restrict=0 the parser produces a non-empty index and resolves -// real kallsyms addresses to named frames. Skips when the host has -// kallsyms restricted. -// -// We don't assert the exact symbol name returned: /proc/kallsyms -// frequently has aliased symbols at the same address (e.g., kernel -// entry points expose both their canonical name and a __pi_* alias), -// and the sort order across aliases is implementation-defined. The -// load-bearing property is that resolution produces *some* valid -// symbol — not "[unknown]" or a raw hex — for an address that came -// from kallsyms itself. -func TestNewKallsymsSymbolizerLive(t *testing.T) { - if !kallsymsReadable() { - t.Skip("requires kptr_restrict=0") - } - k, err := newKallsymsSymbolizer() - if err != nil { - t.Fatalf("newKallsymsSymbolizer: %v", err) - } - if len(k.addrs) == 0 { - t.Fatalf("empty kallsyms index") - } - - addr, _ := pickKnownKernelSymbol(t) - // Probe addr + 1: any aliased symbol at addr is still a valid - // resolution since +1 lands inside whichever function the kernel - // chose to start there. - frames := k.Resolve([]uint64{addr + 1}) - if frames[0].Reason == FailureUnknownAddress { - t.Fatalf("addr+1 resolved as unknown; want named symbol (got %+v)", frames[0]) - } - if strings.HasPrefix(frames[0].Name, "0x") { - t.Fatalf("got raw-hex name %q; want named symbol", frames[0].Name) - } - if frames[0].Offset != 1 { - t.Errorf("offset = %d, want 1 (probe was addr+1)", frames[0].Offset) - } -} diff --git a/symbolize/local_kernel.go b/symbolize/local_kernel.go index 8801587..32b867b 100644 --- a/symbolize/local_kernel.go +++ b/symbolize/local_kernel.go @@ -18,13 +18,13 @@ static blaze_symbolizer_opts make_kernel_opts(_Bool code_info, _Bool inlined_fns // make_kernel_src returns the kernel source blazesym uses by default: // kallsyms=NULL → /proc/kallsyms, vmlinux=NULL → blazesym auto-scans -// /sys/kernel/btf/vmlinux, /boot/vmlinux-*, /proc/kcore, and friends -// for DWARF. On hosts with kernel lockdown=integrity (Secure Boot) one -// of those open() calls returns EACCES — most commonly /proc/kcore, -// which has CAP_SYS_RAWIO + CAP_DAC_READ_SEARCH requirements — and -// blazesym surfaces it as BLAZE_ERR_PERMISSION_DENIED for the whole -// batch. SymbolizeKernel handles this by falling back to a pure-Go -// /proc/kallsyms symbolizer (kallsyms.go). +// /boot/vmlinux-* and /usr/lib/debug/boot/ for DWARF. Since blazesym +// v0.2.4 (commit 987d36c) the KASLR offset — the only thing that +// needed /proc/kcore — is queried lazily and only when a vmlinux DWARF +// resolver is actually present. On the common lockdown=integrity host +// (no /boot/vmlinux DWARF installed) blazesym resolves kallsyms-only +// without ever touching /proc/kcore, so the BLAZE_ERR_PERMISSION_DENIED +// that the old pure-Go fallback existed for no longer occurs. static blaze_symbolize_src_kernel make_kernel_src(void) { blaze_symbolize_src_kernel src; memset(&src, 0, sizeof(src)); @@ -44,7 +44,6 @@ import "C" import ( "bufio" - "errors" "fmt" "os" "strconv" @@ -55,68 +54,31 @@ import ( "unsafe" ) -// errBlazePermissionDenied signals that blazesym returned -// BLAZE_ERR_PERMISSION_DENIED for the kernel source. The -// SymbolizeKernel fallback ladder converts this into a switch to the -// pure-Go /proc/kallsyms symbolizer for the symbolizer's lifetime. -var errBlazePermissionDenied = errors.New("symbolize: blazesym permission denied (kernel lockdown?)") - -// forceFallbackEnv lets operators (and integration tests) force the -// pure-Go /proc/kallsyms fallback without waiting for blazesym to -// fail first. Set PERFAGENT_FORCE_KERNEL_FALLBACK=1 to skip the CGO -// blazesym path on hosts known to be locked down — avoids one wasted -// CGO call per sample batch — and to exercise the fallback in CI on -// hosts that don't naturally hit EPERM. -const forceFallbackEnv = "PERFAGENT_FORCE_KERNEL_FALLBACK" - -// LocalKernelSymbolizer resolves kernel-mode addresses via blazesym, -// with a transparent pure-Go /proc/kallsyms fallback for hosts where -// blazesym can't read its required kernel images (lockdown=integrity, -// Secure Boot, missing CAP_SYS_RAWIO/CAP_DAC_READ_SEARCH). +// LocalKernelSymbolizer resolves kernel-mode addresses via blazesym. // // blazesym path: gives function name + offset + inline expansion + -// source file:line when the host kernel exposes vmlinux DWARF and -// /proc/kcore. Used by default on permissive hosts. -// -// Pure-Go kallsyms path (see kallsyms.go): gives function name + -// offset + module marker only. No DWARF, no inline frames. Sufficient -// for flame graphs and operator decoding — and works under -// lockdown=integrity, which is the common production case. +// source file:line when the host kernel exposes vmlinux DWARF; falls +// back internally to kallsyms-only resolution (name + offset) when no +// vmlinux DWARF is present — including on lockdown=integrity hosts, +// where it no longer needs /proc/kcore (blazesym >= v0.2.4). // -// The fallback decision is sticky: once we've seen -// BLAZE_ERR_PERMISSION_DENIED on this host, every subsequent batch -// goes straight to the pure-Go path. Re-probing blazesym on every -// batch would waste a CGO call per sample. +// If blazesym fails for any reason, SymbolizeKernel preserves the raw +// kernel addresses (Name="0x") so the kernel side of the stack +// still survives into the pprof. type LocalKernelSymbolizer struct { csym *C.blaze_symbolizer closed atomic.Bool mu sync.Mutex - // callBlazesym is the seam under SymbolizeKernel. In production - // it points to invoke (which routes to cgoSymbolize or to the - // pure-Go kallsymsSymbolizer based on useFallback). Tests swap - // it for a stub so the Go-level fallback ladder can be exercised - // without a real blazesym handle and without a real - // /proc/kallsyms read. - callBlazesym func(ips []uint64, useFallback bool) ([]Frame, error) - - // fallback is set once blazesym reports permission-denied on the - // CGO path, or at construction time when forceFallbackEnv is set. - // Subsequent batches skip the failing CGO path and go straight to - // the pure-Go /proc/kallsyms symbolizer. - fallback atomic.Bool - - // kallsymsOnce + kallsymsCache + kallsymsErr lazily build the - // pure-Go /proc/kallsyms index on the first fallback batch and - // reuse it for the symbolizer's lifetime. Parsing is ~3M lines - // of /proc/kallsyms on a typical x86_64 — one-time cost. - kallsymsOnce sync.Once - kallsymsCache *kallsymsSymbolizer - kallsymsErr error + // symbolize is the seam under SymbolizeKernel; in production it + // points to cgoSymbolize. Tests swap it for a stub so the + // raw-address backstop path can be exercised without a real + // blazesym handle. + symbolize func(ips []uint64) ([]Frame, error) - // stats counts observability events (batch counts, fallback - // engagement, raw-address synthesis). Exposed via Stats() for - // end-of-run logging and future /metrics scrape. + // stats counts observability events (batch counts, raw-address + // synthesis, blazesym error buckets). Exposed via Stats() for + // end-of-run logging and the /metrics scrape. stats Counters } @@ -144,33 +106,14 @@ func NewLocalKernelSymbolizer() (*LocalKernelSymbolizer, error) { return nil, fmt.Errorf("blaze_symbolizer_new_opts returned NULL") } s := &LocalKernelSymbolizer{csym: csym} - s.callBlazesym = s.invoke - if os.Getenv(forceFallbackEnv) == "1" { - // Bump the counter so the end-of-run log reflects that we - // ran in fallback mode, matching the semantic when the - // switch happens naturally via EPERM. Without this, the - // forced-fallback case would log fallback_engaged=0 and - // operators couldn't tell the kallsyms path was used. - s.fallback.Store(true) - s.stats.KernelFallbackEngaged.Add(1) - } else if blazesymEPERMMarkerExists() { - // We've seen blazesym EPERM on this boot already. Skip the - // failing attempt — it would just re-read /proc/kallsyms, - // hit EPERM on /proc/kcore (lockdown), and waste ~110ms of - // CPU per agent invocation. Marker is boot_id-scoped, so a - // reboot reverts to attempting blazesym fresh. - s.fallback.Store(true) - s.stats.KernelFallbackEngaged.Add(1) - } + s.symbolize = s.cgoSymbolize return s, nil } -// SymbolizeKernel resolves kernel addresses to frames. On -// BLAZE_ERR_PERMISSION_DENIED from the CGO path, transparently -// switches to the pure-Go /proc/kallsyms symbolizer for the -// symbolizer's remaining lifetime. If even that fails, returns -// raw-address frames (Name="0x", Reason=FailureMissingSymbols) -// so kernel context survives into the pprof. +// SymbolizeKernel resolves kernel addresses to frames via blazesym. +// If blazesym fails, returns raw-address frames (Name="0x", +// Reason=FailureMissingSymbols) so kernel context survives into the +// pprof. func (s *LocalKernelSymbolizer) SymbolizeKernel(ips []uint64) ([]Frame, error) { if s.closed.Load() { return nil, ErrClosed @@ -198,78 +141,23 @@ func (s *LocalKernelSymbolizer) SymbolizeKernel(ips []uint64) ([]Frame, error) { s.stats.KernelBatchHist.Record(uint64(time.Since(t0).Microseconds())) }() - // Sticky fallback: once we've seen permission-denied on the CGO - // path, this host won't recover within the symbolizer's lifetime. - // Skip blazesym on every subsequent batch. - if s.fallback.Load() { - frames, err := s.callBlazesym(ips, true) - if err != nil { - s.stats.KernelBatchFailures.Add(1) - s.stats.KernelRawAddrFrames.Add(uint64(len(ips))) - return rawKernelAddrFrames(ips), nil - } - return frames, nil - } - - frames, err := s.callBlazesym(ips, false) + frames, err := s.symbolize(ips) if err == nil { return frames, nil } - if errors.Is(err, errBlazePermissionDenied) { - if s.fallback.CompareAndSwap(false, true) { - s.stats.KernelFallbackEngaged.Add(1) - // Persist the fact that blazesym EPERM'd on this - // boot so the next perf-agent invocation can skip - // the failing attempt at construction time. Best - // effort — the in-process sticky bit handles the - // rest of this invocation regardless. - _ = writeBlazesymEPERMMarker() - } - frames, err = s.callBlazesym(ips, true) - if err == nil { - return frames, nil - } - } - // Both paths failed — preserve raw kernel addresses so the - // kernel side of the stack survives into the pprof. + // blazesym failed — preserve raw kernel addresses so the kernel + // side of the stack survives into the pprof. s.stats.KernelBatchFailures.Add(1) s.stats.KernelRawAddrFrames.Add(uint64(len(ips))) return rawKernelAddrFrames(ips), nil } -// invoke is the production callBlazesym. useFallback=false routes to -// the CGO blazesym path (full inline + DWARF); useFallback=true -// routes to the pure-Go /proc/kallsyms symbolizer (name+offset only, -// but lockdown-safe). -func (s *LocalKernelSymbolizer) invoke(ips []uint64, useFallback bool) ([]Frame, error) { - if useFallback { - ks, err := s.getKallsymsFallback() - if err != nil { - return nil, err - } - return ks.Resolve(ips), nil - } - return s.cgoSymbolize(ips) -} - -// getKallsymsFallback returns the lazily-built pure-Go /proc/kallsyms -// symbolizer. Built exactly once per LocalKernelSymbolizer lifetime; -// subsequent calls return the cached instance. -func (s *LocalKernelSymbolizer) getKallsymsFallback() (*kallsymsSymbolizer, error) { - s.kallsymsOnce.Do(func() { - s.kallsymsCache, s.kallsymsErr = newKallsymsSymbolizer() - }) - return s.kallsymsCache, s.kallsymsErr -} - -// cgoSymbolize invokes blazesym's kernel source. Returns -// errBlazePermissionDenied on BLAZE_ERR_PERMISSION_DENIED so the -// fallback ladder can switch to the pure-Go path; other blazesym -// errors propagate as wrapped errors. +// cgoSymbolize invokes blazesym's kernel source. // -// Bumps reason-bucketed counters at the error site (roadmap #4) -// so end-of-run logs / future /metrics scrapes can distinguish a -// lockdown host (high KernelLockdownEPERM) from a buggy blazesym +// Bumps reason-bucketed counters at the error site (roadmap #4) so +// end-of-run logs / the /metrics scrape can distinguish a lockdown +// host that still EPERMs (KernelLockdownEPERM — only the narrow +// vmlinux-DWARF-installed case post-v0.2.4) from a buggy blazesym // (any KernelOtherErr at all). func (s *LocalKernelSymbolizer) cgoSymbolize(ips []uint64) ([]Frame, error) { src := C.make_kernel_src() @@ -279,9 +167,9 @@ func (s *LocalKernelSymbolizer) cgoSymbolize(ips []uint64) ([]Frame, error) { errc := C.blaze_err_last() if errc == C.BLAZE_ERR_PERMISSION_DENIED { s.stats.KernelLockdownEPERM.Add(1) - return nil, errBlazePermissionDenied + } else { + s.stats.KernelOtherErr.Add(1) } - s.stats.KernelOtherErr.Add(1) errStr := C.GoString(C.blaze_err_str(errc)) return nil, fmt.Errorf("blaze_symbolize_kernel_abs_addrs: %s (code %d)", errStr, int(errc)) } diff --git a/symbolize/local_kernel_fallback_test.go b/symbolize/local_kernel_fallback_test.go deleted file mode 100644 index 45d6db0..0000000 --- a/symbolize/local_kernel_fallback_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package symbolize - -import ( - "errors" - "fmt" - "testing" -) - -// stubKernelSymbolizer builds a LocalKernelSymbolizer with no CGO state -// so the Go-level fallback ladder in SymbolizeKernel can be exercised -// without a real blazesym handle. The caller's `call` stands in for the -// CGO blazesym invocation: useFallback reflects which kernel source -// variant the production code would have asked for. -func stubKernelSymbolizer(call func(ips []uint64, useFallback bool) ([]Frame, error)) *LocalKernelSymbolizer { - s := &LocalKernelSymbolizer{} - s.callBlazesym = call - return s -} - -// TestSymbolizeKernel_RetriesOnPermissionDenied covers Bug 1: when -// blazesym's default kernel source returns BLAZE_ERR_PERMISSION_DENIED -// (the lockdown=integrity case where on-disk vmlinux candidates are -// unreadable), SymbolizeKernel must retry once asking for the -// kallsyms-only path (vmlinux=""), and surface those frames. -func TestSymbolizeKernel_RetriesOnPermissionDenied(t *testing.T) { - var defaultCalls, fallbackCalls int - s := stubKernelSymbolizer(func(ips []uint64, useFallback bool) ([]Frame, error) { - if !useFallback { - defaultCalls++ - return nil, errBlazePermissionDenied - } - fallbackCalls++ - out := make([]Frame, len(ips)) - for i, ip := range ips { - out[i] = Frame{Address: ip, Name: "stub_sym", Module: "[kernel.kallsyms]"} - } - return out, nil - }) - - frames, err := s.SymbolizeKernel([]uint64{0xffffffff80001000}) - if err != nil { - t.Fatalf("SymbolizeKernel: %v", err) - } - if len(frames) != 1 || frames[0].Name != "stub_sym" { - t.Fatalf("got %+v, want resolved frame from fallback path", frames) - } - if defaultCalls != 1 { - t.Errorf("default-path calls = %d, want 1", defaultCalls) - } - if fallbackCalls != 1 { - t.Errorf("fallback-path calls = %d, want 1", fallbackCalls) - } -} - -// TestSymbolizeKernel_StickyFallback verifies that once SymbolizeKernel -// has observed permission-denied on the default path and switched to -// the fallback, it skips the default path for the symbolizer's -// remaining lifetime — that path is going to fail with the same error -// on the same host, so re-probing it wastes a CGO call per batch. -func TestSymbolizeKernel_StickyFallback(t *testing.T) { - var defaultCalls, fallbackCalls int - s := stubKernelSymbolizer(func(ips []uint64, useFallback bool) ([]Frame, error) { - if !useFallback { - defaultCalls++ - return nil, errBlazePermissionDenied - } - fallbackCalls++ - return []Frame{{Address: ips[0], Name: "ok"}}, nil - }) - - for i := range 3 { - if _, err := s.SymbolizeKernel([]uint64{uint64(0xffffffff80001000) + uint64(i)}); err != nil { - t.Fatalf("batch %d: %v", i, err) - } - } - if defaultCalls != 1 { - t.Errorf("default-path calls = %d, want 1 (sticky after first EPERM)", defaultCalls) - } - if fallbackCalls != 3 { - t.Errorf("fallback-path calls = %d, want 3", fallbackCalls) - } -} - -// TestSymbolizeKernel_RawAddressesOnTotalFailure covers Bug 2: when -// both the default and fallback blazesym paths fail, SymbolizeKernel -// must synthesize Frames with the raw kernel address rendered as -// "0x" in Name and Reason=FailureMissingSymbols, so the kernel -// portion of the stack survives into the pprof. Previously the whole -// batch was discarded, dropping kernel context entirely. -func TestSymbolizeKernel_RawAddressesOnTotalFailure(t *testing.T) { - s := stubKernelSymbolizer(func(ips []uint64, useFallback bool) ([]Frame, error) { - return nil, errors.New("blazesym total failure") - }) - - ips := []uint64{0xffffffff80001234, 0xffffffff80005678} - frames, err := s.SymbolizeKernel(ips) - if err != nil { - t.Fatalf("SymbolizeKernel: expected nil err with raw fallback, got %v", err) - } - if len(frames) != len(ips) { - t.Fatalf("got %d frames, want %d", len(frames), len(ips)) - } - for i, f := range frames { - wantName := fmt.Sprintf("0x%x", ips[i]) - if f.Name != wantName { - t.Errorf("frame[%d].Name = %q, want %q", i, f.Name, wantName) - } - if f.Module != "[kernel.kallsyms]" { - t.Errorf("frame[%d].Module = %q, want [kernel.kallsyms]", i, f.Module) - } - if f.Reason != FailureMissingSymbols { - t.Errorf("frame[%d].Reason = %v, want FailureMissingSymbols", i, f.Reason) - } - if f.Address != ips[i] { - t.Errorf("frame[%d].Address = %#x, want %#x", i, f.Address, ips[i]) - } - } -} - -// TestSymbolizeKernel_DefaultPathSucceedsNoRetry confirms the happy -// path: when the default blazesym call succeeds, the fallback is not -// consulted and the symbolizer stays out of sticky-fallback mode. -func TestSymbolizeKernel_DefaultPathSucceedsNoRetry(t *testing.T) { - var defaultCalls, fallbackCalls int - s := stubKernelSymbolizer(func(ips []uint64, useFallback bool) ([]Frame, error) { - if useFallback { - fallbackCalls++ - t.Fatalf("fallback path called unexpectedly") - } - defaultCalls++ - return []Frame{{Address: ips[0], Name: "ok"}}, nil - }) - if _, err := s.SymbolizeKernel([]uint64{0xffffffff80001000}); err != nil { - t.Fatalf("SymbolizeKernel: %v", err) - } - if _, err := s.SymbolizeKernel([]uint64{0xffffffff80002000}); err != nil { - t.Fatalf("second batch: %v", err) - } - if defaultCalls != 2 || fallbackCalls != 0 { - t.Errorf("default=%d fallback=%d, want 2 / 0", defaultCalls, fallbackCalls) - } -} diff --git a/symbolize/stats.go b/symbolize/stats.go index 9f3bdd7..17c8448 100644 --- a/symbolize/stats.go +++ b/symbolize/stats.go @@ -12,11 +12,11 @@ import ( // Why: under kernel lockdown=integrity the v1.2.0 M1 symbolizer // silently dropped every kernel frame for the lifetime of the agent // — nothing surfaced the problem to operators. These counters make -// "blazesym broke, fallback engaged" / "frames dropped to raw-hex" -// observable so a self-profile lane or /metrics scrape can alert. +// "blazesym broke" / "frames dropped to raw-hex" observable so a +// self-profile lane or /metrics scrape can alert. type Counters struct { // KernelBatches is the number of SymbolizeKernel calls that - // reached the blazesym/fallback layer (after the empty-input + // reached the blazesym layer (after the empty-input // short-circuit). KernelBatches atomic.Uint64 @@ -24,21 +24,14 @@ type Counters struct { // SymbolizeKernel across all batches. KernelInputIPs atomic.Uint64 - // KernelBatchFailures is the number of batches where every - // symbolizer (blazesym + kallsyms fallback) returned an error, - // forcing the raw-address synthesis path. + // KernelBatchFailures is the number of batches where blazesym + // returned an error, forcing the raw-address synthesis path. KernelBatchFailures atomic.Uint64 - // KernelFallbackEngaged is 1 once the symbolizer has switched - // from the blazesym path to the pure-Go /proc/kallsyms path - // (sticky for the symbolizer's lifetime). Non-zero is the - // canary for lockdown=integrity hosts. - KernelFallbackEngaged atomic.Uint64 - // KernelRawAddrFrames is the cumulative count of kernel IPs // that fell to the raw-address synthesis path (Frame.Name = - // "0x"). High values mean blazesym + kallsyms both failed - // — likely a configuration problem on the host. + // "0x"). High values mean blazesym failed — likely a + // configuration problem on the host. KernelRawAddrFrames atomic.Uint64 // Reason-bucketed counters for batch-level blazesym failures. @@ -47,8 +40,10 @@ type Counters struct { // throwing some other error" without re-instrumenting. // // KernelLockdownEPERM bumps each time blazesym returns - // BLAZE_ERR_PERMISSION_DENIED — high values + matching - // KernelFallbackEngaged=1 is the canonical lockdown signature. + // BLAZE_ERR_PERMISSION_DENIED. Post-v0.2.4 this should only fire + // on the narrow lockdown + /boot/vmlinux-DWARF-installed host + // class; a steady stream of these means kernel frames are + // dropping to raw addresses and the host needs attention. KernelLockdownEPERM atomic.Uint64 // KernelOtherErr bumps when blazesym returns a non-EPERM @@ -71,37 +66,35 @@ type Counters struct { // individual fields atomically, not the struct as a whole — fine for // observability reads). type CountersSnapshot struct { - KernelBatches uint64 - KernelInputIPs uint64 - KernelBatchFailures uint64 - KernelFallbackEngaged uint64 - KernelRawAddrFrames uint64 - KernelLockdownEPERM uint64 - KernelOtherErr uint64 - KernelBatchHist LatencyHistSnapshot + KernelBatches uint64 + KernelInputIPs uint64 + KernelBatchFailures uint64 + KernelRawAddrFrames uint64 + KernelLockdownEPERM uint64 + KernelOtherErr uint64 + KernelBatchHist LatencyHistSnapshot } // Snapshot returns the current counter values as a plain struct. func (c *Counters) Snapshot() CountersSnapshot { return CountersSnapshot{ - KernelBatches: c.KernelBatches.Load(), - KernelInputIPs: c.KernelInputIPs.Load(), - KernelBatchFailures: c.KernelBatchFailures.Load(), - KernelFallbackEngaged: c.KernelFallbackEngaged.Load(), - KernelRawAddrFrames: c.KernelRawAddrFrames.Load(), - KernelLockdownEPERM: c.KernelLockdownEPERM.Load(), - KernelOtherErr: c.KernelOtherErr.Load(), - KernelBatchHist: c.KernelBatchHist.Snapshot(), + KernelBatches: c.KernelBatches.Load(), + KernelInputIPs: c.KernelInputIPs.Load(), + KernelBatchFailures: c.KernelBatchFailures.Load(), + KernelRawAddrFrames: c.KernelRawAddrFrames.Load(), + KernelLockdownEPERM: c.KernelLockdownEPERM.Load(), + KernelOtherErr: c.KernelOtherErr.Load(), + KernelBatchHist: c.KernelBatchHist.Snapshot(), } } // String formats the snapshot as a one-line log message — emitted at -// agent shutdown so operators see fallback engagement and frame -// drops without having to add a metrics scrape. +// agent shutdown so operators see frame drops without having to add a +// metrics scrape. func (s CountersSnapshot) String() string { return fmt.Sprintf( - "symbolize: batches=%d input_ips=%d batch_failures=%d fallback_engaged=%d raw_addr_frames=%d eperm=%d other_err=%d batch_p50_us=%d batch_p99_us=%d batch_max_us=%d", - s.KernelBatches, s.KernelInputIPs, s.KernelBatchFailures, s.KernelFallbackEngaged, s.KernelRawAddrFrames, + "symbolize: batches=%d input_ips=%d batch_failures=%d raw_addr_frames=%d eperm=%d other_err=%d batch_p50_us=%d batch_p99_us=%d batch_max_us=%d", + s.KernelBatches, s.KernelInputIPs, s.KernelBatchFailures, s.KernelRawAddrFrames, s.KernelLockdownEPERM, s.KernelOtherErr, s.KernelBatchHist.P50Us, s.KernelBatchHist.P99Us, s.KernelBatchHist.MaxUs, ) diff --git a/symbolize/stats_test.go b/symbolize/stats_test.go index 1861b4a..862d385 100644 --- a/symbolize/stats_test.go +++ b/symbolize/stats_test.go @@ -6,34 +6,40 @@ import ( "testing" ) +// stubKernelSymbolizer builds a LocalKernelSymbolizer whose blazesym +// seam is replaced with call, so the raw-address backstop and stats +// bookkeeping can be exercised without a real blazesym handle. +func stubKernelSymbolizer(call func(ips []uint64) ([]Frame, error)) *LocalKernelSymbolizer { + s := &LocalKernelSymbolizer{} + s.symbolize = call + return s +} + // TestCounters_SnapshotZero asserts a freshly-constructed Counters // reports all-zeros. func TestCounters_SnapshotZero(t *testing.T) { var c Counters s := c.Snapshot() if s.KernelBatches != 0 || s.KernelBatchFailures != 0 || - s.KernelFallbackEngaged != 0 || s.KernelRawAddrFrames != 0 || - s.KernelInputIPs != 0 { + s.KernelRawAddrFrames != 0 || s.KernelInputIPs != 0 { t.Errorf("zero snapshot non-zero: %+v", s) } } // TestCounters_StringContainsBumpedFields asserts the human-readable // String() form surfaces every counter that's been bumped — used for -// the end-of-run log line so operators see fallback engagement and -// failure counts without having to add a /metrics scrape. +// the end-of-run log line so operators see failure counts without +// having to add a /metrics scrape. func TestCounters_StringContainsBumpedFields(t *testing.T) { var c Counters c.KernelBatches.Add(5) - c.KernelFallbackEngaged.Add(1) c.KernelRawAddrFrames.Add(42) c.KernelLockdownEPERM.Add(7) c.KernelOtherErr.Add(2) out := c.Snapshot().String() for _, want := range []string{ - "batches=5", "fallback_engaged=1", "raw_addr_frames=42", - "eperm=7", "other_err=2", + "batches=5", "raw_addr_frames=42", "eperm=7", "other_err=2", } { if !strings.Contains(out, want) { t.Errorf("snapshot string missing %q: %s", want, out) @@ -45,7 +51,7 @@ func TestCounters_StringContainsBumpedFields(t *testing.T) { // successful blazesym batch the input-IPs and batches counters move, // nothing else. func TestLocalKernelSymbolizer_StatsHappyPath(t *testing.T) { - s := stubKernelSymbolizer(func(ips []uint64, useFallback bool) ([]Frame, error) { + s := stubKernelSymbolizer(func(ips []uint64) ([]Frame, error) { return []Frame{{Address: ips[0], Name: "ok"}}, nil }) _, _ = s.SymbolizeKernel([]uint64{0xffffffff80001000, 0xffffffff80002000}) @@ -56,66 +62,28 @@ func TestLocalKernelSymbolizer_StatsHappyPath(t *testing.T) { if got.KernelInputIPs != 2 { t.Errorf("KernelInputIPs = %d, want 2", got.KernelInputIPs) } - if got.KernelFallbackEngaged != 0 { - t.Errorf("KernelFallbackEngaged = %d, want 0", got.KernelFallbackEngaged) - } if got.KernelRawAddrFrames != 0 { t.Errorf("KernelRawAddrFrames = %d, want 0", got.KernelRawAddrFrames) } } -// TestLocalKernelSymbolizer_StatsFallbackEngages asserts the -// fallback_engaged counter bumps exactly once when the default path -// first returns permission-denied, and stays at 1 on subsequent -// batches (sticky). -func TestLocalKernelSymbolizer_StatsFallbackEngages(t *testing.T) { - s := stubKernelSymbolizer(func(ips []uint64, useFallback bool) ([]Frame, error) { - if !useFallback { - return nil, errBlazePermissionDenied - } - return []Frame{{Address: ips[0], Name: "ok"}}, nil - }) - for i := range 3 { - _, _ = s.SymbolizeKernel([]uint64{uint64(0xffffffff80001000) + uint64(i)}) - } - got := s.Stats() - if got.KernelFallbackEngaged != 1 { - t.Errorf("KernelFallbackEngaged = %d, want 1 (sticky)", got.KernelFallbackEngaged) - } - if got.KernelBatches != 3 { - t.Errorf("KernelBatches = %d, want 3", got.KernelBatches) - } -} - -// TestLocalKernelSymbolizer_StatsForcedFallbackBumpsCounter asserts -// that pre-seeding fallback mode (the PERFAGENT_FORCE_KERNEL_FALLBACK=1 -// path) also marks fallback_engaged > 0 — without this, the -// end-of-run log would say fallback_engaged=0 even when the -// symbolizer ran entirely on the kallsyms path, leaving operators -// unable to tell which mode produced their pprof. -func TestLocalKernelSymbolizer_StatsForcedFallbackBumpsCounter(t *testing.T) { - s := stubKernelSymbolizer(func(ips []uint64, useFallback bool) ([]Frame, error) { - return []Frame{{Address: ips[0], Name: "ok"}}, nil - }) - // Simulate constructor-time forced fallback. - s.fallback.Store(true) - s.stats.KernelFallbackEngaged.Add(1) - _, _ = s.SymbolizeKernel([]uint64{0xffffffff80001000}) - got := s.Stats() - if got.KernelFallbackEngaged != 1 { - t.Errorf("KernelFallbackEngaged = %d, want 1 (forced-fallback)", got.KernelFallbackEngaged) - } -} - -// TestLocalKernelSymbolizer_StatsRawAddrFramesOnTotalFailure asserts -// that when both blazesym and the fallback fail, raw_addr_frames -// reflects the number of IPs that fell to the raw-hex path. -func TestLocalKernelSymbolizer_StatsRawAddrFramesOnTotalFailure(t *testing.T) { - s := stubKernelSymbolizer(func(ips []uint64, useFallback bool) ([]Frame, error) { +// TestLocalKernelSymbolizer_StatsRawAddrFramesOnFailure asserts that +// when blazesym fails, the IPs fall to the raw-hex backstop and both +// raw_addr_frames and batch_failures reflect it. +func TestLocalKernelSymbolizer_StatsRawAddrFramesOnFailure(t *testing.T) { + s := stubKernelSymbolizer(func(ips []uint64) ([]Frame, error) { return nil, errors.New("blazesym broken") }) ips := []uint64{0xffffffff80001000, 0xffffffff80002000, 0xffffffff80003000} - _, _ = s.SymbolizeKernel(ips) + frames, _ := s.SymbolizeKernel(ips) + if len(frames) != len(ips) { + t.Fatalf("got %d frames, want %d", len(frames), len(ips)) + } + for i, f := range frames { + if f.Reason != FailureMissingSymbols || f.Address != ips[i] { + t.Errorf("frame %d = %+v, want raw-addr backstop for %#x", i, f, ips[i]) + } + } got := s.Stats() if got.KernelRawAddrFrames != uint64(len(ips)) { t.Errorf("KernelRawAddrFrames = %d, want %d", got.KernelRawAddrFrames, len(ips)) diff --git a/test/integration_test.go b/test/integration_test.go index e74437b..16c764e 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -1913,67 +1913,6 @@ func countDistinctNonSentinelPIDsInPerfData(body []byte) int { return distinct } -// TestKernelStackResolution_ForcedFallback covers the kernel-lockdown -// path: on hosts with lockdown=integrity, blazesym's default kernel -// source fails with BLAZE_ERR_PERMISSION_DENIED and SymbolizeKernel -// retries against the kallsyms-only source (vmlinux=""). This test -// pins the fallback path on every host by setting -// PERFAGENT_FORCE_KERNEL_FALLBACK=1, so a regression in the fallback -// implementation surfaces in normal CI without needing an actual -// locked-down kernel. -// -// Also explicitly verifies that --pid mode (not --all) resolves -// kernel symbols — kernel-mode resolution is a per-symbolizer property -// and should not depend on system-wide capture scope. -func TestKernelStackResolution_ForcedFallback(t *testing.T) { - t.Helper() - requireBPFRunnable(t, getAgentPath(t)) - - if !readKptrRestrictZero() { - t.Skip("requires kptr_restrict=0 (the fallback path resolves via /proc/kallsyms)") - } - - bin := getAgentPath(t) - - cmd, cleanup := spawnIoBoundWorkload(t) - defer cleanup() - - out := filepath.Join(t.TempDir(), "profile.pb.gz") - agent := exec.Command(bin, - "--profile", - "--kernel-stacks", - "--pid", strconv.Itoa(cmd.Process.Pid), - "--duration", "3s", - "--profile-output", out, - ) - // Force the kallsyms-only path so this test always exercises the - // fallback branch, regardless of whether the running kernel has - // lockdown=integrity. - agent.Env = append(os.Environ(), "PERFAGENT_FORCE_KERNEL_FALLBACK=1") - agent.Stdout = os.Stdout - agent.Stderr = os.Stderr - if err := agent.Run(); err != nil { - t.Fatalf("perf-agent run: %v", err) - } - - p := parseProfile(t, out) - got := map[string]bool{} - for _, fn := range p.Function { - got[fn.Name] = true - } - - // The fallback path must still surface kernel symbols. Use the - // same regex as TestKernelStackResolution: common syscall / VFS / - // scheduler / TCP entry points produced by the io_bound workload. - kernelRe := regexp.MustCompile(`^(do_sys_|ksys_|__x64_sys_|vfs_|__schedule|read_|sock_|tcp_)`) - for name := range got { - if kernelRe.MatchString(name) { - return - } - } - t.Fatalf("forced-fallback path produced no kernel symbol; got: %v", sortedKeys(got)) -} - // spawnIoBoundWorkload starts the Go io_bound workload (heavy /dev/zero reads // → frequent syscall/kernel frames) and returns the running command plus a // cleanup func that kills it.