diff --git a/go.mod b/go.mod
index 28c7d76e5..6a94c896b 100644
--- a/go.mod
+++ b/go.mod
@@ -3,14 +3,14 @@ module codeberg.org/thomas-mangin/ze
go 1.26
require (
- charm.land/bubbles/v2 v2.1.0
- charm.land/bubbletea/v2 v2.0.2
- charm.land/lipgloss/v2 v2.0.2
+ charm.land/bubbles/v2 v2.1.1
+ charm.land/bubbletea/v2 v2.0.7
+ charm.land/lipgloss/v2 v2.0.4
charm.land/wish/v2 v2.0.1
github.com/beevik/ntp v1.5.0
github.com/charmbracelet/colorprofile v0.4.3
github.com/charmbracelet/ssh v0.0.0-20250826160808-ebfa259c7309
- github.com/gaissmai/bart v0.26.1
+ github.com/gaissmai/bart v0.28.0
github.com/google/nftables v0.3.0
github.com/insomniacslk/dhcp v0.0.0-20260326115832-991c7910cf36
github.com/miekg/dns v1.1.72
@@ -22,19 +22,19 @@ require (
github.com/vishvananda/netlink v1.3.1
github.com/vishvananda/netns v0.0.5
go.fd.io/govpp v0.13.0
- golang.org/x/crypto v0.53.0
- golang.org/x/net v0.56.0
- golang.org/x/sys v0.46.0
- golang.org/x/term v0.44.0
- golang.org/x/tools v0.45.0
+ golang.org/x/crypto v0.54.0
+ golang.org/x/net v0.57.0
+ golang.org/x/sys v0.47.0
+ golang.org/x/term v0.45.0
+ golang.org/x/tools v0.47.0
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
- google.golang.org/grpc v1.80.0
- google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1
+ google.golang.org/grpc v1.82.0
+ google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2
google.golang.org/protobuf v1.36.11
)
require (
- github.com/cilium/ebpf v0.19.0
+ github.com/cilium/ebpf v0.22.0
github.com/gokrazy/tools v0.0.0-20260406155313-5861e2403dc8
github.com/gokrazy/updater v0.0.0-20250705135802-db129c40879c
github.com/openconfig/gnmi v0.14.1
@@ -49,10 +49,10 @@ require (
github.com/google/renameio/v2 v2.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/spf13/cobra v1.10.1 // indirect
+ github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
golang.org/x/crypto/x509roots/fallback v0.0.0-20250911151450-96dc232fbd79 // indirect
- golang.org/x/oauth2 v0.34.0 // indirect
+ golang.org/x/oauth2 v0.36.0 // indirect
)
require (
@@ -62,8 +62,8 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/keygen v0.5.4 // indirect
- github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
- github.com/charmbracelet/x/ansi v0.11.6 // indirect
+ github.com/charmbracelet/ultraviolet v0.0.0-20260525132238-948f4557a654 // indirect
+ github.com/charmbracelet/x/ansi v0.11.7 // indirect
github.com/charmbracelet/x/conpty v0.1.1 // indirect
github.com/charmbracelet/x/errors v0.0.0-20251110184232-6ab307057ac7 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
@@ -72,37 +72,37 @@ require (
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/creack/pty v1.1.24
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/ftrvxmtrx/fd v0.0.0-20150925145434-c6d800382fff // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/josharian/native v1.1.0 // indirect
- github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
+ github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe // indirect
- github.com/mattn/go-runewidth v0.0.21 // indirect
- github.com/mdlayher/genetlink v1.3.2
- github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42
+ github.com/mattn/go-runewidth v0.0.24 // indirect
+ github.com/mdlayher/genetlink v1.4.0
+ github.com/mdlayher/netlink v1.9.0
github.com/mdlayher/packet v1.1.2
- github.com/mdlayher/socket v0.5.1 // indirect
+ github.com/mdlayher/socket v0.6.0 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
- github.com/prometheus/procfs v0.16.1
+ github.com/prometheus/procfs v0.21.1
github.com/rivo/uniseg v0.4.7 // indirect
- github.com/sirupsen/logrus v1.9.3
+ github.com/sirupsen/logrus v1.9.4
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
- golang.org/x/mod v0.36.0 // indirect
- golang.org/x/sync v0.21.0 // indirect
- golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 // indirect
- golang.org/x/text v0.38.0 // indirect
+ golang.org/x/mod v0.37.0 // indirect
+ golang.org/x/sync v0.22.0 // indirect
+ golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57 // indirect
+ golang.org/x/text v0.40.0 // indirect
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 738967b5d..77baacfcf 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,9 @@
-charm.land/bubbles/v2 v2.1.0 h1:YSnNh5cPYlYjPxRrzs5VEn3vwhtEn3jVGRBT3M7/I0g=
-charm.land/bubbles/v2 v2.1.0/go.mod h1:l97h4hym2hvWBVfmJDtrEHHCtkIKeTEb3TTJ4ZOB3wY=
-charm.land/bubbletea/v2 v2.0.2 h1:4CRtRnuZOdFDTWSff9r8QFt/9+z6Emubz3aDMnf/dx0=
-charm.land/bubbletea/v2 v2.0.2/go.mod h1:3LRff2U4WIYXy7MTxfbAQ+AdfM3D8Xuvz2wbsOD9OHQ=
-charm.land/lipgloss/v2 v2.0.2 h1:xFolbF8JdpNkM2cEPTfXEcW1p6NRzOWTSamRfYEw8cs=
-charm.land/lipgloss/v2 v2.0.2/go.mod h1:KjPle2Qd3YmvP1KL5OMHiHysGcNwq6u83MUjYkFvEkM=
+charm.land/bubbles/v2 v2.1.1 h1:7r55WzBxpo/R3z98hGmY7KKPd3ET6vsf0Fb9sDHOV60=
+charm.land/bubbles/v2 v2.1.1/go.mod h1:GE6M31gaWZVXzGw73OeuTTgy4lX+OtkH0E5ymnNsHxo=
+charm.land/bubbletea/v2 v2.0.7 h1:7qw2tTAVar7m7klOPBYfTB0mniv/RuexsYwMRNxSeL0=
+charm.land/bubbletea/v2 v2.0.7/go.mod h1:DGW2q8gvzHnOpMpZTORs0aySVHCox5C+2Svk0fci1qs=
+charm.land/lipgloss/v2 v2.0.4 h1:lcPeVtcp23SNra7lHy8iYE4UC2aIipVQ47sbGyyxR5Q=
+charm.land/lipgloss/v2 v2.0.4/go.mod h1:0653x8epbZSzdDfO/XPS1a/uYPOBeSsCssOpJOqDzik=
charm.land/log/v2 v2.0.0 h1:SY3Cey7ipx86/MBXQHwsguOT6X1exT94mmJRdzTNs+s=
charm.land/log/v2 v2.0.0/go.mod h1:c3cZSRqm20qUVVAR1WmS/7ab8bgha3C6G7DjPcaVZz0=
charm.land/wish/v2 v2.0.1 h1:xYOsvQG/bYNRoKUBt0AEyMiCv9rksmCOG/fD5WifAzg=
@@ -28,10 +28,10 @@ github.com/charmbracelet/keygen v0.5.4 h1:XQYgf6UEaTGgQSSmiPpIQ78WfseNQp4Pz8N/c1
github.com/charmbracelet/keygen v0.5.4/go.mod h1:t4oBRr41bvK7FaJsAaAQhhkUuHslzFXVjOBwA55CZNM=
github.com/charmbracelet/ssh v0.0.0-20250826160808-ebfa259c7309 h1:dCVbCRRtg9+tsfiTXTp0WupDlHruAXyp+YoxGVofHHc=
github.com/charmbracelet/ssh v0.0.0-20250826160808-ebfa259c7309/go.mod h1:R9cISUs5kAH4Cq/rguNbSwcR+slE5Dfm8FEs//uoIGE=
-github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 h1:eyFRbAmexyt43hVfeyBofiGSEmJ7krjLOYt/9CF5NKA=
-github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8/go.mod h1:SQpCTRNBtzJkwku5ye4S3HEuthAlGy2n9VXZnWkEW98=
-github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
-github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
+github.com/charmbracelet/ultraviolet v0.0.0-20260525132238-948f4557a654 h1:FpSYhY28ucg9ZRr+2wj67FAQ0Ey5yiK0072PmRDJNek=
+github.com/charmbracelet/ultraviolet v0.0.0-20260525132238-948f4557a654/go.mod h1:hFpumms29Smx3LStRfku8vcCTBe1Kq8aCXtHUJa3mjY=
+github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI=
+github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ=
github.com/charmbracelet/x/conpty v0.1.1 h1:s1bUxjoi7EpqiXysVtC+a8RrvPPNcNvAjfi4jxsAuEs=
github.com/charmbracelet/x/conpty v0.1.1/go.mod h1:OmtR77VODEFbiTzGE9G1XiRJAga6011PIm4u5fTNZpk=
github.com/charmbracelet/x/errors v0.0.0-20251110184232-6ab307057ac7 h1:4EG8pCHK5fa8dIxv97VHC8hdkJAz6QNm1WB9BuD/WhY=
@@ -44,8 +44,8 @@ github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
-github.com/cilium/ebpf v0.19.0 h1:Ro/rE64RmFBeA9FGjcTc+KmCeY6jXmryu6FfnzPRIao=
-github.com/cilium/ebpf v0.19.0/go.mod h1:fLCgMo3l8tZmAdM3B2XqdFzXBpwkcSTroaVqN08OWVY=
+github.com/cilium/ebpf v0.22.0 h1:v2ktp0roffpMOj2MMf3idtCQZOsAoC4BJbAJN+ke2bY=
+github.com/cilium/ebpf v0.22.0/go.mod h1:CDzZbe2hC5JjlDC+CY3KFCzlYwN4gbxppYM+Z10bQt4=
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
@@ -53,17 +53,16 @@ github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJ
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 h1:C7t6eeMaEQVy6e8CarIhscYQlNmw5e3G36y7l7Y21Ao=
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/ftrvxmtrx/fd v0.0.0-20150925145434-c6d800382fff h1:zk1wwii7uXmI0znwU+lqg+wFL9G5+vm5I+9rv2let60=
github.com/ftrvxmtrx/fd v0.0.0-20150925145434-c6d800382fff/go.mod h1:yUhRXHewUVJ1k89wHKP68xfzk7kwXUx/DV1nx4EBMbw=
-github.com/gaissmai/bart v0.26.1 h1:+w4rnLGNlA2GDVn382Tfe3jOsK5vOr5n4KmigJ9lbTo=
-github.com/gaissmai/bart v0.26.1/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c=
+github.com/gaissmai/bart v0.28.0 h1:89yZLo8NmyqD0RYgJ3QO9HhqqGGw+oWhf90cZm69Lko=
+github.com/gaissmai/bart v0.28.0/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c=
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
@@ -112,23 +111,23 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
-github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
+github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe h1:ewr1srjRCmcQogPQ/NCx6XCk6LGVmsVCc9Y3vvPZj+Y=
github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
-github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
-github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
-github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
-github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg=
-github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o=
+github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
+github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
+github.com/mdlayher/genetlink v1.4.0 h1:f/Xs7Y2T+GyX9b3dbiUhnLE9InGs5F9RxJ2JwBMl71o=
+github.com/mdlayher/genetlink v1.4.0/go.mod h1:d1hrKr8fwZU2JkcAtQUAzeTrI7nbgQSl+5k1cC0biSA=
+github.com/mdlayher/netlink v1.9.0 h1:G8+GLq2x3v4D4MVIqDdNUhTUC7TKiCy/6MDkmItfKco=
+github.com/mdlayher/netlink v1.9.0/go.mod h1:YBnl5BXsCoRuwBjKKlZ+aYmEoq0r12FDA/3JC+94KDg=
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
-github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
-github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
+github.com/mdlayher/socket v0.6.0 h1:ScZPaAGyO1icQnbFrhPM8mnXyMu9qukC1K4ZoM2IQKU=
+github.com/mdlayher/socket v0.6.0/go.mod h1:q7vozUAnxSqnjHc12Fik5yUKIzfZ8ITCfMkhOtE9z18=
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
@@ -149,36 +148,34 @@ github.com/packetcap/go-pcap v0.0.0-20251215121130-f2cf9f991e7c h1:B5gWB1LB6Oxpo
github.com/packetcap/go-pcap v0.0.0-20251215121130-f2cf9f991e7c/go.mod h1:1jryUz9E2ndKwZBNHzVhLMzS3WHO0fOKydYi9XWWu9w=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
-github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
-github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
+github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
+github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
+github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/sivchari/gomu v0.2.1 h1:w+CwPZXXeYFlLnbcNQ/3VbSClx5yf5pite56V+m9Xuo=
github.com/sivchari/gomu v0.2.1/go.mod h1:kHwGRQvI3lCwm880iN9fwz/gT0oy9dc9SctSnmPZMis=
-github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
-github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
+github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
+github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
@@ -193,86 +190,66 @@ go.fd.io/govpp v0.13.0 h1:MnjH9I5K+X0860CeeuBcMSu3uyUKA6X9AenKzdiGpnA=
go.fd.io/govpp v0.13.0/go.mod h1:MQw6XdULE9qJiqYzIUXPSVyOGWCwVLMhhFRjcf+9hmM=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
-go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
-go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
-go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
-go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
-go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
-go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
-go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
-go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
-go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
-golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
-golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
-golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
-golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
+golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
+golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
golang.org/x/crypto/x509roots/fallback v0.0.0-20250911151450-96dc232fbd79 h1:WZWglxfb13JCTbJyKY1pk0V94spHxJzMAQW29INytRQ=
golang.org/x/crypto/x509roots/fallback v0.0.0-20250911151450-96dc232fbd79/go.mod h1:MEIPiCnxvQEjA4astfaKItNwEVZA5Ki+3+nyGbJ5N18=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
-golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
-golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
-golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
-golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
-golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
-golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
-golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
-golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
-golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
-golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
-golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
-golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
-golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
-golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
+golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
+golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
+golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
+golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
+golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
+golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
+golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
+golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
-golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
-golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
-golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
-golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c h1:6a8FdnNk6bTXBjR4AGKFgUKuo+7GnR3FX5L7CbveeZc=
-golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw=
-golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE=
-golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE=
-golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
-golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
-golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
-golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
-golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
-golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
-golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
-golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
-golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
-golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
-golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
-golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
+golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
+golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57 h1:nwGZBCt+FnXUrGsj5vjzAsEmkcaFvd82BbOjECiFYZc=
+golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57/go.mod h1:3AWMyWHS+caVoiEXpiq6+tzKA40J4vQT3MYr80ZtQpc=
+golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
+golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
+golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
+golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
+golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
+golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
-google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
-google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
-google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1 h1:/WILD1UcXj/ujCxgoL/DvRgt2CP3txG8+FwkUbb9110=
-google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1/go.mod h1:YNKnb2OAApgYn2oYY47Rn7alMr1zWjb2U8Q0aoGWiNc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU=
+google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 h1:rgSNvqscFZ1JgV/4wH5GOsZFSFkR2Eua9As3KIr2LlM=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2/go.mod h1:iMEtFwDlAhjDU9L5mY6U1XLwlIId/G3h+QcBHDIvrJ8=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/charm.land/bubbletea/v2/.golangci.yml b/vendor/charm.land/bubbletea/v2/.golangci.yml
index 929cb0ac9..c90f03161 100644
--- a/vendor/charm.land/bubbletea/v2/.golangci.yml
+++ b/vendor/charm.land/bubbletea/v2/.golangci.yml
@@ -33,6 +33,9 @@ linters:
generated: lax
presets:
- common-false-positives
+ settings:
+ exhaustive:
+ default-signifies-exhaustive: true
issues:
max-issues-per-linter: 0
max-same-issues: 0
diff --git a/vendor/charm.land/bubbletea/v2/README.md b/vendor/charm.land/bubbletea/v2/README.md
index 23cfad00c..4d08d355b 100644
--- a/vendor/charm.land/bubbletea/v2/README.md
+++ b/vendor/charm.land/bubbletea/v2/README.md
@@ -64,7 +64,7 @@ import will be the Bubble Tea library, which we'll call `tea` for short.
```go
package main
-// These imports will be used later on the tutorial. If you save the file
+// These imports will be used later in the tutorial. If you save the file
// now, Go might complain they are unused, but that's fine.
// You may also need to run `go mod tidy` to download bubbletea and its
// dependencies.
@@ -397,6 +397,6 @@ of days past.
Part of [Charm](https://charm.sh).
-
+
Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة
diff --git a/vendor/charm.land/bubbletea/v2/Taskfile.yaml b/vendor/charm.land/bubbletea/v2/Taskfile.yaml
index 35072035d..10e47f306 100644
--- a/vendor/charm.land/bubbletea/v2/Taskfile.yaml
+++ b/vendor/charm.land/bubbletea/v2/Taskfile.yaml
@@ -1,6 +1,6 @@
# https://taskfile.dev
-version: '3'
+version: "3"
tasks:
lint:
@@ -11,4 +11,28 @@ tasks:
test:
desc: Run tests
cmds:
- - go test ./... {{.CLI_ARGS}}
+ - go test -race -count 4 -cpu 1,4 ./... {{.CLI_ARGS}}
+
+ release:
+ desc: Create and push a new tag following semver
+ vars:
+ NEXT:
+ sh: svu next --always || go run github.com/caarlos0/svu/v3@latest next --always
+ prompt: "This will release {{.NEXT}}. Continue?"
+ preconditions:
+ - sh: '[ $(git symbolic-ref --short HEAD) = "main" ]'
+ msg: Not on main branch
+ - sh: "[ $(git status --porcelain=2 | wc -l) = 0 ]"
+ msg: "Git is dirty"
+ - sh: 'gh run list --workflow build.yml --commit $(git rev-parse HEAD) --status success --json conclusion -q ".[0].conclusion" | grep -q success'
+ msg: "Test build for this commit failed or not present"
+ cmds:
+ - task: fetch-tags
+ - git commit --allow-empty -m "{{.NEXT}}"
+ - git tag --annotate --sign -m "{{.NEXT}}" {{.NEXT}} {{.CLI_ARGS}}
+ - echo "Pushing {{.NEXT}}..."
+ - git push origin main --follow-tags
+
+ fetch-tags:
+ cmds:
+ - git fetch --tags
diff --git a/vendor/charm.land/bubbletea/v2/cursed_renderer.go b/vendor/charm.land/bubbletea/v2/cursed_renderer.go
index c95487303..f45fee320 100644
--- a/vendor/charm.land/bubbletea/v2/cursed_renderer.go
+++ b/vendor/charm.land/bubbletea/v2/cursed_renderer.go
@@ -61,7 +61,11 @@ func (s *cursedRenderer) setOptimizations(hardTabs, backspace, mapnl bool) {
s.hardTabs = hardTabs
s.backspace = backspace
s.mapnl = mapnl
- s.scr.SetTabStops(s.width)
+ if s.hardTabs {
+ s.scr.SetTabStops(s.width)
+ } else {
+ s.scr.SetTabStops(-1)
+ }
s.scr.SetBackspace(s.backspace)
s.scr.SetMapNewline(s.mapnl)
s.mu.Unlock()
@@ -131,10 +135,7 @@ func (s *cursedRenderer) start() {
// Both can coexist; terminals ignore what they don't support.
_, _ = s.scr.WriteString(ansi.SetModifyOtherKeys2)
- kittyFlags := ansi.KittyDisambiguateEscapeCodes
- if s.lastView.KeyboardEnhancements.ReportEventTypes {
- kittyFlags |= ansi.KittyReportEventTypes
- }
+ kittyFlags := keyboardEnhancementsFlags(s.lastView.KeyboardEnhancements)
_, _ = s.scr.WriteString(ansi.KittyKeyboard(kittyFlags, 1))
}
@@ -278,6 +279,11 @@ func (s *cursedRenderer) flush(closing bool) error {
}
}
+ // Restore tab stops if we have tab optimizations enabled.
+ if s.starting && s.hardTabs {
+ _, _ = s.scr.WriteString(ansi.SetTabEvery8Columns)
+ }
+
if !s.starting && !closing && s.lastView != nil && viewEquals(s.lastView, &view) && frameArea == s.cellbuf.Bounds() {
// No changes, nothing to do.
return nil
@@ -379,10 +385,7 @@ func (s *cursedRenderer) flush(closing bool) error {
// Enable modifyOtherKeys and Kitty keyboard protocol.
_, _ = s.scr.WriteString(ansi.SetModifyOtherKeys2)
- kittyFlags := ansi.KittyDisambiguateEscapeCodes // always enable basic key disambiguation
- if view.KeyboardEnhancements.ReportEventTypes {
- kittyFlags |= ansi.KittyReportEventTypes
- }
+ kittyFlags := keyboardEnhancementsFlags(view.KeyboardEnhancements)
_, _ = s.scr.WriteString(ansi.KittyKeyboard(kittyFlags, 1))
if !closing {
// Request keyboard enhancements when they change
@@ -593,7 +596,11 @@ func reset(s *cursedRenderer) {
scr.SetColorProfile(s.profile)
scr.SetRelativeCursor(true) // Always start in inline mode
scr.SetFullscreen(false) // Always start in inline mode
- scr.SetTabStops(s.width)
+ if s.hardTabs {
+ scr.SetTabStops(s.width)
+ } else {
+ scr.SetTabStops(-1)
+ }
scr.SetBackspace(s.backspace)
scr.SetMapNewline(s.mapnl)
scr.SetScrollOptim(runtime.GOOS != "windows") // disable scroll optimization on Windows due to bugs in some terminals
@@ -757,8 +764,14 @@ func (s *cursedRenderer) insertAbove(str string) error {
// onMouse implements renderer.
func (s *cursedRenderer) onMouse(m MouseMsg) Cmd {
- if s.lastView != nil && s.lastView.OnMouse != nil {
- return s.lastView.OnMouse(m)
+ var onMouse func(MouseMsg) Cmd
+ s.mu.Lock()
+ if s.lastView != nil {
+ onMouse = s.lastView.OnMouse
+ }
+ s.mu.Unlock()
+ if onMouse != nil {
+ return onMouse(m)
}
return nil
}
@@ -828,3 +841,20 @@ func viewEquals(a, b *View) bool {
return true
}
+
+func keyboardEnhancementsFlags(ke KeyboardEnhancements) int {
+ flags := 1 // always enable basic key disambiguation
+ if ke.ReportEventTypes {
+ flags |= ansi.KittyReportEventTypes
+ }
+ if ke.ReportAlternateKeys {
+ flags |= ansi.KittyReportAlternateKeys
+ }
+ if ke.ReportAllKeysAsEscapeCodes {
+ flags |= ansi.KittyReportAllKeysAsEscapeCodes
+ }
+ if ke.ReportAssociatedText {
+ flags |= ansi.KittyReportAssociatedKeys
+ }
+ return flags
+}
diff --git a/vendor/charm.land/bubbletea/v2/keyboard.go b/vendor/charm.land/bubbletea/v2/keyboard.go
index dfc433496..33747a456 100644
--- a/vendor/charm.land/bubbletea/v2/keyboard.go
+++ b/vendor/charm.land/bubbletea/v2/keyboard.go
@@ -39,3 +39,21 @@ func (k KeyboardEnhancementsMsg) SupportsKeyDisambiguation() bool {
func (k KeyboardEnhancementsMsg) SupportsEventTypes() bool {
return k.Flags&ansi.KittyReportEventTypes != 0
}
+
+// SupportsAlternateKeys returns whether the terminal supports reporting
+// alternate key codes.
+func (k KeyboardEnhancementsMsg) SupportsAlternateKeys() bool {
+ return k.Flags&ansi.KittyReportAlternateKeys != 0
+}
+
+// SupportsAllKeysAsEscapeCodes returns whether the terminal supports reporting
+// all keys as escape codes.
+func (k KeyboardEnhancementsMsg) SupportsAllKeysAsEscapeCodes() bool {
+ return k.Flags&ansi.KittyReportAllKeysAsEscapeCodes != 0
+}
+
+// SupportsAssociatedText returns whether the terminal supports reporting
+// associated text with key events.
+func (k KeyboardEnhancementsMsg) SupportsAssociatedText() bool {
+ return k.Flags&ansi.KittyReportAssociatedKeys != 0
+}
diff --git a/vendor/charm.land/bubbletea/v2/tea.go b/vendor/charm.land/bubbletea/v2/tea.go
index e1dfc97d9..e249d71bb 100644
--- a/vendor/charm.land/bubbletea/v2/tea.go
+++ b/vendor/charm.land/bubbletea/v2/tea.go
@@ -245,6 +245,25 @@ type KeyboardEnhancements struct {
// [KeyPressMsg] with the [Key.IsRepeat] field set indicating that this is
// a it's part of a key repeat sequence.
ReportEventTypes bool
+
+ // ReportAlternateKeys requests the terminal to report alternate key values
+ // in addition to the main ones.
+ // Note that only key events represented as escape codes will affected by
+ // this enhancement.
+ ReportAlternateKeys bool
+
+ // ReportAllKeysAsEscapeCodes requests the terminal to report all key
+ // events, including plain text keys, as escape codes.
+ // When this is enabled, text won't be sent as plain text but instead as
+ // escape codes that encode the key value and modifiers.
+ ReportAllKeysAsEscapeCodes bool
+
+ // ReportAssociatedText requests the terminal to report the text associated
+ // with key events.
+ // Note that this is an enhancement to
+ // [KeyboardEnhancements.ReportAllKeysAsEscapeCodes] and only has an effect
+ // if that is enabled.
+ ReportAssociatedText bool
}
// SetContent is a helper method to set the content of a [View] with a styled
@@ -1328,8 +1347,10 @@ func (p *Program) RestoreTerminal() error {
if err := p.initTerminal(); err != nil {
return err
}
- if err := p.initInputReader(false); err != nil {
- return err
+ if p.input != nil {
+ if err := p.initInputReader(false); err != nil {
+ return err
+ }
}
p.startRenderer()
diff --git a/vendor/charm.land/bubbletea/v2/tty_unix.go b/vendor/charm.land/bubbletea/v2/tty_unix.go
index bf1b49a18..4981a560a 100644
--- a/vendor/charm.land/bubbletea/v2/tty_unix.go
+++ b/vendor/charm.land/bubbletea/v2/tty_unix.go
@@ -40,6 +40,7 @@ const suspendSupported = true
func suspendProcess() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGCONT)
+ defer signal.Stop(c)
_ = syscall.Kill(0, syscall.SIGTSTP)
// blocks until a CONT happens...
<-c
diff --git a/vendor/charm.land/lipgloss/v2/.golangci.yml b/vendor/charm.land/lipgloss/v2/.golangci.yml
index c90f03161..f2e6be064 100644
--- a/vendor/charm.land/lipgloss/v2/.golangci.yml
+++ b/vendor/charm.land/lipgloss/v2/.golangci.yml
@@ -30,6 +30,9 @@ linters:
- text: '(slog|log)\.\w+'
linters:
- noctx
+ - text: "naming"
+ linters:
+ - revive
generated: lax
presets:
- common-false-positives
diff --git a/vendor/charm.land/lipgloss/v2/README.md b/vendor/charm.land/lipgloss/v2/README.md
index 16d497054..4fda64003 100644
--- a/vendor/charm.land/lipgloss/v2/README.md
+++ b/vendor/charm.land/lipgloss/v2/README.md
@@ -893,7 +893,7 @@ thatColor := lightDark(lipgloss.Color("#37CD96"), lipgloss.Color("#22C78A"))
a := lipgloss.NewStyle().Foreground(thisColor).Render("this")
b := lipgloss.NewStyle().Foreground(thatColor).Render("that")
-// Render the appriate colors at runtime:
+// Render the appropriate colors at runtime:
lipgloss.Fprintf(os.Stderr, "my fave colors are %s and %s", a, b)
```
diff --git a/vendor/charm.land/lipgloss/v2/position.go b/vendor/charm.land/lipgloss/v2/position.go
index cea67763f..ce91442d6 100644
--- a/vendor/charm.land/lipgloss/v2/position.go
+++ b/vendor/charm.land/lipgloss/v2/position.go
@@ -39,7 +39,7 @@ func Place(width, height int, hPos, vPos Position, str string, opts ...Whitespac
// PlaceHorizontal places a string or text block horizontally in an unstyled
// block of a given width. If the given width is shorter than the max width of
-// the string (measured by its longest line) this will be a noop.
+// the string (measured by its longest line) this will be a noöp.
func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string {
lines, contentWidth := getLines(str)
gap := width - contentWidth
@@ -86,7 +86,7 @@ func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOpti
// PlaceVertical places a string or text block vertically in an unstyled block
// of a given height. If the given height is shorter than the height of the
-// string (measured by its newlines) then this will be a noop.
+// string (measured by its newlines) then this will be a noöp.
func PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string {
contentHeight := strings.Count(str, "\n") + 1
gap := height - contentHeight
diff --git a/vendor/charm.land/lipgloss/v2/query.go b/vendor/charm.land/lipgloss/v2/query.go
index 25e0d9de9..96cd5b945 100644
--- a/vendor/charm.land/lipgloss/v2/query.go
+++ b/vendor/charm.land/lipgloss/v2/query.go
@@ -54,13 +54,16 @@ func BackgroundColor(in term.File, out term.File) (bg color.Color, err error) {
}
// NOTE: On Unix, one of the given files must be a tty.
+ if !term.IsTerminal(in.Fd()) || !term.IsTerminal(out.Fd()) {
+ return nil, fmt.Errorf("input/output is not a terminal")
+ }
for _, f := range []term.File{in, out} {
if bg, err = backgroundColor(f, f); err == nil {
return bg, nil
}
}
- return
+ return bg, err
}
// HasDarkBackground detects whether the terminal has a light or dark
diff --git a/vendor/charm.land/lipgloss/v2/wrap.go b/vendor/charm.land/lipgloss/v2/wrap.go
index ca0a1e239..ccffe89b1 100644
--- a/vendor/charm.land/lipgloss/v2/wrap.go
+++ b/vendor/charm.land/lipgloss/v2/wrap.go
@@ -63,6 +63,12 @@ func (w *WrapWriter) Link() uv.Link {
// Write writes to the buffer.
func (w *WrapWriter) Write(p []byte) (int, error) {
+ if w.p == nil {
+ // The writer has been closed and its parser returned to the pool.
+ // Writing after close can happen during out-of-order teardown of
+ // nested writer chains; treat it as a no-op rather than panicking.
+ return len(p), nil
+ }
for i := range p {
b := p[i]
w.p.Advance(b)
diff --git a/vendor/github.com/charmbracelet/ultraviolet/.golangci.yml b/vendor/github.com/charmbracelet/ultraviolet/.golangci.yml
index c90f03161..64474889e 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/.golangci.yml
+++ b/vendor/github.com/charmbracelet/ultraviolet/.golangci.yml
@@ -1,41 +1,42 @@
version: "2"
-run:
- tests: false
linters:
enable:
- bodyclose
- - exhaustive
- - goconst
- - godot
- - gomoddirectives
+ # - exhaustive
+ # - goconst
+ # - godot
+ # - godox
+ # - gomoddirectives
- goprintffuncname
- - gosec
+ # - gosec
- misspell
- - nakedret
- - nestif
- - nilerr
+ # - nakedret
+ # - nestif
+ # - nilerr
- noctx
- nolintlint
- - prealloc
- - revive
+ # - prealloc
+ # - revive
- rowserrcheck
- sqlclosecheck
+ - staticcheck
- tparallel
- - unconvert
- - unparam
+ # - unconvert
+ # - unparam
- whitespace
- - wrapcheck
+ # - wrapcheck
+ disable:
+ - errcheck
+ - ineffassign
+ - unused
exclusions:
+ generated: lax
+ presets:
+ - common-false-positives
rules:
- text: '(slog|log)\.\w+'
linters:
- noctx
- generated: lax
- presets:
- - common-false-positives
- settings:
- exhaustive:
- default-signifies-exhaustive: true
issues:
max-issues-per-linter: 0
max-same-issues: 0
diff --git a/vendor/github.com/charmbracelet/ultraviolet/README.md b/vendor/github.com/charmbracelet/ultraviolet/README.md
index eb83cc5ef..b28ccdd61 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/README.md
+++ b/vendor/github.com/charmbracelet/ultraviolet/README.md
@@ -7,108 +7,141 @@
-Ultraviolet is a set of primitives for manipulating terminal emulators, with a focus on terminal user interfaces (TUIs). It provides a set of tools and abstractions for interaction that can handle user input and display dynamic, cell-based content. It’s the product of many years of research, development, collaboration and ingenuity.
+Ultraviolet is a set of primitives for building terminal user interfaces in Go.
+It provides cell-based rendering, cross-platform input handling, and a diffing
+renderer inspired by [ncurses](https://invisible-island.net/ncurses/)—without
+the need for `terminfo` or `termcap` databases.
-Ultraviolet is not a framework by design, however it can be used standalone to create powerful terminal applications. It’s in use in production and powers critical portions of [Bubble Tea v2][bbt] and [Lip Gloss v2][lg], and was instrumental in the development of [Crush][crush].
+Ultraviolet powers [Bubble Tea v2][bbt] and [Lip Gloss v2][lg]. It replaces the
+ad-hoc terminal primitives from earlier versions with a cohesive, imperative API
+that can also be used standalone.
-[crush]: https://github.com/charmbracelet/crush
[bbt]: https://github.com/charmbracelet/bubbletea
[lg]: https://github.com/charmbracelet/lipgloss
-> [!CAUTION]
-> This project currently exists to serve internal use cases. API stability is a goal, but expect no stability guarantees as of now.
+## Install
-## Features
+```bash
+go get github.com/charmbracelet/ultraviolet@latest
+```
+
+## Quick Start
+
+```go
+package main
+
+import (
+ "log"
-Ultraviolet is built with several core features in mind to make terminal
-application development easy and performant:
+ uv "github.com/charmbracelet/ultraviolet"
+ "github.com/charmbracelet/ultraviolet/screen"
+)
-### 👺 The Cursed Renderer
+func main() {
+ t := uv.DefaultTerminal()
+ scr := t.Screen()
-The cell-based rendering model—called _The Cursed Render_—was inspired by the infamous
-[ncurses](https://invisible-island.net/ncurses/) library, which has been an
-essential part of terminal applications for decades. Ultraviolet takes this
-concept and modernizes it for the Go programming language, providing a more
-ergonomic and efficient way to work with terminal cells without the need for
-archaic technologies like `terminfo` or `termcap` databases.
+ scr.EnterAltScreen()
-Unlike ncurses, it supports both full-window and inline use-cases as we see inline TUIs as important in maintaining user context and flow.
+ if err := t.Start(); err != nil {
+ log.Fatalf("failed to start terminal: %v", err)
+ }
+ defer t.Stop()
-### 🏎️ High Speeds and Low Bandwidth
+ ctx := screen.NewContext(scr)
+ text := "Hello, World!"
+ textWidth := scr.StringWidth(text)
-The built-in terminal renderer efficiently handles content updates by utilizing
-a powerful cell-based diffing algorithm that minimizes the amount of data
-written to the terminal using various ANSI escape sequences to accomplish this.
-This allows applications to update only the parts of the terminal that have
-changed, significantly improving performance and responsiveness.
+ display := func() {
+ screen.Clear(scr)
+ bounds := scr.Bounds()
+ x := (bounds.Dx() - textWidth) / 2
+ y := bounds.Dy() / 2
+ ctx.DrawString(text, x, y)
+ scr.Render()
+ scr.Flush()
+ }
-In practical terms, Ultraviolet optimizes for fast redraws that use minimal data transfer. This is very important locally and critically important over the network (for example, via SSH).
+ for ev := range t.Events() {
+ switch ev := ev.(type) {
+ case uv.WindowSizeEvent:
+ scr.Resize(ev.Width, ev.Height)
+ display()
+ case uv.KeyPressEvent:
+ if ev.MatchString("q", "ctrl+c") {
+ return
+ }
+ }
+ }
+}
+```
-### 💬 Universal Input
+## Architecture
-Input handling in terminals can be complex, especially when dealing with
-multiple input sources, different platforms, and ancient terminal baggage.
-Ultraviolet simplifies this by providing a unified interface for handling user
-input, allowing developers to focus on building their applications without
-getting bogged down in the intricacies of terminal input handling.
+Ultraviolet is organized as a set of layered primitives:
-### 🎮 Cross-Platform Compatibility
+- **Terminal** — manages the application lifecycle: raw mode, input event loop,
+ start/stop. Create one with `DefaultTerminal()` or `NewTerminal(console, opts)`.
-Ultraviolet is designed to work seamlessly across different platforms and
-terminal emulators. It abstracts away the differences in terminal capabilities
-and provides a consistent API for developers to work with, ensuring that
-applications built with Ultraviolet will run smoothly on various systems.
+- **TerminalScreen** — the screen state manager. Handles rendering, alternate
+ screen buffer, cursor, mouse modes, keyboard enhancements, bracketed paste,
+ window title, and more. Access it via `terminal.Screen()`.
-On Windows, it uses the [Windows Console API](https://learn.microsoft.com/en-us/windows/console/console-functions) to
-provide a consistent experience, while on Unix-like systems, it relies on the
-standard Termios API along with ANSI escape sequences to manipulate the
-terminal.
+- **Screen** — a minimal interface (`Bounds`, `CellAt`, `SetCell`,
+ `WidthMethod`) implemented by `TerminalScreen`, `Buffer`, `Window`, and
+ `ScreenBuffer`. Write code against `Screen` to stay decoupled from the
+ terminal.
-In short: Ultraviolet provides first-class support for both Unix and Windows-based systems.
+- **Buffer / Window** — off-screen cell buffers. `Buffer` is a flat grid of
+ cells. `Window` adds parent/child relationships and shared-buffer views.
+ Both implement `Screen` and `Drawable`.
-### 🧩 Extensible Architecture
+- **screen package** — drawing helpers that operate on any `Screen`: a
+ `Context` for styled text rendering (`Print`, `DrawString`, etc.) and
+ utility functions like `Clear`, `Fill`, `Clone`.
+
+- **layout package** — a constraint-based layout solver built on the
+ [Cassowary algorithm][casso]. Partition screen space with `Len`, `Min`,
+ `Max`, `Percent`, `Ratio`, and `Fill` constraints.
+
+[casso]: https://en.wikipedia.org/wiki/Cassowary_(software)
+
+## Features
-Ultraviolet is built with extensibility in mind, providing a solid API that can
-be embedded into other applications or used as a foundation for building custom
-terminal user interfaces. It allows developers to create their own components,
-styles, and behaviors, making it a versatile tool for building terminal
-applications.
+- **Cell-based diffing renderer** — only redraws what changed. Optimizes
+ cursor movement, uses ECH/REP/ICH/DCH when available, and supports scroll
+ optimizations. Minimal bandwidth, critical for SSH.
-## FAQ
+- **Universal input** — unified keyboard and mouse event handling across
+ platforms. Supports legacy encodings, Kitty keyboard protocol, SGR mouse,
+ and Windows Console input.
-### 🐈 What about other Charm libraries?
+- **Inline and fullscreen** — works in both alternate screen (fullscreen) and
+ inline mode. Inline TUIs preserve terminal context and scrollback.
-Ultraviolet is not a replacement for existing libraries like [Bubble Tea](https://github.com/charmbracelet/bubbletea) or [Lip
-Gloss](https://github.com/charmbracelet/lipgloss). Instead, it serves as a
-foundation for the latest versions of both of these libraries and others like them, providing the
-underlying primitives and abstractions needed to build terminal user interfaces
-applications and frameworks.
+- **Cross-platform** — first-class support for Unix (termios + ANSI) and
+ Windows (Console API). Consistent behavior across terminal emulators.
-### 🛁 How is it different from Bubble Tea?
+- **Suspend/resume** — `Stop()` and `Start()` can be called repeatedly for
+ suspend/resume cycles, shelling out to editors, or process suspension
+ via `uv.Suspend()`.
-Ultraviolet is a lower-level library that focuses on the core primitives of
-terminal manipulation, rendering, and input handling. It provides the building
-blocks for creating terminal applications, while Bubble Tea is a higher-level
-framework that builds on top of Ultraviolet to provide a more structured and
-opinionated way to build terminal user interfaces.
+## Examples
-### 💋 Is it a replacement for Lip Gloss?
+See the [`examples/`](./examples/) directory for core examples and
+[`examples/advanced/`](./examples/advanced/) for more complex demos.
-Simply put, no. Ultraviolet is not a replacement for Lip Gloss. Instead, it
-provides the underlying rendering capabilities that Lip Gloss can use to create
-styled terminal content. Lip Gloss is a higher-level library that builds on top
-of Ultraviolet by utilizing the cell-based rendering model to provide a
-simplified and ergonomic way to create styled terminal content and composition
-of terminal user interfaces.
+## Tutorial
-## ✏️ Tutorial
+See [TUTORIAL.md](./TUTORIAL.md) for a step-by-step guide to building your
+first Ultraviolet application.
-You can find a simple tutorial on how to create a UV application that displays
-"Hello, World!" on the screen in the [TUTORIAL.md](./TUTORIAL.md) file.
+> [!NOTE]
+> Ultraviolet is in active development. The API may change.
-## Whatcha think?
+## Feedback
-We’d love to hear your thoughts on this project. Feel free to drop us a note!
+We'd love to hear your thoughts on this project. Feel free to drop us a note!
- [Twitter](https://twitter.com/charmcli)
- [Discord](https://charm.land/discord)
diff --git a/vendor/github.com/charmbracelet/ultraviolet/TUTORIAL.md b/vendor/github.com/charmbracelet/ultraviolet/TUTORIAL.md
index 655617213..d456d5a49 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/TUTORIAL.md
+++ b/vendor/github.com/charmbracelet/ultraviolet/TUTORIAL.md
@@ -1,199 +1,167 @@
-# Hello World!
+# Tutorial: Hello World
-This is a simple tutorial on how to create a UV application that displays
-"Hello, World!" on the screen. UV is a terminal UI toolkit that allows you to
-create terminal applications with ease. It provides a simple API to handle
-screen management, input handling, and rendering content.
+This tutorial walks through building a simple Ultraviolet application that
+displays centered "Hello, World!" text. You'll learn the core concepts:
+creating a terminal, managing a screen, handling input events, and rendering.
-## Tutorial
+## Creating a Terminal
-What does UV consist of? A UV application consists of a screen that displays
-content, has some sort of input sources that can be used to interact with the
-screen, and is meant to display content or programs on the screen.
-
-First, we need to create a screen that will display our program. UV comes with
-a `Terminal` screen that is used to display content on a terminal.
+A `Terminal` manages the console, input event loop, and screen state.
```go
-t := uv.NewTerminal(os.Stdin, os.Stdout, os.Environ())
-// Or simply use...
-// t := uv.DefaultTerminal()
+t := uv.DefaultTerminal()
```
-A terminal screen has a few properties that are unique to it. For example, a
-terminal screen can go into raw mode, which is important to disable echoing of
-input characters, and to disable signal handling so that we can receive things
-like ctrl+c without the terminal interfering with our program.
+You can also create a terminal with a custom console and options:
-Another important property of a terminal screen is the alternate screen buffer.
-This property puts the terminal screen into a special mode that allows us to
-display content without interfering with the normal screen buffer.
+```go
+con := uv.NewConsole(os.Stdin, os.Stdout, os.Environ())
+t := uv.NewTerminal(con, &uv.Options{
+ Logger: myLogger, // optional, for debugging I/O
+})
+```
-In this tutorial, we will use the alternate screen buffer to display our
-program so that we don't affect the normal screen buffer.
+## Getting the Screen
-```go
-// Set the terminal to raw mode.
-if err := t.MakeRaw(); err != nil {
- log.Fatal(err)
-}
+The terminal's screen is where you draw content, manage the alternate screen
+buffer, and set cells.
-// Enter the alternate screen buffer. This will
-// only take affect once we flush or display
-// our program on the terminal screen.
-t.EnterAltScreen()
+```go
+scr := t.Screen()
+```
-// My program
-// ...
+## Alternate Screen
-// Make sure we leave the alternate screen buffer when we
-// are done with our program. This will be called automatically
-// when we use [t.Shutdown(ctx)] later.
-t.LeaveAltScreen()
+The alternate screen buffer lets your application display content without
+affecting the user's scrollback. Most fullscreen TUIs use it.
-// Make sure we restore the terminal to its original state
-// before we exit. We don't care about errors here, but you
-// can handle them if you want. This will be called automatically
-// when we use [t.Shutdown(ctx)] later.
-_ = t.Restore() //nolint:errcheck
+```go
+scr.EnterAltScreen()
```
-Now that we have our screen set to raw mode and in the alternate screen buffer,
-we can create our program that will be displayed on the screen. A program is an
-abstraction layer that handles different screen types and implementations. It
-only cares about displaying content on the screen.
+## Starting the Terminal
-We need to start our program before we can display anything on the screen. This
-will ensure that the program and screen are initialized and ready to display
-content. Internally, this will also call `t.Start()` to start the terminal
-screen.
+Starting puts the console into raw mode (disabling echoing and line buffering
+so we receive individual keypresses like ctrl+c), initializes the
+input event loop, and prepares the screen for rendering.
```go
if err := t.Start(); err != nil {
- log.Fatalf("failed to start program: %v", err)
+ log.Fatalf("failed to start terminal: %v", err)
}
+defer t.Stop()
```
-Let's display a simple frame with some text in it. A frame is a container that
-holds the buffer we're displaying. The final cursor position we want our cursor
-to be at, and the viewport area we are working with to display our content.
+`Stop()` restores the console, exits the alternate screen, and cleans up. It
+is safe to call multiple times and supports suspend/resume cycles.
+
+## Drawing Text
+
+You can set individual cells directly:
```go
for i, r := range "Hello, World!" {
- // We iterate over the string to display each character
- // in a separate cell. Ideally, we want each cell
- // to have exactly one grapheme. In this case, since
- // we're using a simple ASCII string, we know that
- // each character is a single grapheme with a width of 1.
- var c uv.Cell
- c.Content = string(r)
- c.Width = 1
- t.SetCell(i, 0, &c)
+ scr.SetCell(i, 0, &uv.Cell{Content: string(r), Width: 1})
}
-// Now we simply render the changes and flush them
-// to the terminal screen.
-_ = p.Display()
```
-Different screen models have different ways to receive input. Some models have
-a remote control, while others have a touch screen. A terminal can receive
-input from various peripherals usually through control codes and escape
-sequences. Our terminal has a `t.Events(ctx)` method that returns a channel
-which will receive events from different terminal input sources.
+Or use the `screen` helper package for convenience:
```go
-// We want to be able to stop the terminal input loop
-// whenever we call cancel().
-ctx, cancel := context.WithCancel(context.Background())
-defer cancel()
-
-for ev := range t.Events(ctx) {
- switch ev := ev.(type) {
- case uv.WindowSizeEvent:
- // Our terminal screen is resizable. This is important
- // as we want to inform our terminal screen with the
- // size we'd like to display our program in.
- // When we're using the full terminal window size,
- // we can assume that the terminal screen will
- // also have the same size as our program.
- // However, with inline programs, usually we want
- // the height to be the height of our program.
- // So if our inline program takes 10 lines, we
- // want to resize the terminal screen to 10 lines
- // high.
- width, height := ev.Width, ev.Height
- if !altscreen {
- height = 10
- }
- t.Resize(width, height)
- case uv.KeyPressEvent:
- if ev.MatchStrings("q", "ctrl+c") {
- // This will stop the input loop and cancel the context.
- cancel()
- }
- }
+ctx := screen.NewContext(scr)
+ctx.DrawString("Hello, World!", 0, 0)
+```
+
+The `Context` supports styled text, links, and wrapping. It implements
+`io.Writer`, so you can use `fmt.Fprint` and friends.
+
+## Rendering
+
+Drawing to the screen is a two-step process:
+
+1. **Render** — computes the minimal diff between the current and new screen
+ state, writing ANSI escape sequences to an internal buffer.
+2. **Flush** — writes the buffer to the terminal. This is the only step that
+ performs real I/O and can return an error.
+
+```go
+scr.Render()
+if err := scr.Flush(); err != nil {
+ log.Fatalf("flush failed: %v", err)
}
```
-Now that we've handled displaying our program and receiving input from the
-terminal, we need to handle the program's lifecycle. We need to make sure that
-we restore the terminal to its original state when we exit the program. A
-terminal program can be stopped gracefully using the `t.Shutdown(ctx)` method.
+## Handling Events
+
+The terminal provides a channel of input events. Range over it to process
+keyboard, mouse, and resize events:
```go
-// We need to make sure we stop the program gracefully
-// after we exit the input loop.
-if err := t.Shutdown(ctx); err != nil {
- log.Fatal(err)
+for ev := range t.Events() {
+ switch ev := ev.(type) {
+ case uv.WindowSizeEvent:
+ scr.Resize(ev.Width, ev.Height)
+ case uv.KeyPressEvent:
+ if ev.MatchString("q", "ctrl+c") {
+ return
+ }
+ }
}
```
-Finally, let's put everything together and create a simple program that displays
-a frame with "Hello, World!" in it. The program will exit when we press
-ctrl+c or q.
+`MatchString` accepts key names and modifier combinations like `"ctrl+a"`,
+`"shift+enter"`, or `"alt+tab"`.
+
+## Putting It Together
+
+Here's the complete program—centered "Hello, World!" that redraws on resize
+and exits on `q` or `ctrl+c`:
```go
+package main
+
+import (
+ "log"
+
+ uv "github.com/charmbracelet/ultraviolet"
+ "github.com/charmbracelet/ultraviolet/screen"
+)
+
func main() {
- t := uv.NewTerminal(os.Stdin, os.Stdout, os.Environ())
+ t := uv.DefaultTerminal()
+ scr := t.Screen()
- if err := t.MakeRaw(); err != nil {
- log.Fatalf("failed to make terminal raw: %v", err)
- }
+ scr.EnterAltScreen()
if err := t.Start(); err != nil {
- log.Fatalf("failed to start program: %v", err)
+ log.Fatalf("failed to start terminal: %v", err)
+ }
+ defer t.Stop()
+
+ ctx := screen.NewContext(scr)
+ text := "Hello, World!"
+ textWidth := scr.StringWidth(text)
+
+ display := func() {
+ screen.Clear(scr)
+ bounds := scr.Bounds()
+ x := (bounds.Dx() - textWidth) / 2
+ y := bounds.Dy() / 2
+ ctx.DrawString(text, x, y)
+ scr.Render()
+ scr.Flush()
}
- t.EnterAltScreen()
-
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
-
- for ev := range t.Events(ctx) {
+ for ev := range t.Events() {
switch ev := ev.(type) {
case uv.WindowSizeEvent:
- width, height := ev.Width, ev.Height
- t.Erase()
- t.Resize(width, height)
+ scr.Resize(ev.Width, ev.Height)
+ display()
case uv.KeyPressEvent:
- if ev.MatchStrings("q", "ctrl+c") {
- cancel()
+ if ev.MatchString("q", "ctrl+c") {
+ return
}
}
-
- for i, r := range "Hello, World!" {
- var c uv.Cell
- c.Content = string(r)
- c.Width = 1
- t.SetCell(i, 0, &c)
- }
- if err := t.Display(); err != nil {
- log.Fatal(err)
- }
- }
-
- if err := t.Shutdown(ctx); err != nil {
- log.Fatal(err)
}
}
```
diff --git a/vendor/github.com/charmbracelet/ultraviolet/buffer.go b/vendor/github.com/charmbracelet/ultraviolet/buffer.go
index d13afbc97..db8f6b888 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/buffer.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/buffer.go
@@ -374,7 +374,12 @@ func (b *Buffer) FillArea(c *Cell, area Rectangle) {
// Clear clears the buffer with space cells and rectangle.
func (b *Buffer) Clear() {
- b.ClearArea(b.Bounds())
+ area := b.Bounds()
+ for y := area.Min.Y; y < area.Max.Y; y++ {
+ for x := area.Min.X; x < area.Max.X; x++ {
+ b.Lines[y][x] = EmptyCell
+ }
+ }
}
// ClearArea clears the buffer with space cells within the specified
@@ -392,12 +397,14 @@ func (b *Buffer) CloneArea(area Rectangle) *Buffer {
}
n := NewBuffer(area.Dx(), area.Dy())
for y := area.Min.Y; y < area.Max.Y; y++ {
- for x := area.Min.X; x < area.Max.X; x++ {
+ for x := area.Min.X; x < area.Max.X; {
c := b.CellAt(x, y)
if c == nil || c.IsZero() {
+ x++
continue
}
n.SetCell(x-area.Min.X, y-area.Min.Y, c)
+ x += max(c.Width, 1)
}
}
return n
@@ -697,11 +704,14 @@ func (b *RenderBuffer) TouchedLines() int {
// SetCell sets the cell at the given x, y position and marks the line as
// touched.
func (b *RenderBuffer) SetCell(x, y int, c *Cell) {
- if !cellEqual(b.CellAt(x, y), c) {
+ if p := b.CellAt(x, y); !cellEqual(p, c) {
width := 1
if c != nil && c.Width > 0 {
width = c.Width
}
+ if p != nil && p.Width > 0 {
+ width = max(width, p.Width)
+ }
b.TouchLine(x, y, width)
}
b.Buffer.SetCell(x, y, c)
@@ -786,3 +796,37 @@ func (b *RenderBuffer) DeleteCellArea(x, y, n int, c *Cell, area Rectangle) {
}
b.TouchLine(x, y, n)
}
+
+// Clear clears the buffer with space cells and marks all lines as touched.
+func (b *RenderBuffer) Clear() {
+ b.Buffer.Clear()
+ w := b.Width()
+ for y := range b.Lines {
+ b.TouchLine(0, y, w)
+ }
+}
+
+// ClearArea clears the buffer with space cells within the specified rectangle
+// and marks the affected lines as touched.
+func (b *RenderBuffer) ClearArea(area Rectangle) {
+ b.Buffer.ClearArea(area)
+ w := area.Max.X - area.Min.X
+ for y := area.Min.Y; y < area.Max.Y; y++ {
+ b.TouchLine(area.Min.X, y, w)
+ }
+}
+
+// Fill fills the buffer with the given cell and marks all lines as touched.
+func (b *RenderBuffer) Fill(c *Cell) {
+ b.FillArea(c, b.Bounds())
+}
+
+// FillArea fills the buffer with the given cell within the specified rectangle
+// and marks the affected lines as touched.
+func (b *RenderBuffer) FillArea(c *Cell, area Rectangle) {
+ b.Buffer.FillArea(c, area)
+ w := area.Max.X - area.Min.X
+ for y := area.Min.Y; y < area.Max.Y; y++ {
+ b.TouchLine(area.Min.X, y, w)
+ }
+}
diff --git a/vendor/github.com/charmbracelet/ultraviolet/doc.go b/vendor/github.com/charmbracelet/ultraviolet/doc.go
index af352053c..27b8a25c3 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/doc.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/doc.go
@@ -1,3 +1,46 @@
-// Package uv (Ultraviolet) is a library for creating terminal-based user
+// Package uv (Ultraviolet) provides primitives for building terminal user
// interfaces in Go.
+//
+// It offers cell-based screen buffers, a diffing terminal renderer, cross-platform
+// input decoding (keyboard, mouse, paste, focus, resize), and a windowing model
+// with off-screen buffers. Ultraviolet powers Bubble Tea v2 and Lip Gloss v2.
+//
+// # Getting Started
+//
+// Create a terminal, get its screen, and run an event loop:
+//
+// t := uv.DefaultTerminal()
+// scr := t.Screen()
+// scr.EnterAltScreen()
+//
+// if err := t.Start(); err != nil {
+// log.Fatal(err)
+// }
+// defer t.Stop()
+//
+// for ev := range t.Events() {
+// switch ev := ev.(type) {
+// case uv.WindowSizeEvent:
+// scr.Resize(ev.Width, ev.Height)
+// case uv.KeyPressEvent:
+// if ev.MatchString("q") { return }
+// }
+// }
+//
+// # Rendering
+//
+// Drawing is a two-step process. [TerminalScreen.Render] diffs the screen and
+// writes escape sequences to an internal buffer. [TerminalScreen.Flush] sends
+// the buffer to the terminal—this is the only method that performs real I/O.
+//
+// scr.Render()
+// scr.Flush()
+//
+// Use the screen package for high-level drawing helpers like
+// [screen.Context.DrawString] and [screen.Clear].
+//
+// # Packages
+//
+// - screen — drawing context and screen manipulation helpers
+// - layout — constraint-based layout solver (Cassowary algorithm)
package uv
diff --git a/vendor/github.com/charmbracelet/ultraviolet/event.go b/vendor/github.com/charmbracelet/ultraviolet/event.go
index bf48d147c..2d0d8cf20 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/event.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/event.go
@@ -94,7 +94,7 @@ type MultiEvent []Event
func (e MultiEvent) String() string {
var sb strings.Builder
for _, ev := range e {
- sb.WriteString(fmt.Sprintf("%v\n", ev))
+ fmt.Fprintf(&sb, "%v\n", ev)
}
return sb.String()
}
diff --git a/vendor/github.com/charmbracelet/ultraviolet/mouse.go b/vendor/github.com/charmbracelet/ultraviolet/mouse.go
index e86d94b8d..a74cdf992 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/mouse.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/mouse.go
@@ -4,16 +4,37 @@ import (
"github.com/charmbracelet/x/ansi"
)
-// MouseMode represents the mouse mode for the terminal. It is used to enable
-// or disable mouse support on the terminal.
-type MouseMode byte
+// MouseMode represents the mouse tracking mode for the terminal.
+type MouseMode uint8
-// Mouse modes.
+// Mouse tracking modes.
+//
+// These determine which mouse events the terminal reports.
+const (
+ MouseModeNone MouseMode = iota // Disable mouse tracking.
+ MouseModePress // Press only (DEC mode 9). Reports button press events.
+ MouseModeClick // Click tracking (DEC mode 1000). Reports button press and release.
+ MouseModeDrag // Drag tracking (DEC mode 1002). Reports press, release, and drag.
+ MouseModeMotion // Motion tracking (DEC mode 1003). Reports all mouse events including motion.
+)
+
+// MouseEncoding represents the encoding used for mouse events.
+type MouseEncoding uint8
+
+// Mouse encodings.
+//
+// These determine how mouse coordinates and buttons are encoded in the
+// terminal's escape sequences. The encoding is only meaningful when mouse
+// tracking is enabled via [MouseMode].
const (
- MouseModeNone MouseMode = iota
- MouseModeClick
- MouseModeDrag
- MouseModeMotion
+ MouseEncodingLegacy MouseEncoding = iota // Legacy X10-compatible encoding. Coordinates limited to 223.
+ MouseEncodingSGR // SGR encoding (DEC mode 1006). No coordinate limit, distinguishes press/release.
+ MouseEncodingSGRPixel // SGR-pixel encoding (DEC mode 1016). Reports pixel coordinates.
+
+ // TODO: support these additional encodings in the future.
+ // MouseEncodingUTF8 // UTF-8 encoding (DEC mode 1005). Coordinates limited to 223.
+ // MouseEncodingUrxvt // urxvt encoding (DEC mode 1015). No coordinate limit.
+ // MouseEncodingSGRPixel // SGR-pixel encoding (DEC mode 1016). Reports pixel coordinates.
)
// MouseButton represents the button that was pressed during a mouse message.
@@ -55,7 +76,8 @@ const (
// messages.
//
// The X and Y coordinates are zero-based, with (0,0) being the upper left
-// corner of the terminal.
+// corner of the terminal. When using [MouseEncodingSGRPixel] (DEC mode 1016),
+// X and Y are in pixel coordinates; otherwise they are in cell coordinates.
//
// // Catch all mouse events
// switch Event := Event.(type) {
@@ -96,3 +118,33 @@ func (m Mouse) String() (s string) {
return s
}
+
+// MousePixelToCell converts a mouse event with pixel coordinates to cell
+// coordinates.
+//
+// This is only meaningful when using [MouseEncodingSGRPixel] encoding, which
+// reports mouse coordinates in pixels rather than cell units. The conversion
+// is based on the terminal's reported pixel dimensions and cell dimensions.
+//
+// On Windows, or other platforms where [Terminal.GetWinsize] doesn't report
+// pixel dimensions, you can query for the terminal window pixel dimensions via
+// [ansi.WindowOp](4) and get back a [PixelSizeEvent] with the pixel
+// dimensions. You can then construct a [Winsize] struct with the pixel
+// dimensions and the cell dimensions from [Terminal.GetWinsize] and pass it to
+// this function.
+func MousePixelToCell(m Mouse, ws *Winsize) Mouse {
+ var col, row int
+ if ws.Xpixel > 0 {
+ col = m.X * int(ws.Col) / int(ws.Xpixel)
+ }
+ if ws.Ypixel > 0 {
+ row = m.Y * int(ws.Row) / int(ws.Ypixel)
+ }
+
+ return Mouse{
+ X: col,
+ Y: row,
+ Button: m.Button,
+ Mod: m.Mod,
+ }
+}
diff --git a/vendor/github.com/charmbracelet/ultraviolet/styled.go b/vendor/github.com/charmbracelet/ultraviolet/styled.go
index baf346bd5..540f6463a 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/styled.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/styled.go
@@ -203,6 +203,12 @@ func printString[T []byte | string](
// Advance the state and data
state = newState
str = str[n:]
+
+ if y >= bounds.Max.Y {
+ // We've reached the bottom of the bounds, stop processing further
+ // lines.
+ break
+ }
}
// Make sure to set the last cell if it's not empty.
diff --git a/vendor/github.com/charmbracelet/ultraviolet/terminal.go b/vendor/github.com/charmbracelet/ultraviolet/terminal.go
index a01951632..bf79dce9f 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/terminal.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/terminal.go
@@ -4,7 +4,6 @@ import (
"fmt"
"os"
"os/signal"
- "sync"
"time"
"golang.org/x/sync/errgroup"
@@ -51,6 +50,10 @@ type Options struct {
//
// This is disabled by default.
UseTerminfoKeys bool
+
+ // Logger is an optional logger for tracing terminal I/O operations.
+ // If nil, no logging is performed.
+ Logger Logger
}
// DefaultOptions returns the default [Terminal] options.
@@ -64,16 +67,15 @@ func DefaultOptions() *Options {
// Terminal represents an interactive terminal application.
type Terminal struct {
- con Console
- opts *Options
- scr *TerminalScreen
- pr pollReader
- buf []byte
- evc chan Event
- errg errgroup.Group
- winch chan os.Signal
- donec chan struct{}
- logger Logger
+ con Console
+ opts *Options
+ scr *TerminalScreen
+ pr pollReader
+ buf []byte
+ evc chan Event
+ errg errgroup.Group
+ winch chan os.Signal
+ donec chan struct{}
}
// DefaultTerminal creates a new [Terminal] instance using the default standard
@@ -124,13 +126,34 @@ func NewTerminal(con Console, opts *Options) *Terminal {
// These channels never close during the terminal's lifetime.
t.evc = make(chan Event)
t.winch = make(chan os.Signal, 1) // buffered to avoid missing signals
+ if opts.Logger != nil {
+ t.scr.rend.SetLogger(opts.Logger)
+ }
return t
}
-// SetLogger sets the terminal's logger for tracing I/O operations.
-func (t *Terminal) SetLogger(logger Logger) {
- t.logger = logger
- t.scr.rend.SetLogger(logger)
+// GetSize returns the current size of the terminal in characters and pixels.
+func (t *Terminal) GetSize() (width, height int, err error) {
+ w, h, err := t.con.GetSize()
+ if err != nil {
+ return 0, 0, fmt.Errorf("getting terminal size: %w", err)
+ }
+ return int(w), int(h), nil
+}
+
+// GetWinsize returns the current size of the terminal as a [Winsize] struct.
+// This includes both character dimensions (columns and rows) and pixel
+// dimensions (xpixel and ypixel).
+//
+// Note that this only returns the pixel dimensions on Unix-like systems that
+// support the TIOCGWINSZ ioctl. On other platforms, the pixel dimensions may
+// be zero or not available.
+func (t *Terminal) GetWinsize() (*Winsize, error) {
+ ws, err := t.con.GetWinsize()
+ if err != nil {
+ return nil, fmt.Errorf("getting terminal winsize: %w", err)
+ }
+ return ws, nil
}
// Screen returns the terminal's screen.
@@ -156,8 +179,8 @@ func (t *Terminal) Start() error {
if evs.lookup {
evs.table = buildKeysTable(t.opts.LegacyKeyEncoding, t.con.Getenv("TERM"), t.opts.UseTerminfoKeys)
}
- if t.logger != nil {
- evs.setLogger(t.logger)
+ if t.opts.Logger != nil {
+ evs.setLogger(t.opts.Logger)
}
bufc := make(chan []byte)
t.donec = make(chan struct{})
@@ -266,9 +289,7 @@ func (t *Terminal) Start() error {
})
// Restore any previous screen state.
- if err := t.scr.Restore(); err != nil {
- return fmt.Errorf("failed to restore terminal screen: %w", err)
- }
+ t.scr.Restore()
if err := t.scr.Flush(); err != nil {
return fmt.Errorf("failed to flush terminal screen: %w", err)
}
@@ -285,22 +306,27 @@ func (t *Terminal) Wait() error {
return nil
}
-// Stop stops the terminal event loop. This is a non-blocking call. Use
-// [Terminal.Wait] to wait for the terminal to exit.
+// Stop stops the terminal event loop. It is safe to call Stop without a
+// prior [Terminal.Start], and safe to call Stop multiple times in a row.
+// After Stop returns, [Terminal.Start] may be called again to resume.
func (t *Terminal) Stop() error {
- sync.OnceFunc(func() {
- close(t.donec)
- })
- signal.Stop(t.winch)
+ if t.donec != nil {
+ select {
+ case <-t.donec:
+ // Already closed.
+ default:
+ close(t.donec)
+ }
+ }
+ if t.winch != nil {
+ signal.Stop(t.winch)
+ }
if t.pr != nil {
t.pr.Cancel()
_ = t.pr.Close()
+ t.pr = nil
}
- if err := t.scr.Reset(); err != nil {
- _ = t.scr.Flush()
- _ = t.con.Restore()
- return fmt.Errorf("failed to reset terminal screen: %w", err)
- }
+ t.scr.Reset()
if err := t.scr.Flush(); err != nil {
_ = t.con.Restore()
return fmt.Errorf("failed to flush terminal screen: %w", err)
diff --git a/vendor/github.com/charmbracelet/ultraviolet/terminal_reader.go b/vendor/github.com/charmbracelet/ultraviolet/terminal_reader.go
index f8dc288b9..b3b79cd00 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/terminal_reader.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/terminal_reader.go
@@ -8,6 +8,7 @@ import (
"io"
"strconv"
"strings"
+ "sync"
"time"
"unicode"
"unicode/utf16"
@@ -142,7 +143,11 @@ func (d *TerminalReader) StreamEvents(ctx context.Context, eventc chan<- Event)
timeout := time.NewTimer(d.EscTimeout)
ttimeout := time.Now().Add(d.EscTimeout)
+ var wg sync.WaitGroup
+ wg.Add(1)
+
go func() {
+ defer wg.Done()
if err := d.streamData(ctx, readc); err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, cancelreader.ErrCanceled) {
errc <- nil
@@ -157,9 +162,11 @@ func (d *TerminalReader) StreamEvents(ctx context.Context, eventc chan<- Event)
select {
case <-ctx.Done():
d.sendEvents(eventc, buf.Bytes(), true)
+ wg.Wait()
return nil
case err := <-errc:
d.sendEvents(eventc, buf.Bytes(), true)
+ wg.Wait()
return err // return the first error encountered
case <-timeout.C:
d.logf("timeout reached")
diff --git a/vendor/github.com/charmbracelet/ultraviolet/terminal_reader_windows.go b/vendor/github.com/charmbracelet/ultraviolet/terminal_reader_windows.go
index 119dae172..8a526b956 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/terminal_reader_windows.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/terminal_reader_windows.go
@@ -295,13 +295,13 @@ func peekNConsoleInputs(console windows.Handle, maxEvents uint32) ([]xwindows.In
func keyEventString(vkc, sc uint16, r rune, keyDown bool, cks uint32, repeatCount uint16) string {
var s strings.Builder
s.WriteString("vkc: ")
- s.WriteString(fmt.Sprintf("%d, 0x%02x", vkc, vkc))
+ fmt.Fprintf(&s, "%d, 0x%02x", vkc, vkc)
s.WriteString(", sc: ")
- s.WriteString(fmt.Sprintf("%d, 0x%02x", sc, sc))
+ fmt.Fprintf(&s, "%d, 0x%02x", sc, sc)
s.WriteString(", r: ")
- s.WriteString(fmt.Sprintf("%q 0x%x", r, r))
+ fmt.Fprintf(&s, "%q 0x%x", r, r)
s.WriteString(", down: ")
- s.WriteString(fmt.Sprintf("%v", keyDown))
+ fmt.Fprintf(&s, "%v", keyDown)
s.WriteString(", cks: [")
if cks&xwindows.LEFT_ALT_PRESSED != 0 {
s.WriteString("left alt, ")
@@ -331,6 +331,6 @@ func keyEventString(vkc, sc uint16, r rune, keyDown bool, cks uint32, repeatCoun
s.WriteString("enhanced key, ")
}
s.WriteString("], repeat count: ")
- s.WriteString(fmt.Sprintf("%d", repeatCount))
+ fmt.Fprintf(&s, "%d", repeatCount)
return s.String()
}
diff --git a/vendor/github.com/charmbracelet/ultraviolet/terminal_renderer.go b/vendor/github.com/charmbracelet/ultraviolet/terminal_renderer.go
index af3d8ee9b..aad5a0ef2 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/terminal_renderer.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/terminal_renderer.go
@@ -137,7 +137,6 @@ type TerminalRenderer struct {
cur, saved cursor // the current and saved cursors
flags tFlag // terminal writer flags.
term string // the terminal type
- scrollHeight int // keeps track of how many lines we've scrolled down (inline mode)
clear bool // whether to force clear the screen
caps capabilities // terminal control sequence capabilities
atPhantom bool // whether the cursor is out of bounds and at a phantom cell
@@ -167,7 +166,6 @@ func NewTerminalRenderer(w io.Writer, env []string) (s *TerminalRenderer) {
s.caps = xtermCaps(s.term)
s.cur = cursor{Cell: EmptyCell, Position: Pos(-1, -1)} // start at -1 to force a move
s.saved = s.cur
- s.scrollHeight = 0
s.oldhash, s.newhash = nil, nil
return
}
@@ -368,9 +366,7 @@ func (s *TerminalRenderer) moveCursor(newbuf *RenderBuffer, x, y int, overwrite
_ = s.buf.WriteByte('\r')
s.cur.X, s.cur.Y = 0, 0
}
- seq, scrollHeight := moveCursor(s, newbuf, x, y, overwrite)
- // If we scrolled the screen, we need to update the scroll height.
- s.scrollHeight = max(s.scrollHeight, scrollHeight)
+ seq := moveCursor(s, newbuf, x, y, overwrite)
_, _ = s.buf.WriteString(seq)
s.cur.X, s.cur.Y = x, y
}
@@ -682,7 +678,7 @@ func (s *TerminalRenderer) putRange(newbuf *RenderBuffer, oldLine, newLine Line,
var j, same int
for j, same = start, 0; j <= end; j++ {
oldCell, newCell := oldLine.At(j), newLine.At(j)
- if same == 0 && oldCell != nil && oldCell.IsZero() {
+ if same == 0 && oldCell != nil && oldCell.IsZero() && newCell.IsZero() {
continue
}
if cellEqual(oldCell, newCell) {
@@ -941,6 +937,7 @@ func (s *TerminalRenderer) transformLine(newbuf *RenderBuffer, y int) {
for next != nil && next.IsZero() {
n++
oLastCell++
+ next = newLine.At(n + 1)
}
}
@@ -1218,7 +1215,7 @@ func (s *TerminalRenderer) Render(newbuf *RenderBuffer) {
}
}
- if !s.flags.Contains(tFullscreen) && s.scrollHeight < newHeight-1 {
+ if !s.flags.Contains(tFullscreen) && (curWidth != newWidth || curHeight != newHeight) {
s.move(newbuf, 0, newHeight-1)
}
@@ -1258,7 +1255,6 @@ func (s *TerminalRenderer) Resize(width, _ int) {
if s.tabs != nil {
s.tabs.Resize(width)
}
- s.scrollHeight = 0
}
// Position returns the cursor position in the screen buffer after applying any
@@ -1314,9 +1310,8 @@ func notLocal(cols, fx, fy, tx, ty int) bool {
//
// It is safe to call this function with a nil [Buffer]. In that case, it won't
// use any optimizations that require the new buffer such as overwrite.
-func relativeCursorMove(s *TerminalRenderer, newbuf *RenderBuffer, fx, fy, tx, ty int, overwrite, useTabs, useBackspace bool) (string, int) {
+func relativeCursorMove(s *TerminalRenderer, newbuf *RenderBuffer, fx, fy, tx, ty int, overwrite, useTabs, useBackspace bool) string {
var seq strings.Builder
- var scrollHeight int
if newbuf == nil {
overwrite = false // We can't overwrite the current buffer.
}
@@ -1332,10 +1327,8 @@ func relativeCursorMove(s *TerminalRenderer, newbuf *RenderBuffer, fx, fy, tx, t
if cud := ansi.CursorDown(n); yseq == "" || len(cud) < len(yseq) {
yseq = cud
}
- shouldScroll := !s.flags.Contains(tFullscreen) && ty > s.scrollHeight
- if shouldScroll || n < len(yseq) { // n is the cost of using newline characters
+ if !s.flags.Contains(tFullscreen) || n < len(yseq) { // n is the cost of using newline characters
yseq = strings.Repeat("\n", n)
- scrollHeight = ty
if s.flags.Contains(tMapNewline) {
fx = 0
}
@@ -1419,8 +1412,6 @@ func relativeCursorMove(s *TerminalRenderer, newbuf *RenderBuffer, fx, fy, tx, t
if cell != nil && cell.Width > 0 {
ovw += cell.String()
i += cell.Width - 1
- } else {
- ovw += " "
}
}
}
@@ -1462,7 +1453,7 @@ func relativeCursorMove(s *TerminalRenderer, newbuf *RenderBuffer, fx, fy, tx, t
seq.WriteString(xseq)
}
- return seq.String(), scrollHeight
+ return seq.String()
}
// moveCursor moves and returns the cursor movement sequence to move the cursor
@@ -1472,7 +1463,7 @@ func relativeCursorMove(s *TerminalRenderer, newbuf *RenderBuffer, fx, fy, tx, t
//
// It is safe to call this function with a nil [Buffer]. In that case, it won't
// use any optimizations that require the new buffer such as overwrite.
-func moveCursor(s *TerminalRenderer, newbuf *RenderBuffer, x, y int, overwrite bool) (seq string, scrollHeight int) {
+func moveCursor(s *TerminalRenderer, newbuf *RenderBuffer, x, y int, overwrite bool) (seq string) {
fx, fy := s.cur.X, s.cur.Y
if !s.flags.Contains(tRelativeCursor) {
@@ -1490,7 +1481,7 @@ func moveCursor(s *TerminalRenderer, newbuf *RenderBuffer, x, y int, overwrite b
// Method #0: Use [ansi.CUP] if the distance is long.
seq = ansi.CursorPosition(x+1, y+1)
if fx == -1 || fy == -1 || width == -1 || notLocal(width, fx, fy, x, y) {
- return seq, 0
+ return seq
}
}
@@ -1514,32 +1505,29 @@ func moveCursor(s *TerminalRenderer, newbuf *RenderBuffer, x, y int, overwrite b
useBackspace := i&1 != 0
// Method #1: Use local movement sequences.
- nseq1, nscrollHeight1 := relativeCursorMove(s, newbuf, fx, fy, x, y, overwrite, useHardTabs, useBackspace)
+ nseq1 := relativeCursorMove(s, newbuf, fx, fy, x, y, overwrite, useHardTabs, useBackspace)
if (i == 0 && len(seq) == 0) || len(nseq1) < len(seq) {
seq = nseq1
- scrollHeight = max(scrollHeight, nscrollHeight1)
}
// Method #2: Use [ansi.CR] and local movement sequences.
- nseq2, nscrollHeight2 := relativeCursorMove(s, newbuf, 0, fy, x, y, overwrite, useHardTabs, useBackspace)
+ nseq2 := relativeCursorMove(s, newbuf, 0, fy, x, y, overwrite, useHardTabs, useBackspace)
nseq2 = "\r" + nseq2
if len(nseq2) < len(seq) {
seq = nseq2
- scrollHeight = max(scrollHeight, nscrollHeight2)
}
if !s.flags.Contains(tRelativeCursor) {
// Method #3: Use [ansi.CursorHomePosition] and local movement sequences.
- nseq3, nscrollHeight3 := relativeCursorMove(s, newbuf, 0, 0, x, y, overwrite, useHardTabs, useBackspace)
+ nseq3 := relativeCursorMove(s, newbuf, 0, 0, x, y, overwrite, useHardTabs, useBackspace)
nseq3 = ansi.CursorHomePosition + nseq3
if len(nseq3) < len(seq) {
seq = nseq3
- scrollHeight = max(scrollHeight, nscrollHeight3)
}
}
}
- return seq, scrollHeight
+ return seq
}
// xtermCaps returns whether the terminal is xterm-like. This means that the
diff --git a/vendor/github.com/charmbracelet/ultraviolet/terminal_screen.go b/vendor/github.com/charmbracelet/ultraviolet/terminal_screen.go
index 99cd0c773..9fdc32816 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/terminal_screen.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/terminal_screen.go
@@ -29,11 +29,14 @@ type TerminalScreen struct {
keyboardEnhancements *KeyboardEnhancements
bracketedPaste bool
mouseMode MouseMode
+ mouseEncoding MouseEncoding
cursor *Cursor // initial state is cursor hidden
backgroundColor color.Color
foregroundColor color.Color
progressBar *ProgressBar
windowTitle string
+ syncUpdates bool // mode 2026
+ resetTabs bool // DECST8C - reset terminal tabs on start
}
var _ Screen = (*TerminalScreen)(nil)
@@ -42,7 +45,7 @@ var _ Screen = (*TerminalScreen)(nil)
func NewTerminalScreen(w io.Writer, env Environ) *TerminalScreen {
s := &TerminalScreen{}
s.buf = &bytes.Buffer{}
- s.win = NewScreen(0, 0)
+ s.win = NewWindow(0, 0, nil)
s.w = w
s.profile = colorprofile.Detect(w, env)
s.rend = NewTerminalRenderer(s.buf, env)
@@ -52,21 +55,26 @@ func NewTerminalScreen(w io.Writer, env Environ) *TerminalScreen {
s.rbuf = NewRenderBuffer(0, 0)
s.env = env
- if f, err := os.OpenFile("uv_debug.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644); err == nil {
- log.SetOutput(f)
- s.rend.SetLogger(log.Default())
+ if debugFile := env.Getenv("UV_DEBUG"); debugFile != "" {
+ if f, err := os.OpenFile(debugFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644); err == nil {
+ log.SetOutput(f)
+ s.rend.SetLogger(log.Default())
+ }
}
// Configure renderer optimizations based on console settings.
f, ok := w.(term.File)
if ok {
state, err := term.GetState(f.Fd())
- if err == nil {
+ if err == nil || isWindows { // Windows supports tabs and backspace by default, so we can ignore errors here.
useTabs, useBspace := optimizeMovements(state)
if useTabs {
s.rend.SetTabStops(0) // the width will be set after calling [TerminalScreen.Resize]
+ } else {
+ s.rend.SetTabStops(-1)
}
s.rend.SetBackspace(useBspace)
+ s.resetTabs = useTabs
}
}
// XXX: Do we still need map nl to crlf handling in the renderer?
@@ -89,6 +97,34 @@ func (s *TerminalScreen) Bounds() Rectangle {
return s.win.Bounds()
}
+// Width returns the width of the terminal screen.
+//
+// Note that this is not the actual width of the terminal window, but rather
+// the width of the screen we're managing. The actual width of the terminal
+// window can be obtained using [Terminal.GetSize] or by reading the "COLUMNS"
+// environment variable.
+func (s *TerminalScreen) Width() int {
+ return s.win.Width()
+}
+
+// Height returns the height of the terminal screen.
+//
+// Note that this is not the actual height of the terminal window, but rather
+// the height of the screen we're managing. The actual height of the terminal
+// window can be obtained using [Terminal.GetSize] or by reading the "LINES"
+// environment variable.
+func (s *TerminalScreen) Height() int {
+ return s.win.Height()
+}
+
+// StringWidth returns the cell width of the given string using the terminal
+// screen's width method. This accounts for the configured [WidthMethod]
+// (e.g. wcwidth vs grapheme width) so callers don't need to import ansi
+// directly.
+func (s *TerminalScreen) StringWidth(str string) int {
+ return s.win.WidthMethod().StringWidth(str)
+}
+
// WidthMethod returns the width method used by the terminal screen.
func (s *TerminalScreen) WidthMethod() WidthMethod {
return s.win.WidthMethod()
@@ -109,13 +145,12 @@ func (s *TerminalScreen) SetColorProfile(profile colorprofile.Profile) {
// Resize resizes the terminal screen to the specified width and height,
// updating the render buffer and renderer accordingly.
-func (s *TerminalScreen) Resize(width, height int) error {
+func (s *TerminalScreen) Resize(width, height int) {
s.win.Resize(width, height)
s.rbuf.Resize(width, height)
s.rend.Resize(width, height)
s.rend.Erase()
s.rbuf.Touched = nil
- return nil
}
// Display clears the screen and draws the given [Drawable] onto the terminal
@@ -128,9 +163,7 @@ func (s *TerminalScreen) Display(d Drawable) error {
s.win.Clear()
d.Draw(s, s.win.Bounds())
}
- if err := s.Render(); err != nil {
- return err
- }
+ s.Render()
return s.Flush()
}
@@ -139,7 +172,7 @@ func (s *TerminalScreen) Display(d Drawable) error {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) Render() error {
+func (s *TerminalScreen) Render() {
for y := 0; y < s.win.Height(); y++ {
for x := 0; x < s.win.Width(); {
cell := s.win.CellAt(x, y)
@@ -156,7 +189,7 @@ func (s *TerminalScreen) Render() error {
}
}
s.rend.Render(s.rbuf)
- return s.rend.Flush()
+ _ = s.rend.Flush()
}
// Flush writes any pending output to the underlying writer.
@@ -176,7 +209,38 @@ func (s *TerminalScreen) Flush() error {
}
}
- _, err := s.w.Write(s.buf.Bytes())
+ var buf bytes.Buffer
+ buf.Grow(s.buf.Len())
+
+ if s.buf.Len() > 0 {
+ if s.syncUpdates {
+ buf.Grow(len(ansi.SetModeSynchronizedOutput) + len(ansi.ResetModeSynchronizedOutput))
+
+ // If synchronized updates are enabled, we need to wrap the output in
+ // the appropriate control sequences to ensure that the terminal treats
+ // it as a single atomic update. This is necessary to prevent flickering
+ // and other visual artifacts that can occur when multiple updates are sent
+ // separately.
+ buf.WriteString(ansi.SetModeSynchronizedOutput)
+ } else if s.cursor != nil && !s.cursor.Hidden {
+ buf.Grow(len(ansi.HideCursor) + len(ansi.ShowCursor))
+
+ // If synchronized updates are not enabled, we need to ensure that
+ // the cursor is hidden before writing any output to prevent
+ // unwanted cursor visual artifacts.
+ buf.WriteString(ansi.HideCursor)
+ }
+
+ buf.Write(s.buf.Bytes())
+
+ if s.syncUpdates {
+ buf.WriteString(ansi.ResetModeSynchronizedOutput)
+ } else if s.cursor != nil && !s.cursor.Hidden {
+ buf.WriteString(ansi.ShowCursor)
+ }
+ }
+
+ _, err := s.w.Write(buf.Bytes())
if err != nil {
return err
}
@@ -190,7 +254,7 @@ func (s *TerminalScreen) Flush() error {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) EnterAltScreen() error {
+func (s *TerminalScreen) EnterAltScreen() {
var sb strings.Builder
sb.WriteString(ansi.SetModeAltScreenSaveCursor)
if s.cursor == nil || s.cursor.Hidden {
@@ -199,12 +263,9 @@ func (s *TerminalScreen) EnterAltScreen() error {
sb.WriteString(ansi.ShowCursor)
}
if s.keyboardEnhancements != nil {
- _ = EncodeKeyboardEnhancements(&sb, s.keyboardEnhancements)
- }
- _, err := s.buf.WriteString(sb.String())
- if err != nil {
- return err
+ EncodeKeyboardEnhancements(&sb, s.keyboardEnhancements)
}
+ s.buf.WriteString(sb.String())
if !s.altScreen {
s.rend.SaveCursor()
@@ -213,8 +274,6 @@ func (s *TerminalScreen) EnterAltScreen() error {
s.rend.SetRelativeCursor(false)
s.altScreen = true
}
-
- return nil
}
// ExitAltScreen switches the terminal back to the main screen buffer, restoring
@@ -222,7 +281,7 @@ func (s *TerminalScreen) EnterAltScreen() error {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) ExitAltScreen() error {
+func (s *TerminalScreen) ExitAltScreen() {
var sb strings.Builder
sb.WriteString(ansi.ResetModeAltScreenSaveCursor)
if s.cursor == nil || s.cursor.Hidden {
@@ -231,12 +290,9 @@ func (s *TerminalScreen) ExitAltScreen() error {
sb.WriteString(ansi.ShowCursor)
}
if s.keyboardEnhancements != nil {
- _ = EncodeKeyboardEnhancements(&sb, s.keyboardEnhancements)
- }
- _, err := s.buf.WriteString(sb.String())
- if err != nil {
- return err
+ EncodeKeyboardEnhancements(&sb, s.keyboardEnhancements)
}
+ s.buf.WriteString(sb.String())
if s.altScreen {
s.rend.RestoreCursor()
@@ -245,8 +301,6 @@ func (s *TerminalScreen) ExitAltScreen() error {
s.rend.SetRelativeCursor(true)
s.altScreen = false
}
-
- return nil
}
// AltScreen returns whether the terminal is currently in the alternate screen
@@ -259,36 +313,24 @@ func (s *TerminalScreen) AltScreen() bool {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) HideCursor() error {
- _, err := s.buf.WriteString(ansi.HideCursor)
- if err != nil {
- return err
- }
-
+func (s *TerminalScreen) HideCursor() {
+ s.buf.WriteString(ansi.HideCursor)
if s.cursor != nil {
s.cursor.Hidden = true
}
-
- return nil
}
// ShowCursor shows the terminal cursor.
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) ShowCursor() error {
- _, err := s.buf.WriteString(ansi.ShowCursor)
- if err != nil {
- return err
- }
-
+func (s *TerminalScreen) ShowCursor() {
+ s.buf.WriteString(ansi.ShowCursor)
if s.cursor != nil {
s.cursor.Hidden = false
} else {
s.cursor = NewCursor(-1, -1)
}
-
- return nil
}
// CursorVisible returns whether the terminal cursor is currently visible.
@@ -301,7 +343,7 @@ func (s *TerminalScreen) CursorVisible() bool {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) SetCursorPosition(x, y int) error {
+func (s *TerminalScreen) SetCursorPosition(x, y int) {
if s.cursor == nil {
s.cursor = NewCursor(x, y)
s.cursor.Hidden = true
@@ -309,7 +351,6 @@ func (s *TerminalScreen) SetCursorPosition(x, y int) error {
s.cursor.X = x
s.cursor.Y = y
}
- return nil
}
// CursorPosition returns the last set cursor position of the terminal. If the
@@ -328,18 +369,13 @@ func (s *TerminalScreen) CursorPosition() (x, y int) {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) SetCursorStyle(shape CursorShape, blink bool) error {
- if err := EncodeCursorStyle(s.buf, shape, blink); err != nil {
- return err
- }
-
+func (s *TerminalScreen) SetCursorStyle(shape CursorShape, blink bool) {
+ EncodeCursorStyle(s.buf, shape, blink)
if s.cursor == nil {
s.cursor = NewCursor(-1, -1)
}
s.cursor.Shape = shape
s.cursor.Blink = blink
-
- return nil
}
// CursorStyle returns the current style of the terminal cursor.
@@ -354,17 +390,12 @@ func (s *TerminalScreen) CursorStyle() (shape CursorShape, blink bool) {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) SetCursorColor(c color.Color) error {
- if err := EncodeCursorColor(s.buf, c); err != nil {
- return err
- }
-
+func (s *TerminalScreen) SetCursorColor(c color.Color) {
+ EncodeCursorColor(s.buf, c)
if s.cursor == nil {
s.cursor = NewCursor(-1, -1)
}
s.cursor.Color = c
-
- return nil
}
// CursorColor returns the current color of the terminal cursor.
@@ -382,14 +413,9 @@ func (s *TerminalScreen) CursorColor() color.Color {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) SetBackgroundColor(c color.Color) error {
- if err := EncodeBackgroundColor(s.buf, c); err != nil {
- return err
- }
-
+func (s *TerminalScreen) SetBackgroundColor(c color.Color) {
+ EncodeBackgroundColor(s.buf, c)
s.backgroundColor = c
-
- return nil
}
// BackgroundColor returns the current background color of the terminal.
@@ -404,14 +430,9 @@ func (s *TerminalScreen) BackgroundColor() color.Color {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) SetForegroundColor(c color.Color) error {
- if err := EncodeForegroundColor(s.buf, c); err != nil {
- return err
- }
-
+func (s *TerminalScreen) SetForegroundColor(c color.Color) {
+ EncodeForegroundColor(s.buf, c)
s.foregroundColor = c
-
- return nil
}
// ForegroundColor returns the current foreground color of the terminal.
@@ -427,30 +448,18 @@ func (s *TerminalScreen) ForegroundColor() color.Color {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) EnableBracketedPaste() error {
- _, err := s.buf.WriteString(ansi.SetModeBracketedPaste)
- if err != nil {
- return err
- }
-
+func (s *TerminalScreen) EnableBracketedPaste() {
+ s.buf.WriteString(ansi.SetModeBracketedPaste)
s.bracketedPaste = true
-
- return nil
}
// DisableBracketedPaste disables bracketed paste mode.
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) DisableBracketedPaste() error {
- _, err := s.buf.WriteString(ansi.ResetModeBracketedPaste)
- if err != nil {
- return err
- }
-
+func (s *TerminalScreen) DisableBracketedPaste() {
+ s.buf.WriteString(ansi.ResetModeBracketedPaste)
s.bracketedPaste = false
-
- return nil
}
// BracketedPaste returns whether bracketed paste mode is currently enabled.
@@ -458,38 +467,60 @@ func (s *TerminalScreen) BracketedPaste() bool {
return s.bracketedPaste
}
-// SetMouseMode sets the mouse mode for the terminal, allowing applications to
-// receive mouse events.
+// SetSynchronizedUpdates sets whether to use synchronized updates (mode 2026),
+// which allows applications to batch updates to the terminal screen and flush
+// them all at once for improved performance.
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) SetMouseMode(mode MouseMode) error {
- if err := EncodeMouseMode(s.buf, mode); err != nil {
- return err
- }
+func (s *TerminalScreen) SetSynchronizedUpdates(enabled bool) {
+ s.syncUpdates = enabled
+}
- s.mouseMode = mode
+// SynchronizedUpdates returns whether synchronized updates (mode 2026) are
+// currently enabled.
+func (s *TerminalScreen) SynchronizedUpdates() bool {
+ return s.syncUpdates
+}
- return nil
+// SetMouseMode sets the mouse tracking mode for the terminal, allowing
+// applications to receive mouse events.
+//
+// The changes can be committed to the underlying writer by calling the
+// [TerminalScreen.Flush] method.
+func (s *TerminalScreen) SetMouseMode(mode MouseMode) {
+ EncodeMouseMode(s.buf, mode)
+ s.mouseMode = mode
}
-// MouseMode returns the current mouse mode of the terminal.
+// MouseMode returns the current mouse tracking mode of the terminal.
func (s *TerminalScreen) MouseMode() MouseMode {
return s.mouseMode
}
-// SetWindowTitle sets the title of the terminal window.
+// SetMouseEncoding sets the mouse encoding for the terminal.
+// The encoding determines how mouse coordinates and buttons are reported.
+// This is only meaningful when mouse tracking is enabled via [TerminalScreen.SetMouseMode].
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) SetWindowTitle(title string) error {
- if err := EncodeWindowTitle(s.buf, title); err != nil {
- return err
- }
+func (s *TerminalScreen) SetMouseEncoding(enc MouseEncoding) {
+ EncodeMouseEncoding(s.buf, enc)
+ s.mouseEncoding = enc
+}
- s.windowTitle = title
+// MouseEncoding returns the current mouse encoding of the terminal.
+func (s *TerminalScreen) MouseEncoding() MouseEncoding {
+ return s.mouseEncoding
+}
- return nil
+// SetWindowTitle sets the title of the terminal window.
+//
+// The changes can be committed to the underlying writer by calling the
+// [TerminalScreen.Flush] method.
+func (s *TerminalScreen) SetWindowTitle(title string) {
+ EncodeWindowTitle(s.buf, title)
+ s.windowTitle = title
}
// WindowTitle returns the current title of the terminal window.
@@ -502,14 +533,9 @@ func (s *TerminalScreen) WindowTitle() string {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) SetKeyboardEnhancements(enh *KeyboardEnhancements) error {
- if err := EncodeKeyboardEnhancements(s.buf, enh); err != nil {
- return err
- }
-
+func (s *TerminalScreen) SetKeyboardEnhancements(enh *KeyboardEnhancements) {
+ EncodeKeyboardEnhancements(s.buf, enh)
s.keyboardEnhancements = enh
-
- return nil
}
// KeyboardEnhancements returns the current keyboard enhancements of the terminal.
@@ -524,14 +550,9 @@ func (s *TerminalScreen) KeyboardEnhancements() *KeyboardEnhancements {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) SetProgressBar(pb *ProgressBar) error {
- if err := EncodeProgressBar(s.buf, pb); err != nil {
- return err
- }
-
+func (s *TerminalScreen) SetProgressBar(pb *ProgressBar) {
+ EncodeProgressBar(s.buf, pb)
s.progressBar = pb
-
- return nil
}
// ProgressBar returns the current progress bar of the terminal.
@@ -547,7 +568,7 @@ func (s *TerminalScreen) ProgressBar() *ProgressBar {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) Reset() error {
+func (s *TerminalScreen) Reset() {
var sb strings.Builder
hasKeyboardEnhancements := s.keyboardEnhancements != nil
@@ -562,7 +583,10 @@ func (s *TerminalScreen) Reset() error {
sb.WriteString(ansi.KittyKeyboard(0, 1))
}
if s.mouseMode != MouseModeNone {
- _ = EncodeMouseMode(&sb, MouseModeNone)
+ EncodeMouseMode(&sb, MouseModeNone)
+ }
+ if s.mouseEncoding != MouseEncodingLegacy {
+ EncodeMouseEncoding(&sb, MouseEncodingLegacy)
}
if s.cursor == nil || !s.cursor.Hidden {
@@ -592,10 +616,7 @@ func (s *TerminalScreen) Reset() error {
sb.WriteString(ansi.ResetProgressBar)
}
- _, err := s.buf.WriteString(sb.String())
- if err != nil {
- return err
- }
+ s.buf.WriteString(sb.String())
// Go to the bottom of the screen.
// We need to go to the bottom of the screen regardless of whether
@@ -608,8 +629,6 @@ func (s *TerminalScreen) Reset() error {
//
// Note that both [TerminalScreen.rend] writes to [TerminalScreen.buf].
s.rend.MoveTo(0, s.win.Height()-1)
-
- return nil
}
// Restore restores the terminal screen to its previous state, applying any
@@ -617,9 +636,12 @@ func (s *TerminalScreen) Reset() error {
//
// The changes can be committed to the underlying writer by calling the
// [TerminalScreen.Flush] method.
-func (s *TerminalScreen) Restore() error {
+func (s *TerminalScreen) Restore() {
var sb strings.Builder
+ if s.resetTabs {
+ sb.WriteString(ansi.SetTabEvery8Columns)
+ }
if s.altScreen {
sb.WriteString(ansi.SetModeAltScreenSaveCursor)
}
@@ -630,53 +652,39 @@ func (s *TerminalScreen) Restore() error {
sb.WriteString(ansi.HideCursor)
}
if s.keyboardEnhancements != nil {
- if err := EncodeKeyboardEnhancements(&sb, s.keyboardEnhancements); err != nil {
- return err
- }
+ EncodeKeyboardEnhancements(&sb, s.keyboardEnhancements)
}
if s.mouseMode != MouseModeNone {
- if err := EncodeMouseMode(&sb, s.mouseMode); err != nil {
- return err
- }
+ EncodeMouseMode(&sb, s.mouseMode)
+ }
+ if s.mouseEncoding != MouseEncodingLegacy {
+ EncodeMouseEncoding(&sb, s.mouseEncoding)
}
if s.cursor != nil {
if s.cursor.Shape != CursorBlock || !s.cursor.Blink {
- _ = EncodeCursorStyle(&sb, s.cursor.Shape, s.cursor.Blink)
+ EncodeCursorStyle(&sb, s.cursor.Shape, s.cursor.Blink)
}
if s.cursor.Color != nil {
- if err := EncodeCursorColor(&sb, s.cursor.Color); err != nil {
- return err
- }
+ EncodeCursorColor(&sb, s.cursor.Color)
}
}
if s.backgroundColor != nil {
- if err := EncodeBackgroundColor(&sb, s.backgroundColor); err != nil {
- return err
- }
+ EncodeBackgroundColor(&sb, s.backgroundColor)
}
if s.foregroundColor != nil {
- if err := EncodeForegroundColor(&sb, s.foregroundColor); err != nil {
- return err
- }
+ EncodeForegroundColor(&sb, s.foregroundColor)
}
if s.bracketedPaste {
sb.WriteString(ansi.SetModeBracketedPaste)
}
if s.windowTitle != "" {
- if err := EncodeWindowTitle(&sb, s.windowTitle); err != nil {
- return err
- }
+ EncodeWindowTitle(&sb, s.windowTitle)
}
if s.progressBar != nil && s.progressBar.State != ProgressBarNone {
- if err := EncodeProgressBar(&sb, s.progressBar); err != nil {
- return err
- }
+ EncodeProgressBar(&sb, s.progressBar)
}
- _, err := s.buf.WriteString(sb.String())
- if err != nil {
- return err
- }
+ s.buf.WriteString(sb.String())
// This needs to be called after restoring the screen state and writing to
// the buffer.
@@ -686,14 +694,10 @@ func (s *TerminalScreen) Restore() error {
// that the restore commands are included in the render output. This
// ensures that the screen is properly restored before rendering any
// changes.
- if err := s.Render(); err != nil {
- return err
- }
+ s.Render()
// Cursor position will be restored by the caller after calling
// [TerminalScreen.Flush].
-
- return nil
}
// Write writes data to the underlying buffer queuing it for output.
diff --git a/vendor/github.com/charmbracelet/ultraviolet/terminal_unix.go b/vendor/github.com/charmbracelet/ultraviolet/terminal_unix.go
index 9caf0a74c..04ed3e3f6 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/terminal_unix.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/terminal_unix.go
@@ -4,10 +4,6 @@
package uv
import (
- "os"
- "os/signal"
- "syscall"
-
"github.com/charmbracelet/x/term"
"github.com/charmbracelet/x/termios"
)
@@ -58,21 +54,3 @@ func getSize(inTty, outTty term.File) (w, h int, err error) {
func optimizeMovements(state *term.State) (useTabs, useBspace bool) {
return supportsHardTabs(uint64(state.Oflag)), supportsBackspace(uint64(state.Lflag)) //nolint:unconvert,nolintlint
}
-
-func startWinch(inTty, outTty term.File) (c chan os.Signal, err error) {
- for _, f := range []term.File{inTty, outTty} {
- if f == nil {
- continue
- }
- if term.IsTerminal(f.Fd()) {
- c = make(chan os.Signal)
- signal.Notify(c, syscall.SIGWINCH)
- return c, nil
- }
- }
- return nil, ErrNotTerminal
-}
-
-func stopWinch(c chan os.Signal) {
- signal.Stop(c)
-}
diff --git a/vendor/github.com/charmbracelet/ultraviolet/terminal_windows.go b/vendor/github.com/charmbracelet/ultraviolet/terminal_windows.go
index 73a39d7bd..a899ba486 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/terminal_windows.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/terminal_windows.go
@@ -5,7 +5,6 @@ package uv
import (
"fmt"
- "os"
"github.com/charmbracelet/x/term"
"golang.org/x/sys/windows"
@@ -76,10 +75,3 @@ func supportsBackspace(uint64) bool {
func supportsHardTabs(uint64) bool {
return true
}
-
-func startWinch(_, _ term.File) (chan os.Signal, error) {
- return nil, ErrPlatformNotSupported
-}
-
-func stopWinch(chan os.Signal) {
-}
diff --git a/vendor/github.com/charmbracelet/ultraviolet/uv.go b/vendor/github.com/charmbracelet/ultraviolet/uv.go
index 546d6f332..dac088a9c 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/uv.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/uv.go
@@ -4,7 +4,6 @@ import (
"fmt"
"image/color"
"io"
- "strings"
"github.com/charmbracelet/x/ansi"
"github.com/lucasb-eyer/go-colorful"
@@ -153,6 +152,25 @@ type KeyboardEnhancements struct {
// [KeyPressEvent] with the [Key.IsRepeat] field set indicating that this
// is a it's part of a key repeat sequence.
ReportEventTypes bool
+
+ // ReportAlternateKeys requests the terminal to report alternate key values
+ // in addition to the main ones.
+ // Note that only key events represented as escape codes will affected by
+ // this enhancement.
+ ReportAlternateKeys bool
+
+ // ReportAllKeysAsEscapeCodes requests the terminal to report all key
+ // events, including plain text keys, as escape codes.
+ // When this is enabled, text won't be sent as plain text but instead as
+ // escape codes that encode the key value and modifiers.
+ ReportAllKeysAsEscapeCodes bool
+
+ // ReportAssociatedText requests the terminal to report the text associated
+ // with key events.
+ // Note that this is an enhancement to
+ // [KeyboardEnhancements.ReportAllKeysAsEscapeCodes] and only has an effect
+ // if that is enabled.
+ ReportAssociatedText bool
}
// NewKeyboardEnhancements returns a new [KeyboardEnhancements] with the given
@@ -182,6 +200,15 @@ func (ke KeyboardEnhancements) Flags() int {
if ke.ReportEventTypes {
bits |= ansi.KittyReportEventTypes
}
+ if ke.ReportAlternateKeys {
+ bits |= ansi.KittyReportAlternateKeys
+ }
+ if ke.ReportAllKeysAsEscapeCodes {
+ bits |= ansi.KittyReportAllKeysAsEscapeCodes
+ }
+ if ke.ReportAssociatedText {
+ bits |= ansi.KittyReportAssociatedKeys
+ }
return bits
}
@@ -274,29 +301,26 @@ func EncodeBracketedPaste(w io.Writer, enable bool) error {
return nil
}
-// EncodeMouseMode encodes the mouse mode to the given writer.
+// EncodeMouseMode encodes the mouse tracking mode to the given writer.
func EncodeMouseMode(w io.Writer, mode MouseMode) error {
- var sb strings.Builder
+ var seq string
switch mode {
case MouseModeNone:
- sb.WriteString(ansi.ResetModeMouseNormal)
- sb.WriteString(ansi.ResetModeMouseButtonEvent)
- sb.WriteString(ansi.ResetModeMouseAnyEvent)
- sb.WriteString(ansi.ResetModeMouseExtSgr)
+ seq = ansi.ResetModeMouseX10 +
+ ansi.ResetModeMouseNormal +
+ ansi.ResetModeMouseButtonEvent +
+ ansi.ResetModeMouseAnyEvent
+ case MouseModePress:
+ seq = ansi.SetModeMouseX10
case MouseModeClick:
- sb.WriteString(ansi.SetModeMouseNormal)
- sb.WriteString(ansi.SetModeMouseExtSgr)
+ seq = ansi.SetModeMouseNormal
case MouseModeDrag:
- sb.WriteString(ansi.SetModeMouseButtonEvent)
- sb.WriteString(ansi.SetModeMouseExtSgr)
+ seq = ansi.SetModeMouseButtonEvent
case MouseModeMotion:
- sb.WriteString(ansi.SetModeMouseAnyEvent)
- sb.WriteString(ansi.SetModeMouseExtSgr)
- default:
- return fmt.Errorf("invalid mouse mode: %d", mode)
+ seq = ansi.SetModeMouseAnyEvent
}
- _, err := io.WriteString(w, sb.String())
+ _, err := io.WriteString(w, seq) //nolint:errcheck
if err != nil {
return fmt.Errorf("failed to set mouse mode: %w", err)
}
@@ -304,6 +328,29 @@ func EncodeMouseMode(w io.Writer, mode MouseMode) error {
return nil
}
+// EncodeMouseEncoding encodes the mouse encoding mode to the given writer.
+// When enc is [MouseEncodingLegacy], all extended encodings are reset.
+func EncodeMouseEncoding(w io.Writer, enc MouseEncoding) error {
+ var seq string
+ switch enc {
+ case MouseEncodingLegacy:
+ seq = ansi.ResetModeMouseExtSgr +
+ ansi.ResetModeMouseExtUrxvt +
+ ansi.ResetModeMouseExtSgrPixel
+ case MouseEncodingSGR:
+ seq = ansi.SetModeMouseExtSgr
+ case MouseEncodingSGRPixel:
+ seq = ansi.SetModeMouseExtSgrPixel
+ }
+
+ _, err := io.WriteString(w, seq) //nolint:errcheck
+ if err != nil {
+ return fmt.Errorf("failed to set mouse encoding: %w", err)
+ }
+
+ return nil
+}
+
// EncodeProgressBar encodes the progress bar to the given writer.
func EncodeProgressBar(w io.Writer, pb *ProgressBar) error {
if pb == nil {
diff --git a/vendor/github.com/charmbracelet/ultraviolet/window.go b/vendor/github.com/charmbracelet/ultraviolet/window.go
index 21c707a84..e90bef28b 100644
--- a/vendor/github.com/charmbracelet/ultraviolet/window.go
+++ b/vendor/github.com/charmbracelet/ultraviolet/window.go
@@ -1,8 +1,6 @@
package uv
-import (
- "github.com/charmbracelet/x/ansi"
-)
+import "github.com/charmbracelet/x/ansi"
// Window represents a rectangular area on the screen. It can be a root window
// with no parent, or a sub-window with a parent window. A window can have its
@@ -105,11 +103,26 @@ func (w *Window) NewView(x, y, width, height int) *Window {
return newWindow(w, x, y, width, height, w.method, true)
}
-// NewScreen creates a new root [Window] with the given size and width method.
+// NewWindow creates a new root [Window] with the given size and width method.
+// If the method is nil, it defaults to [ansi.WcWidth].
+//
+// The [WidthMethod] is used to calculate the width of characters in the
+// window, which is important for correctly rendering text, especially when
+// dealing with wide characters, combining characters, emojis, and other
+// Unicode characters that may have varying widths.
+//
+// Use [ansi.WcWidth] as the default width method, which is a common
+// implementation that handles a wide range of Unicode characters according to
+// the Unicode Standard. Use [ansi.GraphemeWidth] if you know that your
+// terminal supports grapheme clusters and proper rendering of combining
+// characters and emojis, and you want to ensure that the width calculations
+// are based on grapheme clusters rather than individual code points.
//
// This will panic if width or height is negative.
-func NewScreen(width, height int) *Window {
- var method WidthMethod = ansi.WcWidth
+func NewWindow(width, height int, method WidthMethod) *Window {
+ if method == nil {
+ method = ansi.WcWidth
+ }
return newWindow(nil, 0, 0, width, height, &method, false)
}
diff --git a/vendor/github.com/charmbracelet/x/ansi/width.go b/vendor/github.com/charmbracelet/x/ansi/width.go
index fd680cfdc..a4735b8ac 100644
--- a/vendor/github.com/charmbracelet/x/ansi/width.go
+++ b/vendor/github.com/charmbracelet/x/ansi/width.go
@@ -87,7 +87,7 @@ func stringWidth(m Method, s string) int {
for i := 0; i < len(s); i++ {
state, action := parser.Table.Transition(pstate, s[i])
- if state == parser.Utf8State {
+ if action == parser.PrintAction || state == parser.Utf8State {
cluster, w := FirstGraphemeCluster(s[i:], m)
width += w
diff --git a/vendor/github.com/cilium/ebpf/CODEOWNERS b/vendor/github.com/cilium/ebpf/CODEOWNERS
index bd0a61158..be87f4225 100644
--- a/vendor/github.com/cilium/ebpf/CODEOWNERS
+++ b/vendor/github.com/cilium/ebpf/CODEOWNERS
@@ -1,11 +1,17 @@
* @cilium/ebpf-lib-maintainers
-features/ @rgo3
-link/ @mmat11
+/features/ @rgo3
+/link/ @mmat11 @rgo3
-perf/ @florianl
-ringbuf/ @florianl
+/perf/ @florianl
+/ringbuf/ @florianl
-btf/ @dylandreimerink
+/btf/ @dylandreimerink
-docs/ @ti-mo
+/docs/ @cilium/ebpf-go-reviewers @ti-mo
+
+# Windows specific code.
+/docs/**/windows*.md @cilium/ebpf-go-windows-reviewers
+/internal/efw @cilium/ebpf-go-windows-reviewers
+windows/ @cilium/ebpf-go-windows-reviewers # Folders
+*windows*.go @cilium/ebpf-go-windows-reviewers # Go code
diff --git a/vendor/github.com/cilium/ebpf/Makefile b/vendor/github.com/cilium/ebpf/Makefile
index 45462e8d5..d697871e6 100644
--- a/vendor/github.com/cilium/ebpf/Makefile
+++ b/vendor/github.com/cilium/ebpf/Makefile
@@ -1,9 +1,9 @@
# The development version of clang is distributed as the 'clang' binary,
# while stable/released versions have a version number attached.
# Pin the default clang to a stable version.
-CLANG ?= clang-20
-STRIP ?= llvm-strip-20
-OBJCOPY ?= llvm-objcopy-20
+CLANG ?= clang-22
+STRIP ?= llvm-strip-22
+OBJCOPY ?= llvm-objcopy-22
CFLAGS := -O2 -g -Wall -Werror -mcpu=v2 $(CFLAGS)
CI_KERNEL_URL ?= https://github.com/cilium/ci-kernels/raw/master/
@@ -11,23 +11,41 @@ CI_KERNEL_URL ?= https://github.com/cilium/ci-kernels/raw/master/
# Obtain an absolute path to the directory of the Makefile.
# Assume the Makefile is in the root of the repository.
REPODIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
-UIDGID := $(shell stat -c '%u:%g' ${REPODIR})
# Prefer podman if installed, otherwise use docker.
# Note: Setting the var at runtime will always override.
-CONTAINER_ENGINE ?= $(if $(shell command -v podman), podman, docker)
-CONTAINER_RUN_ARGS ?= $(if $(filter ${CONTAINER_ENGINE}, podman), \
- --log-driver=none \
- -v "$(shell go env GOCACHE)":/root/.cache/go-build \
- -v "$(shell go env GOMODCACHE)":/go/pkg/mod, --user "${UIDGID}")
+CONTAINER_ENGINE ?= $(if $(shell command -v podman),podman,docker)
+
+# Configure container runtime arguments based on the container engine.
+CONTAINER_RUN_ARGS := \
+ --env MAKEFLAGS \
+ --env BPF2GO_CC="$(CLANG)" \
+ --env BPF2GO_CFLAGS="$(CFLAGS)" \
+ --env HOME=/tmp \
+ -v "${REPODIR}":/ebpf -w /ebpf \
+ -v "$(shell go env GOCACHE)":/tmp/.cache/go-build \
+ -v "$(shell go env GOPATH)":/go \
+ -v "$(shell go env GOMODCACHE)":/go/pkg/mod
+
+ifeq ($(CONTAINER_ENGINE), podman)
+CONTAINER_RUN_ARGS += --log-driver=none --security-opt label=disable
+else
+CONTAINER_RUN_ARGS += --user "$(shell stat -c '%u:%g' ${REPODIR})"
+endif
IMAGE := $(shell cat ${REPODIR}/testdata/docker/IMAGE)
VERSION := $(shell cat ${REPODIR}/testdata/docker/VERSION)
+TARGETS_EL := \
+ testdata/linked1 \
+ testdata/linked2 \
+ testdata/linked
+
TARGETS := \
testdata/loader-clang-14 \
testdata/loader-clang-17 \
testdata/loader-$(CLANG) \
+ testdata/loader_nobtf \
testdata/manyprogs \
testdata/btf_map_init \
testdata/invalid_map \
@@ -49,6 +67,8 @@ TARGETS := \
testdata/constants \
testdata/errors \
testdata/variables \
+ testdata/arena \
+ testdata/struct_ops \
btf/testdata/relocs \
btf/testdata/relocs_read \
btf/testdata/relocs_read_tgt \
@@ -56,27 +76,27 @@ TARGETS := \
btf/testdata/tags \
cmd/bpf2go/testdata/minimal
-.PHONY: all clean container-all container-shell generate
+HEADERS := $(wildcard testdata/*.h)
+
+.PHONY: all clean godirs container-all container-shell generate format
-.DEFAULT_TARGET = container-all
+.DEFAULT_GOAL = container-all
+
+# Make sure Go-related dirs exist before starting containerized builds,
+# otherwise podman will fail to start.
+godirs:
+ mkdir -p $(shell go env GOCACHE) $(shell go env GOPATH) $(shell go env GOMODCACHE)
# Build all ELF binaries using a containerized LLVM toolchain.
-container-all:
+container-all: godirs
+${CONTAINER_ENGINE} run --rm -ti ${CONTAINER_RUN_ARGS} \
- -v "${REPODIR}":/ebpf -w /ebpf --env MAKEFLAGS \
- --env HOME="/tmp" \
- --env BPF2GO_CC="$(CLANG)" \
- --env BPF2GO_CFLAGS="$(CFLAGS)" \
"${IMAGE}:${VERSION}" \
- make all
+ $(MAKE) all
# (debug) Drop the user into a shell inside the container as root.
# Set BPF2GO_ envs to make 'make generate' just work.
-container-shell:
+container-shell: godirs
${CONTAINER_ENGINE} run --rm -ti ${CONTAINER_RUN_ARGS} \
- -v "${REPODIR}":/ebpf -w /ebpf \
- --env BPF2GO_CC="$(CLANG)" \
- --env BPF2GO_CFLAGS="$(CFLAGS)" \
"${IMAGE}:${VERSION}"
clean:
@@ -86,32 +106,44 @@ clean:
format:
find . -type f -name "*.c" | xargs clang-format -i
-all: format $(addsuffix -el.elf,$(TARGETS)) $(addsuffix -eb.elf,$(TARGETS)) generate
+all: format testdata update-external-deps
ln -srf testdata/loader-$(CLANG)-el.elf testdata/loader-el.elf
ln -srf testdata/loader-$(CLANG)-eb.elf testdata/loader-eb.elf
+ $(MAKE) generate
generate:
- go generate -run "internal/cmd/gentypes" ./...
- go generate -skip "internal/cmd/gentypes" ./...
+ go generate -run "gentypes" ./...
+ go generate -run "stringer" ./...
+ go generate -skip "(gentypes|stringer)" ./...
+
+testdata: $(addsuffix -el.elf,$(TARGETS)) $(addsuffix -eb.elf,$(TARGETS)) $(addsuffix -el.elf,$(TARGETS_EL))
-testdata/loader-%-el.elf: testdata/loader.c
+testdata/loader-%-el.elf: testdata/loader.c $(HEADERS)
$* $(CFLAGS) -target bpfel -c $< -o $@
$(STRIP) -g $@
-testdata/loader-%-eb.elf: testdata/loader.c
+testdata/loader-%-eb.elf: testdata/loader.c $(HEADERS)
$* $(CFLAGS) -target bpfeb -c $< -o $@
$(STRIP) -g $@
-%-el.elf: %.c
+testdata/loader_nobtf-el.elf: testdata/loader.c $(HEADERS)
+ $(CLANG) $(CFLAGS) -g0 -D__NOBTF__ -target bpfel -c $< -o $@
+
+testdata/loader_nobtf-eb.elf: testdata/loader.c $(HEADERS)
+ $(CLANG) $(CFLAGS) -g0 -D__NOBTF__ -target bpfeb -c $< -o $@
+
+%-el.elf: %.c $(HEADERS)
$(CLANG) $(CFLAGS) -target bpfel -c $< -o $@
$(STRIP) -g $@
-%-eb.elf : %.c
+%-eb.elf: %.c $(HEADERS)
$(CLANG) $(CFLAGS) -target bpfeb -c $< -o $@
$(STRIP) -g $@
-.PHONY: update-kernel-deps
-update-kernel-deps: export KERNEL_VERSION?=6.8
-update-kernel-deps:
- ./testdata/sh/update-kernel-deps.sh
- $(MAKE) container-all
+testdata/linked-el.elf: testdata/linked1-el.elf testdata/linked2-el.elf
+ bpftool gen object $@ $^
+
+.PHONY: update-external-deps
+update-external-deps:
+ ./scripts/update-kernel-deps.sh
+ ./scripts/update-efw-deps.sh
diff --git a/vendor/github.com/cilium/ebpf/README.md b/vendor/github.com/cilium/ebpf/README.md
index d471ae769..01a154c61 100644
--- a/vendor/github.com/cilium/ebpf/README.md
+++ b/vendor/github.com/cilium/ebpf/README.md
@@ -59,10 +59,11 @@ This library includes the following packages:
* A version of Go that is [supported by
upstream](https://golang.org/doc/devel/release.html#policy)
-* Linux: CI is run against kernel.org LTS releases. >= 4.4 should work but EOL'ed
+* Linux (amd64, arm64): CI is run against kernel.org LTS releases. >= 4.4 should work but EOL'ed
versions are not supported.
-* Windows: CI is run against Windows Server 2022. Only the latest eBPF for Windows
+* Windows (amd64): CI is run against Windows Server 2022. Only the latest eBPF for Windows
release is supported.
+* Other architectures are best effort. 32bit arches are not supported.
## License
diff --git a/vendor/github.com/cilium/ebpf/asm/alu.go b/vendor/github.com/cilium/ebpf/asm/alu.go
index 282233d32..a4ae72212 100644
--- a/vendor/github.com/cilium/ebpf/asm/alu.go
+++ b/vendor/github.com/cilium/ebpf/asm/alu.go
@@ -1,6 +1,6 @@
package asm
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -output alu_string.go -type=Source,Endianness,ALUOp
+//go:generate go tool stringer -output alu_string.go -type=Source,Endianness,ALUOp
// Source of ALU / ALU64 / Branch operations
//
diff --git a/vendor/github.com/cilium/ebpf/asm/func.go b/vendor/github.com/cilium/ebpf/asm/func.go
index 5ee4e954f..fe75c7578 100644
--- a/vendor/github.com/cilium/ebpf/asm/func.go
+++ b/vendor/github.com/cilium/ebpf/asm/func.go
@@ -2,7 +2,7 @@ package asm
import "github.com/cilium/ebpf/internal/platform"
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -output func_string.go -type=BuiltinFunc
+//go:generate go tool stringer -output func_string.go -type=BuiltinFunc
// BuiltinFunc is a built-in eBPF function.
type BuiltinFunc uint32
diff --git a/vendor/github.com/cilium/ebpf/asm/func_string.go b/vendor/github.com/cilium/ebpf/asm/func_string.go
index d5d624f09..259837974 100644
--- a/vendor/github.com/cilium/ebpf/asm/func_string.go
+++ b/vendor/github.com/cilium/ebpf/asm/func_string.go
@@ -241,33 +241,36 @@ func _() {
_ = x[WindowsFnGetCurrentPidTgid-268435475]
_ = x[WindowsFnGetCurrentLogonId-268435476]
_ = x[WindowsFnIsCurrentAdmin-268435477]
- _ = x[WindowsFnMemcpy-268435478]
- _ = x[WindowsFnMemcmp-268435479]
+ _ = x[WindowsFnMemcpyS-268435478]
+ _ = x[WindowsFnMemcmpS-268435479]
_ = x[WindowsFnMemset-268435480]
- _ = x[WindowsFnMemmove-268435481]
+ _ = x[WindowsFnMemmoveS-268435481]
_ = x[WindowsFnGetSocketCookie-268435482]
_ = x[WindowsFnStrncpyS-268435483]
_ = x[WindowsFnStrncatS-268435484]
_ = x[WindowsFnStrnlenS-268435485]
_ = x[WindowsFnKtimeGetBootMs-268435486]
_ = x[WindowsFnKtimeGetMs-268435487]
+ _ = x[WindowsFnPerfEventOutput-268435488]
+ _ = x[WindowsFnGetCurrentProcessStartKey-268435489]
+ _ = x[WindowsFnGetCurrentThreadCreateTime-268435490]
}
const (
_BuiltinFunc_name_0 = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysCloseFnTimerInitFnTimerSetCallbackFnTimerStartFnTimerCancelFnGetFuncIpFnGetAttachCookieFnTaskPtRegsFnGetBranchSnapshotFnTraceVprintkFnSkcToUnixSockFnKallsymsLookupNameFnFindVmaFnLoopFnStrncmpFnGetFuncArgFnGetFuncRetFnGetFuncArgCntFnGetRetvalFnSetRetvalFnXdpGetBuffLenFnXdpLoadBytesFnXdpStoreBytesFnCopyFromUserTaskFnSkbSetTstampFnImaFileHashFnKptrXchgFnMapLookupPercpuElemFnSkcToMptcpSockFnDynptrFromMemFnRingbufReserveDynptrFnRingbufSubmitDynptrFnRingbufDiscardDynptrFnDynptrReadFnDynptrWriteFnDynptrDataFnTcpRawGenSyncookieIpv4FnTcpRawGenSyncookieIpv6FnTcpRawCheckSyncookieIpv4FnTcpRawCheckSyncookieIpv6FnKtimeGetTaiNsFnUserRingbufDrainFnCgrpStorageGetFnCgrpStorageDelete"
- _BuiltinFunc_name_1 = "WindowsFnMapLookupElemWindowsFnMapUpdateElemWindowsFnMapDeleteElemWindowsFnMapLookupAndDeleteElemWindowsFnTailCallWindowsFnGetPrandomU32WindowsFnKtimeGetBootNsWindowsFnGetSmpProcessorIdWindowsFnKtimeGetNsWindowsFnCsumDiffWindowsFnRingbufOutputWindowsFnTracePrintk2WindowsFnTracePrintk3WindowsFnTracePrintk4WindowsFnTracePrintk5WindowsFnMapPushElemWindowsFnMapPopElemWindowsFnMapPeekElemWindowsFnGetCurrentPidTgidWindowsFnGetCurrentLogonIdWindowsFnIsCurrentAdminWindowsFnMemcpyWindowsFnMemcmpWindowsFnMemsetWindowsFnMemmoveWindowsFnGetSocketCookieWindowsFnStrncpySWindowsFnStrncatSWindowsFnStrnlenSWindowsFnKtimeGetBootMsWindowsFnKtimeGetMs"
+ _BuiltinFunc_name_1 = "WindowsFnMapLookupElemWindowsFnMapUpdateElemWindowsFnMapDeleteElemWindowsFnMapLookupAndDeleteElemWindowsFnTailCallWindowsFnGetPrandomU32WindowsFnKtimeGetBootNsWindowsFnGetSmpProcessorIdWindowsFnKtimeGetNsWindowsFnCsumDiffWindowsFnRingbufOutputWindowsFnTracePrintk2WindowsFnTracePrintk3WindowsFnTracePrintk4WindowsFnTracePrintk5WindowsFnMapPushElemWindowsFnMapPopElemWindowsFnMapPeekElemWindowsFnGetCurrentPidTgidWindowsFnGetCurrentLogonIdWindowsFnIsCurrentAdminWindowsFnMemcpySWindowsFnMemcmpSWindowsFnMemsetWindowsFnMemmoveSWindowsFnGetSocketCookieWindowsFnStrncpySWindowsFnStrncatSWindowsFnStrnlenSWindowsFnKtimeGetBootMsWindowsFnKtimeGetMsWindowsFnPerfEventOutputWindowsFnGetCurrentProcessStartKeyWindowsFnGetCurrentThreadCreateTime"
)
var (
_BuiltinFunc_index_0 = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632, 1643, 1658, 1675, 1693, 1713, 1725, 1743, 1754, 1773, 1794, 1805, 1821, 1849, 1859, 1875, 1886, 1896, 1908, 1928, 1943, 1959, 1974, 1990, 2004, 2015, 2030, 2044, 2066, 2087, 2102, 2116, 2128, 2141, 2156, 2173, 2193, 2200, 2214, 2227, 2241, 2259, 2274, 2285, 2297, 2311, 2327, 2346, 2365, 2378, 2396, 2410, 2424, 2434, 2450, 2460, 2468, 2487, 2497, 2508, 2526, 2538, 2551, 2562, 2579, 2591, 2610, 2624, 2639, 2659, 2668, 2674, 2683, 2695, 2707, 2722, 2733, 2744, 2759, 2773, 2788, 2806, 2820, 2833, 2843, 2864, 2880, 2895, 2917, 2938, 2960, 2972, 2985, 2997, 3021, 3045, 3071, 3097, 3112, 3130, 3146, 3165}
- _BuiltinFunc_index_1 = [...]uint16{0, 22, 44, 66, 97, 114, 136, 159, 185, 204, 221, 243, 264, 285, 306, 327, 347, 366, 386, 412, 438, 461, 476, 491, 506, 522, 546, 563, 580, 597, 620, 639}
+ _BuiltinFunc_index_1 = [...]uint16{0, 22, 44, 66, 97, 114, 136, 159, 185, 204, 221, 243, 264, 285, 306, 327, 347, 366, 386, 412, 438, 461, 477, 493, 508, 525, 549, 566, 583, 600, 623, 642, 666, 700, 735}
)
func (i BuiltinFunc) String() string {
switch {
case i <= 211:
return _BuiltinFunc_name_0[_BuiltinFunc_index_0[i]:_BuiltinFunc_index_0[i+1]]
- case 268435457 <= i && i <= 268435487:
+ case 268435457 <= i && i <= 268435490:
i -= 268435457
return _BuiltinFunc_name_1[_BuiltinFunc_index_1[i]:_BuiltinFunc_index_1[i+1]]
default:
diff --git a/vendor/github.com/cilium/ebpf/asm/func_win.go b/vendor/github.com/cilium/ebpf/asm/func_win.go
index b016f0086..609ed9056 100644
--- a/vendor/github.com/cilium/ebpf/asm/func_win.go
+++ b/vendor/github.com/cilium/ebpf/asm/func_win.go
@@ -1,44 +1,45 @@
-// Code generated by internal/cmd/genwinfunctions.awk; DO NOT EDIT.
+// Code generated by internal/cmd/genwinfunctions.awk; DO NOT EDIT.
package asm
// Code in this file is derived from eBPF for Windows, available under the MIT License.
-import (
- "github.com/cilium/ebpf/internal/platform"
-)
+import "github.com/cilium/ebpf/internal/platform"
// Built-in functions (Windows).
const (
- WindowsFnMapLookupElem = BuiltinFunc(platform.WindowsTag | 1)
- WindowsFnMapUpdateElem = BuiltinFunc(platform.WindowsTag | 2)
- WindowsFnMapDeleteElem = BuiltinFunc(platform.WindowsTag | 3)
- WindowsFnMapLookupAndDeleteElem = BuiltinFunc(platform.WindowsTag | 4)
- WindowsFnTailCall = BuiltinFunc(platform.WindowsTag | 5)
- WindowsFnGetPrandomU32 = BuiltinFunc(platform.WindowsTag | 6)
- WindowsFnKtimeGetBootNs = BuiltinFunc(platform.WindowsTag | 7)
- WindowsFnGetSmpProcessorId = BuiltinFunc(platform.WindowsTag | 8)
- WindowsFnKtimeGetNs = BuiltinFunc(platform.WindowsTag | 9)
- WindowsFnCsumDiff = BuiltinFunc(platform.WindowsTag | 10)
- WindowsFnRingbufOutput = BuiltinFunc(platform.WindowsTag | 11)
- WindowsFnTracePrintk2 = BuiltinFunc(platform.WindowsTag | 12)
- WindowsFnTracePrintk3 = BuiltinFunc(platform.WindowsTag | 13)
- WindowsFnTracePrintk4 = BuiltinFunc(platform.WindowsTag | 14)
- WindowsFnTracePrintk5 = BuiltinFunc(platform.WindowsTag | 15)
- WindowsFnMapPushElem = BuiltinFunc(platform.WindowsTag | 16)
- WindowsFnMapPopElem = BuiltinFunc(platform.WindowsTag | 17)
- WindowsFnMapPeekElem = BuiltinFunc(platform.WindowsTag | 18)
- WindowsFnGetCurrentPidTgid = BuiltinFunc(platform.WindowsTag | 19)
- WindowsFnGetCurrentLogonId = BuiltinFunc(platform.WindowsTag | 20)
- WindowsFnIsCurrentAdmin = BuiltinFunc(platform.WindowsTag | 21)
- WindowsFnMemcpy = BuiltinFunc(platform.WindowsTag | 22)
- WindowsFnMemcmp = BuiltinFunc(platform.WindowsTag | 23)
- WindowsFnMemset = BuiltinFunc(platform.WindowsTag | 24)
- WindowsFnMemmove = BuiltinFunc(platform.WindowsTag | 25)
- WindowsFnGetSocketCookie = BuiltinFunc(platform.WindowsTag | 26)
- WindowsFnStrncpyS = BuiltinFunc(platform.WindowsTag | 27)
- WindowsFnStrncatS = BuiltinFunc(platform.WindowsTag | 28)
- WindowsFnStrnlenS = BuiltinFunc(platform.WindowsTag | 29)
- WindowsFnKtimeGetBootMs = BuiltinFunc(platform.WindowsTag | 30)
- WindowsFnKtimeGetMs = BuiltinFunc(platform.WindowsTag | 31)
+ WindowsFnMapLookupElem = BuiltinFunc(platform.WindowsTag | 1)
+ WindowsFnMapUpdateElem = BuiltinFunc(platform.WindowsTag | 2)
+ WindowsFnMapDeleteElem = BuiltinFunc(platform.WindowsTag | 3)
+ WindowsFnMapLookupAndDeleteElem = BuiltinFunc(platform.WindowsTag | 4)
+ WindowsFnTailCall = BuiltinFunc(platform.WindowsTag | 5)
+ WindowsFnGetPrandomU32 = BuiltinFunc(platform.WindowsTag | 6)
+ WindowsFnKtimeGetBootNs = BuiltinFunc(platform.WindowsTag | 7)
+ WindowsFnGetSmpProcessorId = BuiltinFunc(platform.WindowsTag | 8)
+ WindowsFnKtimeGetNs = BuiltinFunc(platform.WindowsTag | 9)
+ WindowsFnCsumDiff = BuiltinFunc(platform.WindowsTag | 10)
+ WindowsFnRingbufOutput = BuiltinFunc(platform.WindowsTag | 11)
+ WindowsFnTracePrintk2 = BuiltinFunc(platform.WindowsTag | 12)
+ WindowsFnTracePrintk3 = BuiltinFunc(platform.WindowsTag | 13)
+ WindowsFnTracePrintk4 = BuiltinFunc(platform.WindowsTag | 14)
+ WindowsFnTracePrintk5 = BuiltinFunc(platform.WindowsTag | 15)
+ WindowsFnMapPushElem = BuiltinFunc(platform.WindowsTag | 16)
+ WindowsFnMapPopElem = BuiltinFunc(platform.WindowsTag | 17)
+ WindowsFnMapPeekElem = BuiltinFunc(platform.WindowsTag | 18)
+ WindowsFnGetCurrentPidTgid = BuiltinFunc(platform.WindowsTag | 19)
+ WindowsFnGetCurrentLogonId = BuiltinFunc(platform.WindowsTag | 20)
+ WindowsFnIsCurrentAdmin = BuiltinFunc(platform.WindowsTag | 21)
+ WindowsFnMemcpyS = BuiltinFunc(platform.WindowsTag | 22)
+ WindowsFnMemcmpS = BuiltinFunc(platform.WindowsTag | 23)
+ WindowsFnMemset = BuiltinFunc(platform.WindowsTag | 24)
+ WindowsFnMemmoveS = BuiltinFunc(platform.WindowsTag | 25)
+ WindowsFnGetSocketCookie = BuiltinFunc(platform.WindowsTag | 26)
+ WindowsFnStrncpyS = BuiltinFunc(platform.WindowsTag | 27)
+ WindowsFnStrncatS = BuiltinFunc(platform.WindowsTag | 28)
+ WindowsFnStrnlenS = BuiltinFunc(platform.WindowsTag | 29)
+ WindowsFnKtimeGetBootMs = BuiltinFunc(platform.WindowsTag | 30)
+ WindowsFnKtimeGetMs = BuiltinFunc(platform.WindowsTag | 31)
+ WindowsFnPerfEventOutput = BuiltinFunc(platform.WindowsTag | 32)
+ WindowsFnGetCurrentProcessStartKey = BuiltinFunc(platform.WindowsTag | 33)
+ WindowsFnGetCurrentThreadCreateTime = BuiltinFunc(platform.WindowsTag | 34)
)
diff --git a/vendor/github.com/cilium/ebpf/asm/instruction.go b/vendor/github.com/cilium/ebpf/asm/instruction.go
index b2ce72ca8..f966a234b 100644
--- a/vendor/github.com/cilium/ebpf/asm/instruction.go
+++ b/vendor/github.com/cilium/ebpf/asm/instruction.go
@@ -2,10 +2,12 @@ package asm
import (
"crypto/sha1"
+ "crypto/sha256"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
+ "hash"
"io"
"math"
"sort"
@@ -43,6 +45,14 @@ type Instruction struct {
Metadata Metadata
}
+// Width returns how many raw BPF instructions the Instruction occupies within
+// an instruction stream. For example, an Instruction encoding a 64-bit value
+// will typically occupy 2 raw instructions, while a 32-bit constant can be
+// encoded in a single raw instruction.
+func (ins *Instruction) Width() RawInstructionOffset {
+ return RawInstructionOffset(ins.OpCode.rawInstructions())
+}
+
// Unmarshal decodes a BPF instruction.
func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder, platform string) error {
data := make([]byte, InstructionSize)
@@ -233,22 +243,6 @@ func (ins *Instruction) AssociateMap(m FDer) error {
return nil
}
-// RewriteMapPtr changes an instruction to use a new map fd.
-//
-// Returns an error if the instruction doesn't load a map.
-//
-// Deprecated: use AssociateMap instead. If you cannot provide a Map,
-// wrap an fd in a type implementing FDer.
-func (ins *Instruction) RewriteMapPtr(fd int) error {
- if !ins.IsLoadFromMap() {
- return errors.New("not a load from a map")
- }
-
- ins.encodeMapFD(fd)
-
- return nil
-}
-
func (ins *Instruction) encodeMapFD(fd int) {
// Preserve the offset value for direct map loads.
offset := uint64(ins.Constant) & (math.MaxUint32 << 32)
@@ -256,22 +250,6 @@ func (ins *Instruction) encodeMapFD(fd int) {
ins.Constant = int64(offset | rawFd)
}
-// MapPtr returns the map fd for this instruction.
-//
-// The result is undefined if the instruction is not a load from a map,
-// see IsLoadFromMap.
-//
-// Deprecated: use Map() instead.
-func (ins *Instruction) MapPtr() int {
- // If there is a map associated with the instruction, return its FD.
- if fd := ins.Metadata.Get(mapMeta{}); fd != nil {
- return fd.(FDer).FD()
- }
-
- // Fall back to the fd stored in the Constant field
- return ins.mapFd()
-}
-
// mapFd returns the map file descriptor stored in the 32 least significant
// bits of ins' Constant field.
func (ins *Instruction) mapFd() int {
@@ -418,13 +396,19 @@ func (ins Instruction) Format(f fmt.State, c rune) {
}
case cls.IsJump():
- fmt.Fprintf(f, "%v ", op)
switch jop := op.JumpOp(); jop {
case Call:
+ fmt.Fprintf(f, "%v ", op)
switch ins.Src {
case PseudoCall:
- // bpf-to-bpf call
- fmt.Fprint(f, ins.Constant)
+ // bpf-to-bpf call. For JIT-compiled programs, the kernel stores
+ // the PC offset in the 'off' field (ins.Offset) rather than 'imm'
+ // (ins.Constant). See kernel bpf_insn_prepare_dump in verifier.c.
+ if ins.Offset != 0 {
+ fmt.Fprint(f, ins.Offset)
+ } else {
+ fmt.Fprint(f, ins.Constant)
+ }
case PseudoKfuncCall:
// kfunc call
fmt.Fprintf(f, "Kfunc(%d)", ins.Constant)
@@ -433,13 +417,23 @@ func (ins Instruction) Format(f fmt.State, c rune) {
}
case Ja:
+ fmt.Fprintf(f, "%v ", op)
if ins.OpCode.Class() == Jump32Class {
fmt.Fprintf(f, "imm: %d", ins.Constant)
} else {
fmt.Fprintf(f, "off: %d", ins.Offset)
}
+ case JCOND:
+ switch ins.Src {
+ case PseudoMayGoto:
+ fmt.Fprintf(f, "JCond may_goto off: %d", ins.Offset)
+ default:
+ fmt.Fprintf(f, "%v", op)
+ }
+
default:
+ fmt.Fprintf(f, "%v ", op)
fmt.Fprintf(f, "dst: %s off: %d ", ins.Dst, ins.Offset)
if op.Source() == ImmSource {
fmt.Fprintf(f, "imm: %d", ins.Constant)
@@ -486,13 +480,6 @@ func (ins Instruction) WithSymbol(name string) Instruction {
return ins
}
-// Sym creates a symbol.
-//
-// Deprecated: use WithSymbol instead.
-func (ins Instruction) Sym(name string) Instruction {
- return ins.WithSymbol(name)
-}
-
// Symbol returns the value ins has been marked with using WithSymbol,
// otherwise returns an empty string. A symbol is often an Instruction
// at the start of a function body.
@@ -632,39 +619,6 @@ func (insns Instructions) AssociateMap(symbol string, m FDer) error {
return nil
}
-// RewriteMapPtr rewrites all loads of a specific map pointer to a new fd.
-//
-// Returns ErrUnreferencedSymbol if the symbol isn't used.
-//
-// Deprecated: use AssociateMap instead.
-func (insns Instructions) RewriteMapPtr(symbol string, fd int) error {
- if symbol == "" {
- return errors.New("empty symbol")
- }
-
- var found bool
- for i := range insns {
- ins := &insns[i]
- if ins.Reference() != symbol {
- continue
- }
-
- if !ins.IsLoadFromMap() {
- return errors.New("not a load from a map")
- }
-
- ins.encodeMapFD(fd)
-
- found = true
- }
-
- if !found {
- return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol)
- }
-
- return nil
-}
-
// SymbolOffsets returns the set of symbols and their offset in
// the instructions.
func (insns Instructions) SymbolOffsets() (map[string]int, error) {
@@ -817,18 +771,72 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
// It mirrors bpf_prog_calc_tag in the kernel and so can be compared
// to ProgramInfo.Tag to figure out whether a loaded program matches
// certain instructions.
+//
+// Deprecated: The value produced by this method no longer matches tags produced
+// by the kernel since Linux 6.18. Use [Instructions.HasTag] instead.
func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) {
+ // We cannot determine which hashing function to use without probing the kernel.
+ // So use the legacy SHA-1 implementation and deprecate this method.
+ return insns.tagSha1(bo)
+}
+
+// HasTag returns true if the given tag matches the kernel tag of insns.
+func (insns Instructions) HasTag(tag string, bo binary.ByteOrder) (bool, error) {
+ sha256Tag, err := insns.tagSha256(bo)
+ if err != nil {
+ return false, fmt.Errorf("hashing sha256: %w", err)
+ }
+ if tag == sha256Tag {
+ return true, nil
+ }
+
+ sha1Tag, err := insns.tagSha1(bo)
+ if err != nil {
+ return false, fmt.Errorf("hashing sha1: %w", err)
+ }
+
+ return tag == sha1Tag, nil
+}
+
+// tagSha1 calculates the kernel tag for a series of instructions.
+//
+// It mirrors bpf_prog_calc_tag in kernels up to v6.18 and can be compared to
+// ProgramInfo.Tag to figure out whether a loaded Program matches insns.
+func (insns Instructions) tagSha1(bo binary.ByteOrder) (string, error) {
h := sha1.New()
+ if err := insns.hash(h, bo); err != nil {
+ return "", err
+ }
+ return hex.EncodeToString(h.Sum(nil)[:sys.BPF_TAG_SIZE]), nil
+}
+
+// tagSha256 calculates the kernel tag for a series of instructions.
+//
+// It mirrors bpf_prog_calc_tag in the kernel and can be compared to
+// ProgramInfo.Tag to figure out whether a loaded Program matches insns.
+func (insns Instructions) tagSha256(bo binary.ByteOrder) (string, error) {
+ h := sha256.New()
+ if err := insns.hash(h, bo); err != nil {
+ return "", err
+ }
+ return hex.EncodeToString(h.Sum(nil)[:sys.BPF_TAG_SIZE]), nil
+}
+
+// hash calculates the hash of the instruction stream. Map load instructions
+// are zeroed out, since these contain map file descriptors or pointers to
+// maps, which will be different from load to load and would make the hash
+// non-deterministic.
+func (insns Instructions) hash(h hash.Hash, bo binary.ByteOrder) error {
for i, ins := range insns {
if ins.IsLoadFromMap() {
ins.Constant = 0
}
_, err := ins.Marshal(h, bo)
if err != nil {
- return "", fmt.Errorf("instruction %d: %w", i, err)
+ return fmt.Errorf("instruction %d: %w", i, err)
}
}
- return hex.EncodeToString(h.Sum(nil)[:sys.BPF_TAG_SIZE]), nil
+ return nil
}
// encodeFunctionReferences populates the Offset (or Constant, depending on
@@ -968,11 +976,3 @@ func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, erro
return 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
}
}
-
-// IsUnreferencedSymbol returns true if err was caused by
-// an unreferenced symbol.
-//
-// Deprecated: use errors.Is(err, asm.ErrUnreferencedSymbol).
-func IsUnreferencedSymbol(err error) bool {
- return errors.Is(err, ErrUnreferencedSymbol)
-}
diff --git a/vendor/github.com/cilium/ebpf/asm/jump.go b/vendor/github.com/cilium/ebpf/asm/jump.go
index 2738d736b..e8a78093b 100644
--- a/vendor/github.com/cilium/ebpf/asm/jump.go
+++ b/vendor/github.com/cilium/ebpf/asm/jump.go
@@ -1,6 +1,6 @@
package asm
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -output jump_string.go -type=JumpOp
+//go:generate go tool stringer -output jump_string.go -type=JumpOp
// JumpOp affect control flow.
//
@@ -44,6 +44,8 @@ const (
JSLT JumpOp = 0xc0
// JSLE jumps by offset if signed r <= signed imm
JSLE JumpOp = 0xd0
+ // JCOND is a conditional pseudo jump to encode the may_goto instruction
+ JCOND JumpOp = 0xe0
)
// Return emits an exit instruction.
diff --git a/vendor/github.com/cilium/ebpf/asm/jump_string.go b/vendor/github.com/cilium/ebpf/asm/jump_string.go
index 85a4aaffa..240cb7c09 100644
--- a/vendor/github.com/cilium/ebpf/asm/jump_string.go
+++ b/vendor/github.com/cilium/ebpf/asm/jump_string.go
@@ -23,9 +23,10 @@ func _() {
_ = x[JLE-176]
_ = x[JSLT-192]
_ = x[JSLE-208]
+ _ = x[JCOND-224]
}
-const _JumpOp_name = "JaJEqJGTJGEJSetJNEJSGTJSGECallExitJLTJLEJSLTJSLEInvalidJumpOp"
+const _JumpOp_name = "JaJEqJGTJGEJSetJNEJSGTJSGECallExitJLTJLEJSLTJSLEJCONDInvalidJumpOp"
var _JumpOp_map = map[JumpOp]string{
0: _JumpOp_name[0:2],
@@ -42,7 +43,8 @@ var _JumpOp_map = map[JumpOp]string{
176: _JumpOp_name[37:40],
192: _JumpOp_name[40:44],
208: _JumpOp_name[44:48],
- 255: _JumpOp_name[48:61],
+ 224: _JumpOp_name[48:53],
+ 255: _JumpOp_name[53:66],
}
func (i JumpOp) String() string {
diff --git a/vendor/github.com/cilium/ebpf/asm/load_store.go b/vendor/github.com/cilium/ebpf/asm/load_store.go
index 29571a74e..a32a9b318 100644
--- a/vendor/github.com/cilium/ebpf/asm/load_store.go
+++ b/vendor/github.com/cilium/ebpf/asm/load_store.go
@@ -2,7 +2,7 @@ package asm
import "fmt"
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -output load_store_string.go -type=Mode,Size
+//go:generate go tool stringer -output load_store_string.go -type=Mode,Size
// Mode for load and store operations
//
diff --git a/vendor/github.com/cilium/ebpf/asm/metadata.go b/vendor/github.com/cilium/ebpf/asm/metadata.go
index dd368a936..60b8fe31f 100644
--- a/vendor/github.com/cilium/ebpf/asm/metadata.go
+++ b/vendor/github.com/cilium/ebpf/asm/metadata.go
@@ -7,13 +7,13 @@ type Metadata struct {
type metaElement struct {
next *metaElement
- key, value interface{}
+ key, value any
}
// Find the element containing key.
//
// Returns nil if there is no such element.
-func (m *Metadata) find(key interface{}) *metaElement {
+func (m *Metadata) find(key any) *metaElement {
for e := m.head; e != nil; e = e.next {
if e.key == key {
return e
@@ -49,7 +49,7 @@ func (m *Metadata) remove(r *metaElement) {
//
// If value is nil, the key is removed. Avoids modifying old metadata by
// copying if necessary.
-func (m *Metadata) Set(key, value interface{}) {
+func (m *Metadata) Set(key, value any) {
if e := m.find(key); e != nil {
if e.value == value {
// Key is present and the value is the same. Nothing to do.
@@ -72,7 +72,7 @@ func (m *Metadata) Set(key, value interface{}) {
// Get the value of a key.
//
// Returns nil if no value with the given key is present.
-func (m *Metadata) Get(key interface{}) interface{} {
+func (m *Metadata) Get(key any) any {
if e := m.find(key); e != nil {
return e.value
}
diff --git a/vendor/github.com/cilium/ebpf/asm/opcode.go b/vendor/github.com/cilium/ebpf/asm/opcode.go
index c82a1f8fb..9b2f80f0a 100644
--- a/vendor/github.com/cilium/ebpf/asm/opcode.go
+++ b/vendor/github.com/cilium/ebpf/asm/opcode.go
@@ -5,7 +5,7 @@ import (
"strings"
)
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -output opcode_string.go -type=Class
+//go:generate go tool stringer -output opcode_string.go -type=Class
// Class of operations
//
diff --git a/vendor/github.com/cilium/ebpf/asm/opcode_string.go b/vendor/github.com/cilium/ebpf/asm/opcode_string.go
index 58bc3e7e7..07825e0dd 100644
--- a/vendor/github.com/cilium/ebpf/asm/opcode_string.go
+++ b/vendor/github.com/cilium/ebpf/asm/opcode_string.go
@@ -23,8 +23,9 @@ const _Class_name = "LdClassLdXClassStClassStXClassALUClassJumpClassJump32ClassA
var _Class_index = [...]uint8{0, 7, 15, 22, 30, 38, 47, 58, 68}
func (i Class) String() string {
- if i >= Class(len(_Class_index)-1) {
+ idx := int(i) - 0
+ if i < 0 || idx >= len(_Class_index)-1 {
return "Class(" + strconv.FormatInt(int64(i), 10) + ")"
}
- return _Class_name[_Class_index[i]:_Class_index[i+1]]
+ return _Class_name[_Class_index[idx]:_Class_index[idx+1]]
}
diff --git a/vendor/github.com/cilium/ebpf/asm/register.go b/vendor/github.com/cilium/ebpf/asm/register.go
index 457a3b8a8..baf03aa42 100644
--- a/vendor/github.com/cilium/ebpf/asm/register.go
+++ b/vendor/github.com/cilium/ebpf/asm/register.go
@@ -40,6 +40,7 @@ const (
PseudoCall = R1 // BPF_PSEUDO_CALL
PseudoFunc = R4 // BPF_PSEUDO_FUNC
PseudoKfuncCall = R2 // BPF_PSEUDO_KFUNC_CALL
+ PseudoMayGoto = R0 // BPF_MAY_GOTO
)
func (r Register) String() string {
diff --git a/vendor/github.com/cilium/ebpf/btf/btf.go b/vendor/github.com/cilium/ebpf/btf/btf.go
index d26931a04..341156929 100644
--- a/vendor/github.com/cilium/ebpf/btf/btf.go
+++ b/vendor/github.com/cilium/ebpf/btf/btf.go
@@ -1,9 +1,7 @@
package btf
import (
- "bufio"
"debug/elf"
- "encoding/binary"
"errors"
"fmt"
"io"
@@ -69,11 +67,12 @@ func LoadSpec(file string) (*Spec, error) {
func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
file, err := internal.NewSafeELFFile(rd)
if err != nil {
- if bo := guessRawBTFByteOrder(rd); bo != nil {
- return loadRawSpec(io.NewSectionReader(rd, 0, math.MaxInt64), bo, nil)
+ raw, err := io.ReadAll(io.NewSectionReader(rd, 0, math.MaxInt64))
+ if err != nil {
+ return nil, fmt.Errorf("read raw BTF: %w", err)
}
- return nil, err
+ return loadRawSpec(raw, nil)
}
return loadSpecFromELF(file)
@@ -170,15 +169,20 @@ func loadSpecFromELF(file *internal.SafeELFFile) (*Spec, error) {
return nil, err
}
- if btfSection.ReaderAt == nil {
- return nil, fmt.Errorf("compressed BTF is not supported")
+ rawBTF, err := btfSection.Data()
+ if err != nil {
+ return nil, fmt.Errorf("reading .BTF section: %w", err)
}
- spec, err := loadRawSpec(btfSection.ReaderAt, file.ByteOrder, nil)
+ spec, err := loadRawSpec(rawBTF, nil)
if err != nil {
return nil, err
}
+ if spec.decoder.byteOrder != file.ByteOrder {
+ return nil, fmt.Errorf("BTF byte order %s does not match ELF byte order %s", spec.decoder.byteOrder, file.ByteOrder)
+ }
+
spec.elf = &elfData{
sectionSizes,
offsets,
@@ -188,7 +192,7 @@ func loadSpecFromELF(file *internal.SafeELFFile) (*Spec, error) {
return spec, nil
}
-func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder, base *Spec) (*Spec, error) {
+func loadRawSpec(btf []byte, base *Spec) (*Spec, error) {
var (
baseDecoder *decoder
baseStrings *stringTable
@@ -200,25 +204,32 @@ func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder, base *Spec) (*Spec, error
baseStrings = base.strings
}
- buf := internal.NewBufferedSectionReader(btf, 0, math.MaxInt64)
- header, err := parseBTFHeader(buf, bo)
+ header, _, bo, err := parseBTFHeader(btf)
if err != nil {
return nil, fmt.Errorf("parsing .BTF header: %v", err)
}
- stringsSection := io.NewSectionReader(btf, header.stringStart(), int64(header.StringLen))
- rawStrings, err := readStringTable(stringsSection, baseStrings)
+ if header.HdrLen > uint32(len(btf)) {
+ return nil, fmt.Errorf("BTF header length is out of bounds")
+ }
+ btf = btf[header.HdrLen:]
+
+ if uint64(header.StringOff)+uint64(header.StringLen) > uint64(len(btf)) {
+ return nil, fmt.Errorf("string table is out of bounds")
+ }
+ stringsSection := btf[header.StringOff : header.StringOff+header.StringLen]
+
+ rawStrings, err := newStringTable(stringsSection, baseStrings)
if err != nil {
return nil, fmt.Errorf("read string section: %w", err)
}
- typesSection := io.NewSectionReader(btf, header.typeStart(), int64(header.TypeLen))
- rawTypes := make([]byte, header.TypeLen)
- if _, err := io.ReadFull(typesSection, rawTypes); err != nil {
- return nil, fmt.Errorf("read type section: %w", err)
+ if uint64(header.TypeOff)+uint64(header.TypeLen) > uint64(len(btf)) {
+ return nil, fmt.Errorf("types section is out of bounds")
}
+ typesSection := btf[header.TypeOff : header.TypeOff+header.TypeLen]
- decoder, err := newDecoder(rawTypes, bo, rawStrings, baseDecoder)
+ decoder, err := newDecoder(typesSection, bo, rawStrings, baseDecoder)
if err != nil {
return nil, err
}
@@ -226,21 +237,6 @@ func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder, base *Spec) (*Spec, error
return &Spec{decoder, nil}, nil
}
-func guessRawBTFByteOrder(r io.ReaderAt) binary.ByteOrder {
- buf := new(bufio.Reader)
- for _, bo := range []binary.ByteOrder{
- binary.LittleEndian,
- binary.BigEndian,
- } {
- buf.Reset(io.NewSectionReader(r, 0, math.MaxInt64))
- if _, err := parseBTFHeader(buf, bo); err == nil {
- return bo
- }
- }
-
- return nil
-}
-
// fixupDatasec attempts to patch up missing info in Datasecs and its members by
// supplementing them with information from the ELF headers and symbol table.
func (elf *elfData) fixupDatasec(typ Type) error {
@@ -449,15 +445,18 @@ func (s *Spec) AnyTypeByName(name string) (Type, error) {
//
// Returns an error wrapping ErrNotFound if no matching Type exists in the Spec.
// Returns an error wrapping ErrMultipleTypes if multiple candidates are found.
-func (s *Spec) TypeByName(name string, typ interface{}) error {
- typeInterface := reflect.TypeOf((*Type)(nil)).Elem()
+func (s *Spec) TypeByName(name string, typ any) error {
+ if err := internal.IsNilPointer(typ); err != nil {
+ return fmt.Errorf("type argument: %w", err)
+ }
+
+ typeInterface := reflect.TypeFor[Type]()
// typ may be **T or *Type
typValue := reflect.ValueOf(typ)
- if typValue.Kind() != reflect.Ptr {
+ if typValue.Kind() != reflect.Pointer {
return fmt.Errorf("%T is not a pointer", typ)
}
-
typPtr := typValue.Elem()
if !typPtr.CanSet() {
return fmt.Errorf("%T cannot be set", typ)
@@ -466,6 +465,9 @@ func (s *Spec) TypeByName(name string, typ interface{}) error {
wanted := typPtr.Type()
if wanted == typeInterface {
// This is *Type. Unwrap the value's type.
+ if typPtr.IsNil() {
+ return fmt.Errorf("%T points to a nil Type", typ)
+ }
wanted = typPtr.Elem().Type()
}
@@ -500,12 +502,31 @@ func (s *Spec) TypeByName(name string, typ interface{}) error {
return nil
}
+// LoadSplitSpec loads split BTF from the given file.
+//
+// Types from base are used to resolve references in the split BTF.
+// The returned Spec only contains types from the split BTF, not from the base.
+func LoadSplitSpec(file string, base *Spec) (*Spec, error) {
+ fh, err := os.Open(file)
+ if err != nil {
+ return nil, err
+ }
+ defer fh.Close()
+
+ return LoadSplitSpecFromReader(fh, base)
+}
+
// LoadSplitSpecFromReader loads split BTF from a reader.
//
// Types from base are used to resolve references in the split BTF.
// The returned Spec only contains types from the split BTF, not from the base.
func LoadSplitSpecFromReader(r io.ReaderAt, base *Spec) (*Spec, error) {
- return loadRawSpec(r, internal.NativeEndian, base)
+ raw, err := io.ReadAll(io.NewSectionReader(r, 0, math.MaxInt64))
+ if err != nil {
+ return nil, fmt.Errorf("read raw BTF: %w", err)
+ }
+
+ return loadRawSpec(raw, base)
}
// All iterates over all types.
diff --git a/vendor/github.com/cilium/ebpf/btf/btf_types.go b/vendor/github.com/cilium/ebpf/btf/btf_types.go
index d20a31969..469baf60c 100644
--- a/vendor/github.com/cilium/ebpf/btf/btf_types.go
+++ b/vendor/github.com/cilium/ebpf/btf/btf_types.go
@@ -4,13 +4,10 @@ import (
"encoding/binary"
"errors"
"fmt"
- "io"
"unsafe"
-
- "github.com/cilium/ebpf/internal"
)
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind
+//go:generate go tool stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind
// btfKind describes a Type.
type btfKind uint8
@@ -87,47 +84,68 @@ type btfHeader struct {
StringLen uint32
}
-// typeStart returns the offset from the beginning of the .BTF section
-// to the start of its type entries.
-func (h *btfHeader) typeStart() int64 {
- return int64(h.HdrLen + h.TypeOff)
-}
-
-// stringStart returns the offset from the beginning of the .BTF section
-// to the start of its string table.
-func (h *btfHeader) stringStart() int64 {
- return int64(h.HdrLen + h.StringOff)
+type btfLayout struct {
+ Off uint32
+ Len uint32
}
// parseBTFHeader parses the header of the .BTF section.
-func parseBTFHeader(r io.Reader, bo binary.ByteOrder) (*btfHeader, error) {
+func parseBTFHeader(buf []byte) (*btfHeader, *btfLayout, binary.ByteOrder, error) {
var header btfHeader
- if err := binary.Read(r, bo, &header); err != nil {
- return nil, fmt.Errorf("can't read header: %v", err)
+ var bo binary.ByteOrder
+ for _, order := range []binary.ByteOrder{binary.LittleEndian, binary.BigEndian} {
+ n, err := binary.Decode(buf, order, &header)
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("read header: %v", err)
+ }
+
+ if header.Magic != btfMagic {
+ continue
+ }
+
+ buf = buf[n:]
+ bo = order
+ break
}
- if header.Magic != btfMagic {
- return nil, fmt.Errorf("incorrect magic value %v", header.Magic)
+ if bo == nil {
+ return nil, nil, nil, fmt.Errorf("no valid BTF header")
}
if header.Version != 1 {
- return nil, fmt.Errorf("unexpected version %v", header.Version)
+ return nil, nil, nil, fmt.Errorf("unexpected version %v", header.Version)
}
if header.Flags != 0 {
- return nil, fmt.Errorf("unsupported flags %v", header.Flags)
+ return nil, nil, nil, fmt.Errorf("unsupported flags %v", header.Flags)
}
remainder := int64(header.HdrLen) - int64(binary.Size(&header))
if remainder < 0 {
- return nil, errors.New("header length shorter than btfHeader size")
+ return nil, nil, nil, errors.New("header length shorter than minimum BTF header size")
+ }
+
+ if len(buf) < int(remainder) {
+ return nil, nil, nil, errors.New("header length exceeds available data")
+ }
+
+ var layout btfLayout
+ if remainder >= int64(binary.Size(&btfLayout{})) {
+ n, err := binary.Decode(buf, bo, &layout)
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("read layout offset and length: %v", err)
+ }
+ buf = buf[n:]
+ remainder -= int64(n)
}
- if _, err := io.CopyN(internal.DiscardZeroes{}, r, remainder); err != nil {
- return nil, fmt.Errorf("header padding: %v", err)
+ for _, b := range buf[:remainder] {
+ if b != 0 {
+ return nil, nil, nil, errors.New("header contains non-zero trailer")
+ }
}
- return &header, nil
+ return &header, &layout, bo, nil
}
// btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst.
diff --git a/vendor/github.com/cilium/ebpf/btf/btf_types_string.go b/vendor/github.com/cilium/ebpf/btf/btf_types_string.go
index b7a1b80d1..a9d2d82b6 100644
--- a/vendor/github.com/cilium/ebpf/btf/btf_types_string.go
+++ b/vendor/github.com/cilium/ebpf/btf/btf_types_string.go
@@ -18,10 +18,11 @@ const _FuncLinkage_name = "staticglobalextern"
var _FuncLinkage_index = [...]uint8{0, 6, 12, 18}
func (i FuncLinkage) String() string {
- if i < 0 || i >= FuncLinkage(len(_FuncLinkage_index)-1) {
+ idx := int(i) - 0
+ if i < 0 || idx >= len(_FuncLinkage_index)-1 {
return "FuncLinkage(" + strconv.FormatInt(int64(i), 10) + ")"
}
- return _FuncLinkage_name[_FuncLinkage_index[i]:_FuncLinkage_index[i+1]]
+ return _FuncLinkage_name[_FuncLinkage_index[idx]:_FuncLinkage_index[idx+1]]
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
@@ -37,10 +38,11 @@ const _VarLinkage_name = "staticglobalextern"
var _VarLinkage_index = [...]uint8{0, 6, 12, 18}
func (i VarLinkage) String() string {
- if i < 0 || i >= VarLinkage(len(_VarLinkage_index)-1) {
+ idx := int(i) - 0
+ if i < 0 || idx >= len(_VarLinkage_index)-1 {
return "VarLinkage(" + strconv.FormatInt(int64(i), 10) + ")"
}
- return _VarLinkage_name[_VarLinkage_index[i]:_VarLinkage_index[i+1]]
+ return _VarLinkage_name[_VarLinkage_index[idx]:_VarLinkage_index[idx+1]]
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
@@ -73,8 +75,9 @@ const _btfKind_name = "UnknownIntPointerArrayStructUnionEnumForwardTypedefVolati
var _btfKind_index = [...]uint8{0, 7, 10, 17, 22, 28, 33, 37, 44, 51, 59, 64, 72, 76, 85, 88, 95, 100, 107, 114, 120}
func (i btfKind) String() string {
- if i >= btfKind(len(_btfKind_index)-1) {
+ idx := int(i) - 0
+ if i < 0 || idx >= len(_btfKind_index)-1 {
return "btfKind(" + strconv.FormatInt(int64(i), 10) + ")"
}
- return _btfKind_name[_btfKind_index[i]:_btfKind_index[i+1]]
+ return _btfKind_name[_btfKind_index[idx]:_btfKind_index[idx+1]]
}
diff --git a/vendor/github.com/cilium/ebpf/btf/dedup.go b/vendor/github.com/cilium/ebpf/btf/dedup.go
new file mode 100644
index 000000000..783ea09e8
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/btf/dedup.go
@@ -0,0 +1,633 @@
+package btf
+
+import (
+ "errors"
+ "fmt"
+ "hash/maphash"
+ "slices"
+)
+
+// deduper deduplicates BTF types by finding all types in a Type graph that are
+// Equivalent and replaces them with a single instance.
+//
+// See doc comments in types.go to understand the various ways in which Types
+// can relate to each other and how they are compared for equality. We separate
+// Identity (same memory location), Equivalence (same shape/layout), and
+// Compatibility (CO-RE compatible) to be explicit about intent.
+//
+// This deduper opportunistically uses a combination of Identity and Equivalence
+// to find types that can be deduplicated.
+type deduper struct {
+ visited map[Type]struct{}
+ hashCache map[hashCacheKey]uint64
+
+ // Set of types that have been deduplicated.
+ done map[Type]Type
+
+ // Map of hash to types with that hash.
+ hashed map[uint64][]Type
+ eqCache map[typKey]bool
+
+ seed maphash.Seed
+}
+
+func newDeduper() *deduper {
+ return &deduper{
+ make(map[Type]struct{}),
+ make(map[hashCacheKey]uint64),
+ make(map[Type]Type),
+ make(map[uint64][]Type),
+ make(map[typKey]bool),
+ maphash.MakeSeed(),
+ }
+}
+
+func (d *deduper) deduplicate(t Type) (Type, error) {
+ // If we have already attempted to deduplicate this exact type, return the
+ // result.
+ if done, ok := d.done[t]; ok {
+ return done, nil
+ }
+
+ // Visit the subtree, if a type has children, attempt to replace it with a
+ // deduplicated version of those children.
+ for t := range postorder(t, d.visited) {
+ for c := range children(t) {
+ var err error
+ *c, err = d.hashInsert(*c)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ // Finally, deduplicate the root type itself.
+ return d.hashInsert(t)
+}
+
+// hashInsert attempts to deduplicate t by hashing it and comparing against
+// other types with the same hash. Returns the Type to be used as the common
+// substitute at this position in the graph.
+func (d *deduper) hashInsert(t Type) (Type, error) {
+ // If we have deduplicated this type before, return the result of that
+ // deduplication.
+ if done, ok := d.done[t]; ok {
+ return done, nil
+ }
+
+ // Compute the hash of this type. Types with the same hash are candidates for
+ // deduplication.
+ hash, err := d.hash(t, -1)
+ if err != nil {
+ return nil, err
+ }
+
+ // A hash collision is possible, so we need to compare against all candidates
+ // with the same hash.
+ for _, candidate := range d.hashed[hash] {
+ // Pre-size the visited slice, experimentation on VMLinux shows a capacity
+ // of 16 to give the best performance.
+ const visitedCapacity = 16
+ err := d.typesEquivalent(candidate, t, make([]Type, 0, visitedCapacity))
+ if errors.Is(err, errNotEquivalent) {
+ continue
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ // Found a Type that's both Equivalent and hashes to the same value, choose
+ // it as the deduplicated version.
+ d.done[t] = candidate
+
+ return candidate, nil
+ }
+
+ d.hashed[hash] = append(d.hashed[hash], t)
+
+ return t, nil
+}
+
+// The hash of a Type is the same given its pointer and depth budget.
+type hashCacheKey struct {
+ t Type
+ depthBudget int
+}
+
+// hash computes a hash for t. The produced hash is the same for Types which
+// are similar. The hash can collide such that two different Types may produce
+// the same hash, so equivalence must be checked explicitly. It will recurse
+// into children. The initial call should use a depthBudget of -1.
+func (d *deduper) hash(t Type, depthBudget int) (uint64, error) {
+ if depthBudget == 0 {
+ return 0, nil
+ }
+
+ h := &maphash.Hash{}
+ h.SetSeed(d.seed)
+
+ switch t := t.(type) {
+ case *Void:
+ maphash.WriteComparable(h, kindUnknown)
+
+ case *Int:
+ maphash.WriteComparable(h, kindInt)
+ maphash.WriteComparable(h, *t)
+
+ case *Pointer:
+ maphash.WriteComparable(h, kindPointer)
+ // If the depth budget is positive, decrement it every time we follow a
+ // pointer.
+ if depthBudget > 0 {
+ depthBudget--
+ }
+
+ // If this is the first time we are following a pointer, set the depth
+ // budget. This limits amount of recursion we do when hashing pointers that
+ // form cycles. This is cheaper than tracking visited types and works
+ // because hash collisions are allowed.
+ if depthBudget < 0 {
+ depthBudget = 1
+
+ // Double pointers are common in C. However, with a depth budget of 1, all
+ // double pointers would hash the same, causing a performance issue when
+ // checking equivalence. So we give double pointers a bit more budget.
+ if _, ok := t.Target.(*Pointer); ok {
+ depthBudget = 2
+ }
+ }
+ sub, err := d.hash(t.Target, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+
+ case *Array:
+ maphash.WriteComparable(h, kindArray)
+ maphash.WriteComparable(h, t.Nelems)
+ sub, err := d.hash(t.Index, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+ _, err = d.hash(t.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+
+ case *Struct, *Union:
+ // Check the cache to avoid recomputing the hash for this type and depth
+ // budget.
+ key := hashCacheKey{t, depthBudget}
+ if cached, ok := d.hashCache[key]; ok {
+ return cached, nil
+ }
+
+ var members []Member
+ switch t := t.(type) {
+ case *Struct:
+ maphash.WriteComparable(h, kindStruct)
+ maphash.WriteComparable(h, t.Name)
+ maphash.WriteComparable(h, t.Size)
+ members = t.Members
+
+ case *Union:
+ maphash.WriteComparable(h, kindUnion)
+ maphash.WriteComparable(h, t.Name)
+ maphash.WriteComparable(h, t.Size)
+ members = t.Members
+ }
+
+ maphash.WriteComparable(h, len(members))
+ for _, m := range members {
+ maphash.WriteComparable(h, m.Name)
+ maphash.WriteComparable(h, m.Offset)
+ sub, err := d.hash(m.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+ }
+
+ sum := h.Sum64()
+ d.hashCache[key] = sum
+ return sum, nil
+
+ case *Enum:
+ maphash.WriteComparable(h, kindEnum)
+ maphash.WriteComparable(h, t.Name)
+ maphash.WriteComparable(h, t.Size)
+ maphash.WriteComparable(h, t.Signed)
+ for _, v := range t.Values {
+ maphash.WriteComparable(h, v)
+ }
+
+ case *Fwd:
+ maphash.WriteComparable(h, kindForward)
+ maphash.WriteComparable(h, *t)
+
+ case *Typedef:
+ maphash.WriteComparable(h, kindTypedef)
+ maphash.WriteComparable(h, t.Name)
+ sub, err := d.hash(t.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+
+ case *Volatile:
+ maphash.WriteComparable(h, kindVolatile)
+ sub, err := d.hash(t.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+
+ case *Const:
+ maphash.WriteComparable(h, kindConst)
+ sub, err := d.hash(t.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+
+ case *Restrict:
+ maphash.WriteComparable(h, kindRestrict)
+ sub, err := d.hash(t.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+
+ case *Func:
+ maphash.WriteComparable(h, kindFunc)
+ maphash.WriteComparable(h, t.Name)
+ sub, err := d.hash(t.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+
+ case *FuncProto:
+ // It turns out that pointers to function prototypes are common in C code,
+ // function pointers. Function prototypes frequently have similar patterns
+ // of [ptr, ptr] -> int, or [ptr, ptr, ptr] -> int. Causing frequent hash
+ // collisions, for the default depth budget of 1. So allow one additional
+ // level of pointers when we encounter a function prototype.
+ if depthBudget >= 0 {
+ depthBudget++
+ }
+
+ maphash.WriteComparable(h, kindFuncProto)
+ for _, p := range t.Params {
+ maphash.WriteComparable(h, p.Name)
+ sub, err := d.hash(p.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+ }
+ sub, err := d.hash(t.Return, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+
+ case *Var:
+ maphash.WriteComparable(h, kindVar)
+ maphash.WriteComparable(h, t.Name)
+ maphash.WriteComparable(h, t.Linkage)
+ sub, err := d.hash(t.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+
+ case *Datasec:
+ maphash.WriteComparable(h, kindDatasec)
+ maphash.WriteComparable(h, t.Name)
+ for _, v := range t.Vars {
+ maphash.WriteComparable(h, v.Offset)
+ maphash.WriteComparable(h, v.Size)
+ sub, err := d.hash(v.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+ }
+
+ case *declTag:
+ maphash.WriteComparable(h, kindDeclTag)
+ maphash.WriteComparable(h, t.Value)
+ maphash.WriteComparable(h, t.Index)
+ sub, err := d.hash(t.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+
+ case *TypeTag:
+ maphash.WriteComparable(h, kindTypeTag)
+ maphash.WriteComparable(h, t.Value)
+ sub, err := d.hash(t.Type, depthBudget)
+ if err != nil {
+ return 0, err
+ }
+ maphash.WriteComparable(h, sub)
+
+ case *Float:
+ maphash.WriteComparable(h, kindFloat)
+ maphash.WriteComparable(h, *t)
+
+ default:
+ return 0, fmt.Errorf("unsupported type for hashing: %T", t)
+ }
+
+ return h.Sum64(), nil
+}
+
+type typKey struct {
+ a Type
+ b Type
+}
+
+var errNotEquivalent = errors.New("types are not equivalent")
+
+// typesEquivalent checks if two types are Equivalent.
+func (d *deduper) typesEquivalent(ta, tb Type, visited []Type) error {
+ // Fast path: if Types are Identical, they are also Equivalent.
+ if ta == tb {
+ return nil
+ }
+
+ switch a := ta.(type) {
+ case *Void:
+ if _, ok := tb.(*Void); ok {
+ return nil
+ }
+ return errNotEquivalent
+
+ case *Int:
+ b, ok := tb.(*Int)
+ if !ok {
+ return errNotEquivalent
+ }
+ if a.Name != b.Name || a.Size != b.Size || a.Encoding != b.Encoding {
+ return errNotEquivalent
+ }
+ return nil
+
+ case *Enum:
+ b, ok := tb.(*Enum)
+ if !ok {
+ return errNotEquivalent
+ }
+ if a.Name != b.Name || len(a.Values) != len(b.Values) {
+ return errNotEquivalent
+ }
+ for i := range a.Values {
+ if a.Values[i].Name != b.Values[i].Name || a.Values[i].Value != b.Values[i].Value {
+ return errNotEquivalent
+ }
+ }
+ return nil
+
+ case *Fwd:
+ b, ok := tb.(*Fwd)
+ if !ok {
+ return errNotEquivalent
+ }
+ if a.Name != b.Name || a.Kind != b.Kind {
+ return errNotEquivalent
+ }
+ return nil
+
+ case *Float:
+ b, ok := tb.(*Float)
+ if !ok {
+ return errNotEquivalent
+ }
+ if a.Name != b.Name || a.Size != b.Size {
+ return errNotEquivalent
+ }
+ return nil
+
+ case *Array:
+ b, ok := tb.(*Array)
+ if !ok {
+ return errNotEquivalent
+ }
+
+ if a.Nelems != b.Nelems {
+ return errNotEquivalent
+ }
+ if err := d.typesEquivalent(a.Index, b.Index, visited); err != nil {
+ return err
+ }
+ if err := d.typesEquivalent(a.Type, b.Type, visited); err != nil {
+ return err
+ }
+ return nil
+
+ case *Pointer:
+ b, ok := tb.(*Pointer)
+ if !ok {
+ return errNotEquivalent
+ }
+
+ // Detect cycles by tracking visited types. Assume types are Equivalent if
+ // we have already visited this type in the current Equivalence check.
+ if slices.Contains(visited, ta) {
+ return nil
+ }
+ visited = append(visited, ta)
+
+ return d.typesEquivalent(a.Target, b.Target, visited)
+
+ case *Struct, *Union:
+ // Use a cache to avoid recomputation. We only do this for composite types
+ // since they are where types fan out the most. For other types, the
+ // overhead of the lookup and update outweighs performance benefits.
+ cacheKey := typKey{a: ta, b: tb}
+ if equal, ok := d.eqCache[cacheKey]; ok {
+ if equal {
+ return nil
+ }
+ return errNotEquivalent
+ }
+
+ compErr := d.compositeEquivalent(ta, tb, visited)
+ d.eqCache[cacheKey] = compErr == nil
+
+ return compErr
+
+ case *Typedef:
+ b, ok := tb.(*Typedef)
+ if !ok {
+ return errNotEquivalent
+ }
+ if a.Name != b.Name {
+ return errNotEquivalent
+ }
+ return d.typesEquivalent(a.Type, b.Type, visited)
+
+ case *Volatile:
+ b, ok := tb.(*Volatile)
+ if !ok {
+ return errNotEquivalent
+ }
+ return d.typesEquivalent(a.Type, b.Type, visited)
+
+ case *Const:
+ b, ok := tb.(*Const)
+ if !ok {
+ return errNotEquivalent
+ }
+ return d.typesEquivalent(a.Type, b.Type, visited)
+
+ case *Restrict:
+ b, ok := tb.(*Restrict)
+ if !ok {
+ return errNotEquivalent
+ }
+ return d.typesEquivalent(a.Type, b.Type, visited)
+
+ case *Func:
+ b, ok := tb.(*Func)
+ if !ok {
+ return errNotEquivalent
+ }
+ if a.Name != b.Name {
+ return errNotEquivalent
+ }
+ return d.typesEquivalent(a.Type, b.Type, visited)
+
+ case *FuncProto:
+ b, ok := tb.(*FuncProto)
+ if !ok {
+ return errNotEquivalent
+ }
+
+ if err := d.typesEquivalent(a.Return, b.Return, visited); err != nil {
+ return err
+ }
+ if len(a.Params) != len(b.Params) {
+ return errNotEquivalent
+ }
+ for i := range a.Params {
+ if a.Params[i].Name != b.Params[i].Name {
+ return errNotEquivalent
+ }
+ if err := d.typesEquivalent(a.Params[i].Type, b.Params[i].Type, visited); err != nil {
+ return err
+ }
+ }
+ return nil
+
+ case *Var:
+ b, ok := tb.(*Var)
+ if !ok {
+ return errNotEquivalent
+ }
+ if a.Name != b.Name {
+ return errNotEquivalent
+ }
+ if err := d.typesEquivalent(a.Type, b.Type, visited); err != nil {
+ return err
+ }
+ if a.Linkage != b.Linkage {
+ return errNotEquivalent
+ }
+ return nil
+
+ case *Datasec:
+ b, ok := tb.(*Datasec)
+ if !ok {
+ return errNotEquivalent
+ }
+ if a.Name != b.Name || len(a.Vars) != len(b.Vars) {
+ return errNotEquivalent
+ }
+ for i := range a.Vars {
+ if a.Vars[i].Offset != b.Vars[i].Offset ||
+ a.Vars[i].Size != b.Vars[i].Size {
+ return errNotEquivalent
+ }
+
+ if err := d.typesEquivalent(a.Vars[i].Type, b.Vars[i].Type, visited); err != nil {
+ return err
+ }
+ }
+ return nil
+
+ case *declTag:
+ b, ok := tb.(*declTag)
+ if !ok {
+ return errNotEquivalent
+ }
+ if a.Value != b.Value || a.Index != b.Index {
+ return errNotEquivalent
+ }
+ return d.typesEquivalent(a.Type, b.Type, visited)
+
+ case *TypeTag:
+ b, ok := tb.(*TypeTag)
+ if !ok {
+ return errNotEquivalent
+ }
+ if a.Value != b.Value {
+ return errNotEquivalent
+ }
+ if err := d.typesEquivalent(a.Type, b.Type, visited); err != nil {
+ return err
+ }
+ return nil
+
+ default:
+ return fmt.Errorf("unsupported type for equivalence: %T", a)
+ }
+}
+
+// compositeEquivalent checks if two composite types (Struct or Union) are
+// Equivalent.
+func (d *deduper) compositeEquivalent(at, bt Type, visited []Type) error {
+ var ma, mb []Member
+ switch a := at.(type) {
+ case *Struct:
+ b, ok := bt.(*Struct)
+ if !ok {
+ return errNotEquivalent
+ }
+
+ if a.Name != b.Name || a.Size != b.Size || len(a.Members) != len(b.Members) {
+ return errNotEquivalent
+ }
+ ma = a.Members
+ mb = b.Members
+
+ case *Union:
+ b, ok := bt.(*Union)
+ if !ok {
+ return errNotEquivalent
+ }
+
+ if a.Name != b.Name || a.Size != b.Size || len(a.Members) != len(b.Members) {
+ return errNotEquivalent
+ }
+ ma = a.Members
+ mb = b.Members
+ }
+
+ for i := range ma {
+ if ma[i].Name != mb[i].Name || ma[i].Offset != mb[i].Offset {
+ return errNotEquivalent
+ }
+
+ if err := d.typesEquivalent(ma[i].Type, mb[i].Type, visited); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/cilium/ebpf/btf/ext_info.go b/vendor/github.com/cilium/ebpf/btf/ext_info.go
index 6ff5e2b90..2e2aa629f 100644
--- a/vendor/github.com/cilium/ebpf/btf/ext_info.go
+++ b/vendor/github.com/cilium/ebpf/btf/ext_info.go
@@ -13,12 +13,23 @@ import (
"github.com/cilium/ebpf/internal"
)
-// ExtInfos contains ELF section metadata.
+// ExtInfos contains raw, per-section extended BTF metadata from the .BTF.ext
+// ELF section.
type ExtInfos struct {
- // The slices are sorted by offset in ascending order.
- funcInfos map[string]FuncOffsets
- lineInfos map[string]LineOffsets
- relocationInfos map[string]CORERelocationInfos
+ Funcs map[string]FuncOffsets
+ Lines map[string]LineOffsets
+ CORERelos map[string]CORERelocationOffsets
+}
+
+// Section returns the FuncOffsets, LineOffsets and CORERelocationOffsets for
+// the given section name. Returns all nils if ExtInfos is nil, or individual
+// nils if there is no metadata of that type for the section.
+func (ei *ExtInfos) Section(name string) (FuncOffsets, LineOffsets, CORERelocationOffsets) {
+ if ei == nil {
+ return nil, nil, nil
+ }
+
+ return ei.Funcs[name], ei.Lines[name], ei.CORERelos[name]
}
// loadExtInfosFromELF parses ext infos from the .BTF.ext section in an ELF.
@@ -91,7 +102,7 @@ func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec) (*ExtInfos, er
return nil, fmt.Errorf("parsing CO-RE relocation info: %w", err)
}
- coreRelos := make(map[string]CORERelocationInfos, len(btfCORERelos))
+ coreRelos := make(map[string]CORERelocationOffsets, len(btfCORERelos))
for section, brs := range btfCORERelos {
coreRelos[section], err = newRelocationInfos(brs, spec, spec.strings)
if err != nil {
@@ -102,46 +113,6 @@ func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec) (*ExtInfos, er
return &ExtInfos{funcInfos, lineInfos, coreRelos}, nil
}
-type (
- funcInfoMeta struct{}
- coreRelocationMeta struct{}
-)
-
-// Assign per-section metadata from BTF to a section's instructions.
-func (ei *ExtInfos) Assign(insns asm.Instructions, section string) {
- funcInfos := ei.funcInfos[section]
- lineInfos := ei.lineInfos[section]
- reloInfos := ei.relocationInfos[section]
-
- AssignMetadataToInstructions(insns, funcInfos, lineInfos, reloInfos)
-}
-
-// Assign per-instruction metadata to the instructions in insns.
-func AssignMetadataToInstructions(
- insns asm.Instructions,
- funcInfos FuncOffsets,
- lineInfos LineOffsets,
- reloInfos CORERelocationInfos,
-) {
- iter := insns.Iterate()
- for iter.Next() {
- if len(funcInfos) > 0 && funcInfos[0].Offset == iter.Offset {
- *iter.Ins = WithFuncMetadata(*iter.Ins, funcInfos[0].Func)
- funcInfos = funcInfos[1:]
- }
-
- if len(lineInfos) > 0 && lineInfos[0].Offset == iter.Offset {
- *iter.Ins = iter.Ins.WithSource(lineInfos[0].Line)
- lineInfos = lineInfos[1:]
- }
-
- if len(reloInfos.infos) > 0 && reloInfos.infos[0].offset == iter.Offset {
- iter.Ins.Metadata.Set(coreRelocationMeta{}, reloInfos.infos[0].relo)
- reloInfos.infos = reloInfos.infos[1:]
- }
- }
-}
-
// MarshalExtInfos encodes function and line info embedded in insns into kernel
// wire format.
//
@@ -335,8 +306,8 @@ func parseExtInfoRecordSize(r io.Reader, bo binary.ByteOrder) (uint32, error) {
return recordSize, nil
}
-// FuncOffsets is a sorted slice of FuncOffset.
-type FuncOffsets []FuncOffset
+// FuncOffsets is a slice of FuncOffsets sorted by offset.
+type FuncOffsets = []FuncOffset
// The size of a FuncInfo in BTF wire format.
var FuncInfoSize = uint32(binary.Size(bpfFuncInfo{}))
@@ -466,7 +437,7 @@ func parseFuncInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, r
return nil, fmt.Errorf("expected FuncInfo record size %d, but BTF blob contains %d", exp, got)
}
- for i := uint32(0); i < recordNum; i++ {
+ for range recordNum {
if err := binary.Read(r, bo, &fi); err != nil {
return nil, fmt.Errorf("can't read function info: %v", err)
}
@@ -518,8 +489,8 @@ func (li *Line) String() string {
return li.line
}
-// LineOffsets contains a sorted list of line infos.
-type LineOffsets []LineOffset
+// LineOffsets is a slice of LineOffsets sorted by offset.
+type LineOffsets = []LineOffset
// LineOffset represents a line info and its raw instruction offset.
type LineOffset struct {
@@ -671,9 +642,15 @@ func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, r
return nil, fmt.Errorf("expected LineInfo record size %d, but BTF blob contains %d", exp, got)
}
- out := make([]bpfLineInfo, recordNum)
- if err := binary.Read(r, bo, out); err != nil {
- return nil, fmt.Errorf("can't read line info: %v", err)
+ out := make([]bpfLineInfo, 0)
+ chunk := make([]bpfLineInfo, min(1024, recordNum))
+ for remaining := recordNum; remaining > 0; {
+ n := min(uint32(1024), remaining)
+ if err := binary.Read(r, bo, chunk[:n]); err != nil {
+ return nil, fmt.Errorf("can't read line info: %v", err)
+ }
+ out = append(out, chunk[:n]...)
+ remaining -= n
}
if offsetInBytes {
@@ -713,22 +690,32 @@ func (cr *CORERelocation) String() string {
return fmt.Sprintf("CORERelocation(%s, %s[%s], local_id=%d)", cr.kind, cr.typ, cr.accessor, cr.id)
}
+type coreRelocationMeta struct{}
+
+// CORERelocationMetadata returns the CORERelocation associated with ins.
func CORERelocationMetadata(ins *asm.Instruction) *CORERelocation {
relo, _ := ins.Metadata.Get(coreRelocationMeta{}).(*CORERelocation)
return relo
}
-// CORERelocationInfos contains a sorted list of co:re relocation infos.
-type CORERelocationInfos struct {
- infos []coreRelocationInfo
+// WithCORERelocationMetadata associates a CORERelocation with ins and returns
+// the modified Instruction.
+func WithCORERelocationMetadata(ins asm.Instruction, relo *CORERelocation) asm.Instruction {
+ ins.Metadata.Set(coreRelocationMeta{}, relo)
+ return ins
}
-type coreRelocationInfo struct {
- relo *CORERelocation
- offset asm.RawInstructionOffset
+// CORERelocationOffsets is a slice of CORERelocationOffsets sorted by offset.
+type CORERelocationOffsets = []CORERelocationOffset
+
+// CORERelocationOffset represents a CO-RE relocation and an offset at which it
+// should be applied.
+type CORERelocationOffset struct {
+ Relo *CORERelocation
+ Offset asm.RawInstructionOffset
}
-func newRelocationInfo(relo bpfCORERelo, spec *Spec, strings *stringTable) (*coreRelocationInfo, error) {
+func newRelocationInfo(relo bpfCORERelo, spec *Spec, strings *stringTable) (*CORERelocationOffset, error) {
typ, err := spec.TypeByID(relo.TypeID)
if err != nil {
return nil, err
@@ -744,7 +731,7 @@ func newRelocationInfo(relo bpfCORERelo, spec *Spec, strings *stringTable) (*cor
return nil, fmt.Errorf("accessor %q: %s", accessorStr, err)
}
- return &coreRelocationInfo{
+ return &CORERelocationOffset{
&CORERelocation{
typ,
accessor,
@@ -755,20 +742,21 @@ func newRelocationInfo(relo bpfCORERelo, spec *Spec, strings *stringTable) (*cor
}, nil
}
-func newRelocationInfos(brs []bpfCORERelo, spec *Spec, strings *stringTable) (CORERelocationInfos, error) {
- rs := CORERelocationInfos{
- infos: make([]coreRelocationInfo, 0, len(brs)),
- }
+func newRelocationInfos(brs []bpfCORERelo, spec *Spec, strings *stringTable) (CORERelocationOffsets, error) {
+ rs := make(CORERelocationOffsets, 0, len(brs))
+
for _, br := range brs {
relo, err := newRelocationInfo(br, spec, strings)
if err != nil {
- return CORERelocationInfos{}, fmt.Errorf("offset %d: %w", br.InsnOff, err)
+ return nil, fmt.Errorf("offset %d: %w", br.InsnOff, err)
}
- rs.infos = append(rs.infos, *relo)
+ rs = append(rs, *relo)
}
- sort.Slice(rs.infos, func(i, j int) bool {
- return rs.infos[i].offset < rs.infos[j].offset
+
+ sort.Slice(rs, func(i, j int) bool {
+ return rs[i].Offset < rs[j].Offset
})
+
return rs, nil
}
@@ -812,7 +800,7 @@ func parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordNum uint32) ([
var out []bpfCORERelo
var relo bpfCORERelo
- for i := uint32(0); i < recordNum; i++ {
+ for range recordNum {
if err := binary.Read(r, bo, &relo); err != nil {
return nil, fmt.Errorf("can't read CO-RE relocation: %v", err)
}
diff --git a/vendor/github.com/cilium/ebpf/btf/feature.go b/vendor/github.com/cilium/ebpf/btf/feature.go
index 5b427f5d3..df365d42f 100644
--- a/vendor/github.com/cilium/ebpf/btf/feature.go
+++ b/vendor/github.com/cilium/ebpf/btf/feature.go
@@ -135,7 +135,7 @@ var haveEnum64 = internal.NewFeatureTest("ENUM64", func() error {
}, "6.0")
func probeBTF(typ Type) error {
- b, err := NewBuilder([]Type{typ})
+ b, err := NewBuilder([]Type{typ}, nil)
if err != nil {
return err
}
diff --git a/vendor/github.com/cilium/ebpf/btf/handle.go b/vendor/github.com/cilium/ebpf/btf/handle.go
index 2e483e9d9..6b432f5ae 100644
--- a/vendor/github.com/cilium/ebpf/btf/handle.go
+++ b/vendor/github.com/cilium/ebpf/btf/handle.go
@@ -1,7 +1,6 @@
package btf
import (
- "bytes"
"errors"
"fmt"
"math"
@@ -100,6 +99,10 @@ func NewHandleFromRawBTF(btf []byte) (*Handle, error) {
attr.BtfLogLevel = 1
}
+ if errors.Is(err, sys.ErrTokenCapabilities) {
+ return nil, fmt.Errorf("load btf: %w", err)
+ }
+
if err := haveBTF(); err != nil {
return nil, err
}
@@ -153,7 +156,7 @@ func (h *Handle) Spec(base *Spec) (*Spec, error) {
return nil, fmt.Errorf("missing base types")
}
- return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, base)
+ return loadRawSpec(btfBuffer, base)
}
// Close destroys the handle.
@@ -172,6 +175,24 @@ func (h *Handle) FD() int {
return h.fd.Int()
}
+// Clone creates a duplicate of the handle.
+//
+// Closing the duplicate does not affect the original, and vice versa.
+//
+// Cloning a nil Handle returns nil.
+func (h *Handle) Clone() (*Handle, error) {
+ if h == nil {
+ return nil, nil
+ }
+
+ dup, err := h.fd.Dup()
+ if err != nil {
+ return nil, fmt.Errorf("can't clone handle: %w", err)
+ }
+
+ return &Handle{dup, h.size, h.needsKernelBase}, nil
+}
+
// Info returns metadata about the handle.
func (h *Handle) Info() (*HandleInfo, error) {
return newHandleInfoFromFD(h.fd)
diff --git a/vendor/github.com/cilium/ebpf/btf/kernel.go b/vendor/github.com/cilium/ebpf/btf/kernel.go
index fcbe650ce..8e9084b51 100644
--- a/vendor/github.com/cilium/ebpf/btf/kernel.go
+++ b/vendor/github.com/cilium/ebpf/btf/kernel.go
@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path/filepath"
+ "runtime"
"slices"
"sort"
"sync"
@@ -12,59 +13,60 @@ import (
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/linux"
"github.com/cilium/ebpf/internal/platform"
+ "github.com/cilium/ebpf/internal/unix"
)
-// globalCache amortises decoding BTF across all users of the library.
-var globalCache = struct {
- sync.RWMutex
- kernel *Spec
- modules map[string]*Spec
-}{
- modules: make(map[string]*Spec),
-}
-
-// FlushKernelSpec removes any cached kernel type information.
-func FlushKernelSpec() {
- globalCache.Lock()
- defer globalCache.Unlock()
-
- globalCache.kernel = nil
- globalCache.modules = make(map[string]*Spec)
-}
-
// LoadKernelSpec returns the current kernel's BTF information.
//
-// Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file system
-// for vmlinux ELFs. Returns an error wrapping ErrNotSupported if BTF is not enabled.
+// Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file
+// system for vmlinux ELFs. Returns an error wrapping [ErrNotSupported] if BTF
+// is not enabled.
//
// Consider using [Cache] instead.
func LoadKernelSpec() (*Spec, error) {
- spec, err := loadCachedKernelSpec()
- return spec.Copy(), err
-}
+ if platform.IsWindows {
+ return nil, internal.ErrNotSupportedOnOS
+ }
-// load (and cache) the kernel spec.
-//
-// Does not copy Spec.
-func loadCachedKernelSpec() (*Spec, error) {
- globalCache.RLock()
- spec := globalCache.kernel
- globalCache.RUnlock()
+ fh, err := os.Open("/sys/kernel/btf/vmlinux")
+ if err == nil {
+ defer fh.Close()
+
+ info, err := fh.Stat()
+ if err != nil {
+ return nil, fmt.Errorf("stat vmlinux: %w", err)
+ }
+
+ // NB: It's not safe to mmap arbitrary files because mmap(2) doesn't
+ // guarantee that changes made after mmap are not visible in the mapping.
+ //
+ // This is not a problem for vmlinux, since it is always a read-only file.
+ raw, err := unix.Mmap(int(fh.Fd()), 0, int(info.Size()), unix.PROT_READ, unix.MAP_PRIVATE)
+ if err != nil {
+ return LoadSplitSpecFromReader(fh, nil)
+ }
+
+ spec, err := loadRawSpec(raw, nil)
+ if err != nil {
+ _ = unix.Munmap(raw)
+ return nil, fmt.Errorf("load vmlinux: %w", err)
+ }
+
+ runtime.AddCleanup(spec.decoder.sharedBuf, func(b []byte) {
+ _ = unix.Munmap(b)
+ }, raw)
- if spec != nil {
return spec, nil
}
- globalCache.Lock()
- defer globalCache.Unlock()
-
- spec, err := loadKernelSpec()
+ file, err := findVMLinux()
if err != nil {
return nil, err
}
+ defer file.Close()
- globalCache.kernel = spec
- return spec, nil
+ spec, err := LoadSpecFromReader(file)
+ return spec, err
}
// LoadKernelModuleSpec returns the BTF information for the named kernel module.
@@ -75,62 +77,11 @@ func loadCachedKernelSpec() (*Spec, error) {
// Returns an error wrapping ErrNotSupported if BTF is not enabled.
// Returns an error wrapping fs.ErrNotExist if BTF for the specific module doesn't exist.
func LoadKernelModuleSpec(module string) (*Spec, error) {
- spec, err := loadCachedKernelModuleSpec(module)
- return spec.Copy(), err
-}
-
-// load (and cache) a module spec.
-//
-// Does not copy Spec.
-func loadCachedKernelModuleSpec(module string) (*Spec, error) {
- globalCache.RLock()
- spec := globalCache.modules[module]
- globalCache.RUnlock()
-
- if spec != nil {
- return spec, nil
- }
-
- base, err := loadCachedKernelSpec()
- if err != nil {
- return nil, err
- }
-
- // NB: This only allows a single module to be parsed at a time. Not sure
- // it makes a difference.
- globalCache.Lock()
- defer globalCache.Unlock()
-
- spec, err = loadKernelModuleSpec(module, base)
+ base, err := LoadKernelSpec()
if err != nil {
return nil, err
}
-
- globalCache.modules[module] = spec
- return spec, nil
-}
-
-func loadKernelSpec() (_ *Spec, _ error) {
- if platform.IsWindows {
- return nil, internal.ErrNotSupportedOnOS
- }
-
- fh, err := os.Open("/sys/kernel/btf/vmlinux")
- if err == nil {
- defer fh.Close()
-
- spec, err := loadRawSpec(fh, internal.NativeEndian, nil)
- return spec, err
- }
-
- file, err := findVMLinux()
- if err != nil {
- return nil, err
- }
- defer file.Close()
-
- spec, err := LoadSpecFromReader(file)
- return spec, err
+ return loadKernelModuleSpec(module, base)
}
func loadKernelModuleSpec(module string, base *Spec) (*Spec, error) {
@@ -149,7 +100,7 @@ func loadKernelModuleSpec(module string, base *Spec) (*Spec, error) {
}
defer fh.Close()
- return loadRawSpec(fh, internal.NativeEndian, base)
+ return LoadSplitSpecFromReader(fh, base)
}
// findVMLinux scans multiple well-known paths for vmlinux kernel images.
@@ -188,52 +139,48 @@ func findVMLinux() (*os.File, error) {
// Cache allows to amortise the cost of decoding BTF across multiple call-sites.
//
-// It is not safe for concurrent use.
+// It is safe for concurrent use.
type Cache struct {
- KernelTypes *Spec
- ModuleTypes map[string]*Spec
- LoadedModules []string
+ mu sync.RWMutex
+ kernelTypes *Spec
+ moduleTypes map[string]*Spec
+ loadedModules []string
}
-// NewCache creates a new Cache.
+// NewCache returns an empty Cache.
//
-// Opportunistically reuses a global cache if possible.
+// Pass it to [CollectionOptions] to share kernel BTF across multiple
+// Collection loads.
func NewCache() *Cache {
- globalCache.RLock()
- defer globalCache.RUnlock()
-
- // This copy is either a no-op or very cheap, since the spec won't contain
- // any inflated types.
- kernel := globalCache.kernel.Copy()
- if kernel == nil {
- return &Cache{}
- }
-
- modules := make(map[string]*Spec, len(globalCache.modules))
- for name, spec := range globalCache.modules {
- decoder, _ := rebaseDecoder(spec.decoder, kernel.decoder)
- // NB: Kernel module BTF can't contain ELF fixups because it is always
- // read from sysfs.
- modules[name] = &Spec{decoder: decoder}
- }
-
- if len(modules) == 0 {
- return &Cache{kernel, nil, nil}
- }
-
- return &Cache{kernel, modules, nil}
+ return &Cache{}
}
// Kernel is equivalent to [LoadKernelSpec], except that repeated calls do
// not copy the Spec.
+//
+// Returns an error wrapping [ErrNotSupported] if BTF is not enabled.
func (c *Cache) Kernel() (*Spec, error) {
- if c.KernelTypes != nil {
- return c.KernelTypes, nil
+ c.mu.RLock()
+ if c.kernelTypes != nil {
+ spec := c.kernelTypes
+ c.mu.RUnlock()
+ return spec, nil
+ }
+ c.mu.RUnlock()
+
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ return c.kernel()
+}
+
+func (c *Cache) kernel() (*Spec, error) {
+ if c.kernelTypes != nil {
+ return c.kernelTypes, nil
}
var err error
- c.KernelTypes, err = LoadKernelSpec()
- return c.KernelTypes, err
+ c.kernelTypes, err = LoadKernelSpec()
+ return c.kernelTypes, err
}
// Module is equivalent to [LoadKernelModuleSpec], except that repeated calls do
@@ -241,40 +188,53 @@ func (c *Cache) Kernel() (*Spec, error) {
//
// All modules also share the return value of [Kernel] as their base.
func (c *Cache) Module(name string) (*Spec, error) {
- if spec := c.ModuleTypes[name]; spec != nil {
+ c.mu.RLock()
+ if spec := c.moduleTypes[name]; spec != nil {
+ c.mu.RUnlock()
return spec, nil
}
+ c.mu.RUnlock()
+
+ c.mu.Lock()
+ defer c.mu.Unlock()
- if c.ModuleTypes == nil {
- c.ModuleTypes = make(map[string]*Spec)
+ if spec := c.moduleTypes[name]; spec != nil {
+ return spec, nil
}
- base, err := c.Kernel()
- if err != nil {
- return nil, err
+ if c.moduleTypes == nil {
+ c.moduleTypes = make(map[string]*Spec)
}
- spec, err := loadCachedKernelModuleSpec(name)
+ base, err := c.kernel()
if err != nil {
return nil, err
}
- // Important: base is shared between modules. This allows inflating common
- // types only once.
- decoder, err := rebaseDecoder(spec.decoder, base.decoder)
+ spec, err := loadKernelModuleSpec(name, base)
if err != nil {
return nil, err
}
- spec = &Spec{decoder: decoder}
- c.ModuleTypes[name] = spec
- return spec, err
+ c.moduleTypes[name] = spec
+ return spec, nil
}
// Modules returns a sorted list of all loaded modules.
func (c *Cache) Modules() ([]string, error) {
- if c.LoadedModules != nil {
- return c.LoadedModules, nil
+ c.mu.RLock()
+ if c.loadedModules != nil {
+ modules := slices.Clone(c.loadedModules)
+ c.mu.RUnlock()
+ return modules, nil
+ }
+ c.mu.RUnlock()
+
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.loadedModules != nil {
+ return slices.Clone(c.loadedModules), nil
}
btfDir, err := os.Open("/sys/kernel/btf")
@@ -293,6 +253,6 @@ func (c *Cache) Modules() ([]string, error) {
})
sort.Strings(entries)
- c.LoadedModules = entries
- return entries, nil
+ c.loadedModules = entries
+ return slices.Clone(entries), nil
}
diff --git a/vendor/github.com/cilium/ebpf/btf/marshal.go b/vendor/github.com/cilium/ebpf/btf/marshal.go
index 308ce8d34..108660503 100644
--- a/vendor/github.com/cilium/ebpf/btf/marshal.go
+++ b/vendor/github.com/cilium/ebpf/btf/marshal.go
@@ -78,6 +78,13 @@ type Builder struct {
stableIDs map[Type]TypeID
// Explicitly added strings.
strings *stringTableBuilder
+ // Deduplication data structure.
+ deduper *deduper
+}
+
+type BuilderOptions struct {
+ // Deduplicate enables type deduplication.
+ Deduplicate bool
}
// NewBuilder creates a Builder from a list of types.
@@ -85,11 +92,20 @@ type Builder struct {
// It is more efficient than calling [Add] individually.
//
// Returns an error if adding any of the types fails.
-func NewBuilder(types []Type) (*Builder, error) {
+func NewBuilder(types []Type, opts *BuilderOptions) (*Builder, error) {
+ if opts == nil {
+ opts = &BuilderOptions{}
+ }
+
b := &Builder{
make([]Type, 0, len(types)),
make(map[Type]TypeID, len(types)),
nil,
+ nil,
+ }
+
+ if opts.Deduplicate {
+ b.deduper = newDeduper()
}
for _, typ := range types {
@@ -113,13 +129,25 @@ func (b *Builder) Empty() bool {
//
// See [Type] for details on identity.
func (b *Builder) Add(typ Type) (TypeID, error) {
+ if _, ok := typ.(*Void); ok {
+ // Equality is weird for void, since it is a zero sized type.
+ return 0, nil
+ }
+
+ if err := internal.IsNilPointer(typ); err != nil {
+ return 0, fmt.Errorf("invalid type: %w", err)
+ }
+
if b.stableIDs == nil {
b.stableIDs = make(map[Type]TypeID)
}
- if _, ok := typ.(*Void); ok {
- // Equality is weird for void, since it is a zero sized type.
- return 0, nil
+ if b.deduper != nil {
+ var err error
+ typ, err = b.deduper.deduplicate(typ)
+ if err != nil {
+ return 0, err
+ }
}
if ds, ok := typ.(*Datasec); ok {
@@ -144,6 +172,19 @@ func (b *Builder) Add(typ Type) (TypeID, error) {
return id, nil
}
+// Spec marshals the Builder's types and returns a new Spec to query them.
+//
+// The resulting Spec does not share any state with the Builder, subsequent
+// additions to the Builder will not affect the Spec.
+func (b *Builder) Spec() (*Spec, error) {
+ buf, err := b.Marshal(make([]byte, 0), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return loadRawSpec(buf, nil)
+}
+
// Marshal encodes all types in the Marshaler into BTF wire format.
//
// opts may be nil.
diff --git a/vendor/github.com/cilium/ebpf/btf/strings.go b/vendor/github.com/cilium/ebpf/btf/strings.go
index d09151c0e..3e3bd688e 100644
--- a/vendor/github.com/cilium/ebpf/btf/strings.go
+++ b/vendor/github.com/cilium/ebpf/btf/strings.go
@@ -10,7 +10,7 @@ import (
"sync"
)
-// stringTable is contains a sequence of null-terminated strings.
+// stringTable contains a sequence of null-terminated strings.
//
// It is safe for concurrent use.
type stringTable struct {
@@ -28,28 +28,30 @@ type sizedReader interface {
}
func readStringTable(r sizedReader, base *stringTable) (*stringTable, error) {
- // When parsing split BTF's string table, the first entry offset is derived
- // from the last entry offset of the base BTF.
- firstStringOffset := uint32(0)
- if base != nil {
- firstStringOffset = uint32(len(base.bytes))
- }
-
bytes := make([]byte, r.Size())
if _, err := io.ReadFull(r, bytes); err != nil {
return nil, err
}
- if len(bytes) == 0 {
- return nil, errors.New("string table is empty")
- }
+ return newStringTable(bytes, base)
+}
- if bytes[len(bytes)-1] != 0 {
- return nil, errors.New("string table isn't null terminated")
+func newStringTable(bytes []byte, base *stringTable) (*stringTable, error) {
+ // When parsing split BTF's string table, the first entry offset is derived
+ // from the last entry offset of the base BTF.
+ firstStringOffset := uint32(0)
+ if base != nil {
+ firstStringOffset = uint32(len(base.bytes))
}
- if firstStringOffset == 0 && bytes[0] != 0 {
- return nil, errors.New("first item in string table is non-empty")
+ if len(bytes) > 0 {
+ if bytes[len(bytes)-1] != 0 {
+ return nil, errors.New("string table isn't null terminated")
+ }
+
+ if firstStringOffset == 0 && bytes[0] != 0 {
+ return nil, errors.New("first item in string table is non-empty")
+ }
}
return &stringTable{base: base, bytes: bytes}, nil
@@ -92,6 +94,9 @@ func (st *stringTable) lookupSlow(offset uint32) ([]byte, error) {
}
i := bytes.IndexByte(st.bytes[offset:], 0)
+ if i == -1 {
+ return nil, fmt.Errorf("string at offset %d is not null terminated", offset)
+ }
return st.bytes[offset : offset+uint32(i)], nil
}
diff --git a/vendor/github.com/cilium/ebpf/btf/types.go b/vendor/github.com/cilium/ebpf/btf/types.go
index fc0a59744..f24beeb98 100644
--- a/vendor/github.com/cilium/ebpf/btf/types.go
+++ b/vendor/github.com/cilium/ebpf/btf/types.go
@@ -21,15 +21,25 @@ type TypeID = sys.TypeID
// Type represents a type described by BTF.
//
-// Identity of Type follows the [Go specification]: two Types are considered
-// equal if they have the same concrete type and the same dynamic value, aka
-// they point at the same location in memory. This means that the following
-// Types are considered distinct even though they have the same "shape".
+// A Type has three properties where compared to other Types.
+//
+// Identity: follows the [Go specification], two Types are considered identical
+// if they have the same concrete type and the same dynamic value, aka they point
+// at the same location in memory. This means that the following Types are
+// considered distinct even though they have the same "shape".
//
// a := &Int{Size: 1}
// b := &Int{Size: 1}
// a != b
//
+// Equivalence: two Types are considered equivalent if they have the same shape
+// and thus are functionally interchangeable, even if they are located at different
+// memory addresses. The above two Int types are equivalent.
+//
+// Compatibility: two Types are considered compatible according to the rules of CO-RE
+// see [coreAreTypesCompatible] for details. This is a non-commutative property,
+// so A may be compatible with B, but B not compatible with A.
+//
// [Go specification]: https://go.dev/ref/spec#Comparison_operators
type Type interface {
// Type can be formatted using the %s and %v verbs. %s outputs only the
@@ -50,7 +60,7 @@ type Type interface {
// Make a copy of the type, without copying Type members.
copy() Type
- // New implementations must update walkType.
+ // New implementations must update children, deduper.typeHash, and typesEquivalent.
}
var (
@@ -170,8 +180,15 @@ type Struct struct {
Tags []string
}
+// Format supports the width option to %v to limit or extend the number of
+// struct member names printed (default 5).
+//
+// For example, %1v will print only the first member name followed by '...':
+//
+// Struct:"struct"[fields=3 fieldNames=[a ...]]
func (s *Struct) Format(fs fmt.State, verb rune) {
- formatType(fs, verb, s, "fields=", len(s.Members))
+ max, _ := fs.Width()
+ formatType(fs, verb, s, "fields=", len(s.Members), "fieldNames=", memberNames(s.Members, max))
}
func (s *Struct) TypeName() string { return s.Name }
@@ -198,8 +215,15 @@ type Union struct {
Tags []string
}
+// Format supports the width option to %v to limit or extend the number of
+// union member names printed (default 5).
+//
+// For example, %1v will print only the first member name followed by '...':
+//
+// Union:"union"[fields=3 fieldNames=[a ...]]
func (u *Union) Format(fs fmt.State, verb rune) {
- formatType(fs, verb, u, "fields=", len(u.Members))
+ max, _ := fs.Width()
+ formatType(fs, verb, u, "fields=", len(u.Members), "fieldNames=", memberNames(u.Members, max))
}
func (u *Union) TypeName() string { return u.Name }
@@ -217,6 +241,29 @@ func (u *Union) members() []Member {
return u.Members
}
+// memberNames returns the names of members, or its index in the list of members
+// if the name is empty.
+//
+// Returns up to max entries (default 5), followed by '...'.
+func memberNames(members []Member, max int) []string {
+ if max <= 0 {
+ max = 5
+ }
+ names := make([]string, min(len(members), max+1))
+ for i, m := range members {
+ if i >= max {
+ names[i] = "..."
+ break
+ }
+ if m.Name == "" {
+ names[i] = fmt.Sprintf("<%d>", i)
+ continue
+ }
+ names[i] = m.Name
+ }
+ return names
+}
+
func copyMembers(orig []Member) []Member {
cpy := make([]Member, len(orig))
copy(cpy, orig)
@@ -428,6 +475,8 @@ type Func struct {
ParamTags [][]string
}
+type funcInfoMeta struct{}
+
func FuncMetadata(ins *asm.Instruction) *Func {
fn, _ := ins.Metadata.Get(funcInfoMeta{}).(*Func)
return fn
@@ -644,7 +693,7 @@ func Sizeof(typ Type) (int, error) {
elem int64
)
- for i := 0; i < maxResolveDepth; i++ {
+ for range maxResolveDepth {
switch v := typ.(type) {
case *Array:
if n > 0 && int64(v.Nelems) > math.MaxInt64/n {
@@ -840,7 +889,7 @@ type formattableType interface {
// Handles cyclical types by only printing cycles up to a certain depth. Elements
// in extra are separated by spaces unless the preceding element is a string
// ending in '='.
-func formatType(f fmt.State, verb rune, t formattableType, extra ...interface{}) {
+func formatType(f fmt.State, verb rune, t formattableType, extra ...any) {
if verb != 'v' && verb != 's' {
fmt.Fprintf(f, "{UNRECOGNIZED: %c}", verb)
return
diff --git a/vendor/github.com/cilium/ebpf/btf/unmarshal.go b/vendor/github.com/cilium/ebpf/btf/unmarshal.go
index fdcf583da..b56b5a068 100644
--- a/vendor/github.com/cilium/ebpf/btf/unmarshal.go
+++ b/vendor/github.com/cilium/ebpf/btf/unmarshal.go
@@ -13,13 +13,22 @@ import (
"sync"
)
+// sharedBuf is a buffer which may be shared between multiple decoders.
+//
+// It must not be modified. Some sharedBuf may be backed by an mmap-ed file, in
+// which case the sharedBuf has a finalizer. sharedBuf must therefore always be
+// passed as a pointer.
+type sharedBuf struct {
+ raw []byte
+}
+
type decoder struct {
// Immutable fields, may be shared.
base *decoder
byteOrder binary.ByteOrder
- raw []byte
- strings *stringTable
+ *sharedBuf
+ strings *stringTable
// The ID for offsets[0].
firstTypeID TypeID
// Map from TypeID to offset of the marshaled data in raw. Contains an entry
@@ -121,7 +130,7 @@ func newDecoder(raw []byte, bo binary.ByteOrder, strings *stringTable, base *dec
return &decoder{
base,
bo,
- raw,
+ &sharedBuf{raw},
strings,
firstTypeID,
offsets,
@@ -165,31 +174,6 @@ func allBtfTypeOffsets(buf []byte, bo binary.ByteOrder, header *btfType) iter.Se
}
}
-func rebaseDecoder(d *decoder, base *decoder) (*decoder, error) {
- if d.base == nil {
- return nil, fmt.Errorf("rebase split spec: not a split spec")
- }
-
- if len(d.base.raw) != len(base.raw) || (len(d.base.raw) > 0 && &d.base.raw[0] != &base.raw[0]) {
- return nil, fmt.Errorf("rebase split spec: raw BTF differs")
- }
-
- return &decoder{
- base,
- d.byteOrder,
- d.raw,
- d.strings,
- d.firstTypeID,
- d.offsets,
- d.declTags,
- d.namedTypes,
- sync.Mutex{},
- make(map[TypeID]Type),
- make(map[Type]TypeID),
- make(map[TypeID][2]Bits),
- }, nil
-}
-
// Copy performs a deep copy of a decoder and its base.
func (d *decoder) Copy() *decoder {
if d == nil {
@@ -220,7 +204,7 @@ func (d *decoder) copy(copiedTypes map[Type]Type) *decoder {
return &decoder{
d.base.copy(copiedTypes),
d.byteOrder,
- d.raw,
+ d.sharedBuf,
d.strings,
d.firstTypeID,
d.offsets,
@@ -564,7 +548,7 @@ func (d *decoder) inflateType(id TypeID) (typ Type, err error) {
vlen := header.Vlen()
vars := make([]VarSecinfo, 0, vlen)
var bSecInfo btfVarSecinfo
- for i := 0; i < vlen; i++ {
+ for i := range vlen {
n, err := unmarshalBtfVarSecInfo(&bSecInfo, pos, d.byteOrder)
if err != nil {
return nil, fmt.Errorf("can't unmarshal btfVarSecinfo %d, id: %d: %w", i, id, err)
diff --git a/vendor/github.com/cilium/ebpf/collection.go b/vendor/github.com/cilium/ebpf/collection.go
index 77476bd67..42621e74c 100644
--- a/vendor/github.com/cilium/ebpf/collection.go
+++ b/vendor/github.com/cilium/ebpf/collection.go
@@ -6,9 +6,10 @@ import (
"fmt"
"path/filepath"
"reflect"
+ "runtime"
+ "slices"
"strings"
- "github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/btf"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/kallsyms"
@@ -35,6 +36,12 @@ type CollectionOptions struct {
// The given Maps are Clone()d before being used in the Collection, so the
// caller can Close() them freely when they are no longer needed.
MapReplacements map[string]*Map
+
+ // Cache amortises the cost of decoding kernel BTF across multiple
+ // Collection loads. Callers loading more than one Collection should
+ // allocate a Cache via [btf.NewCache] and share it across loads.
+ // If nil, a fresh cache is allocated per load and discarded.
+ Cache *btf.Cache
}
// CollectionSpec describes a collection.
@@ -71,7 +78,7 @@ func (cs *CollectionSpec) Copy() *CollectionSpec {
}
for name, spec := range cs.Variables {
- cpy.Variables[name] = spec.copy(&cpy)
+ cpy.Variables[name] = spec.Copy()
}
if cs.Variables == nil {
cpy.Variables = nil
@@ -93,97 +100,6 @@ func copyMapOfSpecs[T interface{ Copy() T }](m map[string]T) map[string]T {
return cpy
}
-// RewriteMaps replaces all references to specific maps.
-//
-// Use this function to use pre-existing maps instead of creating new ones
-// when calling NewCollection. Any named maps are removed from CollectionSpec.Maps.
-//
-// Returns an error if a named map isn't used in at least one program.
-//
-// Deprecated: Pass CollectionOptions.MapReplacements when loading the Collection
-// instead.
-func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error {
- for symbol, m := range maps {
- // have we seen a program that uses this symbol / map
- seen := false
- for progName, progSpec := range cs.Programs {
- err := progSpec.Instructions.AssociateMap(symbol, m)
-
- switch {
- case err == nil:
- seen = true
-
- case errors.Is(err, asm.ErrUnreferencedSymbol):
- // Not all programs need to use the map
-
- default:
- return fmt.Errorf("program %s: %w", progName, err)
- }
- }
-
- if !seen {
- return fmt.Errorf("map %s not referenced by any programs", symbol)
- }
-
- // Prevent NewCollection from creating rewritten maps
- delete(cs.Maps, symbol)
- }
-
- return nil
-}
-
-// MissingConstantsError is returned by [CollectionSpec.RewriteConstants].
-type MissingConstantsError struct {
- // The constants missing from .rodata.
- Constants []string
-}
-
-func (m *MissingConstantsError) Error() string {
- return fmt.Sprintf("some constants are missing from .rodata: %s", strings.Join(m.Constants, ", "))
-}
-
-// RewriteConstants replaces the value of multiple constants.
-//
-// The constant must be defined like so in the C program:
-//
-// volatile const type foobar;
-// volatile const type foobar = default;
-//
-// Replacement values must be of the same length as the C sizeof(type).
-// If necessary, they are marshalled according to the same rules as
-// map values.
-//
-// From Linux 5.5 the verifier will use constants to eliminate dead code.
-//
-// Returns an error wrapping [MissingConstantsError] if a constant doesn't exist.
-//
-// Deprecated: Use [CollectionSpec.Variables] to interact with constants instead.
-// RewriteConstants is now a wrapper around the VariableSpec API.
-func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error {
- var missing []string
- for n, c := range consts {
- v, ok := cs.Variables[n]
- if !ok {
- missing = append(missing, n)
- continue
- }
-
- if !v.Constant() {
- return fmt.Errorf("variable %s is not a constant", n)
- }
-
- if err := v.Set(c); err != nil {
- return fmt.Errorf("rewriting constant %s: %w", n, err)
- }
- }
-
- if len(missing) != 0 {
- return fmt.Errorf("rewrite constants: %w", &MissingConstantsError{Constants: missing})
- }
-
- return nil
-}
-
// Assign the contents of a CollectionSpec to a struct.
//
// This function is a shortcut to manually checking the presence
@@ -205,22 +121,22 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error
//
// Returns an error if any of the eBPF objects can't be found, or
// if the same Spec is assigned multiple times.
-func (cs *CollectionSpec) Assign(to interface{}) error {
- getValue := func(typ reflect.Type, name string) (interface{}, error) {
+func (cs *CollectionSpec) Assign(to any) error {
+ getValue := func(typ reflect.Type, name string) (any, error) {
switch typ {
- case reflect.TypeOf((*ProgramSpec)(nil)):
+ case reflect.TypeFor[*ProgramSpec]():
if p := cs.Programs[name]; p != nil {
return p, nil
}
return nil, fmt.Errorf("missing program %q", name)
- case reflect.TypeOf((*MapSpec)(nil)):
+ case reflect.TypeFor[*MapSpec]():
if m := cs.Maps[name]; m != nil {
return m, nil
}
return nil, fmt.Errorf("missing map %q", name)
- case reflect.TypeOf((*VariableSpec)(nil)):
+ case reflect.TypeFor[*VariableSpec]():
if v := cs.Variables[name]; v != nil {
return v, nil
}
@@ -261,7 +177,7 @@ func (cs *CollectionSpec) Assign(to interface{}) error {
//
// Returns an error if any of the fields can't be found, or
// if the same Map or Program is assigned multiple times.
-func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error {
+func (cs *CollectionSpec) LoadAndAssign(to any, opts *CollectionOptions) error {
loader, err := newCollectionLoader(cs, opts)
if err != nil {
return err
@@ -273,18 +189,18 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
assignedProgs := make(map[string]bool)
assignedVars := make(map[string]bool)
- getValue := func(typ reflect.Type, name string) (interface{}, error) {
+ getValue := func(typ reflect.Type, name string) (any, error) {
switch typ {
- case reflect.TypeOf((*Program)(nil)):
+ case reflect.TypeFor[*Program]():
assignedProgs[name] = true
return loader.loadProgram(name)
- case reflect.TypeOf((*Map)(nil)):
+ case reflect.TypeFor[*Map]():
assignedMaps[name] = true
return loader.loadMap(name)
- case reflect.TypeOf((*Variable)(nil)):
+ case reflect.TypeFor[*Variable]():
assignedVars[name] = true
return loader.loadVariable(name)
@@ -295,12 +211,12 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
// Load the Maps and Programs requested by the annotated struct.
if err := assignValues(to, getValue); err != nil {
- return err
+ return fmt.Errorf("assign values: %w", err)
}
// Populate the requested maps. Has a chance of lazy-loading other dependent maps.
if err := loader.populateDeferredMaps(); err != nil {
- return err
+ return fmt.Errorf("populate deferred maps: %w", err)
}
// Evaluate the loader's objects after all (lazy)loading has taken place.
@@ -431,13 +347,18 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec
return nil, fmt.Errorf("populating kallsyms caches: %w", err)
}
+ cache := opts.Cache
+ if cache == nil {
+ cache = btf.NewCache()
+ }
+
return &collectionLoader{
coll,
opts,
make(map[string]*Map),
make(map[string]*Program),
make(map[string]*Variable),
- newBTFCache(&opts.Programs),
+ cache,
}, nil
}
@@ -445,20 +366,6 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec
// during individual program loading. Since we have less context available
// at those stages, we batch the lookups here instead to avoid redundant work.
func populateKallsyms(progs map[string]*ProgramSpec) error {
- // Look up associated kernel modules for all symbols referenced by
- // ProgramSpec.AttachTo for program types that support attaching to kmods.
- mods := make(map[string]string)
- for _, p := range progs {
- if p.AttachTo != "" && p.targetsKernelModule() {
- mods[p.AttachTo] = ""
- }
- }
- if len(mods) != 0 {
- if err := kallsyms.AssignModules(mods); err != nil {
- return fmt.Errorf("getting modules from kallsyms: %w", err)
- }
- }
-
// Look up addresses of all kernel symbols referenced by all programs.
addrs := make(map[string]uint64)
for _, p := range progs {
@@ -526,7 +433,11 @@ func (cl *collectionLoader) loadMap(mapName string) (*Map, error) {
return m, nil
}
- m, err := newMapWithOptions(mapSpec, cl.opts.Maps)
+ if err := mapSpec.updateDataSection(cl.coll.Variables, mapName); err != nil {
+ return nil, fmt.Errorf("assembling contents of map %s: %w", mapName, err)
+ }
+
+ m, err := newMapWithOptions(mapSpec, cl.opts.Maps, cl.types)
if err != nil {
return nil, fmt.Errorf("map %s: %w", mapName, err)
}
@@ -595,6 +506,7 @@ func (cl *collectionLoader) loadProgram(progName string) (*Program, error) {
}
cl.programs[progName] = prog
+
return prog, nil
}
@@ -608,19 +520,7 @@ func (cl *collectionLoader) loadVariable(varName string) (*Variable, error) {
return nil, fmt.Errorf("unknown variable %s", varName)
}
- // Get the key of the VariableSpec's MapSpec in the CollectionSpec.
- var mapName string
- for n, ms := range cl.coll.Maps {
- if ms == varSpec.m {
- mapName = n
- break
- }
- }
- if mapName == "" {
- return nil, fmt.Errorf("variable %s: underlying MapSpec %s was removed from CollectionSpec", varName, varSpec.m.Name)
- }
-
- m, err := cl.loadMap(mapName)
+ m, err := cl.loadMap(varSpec.SectionName)
if err != nil {
return nil, fmt.Errorf("variable %s: %w", varName, err)
}
@@ -637,14 +537,14 @@ func (cl *collectionLoader) loadVariable(varName string) (*Variable, error) {
mm, err = m.Memory()
}
if err != nil && !errors.Is(err, ErrNotSupported) {
- return nil, fmt.Errorf("variable %s: getting memory for map %s: %w", varName, mapName, err)
+ return nil, fmt.Errorf("variable %s: getting memory for map %s: %w", varName, varSpec.SectionName, err)
}
v, err := newVariable(
- varSpec.name,
- varSpec.offset,
- varSpec.size,
- varSpec.t,
+ varSpec.Name,
+ varSpec.Offset,
+ varSpec.Size(),
+ varSpec.Type,
mm,
)
if err != nil {
@@ -702,6 +602,13 @@ func (cl *collectionLoader) populateDeferredMaps() error {
}
}
+ if mapSpec.Type == StructOpsMap {
+ // populate StructOps data into `kernVData`
+ if err := cl.populateStructOps(m, mapSpec); err != nil {
+ return err
+ }
+ }
+
// Populate and freeze the map if specified.
if err := m.finalize(mapSpec); err != nil {
return fmt.Errorf("populating map %s: %w", mapName, err)
@@ -711,6 +618,97 @@ func (cl *collectionLoader) populateDeferredMaps() error {
return nil
}
+// populateStructOps translates the user struct bytes into the kernel value struct
+// layout for a struct_ops map and writes the result back to mapSpec.Contents[0].
+func (cl *collectionLoader) populateStructOps(m *Map, mapSpec *MapSpec) error {
+ userType, ok := btf.As[*btf.Struct](mapSpec.Value)
+ if !ok {
+ return fmt.Errorf("value should be a *Struct")
+ }
+
+ userData, err := mapSpec.dataSection()
+ if err != nil {
+ return fmt.Errorf("getting data section: %w", err)
+ }
+ if len(userData) < int(userType.Size) {
+ return fmt.Errorf("user data too short: have %d, need at least %d", len(userData), userType.Size)
+ }
+
+ vType, _, module, err := structOpsFindTarget(userType, cl.types)
+ if err != nil {
+ return fmt.Errorf("struct_ops value type %q: %w", userType.Name, err)
+ }
+ defer module.Close()
+
+ // Find the inner ops struct embedded in the value struct.
+ kType, kTypeOff, err := structOpsFindInnerType(vType)
+ if err != nil {
+ return err
+ }
+
+ kernVData := make([]byte, int(vType.Size))
+ for _, m := range userType.Members {
+ i := slices.IndexFunc(kType.Members, func(km btf.Member) bool {
+ return km.Name == m.Name
+ })
+
+ // Allow field to not exist in target as long as the source is zero.
+ if i == -1 {
+ mSize, err := btf.Sizeof(m.Type)
+ if err != nil {
+ return fmt.Errorf("sizeof(user.%s): %w", m.Name, err)
+ }
+ srcOff := int(m.Offset.Bytes())
+ if srcOff < 0 || srcOff+mSize > len(userData) {
+ return fmt.Errorf("member %q: userdata is too small", m.Name)
+ }
+
+ // let fail if the field in type user type is missing in type kern type
+ if !structOpsIsMemZeroed(userData[srcOff : srcOff+mSize]) {
+ return fmt.Errorf("%s doesn't exist in %s, but it has non-zero value", m.Name, kType.Name)
+ }
+
+ continue
+ }
+
+ km := kType.Members[i]
+
+ switch btf.UnderlyingType(m.Type).(type) {
+ case *btf.Pointer:
+ // If this is a pointer → resolve struct_ops program.
+ psKey := kType.Name + ":" + m.Name
+ for k, ps := range cl.coll.Programs {
+ if ps.AttachTo == psKey {
+ p, ok := cl.programs[k]
+ if !ok || p == nil {
+ return nil
+ }
+ if err := structOpsPopulateValue(km, kernVData[kTypeOff:], p); err != nil {
+ return err
+ }
+ }
+ }
+
+ default:
+ // Otherwise → memcpy the field contents.
+ if err := structOpsCopyMember(m, km, userData, kernVData[kTypeOff:]); err != nil {
+ return fmt.Errorf("field %s: %w", kType.Name, err)
+ }
+ }
+ }
+
+ // Populate the map explicitly and keep a reference on cl.programs.
+ // This is necessary because we may inline fds into kernVData which
+ // may become invalid if the GC frees them.
+ if err := m.Put(uint32(0), kernVData); err != nil {
+ return err
+ }
+ mapSpec.Contents = nil
+ runtime.KeepAlive(cl.programs)
+
+ return nil
+}
+
// resolveKconfig resolves all variables declared in .kconfig and populates
// m.Contents. Does nothing if the given m.Contents is non-empty.
func resolveKconfig(m *MapSpec) error {
@@ -851,31 +849,31 @@ func LoadCollection(file string) (*Collection, error) {
//
// Ownership and Close()ing responsibility is transferred to `to`
// for any successful assigns. On error `to` is left in an undefined state.
-func (coll *Collection) Assign(to interface{}) error {
+func (coll *Collection) Assign(to any) error {
assignedMaps := make(map[string]bool)
assignedProgs := make(map[string]bool)
assignedVars := make(map[string]bool)
// Assign() only transfers already-loaded Maps and Programs. No extra
// loading is done.
- getValue := func(typ reflect.Type, name string) (interface{}, error) {
+ getValue := func(typ reflect.Type, name string) (any, error) {
switch typ {
- case reflect.TypeOf((*Program)(nil)):
+ case reflect.TypeFor[*Program]():
if p := coll.Programs[name]; p != nil {
assignedProgs[name] = true
return p, nil
}
return nil, fmt.Errorf("missing program %q", name)
- case reflect.TypeOf((*Map)(nil)):
+ case reflect.TypeFor[*Map]():
if m := coll.Maps[name]; m != nil {
assignedMaps[name] = true
return m, nil
}
return nil, fmt.Errorf("missing map %q", name)
- case reflect.TypeOf((*Variable)(nil)):
+ case reflect.TypeFor[*Variable]():
if v := coll.Variables[name]; v != nil {
assignedVars[name] = true
return v, nil
@@ -888,7 +886,7 @@ func (coll *Collection) Assign(to interface{}) error {
}
if err := assignValues(to, getValue); err != nil {
- return err
+ return fmt.Errorf("assign values: %w", err)
}
// Finalize ownership transfer
@@ -976,7 +974,7 @@ func ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]struc
// to a struct, attempt to gather its fields as well.
var v reflect.Value
switch field.Type.Kind() {
- case reflect.Ptr:
+ case reflect.Pointer:
if field.Type.Elem().Kind() != reflect.Struct {
continue
}
@@ -1011,18 +1009,16 @@ func ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]struc
//
// getValue is called for every tagged field of 'to' and must return the value
// to be assigned to the field with the given typ and name.
-func assignValues(to interface{},
- getValue func(typ reflect.Type, name string) (interface{}, error)) error {
+func assignValues(to any, getValue func(typ reflect.Type, name string) (any, error)) error {
+ if err := internal.IsNilPointer(to); err != nil {
+ return err
+ }
toValue := reflect.ValueOf(to)
- if toValue.Type().Kind() != reflect.Ptr {
+ if toValue.Type().Kind() != reflect.Pointer {
return fmt.Errorf("%T is not a pointer to struct", to)
}
- if toValue.IsNil() {
- return fmt.Errorf("nil pointer to %T", to)
- }
-
fields, err := ebpfFields(toValue.Elem(), nil)
if err != nil {
return err
diff --git a/vendor/github.com/cilium/ebpf/collection_windows.go b/vendor/github.com/cilium/ebpf/collection_windows.go
index c1bbaa21d..38ce73e8b 100644
--- a/vendor/github.com/cilium/ebpf/collection_windows.go
+++ b/vendor/github.com/cilium/ebpf/collection_windows.go
@@ -11,8 +11,8 @@ import (
)
func loadCollectionFromNativeImage(file string) (_ *Collection, err error) {
- mapFds := make([]efw.FD, 16)
- programFds := make([]efw.FD, 16)
+ mapFds := make([]efw.FD, 32)
+ programFds := make([]efw.FD, 32)
var maps map[string]*Map
var programs map[string]*Program
diff --git a/vendor/github.com/cilium/ebpf/elf_reader.go b/vendor/github.com/cilium/ebpf/elf_reader.go
index e2b21fa57..e2f264912 100644
--- a/vendor/github.com/cilium/ebpf/elf_reader.go
+++ b/vendor/github.com/cilium/ebpf/elf_reader.go
@@ -8,6 +8,8 @@ import (
"errors"
"fmt"
"io"
+ "iter"
+ "maps"
"math"
"os"
"slices"
@@ -116,8 +118,17 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
case sec.Type == elf.SHT_REL:
// Store relocations under the section index of the target
relSections[elf.SectionIndex(sec.Info)] = sec
- case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0:
- sections[idx] = newElfSection(sec, programSection)
+ case sec.Type == elf.SHT_PROGBITS && sec.Size > 0:
+ if (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0 {
+ sections[idx] = newElfSection(sec, programSection)
+ } else if sec.Name == structOpsLinkSec {
+ // classification based on sec names so that struct_ops-specific
+ // sections (.struct_ops.link) is correctly recognized
+ // as non-executable PROGBITS, allowing value placement and link metadata to be loaded.
+ sections[idx] = newElfSection(sec, structOpsSection)
+ } else if sec.Name == structOpsSec {
+ return nil, fmt.Errorf("section %q: got '.struct_ops' section: %w", sec.Name, ErrNotSupported)
+ }
}
}
@@ -186,6 +197,11 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
return nil, fmt.Errorf("load programs: %w", err)
}
+ // assiociate members in structs with ProgramSpecs using relo
+ if err := ec.associateStructOpsRelocs(progs); err != nil {
+ return nil, fmt.Errorf("load struct_ops: %w", err)
+ }
+
return &CollectionSpec{
ec.maps,
progs,
@@ -239,6 +255,7 @@ const (
btfMapSection
programSection
dataSection
+ structOpsSection
)
type elfSection struct {
@@ -263,6 +280,17 @@ func newElfSection(section *elf.Section, kind elfSectionKind) *elfSection {
}
}
+// symbolsSorted returns the section's symbols sorted by offset.
+func (es *elfSection) symbolsSorted() iter.Seq2[uint64, elf.Symbol] {
+ return func(yield func(uint64, elf.Symbol) bool) {
+ for _, off := range slices.Sorted(maps.Keys(es.symbols)) {
+ if !yield(off, es.symbols[off]) {
+ return
+ }
+ }
+ }
+}
+
// assignSymbols takes a list of symbols and assigns them to their
// respective sections, indexed by name.
func (ec *elfCode) assignSymbols(symbols []elf.Symbol) {
@@ -290,10 +318,11 @@ func (ec *elfCode) assignSymbols(symbols []elf.Symbol) {
if symType != elf.STT_NOTYPE && symType != elf.STT_FUNC {
continue
}
- // LLVM emits LBB_ (Local Basic Block) symbols that seem to be jump
- // targets within sections, but BPF has no use for them.
- if symType == elf.STT_NOTYPE && elf.ST_BIND(symbol.Info) == elf.STB_LOCAL &&
- strings.HasPrefix(symbol.Name, "LBB") {
+
+ // Program sections may contain NOTYPE symbols with local scope, these are
+ // usually labels for jumps. We do not care for these for the purposes of
+ // linking and they may overlap with function symbols.
+ if symType == elf.STT_NOTYPE && elf.ST_BIND(symbol.Info) == elf.STB_LOCAL {
continue
}
// Only collect symbols that occur in program/maps/data sections.
@@ -353,6 +382,10 @@ func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) {
return nil, fmt.Errorf("section %v: missing symbols", sec.Name)
}
+ if sec.ReaderAt == nil {
+ return nil, fmt.Errorf("compressed program section is not supported")
+ }
+
funcs, err := ec.loadFunctions(sec)
if err != nil {
return nil, fmt.Errorf("section %v: %w", sec.Name, err)
@@ -405,47 +438,106 @@ func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) {
// be narrowed down to STT_NOTYPE (emitted by clang <8) or STT_FUNC.
//
// The resulting map is indexed by function name.
-func (ec *elfCode) loadFunctions(section *elfSection) (map[string]asm.Instructions, error) {
- r := bufio.NewReader(section.Open())
-
- // Decode the section's instruction stream.
- insns := make(asm.Instructions, 0, section.Size/asm.InstructionSize)
- insns, err := asm.AppendInstructions(insns, r, ec.ByteOrder, platform.Linux)
- if err != nil {
- return nil, fmt.Errorf("decoding instructions for section %s: %w", section.Name, err)
- }
- if len(insns) == 0 {
- return nil, fmt.Errorf("no instructions found in section %s", section.Name)
- }
+func (ec *elfCode) loadFunctions(sec *elfSection) (map[string]asm.Instructions, error) {
+ progs := make(map[string]asm.Instructions)
+
+ // Pull out ExtInfos once per section to avoid map lookups on every
+ // instruction.
+ fo, lo, ro := ec.extInfo.Section(sec.Name)
+
+ // Raw instruction count since start of the section. ExtInfos point at raw
+ // insn offsets and ignore the gaps between symbols in case of linked objects.
+ // We need to count them, we can't obtain this info by any other means.
+ var raw asm.RawInstructionOffset
+
+ // Sort symbols by offset so we can track instructions by their raw offsets.
+ for _, sym := range sec.symbolsSorted() {
+ if progs[sym.Name] != nil {
+ return nil, fmt.Errorf("duplicate symbol %s in section %s", sym.Name, sec.Name)
+ }
- iter := insns.Iterate()
- for iter.Next() {
- ins := iter.Ins
- offset := iter.Offset.Bytes()
+ if sym.Value > math.MaxUint32 || sym.Size > math.MaxUint32 {
+ return nil, fmt.Errorf("symbol %s: offset or size exceeds 32 bits in section %s", sym.Name, sec.Name)
+ }
+ if sym.Value+sym.Size > sec.Size {
+ return nil, fmt.Errorf("symbol %s: size goes out of bounds of section %s", sym.Name, sec.Name)
+ }
- // Tag Symbol Instructions.
- if sym, ok := section.symbols[offset]; ok {
- *ins = ins.WithSymbol(sym.Name)
+ // Decode the symbol's instruction stream, limited to its size.
+ sr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size))
+ insns := make(asm.Instructions, 0, sym.Size/asm.InstructionSize)
+ insns, err := asm.AppendInstructions(insns, sr, ec.ByteOrder, platform.Linux)
+ if err != nil {
+ return nil, fmt.Errorf("decoding instructions for symbol %s in section %s: %w", sym.Name, sec.Name, err)
+ }
+ if len(insns) == 0 {
+ return nil, fmt.Errorf("no instructions found for symbol %s in section %s", sym.Name, sec.Name)
}
- // Apply any relocations for the current instruction.
- // If no relocation is present, resolve any section-relative function calls.
- if rel, ok := section.relocations[offset]; ok {
- if err := ec.relocateInstruction(ins, rel); err != nil {
- return nil, fmt.Errorf("offset %d: relocating instruction: %w", offset, err)
- }
- } else {
- if err := referenceRelativeJump(ins, offset, section.symbols); err != nil {
- return nil, fmt.Errorf("offset %d: resolving relative jump: %w", offset, err)
+ // Mark the first instruction as the start of a function.
+ insns[0] = insns[0].WithSymbol(sym.Name)
+
+ iter := insns.Iterate()
+ for iter.Next() {
+ // Global byte offset of the instruction within the ELF section.
+ offset := sym.Value + iter.Offset.Bytes()
+
+ // Apply any relocations for the current instruction. If no relocation is
+ // present, resolve any section-relative function calls.
+ if rel, ok := sec.relocations[offset]; ok {
+ if err := ec.relocateInstruction(iter.Ins, rel); err != nil {
+ return nil, fmt.Errorf("offset %d in section %s: relocating instruction: %w", offset, sec.Name, err)
+ }
+ } else {
+ if err := referenceRelativeJump(iter.Ins, offset, sec.symbols); err != nil {
+ return nil, fmt.Errorf("offset %d in section %s: resolving relative jump: %w", offset, sec.Name, err)
+ }
}
+
+ assignMetadata(iter.Ins, raw, &fo, &lo, &ro)
+
+ raw += iter.Ins.Width()
}
+
+ // Emit the program's instructions.
+ progs[sym.Name] = insns
+ }
+
+ return progs, nil
+}
+
+// take pops and returns the first item in q if it matches the given predicate
+// f. Otherwise, it returns nil.
+func take[T any](q *[]T, f func(T) bool) *T {
+ if q == nil || len(*q) == 0 {
+ return nil
}
- if ec.extInfo != nil {
- ec.extInfo.Assign(insns, section.Name)
+ out := (*q)[0]
+ if f(out) {
+ *q = (*q)[1:]
+ return &out
}
- return splitSymbols(insns)
+ return nil
+}
+
+// Tag the instruction with any ExtInfo metadata that's pointing at the given
+// raw instruction.
+func assignMetadata(ins *asm.Instruction, raw asm.RawInstructionOffset,
+ fo *btf.FuncOffsets, lo *btf.LineOffsets, ro *btf.CORERelocationOffsets) {
+
+ if f := take(fo, func(f btf.FuncOffset) bool { return f.Offset == raw }); f != nil {
+ *ins = btf.WithFuncMetadata(*ins, f.Func)
+ }
+
+ if l := take(lo, func(l btf.LineOffset) bool { return l.Offset == raw }); l != nil {
+ *ins = ins.WithSource(l.Line)
+ }
+
+ if r := take(ro, func(r btf.CORERelocationOffset) bool { return r.Offset == raw }); r != nil {
+ *ins = btf.WithCORERelocationMetadata(*ins, r.Relo)
+ }
}
// referenceRelativeJump turns a relative jump to another bpf subprogram within
@@ -573,7 +665,7 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err
switch typ {
case elf.STT_NOTYPE, elf.STT_FUNC:
- if bind != elf.STB_GLOBAL {
+ if bind != elf.STB_GLOBAL && bind != elf.STB_WEAK {
return fmt.Errorf("call: %s: %w: %s", name, errUnsupportedBinding, bind)
}
@@ -710,89 +802,102 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err
return nil
}
+// loadMaps iterates over all ELF sections marked as map sections (like .maps)
+// and parses each symbol into a MapSpec.
func (ec *elfCode) loadMaps() error {
for _, sec := range ec.sections {
if sec.kind != mapSection {
continue
}
- nSym := len(sec.symbols)
- if nSym == 0 {
+ if len(sec.symbols) == 0 {
return fmt.Errorf("section %v: no symbols", sec.Name)
}
- if sec.Size%uint64(nSym) != 0 {
- return fmt.Errorf("section %v: map descriptors are not of equal size", sec.Name)
+ if sec.ReaderAt == nil {
+ return fmt.Errorf("compressed map section is not supported")
}
- // If the ELF has BTF, pull out the btf.Var for each map definition to
- // extract decl tags from.
- varsByName := make(map[string]*btf.Var)
- if ec.btf != nil {
- var ds *btf.Datasec
- if err := ec.btf.TypeByName(sec.Name, &ds); err == nil {
- for _, vsi := range ds.Vars {
- v, ok := btf.As[*btf.Var](vsi.Type)
- if !ok {
- return fmt.Errorf("section %v: btf.VarSecInfo doesn't point to a *btf.Var: %T", sec.Name, vsi.Type)
- }
- varsByName[string(v.Name)] = v
- }
- }
+ vars, err := ec.sectionVars(ec.btf, sec.Name)
+ if err != nil {
+ return fmt.Errorf("section %v: loading map variable BTF: %w", sec.Name, err)
}
- var (
- r = bufio.NewReader(sec.Open())
- size = sec.Size / uint64(nSym)
- )
- for i, offset := 0, uint64(0); i < nSym; i, offset = i+1, offset+size {
- mapSym, ok := sec.symbols[offset]
- if !ok {
- return fmt.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset)
+ for _, sym := range sec.symbols {
+ name := sym.Name
+ if ec.maps[name] != nil {
+ return fmt.Errorf("duplicate symbol %s in section %s", name, sec.Name)
}
- mapName := mapSym.Name
- if ec.maps[mapName] != nil {
- return fmt.Errorf("section %v: map %v already exists", sec.Name, mapSym)
+ if sym.Value > math.MaxUint32 || sym.Size > math.MaxUint32 {
+ return fmt.Errorf("symbol %s: offset or size exceeds 32 bits in section %s", sym.Name, sec.Name)
+ }
+ if sym.Value+sym.Size > sec.Size {
+ return fmt.Errorf("symbol %s: size goes out of bounds of section %s", name, sec.Name)
}
- lr := io.LimitReader(r, int64(size))
+ sr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size))
spec := MapSpec{
- Name: sanitizeName(mapName, -1),
+ Name: sanitizeName(name, -1),
}
switch {
- case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil:
- return fmt.Errorf("map %s: missing type", mapName)
- case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil:
- return fmt.Errorf("map %s: missing key size", mapName)
- case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil:
- return fmt.Errorf("map %s: missing value size", mapName)
- case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil:
- return fmt.Errorf("map %s: missing max entries", mapName)
- case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil:
- return fmt.Errorf("map %s: missing flags", mapName)
- }
-
- extra, err := io.ReadAll(lr)
+ case binary.Read(sr, ec.ByteOrder, &spec.Type) != nil:
+ return fmt.Errorf("map %s: missing type", name)
+ case binary.Read(sr, ec.ByteOrder, &spec.KeySize) != nil:
+ return fmt.Errorf("map %s: missing key size", name)
+ case binary.Read(sr, ec.ByteOrder, &spec.ValueSize) != nil:
+ return fmt.Errorf("map %s: missing value size", name)
+ case binary.Read(sr, ec.ByteOrder, &spec.MaxEntries) != nil:
+ return fmt.Errorf("map %s: missing max entries", name)
+ case binary.Read(sr, ec.ByteOrder, &spec.Flags) != nil:
+ return fmt.Errorf("map %s: missing flags", name)
+ }
+
+ extra, err := io.ReadAll(sr)
if err != nil {
- return fmt.Errorf("map %s: reading map tail: %w", mapName, err)
+ return fmt.Errorf("map %s: reading map tail: %w", name, err)
}
if len(extra) > 0 {
spec.Extra = bytes.NewReader(extra)
}
- if v, ok := varsByName[mapName]; ok {
+ if v, ok := vars[name]; ok {
spec.Tags = slices.Clone(v.Tags)
}
- ec.maps[mapName] = &spec
+ ec.maps[name] = &spec
}
}
return nil
}
+// sectionVars looks up the BTF Datasec for the given section name and returns a
+// map of variable names to their btf.Var definitions.
+func (ec *elfCode) sectionVars(spec *btf.Spec, sec string) (map[string]*btf.Var, error) {
+ vars := make(map[string]*btf.Var)
+
+ if spec == nil {
+ return vars, nil
+ }
+
+ var ds *btf.Datasec
+ if err := ec.btf.TypeByName(sec, &ds); err != nil {
+ return vars, nil
+ }
+
+ for _, vsi := range ds.Vars {
+ v, ok := btf.As[*btf.Var](vsi.Type)
+ if !ok {
+ return nil, fmt.Errorf("btf.VarSecInfo doesn't point to a *btf.Var: %T", vsi.Type)
+ }
+ vars[string(v.Name)] = v
+ }
+
+ return vars, nil
+}
+
// loadBTFMaps iterates over all ELF sections marked as BTF map sections
// (like .maps) and parses them into MapSpecs. Dump the .maps section and
// any relocations with `readelf -x .maps -r `.
@@ -806,32 +911,47 @@ func (ec *elfCode) loadBTFMaps() error {
return fmt.Errorf("missing BTF")
}
- // Each section must appear as a DataSec in the ELF's BTF blob.
- var ds *btf.Datasec
- if err := ec.btf.TypeByName(sec.Name, &ds); err != nil {
- return fmt.Errorf("cannot find section '%s' in BTF: %w", sec.Name, err)
+ if sec.ReaderAt == nil {
+ return fmt.Errorf("compressed BTF map section is not supported")
+ }
+
+ vars, err := ec.sectionVars(ec.btf, sec.Name)
+ if err != nil {
+ return fmt.Errorf("section %v: loading map variable BTF: %w", sec.Name, err)
+ }
+
+ if len(vars) != len(sec.symbols) {
+ return fmt.Errorf("section %v: contains %d symbols but %d btf.Vars", sec.Name, len(sec.symbols), len(vars))
+ }
+
+ syms := make(map[string]elf.Symbol)
+ for _, sym := range sec.symbols {
+ syms[sym.Name] = sym
}
- // Open a Reader to the ELF's raw section bytes so we can assert that all
- // of them are zero on a per-map (per-Var) basis. For now, the section's
- // sole purpose is to receive relocations, so all must be zero.
- rs := sec.Open()
+ for _, v := range vars {
+ name := v.Name
- for _, vs := range ds.Vars {
- // BPF maps are declared as and assigned to global variables,
- // so iterate over each Var in the DataSec and validate their types.
- v, ok := vs.Type.(*btf.Var)
+ // Find the ELF symbol corresponding to this Var.
+ sym, ok := syms[name]
if !ok {
- return fmt.Errorf("section %v: unexpected type %s", sec.Name, vs.Type)
+ return fmt.Errorf("section %v: missing symbol for map %s", sec.Name, name)
}
- name := string(v.Name)
+
+ if sym.Value > math.MaxUint32 || sym.Size > math.MaxUint32 {
+ return fmt.Errorf("symbol %s: offset or size exceeds 32 bits in section %s", sym.Name, sec.Name)
+ }
+ if sym.Value+sym.Size > sec.Size {
+ return fmt.Errorf("section %v: symbol %s: size goes out of bounds of section", sec.Name, name)
+ }
+
+ sr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size))
// The BTF metadata for each Var contains the full length of the map
// declaration, so read the corresponding amount of bytes from the ELF.
// This way, we can pinpoint which map declaration contains unexpected
// (and therefore unsupported) data.
- _, err := io.Copy(internal.DiscardZeroes{}, io.LimitReader(rs, int64(vs.Size)))
- if err != nil {
+ if _, err = io.Copy(internal.DiscardZeroes{}, sr); err != nil {
return fmt.Errorf("section %v: map %s: initializing BTF map definitions: %w", sec.Name, name, internal.ErrNotSupported)
}
@@ -845,22 +965,12 @@ func (ec *elfCode) loadBTFMaps() error {
return fmt.Errorf("expected struct, got %s", v.Type)
}
- mapSpec, err := mapSpecFromBTF(sec, &vs, mapStruct, ec.btf, name, false)
+ spec, err := mapSpecFromBTF(sec, sym, v, mapStruct, ec.btf, name, false)
if err != nil {
return fmt.Errorf("map %v: %w", name, err)
}
- ec.maps[name] = mapSpec
- }
-
- // Drain the ELF section reader to make sure all bytes are accounted for
- // with BTF metadata.
- i, err := io.Copy(io.Discard, rs)
- if err != nil {
- return fmt.Errorf("section %v: unexpected error reading remainder of ELF section: %w", sec.Name, err)
- }
- if i > 0 {
- return fmt.Errorf("section %v: %d unexpected remaining bytes in ELF section, invalid BTF?", sec.Name, i)
+ ec.maps[name] = spec
}
}
@@ -870,13 +980,14 @@ func (ec *elfCode) loadBTFMaps() error {
// mapSpecFromBTF produces a MapSpec based on a btf.Struct def representing
// a BTF map definition. The name and spec arguments will be copied to the
// resulting MapSpec, and inner must be true on any recursive invocations.
-func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *btf.Spec, name string, inner bool) (*MapSpec, error) {
+func mapSpecFromBTF(es *elfSection, sym elf.Symbol, v *btf.Var, def *btf.Struct, spec *btf.Spec, name string, inner bool) (*MapSpec, error) {
var (
key, value btf.Type
- keySize, valueSize uint32
+ keySize, valueSize uint64
mapType MapType
- flags, maxEntries uint32
+ flags, maxEntries uint64
pinType PinType
+ mapExtra uint64
innerMapSpec *MapSpec
contents []MapKV
err error
@@ -920,7 +1031,7 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
return nil, fmt.Errorf("can't get size of BTF key: %w", err)
}
- keySize = uint32(size)
+ keySize = uint64(size)
case "value":
if valueSize != 0 {
@@ -939,7 +1050,7 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
return nil, fmt.Errorf("can't get size of BTF value: %w", err)
}
- valueSize = uint32(size)
+ valueSize = uint64(size)
case "key_size":
// Key needs to be nil and keySize needs to be 0 for key_size to be
@@ -1013,7 +1124,7 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
// on kernels 5.2 and up)
// Pass the BTF spec from the parent object, since both parent and
// child must be created from the same BTF blob (on kernels that support BTF).
- innerMapSpec, err = mapSpecFromBTF(es, vs, t, spec, name+"_inner", true)
+ innerMapSpec, err = mapSpecFromBTF(es, sym, v, t, spec, name+"_inner", true)
if err != nil {
return nil, fmt.Errorf("can't parse BTF map definition of inner map: %w", err)
}
@@ -1029,13 +1140,16 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
return nil, fmt.Errorf("unsupported value type %q in 'values' field", t)
}
- contents, err = resolveBTFValuesContents(es, vs, member)
+ contents, err = resolveBTFValuesContents(es, sym, member)
if err != nil {
return nil, fmt.Errorf("resolving values contents: %w", err)
}
case "map_extra":
- return nil, fmt.Errorf("BTF map definition: field %s: %w", member.Name, ErrNotSupported)
+ mapExtra, err = uintFromBTF(member.Type)
+ if err != nil {
+ return nil, fmt.Errorf("resolving map_extra: %w", err)
+ }
default:
return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name)
@@ -1049,41 +1163,48 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
valueSize = 0
}
- v, ok := btf.As[*btf.Var](vs.Type)
- if !ok {
- return nil, fmt.Errorf("BTF map definition: btf.VarSecInfo doesn't point to a *btf.Var: %T", vs.Type)
- }
-
return &MapSpec{
Name: sanitizeName(name, -1),
Type: MapType(mapType),
- KeySize: keySize,
- ValueSize: valueSize,
- MaxEntries: maxEntries,
- Flags: flags,
+ KeySize: uint32(keySize),
+ ValueSize: uint32(valueSize),
+ MaxEntries: uint32(maxEntries),
+ Flags: uint32(flags),
Key: key,
Value: value,
Pinning: pinType,
InnerMap: innerMapSpec,
Contents: contents,
Tags: slices.Clone(v.Tags),
+ MapExtra: mapExtra,
}, nil
}
-// uintFromBTF resolves the __uint macro, which is a pointer to a sized
-// array, e.g. for int (*foo)[10], this function will return 10.
-func uintFromBTF(typ btf.Type) (uint32, error) {
- ptr, ok := typ.(*btf.Pointer)
- if !ok {
- return 0, fmt.Errorf("not a pointer: %v", typ)
- }
+// uintFromBTF resolves the __uint and __ulong macros.
+//
+// __uint emits a pointer to a sized array. For int (*foo)[10], this function
+// will return 10.
+//
+// __ulong emits an enum with a single value that can represent a 64-bit
+// integer. The first (and only) enum value is returned.
+func uintFromBTF(typ btf.Type) (uint64, error) {
+ switch t := typ.(type) {
+ case *btf.Pointer:
+ arr, ok := t.Target.(*btf.Array)
+ if !ok {
+ return 0, fmt.Errorf("not a pointer to array: %v", typ)
+ }
+ return uint64(arr.Nelems), nil
- arr, ok := ptr.Target.(*btf.Array)
- if !ok {
- return 0, fmt.Errorf("not a pointer to array: %v", typ)
- }
+ case *btf.Enum:
+ if len(t.Values) == 0 {
+ return 0, errors.New("enum has no values")
+ }
+ return t.Values[0].Value, nil
- return arr.Nelems, nil
+ default:
+ return 0, fmt.Errorf("not a pointer or enum: %v", typ)
+ }
}
// resolveBTFArrayMacro resolves the __array macro, which declares an array
@@ -1103,58 +1224,74 @@ func resolveBTFArrayMacro(typ btf.Type) (btf.Type, error) {
return ptr.Target, nil
}
-// resolveBTFValuesContents resolves relocations into ELF sections belonging
-// to btf.VarSecinfo's. This can be used on the 'values' member in BTF map
-// definitions to extract static declarations of map contents.
-func resolveBTFValuesContents(es *elfSection, vs *btf.VarSecinfo, member btf.Member) ([]MapKV, error) {
- // The elements of a .values pointer array are not encoded in BTF.
- // Instead, relocations are generated into each array index.
- // However, it's possible to leave certain array indices empty, so all
- // indices' offsets need to be checked for emitted relocations.
-
- // The offset of the 'values' member within the _struct_ (in bits)
- // is the starting point of the array. Convert to bytes. Add VarSecinfo
- // offset to get the absolute position in the ELF blob.
- start := member.Offset.Bytes() + vs.Offset
- // 'values' is encoded in BTF as a zero (variable) length struct
- // member, and its contents run until the end of the VarSecinfo.
- // Add VarSecinfo offset to get the absolute position in the ELF blob.
- end := vs.Size + vs.Offset
- // The size of an address in this section. This determines the width of
- // an index in the array.
- align := uint32(es.Addralign)
-
- // Check if variable-length section is aligned.
- if (end-start)%align != 0 {
- return nil, errors.New("unaligned static values section")
- }
+// valuesRelocations returns an iterator over the relocations in the ELF section
+// corresponding to the elements of a .values array in a BTF map definition. Each
+// iteration yields the array index and the symbol referenced by the relocation
+// at that index. Empty indices are skipped.
+func valuesRelocations(es *elfSection, sym elf.Symbol, member btf.Member) iter.Seq2[uint32, elf.Symbol] {
+ // The elements of a .values pointer array are not encoded in BTF itself.
+ // Instead, each array index receives a relocation pointing at a symbol
+ // (map/prog) in another section. However, it's possible to leave certain
+ // array indices empty, so all indices' offsets need to be checked for emitted
+ // relocations.
+
+ // Absolute offset of the .values member within the section.
+ start := sym.Value + uint64(member.Offset.Bytes())
+
+ // .values is a variable-length struct member, so its contents run until the
+ // end of the symbol. The symbol offset + size is the absolute offset of the
+ // end of the array in the section.
+ end := sym.Value + sym.Size
+
+ // The size of an address in this section. This determines the width of an
+ // index in the array.
+ align := es.Addralign
+
+ // Amount of elements in the .values array.
elems := (end - start) / align
- if elems == 0 {
- return nil, nil
- }
+ return func(yield func(uint32, elf.Symbol) bool) {
+ for i := range uint32(elems) {
+ // off increases by align on each iteration, starting at .values.
+ off := start + (uint64(i) * align)
- contents := make([]MapKV, 0, elems)
+ r, ok := es.relocations[off]
+ if !ok {
+ continue
+ }
- // k is the array index, off is its corresponding ELF section offset.
- for k, off := uint32(0), start; k < elems; k, off = k+1, off+align {
- r, ok := es.relocations[uint64(off)]
- if !ok {
- continue
+ if !yield(i, r) {
+ return
+ }
}
+ }
+
+}
+
+// resolveBTFValuesContents looks up the symbols referenced by the relocations
+// in a .values array and returns them as MapKV pairs, where the key is the
+// array index and the value is the symbol name. Empty indices are skipped.
+func resolveBTFValuesContents(es *elfSection, sym elf.Symbol, member btf.Member) ([]MapKV, error) {
+ var contents []MapKV
+
+ if member.Offset.Bytes() > uint32(sym.Size) {
+ return nil, fmt.Errorf("member offset %d exceeds symbol size %d", member.Offset.Bytes(), sym.Size)
+ }
- // Relocation exists for the current offset in the ELF section.
- // Emit a value stub based on the type of relocation to be replaced by
- // a real fd later in the pipeline before populating the map.
- // Map keys are encoded in MapKV entries, so empty array indices are
- // skipped here.
- switch t := elf.ST_TYPE(r.Info); t {
+ if es.Addralign == 0 {
+ return nil, fmt.Errorf("section has no address alignment, can't resolve .values contents")
+ }
+
+ for i, sym := range valuesRelocations(es, sym, member) {
+ // Emit a value stub based on the type of relocation to be replaced by a
+ // real fd later in the pipeline before populating the Map.
+ switch t := elf.ST_TYPE(sym.Info); t {
case elf.STT_FUNC:
- contents = append(contents, MapKV{uint32(k), r.Name})
+ contents = append(contents, MapKV{i, sym.Name})
case elf.STT_OBJECT:
- contents = append(contents, MapKV{uint32(k), r.Name})
+ contents = append(contents, MapKV{i, sym.Name})
default:
- return nil, fmt.Errorf("unknown relocation type %v for symbol %s", t, r.Name)
+ return nil, fmt.Errorf("unknown relocation type %v for symbol %s", t, sym.Name)
}
}
@@ -1194,14 +1331,15 @@ func (ec *elfCode) loadDataSections() error {
mapSpec.Flags = sys.BPF_F_RDONLY_PROG
}
+ var data []byte
switch sec.Type {
// Only open the section if we know there's actual data to be read.
case elf.SHT_PROGBITS:
- data, err := sec.Data()
+ var err error
+ data, err = sec.Data()
if err != nil {
return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err)
}
- mapSpec.Contents = []MapKV{{uint32(0), data}}
case elf.SHT_NOBITS:
// NOBITS sections like .bss contain only zeroes and are not allocated in
@@ -1209,12 +1347,14 @@ func (ec *elfCode) loadDataSections() error {
// them. Don't attempt reading zeroes from the ELF, instead allocate the
// zeroed memory to support getting and setting VariableSpecs for sections
// like .bss.
- mapSpec.Contents = []MapKV{{uint32(0), make([]byte, sec.Size)}}
+ data = make([]byte, sec.Size)
default:
return fmt.Errorf("data section %s: unknown section type %s", sec.Name, sec.Type)
}
+ mapSpec.Contents = []MapKV{{uint32(0), data}}
+
for off, sym := range sec.symbols {
// Skip symbols marked with the 'hidden' attribute.
if elf.ST_VISIBILITY(sym.Other) == elf.STV_HIDDEN ||
@@ -1243,11 +1383,23 @@ func (ec *elfCode) loadDataSections() error {
continue
}
+ if off+sym.Size > uint64(len(data)) {
+ return fmt.Errorf("data section %s: variable %s exceeds section bounds", sec.Name, sym.Name)
+ }
+
+ if off > math.MaxUint32 {
+ return fmt.Errorf("data section %s: variable %s offset %d exceeds maximum", sec.Name, sym.Name, off)
+ }
+
+ if sym.Size > math.MaxUint32 {
+ return fmt.Errorf("data section %s: variable %s size %d exceeds maximum", sec.Name, sym.Name, sym.Size)
+ }
+
ec.vars[sym.Name] = &VariableSpec{
- name: sym.Name,
- offset: off,
- size: sym.Size,
- m: mapSpec,
+ SectionName: sec.Name,
+ Name: sym.Name,
+ Offset: uint32(off),
+ Value: slices.Clone(data[off : off+sym.Size]),
}
}
@@ -1278,17 +1430,17 @@ func (ec *elfCode) loadDataSections() error {
continue
}
- if uint64(v.Offset) != ev.offset {
- return fmt.Errorf("data section %s: variable %s datasec offset (%d) doesn't match ELF symbol offset (%d)", sec.Name, name, v.Offset, ev.offset)
+ if v.Offset != ev.Offset {
+ return fmt.Errorf("data section %s: variable %s datasec offset (%d) doesn't match ELF symbol offset (%d)", sec.Name, name, v.Offset, ev.Offset)
}
- if uint64(v.Size) != ev.size {
- return fmt.Errorf("data section %s: variable %s size in datasec (%d) doesn't match ELF symbol size (%d)", sec.Name, name, v.Size, ev.size)
+ if v.Size != ev.Size() {
+ return fmt.Errorf("data section %s: variable %s size in datasec (%d) doesn't match ELF symbol size (%d)", sec.Name, name, v.Size, ev.Size())
}
// Decouple the Var in the VariableSpec from the underlying DataSec in
// the MapSpec to avoid modifications from affecting map loads later on.
- ev.t = btf.Copy(vt).(*btf.Var)
+ ev.Type = btf.Copy(vt).(*btf.Var)
}
}
}
@@ -1363,6 +1515,95 @@ func (ec *elfCode) loadKsymsSection() error {
return nil
}
+// associateStructOpsRelocs handles `.struct_ops.link`
+// and associates the target function with the correct struct member in the map.
+func (ec *elfCode) associateStructOpsRelocs(progs map[string]*ProgramSpec) error {
+ for _, sec := range ec.sections {
+ if sec.kind != structOpsSection {
+ continue
+ }
+
+ userData, err := sec.Data()
+ if err != nil {
+ return fmt.Errorf("failed to read section data: %w", err)
+ }
+
+ if ec.btf == nil {
+ return fmt.Errorf("struct_ops section %s: missing BTF", sec.Name)
+ }
+
+ // Resolve the BTF datasec describing variables in this section.
+ var ds *btf.Datasec
+ if err := ec.btf.TypeByName(sec.Name, &ds); err != nil {
+ return fmt.Errorf("datasec %s: %w", sec.Name, err)
+ }
+
+ // Set flags for .struct_ops.link (BPF_F_LINK).
+ flags := uint32(0)
+ if sec.Name == structOpsLinkSec {
+ flags = sys.BPF_F_LINK
+ }
+
+ for _, vsi := range ds.Vars {
+ userSt, baseOff, err := ec.createStructOpsMap(vsi, userData, flags)
+ if err != nil {
+ return err
+ }
+
+ if err := structOpsSetAttachTo(sec, baseOff, userSt, progs); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+// createStructOpsMap() creates and registers a MapSpec for a struct_ops
+func (ec *elfCode) createStructOpsMap(vsi btf.VarSecinfo, userData []byte, flags uint32) (*btf.Struct, uint32, error) {
+ varType, ok := btf.As[*btf.Var](vsi.Type)
+ if !ok {
+ return nil, 0, fmt.Errorf("vsi: expect var, got %T", vsi.Type)
+ }
+
+ mapName := varType.Name
+
+ userSt, ok := btf.As[*btf.Struct](varType.Type)
+ if !ok {
+ return nil, 0, fmt.Errorf("var %s: expect struct, got %T", varType.Name, varType.Type)
+ }
+
+ userSize := userSt.Size
+ baseOff := vsi.Offset
+ if uint64(baseOff)+uint64(userSize) > uint64(len(userData)) {
+ return nil, 0, fmt.Errorf("%s exceeds section", mapName)
+ }
+
+ // Register the MapSpec for this struct_ops instance if doesn't exist
+ if _, exists := ec.maps[mapName]; exists {
+ return nil, 0, fmt.Errorf("struct_ops map %s: already exists", mapName)
+ }
+
+ ec.maps[mapName] = &MapSpec{
+ Name: mapName,
+ Type: StructOpsMap,
+ Key: &btf.Int{Size: 4},
+ KeySize: structOpsKeySize,
+ ValueSize: userSize, // length of the user-struct type
+ Value: userSt,
+ Flags: flags,
+ MaxEntries: 1,
+ Contents: []MapKV{
+ {
+ Key: uint32(0),
+ Value: append([]byte(nil), userData[baseOff:baseOff+userSize]...),
+ },
+ },
+ }
+
+ return userSt, baseOff, nil
+}
+
type libbpfElfSectionDef struct {
pattern string
programType sys.ProgType
@@ -1383,29 +1624,9 @@ const (
_SEC_XDP_FRAGS
_SEC_USDT
- // Ignore any present extra in order to preserve backwards compatibility
- // with earlier versions of the library.
- ignoreExtra
-
_SEC_ATTACHABLE_OPT = _SEC_ATTACHABLE | _SEC_EXP_ATTACH_OPT
)
-func init() {
- // Compatibility with older versions of the library.
- // We prepend libbpf definitions since they contain a prefix match
- // for "xdp".
- elfSectionDefs = append([]libbpfElfSectionDef{
- {"xdp.frags/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP, _SEC_XDP_FRAGS | ignoreExtra},
- {"xdp.frags_devmap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, _SEC_XDP_FRAGS},
- {"xdp_devmap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, 0},
- {"xdp.frags_cpumap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, _SEC_XDP_FRAGS},
- {"xdp_cpumap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, 0},
- // This has been in the library since the beginning of time. Not sure
- // where it came from.
- {"seccomp", sys.BPF_PROG_TYPE_SOCKET_FILTER, 0, _SEC_NONE},
- }, elfSectionDefs...)
-}
-
func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
// Skip optional program marking for now.
sectionName = strings.TrimPrefix(sectionName, "?")
@@ -1426,15 +1647,13 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
if t.flags&_SEC_XDP_FRAGS > 0 {
flags |= sys.BPF_F_XDP_HAS_FRAGS
}
- if t.flags&_SEC_EXP_ATTACH_OPT > 0 {
- if programType == XDP {
- // The library doesn't yet have code to fallback to not specifying
- // attach type. Only do this for XDP since we've enforced correct
- // attach type for all other program types.
- attachType = AttachNone
- }
- }
- if t.flags&ignoreExtra > 0 {
+
+ // The libbpf documentation on program types states: 'The struct_ops attach
+ // format supports struct_ops[.s]/ convention, but name is ignored and
+ // it is recommended to just use plain SEC("struct_ops[.s]").'
+ //
+ // Ignore any extra for struct_ops to match libbpf behaviour.
+ if programType == StructOps {
extra = ""
}
diff --git a/vendor/github.com/cilium/ebpf/elf_sections.go b/vendor/github.com/cilium/ebpf/elf_sections.go
index 43dcfb103..1ded4c4d5 100644
--- a/vendor/github.com/cilium/ebpf/elf_sections.go
+++ b/vendor/github.com/cilium/ebpf/elf_sections.go
@@ -21,8 +21,10 @@ var elfSectionDefs = []libbpfElfSectionDef{
{"kprobe.session+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_SESSION, _SEC_NONE},
{"uprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_NONE},
{"uretprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_NONE},
+ {"uprobe.session+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_SESSION, _SEC_NONE},
{"uprobe.multi.s+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_SLEEPABLE},
{"uretprobe.multi.s+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_SLEEPABLE},
+ {"uprobe.session.s+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_SESSION, _SEC_SLEEPABLE},
{"ksyscall+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE},
{"kretsyscall+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE},
{"usdt+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_USDT},
@@ -49,6 +51,8 @@ var elfSectionDefs = []libbpfElfSectionDef{
{"fentry.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FENTRY, _SEC_ATTACH_BTF | _SEC_SLEEPABLE},
{"fmod_ret.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_MODIFY_RETURN, _SEC_ATTACH_BTF | _SEC_SLEEPABLE},
{"fexit.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FEXIT, _SEC_ATTACH_BTF | _SEC_SLEEPABLE},
+ {"fsession+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FSESSION, _SEC_ATTACH_BTF},
+ {"fsession.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FSESSION, _SEC_ATTACH_BTF | _SEC_SLEEPABLE},
{"freplace+", sys.BPF_PROG_TYPE_EXT, 0, _SEC_ATTACH_BTF},
{"lsm+", sys.BPF_PROG_TYPE_LSM, sys.BPF_LSM_MAC, _SEC_ATTACH_BTF},
{"lsm.s+", sys.BPF_PROG_TYPE_LSM, sys.BPF_LSM_MAC, _SEC_ATTACH_BTF | _SEC_SLEEPABLE},
diff --git a/vendor/github.com/cilium/ebpf/features/doc.go b/vendor/github.com/cilium/ebpf/features/doc.go
new file mode 100644
index 000000000..acc57e3b1
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/features/doc.go
@@ -0,0 +1,19 @@
+// Package features allows probing for BPF features available to the calling process.
+//
+// In general, the error return values from feature probes in this package
+// all have the following semantics unless otherwise specified:
+//
+// err == nil: The feature is available.
+// errors.Is(err, ebpf.ErrNotSupported): The feature is not available.
+// err != nil: Any errors encountered during probe execution, wrapped.
+//
+// Note that the latter case may include false negatives, and that resource
+// creation may succeed despite an error being returned. For example, some
+// map and program types cannot reliably be probed and will return an
+// inconclusive error.
+//
+// As a rule, only `nil` and `ebpf.ErrNotSupported` are conclusive.
+//
+// Probe results are cached by the library and persist throughout any changes
+// to the process' environment, like capability changes.
+package features
diff --git a/vendor/github.com/cilium/ebpf/features/link.go b/vendor/github.com/cilium/ebpf/features/link.go
new file mode 100644
index 000000000..4f440e7bc
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/features/link.go
@@ -0,0 +1,157 @@
+package features
+
+import (
+ "errors"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/asm"
+ "github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/internal/sys"
+ "github.com/cilium/ebpf/internal/unix"
+)
+
+// HaveBPFLinkUprobeMulti probes the running kernel if uprobe_multi link is supported.
+//
+// See the package documentation for the meaning of the error return value.
+func HaveBPFLinkUprobeMulti() error {
+ return haveBPFLinkUprobeMulti()
+}
+
+var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", func() error {
+ prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+ Name: "probe_upm_link",
+ Type: ebpf.Kprobe,
+ Instructions: asm.Instructions{
+ asm.Mov.Imm(asm.R0, 0),
+ asm.Return(),
+ },
+ AttachType: ebpf.AttachTraceUprobeMulti,
+ License: "MIT",
+ })
+ if errors.Is(err, unix.E2BIG) {
+ // Kernel doesn't support AttachType field.
+ return ebpf.ErrNotSupported
+ }
+ if err != nil {
+ return err
+ }
+ defer prog.Close()
+
+ // We try to create uprobe multi link on '/' path which results in
+ // error with -EBADF in case uprobe multi link is supported.
+ fd, err := sys.LinkCreateUprobeMulti(&sys.LinkCreateUprobeMultiAttr{
+ ProgFd: uint32(prog.FD()),
+ AttachType: sys.BPF_TRACE_UPROBE_MULTI,
+ Path: sys.NewStringPointer("/"),
+ Offsets: sys.SlicePointer([]uint64{0}),
+ Count: 1,
+ })
+ switch {
+ case errors.Is(err, unix.EBADF):
+ return nil
+ case errors.Is(err, unix.EINVAL):
+ return ebpf.ErrNotSupported
+ case err != nil:
+ return err
+ }
+
+ // should not happen
+ fd.Close()
+ return errors.New("successfully attached uprobe_multi to /, kernel bug?")
+}, "6.6")
+
+// HaveBPFLinkKprobeMulti probes the running kernel if kprobe_multi link is supported.
+//
+// See the package documentation for the meaning of the error return value.
+func HaveBPFLinkKprobeMulti() error {
+ return haveBPFLinkKprobeMulti()
+}
+
+var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", func() error {
+ prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+ Name: "probe_kpm_link",
+ Type: ebpf.Kprobe,
+ Instructions: asm.Instructions{
+ asm.Mov.Imm(asm.R0, 0),
+ asm.Return(),
+ },
+ AttachType: ebpf.AttachTraceKprobeMulti,
+ License: "MIT",
+ })
+ if errors.Is(err, unix.E2BIG) {
+ // Kernel doesn't support AttachType field.
+ return ebpf.ErrNotSupported
+ }
+ if err != nil {
+ return err
+ }
+ defer prog.Close()
+
+ fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{
+ ProgFd: uint32(prog.FD()),
+ AttachType: sys.BPF_TRACE_KPROBE_MULTI,
+ Count: 1,
+ Syms: sys.NewStringSlicePointer([]string{"vprintk"}),
+ })
+ switch {
+ case errors.Is(err, unix.EINVAL):
+ return ebpf.ErrNotSupported
+ // If CONFIG_FPROBE isn't set.
+ case errors.Is(err, unix.EOPNOTSUPP):
+ return ebpf.ErrNotSupported
+ case err != nil:
+ return err
+ }
+
+ fd.Close()
+
+ return nil
+}, "5.18")
+
+// HaveBPFLinkKprobeSession probes the running kernel if kprobe_session link is supported.
+//
+// See the package documentation for the meaning of the error return value.
+func HaveBPFLinkKprobeSession() error {
+ return haveBPFLinkKprobeSession()
+}
+
+var haveBPFLinkKprobeSession = internal.NewFeatureTest("bpf_link_kprobe_session", func() error {
+ prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+ Name: "probe_kps_link",
+ Type: ebpf.Kprobe,
+ Instructions: asm.Instructions{
+ asm.Mov.Imm(asm.R0, 0),
+ asm.Return(),
+ },
+ AttachType: ebpf.AttachTraceKprobeSession,
+ License: "MIT",
+ })
+ if errors.Is(err, unix.E2BIG) {
+ // Kernel doesn't support AttachType field.
+ return ebpf.ErrNotSupported
+ }
+ if err != nil {
+ return err
+ }
+ defer prog.Close()
+
+ fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{
+ ProgFd: uint32(prog.FD()),
+ AttachType: sys.BPF_TRACE_KPROBE_SESSION,
+ Count: 1,
+ Syms: sys.NewStringSlicePointer([]string{"vprintk"}),
+ })
+ switch {
+ case errors.Is(err, unix.EINVAL):
+ return ebpf.ErrNotSupported
+ // If CONFIG_FPROBE isn't set.
+ case errors.Is(err, unix.EOPNOTSUPP):
+ return ebpf.ErrNotSupported
+ case err != nil:
+ return err
+ }
+
+ fd.Close()
+
+ return nil
+}, "6.10")
diff --git a/vendor/github.com/cilium/ebpf/features/map.go b/vendor/github.com/cilium/ebpf/features/map.go
new file mode 100644
index 000000000..75212552c
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/features/map.go
@@ -0,0 +1,362 @@
+package features
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "unsafe"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/internal/sys"
+ "github.com/cilium/ebpf/internal/unix"
+)
+
+// HaveMapType probes the running kernel for the availability of the specified map type.
+//
+// See the package documentation for the meaning of the error return value.
+func HaveMapType(mt ebpf.MapType) error {
+ return haveMapTypeMatrix.Result(mt)
+}
+
+func probeCgroupStorageMap(mt sys.MapType) error {
+ // keySize needs to be sizeof(struct{u32 + u64}) = 12 (+ padding = 16)
+ // by using unsafe.Sizeof(int) we are making sure that this works on 32bit and 64bit archs
+ return createMap(&sys.MapCreateAttr{
+ MapType: mt,
+ ValueSize: 4,
+ KeySize: uint32(8 + unsafe.Sizeof(int(0))),
+ MaxEntries: 0,
+ })
+}
+
+func probeStorageMap(mt sys.MapType) error {
+ // maxEntries needs to be 0
+ // BPF_F_NO_PREALLOC needs to be set
+ // btf* fields need to be set
+ // see alloc_check for local_storage map types
+ err := createMap(&sys.MapCreateAttr{
+ MapType: mt,
+ KeySize: 4,
+ ValueSize: 4,
+ MaxEntries: 0,
+ MapFlags: sys.BPF_F_NO_PREALLOC,
+ BtfKeyTypeId: 1,
+ BtfValueTypeId: 1,
+ BtfFd: ^uint32(0),
+ })
+ if errors.Is(err, unix.EBADF) {
+ // Triggered by BtfFd.
+ return nil
+ }
+ return err
+}
+
+func probeNestedMap(mt sys.MapType) error {
+ // assign invalid innerMapFd to pass validation check
+ // will return EBADF
+ err := probeMap(&sys.MapCreateAttr{
+ MapType: mt,
+ InnerMapFd: ^uint32(0),
+ })
+ if errors.Is(err, unix.EBADF) {
+ return nil
+ }
+ return err
+}
+
+func probeMap(attr *sys.MapCreateAttr) error {
+ if attr.KeySize == 0 {
+ attr.KeySize = 4
+ }
+ if attr.ValueSize == 0 {
+ attr.ValueSize = 4
+ }
+ attr.MaxEntries = 1
+ return createMap(attr)
+}
+
+func createMap(attr *sys.MapCreateAttr) error {
+ fd, err := sys.MapCreate(attr)
+ if err == nil {
+ fd.Close()
+ return nil
+ }
+
+ switch {
+ // EINVAL occurs when attempting to create a map with an unknown type.
+ // E2BIG occurs when MapCreateAttr contains non-zero bytes past the end
+ // of the struct known by the running kernel, meaning the kernel is too old
+ // to support the given map type.
+ case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
+ return ebpf.ErrNotSupported
+ }
+
+ return err
+}
+
+var haveMapTypeMatrix = internal.FeatureMatrix[ebpf.MapType]{
+ ebpf.Hash: {Version: "3.19"},
+ ebpf.Array: {Version: "3.19"},
+ ebpf.ProgramArray: {Version: "4.2"},
+ ebpf.PerfEventArray: {Version: "4.3"},
+ ebpf.PerCPUHash: {Version: "4.6"},
+ ebpf.PerCPUArray: {Version: "4.6"},
+ ebpf.StackTrace: {
+ Version: "4.6",
+ Fn: func() error {
+ return probeMap(&sys.MapCreateAttr{
+ MapType: sys.BPF_MAP_TYPE_STACK_TRACE,
+ ValueSize: 8, // sizeof(uint64)
+ })
+ },
+ },
+ ebpf.CGroupArray: {Version: "4.8"},
+ ebpf.LRUHash: {Version: "4.10"},
+ ebpf.LRUCPUHash: {Version: "4.10"},
+ ebpf.LPMTrie: {
+ Version: "4.11",
+ Fn: func() error {
+ // keySize and valueSize need to be sizeof(struct{u32 + u8}) + 1 + padding = 8
+ // BPF_F_NO_PREALLOC needs to be set
+ return probeMap(&sys.MapCreateAttr{
+ MapType: sys.BPF_MAP_TYPE_LPM_TRIE,
+ KeySize: 8,
+ ValueSize: 8,
+ MapFlags: sys.BPF_F_NO_PREALLOC,
+ })
+ },
+ },
+ ebpf.ArrayOfMaps: {
+ Version: "4.12",
+ Fn: func() error { return probeNestedMap(sys.BPF_MAP_TYPE_ARRAY_OF_MAPS) },
+ },
+ ebpf.HashOfMaps: {
+ Version: "4.12",
+ Fn: func() error { return probeNestedMap(sys.BPF_MAP_TYPE_HASH_OF_MAPS) },
+ },
+ ebpf.DevMap: {Version: "4.14"},
+ ebpf.SockMap: {Version: "4.14"},
+ ebpf.CPUMap: {Version: "4.15"},
+ ebpf.XSKMap: {Version: "4.18"},
+ ebpf.SockHash: {Version: "4.18"},
+ ebpf.CGroupStorage: {
+ Version: "4.19",
+ Fn: func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_CGROUP_STORAGE) },
+ },
+ ebpf.ReusePortSockArray: {Version: "4.19"},
+ ebpf.PerCPUCGroupStorage: {
+ Version: "4.20",
+ Fn: func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) },
+ },
+ ebpf.Queue: {
+ Version: "4.20",
+ Fn: func() error {
+ return createMap(&sys.MapCreateAttr{
+ MapType: sys.BPF_MAP_TYPE_QUEUE,
+ KeySize: 0,
+ ValueSize: 4,
+ MaxEntries: 1,
+ })
+ },
+ },
+ ebpf.Stack: {
+ Version: "4.20",
+ Fn: func() error {
+ return createMap(&sys.MapCreateAttr{
+ MapType: sys.BPF_MAP_TYPE_STACK,
+ KeySize: 0,
+ ValueSize: 4,
+ MaxEntries: 1,
+ })
+ },
+ },
+ ebpf.SkStorage: {
+ Version: "5.2",
+ Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_SK_STORAGE) },
+ },
+ ebpf.DevMapHash: {Version: "5.4"},
+ ebpf.StructOpsMap: {
+ Version: "5.6",
+ Fn: func() error {
+ // StructOps requires setting a vmlinux type id, but id 1 will always
+ // resolve to some type of integer. This will cause ENOTSUPP.
+ err := probeMap(&sys.MapCreateAttr{
+ MapType: sys.BPF_MAP_TYPE_STRUCT_OPS,
+ BtfVmlinuxValueTypeId: 1,
+ })
+ if errors.Is(err, sys.ENOTSUPP) {
+ // ENOTSUPP means the map type is at least known to the kernel.
+ return nil
+ }
+ return err
+ },
+ },
+ ebpf.RingBuf: {
+ Version: "5.8",
+ Fn: func() error {
+ // keySize and valueSize need to be 0
+ // maxEntries needs to be power of 2 and PAGE_ALIGNED
+ return createMap(&sys.MapCreateAttr{
+ MapType: sys.BPF_MAP_TYPE_RINGBUF,
+ KeySize: 0,
+ ValueSize: 0,
+ MaxEntries: uint32(os.Getpagesize()),
+ })
+ },
+ },
+ ebpf.InodeStorage: {
+ Version: "5.10",
+ Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_INODE_STORAGE) },
+ },
+ ebpf.TaskStorage: {
+ Version: "5.11",
+ Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_TASK_STORAGE) },
+ },
+ ebpf.BloomFilter: {
+ Version: "5.16",
+ Fn: func() error {
+ return createMap(&sys.MapCreateAttr{
+ MapType: sys.BPF_MAP_TYPE_BLOOM_FILTER,
+ KeySize: 0,
+ ValueSize: 4,
+ MaxEntries: 1,
+ })
+ },
+ },
+ ebpf.UserRingbuf: {
+ Version: "6.1",
+ Fn: func() error {
+ // keySize and valueSize need to be 0
+ // maxEntries needs to be power of 2 and PAGE_ALIGNED
+ return createMap(&sys.MapCreateAttr{
+ MapType: sys.BPF_MAP_TYPE_USER_RINGBUF,
+ KeySize: 0,
+ ValueSize: 0,
+ MaxEntries: uint32(os.Getpagesize()),
+ })
+ },
+ },
+ ebpf.CgroupStorage: {
+ Version: "6.2",
+ Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_CGRP_STORAGE) },
+ },
+ ebpf.Arena: {
+ Version: "6.9",
+ Fn: func() error {
+ return createMap(&sys.MapCreateAttr{
+ MapType: sys.BPF_MAP_TYPE_ARENA,
+ KeySize: 0,
+ ValueSize: 0,
+ MaxEntries: 1, // one page
+ MapExtra: 0, // can mmap() at any address
+ MapFlags: sys.BPF_F_MMAPABLE,
+ })
+ },
+ },
+}
+
+func init() {
+ for mt, ft := range haveMapTypeMatrix {
+ ft.Name = mt.String()
+ if ft.Fn == nil {
+ // Avoid referring to the loop variable in the closure.
+ mt := sys.MapType(mt)
+ ft.Fn = func() error { return probeMap(&sys.MapCreateAttr{MapType: mt}) }
+ }
+ }
+}
+
+// MapFlags document which flags may be feature probed.
+type MapFlags uint32
+
+// Flags which may be feature probed.
+const (
+ BPF_F_NO_PREALLOC = sys.BPF_F_NO_PREALLOC
+ BPF_F_RDONLY_PROG = sys.BPF_F_RDONLY_PROG
+ BPF_F_WRONLY_PROG = sys.BPF_F_WRONLY_PROG
+ BPF_F_MMAPABLE = sys.BPF_F_MMAPABLE
+ BPF_F_INNER_MAP = sys.BPF_F_INNER_MAP
+)
+
+// HaveMapFlag probes the running kernel for the availability of the specified map flag.
+//
+// Returns an error if flag is not one of the flags declared in this package.
+// See the package documentation for the meaning of the error return value.
+func HaveMapFlag(flag MapFlags) (err error) {
+ return haveMapFlagsMatrix.Result(flag)
+}
+
+func probeMapFlag(attr *sys.MapCreateAttr) error {
+ // For now, we do not check if the map type is supported because we only support
+ // probing for flags defined on arrays and hashes that are always supported.
+ // In the future, if we allow probing on flags defined on newer types, checking for map type
+ // support will be required.
+ if attr.MapType == sys.BPF_MAP_TYPE_UNSPEC {
+ attr.MapType = sys.BPF_MAP_TYPE_ARRAY
+ }
+
+ attr.KeySize = 4
+ attr.ValueSize = 4
+ attr.MaxEntries = 1
+
+ fd, err := sys.MapCreate(attr)
+ if err == nil {
+ fd.Close()
+ } else if errors.Is(err, unix.EINVAL) {
+ // EINVAL occurs when attempting to create a map with an unknown type or an unknown flag.
+ err = ebpf.ErrNotSupported
+ }
+
+ return err
+}
+
+var haveMapFlagsMatrix = internal.FeatureMatrix[MapFlags]{
+ BPF_F_NO_PREALLOC: {
+ Version: "4.6",
+ Fn: func() error {
+ return probeMapFlag(&sys.MapCreateAttr{
+ MapType: sys.BPF_MAP_TYPE_HASH,
+ MapFlags: BPF_F_NO_PREALLOC,
+ })
+ },
+ },
+ BPF_F_RDONLY_PROG: {
+ Version: "5.2",
+ Fn: func() error {
+ return probeMapFlag(&sys.MapCreateAttr{
+ MapFlags: BPF_F_RDONLY_PROG,
+ })
+ },
+ },
+ BPF_F_WRONLY_PROG: {
+ Version: "5.2",
+ Fn: func() error {
+ return probeMapFlag(&sys.MapCreateAttr{
+ MapFlags: BPF_F_WRONLY_PROG,
+ })
+ },
+ },
+ BPF_F_MMAPABLE: {
+ Version: "5.5",
+ Fn: func() error {
+ return probeMapFlag(&sys.MapCreateAttr{
+ MapFlags: BPF_F_MMAPABLE,
+ })
+ },
+ },
+ BPF_F_INNER_MAP: {
+ Version: "5.10",
+ Fn: func() error {
+ return probeMapFlag(&sys.MapCreateAttr{
+ MapFlags: BPF_F_INNER_MAP,
+ })
+ },
+ },
+}
+
+func init() {
+ for mf, ft := range haveMapFlagsMatrix {
+ ft.Name = fmt.Sprint(mf)
+ }
+}
diff --git a/vendor/github.com/cilium/ebpf/features/misc.go b/vendor/github.com/cilium/ebpf/features/misc.go
new file mode 100644
index 000000000..c039020a9
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/features/misc.go
@@ -0,0 +1,135 @@
+package features
+
+import (
+ "errors"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/asm"
+ "github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/internal/sys"
+)
+
+// HaveLargeInstructions probes the running kernel if more than 4096 instructions
+// per program are supported.
+//
+// Upstream commit c04c0d2b968a ("bpf: increase complexity limit and maximum program size").
+//
+// See the package documentation for the meaning of the error return value.
+func HaveLargeInstructions() error {
+ return haveLargeInstructions()
+}
+
+var haveLargeInstructions = internal.NewFeatureTest(">4096 instructions", func() error {
+ const maxInsns = 4096
+
+ insns := make(asm.Instructions, maxInsns, maxInsns+1)
+ for i := range insns {
+ insns[i] = asm.Mov.Imm(asm.R0, 1)
+ }
+ insns = append(insns, asm.Return())
+
+ return probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.SocketFilter,
+ Instructions: insns,
+ })
+}, "5.2")
+
+// HaveBoundedLoops probes the running kernel if bounded loops are supported.
+//
+// Upstream commit 2589726d12a1 ("bpf: introduce bounded loops").
+//
+// See the package documentation for the meaning of the error return value.
+func HaveBoundedLoops() error {
+ return haveBoundedLoops()
+}
+
+var haveBoundedLoops = internal.NewFeatureTest("bounded loops", func() error {
+ return probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.SocketFilter,
+ Instructions: asm.Instructions{
+ asm.Mov.Imm(asm.R0, 10),
+ asm.Sub.Imm(asm.R0, 1).WithSymbol("loop"),
+ asm.JNE.Imm(asm.R0, 0, "loop"),
+ asm.Return(),
+ },
+ })
+}, "5.3")
+
+// HaveV2ISA probes the running kernel if instructions of the v2 ISA are supported.
+//
+// Upstream commit 92b31a9af73b ("bpf: add BPF_J{LT,LE,SLT,SLE} instructions").
+//
+// See the package documentation for the meaning of the error return value.
+func HaveV2ISA() error {
+ return haveV2ISA()
+}
+
+var haveV2ISA = internal.NewFeatureTest("v2 ISA", func() error {
+ err := probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.SocketFilter,
+ Instructions: asm.Instructions{
+ asm.Mov.Imm(asm.R0, 0),
+ asm.JLT.Imm(asm.R0, 0, "exit"),
+ asm.Mov.Imm(asm.R0, 1),
+ asm.Return().WithSymbol("exit"),
+ },
+ })
+ // This sometimes bubbles up from the JIT on aarch64.
+ if errors.Is(err, sys.ENOTSUPP) {
+ return ebpf.ErrNotSupported
+ }
+ return err
+}, "4.14")
+
+// HaveV3ISA probes the running kernel if instructions of the v3 ISA are supported.
+//
+// Upstream commit 092ed0968bb6 ("bpf: verifier support JMP32").
+//
+// See the package documentation for the meaning of the error return value.
+func HaveV3ISA() error {
+ return haveV3ISA()
+}
+
+var haveV3ISA = internal.NewFeatureTest("v3 ISA", func() error {
+ err := probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.SocketFilter,
+ Instructions: asm.Instructions{
+ asm.Mov.Imm(asm.R0, 0),
+ asm.JLT.Imm32(asm.R0, 0, "exit"),
+ asm.Mov.Imm(asm.R0, 1),
+ asm.Return().WithSymbol("exit"),
+ },
+ })
+ // This sometimes bubbles up from the JIT on aarch64.
+ if errors.Is(err, sys.ENOTSUPP) {
+ return ebpf.ErrNotSupported
+ }
+ return err
+}, "5.1")
+
+// HaveV4ISA probes the running kernel if instructions of the v4 ISA are supported.
+//
+// Upstream commit 1f9a1ea821ff ("bpf: Support new sign-extension load insns").
+//
+// See the package documentation for the meaning of the error return value.
+func HaveV4ISA() error {
+ return haveV4ISA()
+}
+
+var haveV4ISA = internal.NewFeatureTest("v4 ISA", func() error {
+ err := probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.SocketFilter,
+ Instructions: asm.Instructions{
+ asm.Mov.Imm(asm.R0, 0),
+ asm.JEq.Imm(asm.R0, 1, "error"),
+ asm.LongJump("exit"),
+ asm.Mov.Imm(asm.R0, 1).WithSymbol("error"),
+ asm.Return().WithSymbol("exit"),
+ },
+ })
+ // This sometimes bubbles up from the JIT on aarch64.
+ if errors.Is(err, sys.ENOTSUPP) {
+ return ebpf.ErrNotSupported
+ }
+ return err
+}, "6.6")
diff --git a/vendor/github.com/cilium/ebpf/features/prog.go b/vendor/github.com/cilium/ebpf/features/prog.go
new file mode 100644
index 000000000..6441d5931
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/features/prog.go
@@ -0,0 +1,332 @@
+package features
+
+import (
+ "errors"
+ "fmt"
+ "slices"
+ "strings"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/asm"
+ "github.com/cilium/ebpf/btf"
+ "github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/internal/sys"
+ "github.com/cilium/ebpf/internal/unix"
+)
+
+// HaveProgramType probes the running kernel for the availability of the specified program type.
+//
+// See the package documentation for the meaning of the error return value.
+func HaveProgramType(pt ebpf.ProgramType) (err error) {
+ return haveProgramTypeMatrix.Result(pt)
+}
+
+func probeProgram(spec *ebpf.ProgramSpec) error {
+ if spec.Instructions == nil {
+ spec.Instructions = asm.Instructions{
+ asm.LoadImm(asm.R0, 0, asm.DWord),
+ asm.Return(),
+ }
+ }
+ prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{
+ LogDisabled: true,
+ })
+ if err == nil {
+ prog.Close()
+ }
+
+ switch {
+ // EINVAL occurs when attempting to create a program with an unknown type.
+ // E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end
+ // of the struct known by the running kernel, meaning the kernel is too old
+ // to support the given prog type.
+ case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
+ err = ebpf.ErrNotSupported
+ }
+
+ return err
+}
+
+var haveProgramTypeMatrix = internal.FeatureMatrix[ebpf.ProgramType]{
+ ebpf.SocketFilter: {Version: "3.19"},
+ ebpf.Kprobe: {Version: "4.1"},
+ ebpf.SchedCLS: {Version: "4.1"},
+ ebpf.SchedACT: {Version: "4.1"},
+ ebpf.TracePoint: {Version: "4.7"},
+ ebpf.XDP: {Version: "4.8"},
+ ebpf.PerfEvent: {Version: "4.9"},
+ ebpf.CGroupSKB: {Version: "4.10"},
+ ebpf.CGroupSock: {Version: "4.10"},
+ ebpf.LWTIn: {Version: "4.10"},
+ ebpf.LWTOut: {Version: "4.10"},
+ ebpf.LWTXmit: {Version: "4.10"},
+ ebpf.SockOps: {Version: "4.13"},
+ ebpf.SkSKB: {Version: "4.14"},
+ ebpf.CGroupDevice: {Version: "4.15"},
+ ebpf.SkMsg: {Version: "4.17"},
+ ebpf.RawTracepoint: {Version: "4.17"},
+ ebpf.CGroupSockAddr: {
+ Version: "4.17",
+ Fn: func() error {
+ return probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.CGroupSockAddr,
+ AttachType: ebpf.AttachCGroupInet4Connect,
+ })
+ },
+ },
+ ebpf.LWTSeg6Local: {Version: "4.18"},
+ ebpf.LircMode2: {Version: "4.18"},
+ ebpf.SkReuseport: {Version: "4.19"},
+ ebpf.FlowDissector: {Version: "4.20"},
+ ebpf.CGroupSysctl: {Version: "5.2"},
+ ebpf.RawTracepointWritable: {Version: "5.2"},
+ ebpf.CGroupSockopt: {
+ Version: "5.3",
+ Fn: func() error {
+ return probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.CGroupSockopt,
+ AttachType: ebpf.AttachCGroupGetsockopt,
+ })
+ },
+ },
+ ebpf.Tracing: {
+ Version: "5.5",
+ Fn: func() error {
+ return probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.Tracing,
+ AttachType: ebpf.AttachTraceFEntry,
+ AttachTo: "bpf_init",
+ })
+ },
+ },
+ ebpf.StructOps: {
+ Version: "5.6",
+ Fn: func() error {
+ err := probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.StructOps,
+ License: "GPL",
+ })
+ if errors.Is(err, sys.ENOTSUPP) {
+ // ENOTSUPP means the program type is at least known to the kernel.
+ return nil
+ }
+ return err
+ },
+ },
+ ebpf.Extension: {
+ Version: "5.6",
+ Fn: func() error {
+ // create btf.Func to add to first ins of target and extension so both progs are btf powered
+ btfFn := btf.Func{
+ Name: "a",
+ Type: &btf.FuncProto{
+ Return: &btf.Int{},
+ Params: []btf.FuncParam{
+ {Name: "ctx", Type: &btf.Pointer{Target: &btf.Struct{Name: "xdp_md"}}},
+ },
+ },
+ Linkage: btf.GlobalFunc,
+ }
+ insns := asm.Instructions{
+ btf.WithFuncMetadata(asm.Mov.Imm(asm.R0, 0), &btfFn),
+ asm.Return(),
+ }
+
+ // create target prog
+ prog, err := ebpf.NewProgramWithOptions(
+ &ebpf.ProgramSpec{
+ Type: ebpf.XDP,
+ Instructions: insns,
+ },
+ ebpf.ProgramOptions{
+ LogDisabled: true,
+ },
+ )
+ if err != nil {
+ return err
+ }
+ defer prog.Close()
+
+ // probe for Extension prog with target
+ return probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.Extension,
+ Instructions: insns,
+ AttachTarget: prog,
+ AttachTo: btfFn.Name,
+ })
+ },
+ },
+ ebpf.LSM: {
+ Version: "5.7",
+ Fn: func() error {
+ return probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.LSM,
+ AttachType: ebpf.AttachLSMMac,
+ AttachTo: "file_mprotect",
+ License: "GPL",
+ })
+ },
+ },
+ ebpf.SkLookup: {
+ Version: "5.9",
+ Fn: func() error {
+ return probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.SkLookup,
+ AttachType: ebpf.AttachSkLookup,
+ })
+ },
+ },
+ ebpf.Syscall: {
+ Version: "5.14",
+ Fn: func() error {
+ return probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.Syscall,
+ Flags: sys.BPF_F_SLEEPABLE,
+ })
+ },
+ },
+ ebpf.Netfilter: {
+ Version: "6.4",
+ Fn: func() error {
+ return probeProgram(&ebpf.ProgramSpec{
+ Type: ebpf.Netfilter,
+ AttachType: ebpf.AttachNetfilter,
+ })
+ },
+ },
+}
+
+func init() {
+ for key, ft := range haveProgramTypeMatrix {
+ ft.Name = key.String()
+ if ft.Fn == nil {
+ key := key // avoid the dreaded loop variable problem
+ ft.Fn = func() error { return probeProgram(&ebpf.ProgramSpec{Type: key}) }
+ }
+ }
+}
+
+type helperKey struct {
+ typ ebpf.ProgramType
+ helper asm.BuiltinFunc
+}
+
+var helperCache = internal.NewFeatureCache(func(key helperKey) *internal.FeatureTest {
+ return &internal.FeatureTest{
+ Name: fmt.Sprintf("%s for program type %s", key.helper, key.typ),
+ Fn: func() error {
+ return haveProgramHelper(key.typ, key.helper)
+ },
+ }
+})
+
+// HaveProgramHelper probes the running kernel for the availability of the specified helper
+// function to a specified program type.
+// Return values have the following semantics:
+//
+// err == nil: The feature is available.
+// errors.Is(err, ebpf.ErrNotSupported): The feature is not available.
+// err != nil: Any errors encountered during probe execution, wrapped.
+//
+// Note that the latter case may include false negatives, and that program creation may
+// succeed despite an error being returned.
+// Only `nil` and `ebpf.ErrNotSupported` are conclusive.
+//
+// Probe results are cached and persist throughout any process capability changes.
+func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error {
+ return helperCache.Result(helperKey{pt, helper})
+}
+
+func haveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error {
+ if ok := helperProbeNotImplemented(pt); ok {
+ return fmt.Errorf("no feature probe for %v/%v", pt, helper)
+ }
+
+ if err := HaveProgramType(pt); err != nil {
+ return err
+ }
+
+ spec := &ebpf.ProgramSpec{
+ Type: pt,
+ Instructions: asm.Instructions{
+ helper.Call(),
+ asm.LoadImm(asm.R0, 0, asm.DWord),
+ asm.Return(),
+ },
+ License: "GPL",
+ }
+
+ switch pt {
+ case ebpf.CGroupSockAddr:
+ spec.AttachType = ebpf.AttachCGroupInet4Connect
+ case ebpf.CGroupSockopt:
+ spec.AttachType = ebpf.AttachCGroupGetsockopt
+ case ebpf.SkLookup:
+ spec.AttachType = ebpf.AttachSkLookup
+ case ebpf.Syscall:
+ spec.Flags = sys.BPF_F_SLEEPABLE
+ case ebpf.Netfilter:
+ spec.AttachType = ebpf.AttachNetfilter
+ }
+
+ prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{
+ LogLevel: 1,
+ })
+ if err == nil {
+ prog.Close()
+ }
+
+ var verr *ebpf.VerifierError
+ if !errors.As(err, &verr) {
+ return err
+ }
+
+ helperTag := fmt.Sprintf("#%d", helper)
+
+ switch {
+ // EACCES occurs when attempting to create a program probe with a helper
+ // while the register args when calling this helper aren't set up properly.
+ // We interpret this as the helper being available, because the verifier
+ // returns EINVAL if the helper is not supported by the running kernel.
+ case errors.Is(err, unix.EACCES):
+ err = nil
+
+ // EINVAL occurs when attempting to create a program with an unknown helper.
+ case errors.Is(err, unix.EINVAL):
+ // https://github.com/torvalds/linux/blob/09a0fa92e5b45e99cf435b2fbf5ebcf889cf8780/kernel/bpf/verifier.c#L10663
+ if logContainsAll(verr.Log, "invalid func", helperTag) {
+ return ebpf.ErrNotSupported
+ }
+
+ // https://github.com/torvalds/linux/blob/09a0fa92e5b45e99cf435b2fbf5ebcf889cf8780/kernel/bpf/verifier.c#L10668
+ wrongProgramType := logContainsAll(verr.Log, "program of this type cannot use helper", helperTag)
+ // https://github.com/torvalds/linux/blob/59b418c7063d30e0a3e1f592d47df096db83185c/kernel/bpf/verifier.c#L10204
+ // 4.9 doesn't include # in verifier output.
+ wrongProgramType = wrongProgramType || logContainsAll(verr.Log, "unknown func")
+ if wrongProgramType {
+ return fmt.Errorf("program of this type cannot use helper: %w", ebpf.ErrNotSupported)
+ }
+ }
+
+ return err
+}
+
+func logContainsAll(log []string, needles ...string) bool {
+ first := max(len(log)-5, 0) // Check last 5 lines.
+ return slices.ContainsFunc(log[first:], func(line string) bool {
+ for _, needle := range needles {
+ if !strings.Contains(line, needle) {
+ return false
+ }
+ }
+ return true
+ })
+}
+
+func helperProbeNotImplemented(pt ebpf.ProgramType) bool {
+ switch pt {
+ case ebpf.Extension, ebpf.LSM, ebpf.StructOps, ebpf.Tracing:
+ return true
+ }
+ return false
+}
diff --git a/vendor/github.com/cilium/ebpf/features/version.go b/vendor/github.com/cilium/ebpf/features/version.go
new file mode 100644
index 000000000..d54d3ea21
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/features/version.go
@@ -0,0 +1,18 @@
+package features
+
+import "github.com/cilium/ebpf/internal/linux"
+
+// LinuxVersionCode returns the version of the currently running kernel
+// as defined in the LINUX_VERSION_CODE compile-time macro. It is represented
+// in the format described by the KERNEL_VERSION macro from linux/version.h.
+//
+// Do not use the version to make assumptions about the presence of certain
+// kernel features, always prefer feature probes in this package. Some
+// distributions backport or disable eBPF features.
+func LinuxVersionCode() (uint32, error) {
+ v, err := linux.KernelVersion()
+ if err != nil {
+ return 0, err
+ }
+ return v.Kernel(), nil
+}
diff --git a/vendor/github.com/cilium/ebpf/info.go b/vendor/github.com/cilium/ebpf/info.go
index b15202a0f..b2d08de4b 100644
--- a/vendor/github.com/cilium/ebpf/info.go
+++ b/vendor/github.com/cilium/ebpf/info.go
@@ -140,7 +140,7 @@ func newMapInfoFromFd(fd *sys.FD) (*MapInfo, error) {
// /proc/self/fdinfo. It only writes data into fields that have a zero value.
func readMapInfoFromProc(fd *sys.FD, mi *MapInfo) error {
var mapType uint32
- err := scanFdInfo(fd, map[string]interface{}{
+ err := scanFdInfo(fd, map[string]any{
"map_type": &mapType,
"map_id": &mi.id,
"key_size": &mi.KeySize,
@@ -301,8 +301,11 @@ type ProgramInfo struct {
btf btf.ID
loadTime time.Duration
+ restricted bool
+
maps []MapID
insns []byte
+ numInsns uint32
jitedSize uint32
verifiedInstructions uint32
@@ -370,6 +373,7 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
jitedSize: info.JitedProgLen,
loadTime: time.Duration(info.LoadTime),
verifiedInstructions: info.VerifiedInsns,
+ numInsns: info.XlatedProgLen,
}
// Supplement OBJ_INFO with data from /proc/self/fdinfo. It contains fields
@@ -414,9 +418,20 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
if info.XlatedProgLen > 0 {
pi.insns = make([]byte, info.XlatedProgLen)
- info2.XlatedProgLen = info.XlatedProgLen
- info2.XlatedProgInsns = sys.SlicePointer(pi.insns)
- makeSecondCall = true
+ var info3 sys.ProgInfo
+ info3.XlatedProgLen = info.XlatedProgLen
+ info3.XlatedProgInsns = sys.SlicePointer(pi.insns)
+
+ // When kernel.kptr_restrict and net.core.bpf_jit_harden are both set, it causes the
+ // syscall to abort when trying to readback xlated instructions, skipping other info
+ // as well. So request xlated instructions separately.
+ if err := sys.ObjInfo(fd, &info3); err != nil {
+ return nil, err
+ }
+ if info3.XlatedProgInsns.IsNil() {
+ pi.restricted = true
+ pi.insns = nil
+ }
}
if info.NrLineInfo > 0 {
@@ -475,14 +490,56 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
if err := sys.ObjInfo(fd, &info2); err != nil {
return nil, err
}
+ if info.JitedProgLen > 0 && info2.JitedProgInsns.IsNil() {
+ // JIT information is not available due to kernel.kptr_restrict
+ pi.jitedInfo.lineInfos = nil
+ pi.jitedInfo.ksyms = nil
+ pi.jitedInfo.insns = nil
+ pi.jitedInfo.funcLens = nil
+ }
+ }
+
+ if len(pi.Name) == len(info.Name)-1 { // Possibly truncated, check BTF info for full name
+ name, err := readNameFromFunc(&pi)
+ if err == nil {
+ pi.Name = name
+ } // If an error occurs, keep the truncated name, which is better than none
}
return &pi, nil
}
+func readNameFromFunc(pi *ProgramInfo) (string, error) {
+ if pi.numFuncInfos == 0 {
+ return "", errors.New("no function info")
+ }
+
+ spec, err := pi.btfSpec()
+ if err != nil {
+ return "", err
+ }
+
+ funcInfos, err := btf.LoadFuncInfos(
+ bytes.NewReader(pi.funcInfos),
+ internal.NativeEndian,
+ pi.numFuncInfos,
+ spec,
+ )
+ if err != nil {
+ return "", err
+ }
+
+ for _, funcInfo := range funcInfos {
+ if funcInfo.Offset == 0 { // Information about the whole program
+ return funcInfo.Func.Name, nil
+ }
+ }
+ return "", errors.New("no function info about program")
+}
+
func readProgramInfoFromProc(fd *sys.FD, pi *ProgramInfo) error {
var progType uint32
- err := scanFdInfo(fd, map[string]interface{}{
+ err := scanFdInfo(fd, map[string]any{
"prog_type": &progType,
"prog_tag": &pi.Tag,
"memlock": &pi.memlock,
@@ -556,10 +613,17 @@ func (pi *ProgramInfo) btfSpec() (*btf.Spec, error) {
return spec, nil
}
+// ErrRestrictedKernel is returned when kernel address information is restricted
+// by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls.
+var ErrRestrictedKernel = internal.ErrRestrictedKernel
+
// LineInfos returns the BTF line information of the program.
//
// Available from 5.0.
//
+// Returns an error wrapping [ErrRestrictedKernel] if line infos are restricted
+// by sysctls.
+//
// Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns
// ErrNotSupported if the program was created without BTF or if the kernel
// doesn't support the field.
@@ -599,6 +663,9 @@ func (pi *ProgramInfo) LineInfos() (btf.LineOffsets, error) {
// this metadata requires CAP_SYS_ADMIN or equivalent. If capability is
// unavailable, the instructions will be returned without metadata.
//
+// Returns an error wrapping [ErrRestrictedKernel] if instructions are
+// restricted by sysctls.
+//
// Available from 4.13. Requires CAP_BPF or equivalent for plain instructions.
// Requires CAP_SYS_ADMIN for instructions with metadata.
func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
@@ -606,6 +673,10 @@ func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
return nil, fmt.Errorf("read instructions: %w", internal.ErrNotSupportedOnOS)
}
+ if pi.restricted {
+ return nil, fmt.Errorf("instructions: %w", ErrRestrictedKernel)
+ }
+
// If the calling process is not BPF-capable or if the kernel doesn't
// support getting xlated instructions, the field will be zero.
if len(pi.insns) == 0 {
@@ -637,27 +708,20 @@ func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
return nil, fmt.Errorf("unable to get BTF spec: %w", err)
}
- lineInfos, err := btf.LoadLineInfos(
- bytes.NewReader(pi.lineInfos),
- internal.NativeEndian,
- pi.numLineInfos,
- spec,
- )
+ lineInfos, err := btf.LoadLineInfos(bytes.NewReader(pi.lineInfos), internal.NativeEndian, pi.numLineInfos, spec)
if err != nil {
return nil, fmt.Errorf("parse line info: %w", err)
}
- funcInfos, err := btf.LoadFuncInfos(
- bytes.NewReader(pi.funcInfos),
- internal.NativeEndian,
- pi.numFuncInfos,
- spec,
- )
+ funcInfos, err := btf.LoadFuncInfos(bytes.NewReader(pi.funcInfos), internal.NativeEndian, pi.numFuncInfos, spec)
if err != nil {
return nil, fmt.Errorf("parse func info: %w", err)
}
- btf.AssignMetadataToInstructions(insns, funcInfos, lineInfos, btf.CORERelocationInfos{})
+ iter := insns.Iterate()
+ for iter.Next() {
+ assignMetadata(iter.Ins, iter.Offset, &funcInfos, &lineInfos, nil)
+ }
}
}
@@ -671,8 +735,12 @@ func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
return insns, nil
}
-// JitedSize returns the size of the program's JIT-compiled machine code in bytes, which is the
-// actual code executed on the host's CPU. This field requires the BPF JIT compiler to be enabled.
+// JitedSize returns the size of the program's JIT-compiled machine code in
+// bytes, which is the actual code executed on the host's CPU. This field
+// requires the BPF JIT compiler to be enabled.
+//
+// Returns an error wrapping [ErrRestrictedKernel] if jited program size is
+// restricted by sysctls.
//
// Available from 4.13. Reading this metadata requires CAP_BPF or equivalent.
func (pi *ProgramInfo) JitedSize() (uint32, error) {
@@ -682,16 +750,15 @@ func (pi *ProgramInfo) JitedSize() (uint32, error) {
return pi.jitedSize, nil
}
-// TranslatedSize returns the size of the program's translated instructions in bytes, after it has
-// been verified and rewritten by the kernel.
+// TranslatedSize returns the size of the program's translated instructions in
+// bytes, after it has been verified and rewritten by the kernel.
//
// Available from 4.13. Reading this metadata requires CAP_BPF or equivalent.
func (pi *ProgramInfo) TranslatedSize() (int, error) {
- insns := len(pi.insns)
- if insns == 0 {
+ if pi.numInsns == 0 {
return 0, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported)
}
- return insns, nil
+ return int(pi.numInsns), nil
}
// MapIDs returns the maps related to the program.
@@ -782,6 +849,9 @@ func (pi *ProgramInfo) JitedFuncLens() ([]uint32, bool) {
//
// Available from 5.0.
//
+// Returns an error wrapping [ErrRestrictedKernel] if function information is
+// restricted by sysctls.
+//
// Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns
// ErrNotSupported if the program was created without BTF or if the kernel
// doesn't support the field.
@@ -812,7 +882,7 @@ func (pi *ProgramInfo) Memlock() (uint64, bool) {
return pi.memlock, pi.memlock > 0
}
-func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error {
+func scanFdInfo(fd *sys.FD, fields map[string]any) error {
if platform.IsWindows {
return fmt.Errorf("read fdinfo: %w", internal.ErrNotSupportedOnOS)
}
@@ -829,7 +899,7 @@ func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error {
return nil
}
-func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
+func scanFdInfoReader(r io.Reader, fields map[string]any) error {
var (
scanner = bufio.NewScanner(r)
scanned int
diff --git a/vendor/github.com/cilium/ebpf/internal/efw/map.go b/vendor/github.com/cilium/ebpf/internal/efw/map.go
new file mode 100644
index 000000000..82f510fef
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/internal/efw/map.go
@@ -0,0 +1,109 @@
+//go:build windows
+
+package efw
+
+import (
+ "runtime"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+/*
+ebpf_ring_buffer_map_map_buffer(
+
+ fd_t map_fd,
+ _Outptr_result_maybenull_ void** consumer,
+ _Outptr_result_maybenull_ const void** producer,
+ _Outptr_result_buffer_maybenull_(*data_size) const uint8_t** data,
+ _Out_ size_t* data_size) EBPF_NO_EXCEPT;
+*/
+var ebpfRingBufferMapMapBufferProc = newProc("ebpf_ring_buffer_map_map_buffer")
+
+func EbpfRingBufferMapMapBuffer(mapFd int) (consumer, producer, data *uint8, dataLen Size, _ error) {
+ addr, err := ebpfRingBufferMapMapBufferProc.Find()
+ if err != nil {
+ return nil, nil, nil, 0, err
+ }
+
+ err = errorResult(syscall.SyscallN(addr,
+ uintptr(mapFd),
+ uintptr(unsafe.Pointer(&consumer)),
+ uintptr(unsafe.Pointer(&producer)),
+ uintptr(unsafe.Pointer(&data)),
+ uintptr(unsafe.Pointer(&dataLen)),
+ ))
+ if err != nil {
+ return nil, nil, nil, 0, err
+ }
+
+ return consumer, producer, data, dataLen, nil
+}
+
+/*
+ebpf_ring_buffer_map_unmap_buffer(
+
+ fd_t map_fd, _In_ void* consumer, _In_ const void* producer, _In_ const void* data) EBPF_NO_EXCEPT;
+*/
+var ebpfRingBufferMapUnmapBufferProc = newProc("ebpf_ring_buffer_map_unmap_buffer")
+
+func EbpfRingBufferMapUnmapBuffer(mapFd int, consumer, producer, data *uint8) error {
+ addr, err := ebpfRingBufferMapUnmapBufferProc.Find()
+ if err != nil {
+ return err
+ }
+
+ return errorResult(syscall.SyscallN(addr,
+ uintptr(mapFd),
+ uintptr(unsafe.Pointer(consumer)),
+ uintptr(unsafe.Pointer(producer)),
+ uintptr(unsafe.Pointer(data)),
+ ))
+}
+
+/*
+ebpf_result_t ebpf_map_set_wait_handle(
+
+ fd_t map_fd,
+ uint64_t index,
+ ebpf_handle_t handle)
+*/
+var ebpfMapSetWaitHandleProc = newProc("ebpf_map_set_wait_handle")
+
+func EbpfMapSetWaitHandle(mapFd int, index uint64, handle windows.Handle) error {
+ addr, err := ebpfMapSetWaitHandleProc.Find()
+ if err != nil {
+ return err
+ }
+
+ return errorResult(syscall.SyscallN(addr,
+ uintptr(mapFd),
+ uintptr(index),
+ uintptr(handle),
+ ))
+}
+
+/*
+ebpf_result_t ebpf_ring_buffer_map_write(
+
+ fd_t ring_buffer_map_fd,
+ const void* data,
+ size_t data_length)
+*/
+var ebpfRingBufferMapWriteProc = newProc("ebpf_ring_buffer_map_write")
+
+func EbpfRingBufferMapWrite(ringBufferMapFd int, data []byte) error {
+ addr, err := ebpfRingBufferMapWriteProc.Find()
+ if err != nil {
+ return err
+ }
+
+ err = errorResult(syscall.SyscallN(addr,
+ uintptr(ringBufferMapFd),
+ uintptr(unsafe.Pointer(&data[0])),
+ uintptr(len(data)),
+ ))
+ runtime.KeepAlive(data)
+ return err
+}
diff --git a/vendor/github.com/cilium/ebpf/internal/efw/result.go b/vendor/github.com/cilium/ebpf/internal/efw/result.go
index 3275941d3..4c68da931 100644
--- a/vendor/github.com/cilium/ebpf/internal/efw/result.go
+++ b/vendor/github.com/cilium/ebpf/internal/efw/result.go
@@ -5,7 +5,7 @@ package efw
// See https://github.com/microsoft/ebpf-for-windows/blob/main/include/ebpf_result.h
type Result int32
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -tags windows -output result_string_windows.go -type=Result
+//go:generate go tool stringer -tags windows -output result_string_windows.go -type=Result
const (
EBPF_SUCCESS Result = iota
diff --git a/vendor/github.com/cilium/ebpf/internal/feature.go b/vendor/github.com/cilium/ebpf/internal/feature.go
index 82b8d9395..e27064c23 100644
--- a/vendor/github.com/cilium/ebpf/internal/feature.go
+++ b/vendor/github.com/cilium/ebpf/internal/feature.go
@@ -16,6 +16,10 @@ var ErrNotSupported = errors.New("not supported")
// operating system.
var ErrNotSupportedOnOS = fmt.Errorf("%w on %s", ErrNotSupported, runtime.GOOS)
+// ErrRestrictedKernel is returned when kernel address information is restricted
+// by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls.
+var ErrRestrictedKernel = errors.New("restricted by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls")
+
// UnsupportedFeatureError is returned by FeatureTest() functions.
type UnsupportedFeatureError struct {
// The minimum version required for this feature.
diff --git a/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go b/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go
index 9154a8a79..efc64a503 100644
--- a/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go
+++ b/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go
@@ -16,151 +16,6 @@ import (
var errAmbiguousKsym = errors.New("multiple kernel symbols with the same name")
var symAddrs cache[string, uint64]
-var symModules cache[string, string]
-
-// Module returns the kernel module providing the given symbol in the kernel, if
-// any. Returns an empty string and no error if the symbol is not present in the
-// kernel. Only function symbols are considered. Returns an error if multiple
-// symbols with the same name were found.
-//
-// Consider [AssignModules] if you need to resolve multiple symbols, as it will
-// only perform one iteration over /proc/kallsyms.
-func Module(name string) (string, error) {
- if name == "" {
- return "", nil
- }
-
- if mod, ok := symModules.Load(name); ok {
- return mod, nil
- }
-
- request := map[string]string{name: ""}
- if err := AssignModules(request); err != nil {
- return "", err
- }
-
- return request[name], nil
-}
-
-// AssignModules looks up the kernel module providing each given symbol, if any,
-// and assigns them to their corresponding values in the symbols map. Only
-// function symbols are considered. Results of all lookups are cached,
-// successful or otherwise.
-//
-// Any symbols missing in the kernel are ignored. Returns an error if multiple
-// symbols with a given name were found.
-func AssignModules(symbols map[string]string) error {
- if !platform.IsLinux {
- return fmt.Errorf("read /proc/kallsyms: %w", internal.ErrNotSupportedOnOS)
- }
-
- if len(symbols) == 0 {
- return nil
- }
-
- // Attempt to fetch symbols from cache.
- request := make(map[string]string)
- for name := range symbols {
- if mod, ok := symModules.Load(name); ok {
- symbols[name] = mod
- continue
- }
-
- // Mark the symbol to be read from /proc/kallsyms.
- request[name] = ""
- }
- if len(request) == 0 {
- // All symbols satisfied from cache.
- return nil
- }
-
- f, err := os.Open("/proc/kallsyms")
- if err != nil {
- return err
- }
- defer f.Close()
-
- if err := assignModules(f, request); err != nil {
- return fmt.Errorf("assigning symbol modules: %w", err)
- }
-
- // Update the cache with the new symbols. Cache all requested symbols, even if
- // they're missing or don't belong to a module.
- for name, mod := range request {
- symModules.Store(name, mod)
- symbols[name] = mod
- }
-
- return nil
-}
-
-// assignModules assigns kernel symbol modules read from f to values requested
-// by symbols. Always scans the whole input to make sure the user didn't request
-// an ambiguous symbol.
-func assignModules(f io.Reader, symbols map[string]string) error {
- if len(symbols) == 0 {
- return nil
- }
-
- found := make(map[string]struct{})
- r := newReader(f)
- for r.Line() {
- // Only look for function symbols in the kernel's text section (tT).
- s, err, skip := parseSymbol(r, []rune{'t', 'T'})
- if err != nil {
- return fmt.Errorf("parsing kallsyms line: %w", err)
- }
- if skip {
- continue
- }
-
- if _, requested := symbols[string(s.name)]; !requested {
- continue
- }
-
- if _, ok := found[string(s.name)]; ok {
- // We've already seen this symbol. Return an error to avoid silently
- // attaching to a symbol in the wrong module. libbpf also rejects
- // referring to ambiguous symbols.
- //
- // We can't simply check if we already have a value for the given symbol,
- // since many won't have an associated kernel module.
- return fmt.Errorf("symbol %s: duplicate found at address 0x%x (module %q): %w",
- s.name, s.addr, s.mod, errAmbiguousKsym)
- }
-
- symbols[string(s.name)] = string(s.mod)
- found[string(s.name)] = struct{}{}
- }
- if err := r.Err(); err != nil {
- return fmt.Errorf("reading kallsyms: %w", err)
- }
-
- return nil
-}
-
-// Address returns the address of the given symbol in the kernel. Returns 0 and
-// no error if the symbol is not present. Returns an error if multiple addresses
-// were found for a symbol.
-//
-// Consider [AssignAddresses] if you need to resolve multiple symbols, as it
-// will only perform one iteration over /proc/kallsyms.
-func Address(symbol string) (uint64, error) {
- if symbol == "" {
- return 0, nil
- }
-
- if addr, ok := symAddrs.Load(symbol); ok {
- return addr, nil
- }
-
- request := map[string]uint64{symbol: 0}
- if err := AssignAddresses(request); err != nil {
- return 0, err
- }
-
- return request[symbol], nil
-}
// AssignAddresses looks up the addresses of the requested symbols in the kernel
// and assigns them to their corresponding values in the symbols map. Results
@@ -238,6 +93,19 @@ func assignAddresses(f io.Reader, symbols map[string]uint64) error {
return fmt.Errorf("symbol %s(0x%x): duplicate found at address 0x%x: %w", s.name, existing, s.addr, errAmbiguousKsym)
}
if requested {
+ // Reading a symbol with a zero address is a strong indication that
+ // kptr_restrict is set and the process doesn't have CAP_SYSLOG, or
+ // kptr_restrict is set to 2 (never show addresses).
+ //
+ // When running the kernel with KASLR disabled (like CI kernels running in
+ // microVMs), kallsyms will display many absolute symbols at address 0.
+ // This memory is unlikely to contain anything useful, and production
+ // machines are unlikely to run without KASLR.
+ //
+ // Return a helpful error instead of silently returning zero addresses.
+ if s.addr == 0 {
+ return fmt.Errorf("symbol %s: %w", s.name, internal.ErrRestrictedKernel)
+ }
symbols[string(s.name)] = s.addr
}
}
diff --git a/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go b/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go
index 29c62b626..de18dc829 100644
--- a/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go
+++ b/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go
@@ -183,8 +183,8 @@ func putValueString(data []byte, typ btf.Type, value string) error {
// Any Int, which is not bool, of one byte could be used to store char:
// https://github.com/torvalds/linux/blob/1a5304fecee5/tools/lib/bpf/libbpf.c#L3637-L3638
- if contentType.Size != 1 && contentType.Encoding != btf.Bool {
- return fmt.Errorf("cannot add string value, expected array of btf.Int of size 1, got array of btf.Int of size: %v", contentType.Size)
+ if contentType.Size != 1 || contentType.Encoding == btf.Bool {
+ return fmt.Errorf("cannot add string value, expected array of non-bool btf.Int of size 1, got array of btf.Int with encoding %v and size %v", contentType.Encoding, contentType.Size)
}
if !strings.HasPrefix(value, `"`) || !strings.HasSuffix(value, `"`) {
diff --git a/vendor/github.com/cilium/ebpf/internal/mountinfo/mountinfo.go b/vendor/github.com/cilium/ebpf/internal/mountinfo/mountinfo.go
new file mode 100644
index 000000000..a3cb1d19b
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/internal/mountinfo/mountinfo.go
@@ -0,0 +1,150 @@
+// Package mountinfo parses /proc/self/mountinfo to discover filesystem
+// mounts visible in the calling process's mount namespace.
+package mountinfo
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "slices"
+ "strings"
+ "sync"
+
+ "github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/internal/platform"
+)
+
+// Entry represents one parsed line from /proc/self/mountinfo.
+type Entry struct {
+ // MountPoint is the absolute path where the filesystem is mounted,
+ // with octal escapes (\040, \011, \012, \134) decoded.
+ MountPoint string
+
+ // Root is the path within the source filesystem that's exposed at
+ // MountPoint, octal-decoded. It is "/" for a full filesystem mount and
+ // some other path (e.g. "/events") when a subdirectory of the source
+ // filesystem has been bind-mounted onto MountPoint. Callers that want
+ // to use MountPoint as the root of a filesystem should reject entries
+ // where Root != "/".
+ Root string
+
+ // FSType is the filesystem type, e.g. "bpf", "tracefs", "debugfs".
+ FSType string
+}
+
+// Read parses /proc/self/mountinfo and returns one Entry per mount line.
+// The result is cached for the lifetime of the process; mounts that appear
+// or disappear after the first call are not reflected.
+//
+// On non-Linux, returns an error wrapping [internal.ErrNotSupportedOnOS].
+func Read() ([]Entry, error) {
+ return readOnce()
+}
+
+// FindByFSType returns the mount points for filesystems of the given type,
+// in order of appearance, with duplicates removed.
+func FindByFSType(fstype string) ([]string, error) {
+ entries, err := Read()
+ if err != nil {
+ return nil, err
+ }
+ return filterByFSType(entries, fstype), nil
+}
+
+func filterByFSType(entries []Entry, fstype string) []string {
+ var mounts []string
+ for _, e := range entries {
+ if e.FSType != fstype {
+ continue
+ }
+ // Number of matching mounts is expected to be very low; linear
+ // search is faster and more cache-friendly than a map at this
+ // scale, and avoids the allocations a map would incur.
+ if !slices.Contains(mounts, e.MountPoint) {
+ mounts = append(mounts, e.MountPoint)
+ }
+ }
+ return mounts
+}
+
+// parseEntries parses mountinfo data read from r.
+func parseEntries(r io.Reader) ([]Entry, error) {
+ var entries []Entry
+ // Format of /proc/self/mountinfo:
+ //
+ // {id} {parent id} {major:minor} {root} {mount point} {mount options} [optional fields...] - {filesystem type} {source} {superblock options}
+ br := bufio.NewReader(r)
+ for {
+ line, err := br.ReadString('\n')
+ if line != "" {
+ line = strings.TrimRight(line, "\n")
+ if line != "" {
+ entry, perr := parseLine(line)
+ if perr != nil {
+ return nil, perr
+ }
+ entries = append(entries, entry)
+ }
+ }
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ return nil, fmt.Errorf("read mountinfo: %w", err)
+ }
+ }
+
+ return entries, nil
+}
+
+func parseLine(line string) (Entry, error) {
+ firstHalfStr, secondHalfStr, ok := strings.Cut(line, " - ")
+ if !ok {
+ return Entry{}, fmt.Errorf("invalid mountinfo line, missing dash: %q", line)
+ }
+
+ secondHalf := strings.Fields(strings.TrimSpace(secondHalfStr))
+ if len(secondHalf) == 0 {
+ return Entry{}, fmt.Errorf("invalid mountinfo line, too few fields after dash: %q", line)
+ }
+
+ firstHalf := strings.Fields(strings.TrimSpace(firstHalfStr))
+ if len(firstHalf) < 6 {
+ return Entry{}, fmt.Errorf("invalid mountinfo line, too few fields: %q", line)
+ }
+
+ return Entry{
+ Root: unescape(firstHalf[3]),
+ MountPoint: unescape(firstHalf[4]),
+ FSType: secondHalf[0],
+ }, nil
+}
+
+// show_mountinfo in the kernel has the escape set of ' \t\n\\'. Instead of
+// a full octal unescaper, only replace these specific characters.
+var unescaper = strings.NewReplacer(
+ `\040`, " ",
+ `\011`, "\t",
+ `\012`, "\n",
+ `\134`, `\`,
+)
+
+func unescape(s string) string {
+ return unescaper.Replace(s)
+}
+
+var readOnce = sync.OnceValues(func() ([]Entry, error) {
+ if !platform.IsLinux {
+ return nil, fmt.Errorf("mountinfo: %w", internal.ErrNotSupportedOnOS)
+ }
+
+ f, err := os.Open("/proc/self/mountinfo")
+ if err != nil {
+ return nil, fmt.Errorf("open mountinfo: %w", err)
+ }
+ defer f.Close()
+
+ return parseEntries(f)
+})
diff --git a/vendor/github.com/cilium/ebpf/internal/nil.go b/vendor/github.com/cilium/ebpf/internal/nil.go
new file mode 100644
index 000000000..83ba6c6f2
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/internal/nil.go
@@ -0,0 +1,24 @@
+package internal
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// IsNilPointer returns an error if i is a nil interface or a nil pointer.
+// Otherwise, it returns nil.
+func IsNilPointer(i any) error {
+ if i == nil {
+ return fmt.Errorf("nil interface")
+ }
+ v := reflect.ValueOf(i)
+ if v.Kind() != reflect.Pointer {
+ return nil
+ }
+
+ if v.IsNil() {
+ return fmt.Errorf("nil %T", i)
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/cilium/ebpf/internal/sys/doc.go b/vendor/github.com/cilium/ebpf/internal/sys/doc.go
index dfe174448..75d7e4013 100644
--- a/vendor/github.com/cilium/ebpf/internal/sys/doc.go
+++ b/vendor/github.com/cilium/ebpf/internal/sys/doc.go
@@ -3,4 +3,4 @@ package sys
// Regenerate types.go by invoking go generate in the current directory.
-//go:generate go run github.com/cilium/ebpf/internal/cmd/gentypes ../../btf/testdata/vmlinux.btf.gz
+//go:generate go tool gentypes ../../btf/testdata/vmlinux.btf.gz
diff --git a/vendor/github.com/cilium/ebpf/internal/sys/fd.go b/vendor/github.com/cilium/ebpf/internal/sys/fd.go
index 7e769fb54..f12d11c20 100644
--- a/vendor/github.com/cilium/ebpf/internal/sys/fd.go
+++ b/vendor/github.com/cilium/ebpf/internal/sys/fd.go
@@ -21,23 +21,14 @@ const invalidFd = -1
func newFD(value int) *FD {
testmain.TraceFD(value, 1)
- fd := &FD{value}
- runtime.SetFinalizer(fd, (*FD).finalize)
+ fd := &FD{raw: value}
+ fd.cleanup = runtime.AddCleanup(fd, func(raw int) {
+ testmain.LeakFD(raw)
+ _ = unix.Close(raw)
+ }, fd.raw)
return fd
}
-// finalize is set as the FD's runtime finalizer and
-// sends a leak trace before calling FD.Close().
-func (fd *FD) finalize() {
- if fd.raw == invalidFd {
- return
- }
-
- testmain.LeakFD(fd.raw)
-
- _ = fd.Close()
-}
-
func (fd *FD) String() string {
return strconv.FormatInt(int64(fd.raw), 10)
}
@@ -63,6 +54,6 @@ func (fd *FD) Disown() int {
testmain.ForgetFD(value)
fd.raw = invalidFd
- runtime.SetFinalizer(fd, nil)
+ fd.cleanup.Stop()
return value
}
diff --git a/vendor/github.com/cilium/ebpf/internal/sys/fd_other.go b/vendor/github.com/cilium/ebpf/internal/sys/fd_other.go
index 47057395e..2a6423a59 100644
--- a/vendor/github.com/cilium/ebpf/internal/sys/fd_other.go
+++ b/vendor/github.com/cilium/ebpf/internal/sys/fd_other.go
@@ -5,12 +5,14 @@ package sys
import (
"fmt"
"os"
+ "runtime"
"github.com/cilium/ebpf/internal/unix"
)
type FD struct {
- raw int
+ raw int
+ cleanup runtime.Cleanup
}
// NewFD wraps a raw fd with a finalizer.
diff --git a/vendor/github.com/cilium/ebpf/internal/sys/fd_windows.go b/vendor/github.com/cilium/ebpf/internal/sys/fd_windows.go
index f39276382..1291c763f 100644
--- a/vendor/github.com/cilium/ebpf/internal/sys/fd_windows.go
+++ b/vendor/github.com/cilium/ebpf/internal/sys/fd_windows.go
@@ -3,6 +3,7 @@ package sys
import (
"fmt"
"os"
+ "runtime"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/efw"
@@ -12,7 +13,8 @@ import (
//
// It is not equivalent to a real file descriptor or handle.
type FD struct {
- raw int
+ raw int
+ cleanup runtime.Cleanup
}
// NewFD wraps a raw fd with a finalizer.
diff --git a/vendor/github.com/cilium/ebpf/internal/sys/ptr.go b/vendor/github.com/cilium/ebpf/internal/sys/ptr.go
index 173665c2a..aa6c2e91a 100644
--- a/vendor/github.com/cilium/ebpf/internal/sys/ptr.go
+++ b/vendor/github.com/cilium/ebpf/internal/sys/ptr.go
@@ -28,6 +28,10 @@ type TypedPointer[T any] struct {
ptr Pointer
}
+func (p TypedPointer[T]) IsNil() bool {
+ return p.ptr.ptr == nil
+}
+
// SlicePointer creates a [TypedPointer] from a slice.
func SlicePointer[T comparable](s []T) TypedPointer[T] {
return TypedPointer[T]{ptr: UnsafeSlicePointer(s)}
diff --git a/vendor/github.com/cilium/ebpf/internal/sys/syscall.go b/vendor/github.com/cilium/ebpf/internal/sys/syscall.go
index f2fffd26b..3a2ae9c5a 100644
--- a/vendor/github.com/cilium/ebpf/internal/sys/syscall.go
+++ b/vendor/github.com/cilium/ebpf/internal/sys/syscall.go
@@ -18,6 +18,7 @@ const ENOTSUPP = unix.Errno(524)
// ProgInfo
// LinkInfo
// BtfInfo
+// TokenInfo
type Info interface {
info() (unsafe.Pointer, uint32)
}
@@ -72,10 +73,30 @@ func (i *KprobeMultiLinkInfo) info() (unsafe.Pointer, uint32) {
return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))
}
+func (i *UprobeMultiLinkInfo) info() (unsafe.Pointer, uint32) {
+ return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))
+}
+
+func (i *RawTracepointLinkInfo) info() (unsafe.Pointer, uint32) {
+ return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))
+}
+
func (i *KprobeLinkInfo) info() (unsafe.Pointer, uint32) {
return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))
}
+func (i *UprobeLinkInfo) info() (unsafe.Pointer, uint32) {
+ return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))
+}
+
+func (i *TracepointLinkInfo) info() (unsafe.Pointer, uint32) {
+ return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))
+}
+
+func (i *EventLinkInfo) info() (unsafe.Pointer, uint32) {
+ return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))
+}
+
var _ Info = (*BtfInfo)(nil)
func (i *BtfInfo) info() (unsafe.Pointer, uint32) {
@@ -86,9 +107,15 @@ func (i *PerfEventLinkInfo) info() (unsafe.Pointer, uint32) {
return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))
}
+var _ Info = (*TokenInfo)(nil)
+
+func (i *TokenInfo) info() (unsafe.Pointer, uint32) {
+ return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))
+}
+
// ObjInfo retrieves information about a BPF Fd.
//
-// info may be one of MapInfo, ProgInfo, LinkInfo and BtfInfo.
+// info may be one of MapInfo, ProgInfo, LinkInfo, BtfInfo and TokenInfo.
func ObjInfo(fd *FD, info Info) error {
ptr, len := info.info()
err := ObjGetInfoByFd(&ObjGetInfoByFdAttr{
diff --git a/vendor/github.com/cilium/ebpf/internal/sys/syscall_other.go b/vendor/github.com/cilium/ebpf/internal/sys/syscall_other.go
index b99e6e462..969eaa410 100644
--- a/vendor/github.com/cilium/ebpf/internal/sys/syscall_other.go
+++ b/vendor/github.com/cilium/ebpf/internal/sys/syscall_other.go
@@ -3,6 +3,7 @@
package sys
import (
+ "errors"
"fmt"
"os"
"path/filepath"
@@ -24,6 +25,11 @@ func BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
defer unmaskProfilerSignal()
}
+ tok, err := tokenAttr(cmd, attr)
+ if err != nil {
+ return 0, fmt.Errorf("apply token attributes: %w", err)
+ }
+
for {
r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size)
runtime.KeepAlive(attr)
@@ -39,6 +45,24 @@ func BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
err = wrappedErrno{errNo}
}
+ // Tokens are in use, attempt to enrich EPERM with capability information.
+ if tok != nil && errors.Is(err, unix.EPERM) {
+ // Kernels before 6.17 don't have token info, so we can't return accurate
+ // errors. Make the token error a hint by adding a question mark.
+ //
+ // Returning ErrTokenCapabilities here isn't ideal since it's not
+ // conclusive, but since this behaviour is limited to one LTS release
+ // (6.12), and only when tokens are enabled, it's better than having to
+ // account for it in all existing feature probes and tests.
+ if tok.info == nil {
+ return r1, fmt.Errorf("%w (%w?)", err, ErrTokenCapabilities)
+ }
+
+ if terr := tok.Capable(cmd, attr); terr != nil {
+ return r1, fmt.Errorf("%w: %w", terr, err)
+ }
+ }
+
return r1, err
}
}
diff --git a/vendor/github.com/cilium/ebpf/internal/sys/token.go b/vendor/github.com/cilium/ebpf/internal/sys/token.go
new file mode 100644
index 000000000..9a5df3922
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/internal/sys/token.go
@@ -0,0 +1,226 @@
+package sys
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+ "unsafe"
+
+ "github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/internal/mountinfo"
+ "github.com/cilium/ebpf/internal/platform"
+ "github.com/cilium/ebpf/internal/testutils/testmain"
+ "github.com/cilium/ebpf/internal/unix"
+)
+
+var (
+ // ErrTokenCapabilities indicates that the operation failed due to
+ // insufficient capabilities being delegated to the token.
+ ErrTokenCapabilities = errors.New("token insufficiently privileged")
+)
+
+// Token is a BPF token that can be used to create BPF objects that normally
+// require CAP_SYS_ADMIN or CAP_NET_ADMIN.
+//
+// With a valid token, equivalent BPF objects can be created while only being
+// granted CAP_BPF.
+type Token struct {
+ fd *FD
+
+ // Token info is available as of Linux 6.17, may be nil.
+ info *TokenInfo
+}
+
+// FD returns the raw file descriptor of the token.
+func (t *Token) FD() int {
+ return t.fd.Int()
+}
+
+// Capable returns an error if the token does not allow using cmd or some of its
+// provided attributes.
+//
+// Always returns nil on kernels before 6.17, as they don't provide token info.
+func (t *Token) Capable(cmd Cmd, attr unsafe.Pointer) error {
+ if t.info == nil {
+ return nil
+ }
+
+ if !t.cmd(cmd) {
+ return fmt.Errorf("command %s: %w", cmd, ErrTokenCapabilities)
+ }
+
+ switch cmd {
+ case BPF_MAP_CREATE:
+ attr := (*MapCreateAttr)(attr)
+ if !t.mapType(attr.MapType) {
+ return fmt.Errorf("map type %s: %w", attr.MapType, ErrTokenCapabilities)
+ }
+
+ case BPF_PROG_LOAD:
+ attr := (*ProgLoadAttr)(attr)
+ if !t.progType(attr.ProgType) {
+ return fmt.Errorf("program type %s: %w", attr.ProgType, ErrTokenCapabilities)
+ }
+
+ if !t.attachType(attr.ExpectedAttachType) {
+ return fmt.Errorf("attach type %s: %w", attr.ExpectedAttachType, ErrTokenCapabilities)
+ }
+ }
+
+ return nil
+}
+
+// cmd returns true if cmd is allowed by the Token.
+func (t *Token) cmd(cmd Cmd) bool {
+ if t.info == nil {
+ return true
+ }
+ return t.info.AllowedCmds&(uint64(1)<= len(_Cmd_index)-1 {
+ return "Cmd(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _Cmd_name[_Cmd_index[idx]:_Cmd_index[idx+1]]
+}
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[BPF_MAP_TYPE_UNSPEC-0]
+ _ = x[BPF_MAP_TYPE_HASH-1]
+ _ = x[BPF_MAP_TYPE_ARRAY-2]
+ _ = x[BPF_MAP_TYPE_PROG_ARRAY-3]
+ _ = x[BPF_MAP_TYPE_PERF_EVENT_ARRAY-4]
+ _ = x[BPF_MAP_TYPE_PERCPU_HASH-5]
+ _ = x[BPF_MAP_TYPE_PERCPU_ARRAY-6]
+ _ = x[BPF_MAP_TYPE_STACK_TRACE-7]
+ _ = x[BPF_MAP_TYPE_CGROUP_ARRAY-8]
+ _ = x[BPF_MAP_TYPE_LRU_HASH-9]
+ _ = x[BPF_MAP_TYPE_LRU_PERCPU_HASH-10]
+ _ = x[BPF_MAP_TYPE_LPM_TRIE-11]
+ _ = x[BPF_MAP_TYPE_ARRAY_OF_MAPS-12]
+ _ = x[BPF_MAP_TYPE_HASH_OF_MAPS-13]
+ _ = x[BPF_MAP_TYPE_DEVMAP-14]
+ _ = x[BPF_MAP_TYPE_SOCKMAP-15]
+ _ = x[BPF_MAP_TYPE_CPUMAP-16]
+ _ = x[BPF_MAP_TYPE_XSKMAP-17]
+ _ = x[BPF_MAP_TYPE_SOCKHASH-18]
+ _ = x[BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED-19]
+ _ = x[BPF_MAP_TYPE_CGROUP_STORAGE-19]
+ _ = x[BPF_MAP_TYPE_REUSEPORT_SOCKARRAY-20]
+ _ = x[BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED-21]
+ _ = x[BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE-21]
+ _ = x[BPF_MAP_TYPE_QUEUE-22]
+ _ = x[BPF_MAP_TYPE_STACK-23]
+ _ = x[BPF_MAP_TYPE_SK_STORAGE-24]
+ _ = x[BPF_MAP_TYPE_DEVMAP_HASH-25]
+ _ = x[BPF_MAP_TYPE_STRUCT_OPS-26]
+ _ = x[BPF_MAP_TYPE_RINGBUF-27]
+ _ = x[BPF_MAP_TYPE_INODE_STORAGE-28]
+ _ = x[BPF_MAP_TYPE_TASK_STORAGE-29]
+ _ = x[BPF_MAP_TYPE_BLOOM_FILTER-30]
+ _ = x[BPF_MAP_TYPE_USER_RINGBUF-31]
+ _ = x[BPF_MAP_TYPE_CGRP_STORAGE-32]
+ _ = x[BPF_MAP_TYPE_ARENA-33]
+ _ = x[BPF_MAP_TYPE_INSN_ARRAY-34]
+ _ = x[__MAX_BPF_MAP_TYPE-35]
+}
+
+const _MapType_name = "BPF_MAP_TYPE_UNSPECBPF_MAP_TYPE_HASHBPF_MAP_TYPE_ARRAYBPF_MAP_TYPE_PROG_ARRAYBPF_MAP_TYPE_PERF_EVENT_ARRAYBPF_MAP_TYPE_PERCPU_HASHBPF_MAP_TYPE_PERCPU_ARRAYBPF_MAP_TYPE_STACK_TRACEBPF_MAP_TYPE_CGROUP_ARRAYBPF_MAP_TYPE_LRU_HASHBPF_MAP_TYPE_LRU_PERCPU_HASHBPF_MAP_TYPE_LPM_TRIEBPF_MAP_TYPE_ARRAY_OF_MAPSBPF_MAP_TYPE_HASH_OF_MAPSBPF_MAP_TYPE_DEVMAPBPF_MAP_TYPE_SOCKMAPBPF_MAP_TYPE_CPUMAPBPF_MAP_TYPE_XSKMAPBPF_MAP_TYPE_SOCKHASHBPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATEDBPF_MAP_TYPE_REUSEPORT_SOCKARRAYBPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATEDBPF_MAP_TYPE_QUEUEBPF_MAP_TYPE_STACKBPF_MAP_TYPE_SK_STORAGEBPF_MAP_TYPE_DEVMAP_HASHBPF_MAP_TYPE_STRUCT_OPSBPF_MAP_TYPE_RINGBUFBPF_MAP_TYPE_INODE_STORAGEBPF_MAP_TYPE_TASK_STORAGEBPF_MAP_TYPE_BLOOM_FILTERBPF_MAP_TYPE_USER_RINGBUFBPF_MAP_TYPE_CGRP_STORAGEBPF_MAP_TYPE_ARENABPF_MAP_TYPE_INSN_ARRAY__MAX_BPF_MAP_TYPE"
+
+var _MapType_index = [...]uint16{0, 19, 36, 54, 77, 106, 130, 155, 179, 204, 225, 253, 274, 300, 325, 344, 364, 383, 402, 423, 461, 493, 538, 556, 574, 597, 621, 644, 664, 690, 715, 740, 765, 790, 808, 831, 849}
+
+func (i MapType) String() string {
+ idx := int(i) - 0
+ if i < 0 || idx >= len(_MapType_index)-1 {
+ return "MapType(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _MapType_name[_MapType_index[idx]:_MapType_index[idx+1]]
+}
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[BPF_PROG_TYPE_UNSPEC-0]
+ _ = x[BPF_PROG_TYPE_SOCKET_FILTER-1]
+ _ = x[BPF_PROG_TYPE_KPROBE-2]
+ _ = x[BPF_PROG_TYPE_SCHED_CLS-3]
+ _ = x[BPF_PROG_TYPE_SCHED_ACT-4]
+ _ = x[BPF_PROG_TYPE_TRACEPOINT-5]
+ _ = x[BPF_PROG_TYPE_XDP-6]
+ _ = x[BPF_PROG_TYPE_PERF_EVENT-7]
+ _ = x[BPF_PROG_TYPE_CGROUP_SKB-8]
+ _ = x[BPF_PROG_TYPE_CGROUP_SOCK-9]
+ _ = x[BPF_PROG_TYPE_LWT_IN-10]
+ _ = x[BPF_PROG_TYPE_LWT_OUT-11]
+ _ = x[BPF_PROG_TYPE_LWT_XMIT-12]
+ _ = x[BPF_PROG_TYPE_SOCK_OPS-13]
+ _ = x[BPF_PROG_TYPE_SK_SKB-14]
+ _ = x[BPF_PROG_TYPE_CGROUP_DEVICE-15]
+ _ = x[BPF_PROG_TYPE_SK_MSG-16]
+ _ = x[BPF_PROG_TYPE_RAW_TRACEPOINT-17]
+ _ = x[BPF_PROG_TYPE_CGROUP_SOCK_ADDR-18]
+ _ = x[BPF_PROG_TYPE_LWT_SEG6LOCAL-19]
+ _ = x[BPF_PROG_TYPE_LIRC_MODE2-20]
+ _ = x[BPF_PROG_TYPE_SK_REUSEPORT-21]
+ _ = x[BPF_PROG_TYPE_FLOW_DISSECTOR-22]
+ _ = x[BPF_PROG_TYPE_CGROUP_SYSCTL-23]
+ _ = x[BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE-24]
+ _ = x[BPF_PROG_TYPE_CGROUP_SOCKOPT-25]
+ _ = x[BPF_PROG_TYPE_TRACING-26]
+ _ = x[BPF_PROG_TYPE_STRUCT_OPS-27]
+ _ = x[BPF_PROG_TYPE_EXT-28]
+ _ = x[BPF_PROG_TYPE_LSM-29]
+ _ = x[BPF_PROG_TYPE_SK_LOOKUP-30]
+ _ = x[BPF_PROG_TYPE_SYSCALL-31]
+ _ = x[BPF_PROG_TYPE_NETFILTER-32]
+ _ = x[__MAX_BPF_PROG_TYPE-33]
+}
+
+const _ProgType_name = "BPF_PROG_TYPE_UNSPECBPF_PROG_TYPE_SOCKET_FILTERBPF_PROG_TYPE_KPROBEBPF_PROG_TYPE_SCHED_CLSBPF_PROG_TYPE_SCHED_ACTBPF_PROG_TYPE_TRACEPOINTBPF_PROG_TYPE_XDPBPF_PROG_TYPE_PERF_EVENTBPF_PROG_TYPE_CGROUP_SKBBPF_PROG_TYPE_CGROUP_SOCKBPF_PROG_TYPE_LWT_INBPF_PROG_TYPE_LWT_OUTBPF_PROG_TYPE_LWT_XMITBPF_PROG_TYPE_SOCK_OPSBPF_PROG_TYPE_SK_SKBBPF_PROG_TYPE_CGROUP_DEVICEBPF_PROG_TYPE_SK_MSGBPF_PROG_TYPE_RAW_TRACEPOINTBPF_PROG_TYPE_CGROUP_SOCK_ADDRBPF_PROG_TYPE_LWT_SEG6LOCALBPF_PROG_TYPE_LIRC_MODE2BPF_PROG_TYPE_SK_REUSEPORTBPF_PROG_TYPE_FLOW_DISSECTORBPF_PROG_TYPE_CGROUP_SYSCTLBPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLEBPF_PROG_TYPE_CGROUP_SOCKOPTBPF_PROG_TYPE_TRACINGBPF_PROG_TYPE_STRUCT_OPSBPF_PROG_TYPE_EXTBPF_PROG_TYPE_LSMBPF_PROG_TYPE_SK_LOOKUPBPF_PROG_TYPE_SYSCALLBPF_PROG_TYPE_NETFILTER__MAX_BPF_PROG_TYPE"
+
+var _ProgType_index = [...]uint16{0, 20, 47, 67, 90, 113, 137, 154, 178, 202, 227, 247, 268, 290, 312, 332, 359, 379, 407, 437, 464, 488, 514, 542, 569, 606, 634, 655, 679, 696, 713, 736, 757, 780, 799}
+
+func (i ProgType) String() string {
+ idx := int(i) - 0
+ if i < 0 || idx >= len(_ProgType_index)-1 {
+ return "ProgType(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _ProgType_name[_ProgType_index[idx]:_ProgType_index[idx+1]]
+}
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[BPF_CGROUP_INET_INGRESS-0]
+ _ = x[BPF_CGROUP_INET_EGRESS-1]
+ _ = x[BPF_CGROUP_INET_SOCK_CREATE-2]
+ _ = x[BPF_CGROUP_SOCK_OPS-3]
+ _ = x[BPF_SK_SKB_STREAM_PARSER-4]
+ _ = x[BPF_SK_SKB_STREAM_VERDICT-5]
+ _ = x[BPF_CGROUP_DEVICE-6]
+ _ = x[BPF_SK_MSG_VERDICT-7]
+ _ = x[BPF_CGROUP_INET4_BIND-8]
+ _ = x[BPF_CGROUP_INET6_BIND-9]
+ _ = x[BPF_CGROUP_INET4_CONNECT-10]
+ _ = x[BPF_CGROUP_INET6_CONNECT-11]
+ _ = x[BPF_CGROUP_INET4_POST_BIND-12]
+ _ = x[BPF_CGROUP_INET6_POST_BIND-13]
+ _ = x[BPF_CGROUP_UDP4_SENDMSG-14]
+ _ = x[BPF_CGROUP_UDP6_SENDMSG-15]
+ _ = x[BPF_LIRC_MODE2-16]
+ _ = x[BPF_FLOW_DISSECTOR-17]
+ _ = x[BPF_CGROUP_SYSCTL-18]
+ _ = x[BPF_CGROUP_UDP4_RECVMSG-19]
+ _ = x[BPF_CGROUP_UDP6_RECVMSG-20]
+ _ = x[BPF_CGROUP_GETSOCKOPT-21]
+ _ = x[BPF_CGROUP_SETSOCKOPT-22]
+ _ = x[BPF_TRACE_RAW_TP-23]
+ _ = x[BPF_TRACE_FENTRY-24]
+ _ = x[BPF_TRACE_FEXIT-25]
+ _ = x[BPF_MODIFY_RETURN-26]
+ _ = x[BPF_LSM_MAC-27]
+ _ = x[BPF_TRACE_ITER-28]
+ _ = x[BPF_CGROUP_INET4_GETPEERNAME-29]
+ _ = x[BPF_CGROUP_INET6_GETPEERNAME-30]
+ _ = x[BPF_CGROUP_INET4_GETSOCKNAME-31]
+ _ = x[BPF_CGROUP_INET6_GETSOCKNAME-32]
+ _ = x[BPF_XDP_DEVMAP-33]
+ _ = x[BPF_CGROUP_INET_SOCK_RELEASE-34]
+ _ = x[BPF_XDP_CPUMAP-35]
+ _ = x[BPF_SK_LOOKUP-36]
+ _ = x[BPF_XDP-37]
+ _ = x[BPF_SK_SKB_VERDICT-38]
+ _ = x[BPF_SK_REUSEPORT_SELECT-39]
+ _ = x[BPF_SK_REUSEPORT_SELECT_OR_MIGRATE-40]
+ _ = x[BPF_PERF_EVENT-41]
+ _ = x[BPF_TRACE_KPROBE_MULTI-42]
+ _ = x[BPF_LSM_CGROUP-43]
+ _ = x[BPF_STRUCT_OPS-44]
+ _ = x[BPF_NETFILTER-45]
+ _ = x[BPF_TCX_INGRESS-46]
+ _ = x[BPF_TCX_EGRESS-47]
+ _ = x[BPF_TRACE_UPROBE_MULTI-48]
+ _ = x[BPF_CGROUP_UNIX_CONNECT-49]
+ _ = x[BPF_CGROUP_UNIX_SENDMSG-50]
+ _ = x[BPF_CGROUP_UNIX_RECVMSG-51]
+ _ = x[BPF_CGROUP_UNIX_GETPEERNAME-52]
+ _ = x[BPF_CGROUP_UNIX_GETSOCKNAME-53]
+ _ = x[BPF_NETKIT_PRIMARY-54]
+ _ = x[BPF_NETKIT_PEER-55]
+ _ = x[BPF_TRACE_KPROBE_SESSION-56]
+ _ = x[BPF_TRACE_UPROBE_SESSION-57]
+ _ = x[BPF_TRACE_FSESSION-58]
+ _ = x[__MAX_BPF_ATTACH_TYPE-59]
+}
+
+const _AttachType_name = "BPF_CGROUP_INET_INGRESSBPF_CGROUP_INET_EGRESSBPF_CGROUP_INET_SOCK_CREATEBPF_CGROUP_SOCK_OPSBPF_SK_SKB_STREAM_PARSERBPF_SK_SKB_STREAM_VERDICTBPF_CGROUP_DEVICEBPF_SK_MSG_VERDICTBPF_CGROUP_INET4_BINDBPF_CGROUP_INET6_BINDBPF_CGROUP_INET4_CONNECTBPF_CGROUP_INET6_CONNECTBPF_CGROUP_INET4_POST_BINDBPF_CGROUP_INET6_POST_BINDBPF_CGROUP_UDP4_SENDMSGBPF_CGROUP_UDP6_SENDMSGBPF_LIRC_MODE2BPF_FLOW_DISSECTORBPF_CGROUP_SYSCTLBPF_CGROUP_UDP4_RECVMSGBPF_CGROUP_UDP6_RECVMSGBPF_CGROUP_GETSOCKOPTBPF_CGROUP_SETSOCKOPTBPF_TRACE_RAW_TPBPF_TRACE_FENTRYBPF_TRACE_FEXITBPF_MODIFY_RETURNBPF_LSM_MACBPF_TRACE_ITERBPF_CGROUP_INET4_GETPEERNAMEBPF_CGROUP_INET6_GETPEERNAMEBPF_CGROUP_INET4_GETSOCKNAMEBPF_CGROUP_INET6_GETSOCKNAMEBPF_XDP_DEVMAPBPF_CGROUP_INET_SOCK_RELEASEBPF_XDP_CPUMAPBPF_SK_LOOKUPBPF_XDPBPF_SK_SKB_VERDICTBPF_SK_REUSEPORT_SELECTBPF_SK_REUSEPORT_SELECT_OR_MIGRATEBPF_PERF_EVENTBPF_TRACE_KPROBE_MULTIBPF_LSM_CGROUPBPF_STRUCT_OPSBPF_NETFILTERBPF_TCX_INGRESSBPF_TCX_EGRESSBPF_TRACE_UPROBE_MULTIBPF_CGROUP_UNIX_CONNECTBPF_CGROUP_UNIX_SENDMSGBPF_CGROUP_UNIX_RECVMSGBPF_CGROUP_UNIX_GETPEERNAMEBPF_CGROUP_UNIX_GETSOCKNAMEBPF_NETKIT_PRIMARYBPF_NETKIT_PEERBPF_TRACE_KPROBE_SESSIONBPF_TRACE_UPROBE_SESSIONBPF_TRACE_FSESSION__MAX_BPF_ATTACH_TYPE"
+
+var _AttachType_index = [...]uint16{0, 23, 45, 72, 91, 115, 140, 157, 175, 196, 217, 241, 265, 291, 317, 340, 363, 377, 395, 412, 435, 458, 479, 500, 516, 532, 547, 564, 575, 589, 617, 645, 673, 701, 715, 743, 757, 770, 777, 795, 818, 852, 866, 888, 902, 916, 929, 944, 958, 980, 1003, 1026, 1049, 1076, 1103, 1121, 1136, 1160, 1184, 1202, 1223}
+
+func (i AttachType) String() string {
+ idx := int(i) - 0
+ if i < 0 || idx >= len(_AttachType_index)-1 {
+ return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _AttachType_name[_AttachType_index[idx]:_AttachType_index[idx+1]]
+}
diff --git a/vendor/github.com/cilium/ebpf/internal/sysenc/marshal.go b/vendor/github.com/cilium/ebpf/internal/sysenc/marshal.go
index 3f7deb80f..fdd81ac12 100644
--- a/vendor/github.com/cilium/ebpf/internal/sysenc/marshal.go
+++ b/vendor/github.com/cilium/ebpf/internal/sysenc/marshal.go
@@ -68,7 +68,7 @@ func Marshal(data any, size int) (Buffer, error) {
//
// Returns an error if buf can't be unmarshalled according to the behaviour
// of [binary.Decode].
-func Unmarshal(data interface{}, buf []byte) error {
+func Unmarshal(data any, buf []byte) error {
switch value := data.(type) {
case encoding.BinaryUnmarshaler:
return value.UnmarshalBinary(buf)
diff --git a/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go b/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go
index 7f7b3cba5..c42725b4d 100644
--- a/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go
+++ b/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go
@@ -13,6 +13,7 @@ import (
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/linux"
+ "github.com/cilium/ebpf/internal/mountinfo"
"github.com/cilium/ebpf/internal/platform"
"github.com/cilium/ebpf/internal/unix"
)
@@ -23,7 +24,7 @@ var (
ErrInvalidMaxActive = errors.New("can only set maxactive on kretprobes")
)
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -type=ProbeType -linecomment
+//go:generate go tool stringer -type=ProbeType -linecomment
type ProbeType uint8
@@ -72,11 +73,11 @@ func RandomGroup(prefix string) (string, error) {
}
// validIdentifier implements the equivalent of a regex match
-// against "^[a-zA-Z_][0-9a-zA-Z_]*$".
+// against "^[a-zA-Z_][0-9a-zA-Z_-]*$".
//
-// Trace event groups, names and kernel symbols must adhere to this set
-// of characters. Non-empty, first character must not be a number, all
-// characters must be alphanumeric or underscore.
+// Trace event groups, names and kernel symbols must adhere to this set of
+// characters. Non-empty, first character must not be a number or hyphen, all
+// characters must be alphanumeric, underscore or hyphen.
func validIdentifier(s string) bool {
if len(s) < 1 {
return false
@@ -86,7 +87,7 @@ func validIdentifier(s string) bool {
case c >= 'a' && c <= 'z':
case c >= 'A' && c <= 'Z':
case c == '_':
- case i > 0 && c >= '0' && c <= '9':
+ case i > 0 && (c == '-' || c >= '0' && c <= '9'):
default:
return false
@@ -109,15 +110,31 @@ func sanitizeTracefsPath(path ...string) (string, error) {
return p, nil
}
-// getTracefsPath will return a correct path to the tracefs mount point.
-// Since kernel 4.1 tracefs should be mounted by default at /sys/kernel/tracing,
-// but may be also be available at /sys/kernel/debug/tracing if debugfs is mounted.
-// The available tracefs paths will depends on distribution choices.
+// getTracefsPath returns a correct path to the tracefs mount point.
+//
+// The discovery order is:
+//
+// 1. Any tracefs mount listed in /proc/self/mountinfo (kernel 4.1+).
+// This works regardless of where the mount sits in the filesystem,
+// so containers that bind-mount tracefs at a non-canonical path are
+// supported automatically.
+// 2. A debugfs mount with a tracing/ subdirectory, for older systems
+// where tracefs has not been lifted out of debugfs.
+// 3. As a final fallback, probe the canonical kernel paths
+// (/sys/kernel/tracing, /sys/kernel/debug/tracing) directly with
+// statfs. This catches edge cases where /proc/self/mountinfo is
+// unavailable or doesn't report the mount.
var getTracefsPath = sync.OnceValues(func() (string, error) {
if !platform.IsLinux {
return "", fmt.Errorf("tracefs: %w", internal.ErrNotSupportedOnOS)
}
+ if entries, err := mountinfo.Read(); err == nil {
+ if path := findTracefsInEntries(entries); path != "" {
+ return path, nil
+ }
+ }
+
for _, p := range []struct {
path string
fsType int64
@@ -135,6 +152,28 @@ var getTracefsPath = sync.OnceValues(func() (string, error) {
return "", errors.New("neither debugfs nor tracefs are mounted")
})
+// findTracefsInEntries returns the first occurrence of a tracefs in the
+// given entries.
+//
+// Returns an empty string when no usable mount is found.
+func findTracefsInEntries(entries []mountinfo.Entry) string {
+ for _, e := range entries {
+ if e.FSType == "tracefs" && e.Root == "/" {
+ return e.MountPoint
+ }
+ }
+ for _, e := range entries {
+ if e.FSType != "debugfs" || e.Root != "/" {
+ continue
+ }
+ tracing := filepath.Join(e.MountPoint, "tracing")
+ if info, err := os.Stat(tracing); err == nil && info.IsDir() {
+ return tracing
+ }
+ }
+ return ""
+}
+
// sanitizeIdentifier replaces every invalid character for the tracefs api with an underscore.
//
// It is equivalent to calling regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString("_").
@@ -200,6 +239,8 @@ type Event struct {
group, name string
// event id allocated by the kernel. 0 if the event has already been removed.
id uint64
+
+ cleanup runtime.Cleanup
}
// NewEvent creates a new ephemeral trace event.
@@ -316,8 +357,11 @@ func NewEvent(args ProbeArgs) (*Event, error) {
return nil, fmt.Errorf("get trace event id: %w", err)
}
- evt := &Event{args.Type, args.Group, eventName, tid}
- runtime.SetFinalizer(evt, (*Event).Close)
+ evt := &Event{typ: args.Type, group: args.Group, name: eventName, id: tid}
+ evt.cleanup = runtime.AddCleanup(evt, func(*byte) {
+ _ = removeEvent(args.Type, fmt.Sprintf("%s/%s", args.Group, eventName))
+ }, nil)
+
return evt, nil
}
@@ -330,7 +374,7 @@ func (evt *Event) Close() error {
}
evt.id = 0
- runtime.SetFinalizer(evt, nil)
+ evt.cleanup.Stop()
pe := fmt.Sprintf("%s/%s", evt.group, evt.name)
return removeEvent(evt.typ, pe)
}
diff --git a/vendor/github.com/cilium/ebpf/internal/tracefs/probetype_string.go b/vendor/github.com/cilium/ebpf/internal/tracefs/probetype_string.go
index 87cb0a059..ed8471a89 100644
--- a/vendor/github.com/cilium/ebpf/internal/tracefs/probetype_string.go
+++ b/vendor/github.com/cilium/ebpf/internal/tracefs/probetype_string.go
@@ -17,8 +17,9 @@ const _ProbeType_name = "kprobeuprobe"
var _ProbeType_index = [...]uint8{0, 6, 12}
func (i ProbeType) String() string {
- if i >= ProbeType(len(_ProbeType_index)-1) {
+ idx := int(i) - 0
+ if i < 0 || idx >= len(_ProbeType_index)-1 {
return "ProbeType(" + strconv.FormatInt(int64(i), 10) + ")"
}
- return _ProbeType_name[_ProbeType_index[i]:_ProbeType_index[i+1]]
+ return _ProbeType_name[_ProbeType_index[idx]:_ProbeType_index[idx+1]]
}
diff --git a/vendor/github.com/cilium/ebpf/internal/unix/errno_windows.go b/vendor/github.com/cilium/ebpf/internal/unix/errno_windows.go
index 7500cd6d4..266e43daa 100644
--- a/vendor/github.com/cilium/ebpf/internal/unix/errno_windows.go
+++ b/vendor/github.com/cilium/ebpf/internal/unix/errno_windows.go
@@ -9,7 +9,7 @@ import (
"syscall"
)
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -type=Errno -tags=windows -output=errno_string_windows.go
+//go:generate go tool stringer -type=Errno -tags=windows -output=errno_string_windows.go
// Windows specific constants for Unix errnos.
//
@@ -37,6 +37,7 @@ const (
EILSEQ Errno = 42
ENOTSUP Errno = 129
EOPNOTSUPP Errno = 130
+ EOTHER Errno = 131
ETIMEDOUT Errno = 138
EWOULDBLOCK Errno = 140
)
diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go
index 14a0a1929..7cb13372b 100644
--- a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go
+++ b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go
@@ -10,69 +10,83 @@ import (
)
const (
- BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC
- BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE
- BPF_F_RDONLY = linux.BPF_F_RDONLY
- BPF_F_WRONLY = linux.BPF_F_WRONLY
- BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG
- BPF_F_WRONLY_PROG = linux.BPF_F_WRONLY_PROG
- BPF_F_SLEEPABLE = linux.BPF_F_SLEEPABLE
- BPF_F_XDP_HAS_FRAGS = linux.BPF_F_XDP_HAS_FRAGS
- BPF_F_MMAPABLE = linux.BPF_F_MMAPABLE
- BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP
- BPF_F_KPROBE_MULTI_RETURN = linux.BPF_F_KPROBE_MULTI_RETURN
- BPF_F_UPROBE_MULTI_RETURN = linux.BPF_F_UPROBE_MULTI_RETURN
- BPF_F_LOCK = linux.BPF_F_LOCK
- BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN
- BPF_TAG_SIZE = linux.BPF_TAG_SIZE
- BPF_RINGBUF_BUSY_BIT = linux.BPF_RINGBUF_BUSY_BIT
- BPF_RINGBUF_DISCARD_BIT = linux.BPF_RINGBUF_DISCARD_BIT
- BPF_RINGBUF_HDR_SZ = linux.BPF_RINGBUF_HDR_SZ
- SYS_BPF = linux.SYS_BPF
- F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC
- EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD
- EPOLL_CLOEXEC = linux.EPOLL_CLOEXEC
- O_CLOEXEC = linux.O_CLOEXEC
- O_NONBLOCK = linux.O_NONBLOCK
- PROT_NONE = linux.PROT_NONE
- PROT_READ = linux.PROT_READ
- PROT_WRITE = linux.PROT_WRITE
- MAP_ANON = linux.MAP_ANON
- MAP_SHARED = linux.MAP_SHARED
- MAP_FIXED = linux.MAP_FIXED
- MAP_PRIVATE = linux.MAP_PRIVATE
- PERF_ATTR_SIZE_VER1 = linux.PERF_ATTR_SIZE_VER1
- PERF_TYPE_SOFTWARE = linux.PERF_TYPE_SOFTWARE
- PERF_TYPE_TRACEPOINT = linux.PERF_TYPE_TRACEPOINT
- PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT
- PERF_EVENT_IOC_DISABLE = linux.PERF_EVENT_IOC_DISABLE
- PERF_EVENT_IOC_ENABLE = linux.PERF_EVENT_IOC_ENABLE
- PERF_EVENT_IOC_SET_BPF = linux.PERF_EVENT_IOC_SET_BPF
- PerfBitWatermark = linux.PerfBitWatermark
- PerfBitWriteBackward = linux.PerfBitWriteBackward
- PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW
- PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC
- RLIM_INFINITY = linux.RLIM_INFINITY
- RLIMIT_MEMLOCK = linux.RLIMIT_MEMLOCK
- BPF_STATS_RUN_TIME = linux.BPF_STATS_RUN_TIME
- PERF_RECORD_LOST = linux.PERF_RECORD_LOST
- PERF_RECORD_SAMPLE = linux.PERF_RECORD_SAMPLE
- AT_FDCWD = linux.AT_FDCWD
- RENAME_NOREPLACE = linux.RENAME_NOREPLACE
- SO_ATTACH_BPF = linux.SO_ATTACH_BPF
- SO_DETACH_BPF = linux.SO_DETACH_BPF
- SOL_SOCKET = linux.SOL_SOCKET
- SIGPROF = linux.SIGPROF
- SIGUSR1 = linux.SIGUSR1
- SIG_BLOCK = linux.SIG_BLOCK
- SIG_UNBLOCK = linux.SIG_UNBLOCK
- BPF_FS_MAGIC = linux.BPF_FS_MAGIC
- TRACEFS_MAGIC = linux.TRACEFS_MAGIC
- DEBUGFS_MAGIC = linux.DEBUGFS_MAGIC
- BPF_RB_NO_WAKEUP = linux.BPF_RB_NO_WAKEUP
- BPF_RB_FORCE_WAKEUP = linux.BPF_RB_FORCE_WAKEUP
- AF_UNSPEC = linux.AF_UNSPEC
- IFF_UP = linux.IFF_UP
+ BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC
+ BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE
+ BPF_F_RDONLY = linux.BPF_F_RDONLY
+ BPF_F_WRONLY = linux.BPF_F_WRONLY
+ BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG
+ BPF_F_WRONLY_PROG = linux.BPF_F_WRONLY_PROG
+ BPF_F_SLEEPABLE = linux.BPF_F_SLEEPABLE
+ BPF_F_XDP_HAS_FRAGS = linux.BPF_F_XDP_HAS_FRAGS
+ BPF_F_MMAPABLE = linux.BPF_F_MMAPABLE
+ BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP
+ BPF_F_KPROBE_MULTI_RETURN = linux.BPF_F_KPROBE_MULTI_RETURN
+ BPF_F_UPROBE_MULTI_RETURN = linux.BPF_F_UPROBE_MULTI_RETURN
+ BPF_F_LOCK = linux.BPF_F_LOCK
+ BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN
+ BPF_TAG_SIZE = linux.BPF_TAG_SIZE
+ BPF_RINGBUF_BUSY_BIT = linux.BPF_RINGBUF_BUSY_BIT
+ BPF_RINGBUF_DISCARD_BIT = linux.BPF_RINGBUF_DISCARD_BIT
+ BPF_RINGBUF_HDR_SZ = linux.BPF_RINGBUF_HDR_SZ
+ SYS_BPF = linux.SYS_BPF
+ F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC
+ EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD
+ EPOLL_CLOEXEC = linux.EPOLL_CLOEXEC
+ O_RDONLY = linux.O_RDONLY
+ O_DIRECTORY = linux.O_DIRECTORY
+ O_CLOEXEC = linux.O_CLOEXEC
+ O_NONBLOCK = linux.O_NONBLOCK
+ PROT_NONE = linux.PROT_NONE
+ PROT_READ = linux.PROT_READ
+ PROT_WRITE = linux.PROT_WRITE
+ MAP_ANON = linux.MAP_ANON
+ MAP_SHARED = linux.MAP_SHARED
+ MAP_FIXED = linux.MAP_FIXED
+ MAP_PRIVATE = linux.MAP_PRIVATE
+ PERF_ATTR_SIZE_VER1 = linux.PERF_ATTR_SIZE_VER1
+ PERF_TYPE_SOFTWARE = linux.PERF_TYPE_SOFTWARE
+ PERF_TYPE_TRACEPOINT = linux.PERF_TYPE_TRACEPOINT
+ PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT
+ PERF_EVENT_IOC_DISABLE = linux.PERF_EVENT_IOC_DISABLE
+ PERF_EVENT_IOC_ENABLE = linux.PERF_EVENT_IOC_ENABLE
+ PERF_EVENT_IOC_SET_BPF = linux.PERF_EVENT_IOC_SET_BPF
+ PerfBitWatermark = linux.PerfBitWatermark
+ PerfBitWriteBackward = linux.PerfBitWriteBackward
+ PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW
+ PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC
+ RLIM_INFINITY = linux.RLIM_INFINITY
+ RLIMIT_MEMLOCK = linux.RLIMIT_MEMLOCK
+ BPF_STATS_RUN_TIME = linux.BPF_STATS_RUN_TIME
+ PERF_RECORD_LOST = linux.PERF_RECORD_LOST
+ PERF_RECORD_SAMPLE = linux.PERF_RECORD_SAMPLE
+ AT_FDCWD = linux.AT_FDCWD
+ RENAME_NOREPLACE = linux.RENAME_NOREPLACE
+ SO_ATTACH_BPF = linux.SO_ATTACH_BPF
+ SO_DETACH_BPF = linux.SO_DETACH_BPF
+ SOL_SOCKET = linux.SOL_SOCKET
+ SIGPROF = linux.SIGPROF
+ SIGUSR1 = linux.SIGUSR1
+ SIG_BLOCK = linux.SIG_BLOCK
+ SIG_UNBLOCK = linux.SIG_UNBLOCK
+ BPF_FS_MAGIC = linux.BPF_FS_MAGIC
+ TRACEFS_MAGIC = linux.TRACEFS_MAGIC
+ DEBUGFS_MAGIC = linux.DEBUGFS_MAGIC
+ BPF_RB_NO_WAKEUP = linux.BPF_RB_NO_WAKEUP
+ BPF_RB_FORCE_WAKEUP = linux.BPF_RB_FORCE_WAKEUP
+ AF_UNSPEC = linux.AF_UNSPEC
+ IFF_UP = linux.IFF_UP
+ LINUX_CAPABILITY_VERSION_3 = linux.LINUX_CAPABILITY_VERSION_3
+ CLONE_NEWNET = linux.CLONE_NEWNET
+ CLONE_NEWUSER = linux.CLONE_NEWUSER
+ CLONE_NEWNS = linux.CLONE_NEWNS
+ MOVE_MOUNT_F_EMPTY_PATH = linux.MOVE_MOUNT_F_EMPTY_PATH
+ AF_UNIX = linux.AF_UNIX
+ SOCK_STREAM = linux.SOCK_STREAM
+ SOCK_CLOEXEC = linux.SOCK_CLOEXEC
+ FSOPEN_CLOEXEC = linux.FSOPEN_CLOEXEC
+ FSMOUNT_CLOEXEC = linux.FSMOUNT_CLOEXEC
+ MSG_CMSG_CLOEXEC = linux.MSG_CMSG_CLOEXEC
+ SizeofInt = linux.SizeofInt
)
type Statfs_t = linux.Statfs_t
@@ -85,6 +99,9 @@ type EpollEvent = linux.EpollEvent
type PerfEventAttr = linux.PerfEventAttr
type Utsname = linux.Utsname
type CPUSet = linux.CPUSet
+type CapUserData = linux.CapUserData
+type CapUserHeader = linux.CapUserHeader
+type SysProcAttr = linux.SysProcAttr
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return linux.Syscall(trap, a1, a2, a3)
@@ -210,3 +227,67 @@ func SchedGetaffinity(pid int, set *CPUSet) error {
func Auxv() ([][2]uintptr, error) {
return linux.Auxv()
}
+
+func Unshare(flag int) error {
+ return linux.Unshare(flag)
+}
+
+func Setns(fd int, nstype int) error {
+ return linux.Setns(fd, nstype)
+}
+
+func Capget(hdr *CapUserHeader, data *CapUserData) (err error) {
+ return linux.Capget(hdr, data)
+}
+
+func Capset(hdr *CapUserHeader, data *CapUserData) (err error) {
+ return linux.Capset(hdr, data)
+}
+
+func Sendmsg(fd int, p []byte, oob []byte, to linux.Sockaddr, flags int) (err error) {
+ return linux.Sendmsg(fd, p, oob, to, flags)
+}
+
+func Fsopen(fsname string, flags int) (fd int, err error) {
+ return linux.Fsopen(fsname, flags)
+}
+
+func FsconfigSetString(fd int, key string, value string) error {
+ return linux.FsconfigSetString(fd, key, value)
+}
+
+func FsconfigCreate(fd int) (err error) {
+ return linux.FsconfigCreate(fd)
+}
+
+func Fsmount(fd int, flags int, mountAttrs int) (fsfd int, err error) {
+ return linux.Fsmount(fd, flags, mountAttrs)
+}
+
+func MoveMount(fromDirfd int, fromPathName string, toDirfd int, toPathName string, flags int) (err error) {
+ return linux.MoveMount(fromDirfd, fromPathName, toDirfd, toPathName, flags)
+}
+
+func UnixRights(fds ...int) []byte {
+ return linux.UnixRights(fds...)
+}
+
+func Recvmsg(fd int, p []byte, oob []byte, flags int) (n int, oobn int, recvflags int, from linux.Sockaddr, err error) {
+ return linux.Recvmsg(fd, p, oob, flags)
+}
+
+func Socketpair(domain int, typ int, proto int) (fd [2]int, err error) {
+ return linux.Socketpair(domain, typ, proto)
+}
+
+func CmsgSpace(datalen int) int {
+ return linux.CmsgSpace(datalen)
+}
+
+func ParseSocketControlMessage(b []byte) ([]linux.SocketControlMessage, error) {
+ return linux.ParseSocketControlMessage(b)
+}
+
+func ParseUnixRights(m *linux.SocketControlMessage) ([]int, error) {
+ return linux.ParseUnixRights(m)
+}
diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go
index f3f764ebe..5d75582e2 100644
--- a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go
+++ b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go
@@ -33,6 +33,8 @@ const (
EPOLL_CLOEXEC
O_CLOEXEC
O_NONBLOCK
+ O_DIRECTORY
+ O_RDONLY
PROT_NONE
PROT_READ
PROT_WRITE
@@ -73,6 +75,12 @@ const (
BPF_F_LOCK
AF_UNSPEC
IFF_UP
+ LINUX_CAPABILITY_VERSION_3
+ SOCK_CLOEXEC
+ FSOPEN_CLOEXEC
+ FSMOUNT_CLOEXEC
+ MSG_CMSG_CLOEXEC
+ SizeofInt
)
type Statfs_t struct {
@@ -115,6 +123,17 @@ type Sigset_t struct {
Val [4]uint64
}
+type CapUserHeader struct {
+ Version uint32
+ Pid int32
+}
+
+type CapUserData struct {
+ Effective uint32
+ Permitted uint32
+ Inheritable uint32
+}
+
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
return 0, 0, ENOTSUP
}
@@ -288,3 +307,11 @@ func SchedGetaffinity(pid int, set *CPUSet) error {
func Auxv() ([][2]uintptr, error) {
return nil, errNonLinux()
}
+
+func Capget(hdr *CapUserHeader, data *CapUserData) (err error) {
+ return errNonLinux()
+}
+
+func Capset(hdr *CapUserHeader, data *CapUserData) (err error) {
+ return errNonLinux()
+}
diff --git a/vendor/github.com/cilium/ebpf/link/cgroup.go b/vendor/github.com/cilium/ebpf/link/cgroup.go
index 1f1416aa2..64aea1ae3 100644
--- a/vendor/github.com/cilium/ebpf/link/cgroup.go
+++ b/vendor/github.com/cilium/ebpf/link/cgroup.go
@@ -167,6 +167,10 @@ func (cg *progAttachCgroup) Unpin() error {
return fmt.Errorf("can't unpin cgroup: %w", ErrNotSupported)
}
+func (cg *progAttachCgroup) Detach() error {
+ return fmt.Errorf("can't detach cgroup: %w", ErrNotSupported)
+}
+
func (cg *progAttachCgroup) Info() (*Info, error) {
return nil, fmt.Errorf("can't get cgroup info: %w", ErrNotSupported)
}
diff --git a/vendor/github.com/cilium/ebpf/link/kprobe.go b/vendor/github.com/cilium/ebpf/link/kprobe.go
index 0912e0a08..8b27677f9 100644
--- a/vendor/github.com/cilium/ebpf/link/kprobe.go
+++ b/vendor/github.com/cilium/ebpf/link/kprobe.go
@@ -33,8 +33,9 @@ type KprobeOptions struct {
// Increase the maximum number of concurrent invocations of a kretprobe.
// Required when tracing some long running functions in the kernel.
//
- // Deprecated: this setting forces the use of an outdated kernel API and is not portable
- // across kernel versions.
+ // Warning: this setting forces the use of an outdated kernel API and is
+ // not portable across kernel versions. On supported kernels, consider using
+ // fexit programs instead, as they don't have this MaxActive limitation.
RetprobeMaxActive int
// Prefix used for the event name if the kprobe must be attached using tracefs.
// The group name will be formatted as `_`.
diff --git a/vendor/github.com/cilium/ebpf/link/kprobe_multi.go b/vendor/github.com/cilium/ebpf/link/kprobe_multi.go
index 3a2b06a24..b72ea9587 100644
--- a/vendor/github.com/cilium/ebpf/link/kprobe_multi.go
+++ b/vendor/github.com/cilium/ebpf/link/kprobe_multi.go
@@ -8,8 +8,7 @@ import (
"os"
"github.com/cilium/ebpf"
- "github.com/cilium/ebpf/asm"
- "github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/features"
"github.com/cilium/ebpf/internal/sys"
"github.com/cilium/ebpf/internal/unix"
)
@@ -126,11 +125,11 @@ func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Lin
}
if opts.Session {
- if haveFeatErr := haveBPFLinkKprobeSession(); haveFeatErr != nil {
+ if haveFeatErr := features.HaveBPFLinkKprobeSession(); haveFeatErr != nil {
return nil, haveFeatErr
}
} else {
- if haveFeatErr := haveBPFLinkKprobeMulti(); haveFeatErr != nil {
+ if haveFeatErr := features.HaveBPFLinkKprobeMulti(); haveFeatErr != nil {
return nil, haveFeatErr
}
}
@@ -157,12 +156,30 @@ func (kml *kprobeMultiLink) Update(_ *ebpf.Program) error {
func (kml *kprobeMultiLink) Info() (*Info, error) {
var info sys.KprobeMultiLinkInfo
if err := sys.ObjInfo(kml.fd, &info); err != nil {
- return nil, fmt.Errorf("kprobe multi link info: %s", err)
+ return nil, fmt.Errorf("kprobe multi link info: %w", err)
+ }
+ var addrs = make([]uint64, info.Count)
+ var cookies = make([]uint64, info.Count)
+ info = sys.KprobeMultiLinkInfo{
+ Addrs: sys.SlicePointer(addrs),
+ Cookies: sys.SlicePointer(cookies),
+ Count: uint32(len(addrs)),
+ }
+ if err := sys.ObjInfo(kml.fd, &info); err != nil {
+ return nil, fmt.Errorf("kprobe multi link info: %w", err)
+ }
+ if info.Addrs.IsNil() {
+ addrs = nil
+ }
+ if info.Cookies.IsNil() {
+ cookies = nil
}
extra := &KprobeMultiInfo{
- count: info.Count,
- flags: info.Flags,
- missed: info.Missed,
+ Count: info.Count,
+ Flags: info.Flags,
+ Missed: info.Missed,
+ addrs: addrs,
+ cookies: cookies,
}
return &Info{
@@ -172,85 +189,3 @@ func (kml *kprobeMultiLink) Info() (*Info, error) {
extra,
}, nil
}
-
-var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", func() error {
- prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
- Name: "probe_kpm_link",
- Type: ebpf.Kprobe,
- Instructions: asm.Instructions{
- asm.Mov.Imm(asm.R0, 0),
- asm.Return(),
- },
- AttachType: ebpf.AttachTraceKprobeMulti,
- License: "MIT",
- })
- if errors.Is(err, unix.E2BIG) {
- // Kernel doesn't support AttachType field.
- return internal.ErrNotSupported
- }
- if err != nil {
- return err
- }
- defer prog.Close()
-
- fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{
- ProgFd: uint32(prog.FD()),
- AttachType: sys.BPF_TRACE_KPROBE_MULTI,
- Count: 1,
- Syms: sys.NewStringSlicePointer([]string{"vprintk"}),
- })
- switch {
- case errors.Is(err, unix.EINVAL):
- return internal.ErrNotSupported
- // If CONFIG_FPROBE isn't set.
- case errors.Is(err, unix.EOPNOTSUPP):
- return internal.ErrNotSupported
- case err != nil:
- return err
- }
-
- fd.Close()
-
- return nil
-}, "5.18")
-
-var haveBPFLinkKprobeSession = internal.NewFeatureTest("bpf_link_kprobe_session", func() error {
- prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
- Name: "probe_kps_link",
- Type: ebpf.Kprobe,
- Instructions: asm.Instructions{
- asm.Mov.Imm(asm.R0, 0),
- asm.Return(),
- },
- AttachType: ebpf.AttachTraceKprobeSession,
- License: "MIT",
- })
- if errors.Is(err, unix.E2BIG) {
- // Kernel doesn't support AttachType field.
- return internal.ErrNotSupported
- }
- if err != nil {
- return err
- }
- defer prog.Close()
-
- fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{
- ProgFd: uint32(prog.FD()),
- AttachType: sys.BPF_TRACE_KPROBE_SESSION,
- Count: 1,
- Syms: sys.NewStringSlicePointer([]string{"vprintk"}),
- })
- switch {
- case errors.Is(err, unix.EINVAL):
- return internal.ErrNotSupported
- // If CONFIG_FPROBE isn't set.
- case errors.Is(err, unix.EOPNOTSUPP):
- return internal.ErrNotSupported
- case err != nil:
- return err
- }
-
- fd.Close()
-
- return nil
-}, "6.10")
diff --git a/vendor/github.com/cilium/ebpf/link/link.go b/vendor/github.com/cilium/ebpf/link/link.go
index 425811e77..57b328c2e 100644
--- a/vendor/github.com/cilium/ebpf/link/link.go
+++ b/vendor/github.com/cilium/ebpf/link/link.go
@@ -9,6 +9,7 @@ import (
"github.com/cilium/ebpf/btf"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/sys"
+ "github.com/cilium/ebpf/internal/unix"
)
// Type is the kind of link.
@@ -40,6 +41,11 @@ type Link interface {
// not called.
Close() error
+ // Detach the link from its corresponding attachment point.
+ //
+ // May return an error wrapping ErrNotSupported.
+ Detach() error
+
// Info returns metadata on a link.
//
// May return an error wrapping ErrNotSupported.
@@ -49,13 +55,6 @@ type Link interface {
isLink()
}
-// NewLinkFromFD creates a link from a raw fd.
-//
-// Deprecated: use [NewFromFD] instead.
-func NewLinkFromFD(fd int) (Link, error) {
- return NewFromFD(fd)
-}
-
// NewFromFD creates a link from a raw fd.
//
// You should not use fd after calling this function.
@@ -115,7 +114,7 @@ type Info struct {
Type Type
ID ID
Program ebpf.ProgramID
- extra interface{}
+ extra any
}
// RawLink is the low-level API to bpf_link.
@@ -225,6 +224,24 @@ func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error {
return nil
}
+// Detach the link from its corresponding attachment point.
+func (l *RawLink) Detach() error {
+ attr := sys.LinkDetachAttr{
+ LinkFd: l.fd.Uint(),
+ }
+
+ err := sys.LinkDetach(&attr)
+
+ switch {
+ case errors.Is(err, unix.EOPNOTSUPP):
+ return internal.ErrNotSupported
+ case err != nil:
+ return fmt.Errorf("detach link: %w", err)
+ default:
+ return nil
+ }
+}
+
// Info returns metadata about the link.
//
// Linktype specific metadata is not included and can be retrieved
diff --git a/vendor/github.com/cilium/ebpf/link/link_other.go b/vendor/github.com/cilium/ebpf/link/link_other.go
index cd9452fd8..31904aaf5 100644
--- a/vendor/github.com/cilium/ebpf/link/link_other.go
+++ b/vendor/github.com/cilium/ebpf/link/link_other.go
@@ -25,6 +25,7 @@ const (
UprobeMultiType = sys.BPF_LINK_TYPE_UPROBE_MULTI
NetfilterType = sys.BPF_LINK_TYPE_NETFILTER
NetkitType = sys.BPF_LINK_TYPE_NETKIT
+ StructOpsType = sys.BPF_LINK_TYPE_STRUCT_OPS
)
// AttachRawLink creates a raw link.
@@ -102,15 +103,17 @@ func wrapRawLink(raw *RawLink) (_ Link, err error) {
return &netkitLink{*raw}, nil
case XDPType:
return &xdpLink{*raw}, nil
+ case StructOpsType:
+ return &structOpsLink{*raw}, nil
default:
return raw, nil
}
}
type TracingInfo struct {
- AttachType sys.AttachType
- TargetObjId uint32
- TargetBtfId sys.TypeID
+ AttachType sys.AttachType
+ TargetObjectId uint32
+ TargetBtfId sys.TypeID
}
type CgroupInfo struct {
@@ -120,7 +123,7 @@ type CgroupInfo struct {
}
type NetNsInfo struct {
- NetnsIno uint32
+ NetnsInode uint32
AttachType sys.AttachType
}
@@ -134,10 +137,10 @@ type XDPInfo struct {
}
type NetfilterInfo struct {
- Pf uint32
- Hooknum uint32
- Priority int32
- Flags uint32
+ ProtocolFamily NetfilterProtocolFamily
+ Hook NetfilterInetHook
+ Priority int32
+ Flags uint32
}
type NetkitInfo struct {
@@ -145,28 +148,98 @@ type NetkitInfo struct {
AttachType sys.AttachType
}
+type RawTracepointInfo struct {
+ Name string
+}
+
type KprobeMultiInfo struct {
- count uint32
- flags uint32
- missed uint64
+ // Count is the number of addresses hooked by the kprobe.
+ Count uint32
+ Flags uint32
+ Missed uint64
+ addrs []uint64
+ cookies []uint64
+}
+
+type KprobeMultiAddress struct {
+ Address uint64
+ Cookie uint64
+}
+
+// Addresses are the addresses hooked by the kprobe.
+func (kpm *KprobeMultiInfo) Addresses() ([]KprobeMultiAddress, bool) {
+ if kpm.addrs == nil || len(kpm.addrs) != len(kpm.cookies) {
+ return nil, false
+ }
+ addrs := make([]KprobeMultiAddress, len(kpm.addrs))
+ for i := range kpm.addrs {
+ addrs[i] = KprobeMultiAddress{
+ Address: kpm.addrs[i],
+ Cookie: kpm.cookies[i],
+ }
+ }
+ return addrs, true
+}
+
+type UprobeMultiInfo struct {
+ Count uint32
+ Flags uint32
+ Missed uint64
+ offsets []uint64
+ cookies []uint64
+ refCtrOffsets []uint64
+ // File is the path that the file the uprobe was attached to
+ // had at creation time.
+ //
+ // However, due to various circumstances (differing mount namespaces,
+ // file replacement, ...), this path may not point to the same binary
+ // the uprobe was originally attached to.
+ File string
+ pid uint32
}
-// AddressCount is the number of addresses hooked by the kprobe.
-func (kpm *KprobeMultiInfo) AddressCount() (uint32, bool) {
- return kpm.count, kpm.count > 0
+type UprobeMultiOffset struct {
+ Offset uint64
+ Cookie uint64
+ ReferenceCount uint64
}
-func (kpm *KprobeMultiInfo) Flags() (uint32, bool) {
- return kpm.flags, kpm.count > 0
+// Offsets returns the offsets that the uprobe was attached to along with the related cookies and ref counters.
+func (umi *UprobeMultiInfo) Offsets() ([]UprobeMultiOffset, bool) {
+ if umi.offsets == nil || len(umi.cookies) != len(umi.offsets) || len(umi.refCtrOffsets) != len(umi.offsets) {
+ return nil, false
+ }
+ var adresses = make([]UprobeMultiOffset, len(umi.offsets))
+ for i := range umi.offsets {
+ adresses[i] = UprobeMultiOffset{
+ Offset: umi.offsets[i],
+ Cookie: umi.cookies[i],
+ ReferenceCount: umi.refCtrOffsets[i],
+ }
+ }
+ return adresses, true
}
-func (kpm *KprobeMultiInfo) Missed() (uint64, bool) {
- return kpm.missed, kpm.count > 0
+// Pid returns the process ID that this uprobe is attached to.
+//
+// If it does not exist, the uprobe will trigger for all processes.
+func (umi *UprobeMultiInfo) Pid() (uint32, bool) {
+ return umi.pid, umi.pid > 0
}
+const (
+ PerfEventUnspecified = sys.BPF_PERF_EVENT_UNSPEC
+ PerfEventUprobe = sys.BPF_PERF_EVENT_UPROBE
+ PerfEventUretprobe = sys.BPF_PERF_EVENT_URETPROBE
+ PerfEventKprobe = sys.BPF_PERF_EVENT_KPROBE
+ PerfEventKretprobe = sys.BPF_PERF_EVENT_KRETPROBE
+ PerfEventTracepoint = sys.BPF_PERF_EVENT_TRACEPOINT
+ PerfEventEvent = sys.BPF_PERF_EVENT_EVENT
+)
+
type PerfEventInfo struct {
Type sys.PerfEventType
- extra interface{}
+ extra any
}
func (r *PerfEventInfo) Kprobe() *KprobeInfo {
@@ -174,17 +247,50 @@ func (r *PerfEventInfo) Kprobe() *KprobeInfo {
return e
}
+func (r *PerfEventInfo) Uprobe() *UprobeInfo {
+ e, _ := r.extra.(*UprobeInfo)
+ return e
+}
+
+func (r *PerfEventInfo) Tracepoint() *TracepointInfo {
+ e, _ := r.extra.(*TracepointInfo)
+ return e
+}
+
+func (r *PerfEventInfo) Event() *EventInfo {
+ e, _ := r.extra.(*EventInfo)
+ return e
+}
+
type KprobeInfo struct {
- address uint64
- missed uint64
+ Address uint64
+ Missed uint64
+ Function string
+ Offset uint32
}
-func (kp *KprobeInfo) Address() (uint64, bool) {
- return kp.address, kp.address > 0
+type UprobeInfo struct {
+ // File is the path that the file the uprobe was attached to
+ // had at creation time.
+ //
+ // However, due to various circumstances (differing mount namespaces,
+ // file replacement, ...), this path may not point to the same binary
+ // the uprobe was originally attached to.
+ File string
+ Offset uint32
+ Cookie uint64
+ OffsetReferenceCount uint64
}
-func (kp *KprobeInfo) Missed() (uint64, bool) {
- return kp.missed, kp.address > 0
+type TracepointInfo struct {
+ Tracepoint string
+ Cookie uint64
+}
+
+type EventInfo struct {
+ Config uint64
+ Type uint32
+ Cookie uint64
}
// Tracing returns tracing type-specific link info.
@@ -251,6 +357,14 @@ func (r Info) KprobeMulti() *KprobeMultiInfo {
return e
}
+// UprobeMulti returns uprobe-multi type-specific link info.
+//
+// Returns nil if the type-specific link info isn't available.
+func (r Info) UprobeMulti() *UprobeMultiInfo {
+ e, _ := r.extra.(*UprobeMultiInfo)
+ return e
+}
+
// PerfEvent returns perf-event type-specific link info.
//
// Returns nil if the type-specific link info isn't available.
@@ -258,3 +372,11 @@ func (r Info) PerfEvent() *PerfEventInfo {
e, _ := r.extra.(*PerfEventInfo)
return e
}
+
+// RawTracepoint returns raw-tracepoint type-specific link info.
+//
+// Returns nil if the type-specific link info isn't available.
+func (r Info) RawTracepoint() *RawTracepointInfo {
+ e, _ := r.extra.(*RawTracepointInfo)
+ return e
+}
diff --git a/vendor/github.com/cilium/ebpf/link/netfilter.go b/vendor/github.com/cilium/ebpf/link/netfilter.go
index 90e914c51..6b5177794 100644
--- a/vendor/github.com/cilium/ebpf/link/netfilter.go
+++ b/vendor/github.com/cilium/ebpf/link/netfilter.go
@@ -13,13 +13,35 @@ const NetfilterIPDefrag NetfilterAttachFlags = 0 // Enable IP packet defragmenta
type NetfilterAttachFlags uint32
+type NetfilterInetHook = sys.NetfilterInetHook
+
+const (
+ NetfilterInetPreRouting = sys.NF_INET_PRE_ROUTING
+ NetfilterInetLocalIn = sys.NF_INET_LOCAL_IN
+ NetfilterInetForward = sys.NF_INET_FORWARD
+ NetfilterInetLocalOut = sys.NF_INET_LOCAL_OUT
+ NetfilterInetPostRouting = sys.NF_INET_POST_ROUTING
+)
+
+type NetfilterProtocolFamily = sys.NetfilterProtocolFamily
+
+const (
+ NetfilterProtoUnspec = sys.NFPROTO_UNSPEC
+ NetfilterProtoInet = sys.NFPROTO_INET // Inet applies to both IPv4 and IPv6
+ NetfilterProtoIPv4 = sys.NFPROTO_IPV4
+ NetfilterProtoARP = sys.NFPROTO_ARP
+ NetfilterProtoNetdev = sys.NFPROTO_NETDEV
+ NetfilterProtoBridge = sys.NFPROTO_BRIDGE
+ NetfilterProtoIPv6 = sys.NFPROTO_IPV6
+)
+
type NetfilterOptions struct {
// Program must be a netfilter BPF program.
Program *ebpf.Program
// The protocol family.
- ProtocolFamily uint32
- // The number of the hook you are interested in.
- HookNumber uint32
+ ProtocolFamily NetfilterProtocolFamily
+ // The netfilter hook to attach to.
+ Hook NetfilterInetHook
// Priority within hook
Priority int32
// Extra link flags
@@ -51,8 +73,8 @@ func AttachNetfilter(opts NetfilterOptions) (Link, error) {
ProgFd: uint32(opts.Program.FD()),
AttachType: sys.BPF_NETFILTER,
Flags: opts.Flags,
- Pf: uint32(opts.ProtocolFamily),
- Hooknum: uint32(opts.HookNumber),
+ Pf: opts.ProtocolFamily,
+ Hooknum: opts.Hook,
Priority: opts.Priority,
NetfilterFlags: uint32(opts.NetfilterFlags),
}
@@ -75,10 +97,10 @@ func (nf *netfilterLink) Info() (*Info, error) {
return nil, fmt.Errorf("netfilter link info: %s", err)
}
extra := &NetfilterInfo{
- Pf: info.Pf,
- Hooknum: info.Hooknum,
- Priority: info.Priority,
- Flags: info.Flags,
+ ProtocolFamily: info.Pf,
+ Hook: info.Hooknum,
+ Priority: info.Priority,
+ Flags: info.Flags,
}
return &Info{
diff --git a/vendor/github.com/cilium/ebpf/link/netns.go b/vendor/github.com/cilium/ebpf/link/netns.go
index a9f7ee79c..fd7d6c6c2 100644
--- a/vendor/github.com/cilium/ebpf/link/netns.go
+++ b/vendor/github.com/cilium/ebpf/link/netns.go
@@ -44,7 +44,7 @@ func (ns *NetNsLink) Info() (*Info, error) {
return nil, fmt.Errorf("netns link info: %s", err)
}
extra := &NetNsInfo{
- NetnsIno: info.NetnsIno,
+ NetnsInode: info.NetnsIno,
AttachType: info.AttachType,
}
diff --git a/vendor/github.com/cilium/ebpf/link/perf_event.go b/vendor/github.com/cilium/ebpf/link/perf_event.go
index f1f5b84a9..7938b9d9d 100644
--- a/vendor/github.com/cilium/ebpf/link/perf_event.go
+++ b/vendor/github.com/cilium/ebpf/link/perf_event.go
@@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"os"
- "runtime"
"unsafe"
"github.com/cilium/ebpf"
@@ -61,15 +60,11 @@ type perfEvent struct {
func newPerfEvent(fd *sys.FD, event *tracefs.Event) *perfEvent {
pe := &perfEvent{event, fd}
- // Both event and fd have their own finalizer, but we want to
- // guarantee that they are closed in a certain order.
- runtime.SetFinalizer(pe, (*perfEvent).Close)
return pe
}
func (pe *perfEvent) Close() error {
- runtime.SetFinalizer(pe, nil)
-
+ // We close the perf event before attempting to remove the tracefs event.
if err := pe.fd.Close(); err != nil {
return fmt.Errorf("closing perf event fd: %w", err)
}
@@ -137,22 +132,84 @@ func (pl *perfEventLink) PerfEvent() (*os.File, error) {
return fd.File("perf-event")
}
+// queryInfoWithString queries object info that contains a string field.
+//
+// The passed stringField and stringLengthField must point to the string field
+// and its length field inside the info struct respectively.
+//
+// It returns the queried string and fills in the passed info struct.
+func queryInfoWithString(fd *sys.FD, info sys.Info, stringField *sys.TypedPointer[byte], stringLengthField *uint32) (string, error) {
+ // Query info to get the length
+ if err := sys.ObjInfo(fd, info); err != nil {
+ return "", err
+ }
+
+ // The stringLengthField pointer points to a field inside info, so it is now populated.
+ var stringData = make([]byte, *stringLengthField)
+ *stringField = sys.SlicePointer(stringData)
+
+ // Query info again to fill in the string.
+ // Since the stringField pointer points to a field inside info,
+ // the info now contains the pointer to our allocated stringData.
+ if err := sys.ObjInfo(fd, info); err != nil {
+ return "", fmt.Errorf("object info with string: %s", err)
+ }
+
+ return unix.ByteSliceToString(stringData), nil
+}
+
func (pl *perfEventLink) Info() (*Info, error) {
var info sys.PerfEventLinkInfo
if err := sys.ObjInfo(pl.fd, &info); err != nil {
return nil, fmt.Errorf("perf event link info: %s", err)
}
- var extra2 interface{}
+ var extra2 any
switch info.PerfEventType {
- case sys.BPF_PERF_EVENT_KPROBE, sys.BPF_PERF_EVENT_KRETPROBE:
+ case PerfEventKprobe, PerfEventKretprobe:
var kprobeInfo sys.KprobeLinkInfo
- if err := sys.ObjInfo(pl.fd, &kprobeInfo); err != nil {
+ funcName, err := queryInfoWithString(pl.fd, &kprobeInfo, &kprobeInfo.FuncName, &kprobeInfo.NameLen)
+ if err != nil {
return nil, fmt.Errorf("kprobe link info: %s", err)
}
extra2 = &KprobeInfo{
- address: kprobeInfo.Addr,
- missed: kprobeInfo.Missed,
+ Address: kprobeInfo.Addr,
+ Missed: kprobeInfo.Missed,
+ Function: funcName,
+ Offset: kprobeInfo.Offset,
+ }
+ case PerfEventUprobe, PerfEventUretprobe:
+ var uprobeInfo sys.UprobeLinkInfo
+ fileName, err := queryInfoWithString(pl.fd, &uprobeInfo, &uprobeInfo.FileName, &uprobeInfo.NameLen)
+ if err != nil {
+ return nil, fmt.Errorf("uprobe link info: %s", err)
+ }
+ extra2 = &UprobeInfo{
+ Offset: uprobeInfo.Offset,
+ Cookie: uprobeInfo.Cookie,
+ OffsetReferenceCount: uprobeInfo.RefCtrOffset,
+ File: fileName,
+ }
+ case PerfEventTracepoint:
+ var tracepointInfo sys.TracepointLinkInfo
+ tpName, err := queryInfoWithString(pl.fd, &tracepointInfo, &tracepointInfo.TpName, &tracepointInfo.NameLen)
+ if err != nil {
+ return nil, fmt.Errorf("perf event link info: %w", err)
+ }
+ extra2 = &TracepointInfo{
+ Tracepoint: tpName,
+ Cookie: tracepointInfo.Cookie,
+ }
+ case PerfEventEvent:
+ var eventInfo sys.EventLinkInfo
+ err := sys.ObjInfo(pl.fd, &eventInfo)
+ if err != nil {
+ return nil, fmt.Errorf("trace point link info: %s", err)
+ }
+ extra2 = &EventInfo{
+ Config: eventInfo.Config,
+ Type: eventInfo.EventType,
+ Cookie: eventInfo.Cookie,
}
}
@@ -199,6 +256,10 @@ func (pi *perfEventIoctl) Unpin() error {
return fmt.Errorf("perf event ioctl unpin: %w", ErrNotSupported)
}
+func (pi *perfEventIoctl) Detach() error {
+ return fmt.Errorf("perf event ioctl detach: %w", ErrNotSupported)
+}
+
func (pi *perfEventIoctl) Info() (*Info, error) {
return nil, fmt.Errorf("perf event ioctl info: %w", ErrNotSupported)
}
diff --git a/vendor/github.com/cilium/ebpf/link/program.go b/vendor/github.com/cilium/ebpf/link/program.go
index dbd7a9727..4928800bf 100644
--- a/vendor/github.com/cilium/ebpf/link/program.go
+++ b/vendor/github.com/cilium/ebpf/link/program.go
@@ -50,12 +50,11 @@ func RawAttachProgram(opts RawAttachProgramOptions) error {
return fmt.Errorf("attach program: %w", err)
}
- if flags == sys.BPF_F_REPLACE {
- // Ensure that replacing a program works on old kernels.
+ attr.AttachFlags |= flags
+ if flags&sys.BPF_F_REPLACE != 0 {
attr.ReplaceBpfFd = fdOrID
} else {
attr.RelativeFdOrId = fdOrID
- attr.AttachFlags |= flags
}
}
diff --git a/vendor/github.com/cilium/ebpf/link/query.go b/vendor/github.com/cilium/ebpf/link/query.go
index eeca82811..c722ab27b 100644
--- a/vendor/github.com/cilium/ebpf/link/query.go
+++ b/vendor/github.com/cilium/ebpf/link/query.go
@@ -4,6 +4,7 @@ package link
import (
"fmt"
+ "slices"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/internal/sys"
@@ -33,7 +34,12 @@ type QueryResult struct {
// HaveLinkInfo returns true if the kernel supports querying link information
// for a particular [ebpf.AttachType].
func (qr *QueryResult) HaveLinkInfo() bool {
- return qr.Revision > 0
+ return slices.ContainsFunc(qr.Programs,
+ func(ap AttachedProgram) bool {
+ _, ok := ap.LinkID()
+ return ok
+ },
+ )
}
type AttachedProgram struct {
@@ -44,8 +50,7 @@ type AttachedProgram struct {
// LinkID returns the ID associated with the program.
//
// Returns 0, false if the kernel doesn't support retrieving the ID or if the
-// program wasn't attached via a link. See [QueryResult.HaveLinkInfo] if you
-// need to tell the two apart.
+// program wasn't attached via a link.
func (ap *AttachedProgram) LinkID() (ID, bool) {
return ap.linkID, ap.linkID != 0
}
diff --git a/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go b/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go
index 4be9c6a27..5e974e3de 100644
--- a/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go
+++ b/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go
@@ -74,6 +74,10 @@ func (frt *simpleRawTracepoint) Unpin() error {
return fmt.Errorf("unpin raw_tracepoint: %w", ErrNotSupported)
}
+func (frt *simpleRawTracepoint) Detach() error {
+ return fmt.Errorf("detach raw_tracepoint: %w", ErrNotSupported)
+}
+
func (frt *simpleRawTracepoint) Info() (*Info, error) {
return nil, fmt.Errorf("can't get raw_tracepoint info: %w", ErrNotSupported)
}
@@ -87,3 +91,19 @@ var _ Link = (*rawTracepoint)(nil)
func (rt *rawTracepoint) Update(_ *ebpf.Program) error {
return fmt.Errorf("update raw_tracepoint: %w", ErrNotSupported)
}
+
+func (rt *rawTracepoint) Info() (*Info, error) {
+ var info sys.RawTracepointLinkInfo
+ name, err := queryInfoWithString(rt.fd, &info, &info.TpName, &info.TpNameLen)
+ if err != nil {
+ return nil, err
+ }
+ return &Info{
+ info.Type,
+ info.Id,
+ ebpf.ProgramID(info.ProgId),
+ &RawTracepointInfo{
+ Name: name,
+ },
+ }, nil
+}
diff --git a/vendor/github.com/cilium/ebpf/link/struct_ops.go b/vendor/github.com/cilium/ebpf/link/struct_ops.go
new file mode 100644
index 000000000..d1b7f2517
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/struct_ops.go
@@ -0,0 +1,51 @@
+package link
+
+import (
+ "fmt"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/internal/sys"
+)
+
+type structOpsLink struct {
+ RawLink
+}
+
+func (*structOpsLink) Update(*ebpf.Program) error {
+ return fmt.Errorf("update struct_ops link: %w", ErrNotSupported)
+}
+
+type StructOpsOptions struct {
+ Map *ebpf.Map
+}
+
+// AttachStructOps attaches a struct_ops map (created from a ".struct_ops.link"
+// section) to its kernel subsystem via a BPF link.
+func AttachStructOps(opts StructOpsOptions) (Link, error) {
+ m := opts.Map
+
+ if m == nil {
+ return nil, fmt.Errorf("map cannot be nil")
+ }
+
+ if t := m.Type(); t != ebpf.StructOpsMap {
+ return nil, fmt.Errorf("can't attach non-struct_ops map")
+ }
+
+ mapFD := m.FD()
+ if mapFD <= 0 {
+ return nil, fmt.Errorf("invalid map: %s", sys.ErrClosedFd)
+ }
+
+ fd, err := sys.LinkCreate(&sys.LinkCreateAttr{
+ // For struct_ops links, the mapFD must be passed as ProgFd.
+ ProgFd: uint32(mapFD),
+ AttachType: sys.AttachType(ebpf.AttachStructOps),
+ TargetFd: 0,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("attach StructOps: create link: %w", err)
+ }
+
+ return &structOpsLink{RawLink{fd: fd}}, nil
+}
diff --git a/vendor/github.com/cilium/ebpf/link/tracing.go b/vendor/github.com/cilium/ebpf/link/tracing.go
index b33b3dc0e..d0e595e6f 100644
--- a/vendor/github.com/cilium/ebpf/link/tracing.go
+++ b/vendor/github.com/cilium/ebpf/link/tracing.go
@@ -26,9 +26,9 @@ func (f *tracing) Info() (*Info, error) {
return nil, fmt.Errorf("tracing link info: %s", err)
}
extra := &TracingInfo{
- TargetObjId: info.TargetObjId,
- TargetBtfId: info.TargetBtfId,
- AttachType: info.AttachType,
+ TargetObjectId: info.TargetObjId,
+ TargetBtfId: info.TargetBtfId,
+ AttachType: info.AttachType,
}
return &Info{
diff --git a/vendor/github.com/cilium/ebpf/link/uprobe.go b/vendor/github.com/cilium/ebpf/link/uprobe.go
index d20997e9d..83333c4ec 100644
--- a/vendor/github.com/cilium/ebpf/link/uprobe.go
+++ b/vendor/github.com/cilium/ebpf/link/uprobe.go
@@ -38,10 +38,22 @@ var (
type Executable struct {
// Path of the executable on the filesystem.
path string
- // Parsed ELF and dynamic symbols' cachedAddresses.
- cachedAddresses map[string]uint64
- // Keep track of symbol table lazy load.
- cacheAddressesOnce sync.Once
+ // Cached symbol values for all ELF and dynamic symbols.
+ // Before using this, lazyLoadSymbols() must be called.
+ cachedSymbols map[string]symbol
+ // cachedSymbolsOnce tracks the lazy load of cachedSymbols.
+ cachedSymbolsOnce sync.Once
+}
+
+type symbol struct {
+ addr uint64
+ size uint64
+}
+
+// contains returns true if the given address falls within the range
+// covered by the symbol.
+func (s symbol) contains(address uint64) bool {
+ return s.addr <= address && address < s.addr+s.size
}
// UprobeOptions defines additional parameters that will be used
@@ -98,20 +110,13 @@ func OpenExecutable(path string) (*Executable, error) {
return nil, fmt.Errorf("path cannot be empty")
}
- f, err := internal.OpenSafeELFFile(path)
- if err != nil {
- return nil, fmt.Errorf("parse ELF file: %w", err)
- }
- defer f.Close()
-
- if f.Type != elf.ET_EXEC && f.Type != elf.ET_DYN {
- // ELF is not an executable or a shared object.
- return nil, errors.New("the given file is not an executable or a shared object")
+ if _, err := os.Stat(path); err != nil {
+ return nil, fmt.Errorf("stat executable: %w", err)
}
return &Executable{
- path: path,
- cachedAddresses: make(map[string]uint64),
+ path: path,
+ cachedSymbols: make(map[string]symbol),
}, nil
}
@@ -155,22 +160,18 @@ func (ex *Executable) load(f *internal.SafeELFFile) error {
}
}
- ex.cachedAddresses[s.Name] = address
+ ex.cachedSymbols[s.Name] = symbol{
+ addr: address,
+ size: s.Size,
+ }
}
return nil
}
-// address calculates the address of a symbol in the executable.
-//
-// opts must not be nil.
-func (ex *Executable) address(symbol string, address, offset uint64) (uint64, error) {
- if address > 0 {
- return address + offset, nil
- }
-
+func (ex *Executable) lazyLoadSymbols() error {
var err error
- ex.cacheAddressesOnce.Do(func() {
+ ex.cachedSymbolsOnce.Do(func() {
var f *internal.SafeELFFile
f, err = internal.OpenSafeELFFile(ex.path)
if err != nil {
@@ -179,13 +180,30 @@ func (ex *Executable) address(symbol string, address, offset uint64) (uint64, er
}
defer f.Close()
+ if f.Type != elf.ET_EXEC && f.Type != elf.ET_DYN {
+ // ELF is not an executable or a shared object.
+ err = errors.New("the given file is not an executable or a shared object")
+ return
+ }
err = ex.load(f)
})
+ return err
+}
+
+// address calculates the address of a symbol in the executable.
+//
+// opts must not be nil.
+func (ex *Executable) address(symbol string, address, offset uint64) (uint64, error) {
+ if address > 0 {
+ return address + offset, nil
+ }
+
+ err := ex.lazyLoadSymbols()
if err != nil {
return 0, fmt.Errorf("lazy load symbols: %w", err)
}
- address, ok := ex.cachedAddresses[symbol]
+ sym, ok := ex.cachedSymbols[symbol]
if !ok {
return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol)
}
@@ -196,12 +214,39 @@ func (ex *Executable) address(symbol string, address, offset uint64) (uint64, er
//
// Since only offset values are stored and not elf.Symbol, if the value is 0,
// assume it's an external symbol.
- if address == 0 {
+ if sym.addr == 0 {
return 0, fmt.Errorf("cannot resolve %s library call '%s': %w "+
"(consider providing UprobeOptions.Address)", ex.path, symbol, ErrNotSupported)
}
- return address + offset, nil
+ if offset >= sym.size {
+ return 0, fmt.Errorf("offset %d is out of range of symbol %s", offset, symbol)
+ }
+
+ return sym.addr + offset, nil
+}
+
+// SymbolOffset represents an offset within a symbol within a binary.
+type SymbolOffset struct {
+ Symbol string
+ Offset uint64
+}
+
+// Symbol returns the SymbolOffset that the given address points to.
+// This includes the symbol name and the offset within that symbol.
+//
+// If no symbol is found for the given address, ErrNoSymbol is returned.
+func (ex *Executable) Symbol(address uint64) (SymbolOffset, error) {
+ if err := ex.lazyLoadSymbols(); err != nil {
+ return SymbolOffset{}, fmt.Errorf("lazy load symbols: %w", err)
+ }
+
+ for name, symbol := range ex.cachedSymbols {
+ if symbol.contains(address) {
+ return SymbolOffset{name, address - symbol.addr}, nil
+ }
+ }
+ return SymbolOffset{}, ErrNoSymbol
}
// Uprobe attaches the given eBPF program to a perf event that fires when the
@@ -211,12 +256,15 @@ func (ex *Executable) address(symbol string, address, offset uint64) (uint64, er
// ex, _ = OpenExecutable("/bin/bash")
// ex.Uprobe("main", prog, nil)
//
-// When using symbols which belongs to shared libraries,
-// an offset must be provided via options:
+// When tracing a location for which no ELF symbol is available, or a shared
+// library symbol whose static address is zero, set UprobeOptions.Address to
+// the absolute file offset and pass an empty symbol:
//
-// up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123})
+// up, err := ex.Uprobe("", prog, &UprobeOptions{Address: 0x123})
//
-// Note: Setting the Offset field in the options supersedes the symbol's offset.
+// Address bypasses symbol lookup. Offset, if set, is added to the resolved
+// address (whether from symbol resolution or from Address); on its own it
+// does not replace symbol resolution.
//
// Losing the reference to the resulting Link (up) will close the Uprobe
// and prevent further execution of prog. The Link must be Closed during
@@ -247,12 +295,15 @@ func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
// ex, _ = OpenExecutable("/bin/bash")
// ex.Uretprobe("main", prog, nil)
//
-// When using symbols which belongs to shared libraries,
-// an offset must be provided via options:
+// When tracing a location for which no ELF symbol is available, or a shared
+// library symbol whose static address is zero, set UprobeOptions.Address to
+// the absolute file offset and pass an empty symbol:
//
-// up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123})
+// up, err := ex.Uretprobe("", prog, &UprobeOptions{Address: 0x123})
//
-// Note: Setting the Offset field in the options supersedes the symbol's offset.
+// Address bypasses symbol lookup. Offset, if set, is added to the resolved
+// address (whether from symbol resolution or from Address); on its own it
+// does not replace symbol resolution.
//
// Losing the reference to the resulting Link (up) will close the Uprobe
// and prevent further execution of prog. The Link must be Closed during
diff --git a/vendor/github.com/cilium/ebpf/link/uprobe_multi.go b/vendor/github.com/cilium/ebpf/link/uprobe_multi.go
index e34ad7168..f017405aa 100644
--- a/vendor/github.com/cilium/ebpf/link/uprobe_multi.go
+++ b/vendor/github.com/cilium/ebpf/link/uprobe_multi.go
@@ -8,8 +8,7 @@ import (
"os"
"github.com/cilium/ebpf"
- "github.com/cilium/ebpf/asm"
- "github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/features"
"github.com/cilium/ebpf/internal/sys"
"github.com/cilium/ebpf/internal/unix"
)
@@ -108,7 +107,7 @@ func (ex *Executable) uprobeMulti(symbols []string, prog *ebpf.Program, opts *Up
}
if err != nil {
- if haveFeatErr := haveBPFLinkUprobeMulti(); haveFeatErr != nil {
+ if haveFeatErr := features.HaveBPFLinkUprobeMulti(); haveFeatErr != nil {
return nil, haveFeatErr
}
return nil, err
@@ -176,45 +175,54 @@ func (kml *uprobeMultiLink) Update(_ *ebpf.Program) error {
return fmt.Errorf("update uprobe_multi: %w", ErrNotSupported)
}
-var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", func() error {
- prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
- Name: "probe_upm_link",
- Type: ebpf.Kprobe,
- Instructions: asm.Instructions{
- asm.Mov.Imm(asm.R0, 0),
- asm.Return(),
- },
- AttachType: ebpf.AttachTraceUprobeMulti,
- License: "MIT",
- })
- if errors.Is(err, unix.E2BIG) {
- // Kernel doesn't support AttachType field.
- return internal.ErrNotSupported
- }
- if err != nil {
- return err
- }
- defer prog.Close()
-
- // We try to create uprobe multi link on '/' path which results in
- // error with -EBADF in case uprobe multi link is supported.
- fd, err := sys.LinkCreateUprobeMulti(&sys.LinkCreateUprobeMultiAttr{
- ProgFd: uint32(prog.FD()),
- AttachType: sys.BPF_TRACE_UPROBE_MULTI,
- Path: sys.NewStringPointer("/"),
- Offsets: sys.SlicePointer([]uint64{0}),
- Count: 1,
- })
- switch {
- case errors.Is(err, unix.EBADF):
- return nil
- case errors.Is(err, unix.EINVAL):
- return internal.ErrNotSupported
- case err != nil:
- return err
- }
-
- // should not happen
- fd.Close()
- return errors.New("successfully attached uprobe_multi to /, kernel bug?")
-}, "6.6")
+func (kml *uprobeMultiLink) Info() (*Info, error) {
+ var info sys.UprobeMultiLinkInfo
+ if err := sys.ObjInfo(kml.fd, &info); err != nil {
+ return nil, fmt.Errorf("uprobe multi link info: %s", err)
+ }
+ var (
+ path = make([]byte, info.PathSize)
+ refCtrOffsets = make([]uint64, info.Count)
+ addrs = make([]uint64, info.Count)
+ cookies = make([]uint64, info.Count)
+ )
+ info = sys.UprobeMultiLinkInfo{
+ Path: sys.SlicePointer(path),
+ PathSize: uint32(len(path)),
+ Offsets: sys.SlicePointer(addrs),
+ RefCtrOffsets: sys.SlicePointer(refCtrOffsets),
+ Cookies: sys.SlicePointer(cookies),
+ Count: uint32(len(addrs)),
+ }
+ if err := sys.ObjInfo(kml.fd, &info); err != nil {
+ return nil, fmt.Errorf("uprobe multi link info: %s", err)
+ }
+ if info.Path.IsNil() {
+ path = nil
+ }
+ if info.Cookies.IsNil() {
+ cookies = nil
+ }
+ if info.Offsets.IsNil() {
+ addrs = nil
+ }
+ if info.RefCtrOffsets.IsNil() {
+ refCtrOffsets = nil
+ }
+ extra := &UprobeMultiInfo{
+ Count: info.Count,
+ Flags: info.Flags,
+ pid: info.Pid,
+ offsets: addrs,
+ cookies: cookies,
+ refCtrOffsets: refCtrOffsets,
+ File: unix.ByteSliceToString(path),
+ }
+
+ return &Info{
+ info.Type,
+ info.Id,
+ ebpf.ProgramID(info.ProgId),
+ extra,
+ }, nil
+}
diff --git a/vendor/github.com/cilium/ebpf/linker.go b/vendor/github.com/cilium/ebpf/linker.go
index 0b966477f..519c0aed0 100644
--- a/vendor/github.com/cilium/ebpf/linker.go
+++ b/vendor/github.com/cilium/ebpf/linker.go
@@ -54,47 +54,6 @@ func (hs *handles) Close() error {
return errors.Join(errs...)
}
-// splitSymbols splits insns into subsections delimited by Symbol Instructions.
-// insns cannot be empty and must start with a Symbol Instruction.
-//
-// The resulting map is indexed by Symbol name.
-func splitSymbols(insns asm.Instructions) (map[string]asm.Instructions, error) {
- if len(insns) == 0 {
- return nil, errors.New("insns is empty")
- }
-
- currentSym := insns[0].Symbol()
- if currentSym == "" {
- return nil, errors.New("insns must start with a Symbol")
- }
-
- start := 0
- progs := make(map[string]asm.Instructions)
- for i, ins := range insns[1:] {
- i := i + 1
-
- sym := ins.Symbol()
- if sym == "" {
- continue
- }
-
- // New symbol, flush the old one out.
- progs[currentSym] = slices.Clone(insns[start:i])
-
- if progs[sym] != nil {
- return nil, fmt.Errorf("insns contains duplicate Symbol %s", sym)
- }
- currentSym = sym
- start = i
- }
-
- if tail := insns[start:]; len(tail) > 0 {
- progs[currentSym] = slices.Clone(tail)
- }
-
- return progs, nil
-}
-
// The linker is responsible for resolving bpf-to-bpf calls between programs
// within an ELF. Each BPF program must be a self-contained binary blob,
// so when an instruction in one ELF program section wants to jump to
@@ -122,9 +81,8 @@ func hasFunctionReferences(insns asm.Instructions) bool {
// applyRelocations collects and applies any CO-RE relocations in insns.
//
-// Passing a nil target will relocate against the running kernel. insns are
-// modified in place.
-func applyRelocations(insns asm.Instructions, bo binary.ByteOrder, b *btf.Builder, c *btf.Cache) error {
+// insns are modified in place.
+func applyRelocations(insns asm.Instructions, bo binary.ByteOrder, b *btf.Builder, c *btf.Cache, kernelOverride *btf.Spec, extraTargets []*btf.Spec) error {
var relos []*btf.CORERelocation
var reloInsns []*asm.Instruction
iter := insns.Iterate()
@@ -143,30 +101,39 @@ func applyRelocations(insns asm.Instructions, bo binary.ByteOrder, b *btf.Builde
bo = internal.NativeEndian
}
- kernelTarget, err := c.Kernel()
- if err != nil {
- return fmt.Errorf("load kernel spec: %w", err)
- }
+ var targets []*btf.Spec
+ if kernelOverride == nil {
+ kernel, err := c.Kernel()
+ if err != nil {
+ return fmt.Errorf("load kernel spec: %w", err)
+ }
- modules, err := c.Modules()
- // Ignore ErrNotExists to cater to kernels which have CONFIG_DEBUG_INFO_BTF_MODULES
- // or CONFIG_DEBUG_INFO_BTF disabled.
- if err != nil && !errors.Is(err, fs.ErrNotExist) {
- return err
- }
+ modules, err := c.Modules()
+ // Ignore ErrNotExists to cater to kernels which have CONFIG_DEBUG_INFO_BTF_MODULES
+ // or CONFIG_DEBUG_INFO_BTF disabled.
+ if err != nil && !errors.Is(err, fs.ErrNotExist) {
+ return err
+ }
- targets := make([]*btf.Spec, 0, 1+len(modules))
- targets = append(targets, kernelTarget)
+ targets = make([]*btf.Spec, 0, 1+len(modules)+len(extraTargets))
+ targets = append(targets, kernel)
- for _, kmod := range modules {
- spec, err := c.Module(kmod)
- if err != nil {
- return fmt.Errorf("load BTF for kmod %s: %w", kmod, err)
- }
+ for _, kmod := range modules {
+ spec, err := c.Module(kmod)
+ if err != nil {
+ return fmt.Errorf("load BTF for kmod %s: %w", kmod, err)
+ }
- targets = append(targets, spec)
+ targets = append(targets, spec)
+ }
+ } else {
+ // We expect kernelOverride to contain the merged types
+ // of vmlinux and kernel modules, as distributed by btfhub.
+ targets = []*btf.Spec{kernelOverride}
}
+ targets = append(targets, extraTargets...)
+
fixups, err := btf.CORERelocate(relos, targets, bo, b.Add)
if err != nil {
return err
@@ -287,7 +254,7 @@ const kfuncCallPoisonBase = 0xdedc0de
// fixupKfuncs loops over all instructions in search for kfunc calls.
// If at least one is found, the current kernels BTF and module BTFis are searched to set Instruction.Constant
// and Instruction.Offset to the correct values.
-func fixupKfuncs(insns asm.Instructions) (_ handles, err error) {
+func fixupKfuncs(insns asm.Instructions, cache *btf.Cache) (_ handles, err error) {
closeOnError := func(c io.Closer) {
if err != nil {
c.Close()
@@ -308,7 +275,7 @@ fixups:
// Only load kernel BTF if we found at least one kfunc call. kernelSpec can be
// nil if the kernel does not have BTF, in which case we poison all kfunc
// calls.
- kernelSpec, err := btf.LoadKernelSpec()
+ _, err = cache.Kernel()
// ErrNotSupportedOnOS wraps ErrNotSupported, check for it first.
if errors.Is(err, internal.ErrNotSupportedOnOS) {
return nil, fmt.Errorf("kfuncs are not supported on this platform: %w", err)
@@ -338,9 +305,10 @@ fixups:
return nil, fmt.Errorf("kfuncMetaKey doesn't contain kfuncMeta")
}
- // findTargetInKernel returns btf.ErrNotFound if the input btf.Spec is nil.
+ // findTargetInKernel returns [btf.ErrNotFound] if the target can't be found
+ // or if BTF is not enabled.
target := btf.Type((*btf.Func)(nil))
- spec, module, err := findTargetInKernel(kernelSpec, kfm.Func.Name, &target)
+ spec, module, err := findTargetInKernel(kfm.Func.Name, &target, cache)
if errors.Is(err, btf.ErrNotFound) {
if kfm.Binding == elf.STB_WEAK {
if ins.IsKfuncCall() {
@@ -485,7 +453,13 @@ func resolveKconfigReferences(insns asm.Instructions) (_ *Map, err error) {
}
func resolveKsymReferences(insns asm.Instructions) error {
- var missing []string
+ type fixup struct {
+ *asm.Instruction
+ *ksymMeta
+ }
+
+ var symbols map[string]uint64
+ var fixups []fixup
iter := insns.Iterate()
for iter.Next() {
@@ -495,28 +469,47 @@ func resolveKsymReferences(insns asm.Instructions) error {
continue
}
- addr, err := kallsyms.Address(meta.Name)
- if err != nil {
- return fmt.Errorf("resolve ksym %s: %w", meta.Name, err)
- }
- if addr != 0 {
- ins.Constant = int64(addr)
- continue
+ if symbols == nil {
+ symbols = make(map[string]uint64)
}
- if meta.Binding == elf.STB_WEAK {
- // A weak ksym variable in eBPF C means its resolution is optional.
- // Set a zero constant explicitly for clarity.
- ins.Constant = 0
+ symbols[meta.Name] = 0
+ fixups = append(fixups, fixup{
+ iter.Ins, meta,
+ })
+ }
+
+ if len(symbols) == 0 {
+ return nil
+ }
+
+ err := kallsyms.AssignAddresses(symbols)
+ // Tolerate ErrRestrictedKernel during initial lookup, user may have all weak
+ // ksyms and a fallback path.
+ if err != nil && !errors.Is(err, ErrRestrictedKernel) {
+ return fmt.Errorf("resolve ksyms: %w", err)
+ }
+
+ var missing []string
+ for _, fixup := range fixups {
+ addr := symbols[fixup.Name]
+ // A weak ksym variable in eBPF C means its resolution is optional.
+ if addr == 0 && fixup.Binding != elf.STB_WEAK {
+ if !slices.Contains(missing, fixup.Name) {
+ missing = append(missing, fixup.Name)
+ }
continue
}
- if !slices.Contains(missing, meta.Name) {
- missing = append(missing, meta.Name)
- }
+ fixup.Constant = int64(addr)
}
if len(missing) > 0 {
+ if err != nil {
+ // Program contains required ksyms, return the error from above.
+ return fmt.Errorf("resolve required ksyms: %s: %w", strings.Join(missing, ","), err)
+ }
+
return fmt.Errorf("kernel is missing symbol: %s", strings.Join(missing, ","))
}
diff --git a/vendor/github.com/cilium/ebpf/map.go b/vendor/github.com/cilium/ebpf/map.go
index 55974b4eb..884fbebf0 100644
--- a/vendor/github.com/cilium/ebpf/map.go
+++ b/vendor/github.com/cilium/ebpf/map.go
@@ -29,7 +29,6 @@ var (
ErrKeyExist = errors.New("key already exists")
ErrIterationAborted = errors.New("iteration aborted")
ErrMapIncompatible = errors.New("map spec is incompatible with existing map")
- errMapNoBTFValue = errors.New("map spec does not contain a BTF Value")
// pre-allocating these errors here since they may get called in hot code paths
// and cause unnecessary memory allocations
@@ -78,6 +77,11 @@ type MapSpec struct {
// InnerMap is used as a template for ArrayOfMaps and HashOfMaps
InnerMap *MapSpec
+ // MapExtra is an opaque field whose meaning is map-specific.
+ //
+ // Available from 5.16.
+ MapExtra uint64
+
// Extra trailing bytes found in the ELF map definition when using structs
// larger than libbpf's bpf_map_def. nil if no trailing bytes were present.
// Must be nil or empty before instantiating the MapSpec into a Map.
@@ -182,28 +186,71 @@ func (spec *MapSpec) fixupMagicFields() (*MapSpec, error) {
return spec, nil
}
-// dataSection returns the contents and BTF Datasec descriptor of the spec.
-func (ms *MapSpec) dataSection() ([]byte, *btf.Datasec, error) {
- if ms.Value == nil {
- return nil, nil, errMapNoBTFValue
+// dataSection returns the contents of a datasec if the MapSpec represents one.
+func (ms *MapSpec) dataSection() ([]byte, error) {
+ if n := len(ms.Contents); n != 1 {
+ return nil, fmt.Errorf("expected one key, found %d", n)
+ }
+
+ kv := ms.Contents[0]
+ if key, ok := ms.Contents[0].Key.(uint32); !ok || key != 0 {
+ return nil, fmt.Errorf("expected contents to have key 0")
}
- ds, ok := ms.Value.(*btf.Datasec)
+ value, ok := kv.Value.([]byte)
if !ok {
- return nil, nil, fmt.Errorf("map value BTF is a %T, not a *btf.Datasec", ms.Value)
+ return nil, fmt.Errorf("value at first map key is %T, not []byte", kv.Value)
}
- if n := len(ms.Contents); n != 1 {
- return nil, nil, fmt.Errorf("expected one key, found %d", n)
+ return value, nil
+}
+
+// updateDataSection copies the values of variables into MapSpec.Contents[0].Value.
+//
+// Only variables declared in sectionName will be updated.
+func (ms *MapSpec) updateDataSection(vars map[string]*VariableSpec, sectionName string) error {
+ var specs []*VariableSpec
+ for _, vs := range vars {
+ if vs.SectionName != sectionName {
+ continue
+ }
+
+ specs = append(specs, vs)
}
- kv := ms.Contents[0]
- value, ok := kv.Value.([]byte)
- if !ok {
- return nil, nil, fmt.Errorf("value at first map key is %T, not []byte", kv.Value)
+ if len(specs) == 0 {
+ return nil
+ }
+
+ data, err := ms.dataSection()
+ if err != nil {
+ return err
+ }
+
+ // Do not modify the original data slice, ms.Contents is a shallow copy.
+ data = slices.Clone(data)
+
+ slices.SortFunc(specs, func(a, b *VariableSpec) int {
+ return int(int64(a.Offset) - int64(b.Offset))
+ })
+
+ offset := uint32(0)
+ for _, v := range specs {
+ if v.Offset < offset {
+ return fmt.Errorf("variable %s (offset %d) overlaps with previous variable (offset %d)", v.Name, v.Offset, offset)
+ }
+
+ end := v.Offset + v.Size()
+ if int(end) > len(data) {
+ return fmt.Errorf("variable %s exceeds map size", v.Name)
+ }
+
+ copy(data[v.Offset:end], v.Value)
+ offset = end
}
- return value, ds, nil
+ ms.Contents = []MapKV{{Key: uint32(0), Value: data}}
+ return nil
}
func (ms *MapSpec) readOnly() bool {
@@ -216,8 +263,8 @@ func (ms *MapSpec) writeOnly() bool {
// MapKV is used to initialize the contents of a Map.
type MapKV struct {
- Key interface{}
- Value interface{}
+ Key any
+ Value any
}
// Compatible returns nil if an existing map may be used instead of creating
@@ -265,13 +312,15 @@ func (ms *MapSpec) Compatible(m *Map) error {
// Map represents a Map file descriptor.
//
-// It is not safe to close a map which is used by other goroutines.
+// It is not safe to close a Map which is used by other goroutines.
//
-// Methods which take interface{} arguments by default encode
-// them using binary.Read/Write in the machine's native endianness.
+// Map operations taking `any` arguments are encoded using [sysenc.Marshal],
+// which is zero-copy for fixed-size types without padding as well as slices of
+// bytes, but may involve allocations for other types. See its documentation for
+// details.
//
-// Implement encoding.BinaryMarshaler or encoding.BinaryUnmarshaler
-// if you require custom encoding.
+// Implement encoding.BinaryMarshaler or encoding.BinaryUnmarshaler if you
+// require custom encoding or decoding.
type Map struct {
name string
fd *sys.FD
@@ -329,7 +378,7 @@ func NewMap(spec *MapSpec) (*Map, error) {
//
// May return an error wrapping ErrMapIncompatible.
func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) {
- m, err := newMapWithOptions(spec, opts)
+ m, err := newMapWithOptions(spec, opts, btf.NewCache())
if err != nil {
return nil, fmt.Errorf("creating map: %w", err)
}
@@ -342,7 +391,7 @@ func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) {
return m, nil
}
-func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) {
+func newMapWithOptions(spec *MapSpec, opts MapOptions, c *btf.Cache) (_ *Map, err error) {
closeOnError := func(c io.Closer) {
if err != nil {
c.Close()
@@ -392,7 +441,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) {
return nil, errors.New("inner maps cannot be pinned")
}
- template, err := spec.InnerMap.createMap(nil)
+ template, err := spec.InnerMap.createMap(nil, c)
if err != nil {
return nil, fmt.Errorf("inner map: %w", err)
}
@@ -404,7 +453,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) {
innerFd = template.fd
}
- m, err := spec.createMap(innerFd)
+ m, err := spec.createMap(innerFd, c)
if err != nil {
return nil, err
}
@@ -499,7 +548,7 @@ func (m *Map) memorySize() (int, error) {
// createMap validates the spec's properties and creates the map in the kernel
// using the given opts. It does not populate or freeze the map.
-func (spec *MapSpec) createMap(inner *sys.FD) (_ *Map, err error) {
+func (spec *MapSpec) createMap(inner *sys.FD, c *btf.Cache) (_ *Map, err error) {
closeOnError := func(closer io.Closer) {
if err != nil {
closer.Close()
@@ -534,6 +583,7 @@ func (spec *MapSpec) createMap(inner *sys.FD) (_ *Map, err error) {
MaxEntries: spec.MaxEntries,
MapFlags: spec.Flags,
NumaNode: spec.NumaNode,
+ MapExtra: spec.MapExtra,
}
if inner != nil {
@@ -554,6 +604,42 @@ func (spec *MapSpec) createMap(inner *sys.FD) (_ *Map, err error) {
attr.BtfKeyTypeId = keyTypeID
attr.BtfValueTypeId = valueTypeID
}
+
+ if spec.Type == StructOpsMap {
+ if handle == nil {
+ return nil, fmt.Errorf("struct_ops requires BTF")
+ }
+
+ localValue, ok := btf.As[*btf.Struct](spec.Value)
+ if !ok {
+ return nil, fmt.Errorf("struct_ops: value must be struct")
+ }
+
+ targetValue, targetID, module, err := structOpsFindTarget(localValue, c)
+ if err != nil {
+ return nil, fmt.Errorf("struct_ops: %w", err)
+ }
+ defer module.Close()
+
+ spec = spec.Copy()
+ spec.ValueSize = targetValue.Size
+
+ attr.ValueSize = targetValue.Size
+ attr.BtfVmlinuxValueTypeId = targetID
+
+ if module != nil {
+ // BPF_F_VTYPE_BTF_OBJ_FD is required if the type comes from a module
+ attr.MapFlags |= sys.BPF_F_VTYPE_BTF_OBJ_FD
+ // set FD for the kernel module
+ attr.ValueTypeBtfObjFd = int32(module.FD())
+ }
+
+ // StructOpsMap forbids passing BtfKeyTypeId or BtfValueTypeId, but
+ // requires BtfFd. Do the simple thing and just zero out the fields.
+ // See https://github.com/torvalds/linux/blob/9b332cece987ee1790b2ed4c989e28162fa47860/kernel/bpf/syscall.c#L1382-L1384
+ attr.BtfKeyTypeId = 0
+ attr.BtfValueTypeId = 0
+ }
}
fd, err := sys.MapCreate(&attr)
@@ -585,11 +671,16 @@ func handleMapCreateError(attr sys.MapCreateAttr, spec *MapSpec, err error) erro
return err
}
+ if errors.Is(err, sys.ErrTokenCapabilities) {
+ return fmt.Errorf("map create: %w", err)
+ }
+
if errors.Is(err, unix.EPERM) {
return fmt.Errorf("map create: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err)
}
+
if errors.Is(err, unix.EINVAL) {
- if spec.MaxEntries == 0 {
+ if spec.MaxEntries == 0 && !spec.Type.mustHaveZeroMaxEntries() {
return fmt.Errorf("map create: %w (MaxEntries may be incorrectly set to zero)", err)
}
if spec.Type == UnspecifiedMap {
@@ -743,7 +834,7 @@ const LookupLock MapLookupFlags = sys.BPF_F_LOCK
// and *valueOut is not nil.
//
// Returns an error if the key doesn't exist, see ErrKeyNotExist.
-func (m *Map) Lookup(key, valueOut interface{}) error {
+func (m *Map) Lookup(key, valueOut any) error {
return m.LookupWithFlags(key, valueOut, 0)
}
@@ -757,7 +848,7 @@ func (m *Map) Lookup(key, valueOut interface{}) error {
// and *valueOut is not nil.
//
// Returns an error if the key doesn't exist, see ErrKeyNotExist.
-func (m *Map) LookupWithFlags(key, valueOut interface{}, flags MapLookupFlags) error {
+func (m *Map) LookupWithFlags(key, valueOut any, flags MapLookupFlags) error {
if m.typ.hasPerCPUValue() {
return m.lookupPerCPU(key, valueOut, flags)
}
@@ -773,7 +864,7 @@ func (m *Map) LookupWithFlags(key, valueOut interface{}, flags MapLookupFlags) e
// LookupAndDelete retrieves and deletes a value from a Map.
//
// Returns ErrKeyNotExist if the key doesn't exist.
-func (m *Map) LookupAndDelete(key, valueOut interface{}) error {
+func (m *Map) LookupAndDelete(key, valueOut any) error {
return m.LookupAndDeleteWithFlags(key, valueOut, 0)
}
@@ -784,7 +875,7 @@ func (m *Map) LookupAndDelete(key, valueOut interface{}) error {
// contain a spinlock.
//
// Returns ErrKeyNotExist if the key doesn't exist.
-func (m *Map) LookupAndDeleteWithFlags(key, valueOut interface{}, flags MapLookupFlags) error {
+func (m *Map) LookupAndDeleteWithFlags(key, valueOut any, flags MapLookupFlags) error {
if m.typ.hasPerCPUValue() {
return m.lookupAndDeletePerCPU(key, valueOut, flags)
}
@@ -799,7 +890,7 @@ func (m *Map) LookupAndDeleteWithFlags(key, valueOut interface{}, flags MapLooku
// LookupBytes gets a value from Map.
//
// Returns a nil value if a key doesn't exist.
-func (m *Map) LookupBytes(key interface{}) ([]byte, error) {
+func (m *Map) LookupBytes(key any) ([]byte, error) {
valueBytes := make([]byte, m.fullValueSize)
valuePtr := sys.UnsafeSlicePointer(valueBytes)
@@ -823,7 +914,7 @@ func (m *Map) lookupPerCPU(key, valueOut any, flags MapLookupFlags) error {
return unmarshalPerCPUValue(slice, int(m.valueSize), valueBytes)
}
-func (m *Map) lookup(key interface{}, valueOut sys.Pointer, flags MapLookupFlags) error {
+func (m *Map) lookup(key any, valueOut sys.Pointer, flags MapLookupFlags) error {
keyPtr, err := m.marshalKey(key)
if err != nil {
return fmt.Errorf("can't marshal key: %w", err)
@@ -859,6 +950,10 @@ func (m *Map) lookupAndDeletePerCPU(key, valueOut any, flags MapLookupFlags) err
// ensurePerCPUSlice allocates a slice for a per-CPU value if necessary.
func ensurePerCPUSlice(sliceOrPtr any) (any, error) {
+ if err := internal.IsNilPointer(sliceOrPtr); err != nil {
+ return nil, fmt.Errorf("per-cpu value: %w", err)
+ }
+
sliceOrPtrType := reflect.TypeOf(sliceOrPtr)
if sliceOrPtrType.Kind() == reflect.Slice {
// The target is a slice, the caller is responsible for ensuring that
@@ -867,7 +962,7 @@ func ensurePerCPUSlice(sliceOrPtr any) (any, error) {
}
slicePtrType := sliceOrPtrType
- if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice {
+ if slicePtrType.Kind() != reflect.Pointer || slicePtrType.Elem().Kind() != reflect.Slice {
return nil, fmt.Errorf("per-cpu value requires a slice or a pointer to slice")
}
@@ -880,14 +975,14 @@ func ensurePerCPUSlice(sliceOrPtr any) (any, error) {
slice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs)
sliceElemType := sliceType.Elem()
- sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr
+ sliceElemIsPointer := sliceElemType.Kind() == reflect.Pointer
reflect.ValueOf(sliceOrPtr).Elem().Set(slice)
if !sliceElemIsPointer {
return slice.Interface(), nil
}
sliceElemType = sliceElemType.Elem()
- for i := 0; i < possibleCPUs; i++ {
+ for i := range possibleCPUs {
newElem := reflect.New(sliceElemType)
slice.Index(i).Set(newElem)
}
@@ -934,7 +1029,7 @@ const (
// Put replaces or creates a value in map.
//
// It is equivalent to calling Update with UpdateAny.
-func (m *Map) Put(key, value interface{}) error {
+func (m *Map) Put(key, value any) error {
return m.Update(key, value, UpdateAny)
}
@@ -984,7 +1079,7 @@ func (m *Map) update(key any, valuePtr sys.Pointer, flags MapUpdateFlags) error
// Delete removes a value.
//
// Returns ErrKeyNotExist if the key does not exist.
-func (m *Map) Delete(key interface{}) error {
+func (m *Map) Delete(key any) error {
keyPtr, err := m.marshalKey(key)
if err != nil {
return fmt.Errorf("can't marshal key: %w", err)
@@ -1006,7 +1101,7 @@ func (m *Map) Delete(key interface{}) error {
// See NextKeyBytes for details.
//
// Returns ErrKeyNotExist if there is no next key.
-func (m *Map) NextKey(key, nextKeyOut interface{}) error {
+func (m *Map) NextKey(key, nextKeyOut any) error {
nextKeyBytes := makeMapSyscallOutput(nextKeyOut, int(m.keySize))
if err := m.nextKey(key, nextKeyBytes.Pointer()); err != nil {
@@ -1026,7 +1121,7 @@ func (m *Map) NextKey(key, nextKeyOut interface{}) error {
// Use Iterate if you want to traverse all entries in the map.
//
// Returns nil if there are no more keys.
-func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) {
+func (m *Map) NextKeyBytes(key any) ([]byte, error) {
nextKey := make([]byte, m.keySize)
nextKeyPtr := sys.UnsafeSlicePointer(nextKey)
@@ -1038,7 +1133,7 @@ func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) {
return nextKey, err
}
-func (m *Map) nextKey(key interface{}, nextKeyOut sys.Pointer) error {
+func (m *Map) nextKey(key any, nextKeyOut sys.Pointer) error {
var (
keyPtr sys.Pointer
err error
@@ -1098,7 +1193,7 @@ func (m *Map) guessNonExistentKey() ([]byte, error) {
randKey := make([]byte, int(m.keySize))
- for i := 0; i < 4; i++ {
+ for i := range 4 {
switch i {
// For hash maps, the 0 key is less likely to be occupied. They're often
// used for storing data related to pointers, and their access pattern is
@@ -1147,7 +1242,7 @@ func (m *Map) guessNonExistentKey() ([]byte, error) {
// ErrKeyNotExist is returned when the batch lookup has reached
// the end of all possible results, even when partial results
// are returned. It should be used to evaluate when lookup is "done".
-func (m *Map) BatchLookup(cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) {
+func (m *Map) BatchLookup(cursor *MapBatchCursor, keysOut, valuesOut any, opts *BatchOptions) (int, error) {
n, err := m.batchLookup(sys.BPF_MAP_LOOKUP_BATCH, cursor, keysOut, valuesOut, opts)
if err != nil {
return n, fmt.Errorf("map batch lookup: %w", err)
@@ -1171,7 +1266,7 @@ func (m *Map) BatchLookup(cursor *MapBatchCursor, keysOut, valuesOut interface{}
// ErrKeyNotExist is returned when the batch lookup has reached
// the end of all possible results, even when partial results
// are returned. It should be used to evaluate when lookup is "done".
-func (m *Map) BatchLookupAndDelete(cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) {
+func (m *Map) BatchLookupAndDelete(cursor *MapBatchCursor, keysOut, valuesOut any, opts *BatchOptions) (int, error) {
n, err := m.batchLookup(sys.BPF_MAP_LOOKUP_AND_DELETE_BATCH, cursor, keysOut, valuesOut, opts)
if err != nil {
return n, fmt.Errorf("map batch lookup and delete: %w", err)
@@ -1185,7 +1280,7 @@ type MapBatchCursor struct {
opaque []byte
}
-func (m *Map) batchLookup(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) {
+func (m *Map) batchLookup(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut any, opts *BatchOptions) (int, error) {
if m.typ.hasPerCPUValue() {
return m.batchLookupPerCPU(cmd, cursor, keysOut, valuesOut, opts)
}
@@ -1214,23 +1309,24 @@ func (m *Map) batchLookup(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOu
return n, sysErr
}
-func (m *Map) batchLookupPerCPU(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) {
+func (m *Map) batchLookupPerCPU(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut any, opts *BatchOptions) (int, error) {
count, err := sliceLen(keysOut)
if err != nil {
return 0, fmt.Errorf("keys: %w", err)
}
- valueBuf := make([]byte, count*int(m.fullValueSize))
- valuePtr := sys.UnsafeSlicePointer(valueBuf)
+ valueBuf := sysenc.SyscallOutput(valuesOut, count*int(m.fullValueSize))
- n, sysErr := m.batchLookupCmd(cmd, cursor, count, keysOut, valuePtr, opts)
+ n, sysErr := m.batchLookupCmd(cmd, cursor, count, keysOut, valueBuf.Pointer(), opts)
if sysErr != nil && !errors.Is(sysErr, unix.ENOENT) {
return 0, sysErr
}
- err = unmarshalBatchPerCPUValue(valuesOut, count, int(m.valueSize), valueBuf)
- if err != nil {
- return 0, err
+ if bytesBuf := valueBuf.Bytes(); bytesBuf != nil {
+ err = unmarshalBatchPerCPUValue(valuesOut, count, int(m.valueSize), bytesBuf)
+ if err != nil {
+ return 0, err
+ }
}
return n, sysErr
@@ -1259,10 +1355,6 @@ func (m *Map) batchLookupCmd(cmd sys.Cmd, cursor *MapBatchCursor, count int, key
return 0, errors.New("a cursor may not be reused across maps")
}
- if err := haveBatchAPI(); err != nil {
- return 0, err
- }
-
keyBuf := sysenc.SyscallOutput(keysOut, count*int(m.keySize))
attr := sys.MapLookupBatchAttr{
@@ -1282,6 +1374,9 @@ func (m *Map) batchLookupCmd(cmd sys.Cmd, cursor *MapBatchCursor, count int, key
_, sysErr := sys.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
sysErr = wrapMapError(sysErr)
if sysErr != nil && !errors.Is(sysErr, unix.ENOENT) {
+ if haveFeatErr := haveBatchAPI(); haveFeatErr != nil {
+ return 0, haveFeatErr
+ }
return 0, sysErr
}
@@ -1296,7 +1391,7 @@ func (m *Map) batchLookupCmd(cmd sys.Cmd, cursor *MapBatchCursor, count int, key
// simultaneously.
// "keys" and "values" must be of type slice, a pointer
// to a slice or buffer will not work.
-func (m *Map) BatchUpdate(keys, values interface{}, opts *BatchOptions) (int, error) {
+func (m *Map) BatchUpdate(keys, values any, opts *BatchOptions) (int, error) {
if m.typ.hasPerCPUValue() {
return m.batchUpdatePerCPU(keys, values, opts)
}
@@ -1358,7 +1453,7 @@ func (m *Map) batchUpdatePerCPU(keys, values any, opts *BatchOptions) (int, erro
// BatchDelete batch deletes entries in the map by keys.
// "keys" must be of type slice, a pointer to a slice or buffer will not work.
-func (m *Map) BatchDelete(keys interface{}, opts *BatchOptions) (int, error) {
+func (m *Map) BatchDelete(keys any, opts *BatchOptions) (int, error) {
count, err := sliceLen(keys)
if err != nil {
return 0, fmt.Errorf("keys: %w", err)
@@ -1538,7 +1633,7 @@ func (m *Map) finalize(spec *MapSpec) error {
return nil
}
-func (m *Map) marshalKey(data interface{}) (sys.Pointer, error) {
+func (m *Map) marshalKey(data any) (sys.Pointer, error) {
if data == nil {
if m.keySize == 0 {
// Queues have a key length of zero, so passing nil here is valid.
@@ -1550,7 +1645,7 @@ func (m *Map) marshalKey(data interface{}) (sys.Pointer, error) {
return marshalMapSyscallInput(data, int(m.keySize))
}
-func (m *Map) marshalValue(data interface{}) (sys.Pointer, error) {
+func (m *Map) marshalValue(data any) (sys.Pointer, error) {
var (
buf []byte
err error
@@ -1709,7 +1804,7 @@ func newMapIterator(target *Map) *MapIterator {
// the result of Err afterwards.
//
// See Map.Get for further caveats around valueOut.
-func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool {
+func (mi *MapIterator) Next(keyOut, valueOut any) bool {
if mi.err != nil || mi.done {
return false
}
diff --git a/vendor/github.com/cilium/ebpf/marshalers.go b/vendor/github.com/cilium/ebpf/marshalers.go
index d4e719c60..2542f2d0a 100644
--- a/vendor/github.com/cilium/ebpf/marshalers.go
+++ b/vendor/github.com/cilium/ebpf/marshalers.go
@@ -62,7 +62,7 @@ func appendPerCPUSlice(buf []byte, slice any, possibleCPUs, elemLength, alignedE
// Grow increases the slice's capacity, _if_necessary_
buf = slices.Grow(buf, alignedElemLength*possibleCPUs)
- for i := 0; i < sliceLen; i++ {
+ for i := range sliceLen {
elem := sliceValue.Index(i).Interface()
elemBytes, err := sysenc.Marshal(elem, elemLength)
if err != nil {
@@ -118,7 +118,7 @@ func marshalBatchPerCPUValue(slice any, batchLen, elemLength int) ([]byte, error
}
alignedElemLength := internal.Align(elemLength, 8)
buf := make([]byte, 0, batchLen*alignedElemLength*possibleCPUs)
- for i := 0; i < batchLen; i++ {
+ for i := range batchLen {
batch := sliceValue.Slice(i*possibleCPUs, (i+1)*possibleCPUs).Interface()
buf, err = appendPerCPUSlice(buf, batch, possibleCPUs, elemLength, alignedElemLength)
if err != nil {
@@ -150,9 +150,9 @@ func unmarshalPerCPUValue(slice any, elemLength int, buf []byte) error {
}
sliceElemType := sliceType.Elem()
- sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr
+ sliceElemIsPointer := sliceElemType.Kind() == reflect.Pointer
stride := internal.Align(elemLength, 8)
- for i := 0; i < possibleCPUs; i++ {
+ for i := range possibleCPUs {
var elem any
v := sliceValue.Index(i)
if sliceElemIsPointer {
@@ -199,7 +199,7 @@ func unmarshalBatchPerCPUValue(slice any, batchLen, elemLength int, buf []byte)
len(buf), batchLen*fullValueSize)
}
- for i := 0; i < batchLen; i++ {
+ for i := range batchLen {
elem := sliceValue.Slice(i*possibleCPUs, (i+1)*possibleCPUs).Interface()
if err := unmarshalPerCPUValue(elem, elemLength, buf[:fullValueSize]); err != nil {
return fmt.Errorf("batch %d: %w", i, err)
diff --git a/vendor/github.com/cilium/ebpf/memory.go b/vendor/github.com/cilium/ebpf/memory.go
index 3475fb07b..a31fae346 100644
--- a/vendor/github.com/cilium/ebpf/memory.go
+++ b/vendor/github.com/cilium/ebpf/memory.go
@@ -38,6 +38,8 @@ type Memory struct {
b []byte
ro bool
heap bool
+
+ cleanup runtime.Cleanup
}
func newMemory(fd, size int) (*Memory, error) {
@@ -62,26 +64,29 @@ func newMemory(fd, size int) (*Memory, error) {
return nil, fmt.Errorf("setting up memory-mapped region: %w", err)
}
- mm := &Memory{
- b,
- ro,
- false,
- }
- runtime.SetFinalizer(mm, (*Memory).close)
+ mm := &Memory{b: b, ro: ro, heap: false}
+ mm.cleanup = runtime.AddCleanup(mm, memoryCleanupFunc(), b)
return mm, nil
}
-func (mm *Memory) close() {
- if err := unix.Munmap(mm.b); err != nil {
- panic(fmt.Errorf("unmapping memory: %w", err))
+func memoryCleanupFunc() func([]byte) {
+ return func(b []byte) {
+ if err := unix.Munmap(b); err != nil {
+ panic(fmt.Errorf("unmapping memory: %w", err))
+ }
}
+}
+
+func (mm *Memory) close() {
+ mm.cleanup.Stop()
+ memoryCleanupFunc()(mm.b)
mm.b = nil
}
// Size returns the size of the memory-mapped region in bytes.
-func (mm *Memory) Size() int {
- return len(mm.b)
+func (mm *Memory) Size() uint32 {
+ return uint32(len(mm.b))
}
// ReadOnly returns true if the memory-mapped region is read-only.
@@ -90,11 +95,11 @@ func (mm *Memory) ReadOnly() bool {
}
// bounds returns true if an access at off of the given size is within bounds.
-func (mm *Memory) bounds(off uint64, size uint64) bool {
+func (mm *Memory) bounds(off, size uint32) bool {
if off+size < off {
return false
}
- return off+size <= uint64(len(mm.b))
+ return off+size <= uint32(len(mm.b))
}
// ReadAt implements [io.ReaderAt]. Useful for creating a new [io.OffsetWriter].
diff --git a/vendor/github.com/cilium/ebpf/memory_unsafe.go b/vendor/github.com/cilium/ebpf/memory_unsafe.go
index cc254397f..7e00b9e68 100644
--- a/vendor/github.com/cilium/ebpf/memory_unsafe.go
+++ b/vendor/github.com/cilium/ebpf/memory_unsafe.go
@@ -87,6 +87,7 @@ func newUnsafeMemory(fd, size int) (*Memory, error) {
unsafe.Slice((*byte)(alloc), size),
ro,
true,
+ runtime.Cleanup{},
}
return mm, nil
@@ -117,13 +118,10 @@ func allocate(size int) (unsafe.Pointer, error) {
// Allocate a new slice and store a pointer to its backing array.
alloc := unsafe.Pointer(unsafe.SliceData(make([]byte, size)))
- // nolint:govet
- //
// Align the pointer to a page boundary within the allocation. This may alias
- // the initial pointer if it was already page-aligned. Ignore govet warnings
- // since we're calling [runtime.KeepAlive] on the original Go memory.
- aligned := unsafe.Pointer(internal.Align(uintptr(alloc), uintptr(os.Getpagesize())))
- runtime.KeepAlive(alloc)
+ // the initial pointer if it was already page-aligned.
+ aligned := internal.Align(uintptr(alloc), uintptr(os.Getpagesize()))
+ alloc = unsafe.Add(alloc, aligned-uintptr(alloc))
// Return an aligned pointer into the backing array, losing the original
// reference. The runtime.SetFinalizer docs specify that its argument 'must be
@@ -141,7 +139,7 @@ func allocate(size int) (unsafe.Pointer, error) {
// pointer separately, which severely complicates finalizer setup and makes it
// prone to human error. For now, just bump the pointer and treat it as the
// new and only reference to the backing array.
- return aligned, nil
+ return alloc, nil
}
// mapmap memory-maps the given file descriptor at the given address and sets a
@@ -219,7 +217,7 @@ func unmap(size int) func(*byte) {
// The comparable constraint narrows down the set of eligible types to exclude
// slices, maps and functions. These complex types cannot be mapped to memory
// directly.
-func checkUnsafeMemory[T comparable](mm *Memory, off uint64) error {
+func checkUnsafeMemory[T comparable](mm *Memory, off uint32) error {
if mm.b == nil {
return fmt.Errorf("memory-mapped region is nil")
}
@@ -240,12 +238,12 @@ func checkUnsafeMemory[T comparable](mm *Memory, off uint64) error {
return fmt.Errorf("zero-sized type %s: %w", t, ErrInvalidType)
}
- if off%uint64(t.Align()) != 0 {
+ if off%uint32(t.Align()) != 0 {
return fmt.Errorf("unaligned access of memory-mapped region: %d-byte aligned read at offset %d", t.Align(), off)
}
- vs, bs := uint64(size), uint64(len(mm.b))
- if off+vs > bs {
+ vs, bs := uint32(size), uint32(len(mm.b))
+ if !mm.bounds(off, vs) {
return fmt.Errorf("%d-byte value at offset %d exceeds mmap size of %d bytes", vs, off, bs)
}
@@ -334,7 +332,7 @@ func checkType(name string, t reflect.Type) error {
// must be within bounds of the Memory.
//
// To access read-only memory, use [Memory.ReadAt].
-func memoryPointer[T comparable](mm *Memory, off uint64) (*T, error) {
+func memoryPointer[T comparable](mm *Memory, off uint32) (*T, error) {
if err := checkUnsafeMemory[T](mm, off); err != nil {
return nil, fmt.Errorf("memory pointer: %w", err)
}
diff --git a/vendor/github.com/cilium/ebpf/prog.go b/vendor/github.com/cilium/ebpf/prog.go
index 560c045ad..9d120ab6f 100644
--- a/vendor/github.com/cilium/ebpf/prog.go
+++ b/vendor/github.com/cilium/ebpf/prog.go
@@ -8,7 +8,8 @@ import (
"math"
"path/filepath"
"runtime"
- "sort"
+ "slices"
+ "strings"
"time"
"github.com/cilium/ebpf/asm"
@@ -24,6 +25,10 @@ import (
// ErrNotSupported is returned whenever the kernel doesn't support a feature.
var ErrNotSupported = internal.ErrNotSupported
+// ErrProgIncompatible is returned when a loaded Program is incompatible with a
+// given spec.
+var ErrProgIncompatible = errors.New("program is incompatible")
+
// errBadRelocation is returned when the verifier rejects a program due to a
// bad CO-RE relocation.
//
@@ -95,13 +100,10 @@ type ProgramOptions struct {
// use the kernel BTF from a well-known location if nil.
KernelTypes *btf.Spec
- // Type information used for CO-RE relocations of kernel modules,
- // indexed by module name.
- //
- // This is useful in environments where the kernel BTF is not available
- // (containers) or where it is in a non-standard location. Defaults to
- // use the kernel module BTF from a well-known location if nil.
- KernelModuleTypes map[string]*btf.Spec
+ // Additional targets to consider for CO-RE relocations. This can be used to
+ // pass BTF information for kernel modules when it's not present on
+ // KernelTypes.
+ ExtraRelocationTargets []*btf.Spec
}
// ProgramSpec defines a Program.
@@ -114,6 +116,13 @@ type ProgramSpec struct {
// Type determines at which hook in the kernel a program will run.
Type ProgramType
+ // Network interface index the user intends to attach this program to after
+ // loading. Only valid for some program types.
+ //
+ // Provides driver-specific context about the target interface to the
+ // verifier, required when using certain BPF helpers.
+ Ifindex uint32
+
// AttachType of the program, needed to differentiate allowed context
// accesses in some newer program types like CGroupSockAddr.
//
@@ -167,10 +176,34 @@ func (ps *ProgramSpec) Copy() *ProgramSpec {
// Tag calculates the kernel tag for a series of instructions.
//
// Use asm.Instructions.Tag if you need to calculate for non-native endianness.
+//
+// Deprecated: The value produced by this method no longer matches tags produced
+// by the kernel since Linux 6.18. Use [ProgramSpec.Compatible] instead.
func (ps *ProgramSpec) Tag() (string, error) {
return ps.Instructions.Tag(internal.NativeEndian)
}
+// Compatible returns nil if a loaded Program's kernel tag matches the one of
+// the ProgramSpec.
+//
+// Returns [ErrProgIncompatible] if the tags do not match.
+func (ps *ProgramSpec) Compatible(info *ProgramInfo) error {
+ if platform.IsWindows {
+ return fmt.Errorf("%w: Windows does not support tag readback from kernel", internal.ErrNotSupportedOnOS)
+ }
+
+ ok, err := ps.Instructions.HasTag(info.Tag, internal.NativeEndian)
+ if err != nil {
+ return err
+ }
+
+ if !ok {
+ return fmt.Errorf("%w: ProgramSpec and Program tags do not match", ErrProgIncompatible)
+ }
+
+ return nil
+}
+
// targetsKernelModule returns true if the program supports being attached to a
// symbol provided by a kernel module.
func (ps *ProgramSpec) targetsKernelModule() bool {
@@ -209,6 +242,8 @@ type Program struct {
name string
pinnedPath string
typ ProgramType
+
+ btf *btf.Handle
}
// NewProgram creates a new Program.
@@ -233,7 +268,7 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er
return nil, errors.New("can't load a program from a nil spec")
}
- prog, err := newProgramWithOptions(spec, opts, newBTFCache(&opts))
+ prog, err := newProgramWithOptions(spec, opts, btf.NewCache())
if errors.Is(err, asm.ErrUnsatisfiedMapReference) {
return nil, fmt.Errorf("cannot load program without loading its whole collection: %w", err)
}
@@ -241,15 +276,15 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er
}
var (
- coreBadLoad = []byte(fmt.Sprintf("(18) r10 = 0x%x\n", btf.COREBadRelocationSentinel))
+ coreBadLoad = fmt.Appendf(nil, "(18) r10 = 0x%x\n", btf.COREBadRelocationSentinel)
// This log message was introduced by ebb676daa1a3 ("bpf: Print function name in
// addition to function id") which first appeared in v4.10 and has remained
// unchanged since.
- coreBadCall = []byte(fmt.Sprintf("invalid func unknown#%d\n", btf.COREBadRelocationSentinel))
- kfuncBadCall = []byte(fmt.Sprintf("invalid func unknown#%d\n", kfuncCallPoisonBase))
+ coreBadCall = fmt.Appendf(nil, "invalid func unknown#%d\n", btf.COREBadRelocationSentinel)
+ kfuncBadCall = fmt.Appendf(nil, "invalid func unknown#%d\n", kfuncCallPoisonBase)
)
-func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache) (*Program, error) {
+func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache) (result *Program, _ error) {
if len(spec.Instructions) == 0 {
return nil, errors.New("instructions cannot be empty")
}
@@ -284,6 +319,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
ProgName: maybeFillObjName(spec.Name),
ProgType: sys.ProgType(progType),
ProgFlags: spec.Flags,
+ ProgIfindex: spec.Ifindex,
ExpectedAttachType: sys.AttachType(spec.AttachType),
License: sys.NewStringPointer(spec.License),
KernVersion: kv,
@@ -293,7 +329,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
copy(insns, spec.Instructions)
var b btf.Builder
- if err := applyRelocations(insns, spec.ByteOrder, &b, c); err != nil {
+ if err := applyRelocations(insns, spec.ByteOrder, &b, c, opts.KernelTypes, opts.ExtraRelocationTargets); err != nil {
return nil, fmt.Errorf("apply CO-RE relocations: %w", err)
}
@@ -322,12 +358,18 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
attr.LineInfo = sys.SlicePointer(lib)
}
+ var handle *btf.Handle
if !b.Empty() {
- handle, err := btf.NewHandle(&b)
+ var err error
+ handle, err = btf.NewHandle(&b)
if err != nil {
return nil, fmt.Errorf("load BTF: %w", err)
}
- defer handle.Close()
+ defer func() {
+ if result == nil || result.btf != handle {
+ handle.Close()
+ }
+ }()
attr.ProgBtfFd = uint32(handle.FD())
}
@@ -346,7 +388,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
return nil, err
}
- handles, err := fixupKfuncs(insns)
+ handles, err := fixupKfuncs(insns, c)
if err != nil {
return nil, fmt.Errorf("fixing up kfuncs: %w", err)
}
@@ -377,15 +419,51 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
attr.AttachBtfObjFd = uint32(spec.AttachTarget.FD())
defer runtime.KeepAlive(spec.AttachTarget)
} else if spec.AttachTo != "" {
- module, targetID, err := findProgramTargetInKernel(spec.AttachTo, spec.Type, spec.AttachType)
+ var targetMember string
+ attachTo := spec.AttachTo
+
+ if spec.Type == StructOps {
+ attachTo, targetMember, _ = strings.Cut(attachTo, ":")
+ if targetMember == "" {
+ return nil, fmt.Errorf("struct_ops: AttachTo must be ':' (got %s)", spec.AttachTo)
+ }
+ }
+
+ module, targetID, err := findProgramTargetInKernel(attachTo, spec.Type, spec.AttachType, c)
if err != nil && !errors.Is(err, errUnrecognizedAttachType) {
// We ignore errUnrecognizedAttachType since AttachTo may be non-empty
// for programs that don't attach anywhere.
return nil, fmt.Errorf("attach %s/%s: %w", spec.Type, spec.AttachType, err)
}
+ if spec.Type == StructOps {
+ var s *btf.Spec
+
+ target := btf.Type((*btf.Struct)(nil))
+ s, module, err = findTargetInKernel(attachTo, &target, c)
+ if err != nil {
+ return nil, fmt.Errorf("lookup struct_ops kern type %q: %w", attachTo, err)
+ }
+ kType := target.(*btf.Struct)
+
+ targetID, err = s.TypeID(kType)
+ if err != nil {
+ return nil, fmt.Errorf("type id for %s: %w", kType.TypeName(), err)
+ }
+
+ idx := slices.IndexFunc(kType.Members, func(m btf.Member) bool {
+ return m.Name == targetMember
+ })
+ if idx < 0 {
+ return nil, fmt.Errorf("member %q not found in %s", targetMember, kType.Name)
+ }
+
+ // ExpectedAttachType: index of the target member in the struct
+ attr.ExpectedAttachType = sys.AttachType(idx)
+ }
+
attr.AttachBtfId = targetID
- if module != nil {
+ if module != nil && attr.AttachBtfObjFd == 0 {
attr.AttachBtfObjFd = uint32(module.FD())
defer module.Close()
}
@@ -401,7 +479,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
// Loading with logging disabled should never retry.
fd, err = sys.ProgLoad(attr)
if err == nil {
- return &Program{"", fd, spec.Name, "", spec.Type}, nil
+ return &Program{"", fd, spec.Name, "", spec.Type, handle}, nil
}
} else {
// Only specify log size if log level is also specified. Setting size
@@ -421,7 +499,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
fd, err = sys.ProgLoad(attr)
if err == nil {
- return &Program{unix.ByteSliceToString(logBuf), fd, spec.Name, "", spec.Type}, nil
+ return &Program{unix.ByteSliceToString(logBuf), fd, spec.Name, "", spec.Type, handle}, nil
}
if !retryLogAttrs(attr, opts.LogSizeStart, err) {
@@ -435,6 +513,10 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
}
}
+ if errors.Is(err, sys.ErrTokenCapabilities) {
+ return nil, fmt.Errorf("load program: %w", err)
+ }
+
end := bytes.IndexByte(logBuf, 0)
if end < 0 {
end = len(logBuf)
@@ -562,7 +644,7 @@ func newProgramFromFD(fd *sys.FD) (*Program, error) {
return nil, fmt.Errorf("discover program type: %w", err)
}
- return &Program{"", fd, info.Name, "", info.Type}, nil
+ return &Program{"", fd, info.Name, "", info.Type, nil}, nil
}
func (p *Program) String() string {
@@ -597,6 +679,10 @@ func (p *Program) Stats() (*ProgramStats, error) {
// Returns ErrNotSupported if the kernel has no BTF support, or if there is no
// BTF associated with the program.
func (p *Program) Handle() (*btf.Handle, error) {
+ if p.btf != nil {
+ return p.btf.Clone()
+ }
+
info, err := p.Info()
if err != nil {
return nil, err
@@ -610,6 +696,48 @@ func (p *Program) Handle() (*btf.Handle, error) {
return btf.NewHandleFromID(id)
}
+// SetHandle caches program's type information so that subsequent
+// Handle() call doesn't need to query it from the kernel (requires
+// CAP_SYS_ADMIN).
+//
+// The caller remains responsible for closing the btf.Handle; SetHandle
+// makes a private copy.
+//
+// Type info is auto-cached for Programs loaded by the library itself.
+// Use SetHandle() with program references obtained via NewProgramFromFD
+// and LoadPinnedProgram.
+func (p *Program) SetHandle(h *btf.Handle) error {
+ if h == nil {
+ return fmt.Errorf("nil BTF handle")
+ }
+
+ info, err := p.Info()
+ if err != nil {
+ return err
+ }
+
+ handleInfo, err := h.Info()
+ if err != nil {
+ return err
+ }
+
+ if id, ok := info.BTFID(); !ok || id != handleInfo.ID {
+ return fmt.Errorf("program/BTF mismatch")
+ }
+
+ if p.btf != nil {
+ return nil
+ }
+
+ dup, err := h.Clone()
+ if err != nil {
+ return err
+ }
+
+ p.btf = dup
+ return nil
+}
+
// FD gets the file descriptor of the Program.
//
// It is invalid to call this function after Close has been called.
@@ -627,12 +755,18 @@ func (p *Program) Clone() (*Program, error) {
return nil, nil
}
+ btfClone, err := p.btf.Clone()
+ if err != nil {
+ return nil, fmt.Errorf("can't clone program: %w", err)
+ }
+
dup, err := p.fd.Dup()
if err != nil {
+ btfClone.Close()
return nil, fmt.Errorf("can't clone program: %w", err)
}
- return &Program{p.VerifierLog, dup, p.name, "", p.typ}, nil
+ return &Program{p.VerifierLog, dup, p.name, "", p.typ, btfClone}, nil
}
// Pin persists the Program on the BPF virtual file system past the lifetime of
@@ -677,6 +811,10 @@ func (p *Program) Close() error {
return nil
}
+ if err := p.btf.Close(); err != nil {
+ return err
+ }
+
return p.fd.Close()
}
@@ -690,9 +828,9 @@ type RunOptions struct {
// Program's data after Program has run. Caller must allocate. Optional field.
DataOut []byte
// Program's context input. Optional field.
- Context interface{}
+ Context any
// Program's context after Program has run. Must be a pointer or slice. Optional field.
- ContextOut interface{}
+ ContextOut any
// Minimum number of times to run Program. Optional field. Defaults to 1.
//
// The program may be executed more often than this due to interruptions, e.g.
@@ -703,6 +841,11 @@ type RunOptions struct {
// CPU to run Program on. Optional field.
// Note not all program types support this field.
CPU uint32
+ // BatchSize (default 64) affects the kernel's packet buffer allocation behaviour when running
+ // programs with BPF_F_TEST_XDP_LIVE_FRAMES and a non-zero [RunOptions.Repeat] value.
+ // For more details, see the kernel documentation on BPF_PROG_RUN:
+ // https://docs.kernel.org/bpf/bpf_prog_run.html#running-xdp-programs-in-live-frame-mode
+ BatchSize uint32
// Called whenever the syscall is interrupted, and should be set to testing.B.ResetTimer
// or similar. Typically used during benchmarking. Optional field.
//
@@ -851,6 +994,9 @@ func (p *Program) run(opts *RunOptions) (uint32, time.Duration, error) {
var ctxOut []byte
if opts.ContextOut != nil {
ctxOut = make([]byte, binary.Size(opts.ContextOut))
+ } else if platform.IsWindows && len(ctxIn) > 0 {
+ // Windows rejects a non-zero ctxIn with a nil ctxOut.
+ ctxOut = make([]byte, len(ctxIn))
}
attr := sys.ProgRunAttr{
@@ -866,6 +1012,17 @@ func (p *Program) run(opts *RunOptions) (uint32, time.Duration, error) {
CtxOut: sys.SlicePointer(ctxOut),
Flags: opts.Flags,
Cpu: opts.CPU,
+ BatchSize: opts.BatchSize,
+ }
+
+ if p.Type() == Syscall && ctxIn != nil && ctxOut != nil {
+ // Linux syscall program errors on non-nil ctxOut, uses ctxIn
+ // for both input and output. Shield the user from this wart.
+ if len(ctxIn) != len(ctxOut) {
+ return 0, 0, errors.New("length mismatch: Context and ContextOut")
+ }
+ attr.CtxOut, attr.CtxSizeOut = sys.TypedPointer[uint8]{}, 0
+ ctxOut = ctxIn
}
retry:
@@ -914,7 +1071,7 @@ retry:
opts.DataOut = opts.DataOut[:int(attr.DataSizeOut)]
}
- if len(ctxOut) != 0 {
+ if opts.ContextOut != nil {
b := bytes.NewReader(ctxOut)
if err := binary.Read(b, internal.NativeEndian, opts.ContextOut); err != nil {
return 0, 0, fmt.Errorf("failed to decode ContextOut: %v", err)
@@ -968,20 +1125,16 @@ func LoadPinnedProgram(fileName string, opts *LoadPinOptions) (*Program, error)
return nil, fmt.Errorf("%s is not a Program", fileName)
}
- info, err := newProgramInfoFromFd(fd)
- if err != nil {
- _ = fd.Close()
- return nil, fmt.Errorf("info for %s: %w", fileName, err)
- }
+ p, err := newProgramFromFD(fd)
+ if err == nil {
+ p.pinnedPath = fileName
- var progName string
- if haveObjName() == nil {
- progName = info.Name
- } else {
- progName = filepath.Base(fileName)
+ if haveObjName() != nil {
+ p.name = filepath.Base(fileName)
+ }
}
- return &Program{"", fd, progName, fileName, info.Type}, nil
+ return p, err
}
// ProgramGetNextID returns the ID of the next eBPF program.
@@ -1017,7 +1170,7 @@ var errUnrecognizedAttachType = errors.New("unrecognized attach type")
//
// Returns errUnrecognizedAttachType if the combination of progType and attachType
// is not recognised.
-func findProgramTargetInKernel(name string, progType ProgramType, attachType AttachType) (*btf.Handle, btf.TypeID, error) {
+func findProgramTargetInKernel(name string, progType ProgramType, attachType AttachType, cache *btf.Cache) (*btf.Handle, btf.TypeID, error) {
type match struct {
p ProgramType
a AttachType
@@ -1029,6 +1182,10 @@ func findProgramTargetInKernel(name string, progType ProgramType, attachType Att
)
switch (match{progType, attachType}) {
+ case match{StructOps, AttachStructOps}:
+ typeName = name
+ featureName = "struct_ops " + name
+ target = (*btf.Struct)(nil)
case match{LSM, AttachLSMMac}:
typeName = "bpf_lsm_" + name
featureName = name + " LSM hook"
@@ -1057,12 +1214,7 @@ func findProgramTargetInKernel(name string, progType ProgramType, attachType Att
return nil, 0, errUnrecognizedAttachType
}
- spec, err := btf.LoadKernelSpec()
- if err != nil {
- return nil, 0, fmt.Errorf("load kernel spec: %w", err)
- }
-
- spec, module, err := findTargetInKernel(spec, typeName, &target)
+ spec, module, err := findTargetInKernel(typeName, &target, cache)
if errors.Is(err, btf.ErrNotFound) {
return nil, 0, &internal.UnsupportedFeatureError{Name: featureName}
}
@@ -1090,17 +1242,24 @@ func findProgramTargetInKernel(name string, progType ProgramType, attachType Att
// target will point at the found type after a successful call. Searches both
// vmlinux and any loaded modules.
//
-// Returns a non-nil handle if the type was found in a module, or btf.ErrNotFound
-// if the type wasn't found at all.
-func findTargetInKernel(kernelSpec *btf.Spec, typeName string, target *btf.Type) (*btf.Spec, *btf.Handle, error) {
- if kernelSpec == nil {
- return nil, nil, fmt.Errorf("nil kernelSpec: %w", btf.ErrNotFound)
+// Returns a non-nil handle if the type was found in a module, [btf.ErrNotFound]
+// if the type wasn't found or if BTF is not enabled.
+func findTargetInKernel(typeName string, target *btf.Type, cache *btf.Cache) (*btf.Spec, *btf.Handle, error) {
+ kernelSpec, err := cache.Kernel()
+ if err != nil {
+ return nil, nil, fmt.Errorf("load kernel spec: %w (%w)", btf.ErrNotFound, err)
}
- err := kernelSpec.TypeByName(typeName, target)
+ err = kernelSpec.TypeByName(typeName, target)
if errors.Is(err, btf.ErrNotFound) {
- spec, module, err := findTargetInModule(kernelSpec, typeName, target)
+ spec, module, err := findTargetInModule(typeName, target, cache)
if err != nil {
+ // EPERM may be returned when we do not have CAP_SYS_ADMIN.
+ // Wrap error with btf.ErrNotFound so callers can handle it accordingly.
+ if errors.Is(err, unix.EPERM) {
+ return spec, nil, fmt.Errorf("find target in modules: %w (%w)", btf.ErrNotFound, err)
+ }
+
return nil, nil, fmt.Errorf("find target in modules: %w", err)
}
return spec, module, nil
@@ -1117,7 +1276,7 @@ func findTargetInKernel(kernelSpec *btf.Spec, typeName string, target *btf.Type)
// are searched in the order they were loaded.
//
// Returns btf.ErrNotFound if the target can't be found in any module.
-func findTargetInModule(base *btf.Spec, typeName string, target *btf.Type) (*btf.Spec, *btf.Handle, error) {
+func findTargetInModule(typeName string, target *btf.Type, cache *btf.Cache) (*btf.Spec, *btf.Handle, error) {
it := new(btf.HandleIterator)
defer it.Handle.Close()
@@ -1131,7 +1290,7 @@ func findTargetInModule(base *btf.Spec, typeName string, target *btf.Type) (*btf
continue
}
- spec, err := it.Handle.Spec(base)
+ spec, err := cache.Module(info.Name)
if err != nil {
return nil, nil, fmt.Errorf("parse types for module %s: %w", info.Name, err)
}
@@ -1191,19 +1350,3 @@ func findTargetInProgram(prog *Program, name string, progType ProgramType, attac
return spec.TypeID(targetFunc)
}
-
-func newBTFCache(opts *ProgramOptions) *btf.Cache {
- c := btf.NewCache()
- if opts.KernelTypes != nil {
- c.KernelTypes = opts.KernelTypes
- c.ModuleTypes = opts.KernelModuleTypes
- if opts.KernelModuleTypes != nil {
- c.LoadedModules = []string{}
- for name := range opts.KernelModuleTypes {
- c.LoadedModules = append(c.LoadedModules, name)
- }
- sort.Strings(c.LoadedModules)
- }
- }
- return c
-}
diff --git a/vendor/github.com/cilium/ebpf/rlimit/rlimit_linux.go b/vendor/github.com/cilium/ebpf/rlimit/rlimit_linux.go
index b05699c5f..c8cada137 100644
--- a/vendor/github.com/cilium/ebpf/rlimit/rlimit_linux.go
+++ b/vendor/github.com/cilium/ebpf/rlimit/rlimit_linux.go
@@ -76,6 +76,12 @@ func detectMemcgAccounting() error {
return nil
}
+ if errors.Is(mapErr, sys.ErrTokenCapabilities) {
+ // If BPF tokens are in use and array maps are not permitted, assume memcg
+ // accounting is supported since tokens are a newer feature.
+ return nil
+ }
+
// EPERM shows up when map creation would exceed the memory budget.
if errors.Is(mapErr, unix.EPERM) {
return unsupportedMemcgAccounting
diff --git a/vendor/github.com/cilium/ebpf/struct_ops.go b/vendor/github.com/cilium/ebpf/struct_ops.go
new file mode 100644
index 000000000..78e4d35b2
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/struct_ops.go
@@ -0,0 +1,250 @@
+package ebpf
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/cilium/ebpf/btf"
+ "github.com/cilium/ebpf/internal"
+)
+
+const structOpsValuePrefix = "bpf_struct_ops_"
+const structOpsLinkSec = ".struct_ops.link"
+const structOpsSec = ".struct_ops"
+const structOpsKeySize = 4
+
+// structOpsMemberLayout describes the location and type of a struct_ops member.
+type structOpsMemberLayout struct {
+ member btf.Member
+ off int
+ size int
+ typ btf.Type
+}
+
+// newStructOpsMemberLayout returns a layout information from a struct_ops member.
+func newStructOpsMemberLayout(m btf.Member) (*structOpsMemberLayout, error) {
+ if m.BitfieldSize > 0 {
+ return nil, fmt.Errorf("bitfield %s not supported", m.Name)
+ }
+
+ size, err := btf.Sizeof(m.Type)
+ if err != nil {
+ return nil, fmt.Errorf("sizeof(%s): %w", m.Name, err)
+ }
+
+ off := int(m.Offset.Bytes())
+ if off < 0 {
+ return nil, fmt.Errorf("member %q: invalid offset", m.Name)
+ }
+
+ return &structOpsMemberLayout{
+ member: m,
+ off: off,
+ size: size,
+ typ: btf.UnderlyingType(m.Type),
+ }, nil
+}
+
+// bytes returns the portion of `buf` corresponding to the member.
+func (ml *structOpsMemberLayout) bytes(buf []byte) ([]byte, error) {
+ if ml.off < 0 || ml.off+ml.size > len(buf) {
+ return nil, fmt.Errorf("member %q: value buffer too small", ml.member.Name)
+ }
+ return buf[ml.off : ml.off+ml.size], nil
+}
+
+// structOpsFuncPtrMember returns an error unless m is a func pointer member.
+func structOpsFuncPtrMember(m btf.Member) error {
+ kmPtr, ok := btf.As[*btf.Pointer](m.Type)
+ if !ok {
+ return fmt.Errorf("member %s is not a func pointer", m.Name)
+ }
+ if _, isFuncProto := btf.As[*btf.FuncProto](kmPtr.Target); !isFuncProto {
+ return fmt.Errorf("member %s is not a func pointer", m.Name)
+ }
+ return nil
+}
+
+// structOpsFindInnerType returns the "inner" struct inside a value struct_ops type.
+//
+// Given a value like:
+//
+// struct bpf_struct_ops_bpf_testmod_ops {
+// struct bpf_struct_ops_common common;
+// struct bpf_testmod_ops data;
+// };
+//
+// this function returns the *btf.Struct for "bpf_testmod_ops" along with the
+// byte offset of the "data" member inside the value type.
+//
+// The inner struct name is derived by trimming the "bpf_struct_ops_" prefix
+// from the value's name.
+func structOpsFindInnerType(vType *btf.Struct) (*btf.Struct, uint32, error) {
+ innerName := strings.TrimPrefix(vType.Name, structOpsValuePrefix)
+
+ for _, m := range vType.Members {
+ if st, ok := btf.As[*btf.Struct](m.Type); ok && st.Name == innerName {
+ return st, m.Offset.Bytes(), nil
+ }
+ }
+
+ return nil, 0, fmt.Errorf("inner struct %q not found in %s", innerName, vType.Name)
+}
+
+// structOpsFindTarget resolves the kernel-side "value struct" for a struct_ops map.
+func structOpsFindTarget(userType *btf.Struct, cache *btf.Cache) (vType *btf.Struct, id btf.TypeID, module *btf.Handle, err error) {
+ // the kernel value type name, e.g. "bpf_struct_ops_"
+ vTypeName := structOpsValuePrefix + userType.Name
+
+ target := btf.Type((*btf.Struct)(nil))
+ spec, module, err := findTargetInKernel(vTypeName, &target, cache)
+ if errors.Is(err, btf.ErrNotFound) {
+ return nil, 0, nil, fmt.Errorf("%q doesn't exist in kernel: %w", vTypeName, ErrNotSupported)
+ }
+ if err != nil {
+ return nil, 0, nil, fmt.Errorf("lookup value type %q: %w", vTypeName, err)
+ }
+
+ id, err = spec.TypeID(target)
+ if err != nil {
+ return nil, 0, nil, err
+ }
+
+ return target.(*btf.Struct), id, module, nil
+}
+
+// structOpsPopulateValue writes a `prog FD` which references to `p` into the
+// struct_ops value buffer `kernVData` at byte offset `dstOff` corresponding to
+// the member `km`.
+func structOpsPopulateValue(km btf.Member, kernVData []byte, p *Program) error {
+ if err := structOpsFuncPtrMember(km); err != nil {
+ return err
+ }
+
+ layout, err := newStructOpsMemberLayout(km)
+ if err != nil {
+ return err
+ }
+
+ dst, err := layout.bytes(kernVData)
+ if err != nil || len(dst) != 8 {
+ return fmt.Errorf("member %q: value buffer too small for func ptr", km.Name)
+ }
+
+ internal.NativeEndian.PutUint64(dst, uint64(p.FD()))
+ return nil
+}
+
+// structOpsValidateMemberPair checks whether `m` can be copied into `km`.
+func structOpsValidateMemberPair(m, km btf.Member) (int, error) {
+ mLayout, err := newStructOpsMemberLayout(m)
+ if err != nil {
+ return 0, fmt.Errorf("user member %s: %w", m.Name, err)
+ }
+
+ kLayout, err := newStructOpsMemberLayout(km)
+ if err != nil {
+ return 0, fmt.Errorf("kernel member %s: %w", km.Name, err)
+ }
+
+ if mLayout.size != kLayout.size {
+ return 0, fmt.Errorf("size mismatch for %s: user=%d kernel=%d", m.Name, mLayout.size, kLayout.size)
+ }
+
+ if reflect.TypeOf(mLayout.typ) != reflect.TypeOf(kLayout.typ) {
+ return 0, fmt.Errorf("unmatched member type %s != %s (kernel)", m.Name, km.Name)
+ }
+
+ return mLayout.size, nil
+}
+
+// structOpsCopyMemberBytes copies the bytes of `m` into `km`.
+func structOpsCopyMemberBytes(m, km btf.Member, data, kernVData []byte, size int) error {
+ mLayout, err := newStructOpsMemberLayout(m)
+ if err != nil {
+ return fmt.Errorf("user member %s: %w", m.Name, err)
+ }
+
+ kLayout, err := newStructOpsMemberLayout(km)
+ if err != nil {
+ return fmt.Errorf("kernel member %s: %w", km.Name, err)
+ }
+
+ if mLayout.size != size {
+ return fmt.Errorf("member %q: unexpected validated size %d, got %d", m.Name, size, mLayout.size)
+ }
+ if kLayout.size != size {
+ return fmt.Errorf("member %q: unexpected validated size %d, got %d", km.Name, size, kLayout.size)
+ }
+
+ src, err := mLayout.bytes(data)
+ if err != nil {
+ return fmt.Errorf("member %q: userdata is too small", m.Name)
+ }
+
+ dst, err := kLayout.bytes(kernVData)
+ if err != nil {
+ return fmt.Errorf("member %q: value type is too small", km.Name)
+ }
+
+ switch mLayout.typ.(type) {
+ case *btf.Struct, *btf.Union:
+ if !structOpsIsMemZeroed(src) {
+ return fmt.Errorf("non-zero nested struct %s: %w", m.Name, ErrNotSupported)
+ }
+ return nil
+ }
+
+ copy(dst, src)
+ return nil
+}
+
+// structOpsCopyMember copies `m` into `km`.
+func structOpsCopyMember(m, km btf.Member, data []byte, kernVData []byte) error {
+ size, err := structOpsValidateMemberPair(m, km)
+ if err != nil {
+ return err
+ }
+ return structOpsCopyMemberBytes(m, km, data, kernVData, size)
+}
+
+// structOpsIsMemZeroed() checks whether all bytes in data are zero.
+func structOpsIsMemZeroed(data []byte) bool {
+ for _, b := range data {
+ if b != 0 {
+ return false
+ }
+ }
+ return true
+}
+
+// structOpsSetAttachTo sets p.AttachTo in the expected "struct_name:memberName" format
+// based on the struct definition.
+//
+// this relies on the assumption that each member in the
+// `.struct_ops` section has a relocation at its starting byte offset.
+func structOpsSetAttachTo(
+ sec *elfSection,
+ baseOff uint32,
+ userSt *btf.Struct,
+ progs map[string]*ProgramSpec) error {
+ for _, m := range userSt.Members {
+ memberOff := m.Offset
+ sym, ok := sec.relocations[uint64(baseOff+memberOff.Bytes())]
+ if !ok {
+ continue
+ }
+ p, ok := progs[sym.Name]
+ if !ok || p == nil {
+ return fmt.Errorf("program %s not found", sym.Name)
+ }
+
+ if p.Type != StructOps {
+ return fmt.Errorf("program %s is not StructOps", sym.Name)
+ }
+ p.AttachTo = userSt.Name + ":" + m.Name
+ }
+ return nil
+}
diff --git a/vendor/github.com/cilium/ebpf/types.go b/vendor/github.com/cilium/ebpf/types.go
index 56e318208..f87715aae 100644
--- a/vendor/github.com/cilium/ebpf/types.go
+++ b/vendor/github.com/cilium/ebpf/types.go
@@ -5,7 +5,7 @@ import (
"github.com/cilium/ebpf/internal/sys"
)
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -output types_string.go -type=MapType,ProgramType,PinType
+//go:generate go tool stringer -output types_string.go -type=MapType,ProgramType,PinType
// MapType indicates the type map structure
// that will be initialized in the kernel.
@@ -144,7 +144,7 @@ func (mt MapType) hasPerCPUValue() bool {
// canStoreMapOrProgram returns true if the Map stores references to another Map
// or Program.
func (mt MapType) canStoreMapOrProgram() bool {
- return mt.canStoreMap() || mt.canStoreProgram()
+ return mt.canStoreMap() || mt.canStoreProgram() || mt == StructOpsMap
}
// canStoreMap returns true if the map type accepts a map fd
@@ -188,6 +188,16 @@ func (mt MapType) mustHaveNoPrealloc() bool {
return false
}
+// mustHaveZeroMaxEntries returns true if the map type requires MaxEntries to be zero.
+func (mt MapType) mustHaveZeroMaxEntries() bool {
+ switch mt {
+ case CgroupStorage, CGroupStorage, PerCPUCGroupStorage, InodeStorage, TaskStorage, SkStorage:
+ return true
+ }
+
+ return false
+}
+
// ProgramType of the eBPF program
type ProgramType uint32
@@ -252,7 +262,7 @@ func ProgramTypeForPlatform(plat string, value uint32) (ProgramType, error) {
// Will cause invalid argument (EINVAL) at program load time if set incorrectly.
type AttachType uint32
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -type AttachType -trimprefix Attach
+//go:generate go tool stringer -type AttachType -trimprefix Attach
// AttachNone is an alias for AttachCGroupInetIngress for readability reasons.
const AttachNone AttachType = 0
diff --git a/vendor/github.com/cilium/ebpf/types_string.go b/vendor/github.com/cilium/ebpf/types_string.go
index efcd6a7dd..94bc2e26c 100644
--- a/vendor/github.com/cilium/ebpf/types_string.go
+++ b/vendor/github.com/cilium/ebpf/types_string.go
@@ -162,8 +162,9 @@ const _PinType_name = "PinNonePinByName"
var _PinType_index = [...]uint8{0, 7, 16}
func (i PinType) String() string {
- if i >= PinType(len(_PinType_index)-1) {
+ idx := int(i) - 0
+ if i < 0 || idx >= len(_PinType_index)-1 {
return "PinType(" + strconv.FormatInt(int64(i), 10) + ")"
}
- return _PinType_name[_PinType_index[i]:_PinType_index[i+1]]
+ return _PinType_name[_PinType_index[idx]:_PinType_index[idx+1]]
}
diff --git a/vendor/github.com/cilium/ebpf/variable.go b/vendor/github.com/cilium/ebpf/variable.go
index c6fd55cba..51691bb4f 100644
--- a/vendor/github.com/cilium/ebpf/variable.go
+++ b/vendor/github.com/cilium/ebpf/variable.go
@@ -1,9 +1,11 @@
package ebpf
import (
+ "encoding/binary"
"fmt"
"io"
"reflect"
+ "slices"
"github.com/cilium/ebpf/btf"
"github.com/cilium/ebpf/internal/sysenc"
@@ -15,56 +17,52 @@ import (
// All operations on a VariableSpec's underlying MapSpec are performed in the
// host's native endianness.
type VariableSpec struct {
- name string
- offset uint64
- size uint64
-
- m *MapSpec
- t *btf.Var
+ Name string
+ // Name of the section this variable was allocated in.
+ SectionName string
+ // Offset of the variable within the datasec.
+ Offset uint32
+ // Byte representation of the variable's value.
+ Value []byte
+ // Type information of the variable. Optional.
+ Type *btf.Var
}
// Set sets the value of the VariableSpec to the provided input using the host's
// native endianness.
func (s *VariableSpec) Set(in any) error {
- buf, err := sysenc.Marshal(in, int(s.size))
- if err != nil {
- return fmt.Errorf("marshaling value %s: %w", s.name, err)
+ size := int(s.Size())
+ if size == 0 {
+ bs := binary.Size(in)
+ if bs < 0 {
+ return fmt.Errorf("cannot determine binary size of value %v", in)
+ }
+ size = bs
}
- b, _, err := s.m.dataSection()
- if err != nil {
- return fmt.Errorf("getting data section of map %s: %w", s.m.Name, err)
+ if s.Value == nil {
+ s.Value = make([]byte, size)
}
- if int(s.offset+s.size) > len(b) {
- return fmt.Errorf("offset %d(+%d) for variable %s is out of bounds", s.offset, s.size, s.name)
+ buf, err := sysenc.Marshal(in, size)
+ if err != nil {
+ return fmt.Errorf("marshaling value %s: %w", s.Name, err)
}
- // MapSpec.Copy() performs a shallow copy. Fully copy the byte slice
- // to avoid any changes affecting other copies of the MapSpec.
- cpy := make([]byte, len(b))
- copy(cpy, b)
-
- buf.CopyTo(cpy[s.offset : s.offset+s.size])
-
- s.m.Contents[0] = MapKV{Key: uint32(0), Value: cpy}
-
+ buf.CopyTo(s.Value)
return nil
}
// Get writes the value of the VariableSpec to the provided output using the
// host's native endianness.
+//
+// Returns an error if the variable is not initialized or if the unmarshaling fails.
func (s *VariableSpec) Get(out any) error {
- b, _, err := s.m.dataSection()
- if err != nil {
- return fmt.Errorf("getting data section of map %s: %w", s.m.Name, err)
+ if s.Value == nil {
+ return fmt.Errorf("variable is not initialized")
}
- if int(s.offset+s.size) > len(b) {
- return fmt.Errorf("offset %d(+%d) for variable %s is out of bounds", s.offset, s.size, s.name)
- }
-
- if err := sysenc.Unmarshal(out, b[s.offset:s.offset+s.size]); err != nil {
+ if err := sysenc.Unmarshal(out, s.Value); err != nil {
return fmt.Errorf("unmarshaling value: %w", err)
}
@@ -72,61 +70,42 @@ func (s *VariableSpec) Get(out any) error {
}
// Size returns the size of the variable in bytes.
-func (s *VariableSpec) Size() uint64 {
- return s.size
-}
+func (s *VariableSpec) Size() uint32 {
+ if s.Value != nil {
+ return uint32(len(s.Value))
+ }
-// MapName returns the name of the underlying MapSpec.
-func (s *VariableSpec) MapName() string {
- return s.m.Name
-}
+ if s.Type != nil {
+ size, err := btf.Sizeof(s.Type.Type)
+ if err != nil {
+ return 0
+ }
+ return uint32(size)
+ }
-// Offset returns the offset of the variable in the underlying MapSpec.
-func (s *VariableSpec) Offset() uint64 {
- return s.offset
+ return 0
}
-// Constant returns true if the VariableSpec represents a variable that is
-// read-only from the perspective of the BPF program.
+// Constant returns true if the variable is located in a data section intended
+// for constant values.
func (s *VariableSpec) Constant() bool {
- return s.m.readOnly()
-}
-
-// Type returns the [btf.Var] representing the variable in its data section.
-// This is useful for inspecting the variable's decl tags and the type
-// information of the inner type.
-//
-// Returns nil if the original ELF object did not contain BTF information.
-func (s *VariableSpec) Type() *btf.Var {
- return s.t
+ return isConstantDataSection(s.SectionName)
}
func (s *VariableSpec) String() string {
- return fmt.Sprintf("%s (type=%v, map=%s, offset=%d, size=%d)", s.name, s.t, s.m.Name, s.offset, s.size)
+ return fmt.Sprintf("%s (type=%v, section=%s, offset=%d, size=%d)", s.Name, s.Type, s.SectionName, s.Offset, s.Size())
}
-// copy returns a new VariableSpec with the same values as the original,
-// but with a different underlying MapSpec. This is useful when copying a
-// CollectionSpec. Returns nil if a MapSpec with the same name is not found.
-func (s *VariableSpec) copy(cpy *CollectionSpec) *VariableSpec {
- out := &VariableSpec{
- name: s.name,
- offset: s.offset,
- size: s.size,
- }
- if s.t != nil {
- out.t = btf.Copy(s.t).(*btf.Var)
- }
+// Copy the VariableSpec.
+func (s *VariableSpec) Copy() *VariableSpec {
+ cpy := *s
+ cpy.Value = slices.Clone(s.Value)
- // Attempt to find a MapSpec with the same name in the copied CollectionSpec.
- for _, m := range cpy.Maps {
- if m.Name == s.m.Name {
- out.m = m
- return out
- }
+ if s.Type != nil {
+ cpy.Type = btf.Copy(s.Type).(*btf.Var)
}
- return nil
+ return &cpy
}
// Variable is a convenience wrapper for modifying global variables of a
@@ -137,16 +116,16 @@ func (s *VariableSpec) copy(cpy *CollectionSpec) *VariableSpec {
// [ErrNotSupported].
type Variable struct {
name string
- offset uint64
- size uint64
+ offset uint32
+ size uint32
t *btf.Var
mm *Memory
}
-func newVariable(name string, offset, size uint64, t *btf.Var, mm *Memory) (*Variable, error) {
+func newVariable(name string, offset, size uint32, t *btf.Var, mm *Memory) (*Variable, error) {
if mm != nil {
- if int(offset+size) > mm.Size() {
+ if !mm.bounds(offset, size) {
return nil, fmt.Errorf("offset %d(+%d) is out of bounds", offset, size)
}
}
@@ -161,7 +140,7 @@ func newVariable(name string, offset, size uint64, t *btf.Var, mm *Memory) (*Var
}
// Size returns the size of the variable.
-func (v *Variable) Size() uint64 {
+func (v *Variable) Size() uint32 {
return v.size
}
@@ -240,7 +219,6 @@ func checkVariable[T any](v *Variable) error {
}
t := reflect.TypeFor[T]()
- size := uint64(t.Size())
if t.Kind() == reflect.Uintptr && v.size == 8 {
// uintptr is 8 bytes on 64-bit and 4 on 32-bit. In BPF/BTF, pointers are
// always 8 bytes. For the sake of portability, allow accessing 8-byte BPF
@@ -248,8 +226,8 @@ func checkVariable[T any](v *Variable) error {
// pointer should be zero anyway.
return nil
}
- if v.size != size {
- return fmt.Errorf("can't create %d-byte accessor to %d-byte variable: %w", size, v.size, ErrInvalidType)
+ if uintptr(v.size) != t.Size() {
+ return fmt.Errorf("can't create %d-byte accessor to %d-byte variable: %w", t.Size(), v.size, ErrInvalidType)
}
return nil
diff --git a/vendor/github.com/gaissmai/bart/.coderabbit.yaml b/vendor/github.com/gaissmai/bart/.coderabbit.yaml
index 5d2e10c36..0832dbda8 100644
--- a/vendor/github.com/gaissmai/bart/.coderabbit.yaml
+++ b/vendor/github.com/gaissmai/bart/.coderabbit.yaml
@@ -13,7 +13,7 @@ reviews:
review_status: true
commit_status: true
fail_commit_status: false
- collapse_walkthrough: false
+ collapse_walkthrough: true
changed_files_summary: true
sequence_diagrams: true
estimate_code_review_effort: true
@@ -24,10 +24,9 @@ reviews:
auto_apply_labels: false
suggested_reviewers: false
auto_assign_reviewers: false
- poem: true
+ poem: false
labeling_instructions: []
path_filters:
- - '!**/doc/**'
- '!**/testdata/**'
path_instructions: []
abort_on_close: true
diff --git a/vendor/github.com/gaissmai/bart/.golangci.yml b/vendor/github.com/gaissmai/bart/.golangci.yml
index fe6a13250..bb47f8085 100644
--- a/vendor/github.com/gaissmai/bart/.golangci.yml
+++ b/vendor/github.com/gaissmai/bart/.golangci.yml
@@ -23,6 +23,7 @@ linters:
- gochecknoglobals
- nolintlint
- modernize
+ - gocritic
settings:
govet:
enable:
@@ -33,11 +34,13 @@ linters:
excludes:
- G404 # deterministic RNG in tests/benchmarks only
exclusions:
+ generated: lax
rules:
- - path: '(.+)_test\.go'
- linters:
+ - linters:
- gochecknoglobals
- generated: lax
+ - gosec
+ - gocyclo
+ path: _test\.go
presets:
- comments
- common-false-positives
diff --git a/vendor/github.com/gaissmai/bart/NODETYPES.md b/vendor/github.com/gaissmai/bart/NODETYPES.md
index 56a278f97..889c5e4d9 100644
--- a/vendor/github.com/gaissmai/bart/NODETYPES.md
+++ b/vendor/github.com/gaissmai/bart/NODETYPES.md
@@ -1,15 +1,15 @@
# package bart
# Node Types Comparison
-
+
BART implements three different node types, each optimized for specific use cases:
-
+
## Memory Footprint (64-bit systems)
-
+
**Base Components:**
- `BitSet256`: `[4]uint64` = **32 bytes**
- `sparse.Array256[T]`: `BitSet256 + []T` = **56 bytes + n×sizeof(T)**
-
+
**Child Reference Sizing:**
- `childRef`: 8 bytes (pointer) or 16 bytes (interface value storage)
- The actual size depends on implementation: 8B for `*node` pointers, 16B for `interface{}` values
@@ -17,12 +17,12 @@
### BartNode[V] - Dynamic Sparse Node
```go
type BartNode[V any] struct {
- prefixes sparse.Array256[V] // 56 + n×sizeof(V)
+ prefixes sparse.Array256[V] // 56 + n×sizeof(V)
children sparse.Array256[childRef] // 56 + m×sizeof(childRef)
}
```
**Memory Usage:** **112 bytes + n×sizeof(V) + m×sizeof(childRef)**
-
+
### LiteNode - Dynamic Sparse, prefixes Bitset-Only Node
```go
type LiteNode struct {
@@ -36,29 +36,22 @@ type LiteNode struct {
### FastNode[V] - Fixed Array Node
```go
type FastNode[V any] struct {
- prefixes struct {
- bitset.BitSet256
- items [256]*V
- } // 2,048 + 32 bytes BitSet256
- children struct {
- bitset.BitSet256
- items [256]*any // pointer-to-interface for 8‑byte nils
- } // 2,048 + 32 bytes BitSet256
- pfxCount uint16
- cldCount uint16 // + padding
+ prefixes sparse.Array256[V] // 56 + n×sizeof(V)
+ children sparse.Array256[childRef] // 56 + m×sizeof(childRef)
+ childRankCache [256]uint8 // 256
}
```
-**Memory Usage:** **4,168 bytes** (fixed, regardless of occupancy)
-
+**Memory Usage:** **256 + 112 bytes + n×sizeof(V) + m×sizeof(childRef)**
+
## Real-World Example
**Scenario:** Node with 10 prefixes, 5 children
-
+
| Node Type | Base | *Payload | +Children | Total | **Bytes/Prefix** ¹ |
|-----------|------|----------|----------|-----------|------------------|
| LiteNode | 96 | 0 | 5×16=80 | 176 bytes | **17** |
| BartNode[int] | 112 | 10×8=80 | 5×16=80 | 272 bytes | **27** |
- | FastNode[int] | 4,168 | 0 | 0 | 4,168 bytes | **417** |
-
+ | FastNode[int] | 368 | 10×8=80 | 5×16=80 | 528 bytes | **53** |
+
¹ Values assume childRef = 16 bytes and pointer to payload = 8 bytes
## Extended Memory Example with Path Compression
@@ -88,7 +81,7 @@ The actual payload struct referenced by the pointer is **not included** in these
|-----------|------|-----------|-----------|----------|-----------|------------|-------------------|
| **LiteNode** | 96 B | 0 | 15×16=240 B | 336 B | +160 B | +0 B | **16.8** |
| **BartNode[V]** | 112 B | 10×8=80 B | 15×16=240 B | 432 B | +200 B | +40 B | **21.6** |
-| **FastNode[V]** | 4,168 B | 0 | 0 | 4,168 B | +200 B | +40 B | **208.4** |
+| **FastNode[V]** | 368 B | 10×8=80 B | 15×16=240 B | 688 B | +200 B | +40 B | **34.4** |
**Notes:**
1. Prefixes: LiteNode stores no values (0B); BartNode/FastNode store 10×8B pointers to payload structs
@@ -114,26 +107,25 @@ The actual payload struct referenced by the pointer is **not included** in these
- **Reduced trie depth**: Fewer levels = fewer lookups per route
**Important:** The actual payload structs (e.g., routing information, metadata) are stored externally and referenced by the 8-byte pointers. Their sizes are not included in the per-node calculations above, as they are shared or application-specific.
-
+
## Lookup Performance Deep Dive
-
+
All three node types achieve **O(1) per-level lookup performance**, but must traverse trie levels:
-
+
### Trie Structure & Performance
- **8-bit strides per level**: Each trie level handles 8 bits of the IP address
- **IPv4 traversal**: Worst case 4 levels (32÷8), real-world typically 3 levels for /24 routes
- **IPv6 traversal**: Worst case 16 levels (128÷8), real-world typically 6 levels for /48 routes
- **Performance characteristic**: O(trie_depth) not O(number_of_routes)
- **IPv6 vs IPv4**: IPv6 inherently ~2× slower due to deeper tree structure
-
+
### BartNode[V] & LiteNode - Optimized Level Operations
- **Precomputed lookup tables** (`lpm.LookupTbl[idx]`) eliminate search within each level
- **BitSet256 intersections** via `IntersectionTop()` for instant prefix matching
- **Rank-based indirection**: Bitset-to-slice mapping uses precomputed Rank masks
- **Pipeline-friendly**: Only 4 bitset operations (4×uint64) per level, optimized for CPU pipelining
- **No backtracking**: Traditional longest-prefix-match backtracking replaced with direct table lookups
-
-### FastNode[V] - Direct Array Access per Level
-- **Zero indirection per level**: Direct array indexing `prefixes[idx]` and `children[idx]`
-- **Cache-optimal**: Contiguous memory layout within each level
-- **Performance advantage**: Still ~40% faster per level despite sparse optimizations
+
+### FastNode[V] - Cached Child Access per Level
+- **No rank recomputation per level**: Uses `childRankCache[octet]` to index sparse child storage quickly.
+- **Performance advantage**: ~25% faster level traversal
diff --git a/vendor/github.com/gaissmai/bart/README.md b/vendor/github.com/gaissmai/bart/README.md
index 190abb66c..3dbc62505 100644
--- a/vendor/github.com/gaissmai/bart/README.md
+++ b/vendor/github.com/gaissmai/bart/README.md
@@ -38,12 +38,8 @@ sparse arrays for **level compression**.
Combined with a **novel path and fringe compression**, this design reduces
memory consumption by nearly two orders of magnitude compared to classical ART.
-For **bart.Fast** this binary tree is represented with fixed arrays
-without level compression (classical ART), but combined with the same
-novel **path and fringe compression** from BART. This design
-reduces memory consumption by more than an order of magnitude compared
-to classical ART and thus makes ART usable in the first place for large
-routing tables.
+For **bart.Fast**, an additional 256 bytes per node are used to
+achieve faster level traversal in the multibit trie.
**bart.Lite** is a special form of **bart.Table**, but without a payload, and therefore
has the lowest memory overhead while maintaining the same lookup times.
@@ -110,12 +106,13 @@ func main() {
| Aspect | Table | Lite | Fast |
|--------|-------------|-------------|-------------|
- | **Per-level Speed** | ⚡ **O(1)** | ⚡ **O(1)** | 🚀 **O(1), 50-100% faster** |
+ | **Per-level Speed** | ⚡ **O(1)** | ⚡ **O(1)** | 🚀 **O(1), ~25% faster** |
| **Overall Lookup** | O(trie_depth) | O(trie_depth) | O(trie_depth) |
| **IPv4 Performance** | ~3 level traversals | ~3 level traversals | ~3 level traversals |
| **IPv6 Performance** | ~6 level traversals | ~6 level traversals | ~6 level traversals |
| **IPv6 vs IPv4** | ~2× slower | ~2× slower | ~2× slower |
- | **Memory** | efficient | very efficient | inefficient |
+ | **Memory** | efficient | very efficient | less efficient |
+ | **Update** | very fast | best | fast |
A more detailed description can be found in [NODETYPES.md](NODETYPES.md).
@@ -124,15 +121,18 @@ A more detailed description can be found in [NODETYPES.md](NODETYPES.md).
### 🎯 **bart.Table[V]** - The Balanced Choice
- **Recommended** for most routing table use cases
- Near-optimal per-level performance with excellent memory efficiency
+- Very fast updates (insert/delete)
- Perfect balance for both IPv4 and IPv6 routing tables (use it for RIB)
### 🪶 **bart.Lite** - The Minimalist
- **Specialized** for prefix-only operations, no payload
-- Same per-level performance as *bart.Table[V]* but 35% less memory
+- Same per-level performance as *bart.Table[V]* but ~35% less memory
+- Optimal updates (insert/delete), ~25% faster than *bart.Table[V]*
- Ideal for IPv4/IPv6 allowlists and set-based operations (use it for ACL)
-### 🚀 **bart.Fast[V]** - The Performance Champion
-- **50-100% faster** when memory constraints allow
+### 🚀 **bart.Fast[V]** - The Lookup Performance Champion
+- **~25% faster** when memory constraints allow
+- Fast updates (insert/delete), ~50% slower than *bart.Table[V]*
- Best choice for lookup-intensive applications (use it for FIB)
## Bitset Efficiency
@@ -268,11 +268,13 @@ $ GOAMD64=v3 go test -run=xxx -bench=FullFastM/Lookup$ -cpu=1
goos: linux
goarch: amd64
pkg: github.com/gaissmai/bart
-cpu: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
-BenchmarkFullFastMatch4/Lookup 124476082 9.63 ns/op
-BenchmarkFullFastMatch6/Lookup 91032597 13.20 ns/op
-BenchmarkFullFastMiss4/Lookup 134171480 8.93 ns/op
-BenchmarkFullFastMiss6/Lookup 75634396 15.79 ns/op
+cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
+BenchmarkFullFastMatch4/Lookup 129484557 9.274 ns/op
+BenchmarkFullFastMatch6/Lookup 65185531 17.99 ns/op
+BenchmarkFullFastMiss4/Lookup 92448910 12.81 ns/op
+BenchmarkFullFastMiss6/Lookup 64323817 18.66 ns/op
+PASS
+ok github.com/gaissmai/bart 6.647s
```
## Compatibility Guarantees
diff --git a/vendor/github.com/gaissmai/bart/bart.go b/vendor/github.com/gaissmai/bart/bart.go
index 6f2c17e6f..489ce6df9 100644
--- a/vendor/github.com/gaissmai/bart/bart.go
+++ b/vendor/github.com/gaissmai/bart/bart.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2025 Karl Gaissmaier
+// Copyright (c) 2026 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart
@@ -16,13 +16,14 @@ import (
//
// The zero value is ready to use.
//
+// A Table must not be copied by value; always pass by pointer.
+// Nil pointers as receivers or arguments are forbidden and will panic.
+//
// The Table is safe for concurrent reads, but concurrent reads and writes
// must be externally synchronized. Mutation via Insert/Delete requires locks,
// or alternatively, use ...Persist methods which return a modified copy
// without altering the original table (copy-on-write).
//
-// A Table must not be copied by value; always pass by pointer.
-//
// Performance note: Do not pass IPv4-in-IPv6 addresses (e.g., ::ffff:192.0.2.1)
// as input. The methods do not perform automatic unmapping to avoid unnecessary
// overhead for the common case where native addresses are used.
@@ -191,7 +192,7 @@ func (t *Table[V]) Lookup(ip netip.Addr) (val V, ok bool) {
LOOP:
// find leaf node
for depth, octet = range octets {
- depth = depth & nodes.DepthMask // BCE, Lookup must be fast
+ depth &= nodes.DepthMask // BCE, Lookup must be fast
// push current node on stack for fast backtracking
stack[depth] = n
@@ -224,7 +225,7 @@ LOOP:
// start backtracking, unwind the stack, bounds check eliminated
for ; depth >= 0; depth-- {
- depth = depth & nodes.DepthMask // BCE
+ depth &= nodes.DepthMask // BCE
n = stack[depth]
@@ -260,7 +261,7 @@ func (t *Table[V]) LookupPrefix(pfx netip.Prefix) (val V, ok bool) {
// match any address in the provided prefix range.
//
// This is functionally identical to LookupPrefix but additionally returns the
-// matching prefix (lpmPfx) itself along with the value.
+// matching LPM prefix itself along with the value.
//
// This method is slower than LookupPrefix and should only be used if the
// matching lpm entry is also required for other reasons.
@@ -296,7 +297,7 @@ func (t *Table[V]) lookupPrefixLPM(pfx netip.Prefix, withLPM bool) (lpmPfx netip
LOOP:
// find the last node on the octets path in the trie,
for depth, octet = range octets {
- depth = depth & nodes.DepthMask // BCE
+ depth &= nodes.DepthMask // BCE
// stepped one past the last stride of interest; back up to last and break
if depth > lastOctetPlusOne {
@@ -347,7 +348,7 @@ LOOP:
// start backtracking, unwind the stack
for ; depth >= 0; depth-- {
- depth = depth & nodes.DepthMask // BCE
+ depth &= nodes.DepthMask // BCE
n = stack[depth]
diff --git a/vendor/github.com/gaissmai/bart/bartmethodsgenerated.go b/vendor/github.com/gaissmai/bart/bartmethodsgenerated.go
index d7e67abc3..a366a384a 100644
--- a/vendor/github.com/gaissmai/bart/bartmethodsgenerated.go
+++ b/vendor/github.com/gaissmai/bart/bartmethodsgenerated.go
@@ -1,6 +1,6 @@
// Code generated from file "commonmethods_tmpl.go"; DO NOT EDIT.
-// Copyright (c) 2025 Karl Gaissmaier
+// Copyright (c) 2026 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart
@@ -266,9 +266,6 @@ func (t *Table[V]) ModifyPersist(pfx netip.Prefix, cb func(_ V, ok bool) (_ V, d
// Returns an empty iterator if the prefix is invalid.
func (t *Table[V]) Supernets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
if !pfx.IsValid() {
return
}
@@ -298,9 +295,6 @@ func (t *Table[V]) Supernets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
// Returns an empty iterator if the prefix is invalid.
func (t *Table[V]) Subnets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
if !pfx.IsValid() {
return
}
@@ -351,15 +345,12 @@ func (t *Table[V]) OverlapsPrefix(pfx netip.Prefix) bool {
// This is useful for conflict detection, policy enforcement,
// or validating mutually exclusive routing domains.
func (t *Table[V]) Overlaps(o *Table[V]) bool {
- if o == nil {
- return false
- }
return t.Overlaps4(o) || t.Overlaps6(o)
}
// Overlaps4 is like [Table.Overlaps] but for the v4 routing table only.
func (t *Table[V]) Overlaps4(o *Table[V]) bool {
- if o == nil || t.size4 == 0 || o.size4 == 0 {
+ if t.size4 == 0 || o.size4 == 0 {
return false
}
return t.root4.Overlaps(&o.root4, 0)
@@ -367,7 +358,7 @@ func (t *Table[V]) Overlaps4(o *Table[V]) bool {
// Overlaps6 is like [Table.Overlaps] but for the v6 routing table only.
func (t *Table[V]) Overlaps6(o *Table[V]) bool {
- if o == nil || t.size6 == 0 || o.size6 == 0 {
+ if t.size6 == 0 || o.size6 == 0 {
return false
}
return t.root6.Overlaps(&o.root6, 0)
@@ -380,7 +371,15 @@ func (t *Table[V]) Overlaps6(o *Table[V]) bool {
// This duplicate is shallow-copied by default, but if the value type V implements the
// Clone method, the value is deeply cloned before insertion. See also Table.Clone.
func (t *Table[V]) Union(o *Table[V]) {
- if o == nil || o == t || (o.size4 == 0 && o.size6 == 0) {
+ // panics on nil receiver
+ _ = t.size4
+
+ // panics on nil argument
+ if o.size4 == 0 && o.size6 == 0 {
+ return
+ }
+ // t is unchanged
+ if o == t {
return
}
@@ -398,10 +397,17 @@ func (t *Table[V]) Union(o *Table[V]) {
// UnionPersist is similar to [Union] but the receiver isn't modified.
//
// All nodes touched during union are cloned and a new *Table is returned.
-// If o is nil or empty, no nodes are touched and the receiver may be
+// If o is empty, no nodes are touched and the receiver may be
// returned unchanged.
func (t *Table[V]) UnionPersist(o *Table[V]) *Table[V] {
- if o == nil || o == t || (o.size4 == 0 && o.size6 == 0) {
+ // panics on nil receiver
+ _ = t.size4
+
+ // panics on nil argument
+ if o.size4 == 0 && o.size6 == 0 {
+ return t
+ }
+ if o == t {
return t
}
@@ -453,7 +459,7 @@ func (t *Table[V]) UnionPersist(o *Table[V]) *Table[V] {
// The bart package will automatically detect and use this method via Go's
// structural typing.
func (t *Table[V]) Equal(o *Table[V]) bool {
- if o == nil || t.size4 != o.size4 || t.size6 != o.size6 {
+ if t.size4 != o.size4 || t.size6 != o.size6 {
return false
}
if o == t {
@@ -479,10 +485,6 @@ func (t *Table[V]) Equal(o *Table[V]) bool {
// The bart package will automatically detect and use this method via Go's
// structural typing.
func (t *Table[V]) Clone() *Table[V] {
- if t == nil {
- return nil
- }
-
c := new(Table[V])
cloneFn := value.CloneFnFactory[V]()
@@ -539,9 +541,6 @@ func (t *Table[V]) Size6() int {
// }
func (t *Table[V]) All() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRec(stridePath{}, 0, true, yield) && t.root6.AllRec(stridePath{}, 0, false, yield)
}
}
@@ -549,9 +548,6 @@ func (t *Table[V]) All() iter.Seq2[netip.Prefix, V] {
// All4 is like [Table.All] but only for the v4 routing table.
func (t *Table[V]) All4() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRec(stridePath{}, 0, true, yield)
}
}
@@ -559,9 +555,6 @@ func (t *Table[V]) All4() iter.Seq2[netip.Prefix, V] {
// All6 is like [Table.All] but only for the v6 routing table.
func (t *Table[V]) All6() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root6.AllRec(stridePath{}, 0, false, yield)
}
}
@@ -585,9 +578,6 @@ func (t *Table[V]) All6() iter.Seq2[netip.Prefix, V] {
// traversal is required use persistent table methods.
func (t *Table[V]) AllSorted() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRecSorted(stridePath{}, 0, true, yield) &&
t.root6.AllRecSorted(stridePath{}, 0, false, yield)
}
@@ -596,9 +586,6 @@ func (t *Table[V]) AllSorted() iter.Seq2[netip.Prefix, V] {
// AllSorted4 is like [Table.AllSorted] but only for the v4 routing table.
func (t *Table[V]) AllSorted4() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRecSorted(stridePath{}, 0, true, yield)
}
}
@@ -606,9 +593,6 @@ func (t *Table[V]) AllSorted4() iter.Seq2[netip.Prefix, V] {
// AllSorted6 is like [Table.AllSorted] but only for the v6 routing table.
func (t *Table[V]) AllSorted6() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root6.AllRecSorted(stridePath{}, 0, false, yield)
}
}
@@ -636,12 +620,9 @@ func (t *Table[V]) AllSorted6() iter.Seq2[netip.Prefix, V] {
// │ └─ 2001:db8::/32 (V)
// └─ fe80::/10 (V)
func (t *Table[V]) Fprint(w io.Writer) error {
- if w == nil {
+ if w == nil && t != nil {
return fmt.Errorf("nil writer")
}
- if t == nil {
- return nil
- }
// v4
if err := t.fprint(w, true); err != nil {
@@ -691,10 +672,6 @@ func (t *Table[V]) MarshalText() ([]byte, error) {
// MarshalJSON dumps the table into two sorted lists: for ipv4 and ipv6.
// Every root and subnet is an array, not a map, because the order matters.
func (t *Table[V]) MarshalJSON() ([]byte, error) {
- if t == nil {
- return []byte("null"), nil
- }
-
result := struct {
Ipv4 []DumpListNode[V] `json:"ipv4,omitempty"`
Ipv6 []DumpListNode[V] `json:"ipv6,omitempty"`
@@ -714,18 +691,12 @@ func (t *Table[V]) MarshalJSON() ([]byte, error) {
// DumpList4 dumps the ipv4 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build the text or JSON serialization.
func (t *Table[V]) DumpList4() []DumpListNode[V] {
- if t == nil {
- return nil
- }
return t.dumpListRec(&t.root4, 0, stridePath{}, 0, true)
}
// DumpList6 dumps the ipv6 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build custom JSON representation.
func (t *Table[V]) DumpList6() []DumpListNode[V] {
- if t == nil {
- return nil
- }
return t.dumpListRec(&t.root6, 0, stridePath{}, 0, false)
}
@@ -775,10 +746,6 @@ func (t *Table[V]) dumpString() string {
// dump the table structure and all the nodes to w.
func (t *Table[V]) dump(w io.Writer) {
- if t == nil {
- return
- }
-
if t.size4 > 0 {
stats := t.root4.StatsRec()
fmt.Fprintln(w)
diff --git a/vendor/github.com/gaissmai/bart/commonmethods_tmpl.go b/vendor/github.com/gaissmai/bart/commonmethods_tmpl.go
index 710760b38..aa9e37d9e 100644
--- a/vendor/github.com/gaissmai/bart/commonmethods_tmpl.go
+++ b/vendor/github.com/gaissmai/bart/commonmethods_tmpl.go
@@ -1,6 +1,6 @@
//go:build generate
-// Copyright (c) 2025 Karl Gaissmaier
+// Copyright (c) 2026 Karl Gaissmaier
// SPDX-License-Identifier: MIT
//go:generate ./scripts/generate-table-methods.sh
@@ -327,9 +327,6 @@ func (t *_TABLE_TYPE[V]) ModifyPersist(pfx netip.Prefix, cb func(_ V, ok bool) (
// Returns an empty iterator if the prefix is invalid.
func (t *_TABLE_TYPE[V]) Supernets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
if !pfx.IsValid() {
return
}
@@ -359,9 +356,6 @@ func (t *_TABLE_TYPE[V]) Supernets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V]
// Returns an empty iterator if the prefix is invalid.
func (t *_TABLE_TYPE[V]) Subnets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
if !pfx.IsValid() {
return
}
@@ -412,15 +406,12 @@ func (t *_TABLE_TYPE[V]) OverlapsPrefix(pfx netip.Prefix) bool {
// This is useful for conflict detection, policy enforcement,
// or validating mutually exclusive routing domains.
func (t *_TABLE_TYPE[V]) Overlaps(o *_TABLE_TYPE[V]) bool {
- if o == nil {
- return false
- }
return t.Overlaps4(o) || t.Overlaps6(o)
}
// Overlaps4 is like [_TABLE_TYPE.Overlaps] but for the v4 routing table only.
func (t *_TABLE_TYPE[V]) Overlaps4(o *_TABLE_TYPE[V]) bool {
- if o == nil || t.size4 == 0 || o.size4 == 0 {
+ if t.size4 == 0 || o.size4 == 0 {
return false
}
return t.root4.Overlaps(&o.root4, 0)
@@ -428,7 +419,7 @@ func (t *_TABLE_TYPE[V]) Overlaps4(o *_TABLE_TYPE[V]) bool {
// Overlaps6 is like [_TABLE_TYPE.Overlaps] but for the v6 routing table only.
func (t *_TABLE_TYPE[V]) Overlaps6(o *_TABLE_TYPE[V]) bool {
- if o == nil || t.size6 == 0 || o.size6 == 0 {
+ if t.size6 == 0 || o.size6 == 0 {
return false
}
return t.root6.Overlaps(&o.root6, 0)
@@ -441,7 +432,15 @@ func (t *_TABLE_TYPE[V]) Overlaps6(o *_TABLE_TYPE[V]) bool {
// This duplicate is shallow-copied by default, but if the value type V implements the
// Clone method, the value is deeply cloned before insertion. See also _TABLE_TYPE.Clone.
func (t *_TABLE_TYPE[V]) Union(o *_TABLE_TYPE[V]) {
- if o == nil || o == t || (o.size4 == 0 && o.size6 == 0) {
+ // panics on nil receiver
+ _ = t.size4
+
+ // panics on nil argument
+ if o.size4 == 0 && o.size6 == 0 {
+ return
+ }
+ // t is unchanged
+ if o == t {
return
}
@@ -459,10 +458,17 @@ func (t *_TABLE_TYPE[V]) Union(o *_TABLE_TYPE[V]) {
// UnionPersist is similar to [Union] but the receiver isn't modified.
//
// All nodes touched during union are cloned and a new *_TABLE_TYPE is returned.
-// If o is nil or empty, no nodes are touched and the receiver may be
+// If o is empty, no nodes are touched and the receiver may be
// returned unchanged.
func (t *_TABLE_TYPE[V]) UnionPersist(o *_TABLE_TYPE[V]) *_TABLE_TYPE[V] {
- if o == nil || o == t || (o.size4 == 0 && o.size6 == 0) {
+ // panics on nil receiver
+ _ = t.size4
+
+ // panics on nil argument
+ if o.size4 == 0 && o.size6 == 0 {
+ return t
+ }
+ if o == t {
return t
}
@@ -514,7 +520,7 @@ func (t *_TABLE_TYPE[V]) UnionPersist(o *_TABLE_TYPE[V]) *_TABLE_TYPE[V] {
// The bart package will automatically detect and use this method via Go's
// structural typing.
func (t *_TABLE_TYPE[V]) Equal(o *_TABLE_TYPE[V]) bool {
- if o == nil || t.size4 != o.size4 || t.size6 != o.size6 {
+ if t.size4 != o.size4 || t.size6 != o.size6 {
return false
}
if o == t {
@@ -540,10 +546,6 @@ func (t *_TABLE_TYPE[V]) Equal(o *_TABLE_TYPE[V]) bool {
// The bart package will automatically detect and use this method via Go's
// structural typing.
func (t *_TABLE_TYPE[V]) Clone() *_TABLE_TYPE[V] {
- if t == nil {
- return nil
- }
-
c := new(_TABLE_TYPE[V])
cloneFn := value.CloneFnFactory[V]()
@@ -600,9 +602,6 @@ func (t *_TABLE_TYPE[V]) Size6() int {
// }
func (t *_TABLE_TYPE[V]) All() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRec(stridePath{}, 0, true, yield) && t.root6.AllRec(stridePath{}, 0, false, yield)
}
}
@@ -610,9 +609,6 @@ func (t *_TABLE_TYPE[V]) All() iter.Seq2[netip.Prefix, V] {
// All4 is like [_TABLE_TYPE.All] but only for the v4 routing table.
func (t *_TABLE_TYPE[V]) All4() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRec(stridePath{}, 0, true, yield)
}
}
@@ -620,9 +616,6 @@ func (t *_TABLE_TYPE[V]) All4() iter.Seq2[netip.Prefix, V] {
// All6 is like [_TABLE_TYPE.All] but only for the v6 routing table.
func (t *_TABLE_TYPE[V]) All6() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root6.AllRec(stridePath{}, 0, false, yield)
}
}
@@ -646,9 +639,6 @@ func (t *_TABLE_TYPE[V]) All6() iter.Seq2[netip.Prefix, V] {
// traversal is required use persistent table methods.
func (t *_TABLE_TYPE[V]) AllSorted() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRecSorted(stridePath{}, 0, true, yield) &&
t.root6.AllRecSorted(stridePath{}, 0, false, yield)
}
@@ -657,9 +647,6 @@ func (t *_TABLE_TYPE[V]) AllSorted() iter.Seq2[netip.Prefix, V] {
// AllSorted4 is like [_TABLE_TYPE.AllSorted] but only for the v4 routing table.
func (t *_TABLE_TYPE[V]) AllSorted4() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRecSorted(stridePath{}, 0, true, yield)
}
}
@@ -667,9 +654,6 @@ func (t *_TABLE_TYPE[V]) AllSorted4() iter.Seq2[netip.Prefix, V] {
// AllSorted6 is like [_TABLE_TYPE.AllSorted] but only for the v6 routing table.
func (t *_TABLE_TYPE[V]) AllSorted6() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root6.AllRecSorted(stridePath{}, 0, false, yield)
}
}
@@ -697,12 +681,9 @@ func (t *_TABLE_TYPE[V]) AllSorted6() iter.Seq2[netip.Prefix, V] {
// │ └─ 2001:db8::/32 (V)
// └─ fe80::/10 (V)
func (t *_TABLE_TYPE[V]) Fprint(w io.Writer) error {
- if w == nil {
+ if w == nil && t != nil {
return fmt.Errorf("nil writer")
}
- if t == nil {
- return nil
- }
// v4
if err := t.fprint(w, true); err != nil {
@@ -752,10 +733,6 @@ func (t *_TABLE_TYPE[V]) MarshalText() ([]byte, error) {
// MarshalJSON dumps the table into two sorted lists: for ipv4 and ipv6.
// Every root and subnet is an array, not a map, because the order matters.
func (t *_TABLE_TYPE[V]) MarshalJSON() ([]byte, error) {
- if t == nil {
- return []byte("null"), nil
- }
-
result := struct {
Ipv4 []DumpListNode[V] `json:"ipv4,omitempty"`
Ipv6 []DumpListNode[V] `json:"ipv6,omitempty"`
@@ -775,18 +752,12 @@ func (t *_TABLE_TYPE[V]) MarshalJSON() ([]byte, error) {
// DumpList4 dumps the ipv4 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build the text or JSON serialization.
func (t *_TABLE_TYPE[V]) DumpList4() []DumpListNode[V] {
- if t == nil {
- return nil
- }
return t.dumpListRec(&t.root4, 0, stridePath{}, 0, true)
}
// DumpList6 dumps the ipv6 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build custom JSON representation.
func (t *_TABLE_TYPE[V]) DumpList6() []DumpListNode[V] {
- if t == nil {
- return nil
- }
return t.dumpListRec(&t.root6, 0, stridePath{}, 0, false)
}
@@ -836,10 +807,6 @@ func (t *_TABLE_TYPE[V]) dumpString() string {
// dump the table structure and all the nodes to w.
func (t *_TABLE_TYPE[V]) dump(w io.Writer) {
- if t == nil {
- return
- }
-
if t.size4 > 0 {
stats := t.root4.StatsRec()
fmt.Fprintln(w)
diff --git a/vendor/github.com/gaissmai/bart/commontests_tmpl.go b/vendor/github.com/gaissmai/bart/commontests_tmpl.go
index f910d52a6..9af7e48bd 100644
--- a/vendor/github.com/gaissmai/bart/commontests_tmpl.go
+++ b/vendor/github.com/gaissmai/bart/commontests_tmpl.go
@@ -1,6 +1,6 @@
//go:build generate
-// Copyright (c) 2025 Karl Gaissmaier
+// Copyright (c) 2026 Karl Gaissmaier
// SPDX-License-Identifier: MIT
//go:generate ./scripts/generate-table-tests.sh
@@ -100,9 +100,11 @@ func (t *_TABLE_TYPE[V]) flatSorted() golden.Table[V] {
return flat
}
-func TestTableNil__TABLE_TYPE(t *testing.T) {
+func TestTableNilReceiver__TABLE_TYPE(t *testing.T) {
t.Parallel()
+ var tbl1 *_TABLE_TYPE[any] = nil
+
ip4 := mpa("127.0.0.1")
ip6 := mpa("::1")
@@ -110,21 +112,12 @@ func TestTableNil__TABLE_TYPE(t *testing.T) {
pfx6 := mpp("::1/128")
tbl2 := new(_TABLE_TYPE[any])
- tbl2.Insert(pfx4, nil)
- tbl2.Insert(pfx6, nil)
+ tbl2.Insert(pfx4, true)
+ tbl2.Insert(pfx6, true)
- var tbl1 *_TABLE_TYPE[any] = nil
-
- t.Run("mustPanic", func(t *testing.T) {
+ t.Run("mustPanicNilReceiver", func(t *testing.T) {
t.Parallel()
- mustPanic(t, "sizeUpdate", func() { tbl1.sizeUpdate(true, 1) })
- mustPanic(t, "sizeUpdate", func() { tbl1.sizeUpdate(false, 1) })
- mustPanic(t, "rootNodeByVersion", func() { tbl1.rootNodeByVersion(true) })
- mustPanic(t, "rootNodeByVersion", func() { tbl1.rootNodeByVersion(false) })
- mustPanic(t, "fprint", func() { tbl1.fprint(nil, true) })
- mustPanic(t, "fprint", func() { tbl1.fprint(nil, false) })
-
mustPanic(t, "Size", func() { tbl1.Size() })
mustPanic(t, "Size4", func() { tbl1.Size4() })
mustPanic(t, "Size6", func() { tbl1.Size6() })
@@ -140,52 +133,48 @@ func TestTableNil__TABLE_TYPE(t *testing.T) {
mustPanic(t, "Lookup", func() { tbl1.Lookup(ip6) })
mustPanic(t, "LookupPrefix", func() { tbl1.LookupPrefix(pfx4) })
mustPanic(t, "LookupPrefixLPM", func() { tbl1.LookupPrefixLPM(pfx4) })
+ mustPanic(t, "Clone", func() { tbl1.Clone() })
mustPanic(t, "Union", func() { tbl1.Union(tbl2) })
mustPanic(t, "UnionPersist", func() { tbl1.UnionPersist(tbl2) })
- })
-
- t.Run("noPanic", func(t *testing.T) {
- t.Parallel()
-
- noPanic(t, "Overlaps", func() { tbl1.Overlaps(nil) })
- noPanic(t, "Overlaps4", func() { tbl1.Overlaps4(nil) })
- noPanic(t, "Overlaps6", func() { tbl1.Overlaps6(nil) })
-
- noPanic(t, "Overlaps", func() { tbl2.Overlaps(tbl2) })
- noPanic(t, "Overlaps4", func() { tbl2.Overlaps4(tbl2) })
- noPanic(t, "Overlaps6", func() { tbl2.Overlaps6(tbl2) })
-
mustPanic(t, "Overlaps", func() { tbl1.Overlaps(tbl2) })
mustPanic(t, "Overlaps4", func() { tbl1.Overlaps4(tbl2) })
mustPanic(t, "Overlaps6", func() { tbl1.Overlaps6(tbl2) })
mustPanic(t, "OverlapsPrefix", func() { tbl1.OverlapsPrefix(pfx4) })
mustPanic(t, "OverlapsPrefix", func() { tbl1.OverlapsPrefix(pfx6) })
-
mustPanic(t, "Equal", func() { tbl1.Equal(tbl2) })
- noPanic(t, "Equal", func() { tbl1.Equal(tbl1) })
- noPanic(t, "Equal", func() { tbl2.Equal(tbl2) })
-
- noPanic(t, "dump", func() { tbl1.dump(nil) })
- noPanic(t, "dumpString", func() { tbl1.dumpString() })
- noPanic(t, "Clone", func() { tbl1.Clone() })
- noPanic(t, "DumpList4", func() { tbl1.DumpList4() })
- noPanic(t, "DumpList6", func() { tbl1.DumpList6() })
- noPanic(t, "Fprint", func() { tbl1.Fprint(nil) })
- noPanic(t, "MarshalJSON", func() { _, _ = tbl1.MarshalJSON() })
- noPanic(t, "MarshalText", func() { _, _ = tbl1.MarshalText() })
+ mustPanic(t, "DumpList4", func() { tbl1.DumpList4() })
+ mustPanic(t, "DumpList6", func() { tbl1.DumpList6() })
+ mustPanic(t, "Fprint", func() { tbl1.Fprint(nil) })
+ mustPanic(t, "MarshalJSON", func() { _, _ = tbl1.MarshalJSON() })
+ mustPanic(t, "MarshalText", func() { _, _ = tbl1.MarshalText() })
+
+ mustPanicRangeOverFunc[any](t, "All", tbl1.All)
+ mustPanicRangeOverFunc[any](t, "All4", tbl1.All4)
+ mustPanicRangeOverFunc[any](t, "All6", tbl1.All6)
+ mustPanicRangeOverFunc[any](t, "AllSorted", tbl1.AllSorted)
+ mustPanicRangeOverFunc[any](t, "AllSorted4", tbl1.AllSorted4)
+ mustPanicRangeOverFunc[any](t, "AllSorted6", tbl1.AllSorted6)
+ mustPanicRangeOverFunc[any](t, "Subnets", tbl1.Subnets)
+ mustPanicRangeOverFunc[any](t, "Supernets", tbl1.Supernets)
})
+}
+
+func TestTableNilArguments__TABLE_TYPE(t *testing.T) {
+ t.Parallel()
- t.Run("noPanicRangeOverFunc", func(t *testing.T) {
+ t.Run("mustPanicNilArgs", func(t *testing.T) {
t.Parallel()
+ tbl := new(_TABLE_TYPE[bool])
- noPanicRangeOverFunc[any](t, "All", tbl1.All)
- noPanicRangeOverFunc[any](t, "All4", tbl1.All4)
- noPanicRangeOverFunc[any](t, "All6", tbl1.All6)
- noPanicRangeOverFunc[any](t, "AllSorted", tbl1.AllSorted)
- noPanicRangeOverFunc[any](t, "AllSorted4", tbl1.AllSorted4)
- noPanicRangeOverFunc[any](t, "AllSorted6", tbl1.AllSorted6)
- noPanicRangeOverFunc[any](t, "Subnets", tbl1.Subnets)
- noPanicRangeOverFunc[any](t, "Supernets", tbl1.Supernets)
+ tbl.Insert(mpp("127.0.0.1/32"), true)
+ tbl.Insert(mpp("::1/128"), true)
+
+ mustPanic(t, "Union(nil)", func() { tbl.Union(nil) })
+ mustPanic(t, "UnionPersist(nil)", func() { tbl.UnionPersist(nil) })
+ mustPanic(t, "Overlaps(nil)", func() { tbl.Overlaps(nil) })
+ mustPanic(t, "Overlaps4(nil)", func() { tbl.Overlaps4(nil) })
+ mustPanic(t, "Overlaps6(nil)", func() { tbl.Overlaps6(nil) })
+ mustPanic(t, "Equal(nil)", func() { tbl.Equal(nil) })
})
}
@@ -1372,12 +1361,7 @@ func TestTableClone__TABLE_TYPE(t *testing.T) {
prng := rand.New(rand.NewPCG(42, 42))
pfxs := random.RealWorldPrefixes(prng, n)
- var tbl *_TABLE_TYPE[int]
- if tbl.Clone() != nil {
- t.Fatal("expected nil")
- }
-
- tbl = new(_TABLE_TYPE[int])
+ tbl := new(_TABLE_TYPE[int])
for i, pfx := range pfxs {
tbl.Insert(pfx, i)
}
diff --git a/vendor/github.com/gaissmai/bart/fast.go b/vendor/github.com/gaissmai/bart/fast.go
index 3a8ba3500..b3372e27b 100644
--- a/vendor/github.com/gaissmai/bart/fast.go
+++ b/vendor/github.com/gaissmai/bart/fast.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2025 Karl Gaissmaier
+// Copyright (c) 2026 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart
@@ -8,29 +8,25 @@ import (
"sync"
"github.com/gaissmai/bart/internal/art"
+ "github.com/gaissmai/bart/internal/lpm"
"github.com/gaissmai/bart/internal/nodes"
- "github.com/gaissmai/bart/internal/value"
)
-// Fast follows the ART design by Knuth in using fixed arrays at each level
-// combined with the same path and fringe compression invented for BART.
+// Fast is similar to Table but uses additional 256 bytes in each node
+// for faster level traversing in the multibit trie.
//
-// As a result Fast sacrifices memory efficiency to achieve 50-100% higher
-// speed.
+// As a result Fast sacrifices memory efficiency to achieve 25% better
+// speed in lookup and contains.
//
// The zero value is ready to use.
-// A Fast table must not be copied by value; always pass by pointer.
//
-// The payload type MUST NOT be a zero-sized value (for example: struct{}
-// or [0]byte). According to the Go specification:
+// A Fast must not be copied by value; always pass by pointer.
+// Nil pointers as receivers or arguments are forbidden and will panic.
//
-// "Pointers to distinct zero-size variables may or may not be equal."
-//
-// However, the ART algorithm requires that different prefixes, even with
-// identical payload V, result in distinct value pointers. This is not
-// guaranteed for zero-sized values; thus, zero-sized types as payload V
-// would result in undefined behavior so the input methods panic if they
-// detect it.
+// The Fast is safe for concurrent reads, but concurrent reads and writes
+// must be externally synchronized. Mutation via Insert/Delete requires locks,
+// or alternatively, use ...Persist methods which return a modified copy
+// without altering the original table (copy-on-write).
//
// Performance note: Do not pass IPv4-in-IPv6 addresses (e.g., ::ffff:192.0.2.1)
// as input. The methods do not perform automatic unmapping to avoid unnecessary
@@ -38,10 +34,10 @@ import (
// Users should unmap IPv4-in-IPv6 addresses to their native IPv4 form
// (e.g., 192.0.2.1) before calling these methods.
type Fast[V any] struct {
- // used for zero-sized type checks during insert
- once sync.Once
+ // used by -copylocks checker from `go vet`.
+ _ [0]sync.Mutex
- // the root nodes are fast nodes with fixed size arrays
+ // the root nodes, implemented as popcount compressed multibit tries
root4 nodes.FastNode[V]
root6 nodes.FastNode[V]
@@ -50,7 +46,7 @@ type Fast[V any] struct {
size6 int
}
-// rootNodeByVersion, root node getter for ip version and trie levels.
+// rootNodeByVersion, root node getter for ip version.
func (f *Fast[V]) rootNodeByVersion(is4 bool) *nodes.FastNode[V] {
if is4 {
return &f.root4
@@ -65,7 +61,6 @@ func (f *Fast[V]) rootNodeByVersion(is4 bool) *nodes.FastNode[V] {
// The prefix is automatically canonicalized using pfx.Masked() to ensure
// consistent behavior regardless of host bits in the input.
func (f *Fast[V]) Insert(pfx netip.Prefix, val V) {
- f.once.Do(value.PanicOnZST[V])
f.insert(pfx, val)
}
@@ -73,7 +68,7 @@ func (f *Fast[V]) Insert(pfx netip.Prefix, val V) {
//
// All nodes touched during insert are cloned and a new Fast is returned.
// This is not a full [Fast.Clone], all untouched nodes are still referenced
-// from both Tables.
+// from both Fasts.
//
// If the payload type V contains pointers or needs deep copying,
// it must implement the Clone method to support correct cloning.
@@ -81,7 +76,6 @@ func (f *Fast[V]) Insert(pfx netip.Prefix, val V) {
// Due to cloning overhead this is significantly slower than Insert,
// typically taking μsec instead of nsec.
func (f *Fast[V]) InsertPersist(pfx netip.Prefix, val V) *Fast[V] {
- f.once.Do(value.PanicOnZST[V])
return f.insertPersist(pfx, val)
}
@@ -92,6 +86,8 @@ func (f *Fast[V]) InsertPersist(pfx netip.Prefix, val V) *Fast[V] {
// return a new value and a delete flag: del == false inserts or updates,
// del == true deletes the entry if it exists (otherwise no-op).
//
+// The callback is invoked at most once per call.
+//
// The operation is determined by the callback function, which is called with:
//
// val: the current value (or zero value if not found)
@@ -105,14 +101,13 @@ func (f *Fast[V]) InsertPersist(pfx netip.Prefix, val V) *Fast[V] {
// Summary of callback semantics:
//
// | cb-input | cb-return | Ops |
-// ------------------------------------- --------
+// |-----------------|-----------------|--------|
// | (zero, false) | (_, true) | no-op |
// | (zero, false) | (newVal, false) | insert |
// | (oldVal, true) | (newVal, false) | update |
// | (oldVal, true) | (_, true) | delete |
// ------------------------------------- --------
func (f *Fast[V]) Modify(pfx netip.Prefix, cb func(_ V, ok bool) (_ V, del bool)) {
- f.once.Do(value.PanicOnZST[V])
if !pfx.IsValid() {
return
}
@@ -134,9 +129,9 @@ func (f *Fast[V]) Modify(pfx netip.Prefix, cb func(_ V, ok bool) (_ V, del bool)
// This performs longest-prefix matching and returns true if any prefix
// in the routing table contains the IP address, regardless of the associated value.
//
-// It does not return the value nor the prefix of the matching item,
+// It does not return the value or the prefix of the matching item,
// but as a test against an allow-/deny-list it's often sufficient
-// and even few nanoseconds faster than [Table.Lookup].
+// and even few nanoseconds faster than [Fast.Lookup].
func (f *Fast[V]) Contains(ip netip.Addr) bool {
// speed is top priority: no explicit test for ip.IsValid
// if ip is invalid, AsSlice() returns nil, Contains returns false.
@@ -144,28 +139,27 @@ func (f *Fast[V]) Contains(ip netip.Addr) bool {
n := f.rootNodeByVersion(is4)
for _, octet := range ip.AsSlice() {
- if n.Contains(art.OctetToIdx(octet)) {
+ // for contains, any lpm match is good enough, no backtracking needed
+ if n.PrefixCount() != 0 && n.Contains(art.OctetToIdx(octet)) {
return true
}
- kidAny, exists := n.GetChild(octet)
- if !exists {
- // no next node
+ // stop traversing?
+ if !n.Children.Test(octet) {
return false
}
+ kid := n.MustGetChild(octet)
// kid is node or leaf or fringe at octet
- switch kid := kidAny.(type) {
+ switch kid := kid.(type) {
case *nodes.FastNode[V]:
- n = kid // continue
+ n = kid // descend down to next trie level
case *nodes.FringeNode[V]:
// fringe is the default-route for all possible octets below
return true
case *nodes.LeafNode[V]:
- // due to path compression, the octet path between
- // leaf and prefix may diverge
return kid.Prefix.Contains(ip)
}
}
@@ -173,56 +167,83 @@ func (f *Fast[V]) Contains(ip netip.Addr) bool {
return false
}
-// Lookup performs longest-prefix matching for the given IP address and returns
-// the associated value of the most specific matching prefix.
-// Returns the zero value of V and false if no prefix matches.
-// Returns false for invalid IP addresses.
+// Lookup performs a longest prefix match (LPM) lookup for the given address.
+// It finds the most specific (longest) prefix in the routing table that
+// contains the given address and returns its associated value.
//
-// This is the core routing table operation used for packet forwarding decisions.
+// This is the fundamental operation for IP routing decisions, finding the
+// best matching route for a destination address.
//
-// Its semantics are identical to [Table.Lookup].
+// Returns the associated value and true if a matching prefix is found.
+// Returns zero value and false if no prefix contains the address.
func (f *Fast[V]) Lookup(ip netip.Addr) (val V, ok bool) {
if !ip.IsValid() {
return val, ok
}
is4 := ip.Is4()
+ octets := ip.AsSlice()
+
n := f.rootNodeByVersion(is4)
- for _, octet := range ip.AsSlice() {
- // save the current best LPM val, lookup is cheap for nodes.FastNode
- if bestLPM, ok2 := n.Lookup(art.OctetToIdx(octet)); ok2 {
- val = bestLPM
- ok = ok2
- }
+ // stack of the traversed nodes for fast backtracking, if needed
+ stack := [nodes.MaxTreeDepth]*nodes.FastNode[V]{}
- kidAny, exists := n.GetChild(octet)
- if !exists {
- // no next node
- return val, ok
+ // run variable, used after for loop
+ var depth int
+ var octet byte
+
+LOOP:
+ // find leaf node
+ for depth, octet = range octets {
+ depth &= nodes.DepthMask // BCE, Lookup must be fast
+
+ // push current node on stack for fast backtracking
+ stack[depth] = n
+
+ // go down in tight loop to last octet
+ if !n.Children.Test(octet) {
+ // no more nodes below octet
+ break LOOP
}
+ kid := n.MustGetChild(octet)
- // next kid is fast, fringe or leaf node.
- switch kid := kidAny.(type) {
+ // kid is node or leaf or fringe at octet
+ switch kid := kid.(type) {
case *nodes.FastNode[V]:
n = kid
+ continue LOOP // descend down to next trie level
case *nodes.FringeNode[V]:
- // fringe is the default-route for all possible nodes below
+ // fringe is the default-route for all possible octets below
return kid.Value, true
case *nodes.LeafNode[V]:
- // due to path compression, the octet path between
- // leaf and prefix may diverge
if kid.Prefix.Contains(ip) {
return kid.Value, true
}
- // maybe there is a current best value from upper levels
- return val, ok
+ // reached a path compressed prefix, stop traversing
+ break LOOP
+ }
+ }
+
+ // start backtracking, unwind the stack, bounds check eliminated
+ for ; depth >= 0; depth-- {
+ depth &= nodes.DepthMask // BCE
+
+ n = stack[depth]
+
+ // longest prefix match, skip if node has no prefixes
+ if n.PrefixCount() != 0 {
+ idx := art.OctetToIdx(octets[depth])
+ // lookupIdx() manually inlined
+ if lpmIdx, ok2 := n.Prefixes.IntersectionTop(&lpm.LookupTbl[idx]); ok2 {
+ return n.MustGetPrefix(lpmIdx), ok2
+ }
}
}
- panic("unreachable")
+ return val, ok
}
// LookupPrefix performs a longest prefix match lookup for any address within
@@ -244,7 +265,7 @@ func (f *Fast[V]) LookupPrefix(pfx netip.Prefix) (val V, ok bool) {
// match any address in the provided prefix range.
//
// This is functionally identical to LookupPrefix but additionally returns the
-// matching prefix (lpmPfx) itself along with the value.
+// matching LPM prefix itself along with the value.
//
// This method is slower than LookupPrefix and should only be used if the
// matching lpm entry is also required for other reasons.
@@ -280,7 +301,7 @@ func (f *Fast[V]) lookupPrefixLPM(pfx netip.Prefix, withLPM bool) (lpmPfx netip.
LOOP:
// find the last node on the octets path in the trie,
for depth, octet = range octets {
- depth = depth & nodes.DepthMask // BCE
+ depth &= nodes.DepthMask // BCE
// stepped one past the last stride of interest; back up to last and break
if depth > lastOctetPlusOne {
@@ -291,13 +312,13 @@ LOOP:
stack[depth] = n
// go down in tight loop to leaf node
- kidAny, exists := n.GetChild(octet)
- if !exists {
+ if !n.Children.Test(octet) {
break LOOP
}
+ kid := n.MustGetChild(octet)
// kid is node or leaf or fringe at octet
- switch kid := kidAny.(type) {
+ switch kid := kid.(type) {
case *nodes.FastNode[V]:
n = kid
continue LOOP // descend down to next trie level
@@ -331,12 +352,12 @@ LOOP:
// start backtracking, unwind the stack
for ; depth >= 0; depth-- {
- depth = depth & nodes.DepthMask // BCE
+ depth &= nodes.DepthMask // BCE
n = stack[depth]
// longest prefix match, skip if node has no prefixes
- if n.PfxCount == 0 {
+ if n.Prefixes.Len() == 0 {
continue
}
@@ -353,25 +374,27 @@ LOOP:
idx = art.OctetToIdx(octet)
}
- switch withLPM {
- case false: // LookupPrefix
- if val, ok = n.Lookup(idx); ok {
+ // manually inlined: lookupIdx(idx)
+ var topIdx uint8
+ if topIdx, ok = n.Prefixes.IntersectionTop(&lpm.LookupTbl[idx]); ok {
+ val = n.MustGetPrefix(topIdx)
+
+ // called from LookupPrefix
+ if !withLPM {
return netip.Prefix{}, val, ok
}
- case true: // LookupPrefixLPM
- if lpmIdx, val2, ok2 := n.LookupIdx(idx); ok2 {
- // get the bits from depth and lpmIdx
- pfxBits := int(art.PfxBits(depth, lpmIdx))
+ // called from LookupPrefixLPM
- // calculate the lpmPfx from incoming ip and new mask
- // netip.Addr.Prefix canonicalizes. Invariant: art.PfxBits(depth, topIdx)
- // yields a valid mask (v4: 0..32, v6: 0..128), so error is impossible.
- lpmPfx, _ = ip.Prefix(pfxBits)
- return lpmPfx, val2, ok2
- }
+ // get the bits from depth and top idx
+ pfxBits := int(art.PfxBits(depth, topIdx))
+
+ // calculate the lpmPfx from incoming ip and new mask
+ // netip.Addr.Prefix canonicalizes. Invariant: art.PfxBits(depth, topIdx)
+ // yields a valid mask (v4: 0..32, v6: 0..128), so error is impossible.
+ lpmPfx, _ = ip.Prefix(pfxBits)
+ return lpmPfx, val, ok
}
- // continue rewinding the stack
}
return lpmPfx, val, ok
diff --git a/vendor/github.com/gaissmai/bart/fastmethodsgenerated.go b/vendor/github.com/gaissmai/bart/fastmethodsgenerated.go
index 5c72ef4c5..9539d417a 100644
--- a/vendor/github.com/gaissmai/bart/fastmethodsgenerated.go
+++ b/vendor/github.com/gaissmai/bart/fastmethodsgenerated.go
@@ -1,6 +1,6 @@
// Code generated from file "commonmethods_tmpl.go"; DO NOT EDIT.
-// Copyright (c) 2025 Karl Gaissmaier
+// Copyright (c) 2026 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart
@@ -266,9 +266,6 @@ func (t *Fast[V]) ModifyPersist(pfx netip.Prefix, cb func(_ V, ok bool) (_ V, de
// Returns an empty iterator if the prefix is invalid.
func (t *Fast[V]) Supernets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
if !pfx.IsValid() {
return
}
@@ -298,9 +295,6 @@ func (t *Fast[V]) Supernets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
// Returns an empty iterator if the prefix is invalid.
func (t *Fast[V]) Subnets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
if !pfx.IsValid() {
return
}
@@ -351,15 +345,12 @@ func (t *Fast[V]) OverlapsPrefix(pfx netip.Prefix) bool {
// This is useful for conflict detection, policy enforcement,
// or validating mutually exclusive routing domains.
func (t *Fast[V]) Overlaps(o *Fast[V]) bool {
- if o == nil {
- return false
- }
return t.Overlaps4(o) || t.Overlaps6(o)
}
// Overlaps4 is like [Fast.Overlaps] but for the v4 routing table only.
func (t *Fast[V]) Overlaps4(o *Fast[V]) bool {
- if o == nil || t.size4 == 0 || o.size4 == 0 {
+ if t.size4 == 0 || o.size4 == 0 {
return false
}
return t.root4.Overlaps(&o.root4, 0)
@@ -367,7 +358,7 @@ func (t *Fast[V]) Overlaps4(o *Fast[V]) bool {
// Overlaps6 is like [Fast.Overlaps] but for the v6 routing table only.
func (t *Fast[V]) Overlaps6(o *Fast[V]) bool {
- if o == nil || t.size6 == 0 || o.size6 == 0 {
+ if t.size6 == 0 || o.size6 == 0 {
return false
}
return t.root6.Overlaps(&o.root6, 0)
@@ -380,7 +371,15 @@ func (t *Fast[V]) Overlaps6(o *Fast[V]) bool {
// This duplicate is shallow-copied by default, but if the value type V implements the
// Clone method, the value is deeply cloned before insertion. See also Fast.Clone.
func (t *Fast[V]) Union(o *Fast[V]) {
- if o == nil || o == t || (o.size4 == 0 && o.size6 == 0) {
+ // panics on nil receiver
+ _ = t.size4
+
+ // panics on nil argument
+ if o.size4 == 0 && o.size6 == 0 {
+ return
+ }
+ // t is unchanged
+ if o == t {
return
}
@@ -398,10 +397,17 @@ func (t *Fast[V]) Union(o *Fast[V]) {
// UnionPersist is similar to [Union] but the receiver isn't modified.
//
// All nodes touched during union are cloned and a new *Fast is returned.
-// If o is nil or empty, no nodes are touched and the receiver may be
+// If o is empty, no nodes are touched and the receiver may be
// returned unchanged.
func (t *Fast[V]) UnionPersist(o *Fast[V]) *Fast[V] {
- if o == nil || o == t || (o.size4 == 0 && o.size6 == 0) {
+ // panics on nil receiver
+ _ = t.size4
+
+ // panics on nil argument
+ if o.size4 == 0 && o.size6 == 0 {
+ return t
+ }
+ if o == t {
return t
}
@@ -453,7 +459,7 @@ func (t *Fast[V]) UnionPersist(o *Fast[V]) *Fast[V] {
// The bart package will automatically detect and use this method via Go's
// structural typing.
func (t *Fast[V]) Equal(o *Fast[V]) bool {
- if o == nil || t.size4 != o.size4 || t.size6 != o.size6 {
+ if t.size4 != o.size4 || t.size6 != o.size6 {
return false
}
if o == t {
@@ -479,10 +485,6 @@ func (t *Fast[V]) Equal(o *Fast[V]) bool {
// The bart package will automatically detect and use this method via Go's
// structural typing.
func (t *Fast[V]) Clone() *Fast[V] {
- if t == nil {
- return nil
- }
-
c := new(Fast[V])
cloneFn := value.CloneFnFactory[V]()
@@ -539,9 +541,6 @@ func (t *Fast[V]) Size6() int {
// }
func (t *Fast[V]) All() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRec(stridePath{}, 0, true, yield) && t.root6.AllRec(stridePath{}, 0, false, yield)
}
}
@@ -549,9 +548,6 @@ func (t *Fast[V]) All() iter.Seq2[netip.Prefix, V] {
// All4 is like [Fast.All] but only for the v4 routing table.
func (t *Fast[V]) All4() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRec(stridePath{}, 0, true, yield)
}
}
@@ -559,9 +555,6 @@ func (t *Fast[V]) All4() iter.Seq2[netip.Prefix, V] {
// All6 is like [Fast.All] but only for the v6 routing table.
func (t *Fast[V]) All6() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root6.AllRec(stridePath{}, 0, false, yield)
}
}
@@ -585,9 +578,6 @@ func (t *Fast[V]) All6() iter.Seq2[netip.Prefix, V] {
// traversal is required use persistent table methods.
func (t *Fast[V]) AllSorted() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRecSorted(stridePath{}, 0, true, yield) &&
t.root6.AllRecSorted(stridePath{}, 0, false, yield)
}
@@ -596,9 +586,6 @@ func (t *Fast[V]) AllSorted() iter.Seq2[netip.Prefix, V] {
// AllSorted4 is like [Fast.AllSorted] but only for the v4 routing table.
func (t *Fast[V]) AllSorted4() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRecSorted(stridePath{}, 0, true, yield)
}
}
@@ -606,9 +593,6 @@ func (t *Fast[V]) AllSorted4() iter.Seq2[netip.Prefix, V] {
// AllSorted6 is like [Fast.AllSorted] but only for the v6 routing table.
func (t *Fast[V]) AllSorted6() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root6.AllRecSorted(stridePath{}, 0, false, yield)
}
}
@@ -636,12 +620,9 @@ func (t *Fast[V]) AllSorted6() iter.Seq2[netip.Prefix, V] {
// │ └─ 2001:db8::/32 (V)
// └─ fe80::/10 (V)
func (t *Fast[V]) Fprint(w io.Writer) error {
- if w == nil {
+ if w == nil && t != nil {
return fmt.Errorf("nil writer")
}
- if t == nil {
- return nil
- }
// v4
if err := t.fprint(w, true); err != nil {
@@ -691,10 +672,6 @@ func (t *Fast[V]) MarshalText() ([]byte, error) {
// MarshalJSON dumps the table into two sorted lists: for ipv4 and ipv6.
// Every root and subnet is an array, not a map, because the order matters.
func (t *Fast[V]) MarshalJSON() ([]byte, error) {
- if t == nil {
- return []byte("null"), nil
- }
-
result := struct {
Ipv4 []DumpListNode[V] `json:"ipv4,omitempty"`
Ipv6 []DumpListNode[V] `json:"ipv6,omitempty"`
@@ -714,18 +691,12 @@ func (t *Fast[V]) MarshalJSON() ([]byte, error) {
// DumpList4 dumps the ipv4 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build the text or JSON serialization.
func (t *Fast[V]) DumpList4() []DumpListNode[V] {
- if t == nil {
- return nil
- }
return t.dumpListRec(&t.root4, 0, stridePath{}, 0, true)
}
// DumpList6 dumps the ipv6 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build custom JSON representation.
func (t *Fast[V]) DumpList6() []DumpListNode[V] {
- if t == nil {
- return nil
- }
return t.dumpListRec(&t.root6, 0, stridePath{}, 0, false)
}
@@ -775,10 +746,6 @@ func (t *Fast[V]) dumpString() string {
// dump the table structure and all the nodes to w.
func (t *Fast[V]) dump(w io.Writer) {
- if t == nil {
- return
- }
-
if t.size4 > 0 {
stats := t.root4.StatsRec()
fmt.Fprintln(w)
diff --git a/vendor/github.com/gaissmai/bart/internal/art/base_index.go b/vendor/github.com/gaissmai/bart/internal/art/base_index.go
index 06bcaed71..ff264d24a 100644
--- a/vendor/github.com/gaissmai/bart/internal/art/base_index.go
+++ b/vendor/github.com/gaissmai/bart/internal/art/base_index.go
@@ -37,9 +37,19 @@ func PfxToIdx(octet, pfxLen uint8) uint8 {
return octet>>(8-pfxLen) + 1< [128..255] to start
+// with the parent (>>1) in the complete binary tree.
+//
+// Same formula as PfxToIdx, but for octet/8 and a shift (>>1).
+//
+// octet>>(8-pfxLen) + 1<>(8-8) + 1<<8
+// octet + 256
+//
+// got to parent idx:
+// (octet+256)>>1 == octet>>1 + 128
func OctetToIdx(octet uint8) uint8 {
- return 128 + octet>>1
+ return octet>>1 + 128
}
// IdxToPfx returns the octet and prefix len of baseIdx.
diff --git a/vendor/github.com/gaissmai/bart/internal/nodes/bart.go b/vendor/github.com/gaissmai/bart/internal/nodes/bart.go
index f5ab16921..0fa142687 100644
--- a/vendor/github.com/gaissmai/bart/internal/nodes/bart.go
+++ b/vendor/github.com/gaissmai/bart/internal/nodes/bart.go
@@ -14,13 +14,13 @@ import (
// BartNode is a trie level node in the multibit routing table.
//
// Each BartNode contains two conceptually different arrays:
+//
// - Prefixes stores routing entries (prefix -> value),
// laid out as a complete binary tree using the baseIndex()
// function from the ART algorithm.
+//
// - Children: holding subtries or path-compressed leaves/fringes with
// a branching factor of 256 (8 bits per stride).
-// - Children holds subnodes for the 256 possible next-hop paths
-// at this trie level (8-bit stride).
//
// Entries in Children may be:
// - *BartNode[V] -> internal child node for further traversal
@@ -63,7 +63,8 @@ func (n *BartNode[V]) ChildCount() int {
// It returns true if a prefix already existed at that index (indicating an update),
// false if this is a new insertion.
func (n *BartNode[V]) InsertPrefix(idx uint8, val V) (exists bool) {
- return n.Prefixes.InsertAt(idx, val)
+ _, exists = n.Prefixes.InsertAt(idx, val)
+ return
}
// GetPrefix retrieves the value associated with the prefix at the given index.
@@ -83,9 +84,8 @@ func (n *BartNode[V]) MustGetPrefix(idx uint8) (val V) {
func (n *BartNode[V]) AllIndices() iter.Seq2[uint8, V] {
return func(yield func(uint8, V) bool) {
var buf [256]uint8
- for _, idx := range n.Prefixes.AsSlice(&buf) {
- val := n.MustGetPrefix(idx)
- if !yield(idx, val) {
+ for i, idx := range n.Prefixes.AsSlice(&buf) {
+ if !yield(idx, n.Prefixes.Items[i]) {
return
}
}
@@ -103,7 +103,8 @@ func (n *BartNode[V]) DeletePrefix(idx uint8) (exists bool) {
// The child can be a *BartNode[V], *LeafNode[V], or *FringeNode[V].
// Returns true if a child already existed at that address.
func (n *BartNode[V]) InsertChild(addr uint8, child any) (exists bool) {
- return n.Children.InsertAt(addr, child)
+ _, exists = n.Children.InsertAt(addr, child)
+ return
}
// GetChild retrieves the child node at the specified address.
@@ -125,8 +126,7 @@ func (n *BartNode[V]) AllChildren() iter.Seq2[uint8, any] {
var buf [256]uint8
addrs := n.Children.AsSlice(&buf)
for i, addr := range addrs {
- child := n.Children.Items[i]
- if !yield(addr, child) {
+ if !yield(addr, n.Children.Items[i]) {
return
}
}
@@ -172,7 +172,7 @@ func (n *BartNode[V]) LookupIdx(idx uint8) (top uint8, val V, ok bool) {
return top, val, ok
}
-// Lookup is just a simple wrapper for lookupIdx.
+// Lookup is just a simple wrapper for LookupIdx.
func (n *BartNode[V]) Lookup(idx uint8) (val V, ok bool) {
_, val, ok = n.LookupIdx(idx)
return val, ok
diff --git a/vendor/github.com/gaissmai/bart/internal/nodes/bartmethodsgenerated.go b/vendor/github.com/gaissmai/bart/internal/nodes/bartmethodsgenerated.go
index 69c0e8a1c..c23813eb6 100644
--- a/vendor/github.com/gaissmai/bart/internal/nodes/bartmethodsgenerated.go
+++ b/vendor/github.com/gaissmai/bart/internal/nodes/bartmethodsgenerated.go
@@ -294,7 +294,7 @@ func (n *BartNode[V]) Delete(pfx netip.Prefix) (exists bool) {
// find the trie node
for depth, octet := range octets {
- depth = depth & DepthMask // BCE, Delete must be fast
+ depth &= DepthMask // BCE, Delete must be fast
// push current node on stack for path recording
stack[depth] = n
@@ -536,7 +536,7 @@ func (n *BartNode[V]) Modify(pfx netip.Prefix, cb func(val V, found bool) (_ V,
// find the proper trie node to update prefix
for depth, octet := range octets {
- depth = depth & DepthMask // BCE
+ depth &= DepthMask // BCE
// push current node on stack for path recording
stack[depth] = n
diff --git a/vendor/github.com/gaissmai/bart/internal/nodes/commonmethods_tmpl.go b/vendor/github.com/gaissmai/bart/internal/nodes/commonmethods_tmpl.go
index bdcee61cb..92893f58a 100644
--- a/vendor/github.com/gaissmai/bart/internal/nodes/commonmethods_tmpl.go
+++ b/vendor/github.com/gaissmai/bart/internal/nodes/commonmethods_tmpl.go
@@ -328,7 +328,7 @@ func (n *_NODE_TYPE[V]) Delete(pfx netip.Prefix) (exists bool) {
// find the trie node
for depth, octet := range octets {
- depth = depth & DepthMask // BCE, Delete must be fast
+ depth &= DepthMask // BCE, Delete must be fast
// push current node on stack for path recording
stack[depth] = n
@@ -570,7 +570,7 @@ func (n *_NODE_TYPE[V]) Modify(pfx netip.Prefix, cb func(val V, found bool) (_ V
// find the proper trie node to update prefix
for depth, octet := range octets {
- depth = depth & DepthMask // BCE
+ depth &= DepthMask // BCE
// push current node on stack for path recording
stack[depth] = n
diff --git a/vendor/github.com/gaissmai/bart/internal/nodes/fast.go b/vendor/github.com/gaissmai/bart/internal/nodes/fast.go
index 2248fc8ec..66e1c3d11 100644
--- a/vendor/github.com/gaissmai/bart/internal/nodes/fast.go
+++ b/vendor/github.com/gaissmai/bart/internal/nodes/fast.go
@@ -6,236 +6,190 @@ package nodes
import (
"iter"
- "github.com/gaissmai/bart/internal/bitset"
"github.com/gaissmai/bart/internal/lpm"
+ "github.com/gaissmai/bart/internal/sparse"
"github.com/gaissmai/bart/internal/value"
)
-// FastNode is a trie level node in the multibit routing table.
+// Each FastNode contains three conceptually different arrays:
//
-// Each FastNode contains two sections (Prefixes and Children), each with
-// a BitSet256 tracking occupied indices and a fixed-size Items array:
-// - Prefixes.BitSet256: tracks which prefix indices are occupied.
-// - Prefixes.Items: [256]*V storing routing entry values.
-// - Children.BitSet256: tracks which child indices are occupied.
-// - Children.Items: [256]*any holding subtries or path-compressed leaves/fringes
-// (branching factor 256, 8 bits per stride).
-// - PfxCount: cached count of active prefixes (avoids BitSet.Size() calls).
-// - CldCount: cached count of child nodes (avoids BitSet.Size() calls).
+// - Prefixes stores routing entries (prefix -> value),
+// laid out as a complete binary tree using the baseIndex()
+// function from the ART algorithm.
//
-// Prefixes use a complete binary tree layout driven by the baseIndex() function
-// from the ART algorithm for fast LPM lookup.
+// - Children: holding subtries or path-compressed leaves/fringes with
+// a branching factor of 256 (8 bits per stride).
//
-// Entries in Children.Items (each *any containing a pointer) may be:
-// - **FastNode[V] -> internal child node for further traversal
-// - **LeafNode[V] -> path-comp. node (depth < maxDepth - 1)
-// - **FringeNode[V] -> path-comp. node (depth == maxDepth - 1, stride-aligned)
+// - childRankCache for cached rank values for fast trie traversal.
//
-// Note: Children.Items uses *any (pointer to any) instead of any to reduce memory by
-// ~30%, since many slots are nil and *any takes 1 word vs 2 words for nil any.
+// Entries in Children may be:
+// - *FastNode[V] -> internal child node for further traversal
+// - *LeafNode[V] -> path-comp. node (depth < maxDepth - 1)
+// - *FringeNode[V] -> path-comp. node (depth == maxDepth - 1, stride-aligned: /8, /16, ... /128)
+//
+// FastNode is based on [BartNode], but it also uses a cache ([256]uint8)
+// per node to speed up traversal of the multi-bit trie. Lookups become
+// faster, but this requires more memory per prefix,
+// and updates (insertions/deletions) also become slower due to the overhead
+// of managing the cache.
type FastNode[V any] struct {
- // Prefixes holding prefix -> value pointers, organized as a CBT
- // for fast LPM lookup within the node.
- Prefixes struct {
- bitset.BitSet256
- Items [256]*V
- }
+ Prefixes sparse.Array256[V]
+ Children sparse.Array256[any]
+ // map addr to slice idx (aka rank)
+ childRankCache [256]uint8
+}
- // Children holding subtries or path-compressed leaves or fringes.
- Children struct {
- bitset.BitSet256
- Items [256]*any // pointer to any, see explanation above
+// IsEmpty returns true if the node contains no routing entries (prefixes)
+// and no child nodes. Empty nodes are candidates for compression or removal
+// during trie optimization.
+func (n *FastNode[V]) IsEmpty() bool {
+ if n == nil {
+ return true
}
-
- // PfxCount replaces expensive BitSet256.Size() calls. Automatically
- // maintained during InsertPrefix() and DeletePrefix() operations.
- PfxCount uint16
-
- // CldCount replaces expensive BitSet.Size() calls. Automatically
- // maintained during InsertChild() and DeleteChild() operations.
- CldCount uint16
+ return n.Prefixes.Len() == 0 && n.Children.Len() == 0
}
// PrefixCount returns the number of prefixes stored in this node.
func (n *FastNode[V]) PrefixCount() int {
- return int(n.PfxCount)
+ return n.Prefixes.Len()
}
// ChildCount returns the number of slots used in this node.
func (n *FastNode[V]) ChildCount() int {
- return int(n.CldCount)
+ return n.Children.Len()
}
-// IsEmpty returns true if node has neither prefixes nor children
-func (n *FastNode[V]) IsEmpty() bool {
- if n == nil {
- return true
- }
- return n.PfxCount == 0 && n.CldCount == 0
+// InsertPrefix adds or updates a routing entry at the specified index with the given value.
+// It returns true if a prefix already existed at that index (indicating an update),
+// false if this is a new insertion.
+func (n *FastNode[V]) InsertPrefix(idx uint8, val V) (exists bool) {
+ _, exists = n.Prefixes.InsertAt(idx, val)
+ return
}
-// GetChild returns the child node at the specified address and true if it exists.
-// If no child exists at addr, returns nil and false.
-func (n *FastNode[V]) GetChild(addr uint8) (any, bool) {
- if anyPtr := n.Children.Items[addr]; anyPtr != nil {
- return *anyPtr, true
- }
- return nil, false
+// GetPrefix retrieves the value associated with the prefix at the given index.
+// Returns the value and true if found, or zero value and false if not present.
+func (n *FastNode[V]) GetPrefix(idx uint8) (val V, exists bool) {
+ return n.Prefixes.Get(idx)
}
-// MustGetChild returns the child node at the specified address.
-// Panics if no child exists at addr. This method should only be called
-// when the caller has verified the child exists.
-func (n *FastNode[V]) MustGetChild(addr uint8) any {
- // panics if n.children[addr] is nil
- return *n.Children.Items[addr]
+// MustGetPrefix retrieves the value at the specified index, panicking if not found.
+// This method should only be used when the caller is certain the index exists.
+func (n *FastNode[V]) MustGetPrefix(idx uint8) (val V) {
+ return n.Prefixes.MustGet(idx)
}
-// AllChildren returns an iterator over all child nodes.
-// Each iteration yields the child's address (uint8) and the child node (any).
-func (n *FastNode[V]) AllChildren() iter.Seq2[uint8, any] {
- return func(yield func(addr uint8, child any) bool) {
+// AllIndices returns an iterator over all prefix entries.
+// Each iteration yields the prefix index (uint8) and its associated value (V).
+func (n *FastNode[V]) AllIndices() iter.Seq2[uint8, V] {
+ return func(yield func(uint8, V) bool) {
var buf [256]uint8
- for _, addr := range n.Children.AsSlice(&buf) {
- child := *n.Children.Items[addr]
- if !yield(addr, child) {
+ for i, idx := range n.Prefixes.AsSlice(&buf) {
+ if !yield(idx, n.Prefixes.Items[i]) {
return
}
}
}
}
-// InsertChild inserts a child node at the specified address.
-// Returns true if a child already existed at addr (overwrite case),
-// false if this is a new insertion.
+// DeletePrefix removes the prefix at the specified index.
+// Returns true if the prefix existed, otherwise false.
+func (n *FastNode[V]) DeletePrefix(idx uint8) (exists bool) {
+ _, exists = n.Prefixes.DeleteAt(idx)
+ return exists
+}
+
+// InsertChild adds a child node at the specified address (0-255).
+// The child can be a *FastNode[V], *LeafNode[V], or *FringeNode[V].
+// Returns true if a child already existed at that address.
func (n *FastNode[V]) InsertChild(addr uint8, child any) (exists bool) {
- if p := n.Children.Items[addr]; p != nil {
- // Reuse existing *any slot to cut allocations and GC churn
- *p = child // overwrite
- return true
+ var rank0 int
+ rank0, exists = n.Children.InsertAt(addr, child)
+ if exists {
+ // Update only: the value at addr is overwritten in-place by InsertAt.
+ // childRankCache[addr] is not refreshed because the rank is unchanged
+ // the position of addr in the sparse slice does not shift on an update.
+ return
}
- n.Children.Set(addr)
- n.CldCount++
-
- // pointer to any reduces per-slot memory for nil entries versus storing `any` directly.
- p := new(any)
- *p = child
- n.Children.Items[addr] = p
+ // new child inserted? cache the rank value for this addr
+ //nolint:gosec // G115: integer overflow conversion int -> uint8
+ n.childRankCache[addr] = uint8(rank0)
- return false
+ // increment all cached ranks after addr
+ for i := 255; i > int(addr); i-- {
+ n.childRankCache[i]++
+ }
+ return
}
// DeleteChild removes the child node at the specified address.
// This operation is idempotent - removing a non-existent child is safe.
func (n *FastNode[V]) DeleteChild(addr uint8) (exists bool) {
- if n.Children.Items[addr] == nil {
- return false
- }
- n.CldCount--
-
- n.Children.Clear(addr)
- n.Children.Items[addr] = nil
- return true
-}
+ _, exists = n.Children.DeleteAt(addr)
-// InsertPrefix adds or updates a routing entry at the specified index with the given value.
-// It returns true if a prefix already existed at that index (indicating an update),
-// false if this is a new insertion.
-func (n *FastNode[V]) InsertPrefix(idx uint8, val V) (exists bool) {
- if exists = n.Prefixes.Test(idx); !exists {
- n.Prefixes.Set(idx)
- n.PfxCount++
+ // nothing deleted
+ if !exists {
+ return exists
}
- // insert or update
-
- // To ensure allot works as intended, every unique prefix in the
- // FastNode must point to a distinct value pointer, even for identical values.
- // Using new() and assignment guarantees each inserted prefix gets its own address,
- valPtr := new(V)
- *valPtr = val
-
- oldValPtr := n.Prefixes.Items[idx]
-
- // overwrite oldValPtr with valPtr
- n.allot(idx, oldValPtr, valPtr)
+ // decrement all cached ranks after addr
+ for i := 255; i > int(addr); i-- {
+ n.childRankCache[i]--
+ }
return exists
}
-// GetPrefix returns the value for the given prefix index and true if it exists.
-// If no prefix exists at idx, returns the zero value and false.
-func (n *FastNode[V]) GetPrefix(idx uint8) (val V, exists bool) {
- if exists = n.Prefixes.Test(idx); exists {
- val = *n.Prefixes.Items[idx]
+// GetChild retrieves the child node at the specified address.
+// Returns the child and true if found, or nil and false if not present.
+func (n *FastNode[V]) GetChild(addr uint8) (any, bool) {
+ if n.Children.Test(addr) {
+ rank0 := n.childRankCache[addr]
+ return n.Children.Items[rank0], true
}
- return val, exists
+ return nil, false
}
-// MustGetPrefix returns the value for the given prefix index.
-// Panics if no prefix exists at idx. This method should only be called
-// when the caller has verified the prefix exists.
-func (n *FastNode[V]) MustGetPrefix(idx uint8) V {
- return *n.Prefixes.Items[idx]
+// MustGetChild retrieves the child at addr using the pre-cached rank stored in
+// childRankCache[addr] for a direct O(1) array access without an existence check.
+//
+// The caller must guarantee that addr is present (Children.Test(addr) == true).
+// If addr is absent, childRankCache[addr] contains the number of occupied
+// addresses less than addr (maintained by InsertChild/DeleteChild), so the
+// behaviour is undefined: either a wrong child is returned silently, or the
+// call panics with an index-out-of-range error.
+func (n *FastNode[V]) MustGetChild(addr uint8) any {
+ rank0 := n.childRankCache[addr]
+ return n.Children.Items[rank0]
}
-// AllIndices returns an iterator over all prefix entries.
-// Each iteration yields the prefix index (uint8) and its associated value (V).
-func (n *FastNode[V]) AllIndices() iter.Seq2[uint8, V] {
- return func(yield func(uint8, V) bool) {
+// AllChildren returns an iterator over all child nodes.
+// Each iteration yields the child's address (uint8) and the child node (any).
+func (n *FastNode[V]) AllChildren() iter.Seq2[uint8, any] {
+ return func(yield func(addr uint8, child any) bool) {
var buf [256]uint8
- for _, idx := range n.Prefixes.AsSlice(&buf) {
- val := n.MustGetPrefix(idx)
- if !yield(idx, val) {
+ addrs := n.Children.AsSlice(&buf)
+ for i, addr := range addrs {
+ if !yield(addr, n.Children.Items[i]) {
return
}
}
}
}
-// DeletePrefix removes the route at the given index.
-// Returns true if the prefix existed, otherwise false.
-func (n *FastNode[V]) DeletePrefix(idx uint8) (exists bool) {
- if exists = n.Prefixes.Test(idx); !exists {
- // Route entry doesn't exist
- return exists
- }
- n.PfxCount--
-
- valPtr := n.Prefixes.Items[idx]
- parentValPtr := n.Prefixes.Items[idx>>1]
-
- // delete -> overwrite valPtr with parentValPtr
- n.allot(idx, valPtr, parentValPtr)
-
- n.Prefixes.Clear(idx)
- return true
-}
-
-// Contains returns true if the given index has any matching longest-prefix
-// in the current node's prefix table.
+// Contains returns true if an index (idx) has any matching longest-prefix
+// in the current node’s prefix table.
//
-// This function performs a presence check using the ART algorithm's
-// hierarchical prefix structure. It tests whether any ancestor prefix
-// exists for the given index by probing the slot at idx (children inherit
-// ancestor pointers via allot).
-func (n *FastNode[V]) Contains(idx uint8) (ok bool) {
- return n.Prefixes.Items[idx] != nil
-}
-
-// Lookup performs a longest-prefix match (LPM) Lookup for the given index
-// within the current node's prefix table in O(1).
+// This function performs a presence check without retrieving the associated value.
+// It is faster than a full lookup, as it only tests for intersection with the
+// backtracking bitset for the given index.
//
-// The function returns the matched value and true if a matching prefix exists;
-// otherwise, it returns the zero value and false. The Lookup uses the ART
-// algorithm's hierarchical structure to find the most specific
-// matching prefix.
-func (n *FastNode[V]) Lookup(idx uint8) (val V, ok bool) {
- if valPtr := n.Prefixes.Items[idx]; valPtr != nil {
- return *valPtr, true
- }
- return val, ok
+// The prefix table is structured as a complete binary tree (CBT), and LPM testing
+// is done via a bitset operation that maps the traversal path from the given index
+// toward its possible ancestors.
+func (n *FastNode[V]) Contains(idx uint8) bool {
+ return n.Prefixes.Intersects(&lpm.LookupTbl[idx])
}
// LookupIdx performs a longest-prefix match (LPM) lookup for the given index (idx)
@@ -244,63 +198,34 @@ func (n *FastNode[V]) Lookup(idx uint8) (val V, ok bool) {
// The function returns the matched base index, associated value, and true if a
// matching prefix exists at this level; otherwise, ok is false.
//
-// Its semantics are identical to [node.LookupIdx].
+// Internally, the prefix table is organized as a complete binary tree (CBT) indexed
+// via the baseIndex function. Unlike the original ART algorithm, this implementation
+// does not use an allotment-based approach. Instead, it performs CBT backtracking
+// using a bitset-based operation with a precomputed backtracking pattern specific to idx.
func (n *FastNode[V]) LookupIdx(idx uint8) (top uint8, val V, ok bool) {
// top is the idx of the longest-prefix-match
if top, ok = n.Prefixes.IntersectionTop(&lpm.LookupTbl[idx]); ok {
- return top, *n.Prefixes.Items[top], true
+ return top, n.MustGetPrefix(top), true
}
return top, val, ok
}
-// allot updates entries whose stored valPtr matches oldValPtr, in the
-// subtree rooted at idx. Matching entries have their stored oldValPtr set to
-// valPtr, and their value set to val.
-//
-// allot is the core of the ART algorithm, enabling efficient insertion/deletion
-// while preserving very fast lookups.
-//
-// Example of (uninterrupted) allotment sequence:
-//
-// addr/bits: 0/5 -> {0/5, 0/6, 4/6, 0/7, 2/7, 4/7, 6/7}
-// ╭────╮╭─────────┬────╮
-// idx: 32 -> 32 64 65 128 129 130 131
-// ╰─────────╯╰─────────────┴────╯
-//
-// Using an iterative form ensures better inlining opportunities.
-func (n *FastNode[V]) allot(idx uint8, oldValPtr, valPtr *V) {
- // iteration with stack instead of recursion
- stack := make([]uint8, 0, 256)
-
- // start idx
- stack = append(stack, idx)
-
- for i := 0; i < len(stack); i++ {
- idx = stack[i]
-
- // stop this allot path, idx already points to a more specific route.
- if n.Prefixes.Items[idx] != oldValPtr {
- continue // take next path from stack
- }
-
- // overwrite
- n.Prefixes.Items[idx] = valPtr
-
- // max idx is 255, so stop the duplication at 128 and above
- if idx >= 128 {
- continue
- }
-
- // child nodes, it's a complete binary tree
- // left: idx*2
- // right: (idx*2)+1
- stack = append(stack, idx<<1, (idx<<1)+1)
- }
+// Lookup is just a simple wrapper for LookupIdx.
+func (n *FastNode[V]) Lookup(idx uint8) (val V, ok bool) {
+ _, val, ok = n.LookupIdx(idx)
+ return val, ok
}
-// CloneFlat returns a shallow copy of the current FastNode[V],
-// Its semantics are identical to [bartNode.CloneFlat] but the
-// implementation is more complex.
+// CloneFlat returns a shallow copy of the current node, optionally performing deep copies of values.
+//
+// If cloneFn is nil, the stored values in prefixes are copied directly without modification.
+// Otherwise, cloneFn is applied to each stored value for deep cloning.
+// Child nodes are cloned shallowly: LeafNode and FringeNode children are cloned via their clone methods,
+// but child nodes of type *FastNode[V] (subnodes) are assigned as-is without recursive cloning.
+// This method does not recursively clone descendants beyond the immediate children.
+//
+// Note: The returned node is a new instance with copied slices but only shallow copies of nested nodes,
+// except for LeafNode and FringeNode children which are cloned according to cloneFn.
func (n *FastNode[V]) CloneFlat(cloneFn value.CloneFunc[V]) *FastNode[V] {
if n == nil {
return nil
@@ -311,43 +236,32 @@ func (n *FastNode[V]) CloneFlat(cloneFn value.CloneFunc[V]) *FastNode[V] {
return c
}
- // Copy counters and bitsets (by value).
- c.PfxCount = n.PfxCount
- c.CldCount = n.CldCount
- c.Prefixes.BitSet256 = n.Prefixes.BitSet256
- c.Children.BitSet256 = n.Children.BitSet256
-
- // it's a clone of the prefixes ...
- // but the allot algorithm makes it more difficult
- // see also insertPrefix
- for idx, val := range n.AllIndices() {
- newValPtr := new(V)
-
- if cloneFn == nil {
- *newValPtr = val // just copy the value
- } else {
- *newValPtr = cloneFn(val) // clone the value
- }
+ // copy ...
+ c.Prefixes = *(n.Prefixes.Copy())
- oldValPtr := c.Prefixes.Items[idx] // likely nil initially
- c.allot(idx, oldValPtr, newValPtr)
+ // ... and clone the values
+ if cloneFn != nil {
+ for i, v := range c.Prefixes.Items {
+ c.Prefixes.Items[i] = cloneFn(v)
+ }
}
- // flat clone of the children
- for addr, kidAny := range n.AllChildren() {
- switch kid := kidAny.(type) {
- case *FastNode[V]:
- // link with new *any pointer
- c.Children.Items[addr] = &kidAny
+ // copy ...
+ c.childRankCache = n.childRankCache
+ c.Children = *(n.Children.Copy())
+ // Iterate over children to flat clone leaf/fringe nodes;
+ // for *FastNode[V] children, keep shallow references (no recursive clone)
+ for i, anyKid := range c.Children.Items {
+ switch kid := anyKid.(type) {
+ case *FastNode[V]:
+ // Shallow copy
case *LeafNode[V]:
- leafAny := any(kid.CloneLeaf(cloneFn))
- c.Children.Items[addr] = &leafAny
-
+ // Clone leaf nodes, applying cloneFn as needed
+ c.Children.Items[i] = kid.CloneLeaf(cloneFn)
case *FringeNode[V]:
- fringeAny := any(kid.CloneFringe(cloneFn))
- c.Children.Items[addr] = &fringeAny
-
+ // Clone fringe nodes, applying cloneFn as needed
+ c.Children.Items[i] = kid.CloneFringe(cloneFn)
default:
panic("logic error, wrong node type")
}
@@ -356,8 +270,20 @@ func (n *FastNode[V]) CloneFlat(cloneFn value.CloneFunc[V]) *FastNode[V] {
return c
}
-// CloneRec performs a recursive deep copy of the FastNode[V] and all its descendants.
-// Its semantics are identical to [bartNode.cloneRec].
+// CloneRec performs a recursive deep copy of the node and all its descendants.
+//
+// If cloneFn is nil, the stored values are copied directly without modification.
+// Otherwise cloneFn is applied to each stored value for deep cloning.
+//
+// This method first creates a shallow clone of the current node using CloneFlat,
+// applying cloneFn to values as described there. Then it recursively clones all
+// child nodes of type *FastNode[V], performing a full deep clone down the subtree.
+//
+// Child nodes of type *LeafNode[V] and *FringeNode[V] are already cloned
+// by CloneFlat.
+//
+// Returns a new instance of FastNode[V] which is a complete deep clone of the
+// receiver node with all descendants.
func (n *FastNode[V]) CloneRec(cloneFn value.CloneFunc[V]) *FastNode[V] {
if n == nil {
return nil
@@ -367,11 +293,9 @@ func (n *FastNode[V]) CloneRec(cloneFn value.CloneFunc[V]) *FastNode[V] {
c := n.CloneFlat(cloneFn)
// Recursively clone all child nodes of type *FastNode[V]
- for addr, kidAny := range c.AllChildren() {
- switch kid := kidAny.(type) {
- case *FastNode[V]:
- nodeAny := any(kid.CloneRec(cloneFn))
- c.Children.Items[addr] = &nodeAny
+ for i, kidAny := range c.Children.Items {
+ if kid, ok := kidAny.(*FastNode[V]); ok {
+ c.Children.Items[i] = kid.CloneRec(cloneFn)
}
}
diff --git a/vendor/github.com/gaissmai/bart/internal/nodes/fastmethodsgenerated.go b/vendor/github.com/gaissmai/bart/internal/nodes/fastmethodsgenerated.go
index 6b6448a95..fa20016e3 100644
--- a/vendor/github.com/gaissmai/bart/internal/nodes/fastmethodsgenerated.go
+++ b/vendor/github.com/gaissmai/bart/internal/nodes/fastmethodsgenerated.go
@@ -294,7 +294,7 @@ func (n *FastNode[V]) Delete(pfx netip.Prefix) (exists bool) {
// find the trie node
for depth, octet := range octets {
- depth = depth & DepthMask // BCE, Delete must be fast
+ depth &= DepthMask // BCE, Delete must be fast
// push current node on stack for path recording
stack[depth] = n
@@ -536,7 +536,7 @@ func (n *FastNode[V]) Modify(pfx netip.Prefix, cb func(val V, found bool) (_ V,
// find the proper trie node to update prefix
for depth, octet := range octets {
- depth = depth & DepthMask // BCE
+ depth &= DepthMask // BCE
// push current node on stack for path recording
stack[depth] = n
diff --git a/vendor/github.com/gaissmai/bart/internal/nodes/lite.go b/vendor/github.com/gaissmai/bart/internal/nodes/lite.go
index f5fcbdcaf..fff677135 100644
--- a/vendor/github.com/gaissmai/bart/internal/nodes/lite.go
+++ b/vendor/github.com/gaissmai/bart/internal/nodes/lite.go
@@ -57,7 +57,7 @@ func (n *LiteNode[V]) ChildCount() int {
}
// InsertPrefix adds a routing entry at the specified index.
-// It returns true if a prefix already existed at that index
+// It returns true if a prefix already existed at that index,
// false if this is a new insertion.
func (n *LiteNode[V]) InsertPrefix(idx uint8, _ V) (exists bool) {
if exists = n.Prefixes.Test(idx); exists {
@@ -68,13 +68,14 @@ func (n *LiteNode[V]) InsertPrefix(idx uint8, _ V) (exists bool) {
return exists
}
-// prefix is set at the given index.
func (n *LiteNode[V]) GetPrefix(idx uint8) (_ V, exists bool) {
+ // no docstring by intention
exists = n.Prefixes.Test(idx)
return
}
func (n *LiteNode[V]) MustGetPrefix(idx uint8) (_ V) {
+ // no docstring by intention
return
}
@@ -107,7 +108,8 @@ func (n *LiteNode[V]) DeletePrefix(idx uint8) (exists bool) {
// The child can be a *LiteNode[V], *LeafNode, or *FringeNode.
// Returns true if a child already existed at that address.
func (n *LiteNode[V]) InsertChild(addr uint8, child any) (exists bool) {
- return n.Children.InsertAt(addr, child)
+ _, exists = n.Children.InsertAt(addr, child)
+ return
}
// GetChild retrieves the child node at the specified address.
@@ -129,8 +131,7 @@ func (n *LiteNode[V]) AllChildren() iter.Seq2[uint8, any] {
var buf [256]uint8
addrs := n.Children.AsSlice(&buf)
for i, addr := range addrs {
- child := n.Children.Items[i]
- if !yield(addr, child) {
+ if !yield(addr, n.Children.Items[i]) {
return
}
}
@@ -171,7 +172,7 @@ func (n *LiteNode[V]) LookupIdx(idx uint8) (top uint8, _ V, ok bool) {
return
}
-// Lookup is just a simple wrapper for lookupIdx.
+// Lookup is just a simple wrapper for LookupIdx.
func (n *LiteNode[V]) Lookup(idx uint8) (_ V, ok bool) {
_, _, ok = n.LookupIdx(idx)
return
diff --git a/vendor/github.com/gaissmai/bart/internal/nodes/litemethodsgenerated.go b/vendor/github.com/gaissmai/bart/internal/nodes/litemethodsgenerated.go
index e1af8585d..99dfc5062 100644
--- a/vendor/github.com/gaissmai/bart/internal/nodes/litemethodsgenerated.go
+++ b/vendor/github.com/gaissmai/bart/internal/nodes/litemethodsgenerated.go
@@ -294,7 +294,7 @@ func (n *LiteNode[V]) Delete(pfx netip.Prefix) (exists bool) {
// find the trie node
for depth, octet := range octets {
- depth = depth & DepthMask // BCE, Delete must be fast
+ depth &= DepthMask // BCE, Delete must be fast
// push current node on stack for path recording
stack[depth] = n
@@ -536,7 +536,7 @@ func (n *LiteNode[V]) Modify(pfx netip.Prefix, cb func(val V, found bool) (_ V,
// find the proper trie node to update prefix
for depth, octet := range octets {
- depth = depth & DepthMask // BCE
+ depth &= DepthMask // BCE
// push current node on stack for path recording
stack[depth] = n
diff --git a/vendor/github.com/gaissmai/bart/internal/nodes/nodebasics.go b/vendor/github.com/gaissmai/bart/internal/nodes/nodebasics.go
index a2f1088dd..e40eccc6b 100644
--- a/vendor/github.com/gaissmai/bart/internal/nodes/nodebasics.go
+++ b/vendor/github.com/gaissmai/bart/internal/nodes/nodebasics.go
@@ -179,7 +179,7 @@ func NewFringeNode[V any](val V) *FringeNode[V] {
//
// Examples:
//
-// e.g. prefix is addr/8, or addr/16, or ... addr/128
+// e.g., prefix is addr/8, or addr/16, or ... addr/128
// depth < lastOctet : a leaf, path-compressed
// depth == lastOctet : a fringe, path-compressed
// depth == lastOctet+1: a prefix with octet/pfx == 0/0 => idx == 1, a strides default route
@@ -193,7 +193,7 @@ func IsFringe(depth int, pfx netip.Prefix) bool {
return depth == lastOctetPlusOne-1 && lastBits == 0
}
-// cmpIndexRank, sort indexes in prefix sort order.
+// CmpIndexRank, sort indexes in prefix sort order.
func CmpIndexRank(aIdx, bIdx uint8) int {
// convert idx [1..255] to prefix
aOctet, aBits := art.IdxToPfx(aIdx)
@@ -218,7 +218,7 @@ func CmpIndexRank(aIdx, bIdx uint8) int {
//
// Returns the reconstructed netip.Prefix.
func CidrFromPath(path StridePath, depth int, is4 bool, idx uint8) netip.Prefix {
- depth = depth & DepthMask // BCE
+ depth &= DepthMask // BCE
// retrieve the last octet and pfxLen
octet, pfxLen := art.IdxToPfx(idx)
@@ -257,7 +257,7 @@ func CidrFromPath(path StridePath, depth int, is4 bool, idx uint8) netip.Prefix
//
// Returns the reconstructed netip.Prefix for the fringe.
func CidrForFringe(octets []byte, depth int, is4 bool, lastOctet uint8) netip.Prefix {
- depth = depth & DepthMask // BCE
+ depth &= DepthMask // BCE
var path StridePath
copy(path[:], octets)
@@ -285,10 +285,7 @@ func CidrForFringe(octets []byte, depth int, is4 bool, lastOctet uint8) netip.Pr
// LastOctetPlusOneAndLastBits returns the count of full 8‑bit strides (bits/8)
// and the leftover bits in the final stride (bits%8) for pfx.
//
-// lastOctetPlusOne is the count of full 8‑bit strides (bits/8).
-// lastBits is the remaining bit count in the final stride (bits%8),
-//
-// ATTENTION: Split the IP prefixes at 8bit borders, count from 0.
+// ATTENTION: Split the IP prefixes at 8-bit borders, count from 0.
//
// /7, /15, /23, /31, ..., /127
//
@@ -297,16 +294,16 @@ func CidrForFringe(octets []byte, depth int, is4 bool, lastOctet uint8) netip.Pr
//
// 0.0.0.0/0 => lastOctetPlusOne: 0, lastBits: 0 (default route)
// 0.0.0.0/7 => lastOctetPlusOne: 0, lastBits: 7
-// 0.0.0.0/8 => lastOctetPlusOne: 1, lastBits: 0 (possible fringe)
-// 10.0.0.0/8 => lastOctetPlusOne: 1, lastBits: 0 (possible fringe)
+// 0.0.0.0/8 => lastOctetPlusOne: 1, lastBits: 0 (fringe candidate)
+// 10.0.0.0/8 => lastOctetPlusOne: 1, lastBits: 0 (fringe candidate)
// 10.0.0.0/22 => lastOctetPlusOne: 2, lastBits: 6
// 10.0.0.0/29 => lastOctetPlusOne: 3, lastBits: 5
-// 10.0.0.0/32 => lastOctetPlusOne: 4, lastBits: 0 (possible fringe)
+// 10.0.0.0/32 => lastOctetPlusOne: 4, lastBits: 0 (fringe candidate)
//
// ::/0 => lastOctetPlusOne: 0, lastBits: 0 (default route)
-// ::1/128 => lastOctetPlusOne: 16, lastBits: 0 (possible fringe)
+// ::1/128 => lastOctetPlusOne: 16, lastBits: 0 (fringe candidate)
// 2001:db8::/42 => lastOctetPlusOne: 5, lastBits: 2
-// 2001:db8::/56 => lastOctetPlusOne: 7, lastBits: 0 (possible fringe)
+// 2001:db8::/56 => lastOctetPlusOne: 7, lastBits: 0 (fringe candidate)
//
// /32 and /128 prefixes are special, they never form a new node,
// At the end of the trie (IPv4: depth 4, IPv6: depth 16) they are always
@@ -326,8 +323,6 @@ func LastOctetPlusOneAndLastBits(pfx netip.Prefix) (lastOctetPlusOne int, lastBi
// lastOctetPlusOne: range from 0..4 or 0..16 !ATTENTION: not 0..3 or 0..15
// lastBits: range from 0..7
bits := pfx.Bits()
-
- //nolint:gosec // G115: narrowing conversion is safe here (bits in [0..128])
return bits >> 3, uint8(bits & 7)
}
@@ -346,7 +341,7 @@ func (l *LeafNode[V]) CloneLeaf(cloneFn value.CloneFunc[V]) *LeafNode[V] {
return &LeafNode[V]{Prefix: l.Prefix, Value: cloneFn(l.Value)}
}
-// cloneFringe creates and returns a copy of the fringeNode receiver.
+// CloneFringe creates and returns a copy of the FringeNode receiver.
// If cloneFn is nil, the value is copied directly without modification.
// Otherwise, cloneFn is applied to the value for deep cloning.
func (l *FringeNode[V]) CloneFringe(cloneFn value.CloneFunc[V]) *FringeNode[V] {
diff --git a/vendor/github.com/gaissmai/bart/internal/sparse/array256.go b/vendor/github.com/gaissmai/bart/internal/sparse/array256.go
index 76a6e722a..da70fb48a 100644
--- a/vendor/github.com/gaissmai/bart/internal/sparse/array256.go
+++ b/vendor/github.com/gaissmai/bart/internal/sparse/array256.go
@@ -108,24 +108,36 @@ func (a *Array256[T]) Copy() *Array256[T] {
return c
}
-// InsertAt adds the value to the index i. If a value already exists there,
-// it is overwritten and true is returned.
+// InsertAt inserts or overwrites the value at sparse index i.
//
-// Otherwise, the value is inserted, the bit is marked, and false returned.
-func (a *Array256[T]) InsertAt(i uint8, value T) (exists bool) {
- // slot exists, overwrite value
+// If the slot at i already exists, its value is overwritten in-place and
+// (rank0, true) is returned, where rank0 = Rank(i)-1 is the 0-based index
+// into Items[].
+//
+// If the slot is new, the bit for i is set, the value is inserted into Items[]
+// at the correct packed position, and (rank0, false) is returned.
+//
+// rank0 can be cached by the caller to directly access Items[rank0]
+// without a second Rank() call.
+func (a *Array256[T]) InsertAt(i uint8, value T) (rank0 int, exists bool) {
+
+ // slot exists, just overwrite value
if a.Test(i) {
- a.Items[a.Rank(i)-1] = value
- return true
+ rank0 = a.Rank(i) - 1
+ a.Items[rank0] = value
+ return rank0, true
}
// new, insert into bitset ...
a.BitSet256.Set(i)
- // ... and slice
- a.insertItem(a.Rank(i)-1, value)
+ // recalc Rank after Set()
+ rank0 = a.Rank(i) - 1
+
+ // ... and insert value into slice
+ a.insertItem(rank0, value)
- return false
+ return rank0, false
}
// DeleteAt removes the value at index i from the sparse array,
diff --git a/vendor/github.com/gaissmai/bart/internal/tests/random/random.go b/vendor/github.com/gaissmai/bart/internal/tests/random/random.go
index 5dcd32dbd..3da7e833b 100644
--- a/vendor/github.com/gaissmai/bart/internal/tests/random/random.go
+++ b/vendor/github.com/gaissmai/bart/internal/tests/random/random.go
@@ -41,6 +41,7 @@ func Prefix6(prng *rand.Rand) netip.Prefix {
func IP4(prng *rand.Rand) netip.Addr {
var b [4]byte
for i := range b {
+ //nolint:gosec // G115: integer overflow conversion uint -> byte
b[i] = byte(prng.UintN(256))
}
return netip.AddrFrom4(b)
@@ -49,6 +50,7 @@ func IP4(prng *rand.Rand) netip.Addr {
func IP6(prng *rand.Rand) netip.Addr {
var b [16]byte
for i := range b {
+ //nolint:gosec // G115: integer overflow conversion uint -> byte
b[i] = byte(prng.UintN(256))
}
return netip.AddrFrom16(b)
diff --git a/vendor/github.com/gaissmai/bart/internal/value/value.go b/vendor/github.com/gaissmai/bart/internal/value/value.go
index 8fa7bd0b7..c146caaab 100644
--- a/vendor/github.com/gaissmai/bart/internal/value/value.go
+++ b/vendor/github.com/gaissmai/bart/internal/value/value.go
@@ -34,7 +34,6 @@
package value
import (
- "fmt"
"reflect"
)
@@ -68,14 +67,6 @@ func escapeToHeap[V any]() (*V, *V) {
return new(V), new(V)
}
-// PanicOnZST panics if V is a zero sized type.
-// bart.Fast must reject zero-sized types as payload.
-func PanicOnZST[V any]() {
- if IsZST[V]() {
- panic(fmt.Errorf("%T is a zero-sized type, not allowed as payload for bart.Fast", *new(V)))
- }
-}
-
// Equaler is a generic interface for types that can decide their own
// equality logic. It can be used to override the potentially expensive
// default comparison with [reflect.DeepEqual].
diff --git a/vendor/github.com/gaissmai/bart/lite.go b/vendor/github.com/gaissmai/bart/lite.go
index a266c558a..793a5db87 100644
--- a/vendor/github.com/gaissmai/bart/lite.go
+++ b/vendor/github.com/gaissmai/bart/lite.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2025 Karl Gaissmaier
+// Copyright (c) 2026 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart
@@ -21,6 +21,7 @@ import (
// The zero value is ready to use.
//
// A Lite table must not be copied by value; always pass by pointer.
+// Nil pointers as receivers or arguments are forbidden and will panic.
//
// Performance note: Do not pass IPv4-in-IPv6 addresses (e.g., ::ffff:192.0.2.1)
// as input. The methods do not perform automatic unmapping to avoid unnecessary
@@ -73,7 +74,7 @@ func (l *Lite) LookupPrefix(pfx netip.Prefix) bool {
// match any address in the provided prefix range.
//
// This is functionally identical to LookupPrefix but returns the
-// matching prefix (lpmPfx) itself.
+// matching LPM prefix itself.
//
// This method is slower than LookupPrefix and should only be used if the
// matching lpm entry is also required for other reasons.
@@ -185,9 +186,6 @@ func dropSeq2[V any](seq2 iter.Seq2[netip.Prefix, V]) iter.Seq[netip.Prefix] {
// Clone returns a copy of the routing table.
func (l *Lite) Clone() *Lite {
- if l == nil {
- return nil
- }
return &Lite{*l.liteTable.Clone()}
}
@@ -195,22 +193,19 @@ func (l *Lite) Clone() *Lite {
//
// All prefixes from the other table (o) are inserted into the receiver.
func (l *Lite) Union(o *Lite) {
- if o == nil {
- return
- }
l.liteTable.Union(&o.liteTable)
}
// UnionPersist is similar to [Union] but the receiver isn't modified.
//
// All nodes touched during union are cloned and a new *Lite is returned.
-// If o is nil or empty, no nodes are touched and the receiver may be
+// If o is empty, no nodes are touched and the receiver may be
// returned unchanged.
func (l *Lite) UnionPersist(o *Lite) *Lite {
- if o == nil || (o.size4 == 0 && o.size6 == 0) {
+ lp := l.liteTable.UnionPersist(&o.liteTable)
+ if lp == &l.liteTable {
return l
}
- lp := l.liteTable.UnionPersist(&o.liteTable)
//nolint:govet // copy of *lp is here by intention
return &Lite{*lp}
}
@@ -242,25 +237,16 @@ func (l *Lite) UnionPersist(o *Lite) *Lite {
// }
func (l *Lite) All() iter.Seq[netip.Prefix] {
- if l == nil {
- return func(func(netip.Prefix) bool) {}
- }
return dropSeq2(l.liteTable.All())
}
// All4 is like [Lite.All] but only for the v4 routing table.
func (l *Lite) All4() iter.Seq[netip.Prefix] {
- if l == nil {
- return func(func(netip.Prefix) bool) {}
- }
return dropSeq2(l.liteTable.All4())
}
// All6 is like [Lite.All] but only for the v6 routing table.
func (l *Lite) All6() iter.Seq[netip.Prefix] {
- if l == nil {
- return func(func(netip.Prefix) bool) {}
- }
return dropSeq2(l.liteTable.All6())
}
@@ -282,25 +268,16 @@ func (l *Lite) All6() iter.Seq[netip.Prefix] {
// prematurely terminate the iteration. If mutation of the table during
// traversal is required use persistent table methods.
func (l *Lite) AllSorted() iter.Seq[netip.Prefix] {
- if l == nil {
- return func(func(netip.Prefix) bool) {}
- }
return dropSeq2(l.liteTable.AllSorted())
}
// AllSorted4 is like [Lite.AllSorted] but only for the v4 routing table.
func (l *Lite) AllSorted4() iter.Seq[netip.Prefix] {
- if l == nil {
- return func(func(netip.Prefix) bool) {}
- }
return dropSeq2(l.liteTable.AllSorted4())
}
// AllSorted6 is like [Lite.AllSorted] but only for the v6 routing table.
func (l *Lite) AllSorted6() iter.Seq[netip.Prefix] {
- if l == nil {
- return func(func(netip.Prefix) bool) {}
- }
return dropSeq2(l.liteTable.AllSorted6())
}
@@ -318,9 +295,6 @@ func (l *Lite) AllSorted6() iter.Seq[netip.Prefix] {
// The iteration can be stopped early by breaking from the range loop.
// Returns an empty iterator if the prefix is invalid.
func (l *Lite) Subnets(pfx netip.Prefix) iter.Seq[netip.Prefix] {
- if l == nil {
- return func(func(netip.Prefix) bool) {}
- }
return dropSeq2(l.liteTable.Subnets(pfx))
}
@@ -345,9 +319,6 @@ func (l *Lite) Subnets(pfx netip.Prefix) iter.Seq[netip.Prefix] {
// fmt.Println("Matched covering route:", supernet)
// }
func (l *Lite) Supernets(pfx netip.Prefix) iter.Seq[netip.Prefix] {
- if l == nil {
- return func(func(netip.Prefix) bool) {}
- }
return dropSeq2(l.liteTable.Supernets(pfx))
}
@@ -367,25 +338,16 @@ func (l *Lite) Supernets(pfx netip.Prefix) iter.Seq[netip.Prefix] {
// It is intentionally not nil-receiver safe: calling with a nil
// receiver will panic by design.
func (l *Lite) Overlaps(o *Lite) bool {
- if o == nil {
- return false
- }
return l.liteTable.Overlaps(&o.liteTable)
}
// Overlaps4 is like [Lite.Overlaps] but for the v4 routing table only.
func (l *Lite) Overlaps4(o *Lite) bool {
- if o == nil {
- return false
- }
return l.liteTable.Overlaps4(&o.liteTable)
}
// Overlaps6 is like [Lite.Overlaps] but for the v6 routing table only.
func (l *Lite) Overlaps6(o *Lite) bool {
- if o == nil {
- return false
- }
return l.liteTable.Overlaps6(&o.liteTable)
}
@@ -395,27 +357,18 @@ func (l *Lite) Overlaps6(o *Lite) bool {
//
// Note: Lite has no payload values, so this only checks structural equality.
func (l *Lite) Equal(o *Lite) bool {
- if o == nil || l.size4 != o.size4 || l.size6 != o.size6 {
- return false
- }
return l.liteTable.Equal(&o.liteTable)
}
// DumpList4 dumps the ipv4 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build the text or JSON serialization.
func (l *Lite) DumpList4() []DumpListNode[struct{}] {
- if l == nil {
- return nil
- }
return l.liteTable.DumpList4()
}
// DumpList6 dumps the ipv6 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build custom JSON representation.
func (l *Lite) DumpList6() []DumpListNode[struct{}] {
- if l == nil {
- return nil
- }
return l.liteTable.DumpList6()
}
@@ -442,27 +395,18 @@ func (l *Lite) DumpList6() []DumpListNode[struct{}] {
// │ └─ 2001:db8::/32 (V)
// └─ fe80::/10 (V)
func (l *Lite) Fprint(w io.Writer) error {
- if l == nil {
- return nil
- }
return l.liteTable.Fprint(w)
}
// MarshalJSON dumps the table into two sorted lists: for ipv4 and ipv6.
// Every root and subnet is an array, not a map, because the order matters.
func (l *Lite) MarshalJSON() ([]byte, error) {
- if l == nil {
- return []byte("null"), nil
- }
return l.liteTable.MarshalJSON()
}
// MarshalText implements the [encoding.TextMarshaler] interface,
// just a wrapper for [liteTable.Fprint].
func (l *Lite) MarshalText() ([]byte, error) {
- if l == nil {
- return []byte{}, nil
- }
return l.liteTable.MarshalText()
}
@@ -631,7 +575,7 @@ func (l *liteTable[V]) LookupPrefix(pfx netip.Prefix) (val V, exists bool) {
// match any address in the provided prefix range.
//
// This is functionally identical to LookupPrefix but additionally returns the
-// matching prefix (lpmPfx) itself along with the value.
+// matching LPM prefix itself along with the value.
//
// This method is slower than LookupPrefix and should only be used if the
// matching lpm entry is also required for other reasons.
@@ -672,7 +616,7 @@ func (l *liteTable[V]) lookupPrefixLPM(pfx netip.Prefix, withLPM bool) (lpmPfx n
LOOP:
// find the last node on the octets path in the trie,
for depth, octet = range octets {
- depth = depth & nodes.DepthMask // BCE
+ depth &= nodes.DepthMask // BCE
// stepped one past the last stride of interest; back up to last and break
if depth > lastOctetPlusOne {
@@ -723,7 +667,7 @@ LOOP:
// start backtracking, unwind the stack
for ; depth >= 0; depth-- {
- depth = depth & nodes.DepthMask // BCE
+ depth &= nodes.DepthMask // BCE
n = stack[depth]
diff --git a/vendor/github.com/gaissmai/bart/litemethodsgenerated.go b/vendor/github.com/gaissmai/bart/litemethodsgenerated.go
index 98375180b..bc53cf6cc 100644
--- a/vendor/github.com/gaissmai/bart/litemethodsgenerated.go
+++ b/vendor/github.com/gaissmai/bart/litemethodsgenerated.go
@@ -1,6 +1,6 @@
// Code generated from file "commonmethods_tmpl.go"; DO NOT EDIT.
-// Copyright (c) 2025 Karl Gaissmaier
+// Copyright (c) 2026 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart
@@ -266,9 +266,6 @@ func (t *liteTable[V]) ModifyPersist(pfx netip.Prefix, cb func(_ V, ok bool) (_
// Returns an empty iterator if the prefix is invalid.
func (t *liteTable[V]) Supernets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
if !pfx.IsValid() {
return
}
@@ -298,9 +295,6 @@ func (t *liteTable[V]) Supernets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
// Returns an empty iterator if the prefix is invalid.
func (t *liteTable[V]) Subnets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
if !pfx.IsValid() {
return
}
@@ -351,15 +345,12 @@ func (t *liteTable[V]) OverlapsPrefix(pfx netip.Prefix) bool {
// This is useful for conflict detection, policy enforcement,
// or validating mutually exclusive routing domains.
func (t *liteTable[V]) Overlaps(o *liteTable[V]) bool {
- if o == nil {
- return false
- }
return t.Overlaps4(o) || t.Overlaps6(o)
}
// Overlaps4 is like [liteTable.Overlaps] but for the v4 routing table only.
func (t *liteTable[V]) Overlaps4(o *liteTable[V]) bool {
- if o == nil || t.size4 == 0 || o.size4 == 0 {
+ if t.size4 == 0 || o.size4 == 0 {
return false
}
return t.root4.Overlaps(&o.root4, 0)
@@ -367,7 +358,7 @@ func (t *liteTable[V]) Overlaps4(o *liteTable[V]) bool {
// Overlaps6 is like [liteTable.Overlaps] but for the v6 routing table only.
func (t *liteTable[V]) Overlaps6(o *liteTable[V]) bool {
- if o == nil || t.size6 == 0 || o.size6 == 0 {
+ if t.size6 == 0 || o.size6 == 0 {
return false
}
return t.root6.Overlaps(&o.root6, 0)
@@ -380,7 +371,15 @@ func (t *liteTable[V]) Overlaps6(o *liteTable[V]) bool {
// This duplicate is shallow-copied by default, but if the value type V implements the
// Clone method, the value is deeply cloned before insertion. See also liteTable.Clone.
func (t *liteTable[V]) Union(o *liteTable[V]) {
- if o == nil || o == t || (o.size4 == 0 && o.size6 == 0) {
+ // panics on nil receiver
+ _ = t.size4
+
+ // panics on nil argument
+ if o.size4 == 0 && o.size6 == 0 {
+ return
+ }
+ // t is unchanged
+ if o == t {
return
}
@@ -398,10 +397,17 @@ func (t *liteTable[V]) Union(o *liteTable[V]) {
// UnionPersist is similar to [Union] but the receiver isn't modified.
//
// All nodes touched during union are cloned and a new *liteTable is returned.
-// If o is nil or empty, no nodes are touched and the receiver may be
+// If o is empty, no nodes are touched and the receiver may be
// returned unchanged.
func (t *liteTable[V]) UnionPersist(o *liteTable[V]) *liteTable[V] {
- if o == nil || o == t || (o.size4 == 0 && o.size6 == 0) {
+ // panics on nil receiver
+ _ = t.size4
+
+ // panics on nil argument
+ if o.size4 == 0 && o.size6 == 0 {
+ return t
+ }
+ if o == t {
return t
}
@@ -453,7 +459,7 @@ func (t *liteTable[V]) UnionPersist(o *liteTable[V]) *liteTable[V] {
// The bart package will automatically detect and use this method via Go's
// structural typing.
func (t *liteTable[V]) Equal(o *liteTable[V]) bool {
- if o == nil || t.size4 != o.size4 || t.size6 != o.size6 {
+ if t.size4 != o.size4 || t.size6 != o.size6 {
return false
}
if o == t {
@@ -479,10 +485,6 @@ func (t *liteTable[V]) Equal(o *liteTable[V]) bool {
// The bart package will automatically detect and use this method via Go's
// structural typing.
func (t *liteTable[V]) Clone() *liteTable[V] {
- if t == nil {
- return nil
- }
-
c := new(liteTable[V])
cloneFn := value.CloneFnFactory[V]()
@@ -539,9 +541,6 @@ func (t *liteTable[V]) Size6() int {
// }
func (t *liteTable[V]) All() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRec(stridePath{}, 0, true, yield) && t.root6.AllRec(stridePath{}, 0, false, yield)
}
}
@@ -549,9 +548,6 @@ func (t *liteTable[V]) All() iter.Seq2[netip.Prefix, V] {
// All4 is like [liteTable.All] but only for the v4 routing table.
func (t *liteTable[V]) All4() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRec(stridePath{}, 0, true, yield)
}
}
@@ -559,9 +555,6 @@ func (t *liteTable[V]) All4() iter.Seq2[netip.Prefix, V] {
// All6 is like [liteTable.All] but only for the v6 routing table.
func (t *liteTable[V]) All6() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root6.AllRec(stridePath{}, 0, false, yield)
}
}
@@ -585,9 +578,6 @@ func (t *liteTable[V]) All6() iter.Seq2[netip.Prefix, V] {
// traversal is required use persistent table methods.
func (t *liteTable[V]) AllSorted() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRecSorted(stridePath{}, 0, true, yield) &&
t.root6.AllRecSorted(stridePath{}, 0, false, yield)
}
@@ -596,9 +586,6 @@ func (t *liteTable[V]) AllSorted() iter.Seq2[netip.Prefix, V] {
// AllSorted4 is like [liteTable.AllSorted] but only for the v4 routing table.
func (t *liteTable[V]) AllSorted4() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root4.AllRecSorted(stridePath{}, 0, true, yield)
}
}
@@ -606,9 +593,6 @@ func (t *liteTable[V]) AllSorted4() iter.Seq2[netip.Prefix, V] {
// AllSorted6 is like [liteTable.AllSorted] but only for the v6 routing table.
func (t *liteTable[V]) AllSorted6() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
- if t == nil {
- return
- }
_ = t.root6.AllRecSorted(stridePath{}, 0, false, yield)
}
}
@@ -636,12 +620,9 @@ func (t *liteTable[V]) AllSorted6() iter.Seq2[netip.Prefix, V] {
// │ └─ 2001:db8::/32 (V)
// └─ fe80::/10 (V)
func (t *liteTable[V]) Fprint(w io.Writer) error {
- if w == nil {
+ if w == nil && t != nil {
return fmt.Errorf("nil writer")
}
- if t == nil {
- return nil
- }
// v4
if err := t.fprint(w, true); err != nil {
@@ -691,10 +672,6 @@ func (t *liteTable[V]) MarshalText() ([]byte, error) {
// MarshalJSON dumps the table into two sorted lists: for ipv4 and ipv6.
// Every root and subnet is an array, not a map, because the order matters.
func (t *liteTable[V]) MarshalJSON() ([]byte, error) {
- if t == nil {
- return []byte("null"), nil
- }
-
result := struct {
Ipv4 []DumpListNode[V] `json:"ipv4,omitempty"`
Ipv6 []DumpListNode[V] `json:"ipv6,omitempty"`
@@ -714,18 +691,12 @@ func (t *liteTable[V]) MarshalJSON() ([]byte, error) {
// DumpList4 dumps the ipv4 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build the text or JSON serialization.
func (t *liteTable[V]) DumpList4() []DumpListNode[V] {
- if t == nil {
- return nil
- }
return t.dumpListRec(&t.root4, 0, stridePath{}, 0, true)
}
// DumpList6 dumps the ipv6 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build custom JSON representation.
func (t *liteTable[V]) DumpList6() []DumpListNode[V] {
- if t == nil {
- return nil
- }
return t.dumpListRec(&t.root6, 0, stridePath{}, 0, false)
}
@@ -775,10 +746,6 @@ func (t *liteTable[V]) dumpString() string {
// dump the table structure and all the nodes to w.
func (t *liteTable[V]) dump(w io.Writer) {
- if t == nil {
- return
- }
-
if t.size4 > 0 {
stats := t.root4.StatsRec()
fmt.Fprintln(w)
diff --git a/vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md b/vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md
index a10d3fc8d..3879f14aa 100644
--- a/vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md
+++ b/vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md
@@ -8,6 +8,12 @@ but only releases after v1.0.3 properly adhere to it.
## [Unreleased]
+## [1.4.0] - 2026-03-28
+### Added
+- Constructors, decomposers, and blend functions for the CSS Color Level 4 wide-gamut RGB color spaces `DisplayP3`, `A98Rgb`, `ProPhotoRgb`, and `Rec2020` (#81)
+- `XyzD50`, `Color.XyzD50`, `D50ToD65`, and `D65ToD50` for working with D50-based color spaces (#81)
+- `HexColor` now implements `fmt.Stringer`
+
## [1.3.0] - 2025-09-08
### Added
- `BlendLinearRgb` (#50)
@@ -19,7 +25,7 @@ but only releases after v1.0.3 properly adhere to it.
- Functions BlendOkLab and BlendOkLch (#70)
## Changed
-- `Hex()` parsing is much faster (#78)
+- `Hex()` parsing is much faster (#78). However, it doesn't tolerate hex codes with alpha anymore (previously ignoring the alpha was unintentional).
### Fixed
- Fix bug when doing HSV/HCL blending between a gray color and non-gray color (#60)
diff --git a/vendor/github.com/lucasb-eyer/go-colorful/README.md b/vendor/github.com/lucasb-eyer/go-colorful/README.md
index b3bb545cf..1da19f703 100644
--- a/vendor/github.com/lucasb-eyer/go-colorful/README.md
+++ b/vendor/github.com/lucasb-eyer/go-colorful/README.md
@@ -34,6 +34,8 @@ Go-Colorful stores colors in RGB and provides methods from converting these to v
- **CIE LCh(uv):** Called `LuvLCh` in code, this is a cylindrical transformation of the CIE-L\*u\*v\* color space. Like HCL above: H° is in [0..360], C\* almost in [0..1] and L\* as in CIE-L\*u\*v\*.
- **HSLuv:** The better alternative to HSL, see [here](https://www.hsluv.org/) and [here](https://www.kuon.ch/post/2020-03-08-hsluv/). Hue in [0..360], Saturation and Luminance in [0..1].
- **HPLuv:** A variant of HSLuv. The color space is smoother, but only pastel colors can be included. Because the valid colors are limited, it's easy to get invalid Saturation values way above 1.0, indicating the color can't be represented in HPLuv because it's not pastel.
+- **Oklab:** A perceptual color space by Björn Ottosson that improves on CIE-L\*a\*b\* with better perceptual uniformity, especially for blue hues. L in [0..1], a and b roughly in [-0.5..0.5]. See [Oklab](https://bottosson.github.io/posts/oklab/).
+- **Oklch:** The cylindrical (polar) representation of Oklab, similar to HCL. L in [0..1], C roughly in [0..0.5], h° in [0..360].
For the colorspaces where it makes sense (XYZ, Lab, Luv, HCl), the
[D65](http://en.wikipedia.org/wiki/Illuminant_D65) is used as reference white
@@ -96,6 +98,8 @@ c = colorful.Xyy(0.219895, 0.221839, 0.190837)
c = colorful.Lab(0.507850, 0.040585,-0.370945)
c = colorful.Luv(0.507849,-0.194172,-0.567924)
c = colorful.Hcl(276.2440, 0.373160, 0.507849)
+c = colorful.OkLab(0.577227, -0.021391, -0.104541)
+c = colorful.OkLch(0.577227, 0.106707, 258.435657)
fmt.Printf("RGB values: %v, %v, %v", c.R, c.G, c.B)
```
@@ -109,6 +113,8 @@ x, y, Y := c.Xyy()
l, a, b := c.Lab()
l, u, v := c.Luv()
h, c, l := c.Hcl()
+l, a, b = c.OkLab()
+l, c, h = c.OkLch()
```
Note that, because of Go's unfortunate choice of requiring an initial uppercase,
@@ -190,7 +196,7 @@ it only if you really know what you're doing. It will eat your cat.
Blending is highly connected to distance, since it basically "walks through" the
colorspace thus, if the colorspace maps distances well, the walk is "smooth".
-Colorful comes with blending functions in RGB, HSV and any of the LAB spaces.
+Colorful comes with blending functions in RGB, HSV, Oklab, Oklch, and any of the CIE-LAB spaces.
Of course, you'd rather want to use the blending functions of the LAB spaces since
these spaces map distances well but, just in case, here is an example showing
you how the blendings (`#fdffcc` to `#242a42`) are done in the various spaces:
@@ -472,11 +478,12 @@ section above.
Who?
====
-This library was developed by Lucas Beyer with contributions from
-Bastien Dejean (@baskerville), Phil Kulak (@pkulak), Christian Muehlhaeuser (@muesli), and Scott Pakin (@spakin).
-
-It is now maintained by makeworld (@makew0rld).
+This library was originally developed by Lucas Beyer, with notable
+contributions from Bastien Dejean (@baskerville), Phil Kulak (@pkulak),
+Christian Muehlhaeuser (@muesli), Scott Pakin (@spakin), and many others.
+See the [contributors list](https://github.com/lucasb-eyer/go-colorful/graphs/contributors) for the full roster.
+It is currently maintained by makeworld (@makew0rld).
## License
diff --git a/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go b/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go
index ad8b06cc9..26f357304 100644
--- a/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go
+++ b/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go
@@ -34,6 +34,10 @@ func (hc *HexColor) Value() (driver.Value, error) {
return Color(*hc).Hex(), nil
}
+func (hc HexColor) String() string {
+ return Color(hc).Hex()
+}
+
func (e errUnsupportedType) Error() string {
return fmt.Sprintf("unsupported type: got %v, want a %s", e.got, e.want)
}
diff --git a/vendor/github.com/lucasb-eyer/go-colorful/widegamut.go b/vendor/github.com/lucasb-eyer/go-colorful/widegamut.go
new file mode 100644
index 000000000..6805a2b96
--- /dev/null
+++ b/vendor/github.com/lucasb-eyer/go-colorful/widegamut.go
@@ -0,0 +1,290 @@
+package colorful
+
+import "math"
+
+// Wide-gamut RGB color spaces from CSS Color Level 4.
+// https://www.w3.org/TR/css-color-4/#color-conversion-code
+
+/// Bradford ///
+////////////////
+// Bradford chromatic adaptation between D50 and D65 illuminants.
+
+func D50ToD65(x, y, z float64) (xo, yo, zo float64) {
+ xo = 0.9555766*x - 0.0230393*y + 0.0631636*z
+ yo = -0.0282895*x + 1.0099416*y + 0.0210077*z
+ zo = 0.0122982*x - 0.0204830*y + 1.3299098*z
+ return
+}
+
+func D65ToD50(x, y, z float64) (xo, yo, zo float64) {
+ xo = 1.0479298208405488*x + 0.022946793341019088*y - 0.05019222954313557*z
+ yo = 0.029627815688159344*x + 0.990434484573249*y - 0.01707382502938514*z
+ zo = -0.009243058152591178*x + 0.015055144896577895*y + 0.7518742899580008*z
+ return
+}
+
+/// XYZ D50 ///
+///////////////
+
+func XyzD50(x, y, z float64) Color {
+ return Xyz(D50ToD65(x, y, z))
+}
+
+func (col Color) XyzD50() (x, y, z float64) {
+ return D65ToD50(col.Xyz())
+}
+
+/// Display P3 ///
+//////////////////
+// Uses the sRGB transfer function with DCI-P3 primaries.
+
+func DisplayP3ToLinearRgb(r, g, b float64) (rl, gl, bl float64) {
+ rl = linearize(r)
+ gl = linearize(g)
+ bl = linearize(b)
+ return
+}
+
+func LinearDisplayP3ToXyz(r, g, b float64) (x, y, z float64) {
+ x = 0.4865709486482162*r + 0.26566769316909306*g + 0.1982172852343625*b
+ y = 0.2289745640697488*r + 0.6917385218365064*g + 0.079286914093745*b
+ z = 0.04511338185890264*g + 1.043944368900976*b
+ return
+}
+
+func XyzToLinearDisplayP3(x, y, z float64) (r, g, b float64) {
+ r = 2.493496911941425*x - 0.9313836179191239*y - 0.40271078445071684*z
+ g = -0.8294889695615747*x + 1.7626640603183463*y + 0.023624685841943577*z
+ b = 0.035845830243784335*x - 0.07617238926804182*y + 0.9568845240076872*z
+ return
+}
+
+func DisplayP3(r, g, b float64) Color {
+ rl, gl, bl := DisplayP3ToLinearRgb(r, g, b)
+ x, y, z := LinearDisplayP3ToXyz(rl, gl, bl)
+ return Xyz(x, y, z)
+}
+
+func (col Color) DisplayP3() (r, g, b float64) {
+ x, y, z := col.Xyz()
+ rl, gl, bl := XyzToLinearDisplayP3(x, y, z)
+ r = delinearize(rl)
+ g = delinearize(gl)
+ b = delinearize(bl)
+ return
+}
+
+// BlendDisplayP3 blends two colors in the Display P3 color-space.
+// t == 0 results in c1, t == 1 results in c2
+func (c1 Color) BlendDisplayP3(c2 Color, t float64) Color {
+ r1, g1, b1 := c1.DisplayP3()
+ r2, g2, b2 := c2.DisplayP3()
+ return DisplayP3(
+ r1+t*(r2-r1),
+ g1+t*(g2-g1),
+ b1+t*(b2-b1))
+}
+
+/// A98 RGB ///
+///////////////
+// Adobe RGB (1998) color space.
+
+func linearizeA98(v float64) float64 {
+ sign := 1.0
+ if v < 0 {
+ sign = -1.0
+ v = -v
+ }
+ return sign * math.Pow(v, 563.0/256.0)
+}
+
+func delinearizeA98(v float64) float64 {
+ sign := 1.0
+ if v < 0 {
+ sign = -1.0
+ v = -v
+ }
+ return sign * math.Pow(v, 256.0/563.0)
+}
+
+func A98RgbToLinearRgb(r, g, b float64) (rl, gl, bl float64) {
+ rl = linearizeA98(r)
+ gl = linearizeA98(g)
+ bl = linearizeA98(b)
+ return
+}
+
+func LinearA98RgbToXyz(r, g, b float64) (x, y, z float64) {
+ x = 0.5766690429101305*r + 0.1855582379065463*g + 0.1882286462349947*b
+ y = 0.29734497525053605*r + 0.6273635662554661*g + 0.07529145849399788*b
+ z = 0.02703136138641234*r + 0.07068885253582723*g + 0.9913375368376388*b
+ return
+}
+
+func XyzToLinearA98Rgb(x, y, z float64) (r, g, b float64) {
+ r = 2.0415879038107327*x - 0.5650069742788597*y - 0.34473135077832956*z
+ g = -0.9692436362808795*x + 1.8759675015077202*y + 0.04155505740717559*z
+ b = 0.013444280632031142*x - 0.11836239223101838*y + 1.0151749943912054*z
+ return
+}
+
+func A98Rgb(r, g, b float64) Color {
+ rl, gl, bl := A98RgbToLinearRgb(r, g, b)
+ x, y, z := LinearA98RgbToXyz(rl, gl, bl)
+ return Xyz(x, y, z)
+}
+
+func (col Color) A98Rgb() (r, g, b float64) {
+ x, y, z := col.Xyz()
+ rl, gl, bl := XyzToLinearA98Rgb(x, y, z)
+ r = delinearizeA98(rl)
+ g = delinearizeA98(gl)
+ b = delinearizeA98(bl)
+ return
+}
+
+// BlendA98Rgb blends two colors in the A98 RGB color-space.
+// t == 0 results in c1, t == 1 results in c2
+func (c1 Color) BlendA98Rgb(c2 Color, t float64) Color {
+ r1, g1, b1 := c1.A98Rgb()
+ r2, g2, b2 := c2.A98Rgb()
+ return A98Rgb(
+ r1+t*(r2-r1),
+ g1+t*(g2-g1),
+ b1+t*(b2-b1))
+}
+
+/// ProPhoto RGB ///
+////////////////////
+// ProPhoto RGB (ROMM RGB) uses D50 illuminant.
+
+func linearizeProPhoto(v float64) float64 {
+ if v <= 16.0/512.0 {
+ return v / 16.0
+ }
+ return math.Pow(v, 1.8)
+}
+
+func delinearizeProPhoto(v float64) float64 {
+ if v < 1.0/512.0 {
+ return 16.0 * v
+ }
+ return math.Pow(v, 1.0/1.8)
+}
+
+func ProPhotoRgbToLinearRgb(r, g, b float64) (rl, gl, bl float64) {
+ rl = linearizeProPhoto(r)
+ gl = linearizeProPhoto(g)
+ bl = linearizeProPhoto(b)
+ return
+}
+
+func LinearProPhotoRgbToXyzD50(r, g, b float64) (x, y, z float64) {
+ x = 0.7977604896723027*r + 0.13518583717574031*g + 0.0313493495815248*b
+ y = 0.2880711282292934*r + 0.7118432178101014*g + 0.00008565396060525902*b
+ z = 0.8251046025104602 * b
+ return
+}
+
+func XyzD50ToLinearProPhotoRgb(x, y, z float64) (r, g, b float64) {
+ r = 1.3457989731028281*x - 0.25558010007997534*y - 0.05110628506753401*z
+ g = -0.5446224939028347*x + 1.5082327413132781*y + 0.02053603239147973*z
+ b = 1.2119675456389454 * z
+ return
+}
+
+func ProPhotoRgb(r, g, b float64) Color {
+ rl, gl, bl := ProPhotoRgbToLinearRgb(r, g, b)
+ x, y, z := LinearProPhotoRgbToXyzD50(rl, gl, bl)
+ return XyzD50(x, y, z)
+}
+
+func (col Color) ProPhotoRgb() (r, g, b float64) {
+ x, y, z := col.XyzD50()
+ rl, gl, bl := XyzD50ToLinearProPhotoRgb(x, y, z)
+ r = delinearizeProPhoto(rl)
+ g = delinearizeProPhoto(gl)
+ b = delinearizeProPhoto(bl)
+ return
+}
+
+// BlendProPhotoRgb blends two colors in the ProPhoto RGB color-space.
+// t == 0 results in c1, t == 1 results in c2
+func (c1 Color) BlendProPhotoRgb(c2 Color, t float64) Color {
+ r1, g1, b1 := c1.ProPhotoRgb()
+ r2, g2, b2 := c2.ProPhotoRgb()
+ return ProPhotoRgb(
+ r1+t*(r2-r1),
+ g1+t*(g2-g1),
+ b1+t*(b2-b1))
+}
+
+/// Rec. 2020 ///
+/////////////////
+// ITU-R BT.2020 color space.
+
+const (
+ rec2020Alpha = 1.09929682680944
+ rec2020Beta = 0.018053968510807
+)
+
+func linearizeRec2020(v float64) float64 {
+ if v < rec2020Beta*4.5 {
+ return v / 4.5
+ }
+ return math.Pow((v+rec2020Alpha-1)/rec2020Alpha, 1.0/0.45)
+}
+
+func delinearizeRec2020(v float64) float64 {
+ if v < rec2020Beta {
+ return 4.5 * v
+ }
+ return rec2020Alpha*math.Pow(v, 0.45) - (rec2020Alpha - 1)
+}
+
+func Rec2020ToLinearRgb(r, g, b float64) (rl, gl, bl float64) {
+ rl = linearizeRec2020(r)
+ gl = linearizeRec2020(g)
+ bl = linearizeRec2020(b)
+ return
+}
+
+func LinearRec2020ToXyz(r, g, b float64) (x, y, z float64) {
+ x = 0.6369580483012914*r + 0.14461690358620832*g + 0.1688809751641721*b
+ y = 0.2627002120112671*r + 0.6779980715188708*g + 0.05930171646986196*b
+ z = 0.028072693049087428*g + 1.0609850577107909*b
+ return
+}
+
+func XyzToLinearRec2020(x, y, z float64) (r, g, b float64) {
+ r = 1.7166511879712674*x - 0.35567078377639233*y - 0.25336628137365974*z
+ g = -0.666684351832489*x + 1.616481236634939*y + 0.0157685458139402*z
+ b = 0.017639857445310783*x - 0.042770613257808524*y + 0.9421031212354738*z
+ return
+}
+
+func Rec2020(r, g, b float64) Color {
+ rl, gl, bl := Rec2020ToLinearRgb(r, g, b)
+ x, y, z := LinearRec2020ToXyz(rl, gl, bl)
+ return Xyz(x, y, z)
+}
+
+func (col Color) Rec2020() (r, g, b float64) {
+ x, y, z := col.Xyz()
+ rl, gl, bl := XyzToLinearRec2020(x, y, z)
+ r = delinearizeRec2020(rl)
+ g = delinearizeRec2020(gl)
+ b = delinearizeRec2020(bl)
+ return
+}
+
+// BlendRec2020 blends two colors in the Rec. 2020 color-space.
+// t == 0 results in c1, t == 1 results in c2
+func (c1 Color) BlendRec2020(c2 Color, t float64) Color {
+ r1, g1, b1 := c1.Rec2020()
+ r2, g2, b2 := c2.Rec2020()
+ return Rec2020(
+ r1+t*(r2-r1),
+ g1+t*(g2-g1),
+ b1+t*(b2-b1))
+}
diff --git a/vendor/github.com/mattn/go-runewidth/SECURITY.md b/vendor/github.com/mattn/go-runewidth/SECURITY.md
new file mode 100644
index 000000000..a6898ee70
--- /dev/null
+++ b/vendor/github.com/mattn/go-runewidth/SECURITY.md
@@ -0,0 +1,25 @@
+# Security Policy
+
+## Supported Versions
+
+The following versions of go-runewidth are currently supported with
+security updates.
+
+| Version | Supported |
+| -------- | ------------------ |
+| 0.0.23 | :white_check_mark: |
+| < 0.0.23 | :x: |
+
+## Reporting a Vulnerability
+
+If you discover a security vulnerability in go-runewidth, please report it
+privately via GitHub's "Report a vulnerability" feature on the Security tab
+of the repository (https://github.com/mattn/go-runewidth/security), or by
+emailing the maintainer at mattn.jp@gmail.com.
+
+Please include a description of the issue, reproduction steps, and the
+affected version. You can expect an initial response within one week. If
+the vulnerability is accepted, a fix will be prepared and a new release
+will be published; you will be credited in the release notes unless you
+request otherwise. If the report is declined, you will receive an
+explanation of the reasoning.
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go
index a8c9c1fb1..6b958fdd4 100644
--- a/vendor/github.com/mattn/go-runewidth/runewidth.go
+++ b/vendor/github.com/mattn/go-runewidth/runewidth.go
@@ -2,7 +2,9 @@ package runewidth
import (
"os"
+ "sort"
"strings"
+ "unicode/utf8"
"github.com/clipperhouse/uax29/v2/graphemes"
)
@@ -23,10 +25,54 @@ var (
}
)
+var (
+ zerowidth table // combining + nonprint merged for faster zero-width lookup
+ widewidth table // ambiguous + doublewidth merged for EA path
+ eastAsianWidth widthTable
+ eastAsianWidth0 [0x300]byte
+)
+
func init() {
+ zerowidth = mergeIntervals(combining, nonprint)
+ widewidth = mergeIntervals(ambiguous, doublewidth)
+ eastAsianWidth = makeWidthTable(zerowidth, widewidth)
+ for r := range eastAsianWidth0 {
+ eastAsianWidth0[r] = byte(runeWidthEastAsian(rune(r)))
+ }
handleEnv()
}
+func mergeIntervals(t1, t2 table) table {
+ merged := make(table, 0, len(t1)+len(t2))
+ i, j := 0, 0
+ for i < len(t1) && j < len(t2) {
+ if t1[i].first <= t2[j].first {
+ merged = append(merged, t1[i])
+ i++
+ } else {
+ merged = append(merged, t2[j])
+ j++
+ }
+ }
+ merged = append(merged, t1[i:]...)
+ merged = append(merged, t2[j:]...)
+ if len(merged) == 0 {
+ return merged
+ }
+ result := merged[:1]
+ for _, iv := range merged[1:] {
+ last := &result[len(result)-1]
+ if iv.first <= last.last+1 {
+ if iv.last > last.last {
+ last.last = iv.last
+ }
+ } else {
+ result = append(result, iv)
+ }
+ }
+ return result
+}
+
func handleEnv() {
env := os.Getenv("RUNEWIDTH_EASTASIAN")
if env == "" {
@@ -51,15 +97,14 @@ type interval struct {
type table []interval
-func inTables(r rune, ts ...table) bool {
- for _, t := range ts {
- if inTable(r, t) {
- return true
- }
- }
- return false
+type widthInterval struct {
+ first rune
+ last rune
+ width byte
}
+type widthTable []widthInterval
+
func inTable(r rune, t table) bool {
if r < t[0].first {
return false
@@ -86,6 +131,71 @@ func inTable(r rune, t table) bool {
return false
}
+func makeWidthTable(zero, two table) widthTable {
+ wt := make(widthTable, 0, len(zero)+len(two))
+ zi := 0
+ for _, iv := range two {
+ start := iv.first
+ for zi < len(zero) && zero[zi].last < start {
+ zi++
+ }
+ for i := zi; i < len(zero) && zero[i].first <= iv.last; i++ {
+ if start < zero[i].first {
+ wt = append(wt, widthInterval{start, zero[i].first - 1, 2})
+ }
+ if start <= zero[i].last {
+ start = zero[i].last + 1
+ }
+ if start > iv.last {
+ break
+ }
+ }
+ if start <= iv.last {
+ wt = append(wt, widthInterval{start, iv.last, 2})
+ }
+ }
+ for _, iv := range zero {
+ wt = append(wt, widthInterval{iv.first, iv.last, 0})
+ }
+ sort.Slice(wt, func(i, j int) bool {
+ return wt[i].first < wt[j].first
+ })
+ return wt
+}
+
+func inWidthTable(r rune, t widthTable) (int, bool) {
+ if r < t[0].first {
+ return 0, false
+ }
+ if r > t[len(t)-1].last {
+ return 0, false
+ }
+
+ bot := 0
+ top := len(t) - 1
+ for top >= bot {
+ mid := (bot + top) >> 1
+
+ switch {
+ case t[mid].last < r:
+ bot = mid + 1
+ case t[mid].first > r:
+ top = mid - 1
+ default:
+ return int(t[mid].width), true
+ }
+ }
+
+ return 0, false
+}
+
+func runeWidthEastAsian(r rune) int {
+ if w, ok := inWidthTable(r, eastAsianWidth); ok {
+ return w
+ }
+ return 1
+}
+
var private = table{
{0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD},
}
@@ -123,36 +233,35 @@ func (c *Condition) RuneWidth(r rune) int {
}
// optimized version, verified by TestRuneWidthChecksums()
if !c.EastAsianWidth {
- switch {
- case r < 0x20:
- return 0
- case (r >= 0x7F && r <= 0x9F) || r == 0xAD: // nonprint
+ if r < 0x20 {
return 0
- case r < 0x300:
- return 1
- case inTable(r, narrow):
- return 1
- case inTables(r, nonprint, combining):
+ }
+ if (r >= 0x7F && r <= 0x9F) || r == 0xAD { // nonprint
return 0
- case inTable(r, doublewidth):
- return 2
- default:
+ }
+ if r < 0x300 {
return 1
}
- } else {
switch {
- case inTables(r, nonprint, combining):
+ case inTable(r, zerowidth):
return 0
- case inTable(r, narrow):
- return 1
- case inTables(r, ambiguous, doublewidth):
- return 2
- case !c.StrictEmojiNeutral && inTables(r, ambiguous, emoji, narrow):
+ case inTable(r, doublewidth):
return 2
default:
return 1
}
}
+
+ if r < 0x300 {
+ return int(eastAsianWidth0[r])
+ }
+ if w, ok := inWidthTable(r, eastAsianWidth); ok {
+ return w
+ }
+ if !c.StrictEmojiNeutral && inTable(r, emoji) {
+ return 2
+ }
+ return 1
}
// CreateLUT will create an in-memory lookup table of 557056 bytes for faster operation.
@@ -178,6 +287,33 @@ func (c *Condition) CreateLUT() {
// StringWidth return width as you can see
func (c *Condition) StringWidth(s string) (width int) {
+ if len(s) == 1 {
+ b := s[0]
+ if b < 0x20 || b == 0x7F {
+ return 0
+ }
+ return 1
+ }
+ if len(s) > 0 && len(s) <= utf8.UTFMax {
+ r, size := utf8.DecodeRuneInString(s)
+ if size == len(s) {
+ return c.RuneWidth(r)
+ }
+ }
+ // ASCII fast path: no grapheme clustering needed for pure ASCII
+ for i := 0; i < len(s); i++ {
+ b := s[i]
+ if b >= 0x80 {
+ goto graphemes
+ }
+ if b >= 0x20 && b != 0x7F {
+ width++
+ }
+ }
+ return
+
+graphemes:
+ width = 0
g := graphemes.FromString(s)
for g.Next() {
var chWidth int
@@ -257,24 +393,25 @@ func (c *Condition) TruncateLeft(s string, w int, prefix string) string {
// Wrap return string wrapped with w cells
func (c *Condition) Wrap(s string, w int) string {
width := 0
- out := ""
+ var out strings.Builder
+ out.Grow(len(s) + len(s)/w + 1)
for _, r := range s {
cw := c.RuneWidth(r)
if r == '\n' {
- out += string(r)
+ out.WriteRune(r)
width = 0
continue
} else if width+cw > w {
- out += "\n"
+ out.WriteByte('\n')
width = 0
- out += string(r)
+ out.WriteRune(r)
width += cw
continue
}
- out += string(r)
+ out.WriteRune(r)
width += cw
}
- return out
+ return out.String()
}
// FillLeft return string filled in left by spaces in w cells
@@ -313,7 +450,7 @@ func RuneWidth(r rune) int {
// IsAmbiguousWidth returns whether is ambiguous width or not.
func IsAmbiguousWidth(r rune) bool {
- return inTables(r, private, ambiguous)
+ return inTable(r, private) || inTable(r, ambiguous)
}
// IsCombiningWidth returns whether is combining width or not.
diff --git a/vendor/github.com/mdlayher/genetlink/.golangci.yml b/vendor/github.com/mdlayher/genetlink/.golangci.yml
new file mode 100644
index 000000000..1f10166aa
--- /dev/null
+++ b/vendor/github.com/mdlayher/genetlink/.golangci.yml
@@ -0,0 +1,16 @@
+version: "2"
+linters:
+ enable:
+ - misspell
+ - modernize
+ - revive
+ exclusions:
+ generated: lax
+ presets:
+ - comments
+ - common-false-positives
+ - legacy
+ - std-error-handling
+formatters:
+ exclusions:
+ generated: lax
diff --git a/vendor/github.com/mdlayher/genetlink/family_linux.go b/vendor/github.com/mdlayher/genetlink/family_linux.go
index 8c496f5b7..98273d620 100644
--- a/vendor/github.com/mdlayher/genetlink/family_linux.go
+++ b/vendor/github.com/mdlayher/genetlink/family_linux.go
@@ -1,5 +1,4 @@
//go:build linux
-// +build linux
package genetlink
diff --git a/vendor/github.com/mdlayher/netlink/CHANGELOG.md b/vendor/github.com/mdlayher/netlink/CHANGELOG.md
index eac8e924c..64589d14f 100644
--- a/vendor/github.com/mdlayher/netlink/CHANGELOG.md
+++ b/vendor/github.com/mdlayher/netlink/CHANGELOG.md
@@ -1,5 +1,25 @@
# CHANGELOG
+## 1.9.0
+
+**This is the first release of package netlink that only supports Go 1.24+.**
+
+- [Improvement]: updated dependencies and Go version to 1.24; tests now run on
+ Go 1.24-1.26.
+- [New API]: [#236](https://github.com/mdlayher/netlink/pull/236) introduced
+ `Sequence` field to `OpError` for better error correlation.
+- [New API]: [#237](https://github.com/mdlayher/netlink/pull/237) added
+ `netlink.Conn.PID` method to retrieve the connection's PID, also known as port
+ ID.
+- [Improvement]: [#228](https://github.com/mdlayher/netlink/pull/228) fixed
+ skipping of specific tests on big-endian hosts.
+
+## 1.8.0
+
+- [Improvement]: Updated dependencies, test with Go 1.23 to 1.25.
+- [Improvement]: Use Go 1.21's binary.NativeEndian (#220)
+- [Improvement]: Expose socket ReadBuffer & WriteBuffer functions (#223)
+
## v1.7.2
- [Improvement]: updated dependencies, test with Go 1.20.
@@ -29,7 +49,7 @@ Users on older versions of Go must use v1.6.2.**
## v1.6.1
-- [Deprecation] [commit](https://github.com/mdlayher/netlink/commit/d1b69ea8697d721415c259ef8513ab699c6d3e96):
+- [Deprecation] [commit](https://github.com/mdlayher/netlink/commit/d1b69ea8697d721415c259ef8513ab699c6d3e96):
the `netlink.Socket` interface has been marked as deprecated. The abstraction
is awkward to use properly and disables much of the functionality of the Conn
type when the basic interface is implemented. Do not use.
diff --git a/vendor/github.com/mdlayher/netlink/LICENSE.md b/vendor/github.com/mdlayher/netlink/LICENSE.md
index 12f710585..1eea1a534 100644
--- a/vendor/github.com/mdlayher/netlink/LICENSE.md
+++ b/vendor/github.com/mdlayher/netlink/LICENSE.md
@@ -1,6 +1,6 @@
# MIT License
-Copyright (C) 2016-2022 Matt Layher
+Copyright (C) 2016-2026 Matt Layher
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
diff --git a/vendor/github.com/mdlayher/netlink/conn.go b/vendor/github.com/mdlayher/netlink/conn.go
index 7138665bc..a88efee92 100644
--- a/vendor/github.com/mdlayher/netlink/conn.go
+++ b/vendor/github.com/mdlayher/netlink/conn.go
@@ -434,17 +434,20 @@ func (c *Conn) SetOption(option ConnOption, enable bool) error {
return newOpError("set-option", conn.SetOption(option, enable))
}
-// A bufferSetter is a Socket that supports setting connection buffer sizes.
-type bufferSetter interface {
+// A bufferedSocket is a Socket that supports getting & setting connection
+// buffer sizes.
+type bufferedSocket interface {
Socket
SetReadBuffer(bytes int) error
SetWriteBuffer(bytes int) error
+ ReadBuffer() (int, error)
+ WriteBuffer() (int, error)
}
// SetReadBuffer sets the size of the operating system's receive buffer
// associated with the Conn.
func (c *Conn) SetReadBuffer(bytes int) error {
- conn, ok := c.sock.(bufferSetter)
+ conn, ok := c.sock.(bufferedSocket)
if !ok {
return notSupported("set-read-buffer")
}
@@ -455,7 +458,7 @@ func (c *Conn) SetReadBuffer(bytes int) error {
// SetWriteBuffer sets the size of the operating system's transmit buffer
// associated with the Conn.
func (c *Conn) SetWriteBuffer(bytes int) error {
- conn, ok := c.sock.(bufferSetter)
+ conn, ok := c.sock.(bufferedSocket)
if !ok {
return notSupported("set-write-buffer")
}
@@ -463,6 +466,44 @@ func (c *Conn) SetWriteBuffer(bytes int) error {
return newOpError("set-write-buffer", conn.SetWriteBuffer(bytes))
}
+// ReadBuffer reads the size of the operating system's receive buffer
+// associated with the Conn.
+func (c *Conn) ReadBuffer() (int, error) {
+ conn, ok := c.sock.(bufferedSocket)
+ if !ok {
+ return 0, notSupported("get-read-buffer")
+ }
+
+ buff, err := conn.ReadBuffer()
+ if err != nil {
+ return 0, newOpError("get-read-buffer", err)
+ }
+ return buff, nil
+}
+
+// WriteBuffer reads the size of the operating system's transmit buffer
+// associated with the Conn.
+func (c *Conn) WriteBuffer() (int, error) {
+ conn, ok := c.sock.(bufferedSocket)
+ if !ok {
+ return 0, notSupported("get-write-buffer")
+ }
+
+ buff, err := conn.WriteBuffer()
+ if err != nil {
+ return 0, newOpError("get-write-buffer", err)
+ }
+
+ return buff, nil
+}
+
+// PID returns the PID associated with the Conn. It is also known as
+// the port ID in netlink terminology.
+// https://docs.kernel.org/userspace-api/netlink/intro.html#nlmsg-pid
+func (c *Conn) PID() uint32 {
+ return c.pid
+}
+
// A syscallConner is a Socket that supports syscall.Conn.
type syscallConner interface {
Socket
diff --git a/vendor/github.com/mdlayher/netlink/conn_linux.go b/vendor/github.com/mdlayher/netlink/conn_linux.go
index 4af18c99a..1eab74af5 100644
--- a/vendor/github.com/mdlayher/netlink/conn_linux.go
+++ b/vendor/github.com/mdlayher/netlink/conn_linux.go
@@ -212,6 +212,14 @@ func (c *conn) SetReadBuffer(bytes int) error { return c.s.SetReadBuffer(bytes)
// associated with the Conn.
func (c *conn) SetWriteBuffer(bytes int) error { return c.s.SetWriteBuffer(bytes) }
+// ReadBuffer reads the size of the operating system's receive buffer
+// associated with the Conn.
+func (c *conn) ReadBuffer() (int, error) { return c.s.ReadBuffer() }
+
+// WriteBuffer reads the size of the operating system's transmit buffer
+// associated with the Conn.
+func (c *conn) WriteBuffer() (int, error) { return c.s.WriteBuffer() }
+
// SyscallConn returns a raw network connection.
func (c *conn) SyscallConn() (syscall.RawConn, error) { return c.s.SyscallConn() }
diff --git a/vendor/github.com/mdlayher/netlink/errors.go b/vendor/github.com/mdlayher/netlink/errors.go
index 8c0fce7e4..e1fe0ab34 100644
--- a/vendor/github.com/mdlayher/netlink/errors.go
+++ b/vendor/github.com/mdlayher/netlink/errors.go
@@ -69,6 +69,11 @@ type OpError struct {
// is not set, both of these fields will be empty.
Message string
Offset int
+
+ // Sequence is the netlink message sequence number associated with the
+ // error. This field is only populated if the error was produced as the
+ // result of a netlink message; otherwise, it will be zero.
+ Sequence uint32
}
// newOpError is a small wrapper for creating an OpError. As a convenience, it
diff --git a/vendor/github.com/mdlayher/netlink/message.go b/vendor/github.com/mdlayher/netlink/message.go
index 57277165a..6cfefd59c 100644
--- a/vendor/github.com/mdlayher/netlink/message.go
+++ b/vendor/github.com/mdlayher/netlink/message.go
@@ -293,7 +293,8 @@ func checkMessage(m Message) error {
// Error code is a negative integer, convert it into an OS-specific raw
// system call error, but do not wrap with os.NewSyscallError to signify
// that this error was produced by a netlink message; not a system call.
- Err: newError(-1 * int(c)),
+ Err: newError(-1 * int(c)),
+ Sequence: m.Header.Sequence,
}
// TODO(mdlayher): investigate the Capped flag.
diff --git a/vendor/github.com/mdlayher/socket/.golangci.yml b/vendor/github.com/mdlayher/socket/.golangci.yml
new file mode 100644
index 000000000..1f10166aa
--- /dev/null
+++ b/vendor/github.com/mdlayher/socket/.golangci.yml
@@ -0,0 +1,16 @@
+version: "2"
+linters:
+ enable:
+ - misspell
+ - modernize
+ - revive
+ exclusions:
+ generated: lax
+ presets:
+ - comments
+ - common-false-positives
+ - legacy
+ - std-error-handling
+formatters:
+ exclusions:
+ generated: lax
diff --git a/vendor/github.com/mdlayher/socket/CHANGELOG.md b/vendor/github.com/mdlayher/socket/CHANGELOG.md
index b83418532..b94818e7c 100644
--- a/vendor/github.com/mdlayher/socket/CHANGELOG.md
+++ b/vendor/github.com/mdlayher/socket/CHANGELOG.md
@@ -1,5 +1,10 @@
# CHANGELOG
+## v0.5.2
+
+- [Improvement]: Bump build to Go 1.23.0. Note this is required for the latest
+ Go extended library versions.
+
## v0.5.1
- [Improvement]: revert `go.mod` to Go 1.20 to [resolve an issue around Go
diff --git a/vendor/github.com/mdlayher/socket/accept4.go b/vendor/github.com/mdlayher/socket/accept4.go
index e1016b206..48be812ed 100644
--- a/vendor/github.com/mdlayher/socket/accept4.go
+++ b/vendor/github.com/mdlayher/socket/accept4.go
@@ -1,5 +1,4 @@
//go:build dragonfly || freebsd || illumos || linux
-// +build dragonfly freebsd illumos linux
package socket
diff --git a/vendor/github.com/mdlayher/socket/conn.go b/vendor/github.com/mdlayher/socket/conn.go
index 5be502f5a..6057a2e0a 100644
--- a/vendor/github.com/mdlayher/socket/conn.go
+++ b/vendor/github.com/mdlayher/socket/conn.go
@@ -120,7 +120,7 @@ func (c *Conn) ReadContext(ctx context.Context, b []byte) (int, error) {
b = b[:maxRW]
}
- n, err := readT(c, ctx, "read", func(fd int) (int, error) {
+ n, err := readT(ctx, c, "read", func(fd int) (int, error) {
return unix.Read(fd, b)
})
if n == 0 && err == nil && c.facts.zeroReadIsEOF {
@@ -142,12 +142,12 @@ func (c *Conn) WriteContext(ctx context.Context, b []byte) (int, error) {
)
doErr := c.write(ctx, "write", func(fd int) error {
- max := len(b)
- if c.facts.isStream && max-nn > maxRW {
- max = nn + maxRW
+ lenb := len(b)
+ if c.facts.isStream && lenb-nn > maxRW {
+ lenb = nn + maxRW
}
- n, err = unix.Write(fd, b[nn:max])
+ n, err = unix.Write(fd, b[nn:lenb])
if n > 0 {
nn += n
}
@@ -418,7 +418,7 @@ func (c *Conn) Accept(ctx context.Context, flags int) (*Conn, unix.Sockaddr, err
sa unix.Sockaddr
}
- r, err := readT(c, ctx, sysAccept, func(fd int) (ret, error) {
+ r, err := readT(ctx, c, sysAccept, func(fd int) (ret, error) {
// Either accept(2) or accept4(2) depending on the OS.
nfd, sa, err := accept(fd, flags|socketFlags)
return ret{nfd, sa}, err
@@ -464,7 +464,7 @@ func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, er
// have an explicit WaitWrite call like internal/poll does, so we have
// to wait until the runtime calls the closure again to indicate we can
// write.
- progress uint32
+ progress atomic.Uint32
// Capture closure sockaddr and error.
rsa unix.Sockaddr
@@ -472,7 +472,7 @@ func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, er
)
doErr := c.write(ctx, op, func(fd int) error {
- if atomic.AddUint32(&progress, 1) == 1 {
+ if progress.Add(1) == 1 {
// First call: initiate connect.
return unix.Connect(fd, sa)
}
@@ -569,7 +569,7 @@ func (c *Conn) Recvmsg(ctx context.Context, p, oob []byte, flags int) (int, int,
from unix.Sockaddr
}
- r, err := readT(c, ctx, "recvmsg", func(fd int) (ret, error) {
+ r, err := readT(ctx, c, "recvmsg", func(fd int) (ret, error) {
n, oobn, recvflags, from, err := unix.Recvmsg(fd, p, oob, flags)
return ret{n, oobn, recvflags, from}, err
})
@@ -587,7 +587,7 @@ func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Soc
addr unix.Sockaddr
}
- out, err := readT(c, ctx, "recvfrom", func(fd int) (ret, error) {
+ out, err := readT(ctx, c, "recvfrom", func(fd int) (ret, error) {
n, addr, err := unix.Recvfrom(fd, p, flags)
return ret{n, addr}, err
})
@@ -600,7 +600,7 @@ func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Soc
// Sendmsg wraps sendmsg(2).
func (c *Conn) Sendmsg(ctx context.Context, p, oob []byte, to unix.Sockaddr, flags int) (int, error) {
- return writeT(c, ctx, "sendmsg", func(fd int) (int, error) {
+ return writeT(ctx, c, "sendmsg", func(fd int) (int, error) {
return unix.SendmsgN(fd, p, oob, to, flags)
})
}
@@ -645,7 +645,7 @@ func (c *Conn) Shutdown(how int) error {
// read wraps readT to execute a function and capture its error result. This is
// a convenience wrapper for functions which don't return any extra values.
func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error {
- _, err := readT(c, ctx, op, func(fd int) (struct{}, error) {
+ _, err := readT(ctx, c, op, func(fd int) (struct{}, error) {
return struct{}{}, f(fd)
})
return err
@@ -654,7 +654,7 @@ func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error
// write executes f, a write function, against the associated file descriptor.
// op is used to create an *os.SyscallError if the file descriptor is closed.
func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error {
- _, err := writeT(c, ctx, op, func(fd int) (struct{}, error) {
+ _, err := writeT(ctx, c, op, func(fd int) (struct{}, error) {
return struct{}{}, f(fd)
})
return err
@@ -662,7 +662,7 @@ func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error
// readT executes c.rc.Read for op using the input function, returning a newly
// allocated result T.
-func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
+func readT[T any](ctx context.Context, c *Conn, op string, f func(fd int) (T, error)) (T, error) {
return rwT(c, rwContext[T]{
Context: ctx,
Type: read,
@@ -673,7 +673,7 @@ func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, er
// writeT executes c.rc.Write for op using the input function, returning a newly
// allocated result T.
-func writeT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
+func writeT[T any](ctx context.Context, c *Conn, op string, f func(fd int) (T, error)) (T, error) {
return rwT(c, rwContext[T]{
Context: ctx,
Type: write,
@@ -771,9 +771,7 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) {
//
// TODO(mdlayher): is it possible to detect a background context vs a
// context with possible future cancel?
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
select {
case <-rw.Context.Done():
@@ -784,7 +782,7 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) {
case <-doneC:
// Nothing to do.
}
- }()
+ })
}
var (
diff --git a/vendor/github.com/mdlayher/socket/conn_linux.go b/vendor/github.com/mdlayher/socket/conn_linux.go
index 081194f32..b1cdffbdc 100644
--- a/vendor/github.com/mdlayher/socket/conn_linux.go
+++ b/vendor/github.com/mdlayher/socket/conn_linux.go
@@ -1,5 +1,4 @@
//go:build linux
-// +build linux
package socket
diff --git a/vendor/github.com/mdlayher/socket/netns_linux.go b/vendor/github.com/mdlayher/socket/netns_linux.go
index b29115ad1..9f37b7702 100644
--- a/vendor/github.com/mdlayher/socket/netns_linux.go
+++ b/vendor/github.com/mdlayher/socket/netns_linux.go
@@ -1,5 +1,4 @@
//go:build linux
-// +build linux
package socket
@@ -65,7 +64,7 @@ func withNetNS(fd int, fn func() (*Conn, error)) (*Conn, error) {
// No more thread-local state manipulation; return the new Conn.
runtime.UnlockOSThread()
conn = c
- return nil
+ return err
})
if err := eg.Wait(); err != nil {
diff --git a/vendor/github.com/mdlayher/socket/setbuffer_linux.go b/vendor/github.com/mdlayher/socket/setbuffer_linux.go
index 0d4aa4417..ae631893a 100644
--- a/vendor/github.com/mdlayher/socket/setbuffer_linux.go
+++ b/vendor/github.com/mdlayher/socket/setbuffer_linux.go
@@ -1,5 +1,4 @@
//go:build linux
-// +build linux
package socket
diff --git a/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go b/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go
index 40e834310..f4a7e559b 100644
--- a/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go
+++ b/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go
@@ -1,5 +1,4 @@
//go:build !darwin
-// +build !darwin
package socket
diff --git a/vendor/github.com/prometheus/procfs/.golangci.yml b/vendor/github.com/prometheus/procfs/.golangci.yml
index 3c3bf910f..eac920ba8 100644
--- a/vendor/github.com/prometheus/procfs/.golangci.yml
+++ b/vendor/github.com/prometheus/procfs/.golangci.yml
@@ -1,7 +1,9 @@
version: "2"
linters:
enable:
+ - errorlint
- forbidigo
+ - gocritic
- godot
- misspell
- revive
@@ -11,6 +13,20 @@ linters:
forbid:
- pattern: ^fmt\.Print.*$
msg: Do not commit print statements.
+ gocritic:
+ enable-all: true
+ disabled-checks:
+ - commentFormatting
+ - commentedOutCode
+ - deferInLoop
+ - filepathJoin
+ - hugeParam
+ - importShadow
+ - paramTypeCombine
+ - rangeValCopy
+ - tooManyResultsChecker
+ - unnamedResult
+ - whyNoLint
godot:
exclude:
# Ignore "See: URL".
@@ -18,17 +34,21 @@ linters:
capital: true
misspell:
locale: US
+ revive:
+ rules:
+ - name: var-naming
+ # TODO(SuperQ): See: https://github.com/prometheus/prometheus/issues/17766
+ arguments:
+ - []
+ - []
+ - - skip-package-name-checks: true
exclusions:
- generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
- paths:
- - third_party$
- - builtin$
- - examples$
+ warn-unused: true
formatters:
enable:
- gofmt
@@ -37,9 +57,3 @@ formatters:
goimports:
local-prefixes:
- github.com/prometheus/procfs
- exclusions:
- generated: lax
- paths:
- - third_party$
- - builtin$
- - examples$
diff --git a/vendor/github.com/prometheus/procfs/Makefile b/vendor/github.com/prometheus/procfs/Makefile
index 7edfe4d09..bce50a19c 100644
--- a/vendor/github.com/prometheus/procfs/Makefile
+++ b/vendor/github.com/prometheus/procfs/Makefile
@@ -1,4 +1,4 @@
-# Copyright 2018 The Prometheus Authors
+# Copyright The Prometheus Authors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/Makefile.common b/vendor/github.com/prometheus/procfs/Makefile.common
index 0ed55c2ba..a7c5f553e 100644
--- a/vendor/github.com/prometheus/procfs/Makefile.common
+++ b/vendor/github.com/prometheus/procfs/Makefile.common
@@ -1,4 +1,4 @@
-# Copyright 2018 The Prometheus Authors
+# Copyright The Prometheus Authors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -33,7 +33,7 @@ GOHOSTOS ?= $(shell $(GO) env GOHOSTOS)
GOHOSTARCH ?= $(shell $(GO) env GOHOSTARCH)
GO_VERSION ?= $(shell $(GO) version)
-GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION))Error Parsing File
+GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION))
PRE_GO_111 ?= $(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(10|[0-9])\.')
PROMU := $(FIRST_GOPATH)/bin/promu
@@ -55,13 +55,14 @@ ifneq ($(shell command -v gotestsum 2> /dev/null),)
endif
endif
-PROMU_VERSION ?= 0.17.0
+PROMU_VERSION ?= 0.20.0
PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz
SKIP_GOLANGCI_LINT :=
GOLANGCI_LINT :=
GOLANGCI_LINT_OPTS ?=
-GOLANGCI_LINT_VERSION ?= v2.0.2
+GOLANGCI_LINT_VERSION ?= v2.11.4
+GOLANGCI_FMT_OPTS ?=
# golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64.
# windows isn't included here because of the path separator being different.
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
@@ -81,11 +82,32 @@ endif
PREFIX ?= $(shell pwd)
BIN_DIR ?= $(shell pwd)
DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD))
-DOCKERFILE_PATH ?= ./Dockerfile
DOCKERBUILD_CONTEXT ?= ./
DOCKER_REPO ?= prom
-DOCKER_ARCHS ?= amd64
+# Check if deprecated DOCKERFILE_PATH is set
+ifdef DOCKERFILE_PATH
+$(error DOCKERFILE_PATH is deprecated. Use DOCKERFILE_VARIANTS ?= $(DOCKERFILE_PATH) in the Makefile)
+endif
+
+DOCKER_ARCHS ?= amd64 arm64 armv7 ppc64le riscv64 s390x
+DOCKERFILE_VARIANTS ?= $(wildcard Dockerfile Dockerfile.*)
+
+# Function to extract variant from Dockerfile label.
+# Returns the variant name from io.prometheus.image.variant label, or "default" if not found.
+define dockerfile_variant
+$(strip $(or $(shell sed -n 's/.*io\.prometheus\.image\.variant="\([^"]*\)".*/\1/p' $(1)),default))
+endef
+
+# Check for duplicate variant names (including default for Dockerfiles without labels).
+DOCKERFILE_VARIANT_NAMES := $(foreach df,$(DOCKERFILE_VARIANTS),$(call dockerfile_variant,$(df)))
+DOCKERFILE_VARIANT_NAMES_SORTED := $(sort $(DOCKERFILE_VARIANT_NAMES))
+ifneq ($(words $(DOCKERFILE_VARIANT_NAMES)),$(words $(DOCKERFILE_VARIANT_NAMES_SORTED)))
+$(error Duplicate variant names found. Each Dockerfile must have a unique io.prometheus.image.variant label, and only one can be without a label (default))
+endif
+
+# Build variant:dockerfile pairs for shell iteration.
+DOCKERFILE_VARIANTS_WITH_NAMES := $(foreach df,$(DOCKERFILE_VARIANTS),$(call dockerfile_variant,$(df)):$(df))
BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS))
PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS))
@@ -111,7 +133,7 @@ common-all: precheck style check_license lint yamllint unused build test
.PHONY: common-style
common-style:
@echo ">> checking code style"
- @fmtRes=$$($(GOFMT) -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \
+ @fmtRes=$$($(GOFMT) -d $$(git ls-files '*.go' ':!:vendor/*' || find . -path ./vendor -prune -o -name '*.go' -print)); \
if [ -n "$${fmtRes}" ]; then \
echo "gofmt checking failed!"; echo "$${fmtRes}"; echo; \
echo "Please ensure you are using $$($(GO) version) for formatting code."; \
@@ -121,13 +143,19 @@ common-style:
.PHONY: common-check_license
common-check_license:
@echo ">> checking license header"
- @licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \
+ @licRes=$$(for file in $$(git ls-files '*.go' ':!:vendor/*' || find . -path ./vendor -prune -o -type f -iname '*.go' -print) ; do \
awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \
done); \
if [ -n "$${licRes}" ]; then \
echo "license header checking failed:"; echo "$${licRes}"; \
exit 1; \
fi
+ @echo ">> checking for copyright years 2026 or later"
+ @futureYearRes=$$(git grep -E 'Copyright (202[6-9]|20[3-9][0-9])' -- '*.go' ':!:vendor/*' || true); \
+ if [ -n "$${futureYearRes}" ]; then \
+ echo "Files with copyright year 2026 or later found (should use 'Copyright The Prometheus Authors'):"; echo "$${futureYearRes}"; \
+ exit 1; \
+ fi
.PHONY: common-deps
common-deps:
@@ -138,7 +166,7 @@ common-deps:
update-go-deps:
@echo ">> updating Go dependencies"
@for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \
- $(GO) get -d $$m; \
+ $(GO) get $$m; \
done
$(GO) mod tidy
@@ -156,9 +184,13 @@ $(GOTEST_DIR):
@mkdir -p $@
.PHONY: common-format
-common-format:
+common-format: $(GOLANGCI_LINT)
@echo ">> formatting code"
$(GO) fmt $(pkgs)
+ifdef GOLANGCI_LINT
+ @echo ">> formatting code with golangci-lint"
+ $(GOLANGCI_LINT) fmt $(GOLANGCI_FMT_OPTS)
+endif
.PHONY: common-vet
common-vet:
@@ -215,28 +247,142 @@ common-docker-repo-name:
.PHONY: common-docker $(BUILD_DOCKER_ARCHS)
common-docker: $(BUILD_DOCKER_ARCHS)
$(BUILD_DOCKER_ARCHS): common-docker-%:
- docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" \
- -f $(DOCKERFILE_PATH) \
- --build-arg ARCH="$*" \
- --build-arg OS="linux" \
- $(DOCKERBUILD_CONTEXT)
+ @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \
+ dockerfile=$${variant#*:}; \
+ variant_name=$${variant%%:*}; \
+ distroless_arch="$*"; \
+ if [ "$*" = "armv7" ]; then \
+ distroless_arch="arm"; \
+ fi; \
+ if [ "$$dockerfile" = "Dockerfile" ]; then \
+ echo "Building default variant ($$variant_name) for linux-$* using $$dockerfile"; \
+ docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" \
+ -f $$dockerfile \
+ --build-arg ARCH="$*" \
+ --build-arg OS="linux" \
+ --build-arg DISTROLESS_ARCH="$$distroless_arch" \
+ $(DOCKERBUILD_CONTEXT); \
+ if [ "$$variant_name" != "default" ]; then \
+ echo "Tagging default variant with $$variant_name suffix"; \
+ docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" \
+ "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \
+ fi; \
+ else \
+ echo "Building $$variant_name variant for linux-$* using $$dockerfile"; \
+ docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" \
+ -f $$dockerfile \
+ --build-arg ARCH="$*" \
+ --build-arg OS="linux" \
+ --build-arg DISTROLESS_ARCH="$$distroless_arch" \
+ $(DOCKERBUILD_CONTEXT); \
+ fi; \
+ done
.PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS)
common-docker-publish: $(PUBLISH_DOCKER_ARCHS)
$(PUBLISH_DOCKER_ARCHS): common-docker-publish-%:
- docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)"
+ @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \
+ dockerfile=$${variant#*:}; \
+ variant_name=$${variant%%:*}; \
+ if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \
+ echo "Pushing $$variant_name variant for linux-$*"; \
+ docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \
+ fi; \
+ if [ "$$dockerfile" = "Dockerfile" ]; then \
+ echo "Pushing default variant ($$variant_name) for linux-$*"; \
+ docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)"; \
+ fi; \
+ if [ "$(DOCKER_IMAGE_TAG)" = "latest" ]; then \
+ if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \
+ echo "Pushing $$variant_name variant version tags for linux-$*"; \
+ docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name"; \
+ fi; \
+ if [ "$$dockerfile" = "Dockerfile" ]; then \
+ echo "Pushing default variant version tag for linux-$*"; \
+ docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)"; \
+ fi; \
+ fi; \
+ done
DOCKER_MAJOR_VERSION_TAG = $(firstword $(subst ., ,$(shell cat VERSION)))
.PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS)
common-docker-tag-latest: $(TAG_DOCKER_ARCHS)
$(TAG_DOCKER_ARCHS): common-docker-tag-latest-%:
- docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest"
- docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)"
+ @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \
+ dockerfile=$${variant#*:}; \
+ variant_name=$${variant%%:*}; \
+ if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \
+ echo "Tagging $$variant_name variant for linux-$* as latest"; \
+ docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest-$$variant_name"; \
+ docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name"; \
+ fi; \
+ if [ "$$dockerfile" = "Dockerfile" ]; then \
+ echo "Tagging default variant ($$variant_name) for linux-$* as latest"; \
+ docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest"; \
+ docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)"; \
+ fi; \
+ done
.PHONY: common-docker-manifest
common-docker-manifest:
- DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(SANITIZED_DOCKER_IMAGE_TAG))
- DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)"
+ @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \
+ dockerfile=$${variant#*:}; \
+ variant_name=$${variant%%:*}; \
+ if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \
+ echo "Creating manifest for $$variant_name variant"; \
+ refs=""; \
+ for arch in $(DOCKER_ARCHS); do \
+ refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \
+ done; \
+ if [ -z "$$refs" ]; then \
+ echo "Skipping manifest for $$variant_name variant (no supported architectures)"; \
+ continue; \
+ fi; \
+ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" $$refs; \
+ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \
+ fi; \
+ if [ "$$dockerfile" = "Dockerfile" ]; then \
+ echo "Creating default variant ($$variant_name) manifest"; \
+ refs=""; \
+ for arch in $(DOCKER_ARCHS); do \
+ refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:$(SANITIZED_DOCKER_IMAGE_TAG)"; \
+ done; \
+ if [ -z "$$refs" ]; then \
+ echo "Skipping default variant manifest (no supported architectures)"; \
+ continue; \
+ fi; \
+ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" $$refs; \
+ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)"; \
+ fi; \
+ if [ "$(DOCKER_IMAGE_TAG)" = "latest" ]; then \
+ if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \
+ echo "Creating manifest for $$variant_name variant version tag"; \
+ refs=""; \
+ for arch in $(DOCKER_ARCHS); do \
+ refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name"; \
+ done; \
+ if [ -z "$$refs" ]; then \
+ echo "Skipping version-tag manifest for $$variant_name variant (no supported architectures)"; \
+ continue; \
+ fi; \
+ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name" $$refs; \
+ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name"; \
+ fi; \
+ if [ "$$dockerfile" = "Dockerfile" ]; then \
+ echo "Creating default variant version tag manifest"; \
+ refs=""; \
+ for arch in $(DOCKER_ARCHS); do \
+ refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:v$(DOCKER_MAJOR_VERSION_TAG)"; \
+ done; \
+ if [ -z "$$refs" ]; then \
+ echo "Skipping default variant version-tag manifest (no supported architectures)"; \
+ continue; \
+ fi; \
+ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)" $$refs; \
+ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)"; \
+ fi; \
+ fi; \
+ done
.PHONY: promu
promu: $(PROMU)
@@ -248,8 +394,8 @@ $(PROMU):
cp $(PROMU_TMP)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(FIRST_GOPATH)/bin/promu
rm -r $(PROMU_TMP)
-.PHONY: proto
-proto:
+.PHONY: common-proto
+common-proto:
@echo ">> generating code from proto files"
@./scripts/genproto.sh
@@ -261,6 +407,10 @@ $(GOLANGCI_LINT):
| sh -s -- -b $(FIRST_GOPATH)/bin $(GOLANGCI_LINT_VERSION)
endif
+.PHONY: common-print-golangci-lint-version
+common-print-golangci-lint-version:
+ @echo $(GOLANGCI_LINT_VERSION)
+
.PHONY: precheck
precheck::
@@ -275,9 +425,3 @@ $(1)_precheck:
exit 1; \
fi
endef
-
-govulncheck: install-govulncheck
- govulncheck ./...
-
-install-govulncheck:
- command -v govulncheck > /dev/null || go install golang.org/x/vuln/cmd/govulncheck@latest
diff --git a/vendor/github.com/prometheus/procfs/README.md b/vendor/github.com/prometheus/procfs/README.md
index 0718239cf..363524094 100644
--- a/vendor/github.com/prometheus/procfs/README.md
+++ b/vendor/github.com/prometheus/procfs/README.md
@@ -7,7 +7,7 @@ metrics from the pseudo-filesystems /proc and /sys.
backwards-incompatible ways without warnings. Use it at your own risk.
[](https://pkg.go.dev/github.com/prometheus/procfs)
-[](https://circleci.com/gh/prometheus/procfs/tree/master)
+[](https://github.com/prometheus/procfs/actions/workflows/ci.yml)
[](https://goreportcard.com/report/github.com/prometheus/procfs)
## Usage
diff --git a/vendor/github.com/prometheus/procfs/SECURITY.md b/vendor/github.com/prometheus/procfs/SECURITY.md
index fed02d85c..5e6f976db 100644
--- a/vendor/github.com/prometheus/procfs/SECURITY.md
+++ b/vendor/github.com/prometheus/procfs/SECURITY.md
@@ -3,4 +3,4 @@
The Prometheus security policy, including how to report vulnerabilities, can be
found here:
-
+[https://prometheus.io/docs/operating/security/](https://prometheus.io/docs/operating/security/)
diff --git a/vendor/github.com/prometheus/procfs/arp.go b/vendor/github.com/prometheus/procfs/arp.go
index 2e5334415..716bdef10 100644
--- a/vendor/github.com/prometheus/procfs/arp.go
+++ b/vendor/github.com/prometheus/procfs/arp.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -73,15 +73,16 @@ func parseARPEntries(data []byte) ([]ARPEntry, error) {
columns := strings.Fields(line)
width := len(columns)
- if width == expectedHeaderWidth || width == 0 {
+ switch width {
+ case expectedHeaderWidth, 0:
continue
- } else if width == expectedDataWidth {
+ case expectedDataWidth:
entry, err := parseARPEntry(columns)
if err != nil {
return []ARPEntry{}, fmt.Errorf("%w: Failed to parse ARP entry: %v: %w", ErrFileParse, entry, err)
}
entries = append(entries, entry)
- } else {
+ default:
return []ARPEntry{}, fmt.Errorf("%w: %d columns found, but expected %d: %w", ErrFileParse, width, expectedDataWidth, err)
}
diff --git a/vendor/github.com/prometheus/procfs/buddyinfo.go b/vendor/github.com/prometheus/procfs/buddyinfo.go
index 838075009..53243e687 100644
--- a/vendor/github.com/prometheus/procfs/buddyinfo.go
+++ b/vendor/github.com/prometheus/procfs/buddyinfo.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -64,14 +64,12 @@ func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) {
if bucketCount == -1 {
bucketCount = arraySize
- } else {
- if bucketCount != arraySize {
- return nil, fmt.Errorf("%w: mismatch in number of buddyinfo buckets, previous count %d, new count %d", ErrFileParse, bucketCount, arraySize)
- }
+ } else if bucketCount != arraySize {
+ return nil, fmt.Errorf("%w: mismatch in number of buddyinfo buckets, previous count %d, new count %d", ErrFileParse, bucketCount, arraySize)
}
sizes := make([]float64, arraySize)
- for i := 0; i < arraySize; i++ {
+ for i := range arraySize {
sizes[i], err = strconv.ParseFloat(parts[i+4], 64)
if err != nil {
return nil, fmt.Errorf("%w: Invalid valid in buddyinfo: %f: %w", ErrFileParse, sizes[i], err)
diff --git a/vendor/github.com/prometheus/procfs/cmdline.go b/vendor/github.com/prometheus/procfs/cmdline.go
index bf4f3b48c..4f1cac1f0 100644
--- a/vendor/github.com/prometheus/procfs/cmdline.go
+++ b/vendor/github.com/prometheus/procfs/cmdline.go
@@ -1,4 +1,4 @@
-// Copyright 2021 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/cpuinfo.go b/vendor/github.com/prometheus/procfs/cpuinfo.go
index f0950bb49..4b23d8d6b 100644
--- a/vendor/github.com/prometheus/procfs/cpuinfo.go
+++ b/vendor/github.com/prometheus/procfs/cpuinfo.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,7 +12,6 @@
// limitations under the License.
//go:build linux
-// +build linux
package procfs
@@ -502,7 +501,7 @@ func parseCPUInfoRISCV(info []byte) ([]CPUInfo, error) {
return cpuinfo, nil
}
-func parseCPUInfoDummy(_ []byte) ([]CPUInfo, error) { // nolint:unused,deadcode
+func parseCPUInfoDummy(_ []byte) ([]CPUInfo, error) { //nolint:unused
return nil, errors.New("not implemented")
}
diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_armx.go b/vendor/github.com/prometheus/procfs/cpuinfo_armx.go
index 64cfd534c..b09035ff3 100644
--- a/vendor/github.com/prometheus/procfs/cpuinfo_armx.go
+++ b/vendor/github.com/prometheus/procfs/cpuinfo_armx.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,8 +12,6 @@
// limitations under the License.
//go:build linux && (arm || arm64)
-// +build linux
-// +build arm arm64
package procfs
diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_loong64.go b/vendor/github.com/prometheus/procfs/cpuinfo_loong64.go
index d88442f0e..7bb20211f 100644
--- a/vendor/github.com/prometheus/procfs/cpuinfo_loong64.go
+++ b/vendor/github.com/prometheus/procfs/cpuinfo_loong64.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,7 +12,6 @@
// limitations under the License.
//go:build linux
-// +build linux
package procfs
diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_mipsx.go b/vendor/github.com/prometheus/procfs/cpuinfo_mipsx.go
index c11207f3a..fd75d0f79 100644
--- a/vendor/github.com/prometheus/procfs/cpuinfo_mipsx.go
+++ b/vendor/github.com/prometheus/procfs/cpuinfo_mipsx.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,8 +12,6 @@
// limitations under the License.
//go:build linux && (mips || mipsle || mips64 || mips64le)
-// +build linux
-// +build mips mipsle mips64 mips64le
package procfs
diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_others.go b/vendor/github.com/prometheus/procfs/cpuinfo_others.go
index a6b2b3127..3d36ba0e6 100644
--- a/vendor/github.com/prometheus/procfs/cpuinfo_others.go
+++ b/vendor/github.com/prometheus/procfs/cpuinfo_others.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,7 +12,6 @@
// limitations under the License.
//go:build linux && !386 && !amd64 && !arm && !arm64 && !loong64 && !mips && !mips64 && !mips64le && !mipsle && !ppc64 && !ppc64le && !riscv64 && !s390x
-// +build linux,!386,!amd64,!arm,!arm64,!loong64,!mips,!mips64,!mips64le,!mipsle,!ppc64,!ppc64le,!riscv64,!s390x
package procfs
diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_ppcx.go b/vendor/github.com/prometheus/procfs/cpuinfo_ppcx.go
index 003bc2ad4..b3425051e 100644
--- a/vendor/github.com/prometheus/procfs/cpuinfo_ppcx.go
+++ b/vendor/github.com/prometheus/procfs/cpuinfo_ppcx.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,8 +12,6 @@
// limitations under the License.
//go:build linux && (ppc64 || ppc64le)
-// +build linux
-// +build ppc64 ppc64le
package procfs
diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_riscvx.go b/vendor/github.com/prometheus/procfs/cpuinfo_riscvx.go
index 1c9b7313b..72598230c 100644
--- a/vendor/github.com/prometheus/procfs/cpuinfo_riscvx.go
+++ b/vendor/github.com/prometheus/procfs/cpuinfo_riscvx.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,8 +12,6 @@
// limitations under the License.
//go:build linux && (riscv || riscv64)
-// +build linux
-// +build riscv riscv64
package procfs
diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_s390x.go b/vendor/github.com/prometheus/procfs/cpuinfo_s390x.go
index fa3686bc0..50a8239cb 100644
--- a/vendor/github.com/prometheus/procfs/cpuinfo_s390x.go
+++ b/vendor/github.com/prometheus/procfs/cpuinfo_s390x.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,7 +12,6 @@
// limitations under the License.
//go:build linux
-// +build linux
package procfs
diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_x86.go b/vendor/github.com/prometheus/procfs/cpuinfo_x86.go
index a0ef55562..00edb30a5 100644
--- a/vendor/github.com/prometheus/procfs/cpuinfo_x86.go
+++ b/vendor/github.com/prometheus/procfs/cpuinfo_x86.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,8 +12,6 @@
// limitations under the License.
//go:build linux && (386 || amd64)
-// +build linux
-// +build 386 amd64
package procfs
diff --git a/vendor/github.com/prometheus/procfs/crypto.go b/vendor/github.com/prometheus/procfs/crypto.go
index 5f2a37a78..d93b712e0 100644
--- a/vendor/github.com/prometheus/procfs/crypto.go
+++ b/vendor/github.com/prometheus/procfs/crypto.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -48,11 +48,13 @@ type Crypto struct {
Walksize *uint64
}
+var cryptoFile = "crypto"
+
// Crypto parses an crypto-file (/proc/crypto) and returns a slice of
// structs containing the relevant info. More information available here:
// https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html
func (fs FS) Crypto() ([]Crypto, error) {
- path := fs.proc.Path("crypto")
+ path := fs.proc.Path(cryptoFile)
b, err := util.ReadFileNoStat(path)
if err != nil {
return nil, fmt.Errorf("%w: Cannot read file %v: %w", ErrFileRead, b, err)
@@ -82,6 +84,10 @@ func parseCrypto(r io.Reader) ([]Crypto, error) {
continue
}
+ if len(out) == 0 {
+ return nil, fmt.Errorf("%w: parsed invalid line before name parsed: %q", ErrFileParse, text)
+ }
+
kv := strings.Split(text, ":")
if len(kv) != 2 {
return nil, fmt.Errorf("%w: Cannot parse line: %q", ErrFileParse, text)
diff --git a/vendor/github.com/prometheus/procfs/doc.go b/vendor/github.com/prometheus/procfs/doc.go
index f9d961e44..26bfea071 100644
--- a/vendor/github.com/prometheus/procfs/doc.go
+++ b/vendor/github.com/prometheus/procfs/doc.go
@@ -1,4 +1,4 @@
-// Copyright 2014 Prometheus Team
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/fs.go b/vendor/github.com/prometheus/procfs/fs.go
index 9bdaccc7c..8f27912a1 100644
--- a/vendor/github.com/prometheus/procfs/fs.go
+++ b/vendor/github.com/prometheus/procfs/fs.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/fs_statfs_notype.go b/vendor/github.com/prometheus/procfs/fs_statfs_notype.go
index 1b5bdbdf8..0bef25bdd 100644
--- a/vendor/github.com/prometheus/procfs/fs_statfs_notype.go
+++ b/vendor/github.com/prometheus/procfs/fs_statfs_notype.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,7 +12,6 @@
// limitations under the License.
//go:build !freebsd && !linux
-// +build !freebsd,!linux
package procfs
diff --git a/vendor/github.com/prometheus/procfs/fs_statfs_type.go b/vendor/github.com/prometheus/procfs/fs_statfs_type.go
index 80df79c31..d18333039 100644
--- a/vendor/github.com/prometheus/procfs/fs_statfs_type.go
+++ b/vendor/github.com/prometheus/procfs/fs_statfs_type.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,7 +12,6 @@
// limitations under the License.
//go:build freebsd || linux
-// +build freebsd linux
package procfs
diff --git a/vendor/github.com/prometheus/procfs/fscache.go b/vendor/github.com/prometheus/procfs/fscache.go
index 7db863307..9dde85707 100644
--- a/vendor/github.com/prometheus/procfs/fscache.go
+++ b/vendor/github.com/prometheus/procfs/fscache.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -388,20 +388,21 @@ func parseFscacheinfo(r io.Reader) (*Fscacheinfo, error) {
}
}
case "CacheOp:":
- if strings.Split(fields[1], "=")[0] == "alo" {
+ switch strings.Split(fields[1], "=")[0] {
+ case "alo":
err := setFSCacheFields(fields[1:], &m.CacheopAllocationsinProgress, &m.CacheopLookupObjectInProgress,
&m.CacheopLookupCompleteInPorgress, &m.CacheopGrabObjectInProgress)
if err != nil {
return &m, err
}
- } else if strings.Split(fields[1], "=")[0] == "inv" {
+ case "inv":
err := setFSCacheFields(fields[1:], &m.CacheopInvalidations, &m.CacheopUpdateObjectInProgress,
&m.CacheopDropObjectInProgress, &m.CacheopPutObjectInProgress, &m.CacheopAttributeChangeInProgress,
&m.CacheopSyncCacheInProgress)
if err != nil {
return &m, err
}
- } else {
+ default:
err := setFSCacheFields(fields[1:], &m.CacheopReadOrAllocPageInProgress, &m.CacheopReadOrAllocPagesInProgress,
&m.CacheopAllocatePageInProgress, &m.CacheopAllocatePagesInProgress, &m.CacheopWritePagesInProgress,
&m.CacheopUncachePagesInProgress, &m.CacheopDissociatePagesInProgress)
diff --git a/vendor/github.com/prometheus/procfs/internal/fs/fs.go b/vendor/github.com/prometheus/procfs/internal/fs/fs.go
index 3a43e8391..e7ccad66b 100644
--- a/vendor/github.com/prometheus/procfs/internal/fs/fs.go
+++ b/vendor/github.com/prometheus/procfs/internal/fs/fs.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/internal/util/parse.go b/vendor/github.com/prometheus/procfs/internal/util/parse.go
index 5a7d2df06..30c587201 100644
--- a/vendor/github.com/prometheus/procfs/internal/util/parse.go
+++ b/vendor/github.com/prometheus/procfs/internal/util/parse.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/internal/util/readfile.go b/vendor/github.com/prometheus/procfs/internal/util/readfile.go
index 71b7a70eb..0e41f71af 100644
--- a/vendor/github.com/prometheus/procfs/internal/util/readfile.go
+++ b/vendor/github.com/prometheus/procfs/internal/util/readfile.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go b/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go
index d5404a6d7..f6a4a4de6 100644
--- a/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go
+++ b/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,8 +12,6 @@
// limitations under the License.
//go:build (linux || darwin) && !appengine
-// +build linux darwin
-// +build !appengine
package util
diff --git a/vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go b/vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go
index 1d86f5e63..c80e082cb 100644
--- a/vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go
+++ b/vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,7 +12,6 @@
// limitations under the License.
//go:build (linux && appengine) || (!linux && !darwin)
-// +build linux,appengine !linux,!darwin
package util
diff --git a/vendor/github.com/prometheus/procfs/internal/util/valueparser.go b/vendor/github.com/prometheus/procfs/internal/util/valueparser.go
index fe2355d3c..e0ed671ea 100644
--- a/vendor/github.com/prometheus/procfs/internal/util/valueparser.go
+++ b/vendor/github.com/prometheus/procfs/internal/util/valueparser.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/ipvs.go b/vendor/github.com/prometheus/procfs/ipvs.go
index bc3a20c93..5374da9fa 100644
--- a/vendor/github.com/prometheus/procfs/ipvs.go
+++ b/vendor/github.com/prometheus/procfs/ipvs.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/kernel_hung.go b/vendor/github.com/prometheus/procfs/kernel_hung.go
new file mode 100644
index 000000000..0c7a69f99
--- /dev/null
+++ b/vendor/github.com/prometheus/procfs/kernel_hung.go
@@ -0,0 +1,44 @@
+// Copyright The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build !windows
+
+package procfs
+
+import (
+ "os"
+ "strconv"
+ "strings"
+)
+
+// KernelHung contains information about to the kernel's hung_task_detect_count number.
+type KernelHung struct {
+ // Indicates the total number of tasks that have been detected as hung since the system boot.
+ // This file shows up if `CONFIG_DETECT_HUNG_TASK` is enabled.
+ HungTaskDetectCount *uint64
+}
+
+// KernelHung returns values from /proc/sys/kernel/hung_task_detect_count.
+func (fs FS) KernelHung() (KernelHung, error) {
+ data, err := os.ReadFile(fs.proc.Path("sys", "kernel", "hung_task_detect_count"))
+ if err != nil {
+ return KernelHung{}, err
+ }
+ val, err := strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
+ if err != nil {
+ return KernelHung{}, err
+ }
+ return KernelHung{
+ HungTaskDetectCount: &val,
+ }, nil
+}
diff --git a/vendor/github.com/prometheus/procfs/kernel_random.go b/vendor/github.com/prometheus/procfs/kernel_random.go
index db88566bd..e7c5b8cf2 100644
--- a/vendor/github.com/prometheus/procfs/kernel_random.go
+++ b/vendor/github.com/prometheus/procfs/kernel_random.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,7 +12,6 @@
// limitations under the License.
//go:build !windows
-// +build !windows
package procfs
diff --git a/vendor/github.com/prometheus/procfs/loadavg.go b/vendor/github.com/prometheus/procfs/loadavg.go
index 332e76c17..c8c78a65e 100644
--- a/vendor/github.com/prometheus/procfs/loadavg.go
+++ b/vendor/github.com/prometheus/procfs/loadavg.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/mdstat.go b/vendor/github.com/prometheus/procfs/mdstat.go
index 67a9d2b44..d66eeda82 100644
--- a/vendor/github.com/prometheus/procfs/mdstat.go
+++ b/vendor/github.com/prometheus/procfs/mdstat.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -27,13 +27,34 @@ var (
recoveryLinePctRE = regexp.MustCompile(`= (.+)%`)
recoveryLineFinishRE = regexp.MustCompile(`finish=(.+)min`)
recoveryLineSpeedRE = regexp.MustCompile(`speed=(.+)[A-Z]`)
- componentDeviceRE = regexp.MustCompile(`(.*)\[\d+\]`)
+ componentDeviceRE = regexp.MustCompile(`(.*)\[(\d+)\](\([SF]+\))?`)
+ personalitiesPrefix = "Personalities : "
)
+type MDStatComponent struct {
+ // Name of the component device.
+ Name string
+ // DescriptorIndex number of component device, e.g. the order in the superblock.
+ DescriptorIndex int32
+ // Flags per Linux drivers/md/md.[ch] as of v6.12-rc1
+ // Subset that are exposed in mdstat
+ WriteMostly bool
+ Journal bool
+ Faulty bool // "Faulty" is what kernel source uses for "(F)"
+ Spare bool
+ Replacement bool
+ // Some additional flags that are NOT exposed in procfs today; they may
+ // be available via sysfs.
+ // In_sync, Bitmap_sync, Blocked, WriteErrorSeen, FaultRecorded,
+ // BlockedBadBlocks, WantReplacement, Candidate, ...
+}
+
// MDStat holds info parsed from /proc/mdstat.
type MDStat struct {
// Name of the device.
Name string
+ // raid type of the device.
+ Type string
// activity-state of the device.
ActivityState string
// Number of active disks.
@@ -58,8 +79,8 @@ type MDStat struct {
BlocksSyncedFinishTime float64
// current sync speed (in Kilobytes/sec)
BlocksSyncedSpeed float64
- // Name of md component devices
- Devices []string
+ // component devices
+ Devices []MDStatComponent
}
// MDStat parses an mdstat-file (/proc/mdstat) and returns a slice of
@@ -80,28 +101,52 @@ func (fs FS) MDStat() ([]MDStat, error) {
// parseMDStat parses data from mdstat file (/proc/mdstat) and returns a slice of
// structs containing the relevant info.
func parseMDStat(mdStatData []byte) ([]MDStat, error) {
+ // TODO:
+ // - parse global hotspares from the "unused devices" line.
mdStats := []MDStat{}
lines := strings.Split(string(mdStatData), "\n")
+ knownRaidTypes := make(map[string]bool)
for i, line := range lines {
if strings.TrimSpace(line) == "" || line[0] == ' ' ||
- strings.HasPrefix(line, "Personalities") ||
strings.HasPrefix(line, "unused") {
continue
}
+ // Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
+ if len(knownRaidTypes) == 0 && strings.HasPrefix(line, personalitiesPrefix) {
+ personalities := strings.Fields(line[len(personalitiesPrefix):])
+ for _, word := range personalities {
+ word := word[1 : len(word)-1]
+ knownRaidTypes[word] = true
+ }
+ continue
+ }
deviceFields := strings.Fields(line)
if len(deviceFields) < 3 {
return nil, fmt.Errorf("%w: Expected 3+ lines, got %q", ErrFileParse, line)
}
mdName := deviceFields[0] // mdx
- state := deviceFields[2] // active or inactive
+ state := deviceFields[2] // active, inactive, broken
+
+ mdType := "unknown" // raid1, raid5, etc.
+ var deviceStartIndex int
+ if len(deviceFields) > 3 { // mdType may be in the 3rd or 4th field
+ if isRaidType(deviceFields[3], knownRaidTypes) {
+ mdType = deviceFields[3]
+ deviceStartIndex = 4
+ } else if len(deviceFields) > 4 && isRaidType(deviceFields[4], knownRaidTypes) {
+ // if the 3rd field is (...), the 4th field is the mdType
+ mdType = deviceFields[4]
+ deviceStartIndex = 5
+ }
+ }
if len(lines) <= i+3 {
return nil, fmt.Errorf("%w: Too few lines for md device: %q", ErrFileParse, mdName)
}
- // Failed disks have the suffix (F) & Spare disks have the suffix (S).
+ // Failed (Faulty) disks have the suffix (F) & Spare disks have the suffix (S).
fail := int64(strings.Count(line, "(F)"))
spare := int64(strings.Count(line, "(S)"))
active, total, down, size, err := evalStatusLine(lines[i], lines[i+1])
@@ -123,16 +168,20 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
finish := float64(0)
pct := float64(0)
recovering := strings.Contains(lines[syncLineIdx], "recovery")
+ reshaping := strings.Contains(lines[syncLineIdx], "reshape")
resyncing := strings.Contains(lines[syncLineIdx], "resync")
checking := strings.Contains(lines[syncLineIdx], "check")
// Append recovery and resyncing state info.
- if recovering || resyncing || checking {
- if recovering {
+ if recovering || resyncing || checking || reshaping {
+ switch {
+ case recovering:
state = "recovering"
- } else if checking {
+ case reshaping:
+ state = "reshaping"
+ case checking:
state = "checking"
- } else {
+ default:
state = "resyncing"
}
@@ -148,8 +197,14 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
}
}
+ devices, err := evalComponentDevices(deviceFields[deviceStartIndex:])
+ if err != nil {
+ return nil, fmt.Errorf("error parsing components in md device %q: %w", mdName, err)
+ }
+
mdStats = append(mdStats, MDStat{
Name: mdName,
+ Type: mdType,
ActivityState: state,
DisksActive: active,
DisksFailed: fail,
@@ -162,14 +217,24 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
BlocksSyncedPct: pct,
BlocksSyncedFinishTime: finish,
BlocksSyncedSpeed: speed,
- Devices: evalComponentDevices(deviceFields),
+ Devices: devices,
})
}
return mdStats, nil
}
+// check if a string's format is like the mdType
+// Rule 1: mdType should not be like (...)
+// Rule 2: mdType should not be like sda[0]
+// .
+func isRaidType(mdType string, knownRaidTypes map[string]bool) bool {
+ _, ok := knownRaidTypes[mdType]
+ return !strings.ContainsAny(mdType, "([") && ok
+}
+
func evalStatusLine(deviceLine, statusLine string) (active, total, down, size int64, err error) {
+ // e.g. 523968 blocks super 1.2 [4/4] [UUUU]
statusFields := strings.Fields(statusLine)
if len(statusFields) < 1 {
return 0, 0, 0, 0, fmt.Errorf("%w: Unexpected statusline %q: %w", ErrFileParse, statusLine, err)
@@ -260,17 +325,29 @@ func evalRecoveryLine(recoveryLine string) (blocksSynced int64, blocksToBeSynced
return blocksSynced, blocksToBeSynced, pct, finish, speed, nil
}
-func evalComponentDevices(deviceFields []string) []string {
- mdComponentDevices := make([]string, 0)
- if len(deviceFields) > 3 {
- for _, field := range deviceFields[4:] {
- match := componentDeviceRE.FindStringSubmatch(field)
- if match == nil {
- continue
- }
- mdComponentDevices = append(mdComponentDevices, match[1])
+func evalComponentDevices(deviceFields []string) ([]MDStatComponent, error) {
+ mdComponentDevices := make([]MDStatComponent, 0)
+ for _, field := range deviceFields {
+ match := componentDeviceRE.FindStringSubmatch(field)
+ if match == nil {
+ continue
+ }
+ descriptorIndex, err := strconv.ParseInt(match[2], 10, 32)
+ if err != nil {
+ return mdComponentDevices, fmt.Errorf("error parsing int from device %q: %w", match[2], err)
}
+ mdComponentDevices = append(mdComponentDevices, MDStatComponent{
+ Name: match[1],
+ DescriptorIndex: int32(descriptorIndex),
+ // match may contain one or more of these
+ // https://github.com/torvalds/linux/blob/7ec462100ef9142344ddbf86f2c3008b97acddbe/drivers/md/md.c#L8376-L8392
+ Faulty: strings.Contains(match[3], "(F)"),
+ Spare: strings.Contains(match[3], "(S)"),
+ Journal: strings.Contains(match[3], "(J)"),
+ Replacement: strings.Contains(match[3], "(R)"),
+ WriteMostly: strings.Contains(match[3], "(W)"),
+ })
}
- return mdComponentDevices
+ return mdComponentDevices, nil
}
diff --git a/vendor/github.com/prometheus/procfs/meminfo.go b/vendor/github.com/prometheus/procfs/meminfo.go
index 4b2c4050a..342038318 100644
--- a/vendor/github.com/prometheus/procfs/meminfo.go
+++ b/vendor/github.com/prometheus/procfs/meminfo.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -66,6 +66,10 @@ type Meminfo struct {
// Memory which has been evicted from RAM, and is temporarily
// on the disk
SwapFree *uint64
+ // Memory consumed by the zswap backend (compressed size)
+ Zswap *uint64
+ // Amount of anonymous memory stored in zswap (original size)
+ Zswapped *uint64
// Memory which is waiting to get written back to the disk
Dirty *uint64
// Memory which is actively being written back to the disk
@@ -85,6 +89,8 @@ type Meminfo struct {
// amount of memory dedicated to the lowest level of page
// tables.
PageTables *uint64
+ // secondary page tables.
+ SecPageTables *uint64
// NFS pages sent to the server, but not yet committed to
// stable storage
NFSUnstable *uint64
@@ -129,15 +135,18 @@ type Meminfo struct {
Percpu *uint64
HardwareCorrupted *uint64
AnonHugePages *uint64
+ FileHugePages *uint64
ShmemHugePages *uint64
ShmemPmdMapped *uint64
CmaTotal *uint64
CmaFree *uint64
+ Unaccepted *uint64
HugePagesTotal *uint64
HugePagesFree *uint64
HugePagesRsvd *uint64
HugePagesSurp *uint64
Hugepagesize *uint64
+ Hugetlb *uint64
DirectMap4k *uint64
DirectMap2M *uint64
DirectMap1G *uint64
@@ -161,6 +170,8 @@ type Meminfo struct {
MlockedBytes *uint64
SwapTotalBytes *uint64
SwapFreeBytes *uint64
+ ZswapBytes *uint64
+ ZswappedBytes *uint64
DirtyBytes *uint64
WritebackBytes *uint64
AnonPagesBytes *uint64
@@ -171,6 +182,7 @@ type Meminfo struct {
SUnreclaimBytes *uint64
KernelStackBytes *uint64
PageTablesBytes *uint64
+ SecPageTablesBytes *uint64
NFSUnstableBytes *uint64
BounceBytes *uint64
WritebackTmpBytes *uint64
@@ -182,11 +194,14 @@ type Meminfo struct {
PercpuBytes *uint64
HardwareCorruptedBytes *uint64
AnonHugePagesBytes *uint64
+ FileHugePagesBytes *uint64
ShmemHugePagesBytes *uint64
ShmemPmdMappedBytes *uint64
CmaTotalBytes *uint64
CmaFreeBytes *uint64
+ UnacceptedBytes *uint64
HugepagesizeBytes *uint64
+ HugetlbBytes *uint64
DirectMap4kBytes *uint64
DirectMap2MBytes *uint64
DirectMap1GBytes *uint64
@@ -287,6 +302,12 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
case "SwapFree:":
m.SwapFree = &val
m.SwapFreeBytes = &valBytes
+ case "Zswap:":
+ m.Zswap = &val
+ m.ZswapBytes = &valBytes
+ case "Zswapped:":
+ m.Zswapped = &val
+ m.ZswappedBytes = &valBytes
case "Dirty:":
m.Dirty = &val
m.DirtyBytes = &valBytes
@@ -317,6 +338,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
case "PageTables:":
m.PageTables = &val
m.PageTablesBytes = &valBytes
+ case "SecPageTables:":
+ m.SecPageTables = &val
+ m.SecPageTablesBytes = &valBytes
case "NFS_Unstable:":
m.NFSUnstable = &val
m.NFSUnstableBytes = &valBytes
@@ -350,6 +374,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
case "AnonHugePages:":
m.AnonHugePages = &val
m.AnonHugePagesBytes = &valBytes
+ case "FileHugePages:":
+ m.FileHugePages = &val
+ m.FileHugePagesBytes = &valBytes
case "ShmemHugePages:":
m.ShmemHugePages = &val
m.ShmemHugePagesBytes = &valBytes
@@ -362,6 +389,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
case "CmaFree:":
m.CmaFree = &val
m.CmaFreeBytes = &valBytes
+ case "Unaccepted:":
+ m.Unaccepted = &val
+ m.UnacceptedBytes = &valBytes
case "HugePages_Total:":
m.HugePagesTotal = &val
case "HugePages_Free:":
@@ -373,6 +403,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
case "Hugepagesize:":
m.Hugepagesize = &val
m.HugepagesizeBytes = &valBytes
+ case "Hugetlb:":
+ m.Hugetlb = &val
+ m.HugetlbBytes = &valBytes
case "DirectMap4k:":
m.DirectMap4k = &val
m.DirectMap4kBytes = &valBytes
diff --git a/vendor/github.com/prometheus/procfs/mountinfo.go b/vendor/github.com/prometheus/procfs/mountinfo.go
index a704c5e73..8594ae7f1 100644
--- a/vendor/github.com/prometheus/procfs/mountinfo.go
+++ b/vendor/github.com/prometheus/procfs/mountinfo.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -17,10 +17,10 @@ import (
"bufio"
"bytes"
"fmt"
+ "io"
+ "os"
"strconv"
"strings"
-
- "github.com/prometheus/procfs/internal/util"
)
// A MountInfo is a type that describes the details, options
@@ -147,8 +147,7 @@ func mountOptionsParseOptionalFields(o []string) (map[string]string, error) {
// mountOptionsParser parses the mount options, superblock options.
func mountOptionsParser(mountOptions string) map[string]string {
opts := make(map[string]string)
- options := strings.Split(mountOptions, ",")
- for _, opt := range options {
+ for opt := range strings.SplitSeq(mountOptions, ",") {
splitOption := strings.Split(opt, "=")
if len(splitOption) < 2 {
key := splitOption[0]
@@ -161,9 +160,19 @@ func mountOptionsParser(mountOptions string) map[string]string {
return opts
}
+// readMountInfo reads a full mountinfo file (no 1 MiB cap, unlike util.ReadFileNoStat).
+func readMountInfo(path string) ([]byte, error) {
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return io.ReadAll(f)
+}
+
// GetMounts retrieves mountinfo information from `/proc/self/mountinfo`.
func GetMounts() ([]*MountInfo, error) {
- data, err := util.ReadFileNoStat("/proc/self/mountinfo")
+ data, err := readMountInfo("/proc/self/mountinfo")
if err != nil {
return nil, err
}
@@ -172,7 +181,25 @@ func GetMounts() ([]*MountInfo, error) {
// GetProcMounts retrieves mountinfo information from a processes' `/proc//mountinfo`.
func GetProcMounts(pid int) ([]*MountInfo, error) {
- data, err := util.ReadFileNoStat(fmt.Sprintf("/proc/%d/mountinfo", pid))
+ data, err := readMountInfo(fmt.Sprintf("/proc/%d/mountinfo", pid))
+ if err != nil {
+ return nil, err
+ }
+ return parseMountInfo(data)
+}
+
+// GetMounts retrieves mountinfo information from `/proc/self/mountinfo`.
+func (fs FS) GetMounts() ([]*MountInfo, error) {
+ data, err := readMountInfo(fs.proc.Path("self/mountinfo"))
+ if err != nil {
+ return nil, err
+ }
+ return parseMountInfo(data)
+}
+
+// GetProcMounts retrieves mountinfo information from a processes' `/proc//mountinfo`.
+func (fs FS) GetProcMounts(pid int) ([]*MountInfo, error) {
+ data, err := readMountInfo(fs.proc.Path(fmt.Sprintf("%d/mountinfo", pid)))
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/prometheus/procfs/mountstats.go b/vendor/github.com/prometheus/procfs/mountstats.go
index 50caa7327..e503cb3a6 100644
--- a/vendor/github.com/prometheus/procfs/mountstats.go
+++ b/vendor/github.com/prometheus/procfs/mountstats.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -383,7 +383,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
if stats.Opts == nil {
stats.Opts = map[string]string{}
}
- for _, opt := range strings.Split(ss[1], ",") {
+ for opt := range strings.SplitSeq(ss[1], ",") {
split := strings.Split(opt, "=")
if len(split) == 2 {
stats.Opts[split[0]] = split[1]
diff --git a/vendor/github.com/prometheus/procfs/net_conntrackstat.go b/vendor/github.com/prometheus/procfs/net_conntrackstat.go
index 316df5fbb..e9ca35707 100644
--- a/vendor/github.com/prometheus/procfs/net_conntrackstat.go
+++ b/vendor/github.com/prometheus/procfs/net_conntrackstat.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/net_dev.go b/vendor/github.com/prometheus/procfs/net_dev.go
index e66208aa0..7b3e1d61c 100644
--- a/vendor/github.com/prometheus/procfs/net_dev.go
+++ b/vendor/github.com/prometheus/procfs/net_dev.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/net_dev_snmp6.go b/vendor/github.com/prometheus/procfs/net_dev_snmp6.go
index f50b38e35..2a0f60f29 100644
--- a/vendor/github.com/prometheus/procfs/net_dev_snmp6.go
+++ b/vendor/github.com/prometheus/procfs/net_dev_snmp6.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -18,6 +18,7 @@ import (
"errors"
"io"
"os"
+ "path/filepath"
"strconv"
"strings"
)
@@ -56,7 +57,9 @@ func newNetDevSNMP6(dir string) (NetDevSNMP6, error) {
}
for _, iFaceFile := range ifaceFiles {
- f, err := os.Open(dir + "/" + iFaceFile.Name())
+ filePath := filepath.Join(dir, iFaceFile.Name())
+
+ f, err := os.Open(filePath)
if err != nil {
return netDevSNMP6, err
}
diff --git a/vendor/github.com/prometheus/procfs/net_ip_socket.go b/vendor/github.com/prometheus/procfs/net_ip_socket.go
index 19e3378f7..9291f8cd4 100644
--- a/vendor/github.com/prometheus/procfs/net_ip_socket.go
+++ b/vendor/github.com/prometheus/procfs/net_ip_socket.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/net_protocols.go b/vendor/github.com/prometheus/procfs/net_protocols.go
index 8d4b1ac05..eaa996cbc 100644
--- a/vendor/github.com/prometheus/procfs/net_protocols.go
+++ b/vendor/github.com/prometheus/procfs/net_protocols.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -169,7 +169,7 @@ func (pc *NetProtocolCapabilities) parseCapabilities(capabilities []string) erro
&pc.EnterMemoryPressure,
}
- for i := 0; i < len(capabilities); i++ {
+ for i := range capabilities {
switch capabilities[i] {
case "y":
*capabilityFields[i] = true
diff --git a/vendor/github.com/prometheus/procfs/net_route.go b/vendor/github.com/prometheus/procfs/net_route.go
index deb7029fe..fa3812d9d 100644
--- a/vendor/github.com/prometheus/procfs/net_route.go
+++ b/vendor/github.com/prometheus/procfs/net_route.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/net_sockstat.go b/vendor/github.com/prometheus/procfs/net_sockstat.go
index fae62b13d..8b221ebff 100644
--- a/vendor/github.com/prometheus/procfs/net_sockstat.go
+++ b/vendor/github.com/prometheus/procfs/net_sockstat.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -139,9 +139,6 @@ func parseSockstatKVs(kvs []string) (map[string]int, error) {
func parseSockstatProtocol(kvs map[string]int) NetSockstatProtocol {
var nsp NetSockstatProtocol
for k, v := range kvs {
- // Capture the range variable to ensure we get unique pointers for
- // each of the optional fields.
- v := v
switch k {
case "inuse":
nsp.InUse = v
diff --git a/vendor/github.com/prometheus/procfs/net_softnet.go b/vendor/github.com/prometheus/procfs/net_softnet.go
index 71c8059f4..4a2dfa18f 100644
--- a/vendor/github.com/prometheus/procfs/net_softnet.go
+++ b/vendor/github.com/prometheus/procfs/net_softnet.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/net_tcp.go b/vendor/github.com/prometheus/procfs/net_tcp.go
index 0396d7201..2c7f9bc7c 100644
--- a/vendor/github.com/prometheus/procfs/net_tcp.go
+++ b/vendor/github.com/prometheus/procfs/net_tcp.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -25,6 +25,7 @@ type (
// NetTCP returns the IPv4 kernel/networking statistics for TCP datagrams
// read from /proc/net/tcp.
+//
// Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET) instead.
func (fs FS) NetTCP() (NetTCP, error) {
return newNetTCP(fs.proc.Path("net/tcp"))
@@ -32,6 +33,7 @@ func (fs FS) NetTCP() (NetTCP, error) {
// NetTCP6 returns the IPv6 kernel/networking statistics for TCP datagrams
// read from /proc/net/tcp6.
+//
// Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET6) instead.
func (fs FS) NetTCP6() (NetTCP, error) {
return newNetTCP(fs.proc.Path("net/tcp6"))
@@ -39,6 +41,7 @@ func (fs FS) NetTCP6() (NetTCP, error) {
// NetTCPSummary returns already computed statistics like the total queue lengths
// for TCP datagrams read from /proc/net/tcp.
+//
// Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET) instead.
func (fs FS) NetTCPSummary() (*NetTCPSummary, error) {
return newNetTCPSummary(fs.proc.Path("net/tcp"))
@@ -46,6 +49,7 @@ func (fs FS) NetTCPSummary() (*NetTCPSummary, error) {
// NetTCP6Summary returns already computed statistics like the total queue lengths
// for TCP datagrams read from /proc/net/tcp6.
+//
// Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET6) instead.
func (fs FS) NetTCP6Summary() (*NetTCPSummary, error) {
return newNetTCPSummary(fs.proc.Path("net/tcp6"))
diff --git a/vendor/github.com/prometheus/procfs/net_tls_stat.go b/vendor/github.com/prometheus/procfs/net_tls_stat.go
index 13994c178..b1b3f6a6a 100644
--- a/vendor/github.com/prometheus/procfs/net_tls_stat.go
+++ b/vendor/github.com/prometheus/procfs/net_tls_stat.go
@@ -1,4 +1,4 @@
-// Copyright 2023 Prometheus Team
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/net_udp.go b/vendor/github.com/prometheus/procfs/net_udp.go
index 9ac3daf2d..8a3277910 100644
--- a/vendor/github.com/prometheus/procfs/net_udp.go
+++ b/vendor/github.com/prometheus/procfs/net_udp.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/net_unix.go b/vendor/github.com/prometheus/procfs/net_unix.go
index d7e0cacb4..e4d635923 100644
--- a/vendor/github.com/prometheus/procfs/net_unix.go
+++ b/vendor/github.com/prometheus/procfs/net_unix.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/net_wireless.go b/vendor/github.com/prometheus/procfs/net_wireless.go
index 7c597bc87..f74dd3bed 100644
--- a/vendor/github.com/prometheus/procfs/net_wireless.go
+++ b/vendor/github.com/prometheus/procfs/net_wireless.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -114,47 +114,47 @@ func parseWireless(r io.Reader) ([]*Wireless, error) {
qlink, err := strconv.Atoi(strings.TrimSuffix(stats[1], "."))
if err != nil {
- return nil, fmt.Errorf("%w: parse Quality:link as integer %q: %w", ErrFileParse, qlink, err)
+ return nil, fmt.Errorf("%w: parse Quality:link as integer %q: %w", ErrFileParse, stats[1], err)
}
qlevel, err := strconv.Atoi(strings.TrimSuffix(stats[2], "."))
if err != nil {
- return nil, fmt.Errorf("%w: Quality:level as integer %q: %w", ErrFileParse, qlevel, err)
+ return nil, fmt.Errorf("%w: Quality:level as integer %q: %w", ErrFileParse, stats[2], err)
}
qnoise, err := strconv.Atoi(strings.TrimSuffix(stats[3], "."))
if err != nil {
- return nil, fmt.Errorf("%w: Quality:noise as integer %q: %w", ErrFileParse, qnoise, err)
+ return nil, fmt.Errorf("%w: Quality:noise as integer %q: %w", ErrFileParse, stats[3], err)
}
dnwid, err := strconv.Atoi(stats[4])
if err != nil {
- return nil, fmt.Errorf("%w: Discarded:nwid as integer %q: %w", ErrFileParse, dnwid, err)
+ return nil, fmt.Errorf("%w: Discarded:nwid as integer %q: %w", ErrFileParse, stats[4], err)
}
dcrypt, err := strconv.Atoi(stats[5])
if err != nil {
- return nil, fmt.Errorf("%w: Discarded:crypt as integer %q: %w", ErrFileParse, dcrypt, err)
+ return nil, fmt.Errorf("%w: Discarded:crypt as integer %q: %w", ErrFileParse, stats[5], err)
}
dfrag, err := strconv.Atoi(stats[6])
if err != nil {
- return nil, fmt.Errorf("%w: Discarded:frag as integer %q: %w", ErrFileParse, dfrag, err)
+ return nil, fmt.Errorf("%w: Discarded:frag as integer %q: %w", ErrFileParse, stats[6], err)
}
dretry, err := strconv.Atoi(stats[7])
if err != nil {
- return nil, fmt.Errorf("%w: Discarded:retry as integer %q: %w", ErrFileParse, dretry, err)
+ return nil, fmt.Errorf("%w: Discarded:retry as integer %q: %w", ErrFileParse, stats[7], err)
}
dmisc, err := strconv.Atoi(stats[8])
if err != nil {
- return nil, fmt.Errorf("%w: Discarded:misc as integer %q: %w", ErrFileParse, dmisc, err)
+ return nil, fmt.Errorf("%w: Discarded:misc as integer %q: %w", ErrFileParse, stats[8], err)
}
mbeacon, err := strconv.Atoi(stats[9])
if err != nil {
- return nil, fmt.Errorf("%w: Missed:beacon as integer %q: %w", ErrFileParse, mbeacon, err)
+ return nil, fmt.Errorf("%w: Missed:beacon as integer %q: %w", ErrFileParse, stats[9], err)
}
w := &Wireless{
diff --git a/vendor/github.com/prometheus/procfs/net_xfrm.go b/vendor/github.com/prometheus/procfs/net_xfrm.go
index 932ef2046..5a9f497d1 100644
--- a/vendor/github.com/prometheus/procfs/net_xfrm.go
+++ b/vendor/github.com/prometheus/procfs/net_xfrm.go
@@ -1,4 +1,4 @@
-// Copyright 2017 Prometheus Team
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/netstat.go b/vendor/github.com/prometheus/procfs/netstat.go
index 742dff453..dbdae4739 100644
--- a/vendor/github.com/prometheus/procfs/netstat.go
+++ b/vendor/github.com/prometheus/procfs/netstat.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/nfnetlink_queue.go b/vendor/github.com/prometheus/procfs/nfnetlink_queue.go
new file mode 100644
index 000000000..b0a73b11e
--- /dev/null
+++ b/vendor/github.com/prometheus/procfs/nfnetlink_queue.go
@@ -0,0 +1,85 @@
+// Copyright The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+
+ "github.com/prometheus/procfs/internal/util"
+)
+
+const nfNetLinkQueueFormat = "%d %d %d %d %d %d %d %d %d"
+
+// NFNetLinkQueue contains general information about netfilter queues found in /proc/net/netfilter/nfnetlink_queue.
+type NFNetLinkQueue struct {
+ // id of the queue
+ QueueID uint
+ // pid of process handling the queue
+ PeerPID uint
+ // number of packets waiting for a decision
+ QueueTotal uint
+ // indicate how userspace receive packets
+ CopyMode uint
+ // size of copy
+ CopyRange uint
+ // number of items dropped by the kernel because too many packets were waiting a decision.
+ // It queue_total is superior to queue_max_len (1024 per default) the packets are dropped.
+ QueueDropped uint
+ // number of packets dropped by userspace (due to kernel send failure on the netlink socket)
+ QueueUserDropped uint
+ // sequence number of packets queued. It gives a correct approximation of the number of queued packets.
+ SequenceID uint
+ // internal value (number of entity using the queue)
+ Use uint
+}
+
+// NFNetLinkQueue returns information about current state of netfilter queues.
+func (fs FS) NFNetLinkQueue() ([]NFNetLinkQueue, error) {
+ data, err := util.ReadFileNoStat(fs.proc.Path("net/netfilter/nfnetlink_queue"))
+ if err != nil {
+ return nil, err
+ }
+
+ queue := []NFNetLinkQueue{}
+ if len(data) == 0 {
+ return queue, nil
+ }
+
+ scanner := bufio.NewScanner(bytes.NewReader(data))
+ for scanner.Scan() {
+ line := scanner.Text()
+ nFNetLinkQueue, err := parseNFNetLinkQueueLine(line)
+ if err != nil {
+ return nil, err
+ }
+ queue = append(queue, *nFNetLinkQueue)
+ }
+ return queue, nil
+}
+
+// parseNFNetLinkQueueLine parses each line of the /proc/net/netfilter/nfnetlink_queue file.
+func parseNFNetLinkQueueLine(line string) (*NFNetLinkQueue, error) {
+ nFNetLinkQueue := NFNetLinkQueue{}
+ _, err := fmt.Sscanf(
+ line, nfNetLinkQueueFormat,
+ &nFNetLinkQueue.QueueID, &nFNetLinkQueue.PeerPID, &nFNetLinkQueue.QueueTotal, &nFNetLinkQueue.CopyMode,
+ &nFNetLinkQueue.CopyRange, &nFNetLinkQueue.QueueDropped, &nFNetLinkQueue.QueueUserDropped, &nFNetLinkQueue.SequenceID, &nFNetLinkQueue.Use,
+ )
+ if err != nil {
+ return nil, err
+ }
+ return &nFNetLinkQueue, nil
+}
diff --git a/vendor/github.com/prometheus/procfs/proc.go b/vendor/github.com/prometheus/procfs/proc.go
index 368187fa8..39c14aa55 100644
--- a/vendor/github.com/prometheus/procfs/proc.go
+++ b/vendor/github.com/prometheus/procfs/proc.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -49,7 +49,7 @@ func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID }
// Self returns a process for the current process read via /proc/self.
func Self() (Proc, error) {
fs, err := NewFS(DefaultMountPoint)
- if err != nil || errors.Unwrap(err) == ErrMountPoint {
+ if err != nil || errors.Is(err, ErrMountPoint) {
return Proc{}, err
}
return fs.Self()
diff --git a/vendor/github.com/prometheus/procfs/proc_cgroup.go b/vendor/github.com/prometheus/procfs/proc_cgroup.go
index 4a64347c0..7e8a12297 100644
--- a/vendor/github.com/prometheus/procfs/proc_cgroup.go
+++ b/vendor/github.com/prometheus/procfs/proc_cgroup.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -60,7 +60,7 @@ func parseCgroupString(cgroupStr string) (*Cgroup, error) {
}
cgroup.HierarchyID, err = strconv.Atoi(fields[0])
if err != nil {
- return nil, fmt.Errorf("%w: hierarchy ID: %q", ErrFileParse, cgroup.HierarchyID)
+ return nil, fmt.Errorf("%w: hierarchy ID: %q", ErrFileParse, fields[0])
}
if fields[1] != "" {
ssNames := strings.Split(fields[1], ",")
diff --git a/vendor/github.com/prometheus/procfs/proc_cgroups.go b/vendor/github.com/prometheus/procfs/proc_cgroups.go
index 5dd493899..0b275c3b1 100644
--- a/vendor/github.com/prometheus/procfs/proc_cgroups.go
+++ b/vendor/github.com/prometheus/procfs/proc_cgroups.go
@@ -1,4 +1,4 @@
-// Copyright 2021 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -40,13 +40,13 @@ type CgroupSummary struct {
// parseCgroupSummary parses each line of the /proc/cgroup file
// Line format is `subsys_name hierarchy num_cgroups enabled`.
-func parseCgroupSummaryString(CgroupSummaryStr string) (*CgroupSummary, error) {
+func parseCgroupSummaryString(cgroupSummaryStr string) (*CgroupSummary, error) {
var err error
- fields := strings.Fields(CgroupSummaryStr)
+ fields := strings.Fields(cgroupSummaryStr)
// require at least 4 fields
if len(fields) < 4 {
- return nil, fmt.Errorf("%w: 4+ fields required, found %d fields in cgroup info string: %s", ErrFileParse, len(fields), CgroupSummaryStr)
+ return nil, fmt.Errorf("%w: 4+ fields required, found %d fields in cgroup info string: %s", ErrFileParse, len(fields), cgroupSummaryStr)
}
CgroupSummary := &CgroupSummary{
diff --git a/vendor/github.com/prometheus/procfs/proc_environ.go b/vendor/github.com/prometheus/procfs/proc_environ.go
index 57a89895d..5b941de04 100644
--- a/vendor/github.com/prometheus/procfs/proc_environ.go
+++ b/vendor/github.com/prometheus/procfs/proc_environ.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/proc_fdinfo.go b/vendor/github.com/prometheus/procfs/proc_fdinfo.go
index fa761b352..fa57761db 100644
--- a/vendor/github.com/prometheus/procfs/proc_fdinfo.go
+++ b/vendor/github.com/prometheus/procfs/proc_fdinfo.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -60,15 +60,16 @@ func (p Proc) FDInfo(fd string) (*ProcFDInfo, error) {
scanner := bufio.NewScanner(bytes.NewReader(data))
for scanner.Scan() {
text = scanner.Text()
- if rPos.MatchString(text) {
+ switch {
+ case rPos.MatchString(text):
pos = rPos.FindStringSubmatch(text)[1]
- } else if rFlags.MatchString(text) {
+ case rFlags.MatchString(text):
flags = rFlags.FindStringSubmatch(text)[1]
- } else if rMntID.MatchString(text) {
+ case rMntID.MatchString(text):
mntid = rMntID.FindStringSubmatch(text)[1]
- } else if rIno.MatchString(text) {
+ case rIno.MatchString(text):
ino = rIno.FindStringSubmatch(text)[1]
- } else if rInotify.MatchString(text) {
+ case rInotify.MatchString(text):
newInotify, err := parseInotifyInfo(text)
if err != nil {
return nil, err
diff --git a/vendor/github.com/prometheus/procfs/proc_interrupts.go b/vendor/github.com/prometheus/procfs/proc_interrupts.go
index 86b4b4524..643b500d5 100644
--- a/vendor/github.com/prometheus/procfs/proc_interrupts.go
+++ b/vendor/github.com/prometheus/procfs/proc_interrupts.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -42,7 +42,7 @@ type Interrupts map[string]Interrupt
// Interrupts creates a new instance from a given Proc instance.
func (p Proc) Interrupts() (Interrupts, error) {
- data, err := util.ReadFileNoStat(p.path("interrupts"))
+ data, err := util.ReadFileNoStat(p.fs.proc.Path("interrupts"))
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/prometheus/procfs/proc_io.go b/vendor/github.com/prometheus/procfs/proc_io.go
index d15b66ddb..dd8086ba2 100644
--- a/vendor/github.com/prometheus/procfs/proc_io.go
+++ b/vendor/github.com/prometheus/procfs/proc_io.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/proc_limits.go b/vendor/github.com/prometheus/procfs/proc_limits.go
index 9530b14bc..4b7d33784 100644
--- a/vendor/github.com/prometheus/procfs/proc_limits.go
+++ b/vendor/github.com/prometheus/procfs/proc_limits.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -19,6 +19,7 @@ import (
"os"
"regexp"
"strconv"
+ "strings"
)
// ProcLimits represents the soft limits for each of the process's resource
@@ -74,7 +75,7 @@ const (
)
var (
- limitsMatch = regexp.MustCompile(`(Max \w+\s{0,1}?\w*\s{0,1}\w*)\s{2,}(\w+)\s+(\w+)`)
+ limitsMatch = regexp.MustCompile(`(Max \w+\s??\w*\s?\w*)\s{2,}(\w+)\s+(\w+)`)
)
// NewLimits returns the current soft limits of the process.
@@ -106,7 +107,7 @@ func (p Proc) Limits() (ProcLimits, error) {
return ProcLimits{}, fmt.Errorf("%w: couldn't parse %q line %q", ErrFileParse, f.Name(), s.Text())
}
- switch fields[1] {
+ switch strings.TrimSpace(fields[1]) {
case "Max cpu time":
l.CPUTime, err = parseUint(fields[2])
case "Max file size":
diff --git a/vendor/github.com/prometheus/procfs/proc_maps.go b/vendor/github.com/prometheus/procfs/proc_maps.go
index 7e75c286b..08b89a6eb 100644
--- a/vendor/github.com/prometheus/procfs/proc_maps.go
+++ b/vendor/github.com/prometheus/procfs/proc_maps.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,8 +12,6 @@
// limitations under the License.
//go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) && !js
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
-// +build !js
package procfs
diff --git a/vendor/github.com/prometheus/procfs/proc_netstat.go b/vendor/github.com/prometheus/procfs/proc_netstat.go
index 4248c1716..7f94cc891 100644
--- a/vendor/github.com/prometheus/procfs/proc_netstat.go
+++ b/vendor/github.com/prometheus/procfs/proc_netstat.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/proc_ns.go b/vendor/github.com/prometheus/procfs/proc_ns.go
index 0f8f847f9..5fc0eb9e2 100644
--- a/vendor/github.com/prometheus/procfs/proc_ns.go
+++ b/vendor/github.com/prometheus/procfs/proc_ns.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/proc_psi.go b/vendor/github.com/prometheus/procfs/proc_psi.go
index ccd35f153..cc2c5de87 100644
--- a/vendor/github.com/prometheus/procfs/proc_psi.go
+++ b/vendor/github.com/prometheus/procfs/proc_psi.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/proc_smaps.go b/vendor/github.com/prometheus/procfs/proc_smaps.go
index 9a297afcf..f637309b3 100644
--- a/vendor/github.com/prometheus/procfs/proc_smaps.go
+++ b/vendor/github.com/prometheus/procfs/proc_smaps.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,7 +12,6 @@
// limitations under the License.
//go:build !windows
-// +build !windows
package procfs
diff --git a/vendor/github.com/prometheus/procfs/proc_snmp.go b/vendor/github.com/prometheus/procfs/proc_snmp.go
index 4bdc90b07..8d9a9bcd6 100644
--- a/vendor/github.com/prometheus/procfs/proc_snmp.go
+++ b/vendor/github.com/prometheus/procfs/proc_snmp.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/proc_snmp6.go b/vendor/github.com/prometheus/procfs/proc_snmp6.go
index fb7fd3995..841fef464 100644
--- a/vendor/github.com/prometheus/procfs/proc_snmp6.go
+++ b/vendor/github.com/prometheus/procfs/proc_snmp6.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/proc_stat.go b/vendor/github.com/prometheus/procfs/proc_stat.go
index 06a8d931c..02e3f9e31 100644
--- a/vendor/github.com/prometheus/procfs/proc_stat.go
+++ b/vendor/github.com/prometheus/procfs/proc_stat.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -101,6 +101,12 @@ type ProcStat struct {
RSS int
// Soft limit in bytes on the rss of the process.
RSSLimit uint64
+ // The address above which program text can run.
+ StartCode uint64
+ // The address below which program text can run.
+ EndCode uint64
+ // The address of the start (i.e., bottom) of the stack.
+ StartStack uint64
// CPU number last executed on.
Processor uint
// Real-time scheduling priority, a number in the range 1 to 99 for processes
@@ -177,9 +183,9 @@ func (p Proc) Stat() (ProcStat, error) {
&s.VSize,
&s.RSS,
&s.RSSLimit,
- &ignoreUint64,
- &ignoreUint64,
- &ignoreUint64,
+ &s.StartCode,
+ &s.EndCode,
+ &s.StartStack,
&ignoreUint64,
&ignoreUint64,
&ignoreUint64,
diff --git a/vendor/github.com/prometheus/procfs/proc_statm.go b/vendor/github.com/prometheus/procfs/proc_statm.go
new file mode 100644
index 000000000..6bcc97ec9
--- /dev/null
+++ b/vendor/github.com/prometheus/procfs/proc_statm.go
@@ -0,0 +1,117 @@
+// Copyright The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+ "os"
+ "strconv"
+ "strings"
+
+ "github.com/prometheus/procfs/internal/util"
+)
+
+// - https://man7.org/linux/man-pages/man5/proc_pid_statm.5.html
+
+// ProcStatm Provides memory usage information for a process, measured in memory pages.
+// Read from /proc/[pid]/statm.
+type ProcStatm struct {
+ // The process ID.
+ PID int
+ // total program size (same as VmSize in status)
+ Size uint64
+ // resident set size (same as VmRSS in status)
+ Resident uint64
+ // number of resident shared pages (i.e., backed by a file)
+ Shared uint64
+ // text (code)
+ Text uint64
+ // library (unused since Linux 2.6; always 0)
+ Lib uint64
+ // data + stack
+ Data uint64
+ // dirty pages (unused since Linux 2.6; always 0)
+ Dt uint64
+}
+
+// NewStatm returns the current status information of the process.
+//
+// Deprecated: Use p.Statm() instead.
+func (p Proc) NewStatm() (ProcStatm, error) {
+ return p.Statm()
+}
+
+// Statm returns the current memory usage information of the process.
+func (p Proc) Statm() (ProcStatm, error) {
+ data, err := util.ReadFileNoStat(p.path("statm"))
+ if err != nil {
+ return ProcStatm{}, err
+ }
+
+ statmSlice, err := parseStatm(data)
+ if err != nil {
+ return ProcStatm{}, err
+ }
+
+ procStatm := ProcStatm{
+ PID: p.PID,
+ Size: statmSlice[0],
+ Resident: statmSlice[1],
+ Shared: statmSlice[2],
+ Text: statmSlice[3],
+ Lib: statmSlice[4],
+ Data: statmSlice[5],
+ Dt: statmSlice[6],
+ }
+
+ return procStatm, nil
+}
+
+// parseStatm return /proc/[pid]/statm data to uint64 slice.
+func parseStatm(data []byte) ([]uint64, error) {
+ var statmSlice []uint64
+ statmItems := strings.Fields(string(data))
+ for i := range statmItems {
+ statmItem, err := strconv.ParseUint(statmItems[i], 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ statmSlice = append(statmSlice, statmItem)
+ }
+ return statmSlice, nil
+}
+
+// SizeBytes returns the process of total program size in bytes.
+func (s ProcStatm) SizeBytes() uint64 {
+ return s.Size * uint64(os.Getpagesize())
+}
+
+// ResidentBytes returns the process of resident set size in bytes.
+func (s ProcStatm) ResidentBytes() uint64 {
+ return s.Resident * uint64(os.Getpagesize())
+}
+
+// SHRBytes returns the process of share memory size in bytes.
+func (s ProcStatm) SHRBytes() uint64 {
+ return s.Shared * uint64(os.Getpagesize())
+}
+
+// TextBytes returns the process of text (code) size in bytes.
+func (s ProcStatm) TextBytes() uint64 {
+ return s.Text * uint64(os.Getpagesize())
+}
+
+// DataBytes returns the process of data + stack size in bytes.
+func (s ProcStatm) DataBytes() uint64 {
+ return s.Data * uint64(os.Getpagesize())
+}
diff --git a/vendor/github.com/prometheus/procfs/proc_status.go b/vendor/github.com/prometheus/procfs/proc_status.go
index dd8aa5688..12d65581c 100644
--- a/vendor/github.com/prometheus/procfs/proc_status.go
+++ b/vendor/github.com/prometheus/procfs/proc_status.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -16,7 +16,7 @@ package procfs
import (
"bytes"
"math/bits"
- "sort"
+ "slices"
"strconv"
"strings"
@@ -83,6 +83,19 @@ type ProcStatus struct {
// CpusAllowedList: List of cpu cores processes are allowed to run on.
CpusAllowedList []uint64
+
+ // CapInh is the bitmap of inheritable capabilities
+ //
+ // See: https://www.kernel.org/doc/man-pages/online/pages/man7/capabilities.7.html
+ CapInh uint64
+ // CapPrm is the bitmap of permitted capabilities
+ CapPrm uint64
+ // CapEff is the bitmap of effective capabilities
+ CapEff uint64
+ // CapBnd is the bitmap of bounding capabilities
+ CapBnd uint64
+ // CapAmb is the bitmap of ambient capabilities
+ CapAmb uint64
}
// NewStatus returns the current status information of the process.
@@ -94,8 +107,7 @@ func (p Proc) NewStatus() (ProcStatus, error) {
s := ProcStatus{PID: p.PID}
- lines := strings.Split(string(data), "\n")
- for _, line := range lines {
+ for line := range strings.SplitSeq(string(data), "\n") {
if !bytes.Contains([]byte(line), []byte(":")) {
continue
}
@@ -191,6 +203,36 @@ func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintByt
s.NonVoluntaryCtxtSwitches = vUint
case "Cpus_allowed_list":
s.CpusAllowedList = calcCpusAllowedList(vString)
+ case "CapInh":
+ var err error
+ s.CapInh, err = strconv.ParseUint(vString, 16, 64)
+ if err != nil {
+ return err
+ }
+ case "CapPrm":
+ var err error
+ s.CapPrm, err = strconv.ParseUint(vString, 16, 64)
+ if err != nil {
+ return err
+ }
+ case "CapEff":
+ var err error
+ s.CapEff, err = strconv.ParseUint(vString, 16, 64)
+ if err != nil {
+ return err
+ }
+ case "CapBnd":
+ var err error
+ s.CapBnd, err = strconv.ParseUint(vString, 16, 64)
+ if err != nil {
+ return err
+ }
+ case "CapAmb":
+ var err error
+ s.CapAmb, err = strconv.ParseUint(vString, 16, 64)
+ if err != nil {
+ return err
+ }
}
return nil
@@ -222,7 +264,7 @@ func calcCpusAllowedList(cpuString string) []uint64 {
}
- sort.Slice(g, func(i, j int) bool { return g[i] < g[j] })
+ slices.Sort(g)
return g
}
diff --git a/vendor/github.com/prometheus/procfs/proc_sys.go b/vendor/github.com/prometheus/procfs/proc_sys.go
index 3810d1ac9..52658a4d5 100644
--- a/vendor/github.com/prometheus/procfs/proc_sys.go
+++ b/vendor/github.com/prometheus/procfs/proc_sys.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/schedstat.go b/vendor/github.com/prometheus/procfs/schedstat.go
index 5f7f32dc8..fafd8dff7 100644
--- a/vendor/github.com/prometheus/procfs/schedstat.go
+++ b/vendor/github.com/prometheus/procfs/schedstat.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/slab.go b/vendor/github.com/prometheus/procfs/slab.go
index 8611c9017..32a04678a 100644
--- a/vendor/github.com/prometheus/procfs/slab.go
+++ b/vendor/github.com/prometheus/procfs/slab.go
@@ -1,4 +1,4 @@
-// Copyright 2020 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/softirqs.go b/vendor/github.com/prometheus/procfs/softirqs.go
index 403e6ae70..47b73a729 100644
--- a/vendor/github.com/prometheus/procfs/softirqs.go
+++ b/vendor/github.com/prometheus/procfs/softirqs.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/stat.go b/vendor/github.com/prometheus/procfs/stat.go
index e36b41c18..593ad0f62 100644
--- a/vendor/github.com/prometheus/procfs/stat.go
+++ b/vendor/github.com/prometheus/procfs/stat.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -16,6 +16,7 @@ package procfs
import (
"bufio"
"bytes"
+ "errors"
"fmt"
"io"
"strconv"
@@ -92,7 +93,7 @@ func parseCPUStat(line string) (CPUStat, int64, error) {
&cpuStat.Iowait, &cpuStat.IRQ, &cpuStat.SoftIRQ, &cpuStat.Steal,
&cpuStat.Guest, &cpuStat.GuestNice)
- if err != nil && err != io.EOF {
+ if err != nil && !errors.Is(err, io.EOF) {
return CPUStat{}, -1, fmt.Errorf("%w: couldn't parse %q (cpu): %w", ErrFileParse, line, err)
}
if count == 0 {
diff --git a/vendor/github.com/prometheus/procfs/swaps.go b/vendor/github.com/prometheus/procfs/swaps.go
index 65fec834b..ee17bf488 100644
--- a/vendor/github.com/prometheus/procfs/swaps.go
+++ b/vendor/github.com/prometheus/procfs/swaps.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/thread.go b/vendor/github.com/prometheus/procfs/thread.go
index 80e0e947b..0cfbb5418 100644
--- a/vendor/github.com/prometheus/procfs/thread.go
+++ b/vendor/github.com/prometheus/procfs/thread.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/prometheus/procfs/vm.go b/vendor/github.com/prometheus/procfs/vm.go
index 51c49d89e..52180c03e 100644
--- a/vendor/github.com/prometheus/procfs/vm.go
+++ b/vendor/github.com/prometheus/procfs/vm.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,7 +12,6 @@
// limitations under the License.
//go:build !windows
-// +build !windows
package procfs
diff --git a/vendor/github.com/prometheus/procfs/zoneinfo.go b/vendor/github.com/prometheus/procfs/zoneinfo.go
index e54d94b09..63d1898bc 100644
--- a/vendor/github.com/prometheus/procfs/zoneinfo.go
+++ b/vendor/github.com/prometheus/procfs/zoneinfo.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,7 +12,6 @@
// limitations under the License.
//go:build !windows
-// +build !windows
package procfs
@@ -88,11 +87,9 @@ func parseZoneinfo(zoneinfoData []byte) ([]Zoneinfo, error) {
zoneinfo := []Zoneinfo{}
- zoneinfoBlocks := bytes.Split(zoneinfoData, []byte("\nNode"))
- for _, block := range zoneinfoBlocks {
+ for block := range bytes.SplitSeq(zoneinfoData, []byte("\nNode")) {
var zoneinfoElement Zoneinfo
- lines := strings.Split(string(block), "\n")
- for _, line := range lines {
+ for line := range strings.SplitSeq(string(block), "\n") {
if nodeZone := nodeZoneRE.FindStringSubmatch(line); nodeZone != nil {
zoneinfoElement.Node = nodeZone[1]
diff --git a/vendor/github.com/sirupsen/logrus/.golangci.yml b/vendor/github.com/sirupsen/logrus/.golangci.yml
index 65dc28503..792db3618 100644
--- a/vendor/github.com/sirupsen/logrus/.golangci.yml
+++ b/vendor/github.com/sirupsen/logrus/.golangci.yml
@@ -1,40 +1,67 @@
+version: "2"
run:
- # do not run on test files yet
tests: false
-
-# all available settings of specific linters
-linters-settings:
- errcheck:
- # report about not checking of errors in type assetions: `a := b.(MyStruct)`;
- # default is false: such cases aren't reported by default.
- check-type-assertions: false
-
- # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
- # default is false: such cases aren't reported by default.
- check-blank: false
-
- lll:
- line-length: 100
- tab-width: 4
-
- prealloc:
- simple: false
- range-loops: false
- for-loops: false
-
- whitespace:
- multi-if: false # Enforces newlines (or comments) after every multi-line if statement
- multi-func: false # Enforces newlines (or comments) after every multi-line function signature
-
linters:
enable:
- - megacheck
- - govet
+ - asasalint
+ - asciicheck
+ - bidichk
+ - bodyclose
+ - contextcheck
+ - durationcheck
+ - errchkjson
+ - errorlint
+ - exhaustive
+ - gocheckcompilerdirectives
+ - gochecksumtype
+ - gosec
+ - gosmopolitan
+ - loggercheck
+ - makezero
+ - musttag
+ - nilerr
+ - nilnesserr
+ - noctx
+ - protogetter
+ - reassign
+ - recvcheck
+ - rowserrcheck
+ - spancheck
+ - sqlclosecheck
+ - testifylint
+ - unparam
+ - zerologlint
disable:
- - maligned
- prealloc
- disable-all: false
- presets:
- - bugs
- - unused
- fast: false
+ settings:
+ errcheck:
+ check-type-assertions: false
+ check-blank: false
+ lll:
+ line-length: 100
+ tab-width: 4
+ prealloc:
+ simple: false
+ range-loops: false
+ for-loops: false
+ whitespace:
+ multi-if: false
+ multi-func: false
+ exclusions:
+ generated: lax
+ presets:
+ - comments
+ - common-false-positives
+ - legacy
+ - std-error-handling
+ paths:
+ - third_party$
+ - builtin$
+ - examples$
+formatters:
+ exclusions:
+ generated: lax
+ paths:
+ - third_party$
+ - builtin$
+ - examples$
diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md
index 7567f6128..098608ff4 100644
--- a/vendor/github.com/sirupsen/logrus/CHANGELOG.md
+++ b/vendor/github.com/sirupsen/logrus/CHANGELOG.md
@@ -37,7 +37,7 @@ Features:
# 1.6.0
Fixes:
* end of line cleanup
- * revert the entry concurrency bug fix whic leads to deadlock under some circumstances
+ * revert the entry concurrency bug fix which leads to deadlock under some circumstances
* update dependency on go-windows-terminal-sequences to fix a crash with go 1.14
Features:
@@ -129,7 +129,7 @@ This new release introduces:
which is mostly useful for logger wrapper
* a fix reverting the immutability of the entry given as parameter to the hooks
a new configuration field of the json formatter in order to put all the fields
- in a nested dictionnary
+ in a nested dictionary
* a new SetOutput method in the Logger
* a new configuration of the textformatter to configure the name of the default keys
* a new configuration of the text formatter to disable the level truncation
diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md
index d1d4a85fd..cc5dab7eb 100644
--- a/vendor/github.com/sirupsen/logrus/README.md
+++ b/vendor/github.com/sirupsen/logrus/README.md
@@ -1,4 +1,4 @@
-# Logrus
[](https://github.com/sirupsen/logrus/actions?query=workflow%3ACI) [](https://travis-ci.org/sirupsen/logrus) [](https://pkg.go.dev/github.com/sirupsen/logrus)
+# Logrus
[](https://github.com/sirupsen/logrus/actions?query=workflow%3ACI) [](https://pkg.go.dev/github.com/sirupsen/logrus)
Logrus is a structured logger for Go (golang), completely API compatible with
the standard library logger.
@@ -40,7 +40,7 @@ plain text):

-With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
+With `logrus.SetFormatter(&logrus.JSONFormatter{})`, for easy parsing by logstash
or Splunk:
```text
@@ -60,9 +60,9 @@ ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
```
-With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
+With the default `logrus.SetFormatter(&logrus.TextFormatter{})` when a TTY is not
attached, the output is compatible with the
-[logfmt](http://godoc.org/github.com/kr/logfmt) format:
+[logfmt](https://pkg.go.dev/github.com/kr/logfmt) format:
```text
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
@@ -75,17 +75,18 @@ time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x20822
To ensure this behaviour even if a TTY is attached, set your formatter as follows:
```go
- log.SetFormatter(&log.TextFormatter{
- DisableColors: true,
- FullTimestamp: true,
- })
+logrus.SetFormatter(&logrus.TextFormatter{
+ DisableColors: true,
+ FullTimestamp: true,
+})
```
#### Logging Method Name
If you wish to add the calling method as a field, instruct the logger via:
+
```go
-log.SetReportCaller(true)
+logrus.SetReportCaller(true)
```
This adds the caller as 'method' like so:
@@ -100,11 +101,11 @@ time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcr
Note that this does add measurable overhead - the cost will depend on the version of Go, but is
between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your
environment via benchmarks:
-```
+
+```bash
go test -bench=.*CallerTracing
```
-
#### Case-sensitivity
The organization's name was changed to lower-case--and this will not be changed
@@ -118,12 +119,10 @@ The simplest way to use Logrus is simply the package-level exported logger:
```go
package main
-import (
- log "github.com/sirupsen/logrus"
-)
+import "github.com/sirupsen/logrus"
func main() {
- log.WithFields(log.Fields{
+ logrus.WithFields(logrus.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}
@@ -139,6 +138,7 @@ package main
import (
"os"
+
log "github.com/sirupsen/logrus"
)
@@ -190,26 +190,27 @@ package main
import (
"os"
+
"github.com/sirupsen/logrus"
)
// Create a new instance of the logger. You can have any number of instances.
-var log = logrus.New()
+var logger = logrus.New()
func main() {
// The API for setting attributes is a little different than the package level
- // exported logger. See Godoc.
- log.Out = os.Stdout
+ // exported logger. See Godoc.
+ logger.Out = os.Stdout
// You could set this to any `io.Writer` such as a file
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
// if err == nil {
- // log.Out = file
+ // logger.Out = file
// } else {
- // log.Info("Failed to log to file, using default stderr")
+ // logger.Info("Failed to log to file, using default stderr")
// }
- log.WithFields(logrus.Fields{
+ logger.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
@@ -219,12 +220,12 @@ func main() {
#### Fields
Logrus encourages careful, structured logging through logging fields instead of
-long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
+long, unparseable error messages. For example, instead of: `logrus.Fatalf("Failed
to send event %s to topic %s with key %d")`, you should log the much more
discoverable:
```go
-log.WithFields(log.Fields{
+logrus.WithFields(logrus.Fields{
"event": event,
"topic": topic,
"key": key,
@@ -245,12 +246,12 @@ seen as a hint you should add a field, however, you can still use the
Often it's helpful to have fields _always_ attached to log statements in an
application or parts of one. For example, you may want to always log the
`request_id` and `user_ip` in the context of a request. Instead of writing
-`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
+`logger.WithFields(logrus.Fields{"request_id": request_id, "user_ip": user_ip})` on
every line, you can create a `logrus.Entry` to pass around instead:
```go
-requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
-requestLogger.Info("something happened on that request") # will log request_id and user_ip
+requestLogger := logger.WithFields(logrus.Fields{"request_id": request_id, "user_ip": user_ip})
+requestLogger.Info("something happened on that request") // will log request_id and user_ip
requestLogger.Warn("something not great happened")
```
@@ -264,28 +265,31 @@ Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
`init`:
```go
+package main
+
import (
- log "github.com/sirupsen/logrus"
- "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
- logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
"log/syslog"
+
+ "github.com/sirupsen/logrus"
+ airbrake "gopkg.in/gemnasium/logrus-airbrake-hook.v2"
+ logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
)
func init() {
// Use the Airbrake hook to report errors that have Error severity or above to
// an exception tracker. You can create custom hooks, see the Hooks section.
- log.AddHook(airbrake.NewHook(123, "xyz", "production"))
+ logrus.AddHook(airbrake.NewHook(123, "xyz", "production"))
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
- log.Error("Unable to connect to local syslog daemon")
+ logrus.Error("Unable to connect to local syslog daemon")
} else {
- log.AddHook(hook)
+ logrus.AddHook(hook)
}
}
```
-Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
+Note: Syslog hooks also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
@@ -295,15 +299,15 @@ A list of currently known service hooks can be found in this wiki [page](https:/
Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic.
```go
-log.Trace("Something very low level.")
-log.Debug("Useful debugging information.")
-log.Info("Something noteworthy happened!")
-log.Warn("You should probably take a look at this.")
-log.Error("Something failed but I'm not quitting.")
+logrus.Trace("Something very low level.")
+logrus.Debug("Useful debugging information.")
+logrus.Info("Something noteworthy happened!")
+logrus.Warn("You should probably take a look at this.")
+logrus.Error("Something failed but I'm not quitting.")
// Calls os.Exit(1) after logging
-log.Fatal("Bye.")
+logrus.Fatal("Bye.")
// Calls panic() after logging
-log.Panic("I'm bailing.")
+logrus.Panic("I'm bailing.")
```
You can set the logging level on a `Logger`, then it will only log entries with
@@ -311,13 +315,13 @@ that severity or anything above it:
```go
// Will log anything that is info or above (warn, error, fatal, panic). Default.
-log.SetLevel(log.InfoLevel)
+logrus.SetLevel(logrus.InfoLevel)
```
-It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
+It may be useful to set `logrus.Level = logrus.DebugLevel` in a debug or verbose
environment if your application has that.
-Note: If you want different log levels for global (`log.SetLevel(...)`) and syslog logging, please check the [syslog hook README](hooks/syslog/README.md#different-log-levels-for-local-and-remote-logging).
+Note: If you want different log levels for global (`logrus.SetLevel(...)`) and syslog logging, please check the [syslog hook README](hooks/syslog/README.md#different-log-levels-for-local-and-remote-logging).
#### Entries
@@ -340,17 +344,17 @@ could do:
```go
import (
- log "github.com/sirupsen/logrus"
+ "github.com/sirupsen/logrus"
)
func init() {
// do something here to set environment depending on an environment variable
// or command-line flag
if Environment == "production" {
- log.SetFormatter(&log.JSONFormatter{})
+ logrus.SetFormatter(&logrus.JSONFormatter{})
} else {
// The TextFormatter is default, you don't actually have to do this.
- log.SetFormatter(&log.TextFormatter{})
+ logrus.SetFormatter(&logrus.TextFormatter{})
}
}
```
@@ -372,11 +376,11 @@ The built-in logging formatters are:
* When colors are enabled, levels are truncated to 4 characters by default. To disable
truncation set the `DisableLevelTruncation` field to `true`.
* When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text.
- * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
+ * All options are listed in the [generated docs](https://pkg.go.dev/github.com/sirupsen/logrus#TextFormatter).
* `logrus.JSONFormatter`. Logs fields as JSON.
- * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
+ * All options are listed in the [generated docs](https://pkg.go.dev/github.com/sirupsen/logrus#JSONFormatter).
-Third party logging formatters:
+Third-party logging formatters:
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
@@ -384,7 +388,7 @@ Third party logging formatters:
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo.
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
-* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files.
+* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Save log to files.
* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added.
You can define your formatter by implementing the `Formatter` interface,
@@ -393,10 +397,9 @@ requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
default ones (see Entries section above):
```go
-type MyJSONFormatter struct {
-}
+type MyJSONFormatter struct{}
-log.SetFormatter(new(MyJSONFormatter))
+logrus.SetFormatter(new(MyJSONFormatter))
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
// Note this doesn't include Time, Level and Message which are available on
@@ -455,17 +458,18 @@ entries. It should not be a feature of the application-level logger.
#### Testing
-Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
+Logrus has a built-in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just adds the `test` hook
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
```go
import(
+ "testing"
+
"github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
- "testing"
)
func TestSomething(t*testing.T){
@@ -486,15 +490,15 @@ func TestSomething(t*testing.T){
Logrus can register one or more functions that will be called when any `fatal`
level message is logged. The registered handlers will be executed before
logrus performs an `os.Exit(1)`. This behavior may be helpful if callers need
-to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
+to gracefully shut down. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
-```
-...
+```go
+// ...
handler := func() {
- // gracefully shutdown something...
+ // gracefully shut down something...
}
logrus.RegisterExitHandler(handler)
-...
+// ...
```
#### Thread safety
@@ -502,7 +506,7 @@ logrus.RegisterExitHandler(handler)
By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs.
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
-Situation when locking is not needed includes:
+Situations when locking is not needed include:
* You have no hooks registered, or hooks calling is already thread-safe.
diff --git a/vendor/github.com/sirupsen/logrus/appveyor.yml b/vendor/github.com/sirupsen/logrus/appveyor.yml
index df9d65c3a..e90f09ea6 100644
--- a/vendor/github.com/sirupsen/logrus/appveyor.yml
+++ b/vendor/github.com/sirupsen/logrus/appveyor.yml
@@ -1,14 +1,12 @@
-version: "{build}"
+# Minimal stub to satisfy AppVeyor CI
+version: 1.0.{build}
platform: x64
-clone_folder: c:\gopath\src\github.com\sirupsen\logrus
-environment:
- GOPATH: c:\gopath
+shallow_clone: true
+
branches:
only:
- master
-install:
- - set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
- - go version
+ - main
+
build_script:
- - go get -t
- - go test
+ - echo "No-op build to satisfy AppVeyor CI"
diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go
index 71cdbbc35..71d796d0b 100644
--- a/vendor/github.com/sirupsen/logrus/entry.go
+++ b/vendor/github.com/sirupsen/logrus/entry.go
@@ -34,13 +34,15 @@ func init() {
minimumCallerDepth = 1
}
-// Defines the key when adding errors using WithError.
+// ErrorKey defines the key when adding errors using [WithError], [Logger.WithError].
var ErrorKey = "error"
-// An entry is the final or intermediate Logrus logging entry. It contains all
+// Entry is the final or intermediate Logrus logging entry. It contains all
// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
// reused and passed around as much as you wish to avoid field duplication.
+//
+//nolint:recvcheck // the methods of "Entry" use pointer receiver and non-pointer receiver.
type Entry struct {
Logger *Logger
@@ -86,12 +88,12 @@ func (entry *Entry) Dup() *Entry {
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
}
-// Returns the bytes representation of this entry from the formatter.
+// Bytes returns the bytes representation of this entry from the formatter.
func (entry *Entry) Bytes() ([]byte, error) {
return entry.Logger.Formatter.Format(entry)
}
-// Returns the string representation from the reader and ultimately the
+// String returns the string representation from the reader and ultimately the
// formatter.
func (entry *Entry) String() (string, error) {
serialized, err := entry.Bytes()
@@ -102,12 +104,13 @@ func (entry *Entry) String() (string, error) {
return str, nil
}
-// Add an error as single field (using the key defined in ErrorKey) to the Entry.
+// WithError adds an error as single field (using the key defined in [ErrorKey])
+// to the Entry.
func (entry *Entry) WithError(err error) *Entry {
return entry.WithField(ErrorKey, err)
}
-// Add a context to the Entry.
+// WithContext adds a context to the Entry.
func (entry *Entry) WithContext(ctx context.Context) *Entry {
dataCopy := make(Fields, len(entry.Data))
for k, v := range entry.Data {
@@ -116,12 +119,12 @@ func (entry *Entry) WithContext(ctx context.Context) *Entry {
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
}
-// Add a single field to the Entry.
+// WithField adds a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry {
return entry.WithFields(Fields{key: value})
}
-// Add a map of fields to the Entry.
+// WithFields adds a map of fields to the Entry.
func (entry *Entry) WithFields(fields Fields) *Entry {
data := make(Fields, len(entry.Data)+len(fields))
for k, v := range entry.Data {
@@ -150,7 +153,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
}
-// Overrides the time of the Entry.
+// WithTime overrides the time of the Entry.
func (entry *Entry) WithTime(t time.Time) *Entry {
dataCopy := make(Fields, len(entry.Data))
for k, v := range entry.Data {
@@ -204,7 +207,7 @@ func getCaller() *runtime.Frame {
// If the caller isn't part of this package, we're done
if pkg != logrusPackage {
- return &f //nolint:scopelint
+ return &f
}
}
@@ -432,7 +435,7 @@ func (entry *Entry) Panicln(args ...interface{}) {
entry.Logln(PanicLevel, args...)
}
-// Sprintlnn => Sprint no newline. This is to get the behavior of how
+// sprintlnn => Sprint no newline. This is to get the behavior of how
// fmt.Sprintln where spaces are always added between operands, regardless of
// their type. Instead of vendoring the Sprintln implementation to spare a
// string allocation, we do the simplest thing.
diff --git a/vendor/github.com/sirupsen/logrus/hooks.go b/vendor/github.com/sirupsen/logrus/hooks.go
index 3f151cdc3..9ab978a45 100644
--- a/vendor/github.com/sirupsen/logrus/hooks.go
+++ b/vendor/github.com/sirupsen/logrus/hooks.go
@@ -1,16 +1,16 @@
package logrus
-// A hook to be fired when logging on the logging levels returned from
-// `Levels()` on your implementation of the interface. Note that this is not
+// Hook describes hooks to be fired when logging on the logging levels returned from
+// [Hook.Levels] on your implementation of the interface. Note that this is not
// fired in a goroutine or a channel with workers, you should handle such
-// functionality yourself if your call is non-blocking and you don't wish for
+// functionality yourself if your call is non-blocking, and you don't wish for
// the logging calls for levels returned from `Levels()` to block.
type Hook interface {
Levels() []Level
Fire(*Entry) error
}
-// Internal type for storing the hooks on a logger instance.
+// LevelHooks is an internal type for storing the hooks on a logger instance.
type LevelHooks map[Level][]Hook
// Add a hook to an instance of logger. This is called with
diff --git a/vendor/github.com/sirupsen/logrus/logger.go b/vendor/github.com/sirupsen/logrus/logger.go
index 5ff0aef6d..f5b8c439e 100644
--- a/vendor/github.com/sirupsen/logrus/logger.go
+++ b/vendor/github.com/sirupsen/logrus/logger.go
@@ -72,16 +72,16 @@ func (mw *MutexWrap) Disable() {
mw.disabled = true
}
-// Creates a new logger. Configuration should be set by changing `Formatter`,
-// `Out` and `Hooks` directly on the default logger instance. You can also just
+// New Creates a new logger. Configuration should be set by changing [Formatter],
+// Out and Hooks directly on the default Logger instance. You can also just
// instantiate your own:
//
-// var log = &logrus.Logger{
-// Out: os.Stderr,
-// Formatter: new(logrus.TextFormatter),
-// Hooks: make(logrus.LevelHooks),
-// Level: logrus.DebugLevel,
-// }
+// var log = &logrus.Logger{
+// Out: os.Stderr,
+// Formatter: new(logrus.TextFormatter),
+// Hooks: make(logrus.LevelHooks),
+// Level: logrus.DebugLevel,
+// }
//
// It's recommended to make this a global instance called `log`.
func New() *Logger {
@@ -118,30 +118,30 @@ func (logger *Logger) WithField(key string, value interface{}) *Entry {
return entry.WithField(key, value)
}
-// Adds a struct of fields to the log entry. All it does is call `WithField` for
-// each `Field`.
+// WithFields adds a struct of fields to the log entry. It calls [Entry.WithField]
+// for each Field.
func (logger *Logger) WithFields(fields Fields) *Entry {
entry := logger.newEntry()
defer logger.releaseEntry(entry)
return entry.WithFields(fields)
}
-// Add an error as single field to the log entry. All it does is call
-// `WithError` for the given `error`.
+// WithError adds an error as single field to the log entry. It calls
+// [Entry.WithError] for the given error.
func (logger *Logger) WithError(err error) *Entry {
entry := logger.newEntry()
defer logger.releaseEntry(entry)
return entry.WithError(err)
}
-// Add a context to the log entry.
+// WithContext add a context to the log entry.
func (logger *Logger) WithContext(ctx context.Context) *Entry {
entry := logger.newEntry()
defer logger.releaseEntry(entry)
return entry.WithContext(ctx)
}
-// Overrides the time of the log entry.
+// WithTime overrides the time of the log entry.
func (logger *Logger) WithTime(t time.Time) *Entry {
entry := logger.newEntry()
defer logger.releaseEntry(entry)
@@ -347,9 +347,9 @@ func (logger *Logger) Exit(code int) {
logger.ExitFunc(code)
}
-//When file is opened with appending mode, it's safe to
-//write concurrently to a file (within 4k message on Linux).
-//In these cases user can choose to disable the lock.
+// SetNoLock disables the lock for situations where a file is opened with
+// appending mode, and safe for concurrent writes to the file (within 4k
+// message on Linux). In these cases user can choose to disable the lock.
func (logger *Logger) SetNoLock() {
logger.mu.Disable()
}
diff --git a/vendor/github.com/sirupsen/logrus/logrus.go b/vendor/github.com/sirupsen/logrus/logrus.go
index 2f16224cb..37fc4fef8 100644
--- a/vendor/github.com/sirupsen/logrus/logrus.go
+++ b/vendor/github.com/sirupsen/logrus/logrus.go
@@ -6,13 +6,15 @@ import (
"strings"
)
-// Fields type, used to pass to `WithFields`.
+// Fields type, used to pass to [WithFields].
type Fields map[string]interface{}
// Level type
+//
+//nolint:recvcheck // the methods of "Entry" use pointer receiver and non-pointer receiver.
type Level uint32
-// Convert the Level to a string. E.g. PanicLevel becomes "panic".
+// Convert the Level to a string. E.g. [PanicLevel] becomes "panic".
func (level Level) String() string {
if b, err := level.MarshalText(); err == nil {
return string(b)
@@ -77,7 +79,7 @@ func (level Level) MarshalText() ([]byte, error) {
return nil, fmt.Errorf("not a valid logrus level %d", level)
}
-// A constant exposing all logging levels
+// AllLevels exposing all logging levels.
var AllLevels = []Level{
PanicLevel,
FatalLevel,
@@ -119,8 +121,8 @@ var (
)
// StdLogger is what your logrus-enabled library should take, that way
-// it'll accept a stdlib logger and a logrus logger. There's no standard
-// interface, this is the closest we get, unfortunately.
+// it'll accept a stdlib logger ([log.Logger]) and a logrus logger.
+// There's no standard interface, so this is the closest we get, unfortunately.
type StdLogger interface {
Print(...interface{})
Printf(string, ...interface{})
@@ -135,7 +137,8 @@ type StdLogger interface {
Panicln(...interface{})
}
-// The FieldLogger interface generalizes the Entry and Logger types
+// FieldLogger extends the [StdLogger] interface, generalizing
+// the [Entry] and [Logger] types.
type FieldLogger interface {
WithField(key string, value interface{}) *Entry
WithFields(fields Fields) *Entry
@@ -176,8 +179,9 @@ type FieldLogger interface {
// IsPanicEnabled() bool
}
-// Ext1FieldLogger (the first extension to FieldLogger) is superfluous, it is
-// here for consistancy. Do not use. Use Logger or Entry instead.
+// Ext1FieldLogger (the first extension to [FieldLogger]) is superfluous, it is
+// here for consistency. Do not use. Use [FieldLogger], [Logger] or [Entry]
+// instead.
type Ext1FieldLogger interface {
FieldLogger
Tracef(format string, args ...interface{})
diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
index 499789984..69956b425 100644
--- a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
+++ b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
@@ -1,4 +1,4 @@
-// +build darwin dragonfly freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd hurd
// +build !js
package logrus
diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go
index 04748b851..c9aed267a 100644
--- a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go
+++ b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go
@@ -1,5 +1,7 @@
+//go:build (linux || aix || zos) && !js && !wasi
// +build linux aix zos
// +build !js
+// +build !wasi
package logrus
diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_wasi.go b/vendor/github.com/sirupsen/logrus/terminal_check_wasi.go
new file mode 100644
index 000000000..2822b212f
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/terminal_check_wasi.go
@@ -0,0 +1,8 @@
+//go:build wasi
+// +build wasi
+
+package logrus
+
+func isTerminal(fd int) bool {
+ return false
+}
diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_wasip1.go b/vendor/github.com/sirupsen/logrus/terminal_check_wasip1.go
new file mode 100644
index 000000000..108a6be12
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/terminal_check_wasip1.go
@@ -0,0 +1,8 @@
+//go:build wasip1
+// +build wasip1
+
+package logrus
+
+func isTerminal(fd int) bool {
+ return false
+}
diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go
index be2c6efe5..6dfeb18b1 100644
--- a/vendor/github.com/sirupsen/logrus/text_formatter.go
+++ b/vendor/github.com/sirupsen/logrus/text_formatter.go
@@ -306,6 +306,7 @@ func (f *TextFormatter) needsQuoting(text string) bool {
return false
}
for _, ch := range text {
+ //nolint:staticcheck // QF1001: could apply De Morgan's law
if !((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') ||
@@ -334,6 +335,6 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
if !f.needsQuoting(stringVal) {
b.WriteString(stringVal)
} else {
- b.WriteString(fmt.Sprintf("%q", stringVal))
+ fmt.Fprintf(b, "%q", stringVal)
}
}
diff --git a/vendor/github.com/spf13/cobra/.golangci.yml b/vendor/github.com/spf13/cobra/.golangci.yml
index 6acf8ab1e..104dc2440 100644
--- a/vendor/github.com/spf13/cobra/.golangci.yml
+++ b/vendor/github.com/spf13/cobra/.golangci.yml
@@ -57,3 +57,10 @@ linters:
- common-false-positives
- legacy
- std-error-handling
+ settings:
+ govet:
+ # Disable buildtag check to allow dual build tag syntax (both //go:build and // +build).
+ # This is necessary for Go 1.15 compatibility since //go:build was introduced in Go 1.17.
+ # This can be removed once Cobra requires Go 1.17 or higher.
+ disable:
+ - buildtag
diff --git a/vendor/github.com/spf13/cobra/command.go b/vendor/github.com/spf13/cobra/command.go
index 78088db69..c05fed45a 100644
--- a/vendor/github.com/spf13/cobra/command.go
+++ b/vendor/github.com/spf13/cobra/command.go
@@ -557,7 +557,7 @@ func (c *Command) FlagErrorFunc() (f func(*Command, error) error) {
}
}
-var minUsagePadding = 25
+const minUsagePadding = 25
// UsagePadding return padding for the usage.
func (c *Command) UsagePadding() int {
@@ -567,7 +567,7 @@ func (c *Command) UsagePadding() int {
return c.parent.commandsMaxUseLen
}
-var minCommandPathPadding = 11
+const minCommandPathPadding = 11
// CommandPathPadding return padding for the command path.
func (c *Command) CommandPathPadding() int {
@@ -577,7 +577,7 @@ func (c *Command) CommandPathPadding() int {
return c.parent.commandsMaxCommandPathLen
}
-var minNamePadding = 11
+const minNamePadding = 11
// NamePadding returns padding for the name.
func (c *Command) NamePadding() int {
@@ -1939,7 +1939,7 @@ type tmplFunc struct {
fn func(io.Writer, interface{}) error
}
-var defaultUsageTemplate = `Usage:{{if .Runnable}}
+const defaultUsageTemplate = `Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
@@ -2039,7 +2039,7 @@ func defaultUsageFunc(w io.Writer, in interface{}) error {
return nil
}
-var defaultHelpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
+const defaultHelpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
@@ -2061,7 +2061,7 @@ func defaultHelpFunc(w io.Writer, in interface{}) error {
return nil
}
-var defaultVersionTemplate = `{{with .DisplayName}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}}
+const defaultVersionTemplate = `{{with .DisplayName}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}}
`
// defaultVersionFunc is equivalent to executing defaultVersionTemplate. The two should be changed in sync.
diff --git a/vendor/golang.org/x/crypto/argon2/argon2.go b/vendor/golang.org/x/crypto/argon2/argon2.go
index 2b65ec91a..57ab8371c 100644
--- a/vendor/golang.org/x/crypto/argon2/argon2.go
+++ b/vendor/golang.org/x/crypto/argon2/argon2.go
@@ -17,8 +17,8 @@
// It uses data-independent memory access, which is preferred for password
// hashing and password-based key derivation. Argon2i requires more passes over
// memory than Argon2id to protect from trade-off attacks. The recommended
-// parameters (taken from [RFC 9106 Section 7.3]) for non-interactive operations are time=3 and to
-// use the maximum available memory.
+// parameters (taken from [RFC 9106 Section 7.3]) for non-interactive
+// operations are time=3 and to use the maximum available memory.
//
// # Argon2id
//
@@ -26,11 +26,14 @@
// Argon2i and Argon2d. It uses data-independent memory access for the first
// half of the first iteration over the memory and data-dependent memory access
// for the rest. Argon2id is side-channel resistant and provides better brute-
-// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
-// parameters for non-interactive operations (taken from [RFC 9106 Section 7.3]) are time=1 and to
-// use the maximum available memory.
+// force cost savings due to time-memory tradeoffs than Argon2i. [RFC 9106
+// Section 4] recommends time=1, memory=2*1024*1024 KiB (2 GiB), and threads=4
+// as the first recommended option. If much less memory is available, it
+// recommends time=3, memory=64*1024 KiB (64 MiB), and threads=4 as the second
+// recommended option.
//
// [argon2-specs.pdf]: https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
+// [RFC 9106 Section 4]: https://www.rfc-editor.org/rfc/rfc9106.html#section-4
// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
package argon2
@@ -59,9 +62,9 @@ const (
//
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
//
-// [RFC 9106 Section 7.3] recommends time=3, and memory=32*1024 as a sensible number.
-// If using that amount of memory (32 MB) is not possible in some contexts then
-// the time parameter can be increased to compensate.
+// The example above uses time=3 and memory=32*1024. Argon2i generally
+// requires more passes over memory than Argon2id. If in doubt, prefer IDKey
+// and its Argon2id parameter recommendations.
//
// The time parameter specifies the number of passes over the memory and the
// memory parameter specifies the size of the memory in KiB. For example
@@ -69,8 +72,6 @@ const (
// adjusted to the number of available CPUs. The cost parameters should be
// increased as memory latency and CPU parallelism increases. Remember to get a
// good random salt.
-//
-// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
}
@@ -83,20 +84,20 @@ func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint3
// For example, you can get a derived key for e.g. AES-256 (which needs a
// 32-byte key) by doing:
//
-// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
+// key := argon2.IDKey([]byte("some password"), salt, 1, 2*1024*1024, 4, 32)
//
-// [RFC 9106 Section 7.3] recommends time=1, and memory=64*1024 as a sensible number.
-// If using that amount of memory (64 MB) is not possible in some contexts then
-// the time parameter can be increased to compensate.
+// The example above uses the first [RFC 9106 Section 4] recommended option.
+// If much less memory is available, the second recommended option is time=3,
+// memory=64*1024 KiB (64 MiB), and threads=4.
//
// The time parameter specifies the number of passes over the memory and the
// memory parameter specifies the size of the memory in KiB. For example
-// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
-// adjusted to the numbers of available CPUs. The cost parameters should be
+// memory=2*1024*1024 sets the memory cost to ~2 GiB. The number of threads can
+// be adjusted to the numbers of available CPUs. The cost parameters should be
// increased as memory latency and CPU parallelism increases. Remember to get a
// good random salt.
//
-// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
+// [RFC 9106 Section 4]: https://www.rfc-editor.org/rfc/rfc9106.html#section-4
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
}
diff --git a/vendor/golang.org/x/crypto/ssh/agent/forward.go b/vendor/golang.org/x/crypto/ssh/agent/forward.go
index fd24ba900..5e7a0ea40 100644
--- a/vendor/golang.org/x/crypto/ssh/agent/forward.go
+++ b/vendor/golang.org/x/crypto/ssh/agent/forward.go
@@ -41,6 +41,7 @@ func ForwardToAgent(client *ssh.Client, keyring Agent) error {
continue
}
go ssh.DiscardRequests(reqs)
+ go io.Copy(io.Discard, channel.Stderr())
go func() {
ServeAgent(keyring, channel)
channel.Close()
@@ -72,6 +73,7 @@ func ForwardToRemote(client *ssh.Client, addr string) error {
continue
}
go ssh.DiscardRequests(reqs)
+ go io.Copy(io.Discard, channel.Stderr())
go forwardUnixSocket(channel, addr)
}
}()
diff --git a/vendor/golang.org/x/crypto/ssh/agent/server.go b/vendor/golang.org/x/crypto/ssh/agent/server.go
index f05d22fb3..782c54eb7 100644
--- a/vendor/golang.org/x/crypto/ssh/agent/server.go
+++ b/vendor/golang.org/x/crypto/ssh/agent/server.go
@@ -304,19 +304,60 @@ func parseEd25519Key(req []byte) (*AddedKey, error) {
return addedKey, nil
}
+func checkDSAParams(param *dsa.Parameters) error {
+ // SSH specifies FIPS 186-2, which only provided a single size
+ // (1024 bits) DSA key. FIPS 186-3 allows for larger key
+ // sizes, which would confuse SSH.
+ if l := param.P.BitLen(); l != 1024 {
+ return fmt.Errorf("ssh: unsupported DSA key size %d", l)
+ }
+
+ // FIPS 186-2 specifies that Q must be exactly 160 bits. We must enforce
+ // this to prevent DoS attacks where an attacker sends a huge Q which makes
+ // verification slow.
+ if l := param.Q.BitLen(); l != 160 {
+ return fmt.Errorf("ssh: unsupported DSA sub-prime size %d", l)
+ }
+
+ // The generator G is an element of the group, so it must be strictly less
+ // than the modulus P.
+ if param.G.Cmp(param.P) >= 0 {
+ return errors.New("ssh: DSA generator larger than modulus")
+ }
+
+ // G must be positive.
+ if param.G.Sign() <= 0 {
+ return errors.New("ssh: DSA generator must be positive")
+ }
+
+ return nil
+}
+
func parseDSAKey(req []byte) (*AddedKey, error) {
var k dsaKeyMsg
if err := ssh.Unmarshal(req, &k); err != nil {
return nil, err
}
+ params := dsa.Parameters{
+ P: k.P,
+ Q: k.Q,
+ G: k.G,
+ }
+ if err := checkDSAParams(¶ms); err != nil {
+ return nil, err
+ }
+
+ // The public value Y must be a non-zero element of the group, i.e. strictly
+ // between 0 and P, to prevent a maliciously oversized Y from slowing
+ // signature operations.
+ if k.Y.Sign() <= 0 || k.Y.Cmp(k.P) >= 0 {
+ return nil, errors.New("agent: DSA public value Y out of range")
+ }
+
priv := &dsa.PrivateKey{
PublicKey: dsa.PublicKey{
- Parameters: dsa.Parameters{
- P: k.P,
- Q: k.Q,
- G: k.G,
- },
- Y: k.Y,
+ Parameters: params,
+ Y: k.Y,
},
X: k.X,
}
diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go
index 334861b7f..64377715e 100644
--- a/vendor/golang.org/x/crypto/ssh/keys.go
+++ b/vendor/golang.org/x/crypto/ssh/keys.go
@@ -182,14 +182,19 @@ func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey
}
hosts := string(keyFields[0])
- // keyFields[1] contains the key type (e.g. “ssh-rsa”).
- // However, that information is duplicated inside the
- // base64-encoded key and so is ignored here.
+ // keyFields[1] contains the key type (e.g. "ssh-rsa"). This information
+ // is duplicated within the base64-encoded key blob. As OpenSSH's
+ // sshkey_read does, we verify that the declared key type matches the
+ // type embedded in the key blob.
+ wantType := string(keyFields[1])
key := bytes.Join(keyFields[2:], []byte(" "))
if pubKey, comment, err = parseAuthorizedKey(key); err != nil {
return "", nil, nil, "", nil, err
}
+ if pubKey.Type() != wantType {
+ return "", nil, nil, "", nil, fmt.Errorf("ssh: known hosts key type mismatch: human-readable type %q, encoded type %q", wantType, pubKey.Type())
+ }
return marker, strings.Split(hosts, ","), pubKey, comment, rest, nil
}
@@ -228,10 +233,17 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str
}
if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
- return out, comment, options, rest, nil
- } else {
- lastErr = err
+ // The first field contains the declared key type. As OpenSSH's
+ // sshkey_read does, we verify that it matches the type embedded in
+ // the key blob. Without this check, a single-token option (e.g.
+ // "restrict") appearing in the key type position could be silently
+ // discarded along with its intended effect.
+ if string(in[:i]) == out.Type() {
+ return out, comment, options, rest, nil
+ }
+ err = fmt.Errorf("ssh: authorized keys key type mismatch: human-readable type %q, encoded type %q", in[:i], out.Type())
}
+ lastErr = err
// No key type recognised. Maybe there's an options field at
// the beginning.
@@ -271,11 +283,15 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str
}
if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
- options = candidateOptions
- return out, comment, options, rest, nil
- } else {
- lastErr = err
+ // As above, the declared key type (here following the options
+ // field) must match the type embedded in the key blob.
+ if string(in[:i]) == out.Type() {
+ options = candidateOptions
+ return out, comment, options, rest, nil
+ }
+ err = fmt.Errorf("ssh: authorized keys key type mismatch: human-readable type %q, encoded type %q", in[:i], out.Type())
}
+ lastErr = err
in = rest
continue
diff --git a/vendor/golang.org/x/crypto/ssh/messages.go b/vendor/golang.org/x/crypto/ssh/messages.go
index ab22c3d38..de86f71cf 100644
--- a/vendor/golang.org/x/crypto/ssh/messages.go
+++ b/vendor/golang.org/x/crypto/ssh/messages.go
@@ -44,7 +44,16 @@ type disconnectMsg struct {
}
func (d *disconnectMsg) Error() string {
- return fmt.Sprintf("ssh: disconnect, reason %d: %s", d.Reason, d.Message)
+ return fmt.Sprintf("ssh: disconnect, reason %d: %q", d.Reason, sanitizeString(d.Message))
+}
+
+func sanitizeString(s string) string {
+ return strings.Map(func(r rune) rune {
+ if r == '\t' || (r >= ' ' && r < 0x7f) {
+ return r
+ }
+ return -1
+ }, s)
}
// See RFC 4253, section 7.1.
diff --git a/vendor/golang.org/x/mod/modfile/read.go b/vendor/golang.org/x/mod/modfile/read.go
index 504a2f1df..5b528c718 100644
--- a/vendor/golang.org/x/mod/modfile/read.go
+++ b/vendor/golang.org/x/mod/modfile/read.go
@@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"os"
+ "slices"
"strconv"
"strings"
"unicode"
@@ -105,8 +106,7 @@ func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line {
if hint == nil {
// If no hint given, add to the last statement of the given type.
Loop:
- for i := len(x.Stmt) - 1; i >= 0; i-- {
- stmt := x.Stmt[i]
+ for _, stmt := range slices.Backward(x.Stmt) {
switch stmt := stmt.(type) {
case *Line:
if stmt.Token != nil && stmt.Token[0] == tokens[0] {
@@ -718,9 +718,7 @@ func (in *input) assignComments() {
}
// Assign suffix comments to syntax immediately before.
- for i := len(in.post) - 1; i >= 0; i-- {
- x := in.post[i]
-
+ for _, x := range slices.Backward(in.post) {
start, end := x.Span()
if debug {
fmt.Fprintf(os.Stderr, "post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte)
diff --git a/vendor/golang.org/x/mod/modfile/rule.go b/vendor/golang.org/x/mod/modfile/rule.go
index c5b8305de..9ab203b56 100644
--- a/vendor/golang.org/x/mod/modfile/rule.go
+++ b/vendor/golang.org/x/mod/modfile/rule.go
@@ -327,6 +327,7 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parse
}
var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))?([a-z]+[0-9]+)?$`)
+
var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].*)$`)
// Toolchains must be named beginning with `go1`,
@@ -1272,6 +1273,17 @@ func (f *File) SetRequire(req []*Require) {
// SetRequireSeparateIndirect will split it into a direct-only and indirect-only
// block. This aids in the transition to separate blocks.
func (f *File) SetRequireSeparateIndirect(req []*Require) {
+ f.setRequireSeparateIndirect(req, false)
+}
+
+// SetRequireAtMostTwo is like SetRequireSeparateIndirect but it aggressively
+// consolidates all requirements into at most two blocks (one direct, one indirect).
+// It ignores existing blocks and comments when deciding where to place requirements.
+func (f *File) SetRequireAtMostTwo(req []*Require) {
+ f.setRequireSeparateIndirect(req, true)
+}
+
+func (f *File) setRequireSeparateIndirect(req []*Require, simplify bool) {
// hasComments returns whether a line or block has comments
// other than "indirect".
hasComments := func(c Comments) bool {
@@ -1304,6 +1316,17 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
}
// Examine existing require lines and blocks.
+ need := make(map[string]*Require)
+ for _, r := range req {
+ need[r.Mod.Path] = r
+ }
+ lineIndirect := make(map[*Line]bool)
+ for _, r := range f.Require {
+ if n := need[r.Mod.Path]; n != nil {
+ lineIndirect[r.Syntax] = n.Indirect
+ }
+ }
+
var (
// We may insert new requirements into the last uncommented
// direct-only and indirect-only blocks. We may also move requirements
@@ -1321,7 +1344,9 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
// Track the block each requirement belongs to (if any) so we can
// move them later.
- lineToBlock = make(map[*Line]*LineBlock)
+ lineToBlock = make(map[*Line]*LineBlock)
+ directBlockComments []Comment
+ indirectBlockComments []Comment
)
for i, stmt := range f.Syntax.Stmt {
switch stmt := stmt.(type) {
@@ -1364,6 +1389,24 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
if allIndirect {
lastIndirectIndex = i
}
+ if simplify {
+ anyDirect := false
+ for _, line := range stmt.Line {
+ if ind, ok := lineIndirect[line]; ok && !ind {
+ anyDirect = true
+ break
+ }
+ }
+ target := &directBlockComments
+ if !anyDirect && len(stmt.Line) > 0 {
+ target = &indirectBlockComments
+ }
+ if len(*target) > 0 && len(stmt.Comments.Before) > 0 {
+ *target = append(*target, Comment{Token: "//"})
+ }
+ *target = append(*target, stmt.Comments.Before...)
+ stmt.Comments.Before = nil
+ }
}
}
@@ -1422,6 +1465,15 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
lastIndirectBlock = ensureBlock(lastIndirectIndex)
}
+ if simplify {
+ if len(directBlockComments) > 0 {
+ lastDirectBlock.Comments.Before = append(lastDirectBlock.Comments.Before, directBlockComments...)
+ }
+ if len(indirectBlockComments) > 0 {
+ lastIndirectBlock.Comments.Before = append(lastIndirectBlock.Comments.Before, indirectBlockComments...)
+ }
+ }
+
// Delete requirements we don't want anymore.
// Update versions and indirect comments on requirements we want to keep.
// If a requirement is in last{Direct,Indirect}Block with the wrong
@@ -1430,10 +1482,6 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
// correct block.
//
// Some blocks may be empty after this. Cleanup will remove them.
- need := make(map[string]*Require)
- for _, r := range req {
- need[r.Mod.Path] = r
- }
have := make(map[string]*Require)
for _, r := range f.Require {
path := r.Mod.Path
@@ -1446,10 +1494,10 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
r.setVersion(need[path].Mod.Version)
r.setIndirect(need[path].Indirect)
if need[path].Indirect &&
- (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) {
+ (simplify || oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) {
moveReq(r, lastIndirectBlock)
} else if !need[path].Indirect &&
- (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) {
+ (simplify || oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) {
moveReq(r, lastDirectBlock)
}
}
@@ -1736,8 +1784,7 @@ func removeDups(syntax *FileSyntax, exclude *[]*Exclude, replace *[]*Replace, to
// Remove duplicate replacements.
// Later replacements take priority over earlier ones.
haveReplace := make(map[module.Version]bool)
- for i := len(*replace) - 1; i >= 0; i-- {
- x := (*replace)[i]
+ for _, x := range slices.Backward(*replace) {
if haveReplace[x.Old] {
kill[x.Syntax] = true
continue
diff --git a/vendor/golang.org/x/net/bpf/doc.go b/vendor/golang.org/x/net/bpf/doc.go
index 04ec1c8ab..1ea566b7f 100644
--- a/vendor/golang.org/x/net/bpf/doc.go
+++ b/vendor/golang.org/x/net/bpf/doc.go
@@ -49,6 +49,13 @@ to extensions, which are essentially calls to kernel utility
functions. Currently, the only extensions supported by this package
are the Linux packet filter extensions.
+# Security Considerations
+
+The implementation of the BPF VM in this package is suitable for
+testing BPF programs. It aims for consistency with other BPF VM
+implementations, but divergence in behavior is not considered a
+security issue.
+
# Examples
This packet filter selects all ARP packets.
diff --git a/vendor/golang.org/x/net/http2/transport_wrap.go b/vendor/golang.org/x/net/http2/transport_wrap.go
index eab2e6b07..534e77ab9 100644
--- a/vendor/golang.org/x/net/http2/transport_wrap.go
+++ b/vendor/golang.org/x/net/http2/transport_wrap.go
@@ -55,7 +55,7 @@ type transportConfig struct {
// Registered is called by net/http.Transport.RegisterProtocol,
// to let us know that it understands the registration mechanism we're using.
func (t transportConfig) Registered(t1 *http.Transport) {
- t.t.t1 = t1
+ t.t.lazyt1 = t1
}
func (t transportConfig) DisableCompression() bool {
@@ -145,29 +145,30 @@ func (t transportConfig) DialFromContext(ctx context.Context, network, address s
type transportInternal struct {
initOnce sync.Once
- t1 *http.Transport
+ lazyt1 *http.Transport
}
-func (t *Transport) init() {
+func (t *Transport) init() *http.Transport {
t.initOnce.Do(func() {
- if t.t1 != nil {
+ if t.lazyt1 != nil {
return
}
t1 := &http.Transport{}
t.configure(t1)
})
+ return t.lazyt1
}
func (t *Transport) configure(t1 *http.Transport) {
t1.RegisterProtocol("http/2", transportConfig{t})
- // tr2.t1 is set by transportConfig.Registered.
- if t.t1 != t1 {
+ // tr2.lazyt1 is set by transportConfig.Registered.
+ if t.lazyt1 != t1 {
panic("http2: net/http does not support this version of x/net/http2")
}
}
func (t *Transport) roundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
- t.init()
+ t1 := t.init()
if req.URL.Scheme == "http" && !t.AllowHTTP {
return nil, errors.New("http2: unencrypted HTTP/2 not enabled")
@@ -188,22 +189,23 @@ func (t *Transport) roundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
ctx := context.WithValue(req.Context(), http2TransportContextKey{}, t)
req = req.WithContext(ctx)
- return t.t1.RoundTrip(req)
+ return t1.RoundTrip(req)
}
func (t *Transport) closeIdleConnections() {
- t.init()
- t.t1.CloseIdleConnections()
+ t1 := t.init()
+ t1.CloseIdleConnections()
}
func (t *Transport) newUserClientConn(c net.Conn) (*ClientConn, error) {
+ t1 := t.init()
// http.Transport's NewClientConn doesn't provide a supported way to create
// a connection from a net.Conn. (This might be useful to add in the future?)
// We're going to craftily sneak one in via the context key, with the
// scheme of "http/2" telling NewClientConn to look for it.
ctx := context.WithValue(context.Background(), netConnContextKey{}, c)
- nhcc, err := t.t1.NewClientConn(ctx, "http/2", "")
+ nhcc, err := t1.NewClientConn(ctx, "http/2", "")
if err != nil {
return nil, err
}
diff --git a/vendor/golang.org/x/net/idna/idna.go b/vendor/golang.org/x/net/idna/idna.go
index 22767125b..e2f28fed4 100644
--- a/vendor/golang.org/x/net/idna/idna.go
+++ b/vendor/golang.org/x/net/idna/idna.go
@@ -400,7 +400,11 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
// Spec says keep the old label.
continue
}
- if unicode16 && err == nil && len(u) > 0 && isASCII(u) {
+ if err == nil && len(u) > 0 && isASCII(u) {
+ // UTS 43 pre-revision 33 doesn't classify a xn-- label
+ // which contains only ASCII characters as an error,
+ // but that's a specification bug and a security issue.
+ // Always return an error in this case.
err = punyError(enc)
}
isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight
diff --git a/vendor/golang.org/x/sys/cpu/parse.go b/vendor/golang.org/x/sys/cpu/parse.go
index 56a7e1a17..12a99af5c 100644
--- a/vendor/golang.org/x/sys/cpu/parse.go
+++ b/vendor/golang.org/x/sys/cpu/parse.go
@@ -6,38 +6,50 @@ package cpu
import "strconv"
-// parseRelease parses a dot-separated version number. It follows the semver
-// syntax, but allows the minor and patch versions to be elided.
+// parseRelease parses a dot-separated version number from the prefix
+// of rel. It returns ok=true only if at least the major and minor
+// components were successfully parsed; the patch component is
+// best-effort. Trailing vendor or build suffixes such as
+// "-generic", "+", "_hi3535", or "-rc1" are ignored.
//
// This is a copy of the Go runtime's parseRelease from
-// https://golang.org/cl/209597.
+// https://golang.org/cl/209597, updated in https://golang.org/cl/781800.
func parseRelease(rel string) (major, minor, patch int, ok bool) {
- // Strip anything after a dash or plus.
- for i := range len(rel) {
- if rel[i] == '-' || rel[i] == '+' {
- rel = rel[:i]
- break
+ // next consumes a run of decimal digits from the front of rel,
+ // returning the parsed value. If the digits are followed by a
+ // '.', it is consumed and more is set so the caller knows to
+ // parse another component; otherwise scanning terminates and
+ // the rest of rel is discarded.
+ next := func() (n int, more, ok bool) {
+ i := 0
+ for i < len(rel) && rel[i] >= '0' && rel[i] <= '9' {
+ i++
}
- }
-
- next := func() (int, bool) {
- for i := range len(rel) {
- if rel[i] == '.' {
- ver, err := strconv.Atoi(rel[:i])
- rel = rel[i+1:]
- return ver, err == nil
- }
+ if i == 0 {
+ return 0, false, false
+ }
+ n, err := strconv.Atoi(rel[:i])
+ if err != nil {
+ return 0, false, false
+ }
+ if i < len(rel) && rel[i] == '.' {
+ rel = rel[i+1:]
+ return n, true, true
}
- ver, err := strconv.Atoi(rel)
rel = ""
- return ver, err == nil
+ return n, false, true
+ }
+
+ var more bool
+ if major, more, ok = next(); !ok || !more {
+ return 0, 0, 0, false
}
- if major, ok = next(); !ok || rel == "" {
- return
+ if minor, more, ok = next(); !ok {
+ return 0, 0, 0, false
}
- if minor, ok = next(); !ok || rel == "" {
- return
+ if !more {
+ return major, minor, 0, true
}
- patch, ok = next()
- return
+ patch, _, _ = next()
+ return major, minor, patch, true
}
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go
index ce4d7ab1e..21e2bfa39 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux.go
@@ -1874,6 +1874,7 @@ func Dup2(oldfd, newfd int) error {
//sys Dup3(oldfd int, newfd int, flags int) (err error)
//sysnb EpollCreate1(flag int) (fd int, err error)
//sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error)
+//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
//sys Eventfd(initval uint, flags int) (fd int, err error) = SYS_EVENTFD2
//sys Exit(code int) = SYS_EXIT_GROUP
//sys Fallocate(fd int, mode uint32, off int64, len int64) (err error)
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_386.go b/vendor/golang.org/x/sys/unix/syscall_linux_386.go
index 506dafa7b..210d545c9 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_386.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_386.go
@@ -20,7 +20,6 @@ func setTimeval(sec, usec int64) Timeval {
// 64-bit file system and 32-bit uid calls
// (386 default is 32-bit file system and 16-bit uid).
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64_64
//sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go b/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go
index d557cf8de..a9a52f231 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go
@@ -6,7 +6,6 @@
package unix
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error)
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go
index ecf92bfa2..54474c20f 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go
@@ -44,7 +44,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
// 64-bit file system and 32-bit uid calls
// (16-bit uid calls are not always supported in newer kernels)
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
//sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go
index 173738077..e9f30db97 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go
@@ -8,7 +8,6 @@ package unix
import "unsafe"
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error)
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go
index a3fd1d0b8..6f09ca200 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go
@@ -8,7 +8,6 @@ package unix
import "unsafe"
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstatfs(fd int, buf *Statfs_t) (err error)
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go b/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go
index 70963a95a..ca3b56597 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go
@@ -6,7 +6,6 @@
package unix
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstatfs(fd int, buf *Statfs_t) (err error)
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go b/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go
index c218ebd28..54ba667b1 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go
@@ -13,7 +13,6 @@ import (
func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Ftruncate(fd int, length int64) (err error) = SYS_FTRUNCATE64
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go b/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go
index e6c48500c..ce4628590 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go
@@ -11,7 +11,6 @@ import (
"unsafe"
)
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
//sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go b/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go
index 7286a9aa8..33f7af380 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go
@@ -6,7 +6,6 @@
package unix
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error)
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go
index fc5543c5f..c658871e3 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go
@@ -8,7 +8,6 @@ package unix
import "unsafe"
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error)
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go b/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go
index 66f31210d..2c8587691 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go
@@ -10,7 +10,6 @@ import (
"unsafe"
)
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error)
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go b/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go
index 11d1f1698..4964119af 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go
@@ -6,7 +6,6 @@
package unix
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error)
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go
index 9d72a6b73..5bb51d7ae 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go
@@ -1359,6 +1359,7 @@ const (
FAN_UNLIMITED_MARKS = 0x20
FAN_UNLIMITED_QUEUE = 0x10
FD_CLOEXEC = 0x1
+ FD_PIDFS_ROOT = -0x2712
FD_SETSIZE = 0x400
FF0 = 0x0
FIB_RULE_DEV_DETACHED = 0x8
@@ -1970,6 +1971,8 @@ const (
MADV_DONTNEED = 0x4
MADV_DONTNEED_LOCKED = 0x18
MADV_FREE = 0x8
+ MADV_GUARD_INSTALL = 0x66
+ MADV_GUARD_REMOVE = 0x67
MADV_HUGEPAGE = 0xe
MADV_HWPOISON = 0x64
MADV_KEEPONFORK = 0x13
@@ -2114,7 +2117,7 @@ const (
MS_NOSEC = 0x10000000
MS_NOSUID = 0x2
MS_NOSYMFOLLOW = 0x100
- MS_NOUSER = -0x80000000
+ MS_NOUSER = 0x80000000
MS_POSIXACL = 0x10000
MS_PRIVATE = 0x40000
MS_RDONLY = 0x1
@@ -3786,6 +3789,9 @@ const (
TCPOPT_TIMESTAMP = 0x8
TCPOPT_TSTAMP_HDR = 0x101080a
TCPOPT_WINDOW = 0x3
+ TCP_AO_KEYF_EXCLUDE_OPT = 0x2
+ TCP_AO_KEYF_IFINDEX = 0x1
+ TCP_AO_MAXKEYLEN = 0x50
TCP_CC_INFO = 0x1a
TCP_CM_INQ = 0x24
TCP_CONGESTION = 0xd
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go
index 80f40e401..5788c2a58 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go
@@ -700,6 +700,23 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(events) > 0 {
+ _p0 = unsafe.Pointer(&events[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Eventfd(initval uint, flags int) (fd int, err error) {
r0, _, e1 := Syscall(SYS_EVENTFD2, uintptr(initval), uintptr(flags), 0)
fd = int(r0)
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go
index 4def3e9fc..254f33988 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64_64, uintptr(fd), uintptr(offset), uintptr(offset>>32), uintptr(length), uintptr(length>>32), uintptr(advice))
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go
index fef2bc8ba..27c05db1a 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go
index a9fd76a88..840d85bfc 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go
@@ -213,23 +213,6 @@ func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fchown(fd int, uid int, gid int) (err error) {
_, _, e1 := Syscall(SYS_FCHOWN32, uintptr(fd), uintptr(uid), uintptr(gid))
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go
index 460065028..fe414498b 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go
index c8987d264..eb358ce05 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go
index 921f43061..c437622f1 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall9(SYS_FADVISE64, uintptr(fd), 0, uintptr(offset>>32), uintptr(offset), uintptr(length>>32), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go
index 44f067829..bc4ca2558 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go
index e7fa0abf0..5051435ce 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go
index 8c5125675..33aa5418a 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall9(SYS_FADVISE64, uintptr(fd), 0, uintptr(offset), uintptr(offset>>32), uintptr(length), uintptr(length>>32), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go
index 7392fd45e..3bef8ef1d 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fchown(fd int, uid int, gid int) (err error) {
_, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go
index 41180434e..fc1bd4e2c 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go
index 40c6ce7ae..d78fe7dab 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go
index 2cfe34adb..76dcf87d0 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go
index 61e6f0709..2cf020f2b 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go
index 834b84204..527637623 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(events) > 0 {
- _p0 = unsafe.Pointer(&events[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
- n = int(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/windows/security_windows.go b/vendor/golang.org/x/sys/windows/security_windows.go
index 6c955cea1..783621561 100644
--- a/vendor/golang.org/x/sys/windows/security_windows.go
+++ b/vendor/golang.org/x/sys/windows/security_windows.go
@@ -1109,17 +1109,53 @@ const (
)
// This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions.
+//
+// Go pointers stored in a TrusteeValue must be pinned using [runtime.Pinner]
+// for the lifetime of the TrusteeValue.
type TrusteeValue uintptr
+// TrusteeValueFromString is unsafe and should not be used.
+//
+// It returns a uintptr containing a reference to newly-allocated memory
+// which will be freed by the garbage collector.
+// There is no way for the caller to safely reference this memory.
+//
+// To create a [TrusteeValue] from a string, use:
+//
+// p, err := windows.UTF16PtrFromString(s)
+// if err != nil {
+// // handle error
+// }
+//
+// // Pin the string for as long as it is used.
+// var pinner runtime.Pinner
+// pinner.Pin(p)
+// defer pinner.Unpin()
+//
+// tv := TrusteeValue(unsafe.Pointer(p))
+//
+// Deprecated: TrusteeValueFromString is unsafe and should not be used.
func TrusteeValueFromString(str string) TrusteeValue {
return TrusteeValue(unsafe.Pointer(StringToUTF16Ptr(str)))
}
+
+// TrusteeValueFromSID returns a [TrusteeValue] referencing sid.
+//
+// The caller must pin sid using a [runtime.Pinner] for the lifetime of the TrusteeValue.
func TrusteeValueFromSID(sid *SID) TrusteeValue {
return TrusteeValue(unsafe.Pointer(sid))
}
+
+// TrusteeValueFromObjectsAndSid returns a [TrusteeValue] referencing objectsAndSid.
+//
+// The caller must pin objectsAndSid using a [runtime.Pinner] for the lifetime of the TrusteeValue.
func TrusteeValueFromObjectsAndSid(objectsAndSid *OBJECTS_AND_SID) TrusteeValue {
return TrusteeValue(unsafe.Pointer(objectsAndSid))
}
+
+// TrusteeValueFromObjectsAndName returns a [TrusteeValue] referencing objectsAndName.
+//
+// The caller must pin objectsAndName using a [runtime.Pinner] for the lifetime of the TrusteeValue.
func TrusteeValueFromObjectsAndName(objectsAndName *OBJECTS_AND_NAME) TrusteeValue {
return TrusteeValue(unsafe.Pointer(objectsAndName))
}
diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go
index 9755bca9f..e6966b4c3 100644
--- a/vendor/golang.org/x/sys/windows/syscall_windows.go
+++ b/vendor/golang.org/x/sys/windows/syscall_windows.go
@@ -1728,11 +1728,15 @@ func (s *NTUnicodeString) String() string {
// the more common *uint16 string type.
func NewNTString(s string) (*NTString, error) {
var nts NTString
- s8, err := BytePtrFromString(s)
+ s8, err := ByteSliceFromString(s)
if err != nil {
return nil, err
}
- RtlInitString(&nts, s8)
+ // The source string plus its terminating NUL must fit within MAX_USHORT.
+ if len(s8) > MAX_USHORT {
+ return nil, syscall.EINVAL
+ }
+ RtlInitString(&nts, &s8[0])
return &nts, nil
}
diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go
index d2574a73e..75a50b316 100644
--- a/vendor/golang.org/x/sys/windows/types_windows.go
+++ b/vendor/golang.org/x/sys/windows/types_windows.go
@@ -169,6 +169,7 @@ const (
FORMAT_MESSAGE_ARGUMENT_ARRAY = 8192
FORMAT_MESSAGE_MAX_WIDTH_MASK = 255
+ MAX_USHORT = 0xffff
MAX_PATH = 260
MAX_LONG_PATH = 32768
diff --git a/vendor/golang.org/x/text/unicode/norm/forminfo.go b/vendor/golang.org/x/text/unicode/norm/forminfo.go
index f3a234e5f..b3cf5d9bd 100644
--- a/vendor/golang.org/x/text/unicode/norm/forminfo.go
+++ b/vendor/golang.org/x/text/unicode/norm/forminfo.go
@@ -121,8 +121,12 @@ func (p Properties) BoundaryAfter() bool {
//
// When all 6 bits are zero, the character is inert, meaning it is never
// influenced by normalization.
+//
+// We set flags to 0x80 (high bit 7 unused in quick check data) to indicate an invalid rune.
type qcInfo uint8
+func (p Properties) isInvalid() bool { return p.flags == 0x80 }
+
func (p Properties) isYesC() bool { return p.flags&0x10 == 0 }
func (p Properties) isYesD() bool { return p.flags&0x4 == 0 }
@@ -247,6 +251,9 @@ func (f Form) PropertiesString(s string) Properties {
// to a Properties. See the comment at the top of the file
// for more information on the format.
func compInfo(v uint16, sz int) Properties {
+ if sz == 0 {
+ return Properties{flags: 0x80, size: 1}
+ }
if v == 0 {
return Properties{size: uint8(sz)}
} else if v >= 0x8000 {
@@ -254,7 +261,7 @@ func compInfo(v uint16, sz int) Properties {
size: uint8(sz),
ccc: uint8(v),
tccc: uint8(v),
- flags: qcInfo(v >> 8),
+ flags: qcInfo(v>>8) & 0x3f,
}
if p.ccc > 0 || p.combinesBackward() {
p.nLead = uint8(p.flags & 0x3)
diff --git a/vendor/golang.org/x/text/unicode/norm/iter.go b/vendor/golang.org/x/text/unicode/norm/iter.go
index 417c6b268..3cc059224 100644
--- a/vendor/golang.org/x/text/unicode/norm/iter.go
+++ b/vendor/golang.org/x/text/unicode/norm/iter.go
@@ -376,16 +376,12 @@ func nextComposed(i *Iter) []byte {
goto doNorm
}
prevCC = i.info.tccc
- sz := int(i.info.size)
- if sz == 0 {
- sz = 1 // illegal rune: copy byte-by-byte
- }
- p := outp + sz
+ p := outp + int(i.info.size)
if p > len(i.buf) {
break
}
outp = p
- i.p += sz
+ i.p += int(i.info.size)
if i.p >= i.rb.nsrc {
i.setDone()
break
diff --git a/vendor/golang.org/x/text/unicode/norm/normalize.go b/vendor/golang.org/x/text/unicode/norm/normalize.go
index 4747ad07a..60b1511ca 100644
--- a/vendor/golang.org/x/text/unicode/norm/normalize.go
+++ b/vendor/golang.org/x/text/unicode/norm/normalize.go
@@ -148,7 +148,7 @@ func (f Form) IsNormalString(s string) bool {
// patched buffer and whether the decomposition is still in progress.
func patchTail(rb *reorderBuffer) bool {
info, p := lastRuneStart(&rb.f, rb.out)
- if p == -1 || info.size == 0 {
+ if p == -1 || info.isInvalid() {
return true
}
end := p + int(info.size)
@@ -225,7 +225,7 @@ func doAppend(rb *reorderBuffer, out []byte, p int) []byte {
}
fd := &rb.f
if doMerge {
- var info Properties
+ info := Properties{flags: 0x80, size: 1} // invalid rune
if p < n {
info = fd.info(src, p)
if !info.BoundaryBefore() || info.nLeadingNonStarters() > 0 {
@@ -235,7 +235,7 @@ func doAppend(rb *reorderBuffer, out []byte, p int) []byte {
p = decomposeSegment(rb, p, true)
}
}
- if info.size == 0 {
+ if info.isInvalid() {
rb.doFlush()
// Append incomplete UTF-8 encoding.
return src.appendSlice(rb.out, p, n)
@@ -314,7 +314,7 @@ func (f *formInfo) quickSpan(src input, i, end int, atEOF bool) (n int, ok bool)
continue
}
info := f.info(src, i)
- if info.size == 0 {
+ if info.isInvalid() {
if atEOF {
// include incomplete runes
return n, true
@@ -379,7 +379,7 @@ func (f Form) firstBoundary(src input, nsrc int) int {
// CGJ insertion points correctly. Luckily it doesn't have to.
for {
info := fd.info(src, i)
- if info.size == 0 {
+ if info.isInvalid() {
return -1
}
if s := ss.next(info); s != ssSuccess {
@@ -424,7 +424,7 @@ func (f Form) nextBoundary(src input, nsrc int, atEOF bool) int {
}
fd := formTable[f]
info := fd.info(src, 0)
- if info.size == 0 {
+ if info.isInvalid() {
if atEOF {
return 1
}
@@ -435,7 +435,7 @@ func (f Form) nextBoundary(src input, nsrc int, atEOF bool) int {
for i := int(info.size); i < nsrc; i += int(info.size) {
info = fd.info(src, i)
- if info.size == 0 {
+ if info.isInvalid() {
if atEOF {
return i
}
@@ -465,7 +465,7 @@ func lastBoundary(fd *formInfo, b []byte) int {
if p == -1 {
return -1
}
- if info.size == 0 { // ends with incomplete rune
+ if info.isInvalid() { // ends with incomplete rune
if p == 0 { // starts with incomplete rune
return -1
}
@@ -504,7 +504,7 @@ func lastBoundary(fd *formInfo, b []byte) int {
func decomposeSegment(rb *reorderBuffer, sp int, atEOF bool) int {
// Force one character to be consumed.
info := rb.f.info(rb.src, sp)
- if info.size == 0 {
+ if info.isInvalid() {
return 0
}
if s := rb.ss.next(info); s == ssStarter {
@@ -528,7 +528,7 @@ func decomposeSegment(rb *reorderBuffer, sp int, atEOF bool) int {
break
}
info = rb.f.info(rb.src, sp)
- if info.size == 0 {
+ if info.isInvalid() {
if !atEOF {
return int(iShortSrc)
}
diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go
index de683684a..1e5549abe 100644
--- a/vendor/golang.org/x/tools/go/packages/packages.go
+++ b/vendor/golang.org/x/tools/go/packages/packages.go
@@ -815,6 +815,12 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
needsrc: needsrc,
goVersion: response.GoVersion,
}
+ // Don't trust the driver to respond with duplicate-free
+ // package names (go.dev/issue/63822).
+ if _, ok := ld.pkgs[lpkg.ID]; ok {
+ return nil, fmt.Errorf("%s response contained duplicate packages for ID %q",
+ cond(ld.externalDriver, "go/packages driver", "go list"), lpkg.ID)
+ }
ld.pkgs[lpkg.ID] = lpkg
if rootIndex >= 0 {
initial[rootIndex] = lpkg
@@ -1589,3 +1595,11 @@ func usesExportData(cfg *Config) bool {
}
type unit struct{}
+
+func cond[T any](cond bool, t, f T) T {
+ if cond {
+ return t
+ } else {
+ return f
+ }
+}
diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go
index 4c9450f4e..686f171d3 100644
--- a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go
+++ b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go
@@ -823,6 +823,9 @@ func (p *iexporter) doDecl(obj types.Object) {
w.pos(m.Pos())
w.string(m.Name())
sig, _ := m.Type().(*types.Signature)
+ if w.p.version >= iexportVersionGenericMethods && w.bool(sig.TypeParams().Len() > 0) {
+ w.tparamList(obj.Name()+"."+m.Name(), sig.TypeParams(), obj.Pkg())
+ }
// Receiver type parameters are type arguments of the receiver type, so
// their name must be qualified before exporting recv.
diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go
index 1ee4e9354..7b1723e0f 100644
--- a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go
+++ b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go
@@ -48,13 +48,14 @@ func (r *intReader) uint64() uint64 {
// Keep this in sync with constants in iexport.go.
const (
- iexportVersionGo1_11 = 0
- iexportVersionPosCol = 1
- iexportVersionGo1_18 = 2
- iexportVersionGenerics = 2
- iexportVersion = iexportVersionGenerics
-
- iexportVersionCurrent = 2
+ iexportVersionGo1_11 = 0
+ iexportVersionPosCol = 1
+ iexportVersionGo1_18 = 2
+ iexportVersionGenerics = 2
+ iexportVersionGenericMethods = 3
+ iexportVersion = iexportVersionGenericMethods
+
+ iexportVersionCurrent = 3
)
type ident struct {
@@ -179,9 +180,9 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte
version = int64(r.uint64())
switch version {
- case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
+ case iexportVersionGenericMethods, iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
default:
- if version > iexportVersionGo1_18 {
+ if version > iexportVersionGenericMethods {
errorf("unstable iexport format version %d, just rebuild compiler and std library", version)
} else {
errorf("unknown iexport format version %d", version)
@@ -614,6 +615,10 @@ func (r *importReader) obj(pkg *types.Package, name string) {
for n := r.uint64(); n > 0; n-- {
mpos := r.pos()
mname := r.ident()
+ var tpars []*types.TypeParam
+ if r.p.version >= iexportVersionGenericMethods && r.bool() {
+ tpars = r.tparamList()
+ }
recv := r.param(pkg)
// If the receiver has any targs, set those as the
@@ -628,8 +633,7 @@ func (r *importReader) obj(pkg *types.Package, name string) {
rparams[i] = types.Unalias(targs.At(i)).(*types.TypeParam)
}
}
- msig := r.signature(pkg, recv, rparams, nil)
-
+ msig := r.signature(pkg, recv, rparams, tpars)
named.AddMethod(types.NewFunc(mpos, pkg, mname, msig))
}
}
diff --git a/vendor/golang.org/x/tools/internal/imports/fix.go b/vendor/golang.org/x/tools/internal/imports/fix.go
index d29ecfb01..b99ea6c91 100644
--- a/vendor/golang.org/x/tools/internal/imports/fix.go
+++ b/vendor/golang.org/x/tools/internal/imports/fix.go
@@ -273,7 +273,6 @@ func (p *pass) loadPackageNames(ctx context.Context, imports []*ImportInfo) erro
}
unknown = append(unknown, imp.ImportPath)
}
-
names, err := p.source.LoadPackageNames(ctx, p.srcDir, unknown)
if err != nil {
return err
diff --git a/vendor/golang.org/x/tools/internal/imports/imports.go b/vendor/golang.org/x/tools/internal/imports/imports.go
index b5f5218b5..072293835 100644
--- a/vendor/golang.org/x/tools/internal/imports/imports.go
+++ b/vendor/golang.org/x/tools/internal/imports/imports.go
@@ -74,6 +74,10 @@ func Process(filename string, src []byte, opt *Options) (formatted []byte, err e
// Note that filename's directory influences which imports can be chosen,
// so it is important that filename be accurate.
func FixImports(ctx context.Context, filename string, src []byte, goroot string, logf func(string, ...any), source Source) (fixes []*ImportFix, err error) {
+ if source == nil {
+ // In case someone adds a defective call from a new place
+ panic("source is nil")
+ }
ctx, done := event.Start(ctx, "imports.FixImports")
defer done()
diff --git a/vendor/golang.org/x/tools/internal/stdlib/deps.go b/vendor/golang.org/x/tools/internal/stdlib/deps.go
index dacfc1dff..0f73c719c 100644
--- a/vendor/golang.org/x/tools/internal/stdlib/deps.go
+++ b/vendor/golang.org/x/tools/internal/stdlib/deps.go
@@ -12,366 +12,386 @@ type pkginfo struct {
}
var deps = [...]pkginfo{
- {"archive/tar", "\x03q\x03F=\x01\n\x01$\x01\x01\x02\x05\b\x02\x01\x02\x02\r"},
- {"archive/zip", "\x02\x04g\a\x03\x13\x021=\x01+\x05\x01\x0f\x03\x02\x0f\x04"},
- {"bufio", "\x03q\x86\x01D\x15"},
- {"bytes", "t+[\x03\fH\x02\x02"},
+ {"archive/tar", "\x03{\x03F>\x01\n\x01&\x01\x01\x02\x05\b\x02\x01\x02\x02\r"},
+ {"archive/zip", "\x02\x04j\x0e\x03\x12\x022>\x01-\x05\x01\x0f\x03\x02\x0f\x04"},
+ {"bufio", "\x03{\x87\x01F\x15"},
+ {"bytes", "~*]\x03\fJ\x02\x02"},
{"cmp", ""},
- {"compress/bzip2", "\x02\x02\xf6\x01A"},
- {"compress/flate", "\x02r\x03\x83\x01\f\x033\x01\x03"},
- {"compress/gzip", "\x02\x04g\a\x03\x15nU"},
- {"compress/lzw", "\x02r\x03\x83\x01"},
- {"compress/zlib", "\x02\x04g\a\x03\x13\x01o"},
- {"container/heap", "\xbc\x02"},
+ {"compress/bzip2", "\x02\x02\x81\x02C"},
+ {"compress/flate", "\x02|\x03\x84\x01\f\x034\x02\x03"},
+ {"compress/gzip", "\x02\x04j\x0e\x03\x14pW"},
+ {"compress/lzw", "\x02|\x03\x84\x01"},
+ {"compress/zlib", "\x02\x04j\x0e\x03\x12\x01q"},
+ {"container/heap", "\xc9\x02"},
{"container/list", ""},
{"container/ring", ""},
- {"context", "t\\p\x01\x0e"},
- {"crypto", "\x8a\x01pC"},
- {"crypto/aes", "\x10\v\t\x99\x02"},
- {"crypto/cipher", "\x03!\x01\x01 \x12\x1c,Z"},
- {"crypto/des", "\x10\x16 .,\x9d\x01\x03"},
- {"crypto/dsa", "F\x03+\x86\x01\r"},
- {"crypto/ecdh", "\x03\v\r\x10\x04\x17\x03\x0f\x1c\x86\x01"},
- {"crypto/ecdsa", "\x0e\x05\x03\x05\x01\x10\b\v\x06\x01\x03\x0e\x01\x1c\x86\x01\r\x05L\x01"},
- {"crypto/ed25519", "\x0e\x1f\x12\a\x03\b\a\x1cI=C"},
- {"crypto/elliptic", "4@\x86\x01\r9"},
- {"crypto/fips140", "#\x05\x95\x01\x98\x01"},
- {"crypto/hkdf", "0\x15\x01.\x16"},
- {"crypto/hmac", "\x1b\x16\x14\x01\x122"},
- {"crypto/hpke", "\x03\v\x02\x03\x04\x01\f\x01\x05\x1f\x05\a\x01\x01\x1d\x03\x13\x16\x9b\x01\x1c"},
- {"crypto/internal/boring", "\x0e\x02\x0el"},
- {"crypto/internal/boring/bbig", "\x1b\xec\x01N"},
- {"crypto/internal/boring/bcache", "\xc1\x02\x14"},
+ {"context", "~]r\x01\x0e"},
+ {"crypto", "\x93\x01rE"},
+ {"crypto/aes", "\x10\v\n\xa5\x02"},
+ {"crypto/cipher", "\x03\"\x01\x01 \x13$+\\"},
+ {"crypto/des", "\x10\x17 7+\xa1\x01\x03"},
+ {"crypto/dsa", "G\x034\x87\x01\r"},
+ {"crypto/ecdh", "\x03\v\r\x11\x04\x17\x03\x10$\x87\x01"},
+ {"crypto/ecdsa", "\x0e\x05\x03\x05\x01\x11\b\v\x06\x01\x03\x0f\x01$\x87\x01\r\x05O\x01"},
+ {"crypto/ed25519", "\x0e \x12\a\x03\t\a$I>E"},
+ {"crypto/elliptic", "5I\x87\x01\r;"},
+ {"crypto/fips140", "$\x05\x9e\x01\x9b\x01"},
+ {"crypto/hkdf", "1\x15\x017\x15"},
+ {"crypto/hmac", "\x1b\x17\x14\x01\x139"},
+ {"crypto/hpke", "\x03\v\x02\x03\x04\x01\r\x01\x05\x1f\x06\a\x01\x01%\x03\x12\x16\x9f\x01\x1d"},
+ {"crypto/internal/boring", "\x0e\x02\x0eu"},
+ {"crypto/internal/boring/bbig", "\x1b\xf7\x01P"},
+ {"crypto/internal/boring/bcache", "\xce\x02\x14"},
{"crypto/internal/boring/sig", ""},
{"crypto/internal/constanttime", ""},
- {"crypto/internal/cryptotest", "\x03\r\v\b%\x10\x19\x06\x13\x12 \x04\x06\t\x19\x01\x11\x11\x1b\x01\a\x05\b\x03\x05\f"},
- {"crypto/internal/entropy", "K"},
- {"crypto/internal/entropy/v1.0.0", "D0\x95\x018\x14"},
- {"crypto/internal/fips140", "C1\xbf\x01\v\x17"},
- {"crypto/internal/fips140/aes", "\x03 \x03\x02\x14\x05\x01\x01\x05,\x95\x014"},
- {"crypto/internal/fips140/aes/gcm", "#\x01\x02\x02\x02\x12\x05\x01\x06,\x92\x01"},
- {"crypto/internal/fips140/alias", "\xd5\x02"},
- {"crypto/internal/fips140/bigmod", "(\x19\x01\x06,\x95\x01"},
- {"crypto/internal/fips140/check", "#\x0e\a\t\x02\xb7\x01["},
- {"crypto/internal/fips140/check/checktest", "(\x8b\x02\""},
- {"crypto/internal/fips140/drbg", "\x03\x1f\x01\x01\x04\x14\x05\n)\x86\x01\x0f7\x01"},
- {"crypto/internal/fips140/ecdh", "\x03 \x05\x02\n\r3\x86\x01\x0f7"},
- {"crypto/internal/fips140/ecdsa", "\x03 \x04\x01\x02\a\x03\x06:\x16pF"},
- {"crypto/internal/fips140/ed25519", "\x03 \x05\x02\x04\f:\xc9\x01\x03"},
- {"crypto/internal/fips140/edwards25519", "\x1f\t\a\x123\x95\x017"},
- {"crypto/internal/fips140/edwards25519/field", "(\x14\x053\x95\x01"},
- {"crypto/internal/fips140/hkdf", "\x03 \x05\t\a<\x16"},
- {"crypto/internal/fips140/hmac", "\x03 \x15\x01\x01:\x16"},
- {"crypto/internal/fips140/mldsa", "\x03\x1c\x04\x05\x02\x0e\x01\x03\x053\x95\x017"},
- {"crypto/internal/fips140/mlkem", "\x03 \x05\x02\x0f\x03\x053\xcc\x01"},
- {"crypto/internal/fips140/nistec", "\x1f\t\r\f3\x95\x01*\r\x15"},
- {"crypto/internal/fips140/nistec/fiat", "(\x148\x95\x01"},
- {"crypto/internal/fips140/pbkdf2", "\x03 \x05\t\a<\x16"},
- {"crypto/internal/fips140/rsa", "\x03\x1c\x04\x04\x01\x02\x0e\x01\x01\x028\x16pF"},
- {"crypto/internal/fips140/sha256", "\x03 \x1e\x01\x06,\x16\x7f"},
- {"crypto/internal/fips140/sha3", "\x03 \x19\x05\x012\x95\x01L"},
- {"crypto/internal/fips140/sha512", "\x03 \x1e\x01\x06,\x16\x7f"},
- {"crypto/internal/fips140/ssh", "(b"},
- {"crypto/internal/fips140/subtle", "\x1f\a\x1b\xc8\x01"},
- {"crypto/internal/fips140/tls12", "\x03 \x05\t\a\x02:\x16"},
- {"crypto/internal/fips140/tls13", "\x03 \x05\b\b\t3\x16"},
- {"crypto/internal/fips140cache", "\xb3\x02\r'"},
+ {"crypto/internal/cryptotest", "\x03\r\v\t%\x11\x1a\r\x12\x12!\x04\x06\n\x19\x01\x11\x11\x1d\x01\a\x03\x02\b\x02\x01\x05\f"},
+ {"crypto/internal/cryptotest/wycheproof", "\x0e\x12S\x01\f\x01r@\x05\x03\x15\x10"},
+ {"crypto/internal/entropy", "L"},
+ {"crypto/internal/entropy/v1.0.0", "E9\x96\x01:\x14"},
+ {"crypto/internal/fips140", "D:\xc2\x01\v\x17"},
+ {"crypto/internal/fips140/aes", "\x03!\x03\x02\x14\x05\x01\x01\x055\x96\x016"},
+ {"crypto/internal/fips140/aes/gcm", "$\x01\x02\x02\x02\x12\x05\x01\x065\x93\x01"},
+ {"crypto/internal/fips140/alias", "\xe2\x02"},
+ {"crypto/internal/fips140/bigmod", ")\x19\x01\x065\x96\x01"},
+ {"crypto/internal/fips140/check", "$\x0e\a\t\x02\xc1\x01]"},
+ {"crypto/internal/fips140/check/checktest", ")\x97\x02\""},
+ {"crypto/internal/fips140/drbg", "\x03 \x01\x01\x04\x14\x05\n2\x87\x01\x0f9\x01"},
+ {"crypto/internal/fips140/ecdh", "\x03!\x05\x02\n\r<\x87\x01\x0f9"},
+ {"crypto/internal/fips140/ecdsa", "\x03!\x04\x01\x02\a\x03\x06C\x15r\x0f9"},
+ {"crypto/internal/fips140/ed25519", "\x03!\x05\x02\x04\fC\xcc\x01\x03"},
+ {"crypto/internal/fips140/edwards25519", "\x1f\n\a\x12<\x96\x019"},
+ {"crypto/internal/fips140/edwards25519/field", ")\x14\x05<\x96\x01"},
+ {"crypto/internal/fips140/hkdf", "\x03!\x05\t\aE\x15"},
+ {"crypto/internal/fips140/hmac", "\x03!\x15\x01\x01C\x15"},
+ {"crypto/internal/fips140/mldsa", "\x03\x1c\x05\x05\x02\x0e\x01\x03\x05<\x96\x019"},
+ {"crypto/internal/fips140/mlkem", "\x03!\x05\x02\x0f\x03\x05<\xcf\x01"},
+ {"crypto/internal/fips140/nistec", "\x1f\n\r\f<\x96\x01,\r\x15"},
+ {"crypto/internal/fips140/nistec/fiat", ")\x14A\x96\x01"},
+ {"crypto/internal/fips140/pbkdf2", "\x03!\x05\t\aE\x15"},
+ {"crypto/internal/fips140/rsa", "\x03\x1c\x05\x04\x01\x02\x0e\x01\x01\x02A\x15rH"},
+ {"crypto/internal/fips140/sha256", "\x03!\x1e\x01\x065\x15\x81\x01"},
+ {"crypto/internal/fips140/sha3", "\x03!\x19\x05\x01;\x96\x01N"},
+ {"crypto/internal/fips140/sha512", "\x03!\x1e\x01\x065\x15\x81\x01"},
+ {"crypto/internal/fips140/ssh", ")j"},
+ {"crypto/internal/fips140/subtle", "\x1f\b\x1b\xd2\x01"},
+ {"crypto/internal/fips140/tls12", "\x03!\x05\t\a\x02C\x15"},
+ {"crypto/internal/fips140/tls13", "\x03!\x05\b\b\t<\x15"},
+ {"crypto/internal/fips140cache", "\xc0\x02\r."},
{"crypto/internal/fips140deps", ""},
- {"crypto/internal/fips140deps/byteorder", "\xa0\x01"},
- {"crypto/internal/fips140deps/cpu", "\xb5\x01\a"},
- {"crypto/internal/fips140deps/godebug", "\xbd\x01"},
- {"crypto/internal/fips140deps/time", "\xcf\x02"},
- {"crypto/internal/fips140hash", "9\x1d4\xcb\x01"},
- {"crypto/internal/fips140only", "\x17\x13\x0e\x01\x01Pp"},
+ {"crypto/internal/fips140deps/byteorder", "\xa9\x01"},
+ {"crypto/internal/fips140deps/cpu", "\xbe\x01\b"},
+ {"crypto/internal/fips140deps/godebug", "\xc7\x01"},
+ {"crypto/internal/fips140deps/time", "\xe2\x02"},
+ {"crypto/internal/fips140hash", ":\x1e;\xcf\x01"},
+ {"crypto/internal/fips140only", "\x17\x14\x0e\x01\x01Xr"},
{"crypto/internal/fips140test", ""},
- {"crypto/internal/impl", "\xbe\x02"},
- {"crypto/internal/rand", "\x1b\x0f s=["},
- {"crypto/internal/randutil", "\xfa\x01\x12"},
- {"crypto/internal/sysrand", "tq! \r\r\x01\x01\r\x06"},
- {"crypto/internal/sysrand/internal/seccomp", "t"},
- {"crypto/md5", "\x0e8.\x16\x16i"},
- {"crypto/mlkem", "\x0e%"},
- {"crypto/mlkem/mlkemtest", "3\x13\b&"},
- {"crypto/pbkdf2", "6\x0f\x01.\x16"},
- {"crypto/rand", "\x1b\x0f\x1c\x03+\x86\x01\rN"},
- {"crypto/rc4", "& .\xc9\x01"},
- {"crypto/rsa", "\x0e\r\x01\v\x10\x0e\x01\x03\b\a\x1c\x03\x133=\f\x01"},
- {"crypto/sha1", "\x0e\r+\x02,\x16\x16\x15T"},
- {"crypto/sha256", "\x0e\r\x1dR"},
- {"crypto/sha3", "\x0e+Q\xcb\x01"},
- {"crypto/sha512", "\x0e\r\x1fP"},
- {"crypto/subtle", "\x1f\x1d\x9f\x01z"},
- {"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x02\x01\x01\x01\t\x01\x18\x01\x0f\x01\x03\x01\x01\x01\x01\x02\x01\x02\x01\x17\x02\x03\x13\x16\x15\b=\x16\x16\r\b\x01\x01\x01\x02\x01\x0e\x06\x02\x01\x0f"},
- {"crypto/tls/internal/fips140tls", "\x17\xaa\x02"},
- {"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x017\x06\x01\x01\x02\x05\x0e\x06\x02\x02\x03F\x03:\x01\x02\b\x01\x01\x02\a\x10\x05\x01\x06\a\b\x02\x01\x02\x0f\x02\x01\x01\x02\x03\x01"},
- {"crypto/x509/pkix", "j\x06\a\x90\x01H"},
- {"database/sql", "\x03\nQ\x16\x03\x83\x01\v\a\"\x05\b\x02\x03\x01\x0e\x02\x02\x02"},
- {"database/sql/driver", "\rg\x03\xb7\x01\x0f\x12"},
- {"debug/buildinfo", "\x03^\x02\x01\x01\b\a\x03g\x1a\x02\x01+\x0f "},
- {"debug/dwarf", "\x03j\a\x03\x83\x011\x11\x01\x01"},
- {"debug/elf", "\x03\x06W\r\a\x03g\x1b\x01\f \x17\x01\x17"},
- {"debug/gosym", "\x03j\n$\xa1\x01\x01\x01\x02"},
- {"debug/macho", "\x03\x06W\r\ng\x1c,\x17\x01"},
- {"debug/pe", "\x03\x06W\r\a\x03g\x1c,\x17\x01\x17"},
- {"debug/plan9obj", "m\a\x03g\x1c,"},
- {"embed", "t+B\x19\x01T"},
+ {"crypto/internal/impl", "\xcb\x02"},
+ {"crypto/internal/rand", "\x1b\x10 |>]"},
+ {"crypto/internal/randutil", "\x85\x02\x12"},
+ {"crypto/internal/sysrand", "~r!\"\r\r\x01\x01\r\x06"},
+ {"crypto/internal/sysrand/internal/seccomp", "~"},
+ {"crypto/md5", "\x0e97\x15\x16k"},
+ {"crypto/mldsa", "\x0e%K\x87\x01"},
+ {"crypto/mlkem", "\x0e&"},
+ {"crypto/mlkem/mlkemtest", "4\x13\t."},
+ {"crypto/pbkdf2", "7\x0f\x017\x15"},
+ {"crypto/rand", "\x1b\x10\x1c\x034\x87\x01\rP"},
+ {"crypto/rc4", "' 7\xcc\x01"},
+ {"crypto/rsa", "\x0e\r\x01\f\x10\x0e\x01\x03\t\a$\x03\x124>\f\x01"},
+ {"crypto/sha1", "\x0e\r,\x025\x15\x16\x15V"},
+ {"crypto/sha256", "\x0e\r\x1eZ"},
+ {"crypto/sha3", "\x0e,Y\xcf\x01"},
+ {"crypto/sha512", "\x0e\r X"},
+ {"crypto/subtle", "\x1f\x1e\xa9\x01|"},
+ {"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\n\x01\x18\x01\x0f\x01\x01\x03\x01\x01\x01\x01\x02\x01\x02\x01\x1f\x02\x03\x12\x16\x15\t>\x16\x18\r\b\x01\x01\x01\x02\x01\x0e\x06\x03\x01\x15"},
+ {"crypto/tls/internal/fips140tls", "\x17\xb7\x02"},
+ {"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x01\x017\x01\x06\x01\x01\x02\x05\x0f\x06\t\x02\x03F\x03;\x01\x02\b\x01\x01\x02\a\x12\x05\x01\x06\a\b\x02\x01\x02\x0f\x02\x01\x01\x02\x04\x01"},
+ {"crypto/x509/pkix", "m\x06\x0e\x91\x019\x11"},
+ {"database/sql", "\x03\nS\x01\x1d\x03\x84\x01\v\a$\x05\b\x02\x03\x01\x0e\x02\x02\x02\x01"},
+ {"database/sql/driver", "\rT\x1d\x03\xba\x01\x0f\x12\a"},
+ {"database/sql/internal", ""},
+ {"debug/buildinfo", "\x03a\x02\x01\x01\b\x0e\x03h\x1a\x02\x01-\x0f "},
+ {"debug/dwarf", "\x03m\x0e\x03\x84\x013\x11\x01\x01"},
+ {"debug/elf", "\x03\x06Z\r\x0e\x03h\x1b\x01\f\"\x17\x01\x17"},
+ {"debug/gosym", "\x03m\x11#\xa5\x01\x01\x01\x02"},
+ {"debug/macho", "\x03\x06Z\r\x11h\x1c.\x17\x01"},
+ {"debug/pe", "\x03\x06Z\r\x0e\x03h\x1c.\x17\x01\x17"},
+ {"debug/plan9obj", "p\x0e\x03h\x1c."},
+ {"embed", "~*D\x19\x01V"},
{"embed/internal/embedtest", ""},
{"encoding", ""},
- {"encoding/ascii85", "\xfa\x01C"},
- {"encoding/asn1", "\x03q\x03g(\x01'\r\x02\x01\x11\x03\x01"},
- {"encoding/base32", "\xfa\x01A\x02"},
- {"encoding/base64", "\xa0\x01ZA\x02"},
- {"encoding/binary", "t\x86\x01\f(\r\x05"},
- {"encoding/csv", "\x02\x01q\x03\x83\x01D\x13\x02"},
- {"encoding/gob", "\x02f\x05\a\x03g\x1c\v\x01\x03\x1d\b\x12\x01\x10\x02"},
- {"encoding/hex", "t\x03\x83\x01A\x03"},
- {"encoding/json", "\x03\x01d\x04\b\x03\x83\x01\f(\r\x02\x01\x02\x11\x01\x01\x02"},
- {"encoding/pem", "\x03i\b\x86\x01A\x03"},
- {"encoding/xml", "\x02\x01e\f\x03\x83\x014\x05\n\x01\x02\x11\x02"},
- {"errors", "\xd0\x01\x85\x01"},
- {"expvar", "qLA\b\v\x15\r\b\x02\x03\x01\x12"},
- {"flag", "h\f\x03\x83\x01,\b\x05\b\x02\x01\x11"},
- {"fmt", "tF'\x19\f \b\r\x02\x03\x13"},
- {"go/ast", "\x03\x01s\x0f\x01s\x03)\b\r\x02\x01\x13\x02"},
- {"go/build", "\x02\x01q\x03\x01\x02\x02\b\x02\x01\x17\x1f\x04\x02\b\x1c\x13\x01+\x01\x04\x01\a\b\x02\x01\x13\x02\x02"},
- {"go/build/constraint", "t\xc9\x01\x01\x13\x02"},
- {"go/constant", "w\x10\x7f\x01\x024\x01\x02\x13"},
- {"go/doc", "\x04s\x01\x05\n=61\x10\x02\x01\x13\x02"},
- {"go/doc/comment", "\x03t\xc4\x01\x01\x01\x01\x13\x02"},
- {"go/format", "\x03t\x01\f\x01\x02sD"},
- {"go/importer", "y\a\x01\x02\x04\x01r9"},
- {"go/internal/gccgoimporter", "\x02\x01^\x13\x03\x04\f\x01p\x02,\x01\x05\x11\x01\r\b"},
- {"go/internal/gcimporter", "\x02u\x10\x010\x05\r0,\x15\x03\x02"},
- {"go/internal/scannerhooks", "\x87\x01"},
- {"go/internal/srcimporter", "w\x01\x01\v\x03\x01r,\x01\x05\x12\x02\x15"},
- {"go/parser", "\x03q\x03\x01\x02\b\x04\x01s\x01+\x06\x12"},
- {"go/printer", "w\x01\x02\x03\ns\f \x15\x02\x01\x02\f\x05\x02"},
- {"go/scanner", "\x03t\v\x05s2\x10\x01\x14\x02"},
- {"go/token", "\x04s\x86\x01>\x02\x03\x01\x10\x02"},
- {"go/types", "\x03\x01\x06j\x03\x01\x03\t\x03\x024\x063\x04\x03\t \x06\a\b\x01\x01\x01\x02\x01\x10\x02\x02"},
- {"go/version", "\xc2\x01|"},
- {"hash", "\xfa\x01"},
- {"hash/adler32", "t\x16\x16"},
- {"hash/crc32", "t\x16\x16\x15\x8b\x01\x01\x14"},
- {"hash/crc64", "t\x16\x16\xa0\x01"},
- {"hash/fnv", "t\x16\x16i"},
- {"hash/maphash", "\x8a\x01\x11<~"},
- {"html", "\xbe\x02\x02\x13"},
- {"html/template", "\x03n\x06\x19-=\x01\n!\x05\x01\x02\x03\f\x01\x02\r\x01\x03\x02"},
- {"image", "\x02r\x1fg\x0f4\x03\x01"},
+ {"encoding/ascii85", "\x85\x02E"},
+ {"encoding/asn1", "\x03{\x03h(\x01)\r\x02\x01\x11\x03\x01"},
+ {"encoding/base32", "\x85\x02C\x02"},
+ {"encoding/base64", "\xa9\x01\\C\x02"},
+ {"encoding/binary", "~\x87\x01\f*\r\x05"},
+ {"encoding/csv", "\x02\x01{\x03\x84\x01F\x13\x02"},
+ {"encoding/gob", "\x02i\x05\x0e\x03h\x1c\v\x01\x03\x1f\b\x12\x01\x10\x02"},
+ {"encoding/hex", "~\x03\x84\x01C\x03"},
+ {"encoding/json", "\x03\x01g\n\x01\x01\x02\x01\x01\x03\x03\x84\x016\x0f\x01"},
+ {"encoding/json/internal", "~"},
+ {"encoding/json/internal/jsonflags", "u"},
+ {"encoding/json/internal/jsonopts", "u\x01"},
+ {"encoding/json/internal/jsontest", "\x03f\x15\x03\x83\x01\x01\x012\b\b\x03\x02\x0f"},
+ {"encoding/json/internal/jsonwire", "\x04r\b\x87\x01\f7\x02\x01\x13\x01\x01"},
+ {"encoding/json/jsontext", "\x03r\x01\x01\x02\x05\x87\x01\x03\t\x034\x02\x01\x02\x13"},
+ {"encoding/json/v2", "\x03\x01g\x03\x01\x01\x03\x02\x01\x01\x02\x01\x04\x03\x84\x01\f\x03'\r\x02\x01\x02\x0f\x02\x02"},
+ {"encoding/pem", "\x03l\x0f\x87\x01C\x03"},
+ {"encoding/xml", "\x02\x01h\x13\x03\x84\x016\x05\n\x01\x02\x11\x02"},
+ {"errors", "\xdb\x01\x87\x01"},
+ {"expvar", "tSB\b\v\x17\r\b\x02\x03\x01\x12"},
+ {"flag", "k\x13\x03\x84\x01.\b\x05\b\x02\x01\x11"},
+ {"fmt", "~E)\x19\f\"\b\r\x02\x03\x13"},
+ {"go/ast", "\x03\x01}\x0e\x01u\x03+\b\r\x02\x01\x13\x02"},
+ {"go/build", "\x02\x01{\x03\x01\x02\x02\a\x02\x01\x17 \x04\x02\t\x1c\x13\x01-\x01\x04\x01\a\b\x02\x01\x13\x02\x02"},
+ {"go/build/constraint", "~\xcc\x01\x01\x13\x02"},
+ {"go/constant", "\x81\x01\x0f\x81\x01\x01\x026\x01\x02\x13"},
+ {"go/doc", "\x04}\x01\x05\t>73\x10\x02\x01\x13\x02"},
+ {"go/doc/comment", "\x03~\xc7\x01\x01\x01\x01\x13\x02"},
+ {"go/format", "\x03~\x01\v\x01\x02uF"},
+ {"go/importer", "\x83\x01\a\x01\x01\x04\x01t;"},
+ {"go/internal/gccgoimporter", "\x02\x01a\x1a\x03\x04\v\x01r\x02.\x01\x05\x11\x01\r\b"},
+ {"go/internal/gcimporter", "\x02\x7f\x0f\x010\x140.\x15\x03\x02"},
+ {"go/internal/srcimporter", "\x81\x01\x01\x01\n\x03\x01t.\x01\x05\x12\x02\x15"},
+ {"go/parser", "\x03{\x03\x01\x02\v\x01u\x01-\x06\x12"},
+ {"go/printer", "\x81\x01\x01\x02\x03\tu\f\"\x15\x02\x01\x02\f\x05\x02"},
+ {"go/scanner", "\x03~\x0fu4\x10\x01\x14\x02"},
+ {"go/token", "\x04}\x87\x01@\x02\x03\x01\x10\x02"},
+ {"go/types", "\x03\x01\x06t\x03\x01\x03\b\x03\x02\x0654\x04\x03\t\"\x06\a\b\x01\x01\x01\x02\x01\x10\x02\x02"},
+ {"go/version", "\xcc\x01\x7f"},
+ {"hash", "\x85\x02"},
+ {"hash/adler32", "~\x15\x16"},
+ {"hash/crc32", "~\x15\x16\x15\x8f\x01\x01\x14"},
+ {"hash/crc64", "~\x15\x16\xa4\x01"},
+ {"hash/fnv", "~\x15\x16k"},
+ {"hash/maphash", "\x93\x01\x11>\x80\x01"},
+ {"html", "\xcb\x02\x02\x13"},
+ {"html/template", "\x03q\r\x18.>\x01\n#\x05\x01\x02\x03\n\x02\x01\x02\r\x01\x03\x02"},
+ {"image", "\x02|\x1ei\x0f6\x03\x01"},
{"image/color", ""},
- {"image/color/palette", "\x93\x01"},
- {"image/draw", "\x92\x01\x01\x04"},
- {"image/gif", "\x02\x01\x05l\x03\x1b\x01\x01\x01\vZ\x0f"},
- {"image/internal/imageutil", "\x92\x01"},
- {"image/jpeg", "\x02r\x1e\x01\x04c"},
- {"image/png", "\x02\ad\n\x13\x02\x06\x01gC"},
- {"index/suffixarray", "\x03j\a\x86\x01\f+\n\x01"},
- {"internal/abi", "\xbc\x01\x99\x01"},
- {"internal/asan", "\xd5\x02"},
- {"internal/bisect", "\xb3\x02\r\x01"},
- {"internal/buildcfg", "wHg\x06\x02\x05\n\x01"},
- {"internal/bytealg", "\xb5\x01\xa0\x01"},
+ {"image/color/palette", "\x9c\x01"},
+ {"image/draw", "\x9b\x01\x01\x04"},
+ {"image/gif", "\x02\x01\x05v\x03\x1a\x01\x01\x01\v\\\x0f"},
+ {"image/internal/imageutil", "\x9b\x01"},
+ {"image/jpeg", "\x02|\x1d\x01\x04e"},
+ {"image/png", "\x02\ag\x11\x12\x02\x06\x01iE"},
+ {"index/suffixarray", "\x03m\x0e\x87\x01\f-\n\x01"},
+ {"internal/abi", "\xc6\x01\x9c\x01"},
+ {"internal/asan", "\xe2\x02"},
+ {"internal/bisect", "\xc0\x02\r\x01"},
+ {"internal/buildcfg", "\x81\x01Hj\x06\x02\x05\n\x01"},
+ {"internal/bytealg", "\xbe\x01\xa4\x01"},
{"internal/byteorder", ""},
{"internal/cfg", ""},
- {"internal/cgrouptest", "w[T\x06\x0f\x02\x01\x04\x01"},
- {"internal/chacha8rand", "\xa0\x01\x15\a\x99\x01"},
+ {"internal/cgrouptest", "\x81\x01\\V\x06\x0f\x02\x01\x04\x01"},
+ {"internal/chacha8rand", "\xa9\x01\x15\b\x9c\x01"},
{"internal/copyright", ""},
{"internal/coverage", ""},
{"internal/coverage/calloc", ""},
- {"internal/coverage/cfile", "q\x06\x17\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01\"\x02',\x06\a\n\x01\x03\x0e\x06"},
- {"internal/coverage/cformat", "\x04s.\x04Q\v6\x01\x02\x0e"},
- {"internal/coverage/cmerge", "w.a"},
- {"internal/coverage/decodecounter", "m\n.\v\x02H,\x17\x18"},
- {"internal/coverage/decodemeta", "\x02k\n\x17\x17\v\x02H,"},
- {"internal/coverage/encodecounter", "\x02k\n.\f\x01\x02F\v!\x15"},
- {"internal/coverage/encodemeta", "\x02\x01j\n\x13\x04\x17\r\x02F,/"},
- {"internal/coverage/pods", "\x04s.\x81\x01\x06\x05\n\x02\x01"},
- {"internal/coverage/rtcov", "\xd5\x02"},
- {"internal/coverage/slicereader", "m\n\x83\x01["},
- {"internal/coverage/slicewriter", "w\x83\x01"},
- {"internal/coverage/stringtab", "w9\x04F"},
+ {"internal/coverage/cfile", "t\r\x16\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01$\x02'.\x06\a\n\x01\x03\x0e\x06"},
+ {"internal/coverage/cformat", "\x04}-\x04S\v8\x01\x02\x0e"},
+ {"internal/coverage/cmerge", "\x81\x01-c"},
+ {"internal/coverage/decodecounter", "p\x11-\v\x02J.\x17\x18"},
+ {"internal/coverage/decodemeta", "\x02n\x11\x16\x17\v\x02J."},
+ {"internal/coverage/encodecounter", "\x02n\x11-\f\x01\x02H\v#\x15"},
+ {"internal/coverage/encodemeta", "\x02\x01m\x11\x12\x04\x17\r\x02H./"},
+ {"internal/coverage/pods", "\x04}-\x85\x01\x06\x05\n\x02\x01"},
+ {"internal/coverage/rtcov", "\xe2\x02"},
+ {"internal/coverage/slicereader", "p\x11\x84\x01]"},
+ {"internal/coverage/slicewriter", "\x81\x01\x84\x01"},
+ {"internal/coverage/stringtab", "\x81\x018\x04H"},
{"internal/coverage/test", ""},
{"internal/coverage/uleb128", ""},
- {"internal/cpu", "\xd5\x02"},
- {"internal/dag", "\x04s\xc4\x01\x03"},
- {"internal/diff", "\x03t\xc5\x01\x02"},
- {"internal/exportdata", "\x02\x01q\x03\x02e\x1c,\x01\x05\x11\x01\x02"},
- {"internal/filepathlite", "t+B\x1a@"},
- {"internal/fmtsort", "\x04\xaa\x02\r"},
- {"internal/fuzz", "\x03\nH\x18\x04\x03\x03\x01\f\x036=\f\x03\x1d\x01\x05\x02\x05\n\x01\x02\x01\x01\r\x04\x02"},
+ {"internal/cpu", "\xe2\x02"},
+ {"internal/dag", "\x04}\xc7\x01\x03"},
+ {"internal/diff", "\x03~\xc8\x01\x02"},
+ {"internal/exportdata", "\x02\x01{\x03\x02f\x1c.\x01\x05\x11\x01\x02"},
+ {"internal/filepathlite", "~*D\x1aB"},
+ {"internal/fmtsort", "\x04\xb7\x02\r"},
+ {"internal/fuzz", "\x03\nJ\x19\x04\n\x03\x01\v\x037>\f\x03\x1f\x01\x05\x02\x05\n\x01\x02\x01\x01\r\x04\x02"},
+ {"internal/gate", "\r"},
{"internal/goarch", ""},
- {"internal/godebug", "\x9d\x01!\x82\x01\x01\x14"},
+ {"internal/godebug", "\xa6\x01\"\x85\x01\x01\x14"},
{"internal/godebugs", ""},
{"internal/goexperiment", ""},
{"internal/goos", ""},
- {"internal/goroot", "\xa6\x02\x01\x05\x12\x02"},
+ {"internal/goroot", "\xb3\x02\x01\x05\x12\x02"},
{"internal/gover", "\x04"},
{"internal/goversion", ""},
- {"internal/lazyregexp", "\xa6\x02\v\r\x02"},
- {"internal/lazytemplate", "\xfa\x01,\x18\x02\r"},
- {"internal/msan", "\xd5\x02"},
+ {"internal/lazyregexp", "\xb3\x02\v\r\x02"},
+ {"internal/lazytemplate", "\x85\x02.\x18\x02\r"},
+ {"internal/msan", "\xe2\x02"},
+ {"internal/nettest", "\x03\nqG@\f\n\x12\x06\x15\x05\x0f"},
{"internal/nettrace", ""},
- {"internal/obscuretestdata", "l\x8e\x01,"},
- {"internal/oserror", "t"},
- {"internal/pkgbits", "\x03R\x18\a\x03\x04\fs\r\x1f\r\n\x01"},
+ {"internal/obscuretestdata", "o\x96\x01."},
+ {"internal/oserror", "~"},
+ {"internal/pkgbits", "\x03T\x19\x0e\x03\x04\vu\r!\r\n\x01"},
{"internal/platform", ""},
- {"internal/poll", "tl\x05\x159\r\x01\x01\r\x06"},
- {"internal/profile", "\x03\x04m\x03\x83\x017\n\x01\x01\x01\x11"},
+ {"internal/poll", "~m\x05\x15;\r\x01\x01\r\x06"},
+ {"internal/profile", "\x03\x04w\x03\x84\x019\n\x01\x01\x01\x11"},
{"internal/profilerecord", ""},
- {"internal/race", "\x9b\x01\xba\x01"},
- {"internal/reflectlite", "\x9b\x01!;<\""},
- {"internal/runtime/atomic", "\xbc\x01\x99\x01"},
- {"internal/runtime/cgroup", "\x9f\x01=\x04u"},
- {"internal/runtime/exithook", "\xd1\x01\x84\x01"},
- {"internal/runtime/gc", "\xbc\x01"},
- {"internal/runtime/gc/internal/gen", "\nc\n\x18k\x04\v\x1d\b\x10\x02"},
- {"internal/runtime/gc/scan", "\xb5\x01\a\x18\az"},
- {"internal/runtime/maps", "\x9b\x01\x01 \n\t\t\x03z"},
- {"internal/runtime/math", "\xbc\x01"},
+ {"internal/race", "\xa4\x01\xbe\x01"},
+ {"internal/reflectlite", "\xa4\x01\"<>\""},
+ {"internal/runtime/atomic", "\xc6\x01\x9c\x01"},
+ {"internal/runtime/cgroup", "\xa8\x01?\x04w"},
+ {"internal/runtime/exithook", "\xdc\x01\x86\x01"},
+ {"internal/runtime/gc", "\xc6\x01"},
+ {"internal/runtime/gc/internal/gen", "\nf\x11\x17m\x04\v\x1f\b\x10\x02"},
+ {"internal/runtime/gc/scan", "\xbe\x01\b\x19\a|"},
+ {"internal/runtime/maps", "\xa4\x01\x01\x04\x15\b\x03\a\n\t\x03.N"},
+ {"internal/runtime/math", "\xc6\x01"},
{"internal/runtime/pprof/label", ""},
{"internal/runtime/startlinetest", ""},
- {"internal/runtime/sys", "\xbc\x01\x04"},
- {"internal/runtime/syscall/linux", "\xbc\x01\x99\x01"},
+ {"internal/runtime/sys", "\xc6\x01\x04"},
+ {"internal/runtime/syscall/linux", "\xc6\x01\x9c\x01"},
{"internal/runtime/wasitest", ""},
- {"internal/saferio", "\xfa\x01["},
- {"internal/singleflight", "\xc0\x02"},
- {"internal/strconv", "\x89\x02L"},
- {"internal/stringslite", "\x9f\x01\xb6\x01"},
- {"internal/sync", "\x9b\x01!\x13r\x14"},
- {"internal/synctest", "\x9b\x01\xba\x01"},
- {"internal/syscall/execenv", "\xc2\x02"},
- {"internal/syscall/unix", "\xb3\x02\x0e\x01\x13"},
- {"internal/sysinfo", "\x02\x01\xb2\x01E,\x18\x02"},
+ {"internal/saferio", "\x85\x02]"},
+ {"internal/singleflight", "\xcd\x02"},
+ {"internal/strconv", "\x94\x02N"},
+ {"internal/stringslite", "\xa8\x01\xba\x01"},
+ {"internal/sync", "\xa4\x01\"\x14t\x14"},
+ {"internal/synctest", "\xa4\x01\xbe\x01"},
+ {"internal/syscall/execenv", "\xcf\x02"},
+ {"internal/syscall/unix", "\xeb\x01U\x0e\x01\x13"},
+ {"internal/sysinfo", "\x02\x01\xbb\x01G.\x18\x02"},
{"internal/syslist", ""},
- {"internal/testenv", "\x03\ng\x02\x01*\x1b\x0f0+\x01\x05\a\n\x01\x02\x02\x01\f"},
- {"internal/testhash", "\x03\x87\x01p\x118\f"},
- {"internal/testlog", "\xc0\x02\x01\x14"},
- {"internal/testpty", "t\x03\xaf\x01"},
- {"internal/trace", "\x02\x01\x01\x06c\a\x03w\x03\x03\x06\x03\t+\n\x01\x01\x01\x11\x06"},
- {"internal/trace/internal/testgen", "\x03j\nu\x03\x02\x03\x011\v\r\x11"},
- {"internal/trace/internal/tracev1", "\x03\x01i\a\x03}\x06\f5\x01"},
- {"internal/trace/raw", "\x02k\nz\x03\x06C\x01\x13"},
- {"internal/trace/testtrace", "\x02\x01q\x03q\x04\x03\x05\x01\x05,\v\x02\b\x02\x01\x05"},
+ {"internal/testenv", "\x03\nq\x02\x01)\x1c\x100-\x01\x05\a\n\x01\x02\x02\x01\f"},
+ {"internal/testhash", "\x03\x90\x01r\x11:\f"},
+ {"internal/testlog", "\xcd\x02\x01\x14"},
+ {"internal/testpty", "~\x03\xb2\x01"},
+ {"internal/trace", "\x02\x01\x01\x06f\x0e\x03x\x03\x03\x06\x03\t-\n\x01\x01\x01\x11\x06"},
+ {"internal/trace/internal/testgen", "\x03m\x11v\x03\x02\x03\x013\v\r\x11"},
+ {"internal/trace/internal/tracev1", "\x03\x01l\x0e\x03~\x06\f7\x01"},
+ {"internal/trace/raw", "\x02n\x11{\x03\x06E\x01\x13"},
+ {"internal/trace/testtrace", "\x02\x01{\x03r\x04\x03\x05\x01\x05.\v\x02\b\x02\x01\x05"},
{"internal/trace/tracev2", ""},
- {"internal/trace/traceviewer", "\x02d\v\x06\x1a<\x1f\a\a\x04\b\v\x15\x01\x05\a\n\x01\x02\x0f"},
+ {"internal/trace/traceviewer", "\x02g\v\r\x19>\x1f\a\a\x04\b\v\x17\x01\x05\a\n\x01\x02\x0f"},
{"internal/trace/traceviewer/format", ""},
- {"internal/trace/version", "wz\t"},
- {"internal/txtar", "\x03t\xaf\x01\x18"},
- {"internal/types/errors", "\xbd\x02"},
- {"internal/unsafeheader", "\xd5\x02"},
- {"internal/xcoff", "`\r\a\x03g\x1c,\x17\x01"},
- {"internal/zstd", "m\a\x03\x83\x01\x0f"},
- {"io", "t\xcc\x01"},
- {"io/fs", "t+*11\x10\x14\x04"},
- {"io/ioutil", "\xfa\x01\x01+\x15\x03"},
- {"iter", "\xcf\x01d\""},
- {"log", "w\x83\x01\x05'\r\r\x01\x0e"},
+ {"internal/trace/version", "\x81\x01{\t"},
+ {"internal/txtar", "\x03~\xb2\x01\x18"},
+ {"internal/types/errors", "\xca\x02"},
+ {"internal/unsafeheader", "\xe2\x02"},
+ {"internal/xcoff", "c\r\x0e\x03h\x1c.\x17\x01"},
+ {"internal/zstd", "p\x0e\x03\x84\x01\x0f"},
+ {"io", "~\xcf\x01"},
+ {"io/fs", "~*,13\x10\x14\x04"},
+ {"io/ioutil", "\x85\x02\x01-\x15\x03"},
+ {"iter", "\xda\x01f\""},
+ {"log", "\x81\x01\x84\x01\x05)\r\r\x01\x0e"},
{"log/internal", ""},
- {"log/slog", "\x03\n[\t\x03\x03\x83\x01\x04\x01\x02\x02\x03(\x05\b\x02\x01\x02\x01\x0e\x02\x02\x02"},
+ {"log/slog", "\x03\n^\t\n\x03H<\x04\x01\x02\x02\x03*\x05\b\x02\x01\x02\x01\x0e\x02\x02\x02"},
{"log/slog/internal", ""},
- {"log/slog/internal/benchmarks", "\rg\x03\x83\x01\x06\x03:\x12"},
- {"log/slog/internal/buffer", "\xc0\x02"},
- {"log/syslog", "t\x03\x87\x01\x12\x16\x18\x02\x0f"},
- {"maps", "\xfd\x01X"},
- {"math", "\xb5\x01TL"},
- {"math/big", "\x03q\x03)\x15E\f\x03\x020\x02\x01\x02\x15"},
- {"math/big/internal/asmgen", "\x03\x01s\x92\x012\x03"},
- {"math/bits", "\xd5\x02"},
- {"math/cmplx", "\x86\x02\x03"},
- {"math/rand", "\xbd\x01I:\x01\x14"},
- {"math/rand/v2", "t,\x03c\x03L"},
- {"mime", "\x02\x01i\b\x03\x83\x01\v!\x15\x03\x02\x11\x02"},
- {"mime/multipart", "\x02\x01N#\x03F=\v\x01\a\x02\x15\x02\x06\x0f\x02\x01\x17"},
- {"mime/quotedprintable", "\x02\x01t\x83\x01"},
- {"net", "\x04\tg+\x1e\n\x05\x13\x01\x01\x04\x15\x01%\x06\r\b\x05\x01\x01\r\x06\a"},
- {"net/http", "\x02\x01\x03\x01\x04\x02D\b\x13\x01\a\x03F=\x01\x03\a\x01\x03\x02\x02\x01\x02\x06\x02\x01\x01\n\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\x0e\x02\x02\x02\b\x01\x01\x01"},
- {"net/http/cgi", "\x02W\x1b\x03\x83\x01\x04\a\v\x01\x13\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x11\x0e"},
- {"net/http/cookiejar", "\x04p\x03\x99\x01\x01\b\a\x05\x16\x03\x02\x0f\x04"},
- {"net/http/fcgi", "\x02\x01\n`\a\x03\x83\x01\x16\x01\x01\x14\x18\x02\x0f"},
- {"net/http/httptest", "\x02\x01\nL\x02\x1b\x01\x83\x01\x04\x12\x01\n\t\x02\x17\x01\x02\x0f\x0e"},
- {"net/http/httptrace", "\rLnI\x14\n!"},
- {"net/http/httputil", "\x02\x01\ng\x03\x83\x01\x04\x0f\x03\x01\x05\x02\x01\v\x01\x19\x02\x01\x0e\x0e"},
- {"net/http/internal", "\x02\x01q\x03\x83\x01"},
- {"net/http/internal/ascii", "\xbe\x02\x13"},
- {"net/http/internal/httpcommon", "\rg\x03\x9f\x01\x0e\x01\x17\x01\x01\x02\x1d\x02"},
- {"net/http/internal/testcert", "\xbe\x02"},
- {"net/http/pprof", "\x02\x01\nj\x19-\x02\x0e-\x04\x13\x14\x01\r\x04\x03\x01\x02\x01\x11"},
+ {"log/slog/internal/benchmarks", "\rq\x03\x84\x01\x06\x03<\x12"},
+ {"log/slog/internal/buffer", "\xcd\x02"},
+ {"log/syslog", "~\x03\x88\x01\x12\x18\x18\x02\x0f"},
+ {"maps", "\x88\x02Z"},
+ {"math", "\xbe\x01VN"},
+ {"math/big", "\x03{\x03(\x15G\f\x03\x022\x02\x01\x02\x15"},
+ {"math/big/internal/asmgen", "\x03\x01}\x93\x014\x03"},
+ {"math/bits", "\xe2\x02"},
+ {"math/cmplx", "\x91\x02\x03"},
+ {"math/rand", "\xc7\x01J<\x01\x14"},
+ {"math/rand/v2", "~+\x03e\x03N"},
+ {"mime", "\x02\x01l\x0f\x03\x84\x01\v#\x15\x03\x02\x11\x02"},
+ {"mime/multipart", "\x02\x01P+\x03F>\v\x01\a\x02\x17\x02\x06\x0f\x02\x01\x17"},
+ {"mime/quotedprintable", "\x02\x01~\x84\x01"},
+ {"net", "\x04\tq*\x1f\v\x05\x13\x01\x01\x04\x15\x01'\x06\r\b\x05\x01\x01\r\x06\t"},
+ {"net/http", "\x02\x01\x03\x01\x04\x02N\x14\x0f\x03F>\x01\x03\a\x01\x06\x01\x01\x02\x06\x02\x01\x01\f\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\x0e\x02\x02\x02\n\x01\x03"},
+ {"net/http/cgi", "\x02Y#\x03\x84\x01\x04\a\v\x01\x15\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x11\x10"},
+ {"net/http/cookiejar", "\x04z\x03\x9a\x01\x01\b\t\x05\x16\x03\x02\x0f\x04"},
+ {"net/http/fcgi", "\x02\x01\nc\x0e\x03\x84\x01\x16\x01\x01\x16\x18\x02\x0f"},
+ {"net/http/httptest", "\x02\x01\nN\x02#\x01P4\x04\x12\x01\f\t\x02\r\n\x01\x02\x03\f\x06\n"},
+ {"net/http/httptrace", "\rNwI\x16+"},
+ {"net/http/httputil", "\x02\x01\nq\x03F>\x04\x0f\x03\x01\x05\x02\x01\r\x01\x19\x02\x01\x0e\x10"},
+ {"net/http/internal", "\x02\x01m\x0e\x03\x84\x01"},
+ {"net/http/internal/ascii", "\xcb\x02\x13"},
+ {"net/http/internal/http2", "\x02\x01\x03\x01\x06F\b\x15\x0e\x03\x84\x01\x01\x03\b\x03\x02\x03\x02\x06\x02\x03\x01\n\x01\x01\b\x05\b\x02\x01\x02\x01\x0e\x10\x02\x02"},
+ {"net/http/internal/httpcommon", "\rq\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
+ {"net/http/internal/httpsfv", "\xc8\x02\x02\x01\x11\x04"},
+ {"net/http/internal/testcert", "\xcb\x02"},
+ {"net/http/pprof", "\x02\x01\nt\x18.\x11-\x04\x13\x16\x01\r\x04\x03\x01\x02\x01\x11"},
{"net/internal/cgotest", ""},
- {"net/internal/socktest", "w\xc9\x01\x02"},
- {"net/mail", "\x02r\x03\x83\x01\x04\x0f\x03\x14\x1a\x02\x0f\x04"},
- {"net/netip", "\x04p+\x01f\x034\x17"},
- {"net/rpc", "\x02m\x05\x03\x10\ni\x04\x12\x01\x1d\r\x03\x02"},
- {"net/rpc/jsonrpc", "q\x03\x03\x83\x01\x16\x11\x1f"},
- {"net/smtp", "\x194\f\x13\b\x03\x83\x01\x16\x14\x1a"},
- {"net/textproto", "\x02\x01q\x03\x83\x01\f\n-\x01\x02\x15"},
- {"net/url", "t\x03Fc\v\x10\x02\x01\x17"},
- {"os", "t+\x01\x19\x03\x10\x14\x01\x03\x01\x05\x10\x018\b\x05\x01\x01\r\x06"},
- {"os/exec", "\x03\ngI'\x01\x15\x01+\x06\a\n\x01\x03\x01\r"},
- {"os/exec/internal/fdtest", "\xc2\x02"},
- {"os/signal", "\r\x99\x02\x15\x05\x02"},
- {"os/user", "\x02\x01q\x03\x83\x01,\r\n\x01\x02"},
- {"path", "t+\xb4\x01"},
- {"path/filepath", "t+\x1aB+\r\b\x03\x04\x11"},
- {"plugin", "t"},
- {"reflect", "t'\x04\x1d\x13\b\x04\x05\x17\x06\t-\n\x03\x11\x02\x02"},
+ {"net/internal/socktest", "\x81\x01\xcc\x01\x02"},
+ {"net/mail", "\x02|\x03\x84\x01\x04\x0f\x03\x16\x1a\x02\x0f\x04"},
+ {"net/netip", "\x04z*\x01h\x036\x17"},
+ {"net/rpc", "\x02p\f\x03\x0f\nk\x04\x12\x01\x1f\r\x03\x02"},
+ {"net/rpc/jsonrpc", "t\n\x03\x84\x01\x16\x13\x1f"},
+ {"net/smtp", "\x195\r\x14\x0f\x03\x84\x01\x16\x16\x1a"},
+ {"net/textproto", "\x02\x01{\x03\x84\x01\f\n/\x01\x02\x15"},
+ {"net/url", "~\x03Ff\v\x10\x02\x01\x17"},
+ {"os", "~*\x01\x19\x04\x11\x14\x01\x03\x01\x05\x10\x01:\b\x05\x01\x01\r\x06"},
+ {"os/exec", "\x03\nqI(\x01\x15\x01-\x06\a\n\x01\x03\x01\r"},
+ {"os/exec/internal/fdtest", "\xcf\x02"},
+ {"os/signal", "\r\xa6\x02\x15\x05\x02"},
+ {"os/user", "\x02\x01{\x03\x84\x01.\r\n\x01\x02"},
+ {"path", "~*\xb8\x01"},
+ {"path/filepath", "~*\x1aD-\r\b\x03\x04\x11"},
+ {"plugin", "~"},
+ {"reflect", "~&\x04\x1e\x03\x11\b\x04\x05\x17\x06\t/\n\x03\x11\x02\x02"},
{"reflect/internal/example1", ""},
{"reflect/internal/example2", ""},
- {"regexp", "\x03\xf7\x018\t\x02\x01\x02\x11\x02"},
- {"regexp/syntax", "\xbb\x02\x01\x01\x01\x02\x11\x02"},
- {"runtime", "\x9b\x01\x04\x01\x03\f\x06\a\x02\x01\x01\x0e\x03\x01\x01\x01\x02\x01\x01\x01\x02\x01\x04\x01\x10\x18L"},
- {"runtime/coverage", "\xa7\x01S"},
- {"runtime/debug", "wUZ\r\b\x02\x01\x11\x06"},
- {"runtime/metrics", "\xbe\x01H-\""},
- {"runtime/pprof", "\x02\x01\x01\x03\x06`\a\x03$$\x0f\v!\f \r\b\x01\x01\x01\x02\x02\n\x03\x06"},
- {"runtime/race", "\xb9\x02"},
+ {"regexp", "\x03\x82\x02\x037\t\x02\x01\x02\x11\x02"},
+ {"regexp/syntax", "\xc8\x02\x01\x01\x01\x02\x11\x02"},
+ {"runtime", "\xa4\x01\x04\x01\x03\f\x06\b\x02\x01\x01\x0f\x03\x01\x01\x01\x02\x01\x01\x01\x02\x01\x04\x01\x10\x18N"},
+ {"runtime/coverage", "\xb0\x01U"},
+ {"runtime/debug", "\x81\x01V\\\r\b\x02\x01\x11\x06"},
+ {"runtime/metrics", "\xc8\x01I/\""},
+ {"runtime/pprof", "\x02\x01\x01\x03\x06c\x0e\x03#5\v!\f\"\r\b\x01\x01\x01\x02\x02\n\x03\x06"},
+ {"runtime/race", "\xc6\x02"},
{"runtime/race/internal/amd64v1", ""},
- {"runtime/trace", "\rg\x03z\t9\b\x05\x01\x0e\x06"},
- {"slices", "\x04\xf9\x01\fL"},
- {"sort", "\xd0\x0192"},
- {"strconv", "t+A\x01r"},
- {"strings", "t'\x04B\x19\x03\f7\x11\x02\x02"},
+ {"runtime/trace", "\rq\x03{\t;\b\x05\x01\x0e\x06"},
+ {"slices", "\x04\x84\x02\fN"},
+ {"sort", "\xdb\x0194"},
+ {"strconv", "~*C\x01t"},
+ {"strings", "~&\x04D\x19\x03\f9\x11\x02\x02"},
{"structs", ""},
- {"sync", "\xcf\x01\x13\x01P\x0e\x14"},
- {"sync/atomic", "\xd5\x02"},
- {"syscall", "t(\x03\x01\x1c\n\x03\x06\r\x04S\b\x05\x01\x14"},
- {"testing", "\x03\ng\x02\x01X\x17\x14\f\x05\x1b\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\x0e\x02\x04"},
- {"testing/cryptotest", "QOZ\x124\x03\x12"},
- {"testing/fstest", "t\x03\x83\x01\x01\n&\x10\x03\t\b"},
- {"testing/internal/testdeps", "\x02\v\xae\x01/\x10,\x03\x05\x03\x06\a\x02\x0f"},
- {"testing/iotest", "\x03q\x03\x83\x01\x04"},
- {"testing/quick", "v\x01\x8f\x01\x05#\x10\x11"},
- {"testing/slogtest", "\rg\x03\x89\x01.\x05\x10\f"},
- {"testing/synctest", "\xe3\x01`\x12"},
- {"text/scanner", "\x03t\x83\x01,+\x02"},
- {"text/tabwriter", "w\x83\x01Y"},
- {"text/template", "t\x03C@\x01\n \x01\x05\x01\x02\x05\v\x02\x0e\x03\x02"},
- {"text/template/parse", "\x03t\xbc\x01\n\x01\x13\x02"},
- {"time", "t+\x1e$(*\r\x02\x13"},
- {"time/tzdata", "t\xce\x01\x13"},
+ {"sync", "\xda\x01\x02\x11\x01R\x0e\x14"},
+ {"sync/atomic", "\xe2\x02"},
+ {"syscall", "~'\x03\x01\x1d\n\x04\x06\r\x04U\b\x05\x01\x14"},
+ {"testing", "\x03\nq\x02\x01Y\x17\x14\f\x05\x1d\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\x0e\x02\x04"},
+ {"testing/cryptotest", "SV\\\x126\x03\x12"},
+ {"testing/fstest", "~\x03\x84\x01\x01\n(\x10\x03\t\b"},
+ {"testing/internal/testdeps", "\x02\v\xb7\x011\x10.\x03\x05\x03\x06\a\x02\x0f"},
+ {"testing/iotest", "\x03{\x03\x84\x01\x04"},
+ {"testing/quick", "\x80\x01\x01\x90\x01\x05%\x10\x11"},
+ {"testing/slogtest", "\rq\x03\x8a\x010\x05\x10\f"},
+ {"testing/synctest", "\xee\x01b\f\x06"},
+ {"text/scanner", "\x03~\x84\x01.+\x02"},
+ {"text/tabwriter", "\x81\x01\x84\x01["},
+ {"text/template", "~\x03BB\x01\n\"\x01\x05\x01\x02\x05\v\x02\x0e\x03\x02"},
+ {"text/template/parse", "\x03~\xbf\x01\n\x01\x13\x02"},
+ {"time", "~*D(,\r\x02\x13"},
+ {"time/tzdata", "~\xd1\x01\x13"},
{"unicode", ""},
{"unicode/utf16", ""},
{"unicode/utf8", ""},
- {"unique", "\x9b\x01!%\x01Q\r\x01\x14\x12"},
+ {"unique", "\xa4\x01\"&\x01S\r\x01\x14\x19"},
{"unsafe", ""},
- {"vendor/golang.org/x/crypto/chacha20", "\x10]\a\x95\x01*'"},
- {"vendor/golang.org/x/crypto/chacha20poly1305", "\x10\aV\a\xe2\x01\x04\x01\a"},
- {"vendor/golang.org/x/crypto/cryptobyte", "j\n\x03\x90\x01'!\n"},
+ {"uuid", "\x03\x01O\x1d\x03\v\xcf\x01\x0f"},
+ {"vendor/golang.org/x/crypto/chacha20", "\x10`\x0e\x96\x01,)"},
+ {"vendor/golang.org/x/crypto/chacha20poly1305", "\x10\aY\x0e\xe6\x01\x05\x01\f"},
+ {"vendor/golang.org/x/crypto/cryptobyte", "m\x11\x03\x91\x01)!\v"},
{"vendor/golang.org/x/crypto/cryptobyte/asn1", ""},
- {"vendor/golang.org/x/crypto/internal/alias", "\xd5\x02"},
- {"vendor/golang.org/x/crypto/internal/poly1305", "X\x15\x9c\x01"},
- {"vendor/golang.org/x/net/dns/dnsmessage", "t\xc7\x01"},
- {"vendor/golang.org/x/net/http/httpguts", "\x90\x02\x14\x1a\x15\r"},
- {"vendor/golang.org/x/net/http/httpproxy", "t\x03\x99\x01\x10\x05\x01\x18\x15\r"},
- {"vendor/golang.org/x/net/http2/hpack", "\x03q\x03\x83\x01F"},
- {"vendor/golang.org/x/net/idna", "w\x8f\x018\x15\x10\x02\x01"},
- {"vendor/golang.org/x/net/nettest", "\x03j\a\x03\x83\x01\x11\x05\x16\x01\f\n\x01\x02\x02\x01\f"},
- {"vendor/golang.org/x/sys/cpu", "\xa6\x02\r\n\x01\x17"},
- {"vendor/golang.org/x/text/secure/bidirule", "t\xdf\x01\x11\x01"},
- {"vendor/golang.org/x/text/transform", "\x03q\x86\x01Y"},
- {"vendor/golang.org/x/text/unicode/bidi", "\x03\bl\x87\x01>\x17"},
- {"vendor/golang.org/x/text/unicode/norm", "m\n\x83\x01F\x13\x11"},
- {"weak", "\x9b\x01\x98\x01\""},
+ {"vendor/golang.org/x/crypto/hkdf", "\x18\x01e\x15r"},
+ {"vendor/golang.org/x/crypto/internal/alias", "\xe2\x02"},
+ {"vendor/golang.org/x/crypto/internal/poly1305", "Z\x16\xa4\x01"},
+ {"vendor/golang.org/x/net/dns/dnsmessage", "~\xca\x01"},
+ {"vendor/golang.org/x/net/http/httpguts", "\x9b\x02\x16\x1a\x15\x10"},
+ {"vendor/golang.org/x/net/http/httpproxy", "~\x03\x9a\x01\x12\x05\x01\x18\x15\x10"},
+ {"vendor/golang.org/x/net/http2/hpack", "\x03{\x03\x84\x01H"},
+ {"vendor/golang.org/x/net/http3", "\x9c\x02@\x06\x0f\x04"},
+ {"vendor/golang.org/x/net/idna", "\x81\x01\x90\x01:\x13\x02\x17\x02\x01"},
+ {"vendor/golang.org/x/net/internal/http3", "\rN#\x03\x84\x01\v\x04\a\x01\x05\x10\x01\x16\x02\x01\x02\x0f\x10\x02\x04\x03"},
+ {"vendor/golang.org/x/net/internal/httpcommon", "\rq\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
+ {"vendor/golang.org/x/net/internal/quic/quicwire", "p"},
+ {"vendor/golang.org/x/net/nettest", "\x03m\x0e\x03\x84\x01\x11\x05\x18\x01\f\n\x01\x02\x02\x01\f"},
+ {"vendor/golang.org/x/net/quic", "\x03\n\x01\x01\x01\t:\x04\x04\x15\x03\v\x03\x12r\x06\x06\x06\x04\x12\x06\x15\x02\x01\x02\x01\x01\r\x06\x02\x01\x01\x02\v"},
+ {"vendor/golang.org/x/sys/cpu", "\xb3\x02\r\n\x01\x17"},
+ {"vendor/golang.org/x/text/secure/bidirule", "~\xe2\x01\x18\x01"},
+ {"vendor/golang.org/x/text/transform", "\x03{\x87\x01["},
+ {"vendor/golang.org/x/text/unicode/bidi", "\x03\bv\x88\x01@\x17"},
+ {"vendor/golang.org/x/text/unicode/norm", "p\x11\x84\x01H\x13\x18"},
+ {"weak", "\xa4\x01\x9c\x01\""},
}
// bootstrap is the list of bootstrap packages extracted from cmd/dist.
@@ -408,6 +428,7 @@ var bootstrap = map[string]bool{
"cmd/compile/internal/logopt": true,
"cmd/compile/internal/loong64": true,
"cmd/compile/internal/loopvar": true,
+ "cmd/compile/internal/midway": true,
"cmd/compile/internal/mips": true,
"cmd/compile/internal/mips64": true,
"cmd/compile/internal/noder": true,
@@ -512,6 +533,7 @@ var bootstrap = map[string]bool{
"internal/race": true,
"internal/runtime/gc": true,
"internal/saferio": true,
+ "internal/strconv": true,
"internal/syscall/unix": true,
"internal/types/errors": true,
"internal/unsafeheader": true,
diff --git a/vendor/golang.org/x/tools/internal/stdlib/manifest.go b/vendor/golang.org/x/tools/internal/stdlib/manifest.go
index 33e4f505f..2180c296e 100644
--- a/vendor/golang.org/x/tools/internal/stdlib/manifest.go
+++ b/vendor/golang.org/x/tools/internal/stdlib/manifest.go
@@ -270,6 +270,7 @@ var PackageSymbols = map[string][]Symbol{
{"ContainsRune", Func, 7, "func(b []byte, r rune) bool"},
{"Count", Func, 0, "func(s []byte, sep []byte) int"},
{"Cut", Func, 18, "func(s []byte, sep []byte) (before []byte, after []byte, found bool)"},
+ {"CutLast", Func, 27, "func(s []byte, sep []byte) (before []byte, after []byte, found bool)"},
{"CutPrefix", Func, 20, "func(s []byte, prefix []byte) (after []byte, found bool)"},
{"CutSuffix", Func, 20, "func(s []byte, suffix []byte) (before []byte, found bool)"},
{"Equal", Func, 0, "func(a []byte, b []byte) bool"},
@@ -538,6 +539,7 @@ var PackageSymbols = map[string][]Symbol{
{"MD4", Const, 0, ""},
{"MD5", Const, 0, ""},
{"MD5SHA1", Const, 0, ""},
+ {"MLDSAMu", Const, 27, ""},
{"MessageSigner", Type, 25, ""},
{"PrivateKey", Type, 0, ""},
{"PublicKey", Type, 2, ""},
@@ -812,6 +814,40 @@ var PackageSymbols = map[string][]Symbol{
{"Size", Const, 0, ""},
{"Sum", Func, 2, "func(data []byte) [16]byte"},
},
+ "crypto/mldsa": {
+ {"(*Options).HashFunc", Method, 27, ""},
+ {"(*PrivateKey).Bytes", Method, 27, ""},
+ {"(*PrivateKey).Equal", Method, 27, ""},
+ {"(*PrivateKey).Public", Method, 27, ""},
+ {"(*PrivateKey).PublicKey", Method, 27, ""},
+ {"(*PrivateKey).Sign", Method, 27, ""},
+ {"(*PrivateKey).SignDeterministic", Method, 27, ""},
+ {"(*PublicKey).Bytes", Method, 27, ""},
+ {"(*PublicKey).Equal", Method, 27, ""},
+ {"(*PublicKey).Parameters", Method, 27, ""},
+ {"(Parameters).PublicKeySize", Method, 27, ""},
+ {"(Parameters).SignatureSize", Method, 27, ""},
+ {"(Parameters).String", Method, 27, ""},
+ {"GenerateKey", Func, 27, "func(params Parameters) (*PrivateKey, error)"},
+ {"MLDSA44", Func, 27, "func() Parameters"},
+ {"MLDSA44PublicKeySize", Const, 27, ""},
+ {"MLDSA44SignatureSize", Const, 27, ""},
+ {"MLDSA65", Func, 27, "func() Parameters"},
+ {"MLDSA65PublicKeySize", Const, 27, ""},
+ {"MLDSA65SignatureSize", Const, 27, ""},
+ {"MLDSA87", Func, 27, "func() Parameters"},
+ {"MLDSA87PublicKeySize", Const, 27, ""},
+ {"MLDSA87SignatureSize", Const, 27, ""},
+ {"NewPrivateKey", Func, 27, "func(params Parameters, seed []byte) (*PrivateKey, error)"},
+ {"NewPublicKey", Func, 27, "func(params Parameters, encoding []byte) (*PublicKey, error)"},
+ {"Options", Type, 27, ""},
+ {"Options.Context", Field, 27, ""},
+ {"Parameters", Type, 27, ""},
+ {"PrivateKey", Type, 27, ""},
+ {"PrivateKeySize", Const, 27, ""},
+ {"PublicKey", Type, 27, ""},
+ {"Verify", Func, 27, "func(pk *PublicKey, message []byte, signature []byte, opts *Options) error"},
+ },
"crypto/mlkem": {
{"(*DecapsulationKey1024).Bytes", Method, 24, ""},
{"(*DecapsulationKey1024).Decapsulate", Method, 24, ""},
@@ -1120,6 +1156,7 @@ var PackageSymbols = map[string][]Symbol{
{"ConnectionState.ECHAccepted", Field, 23, ""},
{"ConnectionState.HandshakeComplete", Field, 0, ""},
{"ConnectionState.HelloRetryRequest", Field, 26, ""},
+ {"ConnectionState.LocalCertificate", Field, 27, ""},
{"ConnectionState.NegotiatedProtocol", Field, 0, ""},
{"ConnectionState.NegotiatedProtocolIsMutual", Field, 0, ""},
{"ConnectionState.OCSPResponse", Field, 5, ""},
@@ -1152,6 +1189,10 @@ var PackageSymbols = map[string][]Symbol{
{"InsecureCipherSuites", Func, 14, "func() []*CipherSuite"},
{"Listen", Func, 0, "func(network string, laddr string, config *Config) (net.Listener, error)"},
{"LoadX509KeyPair", Func, 0, "func(certFile string, keyFile string) (Certificate, error)"},
+ {"MLDSA44", Const, 27, ""},
+ {"MLDSA65", Const, 27, ""},
+ {"MLDSA87", Const, 27, ""},
+ {"MLKEM1024", Const, 27, ""},
{"NewLRUClientSessionCache", Func, 3, "func(capacity int) ClientSessionCache"},
{"NewListener", Func, 0, "func(inner net.Listener, config *Config) net.Listener"},
{"NewResumptionState", Func, 21, "func(ticket []byte, state *SessionState) (*ClientSessionState, error)"},
@@ -1166,6 +1207,7 @@ var PackageSymbols = map[string][]Symbol{
{"ParseSessionState", Func, 21, "func(data []byte) (*SessionState, error)"},
{"QUICClient", Func, 21, "func(config *QUICConfig) *QUICConn"},
{"QUICConfig", Type, 21, ""},
+ {"QUICConfig.ClientHelloInfoConn", Field, 27, ""},
{"QUICConfig.EnableSessionEvents", Field, 23, ""},
{"QUICConfig.TLSConfig", Field, 21, ""},
{"QUICConn", Type, 21, ""},
@@ -1334,6 +1376,7 @@ var PackageSymbols = map[string][]Symbol{
{"Certificate.PublicKeyAlgorithm", Field, 0, ""},
{"Certificate.Raw", Field, 0, ""},
{"Certificate.RawIssuer", Field, 0, ""},
+ {"Certificate.RawSignatureAlgorithm", Field, 27, ""},
{"Certificate.RawSubject", Field, 0, ""},
{"Certificate.RawSubjectPublicKeyInfo", Field, 0, ""},
{"Certificate.RawTBSCertificate", Field, 0, ""},
@@ -1362,6 +1405,7 @@ var PackageSymbols = map[string][]Symbol{
{"CertificateRequest.PublicKey", Field, 3, ""},
{"CertificateRequest.PublicKeyAlgorithm", Field, 3, ""},
{"CertificateRequest.Raw", Field, 3, ""},
+ {"CertificateRequest.RawSignatureAlgorithm", Field, 27, ""},
{"CertificateRequest.RawSubject", Field, 3, ""},
{"CertificateRequest.RawSubjectPublicKeyInfo", Field, 3, ""},
{"CertificateRequest.RawTBSCertificateRequest", Field, 3, ""},
@@ -1422,6 +1466,10 @@ var PackageSymbols = map[string][]Symbol{
{"KeyUsageKeyEncipherment", Const, 0, ""},
{"MD2WithRSA", Const, 0, ""},
{"MD5WithRSA", Const, 0, ""},
+ {"MLDSA", Const, 27, ""},
+ {"MLDSA44", Const, 27, ""},
+ {"MLDSA65", Const, 27, ""},
+ {"MLDSA87", Const, 27, ""},
{"MarshalECPrivateKey", Func, 2, "func(key *ecdsa.PrivateKey) ([]byte, error)"},
{"MarshalPKCS1PrivateKey", Func, 0, "func(key *rsa.PrivateKey) []byte"},
{"MarshalPKCS1PublicKey", Func, 10, "func(key *rsa.PublicKey) []byte"},
@@ -1468,6 +1516,7 @@ var PackageSymbols = map[string][]Symbol{
{"RevocationList.Number", Field, 15, ""},
{"RevocationList.Raw", Field, 19, ""},
{"RevocationList.RawIssuer", Field, 19, ""},
+ {"RevocationList.RawSignatureAlgorithm", Field, 27, ""},
{"RevocationList.RawTBSRevocationList", Field, 19, ""},
{"RevocationList.RevokedCertificateEntries", Field, 21, ""},
{"RevocationList.RevokedCertificates", Field, 15, ""},
@@ -1648,6 +1697,7 @@ var PackageSymbols = map[string][]Symbol{
{"(Scanner).Scan", Method, 0, ""},
{"ColumnType", Type, 8, ""},
{"Conn", Type, 9, ""},
+ {"ConvertAssign", Func, 27, "func(scanCtx driver.ScanContext, dest any, src driver.Value) error"},
{"DB", Type, 0, ""},
{"DBStats", Type, 5, ""},
{"DBStats.Idle", Field, 11, ""},
@@ -1744,6 +1794,11 @@ var PackageSymbols = map[string][]Symbol{
{"(Rows).Next", Method, 0, ""},
{"(RowsAffected).LastInsertId", Method, 0, ""},
{"(RowsAffected).RowsAffected", Method, 0, ""},
+ {"(RowsColumnScanner).Close", Method, 27, ""},
+ {"(RowsColumnScanner).Columns", Method, 27, ""},
+ {"(RowsColumnScanner).Next", Method, 27, ""},
+ {"(RowsColumnScanner).NextRow", Method, 27, ""},
+ {"(RowsColumnScanner).ScanColumn", Method, 27, ""},
{"(RowsColumnTypeDatabaseTypeName).Close", Method, 8, ""},
{"(RowsColumnTypeDatabaseTypeName).ColumnTypeDatabaseTypeName", Method, 8, ""},
{"(RowsColumnTypeDatabaseTypeName).Columns", Method, 8, ""},
@@ -1815,12 +1870,14 @@ var PackageSymbols = map[string][]Symbol{
{"ResultNoRows", Var, 0, ""},
{"Rows", Type, 0, ""},
{"RowsAffected", Type, 0, ""},
+ {"RowsColumnScanner", Type, 27, ""},
{"RowsColumnTypeDatabaseTypeName", Type, 8, ""},
{"RowsColumnTypeLength", Type, 8, ""},
{"RowsColumnTypeNullable", Type, 8, ""},
{"RowsColumnTypePrecisionScale", Type, 8, ""},
{"RowsColumnTypeScanType", Type, 8, ""},
{"RowsNextResultSet", Type, 8, ""},
+ {"ScanContext", Type, 27, ""},
{"SessionResetter", Type, 10, ""},
{"Stmt", Type, 0, ""},
{"StmtExecContext", Type, 8, ""},
@@ -5038,24 +5095,32 @@ var PackageSymbols = map[string][]Symbol{
{"(*InvalidUnmarshalError).Error", Method, 0, ""},
{"(*MarshalerError).Error", Method, 0, ""},
{"(*MarshalerError).Unwrap", Method, 13, ""},
+ {"(*Number).UnmarshalJSONFrom", Method, 27, ""},
{"(*RawMessage).MarshalJSON", Method, 0, ""},
{"(*RawMessage).UnmarshalJSON", Method, 0, ""},
{"(*SyntaxError).Error", Method, 0, ""},
{"(*UnmarshalFieldError).Error", Method, 0, ""},
{"(*UnmarshalTypeError).Error", Method, 0, ""},
+ {"(*UnmarshalTypeError).Unwrap", Method, 27, ""},
{"(*UnsupportedTypeError).Error", Method, 0, ""},
{"(*UnsupportedValueError).Error", Method, 0, ""},
{"(Delim).String", Method, 5, ""},
{"(Marshaler).MarshalJSON", Method, 0, ""},
{"(Number).Float64", Method, 1, ""},
{"(Number).Int64", Method, 1, ""},
+ {"(Number).MarshalJSONTo", Method, 27, ""},
{"(Number).String", Method, 1, ""},
{"(RawMessage).MarshalJSON", Method, 8, ""},
{"(Unmarshaler).UnmarshalJSON", Method, 0, ""},
+ {"CallMethodsWithLegacySemantics", Func, 27, "func(v bool) Options"},
{"Compact", Func, 0, "func(dst *bytes.Buffer, src []byte) error"},
{"Decoder", Type, 0, ""},
+ {"DefaultOptionsV1", Func, 27, "func() Options"},
{"Delim", Type, 5, ""},
{"Encoder", Type, 0, ""},
+ {"FormatByteArrayAsArray", Func, 27, "func(v bool) Options"},
+ {"FormatBytesWithLegacySemantics", Func, 27, "func(v bool) Options"},
+ {"FormatDurationAsNano", Func, 27, "func(v bool) Options"},
{"HTMLEscape", Func, 0, "func(dst *bytes.Buffer, src []byte)"},
{"Indent", Func, 0, "func(dst *bytes.Buffer, src []byte, prefix string, indent string) error"},
{"InvalidUTF8Error", Type, 0, ""},
@@ -5068,19 +5133,29 @@ var PackageSymbols = map[string][]Symbol{
{"MarshalerError", Type, 0, ""},
{"MarshalerError.Err", Field, 0, ""},
{"MarshalerError.Type", Field, 0, ""},
+ {"MatchCaseSensitiveDelimiter", Func, 27, "func(v bool) Options"},
+ {"MergeWithLegacySemantics", Func, 27, "func(v bool) Options"},
{"NewDecoder", Func, 0, "func(r io.Reader) *Decoder"},
{"NewEncoder", Func, 0, "func(w io.Writer) *Encoder"},
{"Number", Type, 1, ""},
+ {"OmitEmptyWithLegacySemantics", Func, 27, "func(v bool) Options"},
+ {"Options", Type, 27, ""},
+ {"ParseBytesWithLooseRFC4648", Func, 27, "func(v bool) Options"},
+ {"ParseTimeWithLooseRFC3339", Func, 27, "func(v bool) Options"},
{"RawMessage", Type, 0, ""},
+ {"ReportErrorsWithLegacySemantics", Func, 27, "func(v bool) Options"},
+ {"StringifyWithLegacySemantics", Func, 27, "func(v bool) Options"},
{"SyntaxError", Type, 0, ""},
{"SyntaxError.Offset", Field, 0, ""},
{"Token", Type, 5, ""},
{"Unmarshal", Func, 0, "func(data []byte, v any) error"},
+ {"UnmarshalArrayFromAnyLength", Func, 27, "func(v bool) Options"},
{"UnmarshalFieldError", Type, 0, ""},
{"UnmarshalFieldError.Field", Field, 0, ""},
{"UnmarshalFieldError.Key", Field, 0, ""},
{"UnmarshalFieldError.Type", Field, 0, ""},
{"UnmarshalTypeError", Type, 0, ""},
+ {"UnmarshalTypeError.Err", Field, 27, ""},
{"UnmarshalTypeError.Field", Field, 8, ""},
{"UnmarshalTypeError.Offset", Field, 5, ""},
{"UnmarshalTypeError.Struct", Field, 8, ""},
@@ -5094,6 +5169,158 @@ var PackageSymbols = map[string][]Symbol{
{"UnsupportedValueError.Value", Field, 0, ""},
{"Valid", Func, 9, "func(data []byte) bool"},
},
+ "encoding/json/jsontext": {
+ {"(*Decoder).InputOffset", Method, 27, ""},
+ {"(*Decoder).Options", Method, 27, ""},
+ {"(*Decoder).PeekKind", Method, 27, ""},
+ {"(*Decoder).ReadToken", Method, 27, ""},
+ {"(*Decoder).ReadValue", Method, 27, ""},
+ {"(*Decoder).Reset", Method, 27, ""},
+ {"(*Decoder).SkipValue", Method, 27, ""},
+ {"(*Decoder).StackDepth", Method, 27, ""},
+ {"(*Decoder).StackIndex", Method, 27, ""},
+ {"(*Decoder).StackPointer", Method, 27, ""},
+ {"(*Decoder).UnreadBuffer", Method, 27, ""},
+ {"(*Encoder).AvailableBuffer", Method, 27, ""},
+ {"(*Encoder).Options", Method, 27, ""},
+ {"(*Encoder).OutputOffset", Method, 27, ""},
+ {"(*Encoder).Reset", Method, 27, ""},
+ {"(*Encoder).StackDepth", Method, 27, ""},
+ {"(*Encoder).StackIndex", Method, 27, ""},
+ {"(*Encoder).StackPointer", Method, 27, ""},
+ {"(*Encoder).WriteToken", Method, 27, ""},
+ {"(*Encoder).WriteValue", Method, 27, ""},
+ {"(*SyntacticError).Error", Method, 27, ""},
+ {"(*SyntacticError).Unwrap", Method, 27, ""},
+ {"(*Value).Canonicalize", Method, 27, ""},
+ {"(*Value).Compact", Method, 27, ""},
+ {"(*Value).Format", Method, 27, ""},
+ {"(*Value).Indent", Method, 27, ""},
+ {"(*Value).UnmarshalJSON", Method, 27, ""},
+ {"(Kind).String", Method, 27, ""},
+ {"(Pointer).AppendToken", Method, 27, ""},
+ {"(Pointer).Contains", Method, 27, ""},
+ {"(Pointer).IsValid", Method, 27, ""},
+ {"(Pointer).LastToken", Method, 27, ""},
+ {"(Pointer).Parent", Method, 27, ""},
+ {"(Pointer).Tokens", Method, 27, ""},
+ {"(Token).Bool", Method, 27, ""},
+ {"(Token).Clone", Method, 27, ""},
+ {"(Token).Float", Method, 27, ""},
+ {"(Token).Float32", Method, 27, ""},
+ {"(Token).Int", Method, 27, ""},
+ {"(Token).Kind", Method, 27, ""},
+ {"(Token).String", Method, 27, ""},
+ {"(Token).Uint", Method, 27, ""},
+ {"(Value).Clone", Method, 27, ""},
+ {"(Value).IsValid", Method, 27, ""},
+ {"(Value).Kind", Method, 27, ""},
+ {"(Value).MarshalJSON", Method, 27, ""},
+ {"(Value).String", Method, 27, ""},
+ {"AllowDuplicateNames", Func, 27, "func(v bool) Options"},
+ {"AllowInvalidUTF8", Func, 27, "func(v bool) Options"},
+ {"AppendFloat", Func, 27, "func(dst []byte, src float64, bits int) []byte"},
+ {"AppendFormat", Func, 27, "func[Bytes ~[]byte | ~string](dst []byte, src Bytes, opts ...Options) ([]byte, error)"},
+ {"AppendQuote", Func, 27, "func[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error)"},
+ {"AppendUnquote", Func, 27, "func[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error)"},
+ {"BeginArray", Var, 27, ""},
+ {"BeginObject", Var, 27, ""},
+ {"Bool", Func, 27, "func(b bool) Token"},
+ {"CanonicalizeRawFloats", Func, 27, "func(v bool) Options"},
+ {"CanonicalizeRawInts", Func, 27, "func(v bool) Options"},
+ {"Decoder", Type, 27, ""},
+ {"Encoder", Type, 27, ""},
+ {"EndArray", Var, 27, ""},
+ {"EndObject", Var, 27, ""},
+ {"ErrDuplicateName", Var, 27, ""},
+ {"ErrNonStringName", Var, 27, ""},
+ {"EscapeForHTML", Func, 27, "func(v bool) Options"},
+ {"EscapeForJS", Func, 27, "func(v bool) Options"},
+ {"False", Var, 27, ""},
+ {"Float", Func, 27, "func(n float64) Token"},
+ {"Float32", Func, 27, "func(n float32) Token"},
+ {"Int", Func, 27, "func(n int64) Token"},
+ {"Internal", Var, 27, ""},
+ {"Kind", Type, 27, ""},
+ {"KindBeginArray", Const, 27, ""},
+ {"KindBeginObject", Const, 27, ""},
+ {"KindEndArray", Const, 27, ""},
+ {"KindEndObject", Const, 27, ""},
+ {"KindFalse", Const, 27, ""},
+ {"KindInvalid", Const, 27, ""},
+ {"KindNull", Const, 27, ""},
+ {"KindNumber", Const, 27, ""},
+ {"KindString", Const, 27, ""},
+ {"KindTrue", Const, 27, ""},
+ {"Multiline", Func, 27, "func(v bool) Options"},
+ {"NewDecoder", Func, 27, "func(r io.Reader, opts ...Options) *Decoder"},
+ {"NewEncoder", Func, 27, "func(w io.Writer, opts ...Options) *Encoder"},
+ {"Null", Var, 27, ""},
+ {"Options", Type, 27, ""},
+ {"Pointer", Type, 27, ""},
+ {"PreserveRawStrings", Func, 27, "func(v bool) Options"},
+ {"ReorderRawObjects", Func, 27, "func(v bool) Options"},
+ {"SpaceAfterColon", Func, 27, "func(v bool) Options"},
+ {"SpaceAfterComma", Func, 27, "func(v bool) Options"},
+ {"String", Func, 27, "func(s string) Token"},
+ {"SyntacticError", Type, 27, ""},
+ {"SyntacticError.ByteOffset", Field, 27, ""},
+ {"SyntacticError.Err", Field, 27, ""},
+ {"SyntacticError.JSONPointer", Field, 27, ""},
+ {"Token", Type, 27, ""},
+ {"True", Var, 27, ""},
+ {"Uint", Func, 27, "func(n uint64) Token"},
+ {"Value", Type, 27, ""},
+ {"WithIndent", Func, 27, "func(indent string) Options"},
+ {"WithIndentPrefix", Func, 27, "func(prefix string) Options"},
+ },
+ "encoding/json/v2": {
+ {"(*SemanticError).Error", Method, 27, ""},
+ {"(*SemanticError).Unwrap", Method, 27, ""},
+ {"(Marshaler).MarshalJSON", Method, 27, ""},
+ {"(MarshalerTo).MarshalJSONTo", Method, 27, ""},
+ {"(Unmarshaler).UnmarshalJSON", Method, 27, ""},
+ {"(UnmarshalerFrom).UnmarshalJSONFrom", Method, 27, ""},
+ {"DefaultOptionsV2", Func, 27, "func() Options"},
+ {"Deterministic", Func, 27, "func(v bool) Options"},
+ {"ErrUnknownName", Var, 27, ""},
+ {"FormatNilMapAsNull", Func, 27, "func(v bool) Options"},
+ {"FormatNilSliceAsNull", Func, 27, "func(v bool) Options"},
+ {"GetOption", Func, 27, "func[T any](opts Options, setter func(T) Options) (T, bool)"},
+ {"JoinMarshalers", Func, 27, "func(ms ...*Marshalers) *Marshalers"},
+ {"JoinOptions", Func, 27, "func(srcs ...Options) Options"},
+ {"JoinUnmarshalers", Func, 27, "func(us ...*Unmarshalers) *Unmarshalers"},
+ {"Marshal", Func, 27, "func(in any, opts ...Options) (out []byte, err error)"},
+ {"MarshalEncode", Func, 27, "func(out *jsontext.Encoder, in any, opts ...Options) (err error)"},
+ {"MarshalFunc", Func, 27, "func[T any](fn func(T) ([]byte, error)) *Marshalers"},
+ {"MarshalToFunc", Func, 27, "func[T any](fn func(*jsontext.Encoder, T) error) *Marshalers"},
+ {"MarshalWrite", Func, 27, "func(out io.Writer, in any, opts ...Options) (err error)"},
+ {"Marshaler", Type, 27, ""},
+ {"MarshalerTo", Type, 27, ""},
+ {"Marshalers", Type, 27, ""},
+ {"MatchCaseInsensitiveNames", Func, 27, "func(v bool) Options"},
+ {"OmitZeroStructFields", Func, 27, "func(v bool) Options"},
+ {"Options", Type, 27, ""},
+ {"RejectUnknownMembers", Func, 27, "func(v bool) Options"},
+ {"SemanticError", Type, 27, ""},
+ {"SemanticError.ByteOffset", Field, 27, ""},
+ {"SemanticError.Err", Field, 27, ""},
+ {"SemanticError.GoType", Field, 27, ""},
+ {"SemanticError.JSONKind", Field, 27, ""},
+ {"SemanticError.JSONPointer", Field, 27, ""},
+ {"SemanticError.JSONValue", Field, 27, ""},
+ {"StringifyNumbers", Func, 27, "func(v bool) Options"},
+ {"Unmarshal", Func, 27, "func(in []byte, out any, opts ...Options) (err error)"},
+ {"UnmarshalDecode", Func, 27, "func(in *jsontext.Decoder, out any, opts ...Options) (err error)"},
+ {"UnmarshalFromFunc", Func, 27, "func[T any](fn func(*jsontext.Decoder, T) error) *Unmarshalers"},
+ {"UnmarshalFunc", Func, 27, "func[T any](fn func([]byte, T) error) *Unmarshalers"},
+ {"UnmarshalRead", Func, 27, "func(in io.Reader, out any, opts ...Options) (err error)"},
+ {"Unmarshaler", Type, 27, ""},
+ {"UnmarshalerFrom", Type, 27, ""},
+ {"Unmarshalers", Type, 27, ""},
+ {"WithMarshalers", Func, 27, "func(v *Marshalers) Options"},
+ {"WithUnmarshalers", Func, 27, "func(v *Unmarshalers) Options"},
+ },
"encoding/pem": {
{"Block", Type, 0, ""},
{"Block.Bytes", Field, 0, ""},
@@ -6002,6 +6229,7 @@ var PackageSymbols = map[string][]Symbol{
{"Shift", Func, 5, "func(x Value, op token.Token, s uint) Value"},
{"Sign", Func, 5, "func(x Value) int"},
{"String", Const, 5, ""},
+ {"StringLen", Func, 27, "func(x Value) int64"},
{"StringVal", Func, 5, "func(x Value) string"},
{"ToComplex", Func, 6, "func(x Value) Value"},
{"ToFloat", Func, 6, "func(x Value) Value"},
@@ -6183,6 +6411,7 @@ var PackageSymbols = map[string][]Symbol{
{"(*ErrorList).Add", Method, 0, ""},
{"(*ErrorList).RemoveMultiples", Method, 0, ""},
{"(*ErrorList).Reset", Method, 0, ""},
+ {"(*Scanner).End", Method, 27, ""},
{"(*Scanner).Init", Method, 0, ""},
{"(*Scanner).Scan", Method, 0, ""},
{"(Error).Error", Method, 0, ""},
@@ -6222,6 +6451,7 @@ var PackageSymbols = map[string][]Symbol{
{"(*File).SetLines", Method, 0, ""},
{"(*File).SetLinesForContent", Method, 0, ""},
{"(*File).Size", Method, 0, ""},
+ {"(*File).String", Method, 27, ""},
{"(*FileSet).AddExistingFiles", Method, 25, ""},
{"(*FileSet).AddFile", Method, 0, ""},
{"(*FileSet).Base", Method, 0, ""},
@@ -6529,6 +6759,7 @@ var PackageSymbols = map[string][]Symbol{
{"(*Tuple).Variables", Method, 24, ""},
{"(*TypeList).At", Method, 18, ""},
{"(*TypeList).Len", Method, 18, ""},
+ {"(*TypeList).String", Method, 27, ""},
{"(*TypeList).Types", Method, 24, ""},
{"(*TypeName).Exported", Method, 5, ""},
{"(*TypeName).Id", Method, 5, ""},
@@ -6547,6 +6778,7 @@ var PackageSymbols = map[string][]Symbol{
{"(*TypeParam).Underlying", Method, 18, ""},
{"(*TypeParamList).At", Method, 18, ""},
{"(*TypeParamList).Len", Method, 18, ""},
+ {"(*TypeParamList).String", Method, 27, ""},
{"(*TypeParamList).TypeParams", Method, 24, ""},
{"(*Union).Len", Method, 18, ""},
{"(*Union).String", Method, 18, ""},
@@ -6571,9 +6803,14 @@ var PackageSymbols = map[string][]Symbol{
{"(Checker).PkgNameOf", Method, 22, ""},
{"(Checker).TypeOf", Method, 5, ""},
{"(Error).Error", Method, 5, ""},
+ {"(Hasher).Equal", Method, 27, ""},
+ {"(Hasher).Hash", Method, 27, ""},
+ {"(HasherIgnoreTags).Equal", Method, 27, ""},
+ {"(HasherIgnoreTags).Hash", Method, 27, ""},
{"(Importer).Import", Method, 5, ""},
{"(ImporterFrom).Import", Method, 6, ""},
{"(ImporterFrom).ImportFrom", Method, 6, ""},
+ {"(Instance).String", Method, 27, ""},
{"(Object).Exported", Method, 5, ""},
{"(Object).Id", Method, 5, ""},
{"(Object).Name", Method, 5, ""},
@@ -6643,6 +6880,8 @@ var PackageSymbols = map[string][]Symbol{
{"Float32", Const, 5, ""},
{"Float64", Const, 5, ""},
{"Func", Type, 5, ""},
+ {"Hasher", Type, 27, ""},
+ {"HasherIgnoreTags", Type, 27, ""},
{"Id", Func, 5, "func(pkg *Package, name string) string"},
{"Identical", Func, 5, "func(x Type, y Type) bool"},
{"IdenticalIgnoreTags", Func, 8, "func(x Type, y Type) bool"},
@@ -6877,9 +7116,13 @@ var PackageSymbols = map[string][]Symbol{
{"(*Hash).Write", Method, 14, ""},
{"(*Hash).WriteByte", Method, 14, ""},
{"(*Hash).WriteString", Method, 14, ""},
+ {"(ComparableHasher).Equal", Method, 27, ""},
+ {"(ComparableHasher).Hash", Method, 27, ""},
{"Bytes", Func, 19, "func(seed Seed, b []byte) uint64"},
{"Comparable", Func, 24, "func[T comparable](seed Seed, v T) uint64"},
+ {"ComparableHasher", Type, 27, ""},
{"Hash", Type, 14, ""},
+ {"Hasher", Type, 27, ""},
{"MakeSeed", Func, 14, "func() Seed"},
{"Seed", Type, 14, ""},
{"String", Func, 19, "func(seed Seed, s string) uint64"},
@@ -8035,6 +8278,7 @@ var PackageSymbols = map[string][]Symbol{
{"(*Int).CmpAbs", Method, 10, ""},
{"(*Int).Div", Method, 0, ""},
{"(*Int).DivMod", Method, 0, ""},
+ {"(*Int).Divide", Method, 27, ""},
{"(*Int).Exp", Method, 0, ""},
{"(*Int).FillBytes", Method, 15, ""},
{"(*Int).Float64", Method, 21, ""},
@@ -8119,9 +8363,11 @@ var PackageSymbols = map[string][]Symbol{
{"Accuracy", Type, 5, ""},
{"AwayFromZero", Const, 5, ""},
{"Below", Const, 5, ""},
+ {"Ceil", Const, 27, ""},
{"ErrNaN", Type, 5, ""},
{"Exact", Const, 5, ""},
{"Float", Type, 5, ""},
+ {"Floor", Const, 27, ""},
{"Int", Type, 0, ""},
{"Jacobi", Func, 5, "func(x *Int, y *Int) int"},
{"MaxBase", Const, 0, ""},
@@ -8133,12 +8379,14 @@ var PackageSymbols = map[string][]Symbol{
{"NewRat", Func, 0, "func(a int64, b int64) *Rat"},
{"ParseFloat", Func, 5, "func(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error)"},
{"Rat", Type, 0, ""},
+ {"Round", Const, 27, ""},
{"RoundingMode", Type, 5, ""},
{"ToNearestAway", Const, 5, ""},
{"ToNearestEven", Const, 5, ""},
{"ToNegativeInf", Const, 5, ""},
{"ToPositiveInf", Const, 5, ""},
{"ToZero", Const, 5, ""},
+ {"Trunc", Const, 27, ""},
{"Word", Type, 0, ""},
},
"math/bits": {
@@ -8290,6 +8538,7 @@ var PackageSymbols = map[string][]Symbol{
{"(*Rand).Int64", Method, 22, ""},
{"(*Rand).Int64N", Method, 22, ""},
{"(*Rand).IntN", Method, 22, ""},
+ {"(*Rand).N", Method, 27, ""},
{"(*Rand).NormFloat64", Method, 22, ""},
{"(*Rand).Perm", Method, 22, ""},
{"(*Rand).Shuffle", Method, 22, ""},
@@ -8985,7 +9234,7 @@ var PackageSymbols = map[string][]Symbol{
{"NoBody", Var, 8, ""},
{"NotFound", Func, 0, "func(w ResponseWriter, r *Request)"},
{"NotFoundHandler", Func, 0, "func() Handler"},
- {"ParseCookie", Func, 23, "func(line string) ([]*Cookie, error)"},
+ {"ParseCookie", Func, 23, "func(line string) (#rv1 []*Cookie, #rv2 error)"},
{"ParseHTTPVersion", Func, 0, "func(vers string) (major int, minor int, ok bool)"},
{"ParseSetCookie", Func, 23, "func(line string) (*Cookie, error)"},
{"ParseTime", Func, 1, "func(text string) (t time.Time, err error)"},
@@ -9061,6 +9310,7 @@ var PackageSymbols = map[string][]Symbol{
{"Server.BaseContext", Field, 13, ""},
{"Server.ConnContext", Field, 13, ""},
{"Server.ConnState", Field, 3, ""},
+ {"Server.DisableClientPriority", Field, 27, ""},
{"Server.DisableGeneralOptionsHandler", Field, 20, ""},
{"Server.ErrorLog", Field, 3, ""},
{"Server.HTTP2", Field, 24, ""},
@@ -9226,6 +9476,7 @@ var PackageSymbols = map[string][]Symbol{
{"NewRequestWithContext", Func, 23, "func(ctx context.Context, method string, target string, body io.Reader) *http.Request"},
{"NewServer", Func, 0, "func(handler http.Handler) *Server"},
{"NewTLSServer", Func, 0, "func(handler http.Handler) *Server"},
+ {"NewTestServer", Func, 27, "func(t testing.TB, handler http.Handler) *Server"},
{"NewUnstartedServer", Func, 0, "func(handler http.Handler) *Server"},
{"ResponseRecorder", Type, 0, ""},
{"ResponseRecorder.Body", Field, 0, ""},
@@ -9596,6 +9847,7 @@ var PackageSymbols = map[string][]Symbol{
{"(*Error).Timeout", Method, 6, ""},
{"(*Error).Unwrap", Method, 13, ""},
{"(*URL).AppendBinary", Method, 24, ""},
+ {"(*URL).Clone", Method, 27, ""},
{"(*URL).EscapedFragment", Method, 15, ""},
{"(*URL).EscapedPath", Method, 5, ""},
{"(*URL).Hostname", Method, 8, ""},
@@ -9616,6 +9868,7 @@ var PackageSymbols = map[string][]Symbol{
{"(EscapeError).Error", Method, 0, ""},
{"(InvalidHostError).Error", Method, 6, ""},
{"(Values).Add", Method, 0, ""},
+ {"(Values).Clone", Method, 27, ""},
{"(Values).Del", Method, 0, ""},
{"(Values).Encode", Method, 0, ""},
{"(Values).Get", Method, 0, ""},
@@ -10793,6 +11046,7 @@ var PackageSymbols = map[string][]Symbol{
{"ContainsRune", Func, 0, "func(s string, r rune) bool"},
{"Count", Func, 0, "func(s string, substr string) int"},
{"Cut", Func, 18, "func(s string, sep string) (before string, after string, found bool)"},
+ {"CutLast", Func, 27, "func(s string, sep string) (before string, after string, found bool)"},
{"CutPrefix", Func, 20, "func(s string, prefix string) (after string, found bool)"},
{"CutSuffix", Func, 20, "func(s string, suffix string) (before string, found bool)"},
{"EqualFold", Func, 0, "func(s string, t string) bool"},
@@ -17476,6 +17730,7 @@ var PackageSymbols = map[string][]Symbol{
{"TestHandler", Func, 21, "func(h slog.Handler, results func() []map[string]any) error"},
},
"testing/synctest": {
+ {"Sleep", Func, 27, "func(d time.Duration)"},
{"Test", Func, 25, "func(t *testing.T, f func(*testing.T))"},
{"Wait", Func, 25, "func()"},
},
@@ -17980,6 +18235,7 @@ var PackageSymbols = map[string][]Symbol{
{"Bassa_Vah", Var, 4, ""},
{"Batak", Var, 0, ""},
{"Bengali", Var, 0, ""},
+ {"Beria_Erfe", Var, 27, ""},
{"Bhaiksuki", Var, 7, ""},
{"Bidi_Control", Var, 0, ""},
{"Bopomofo", Var, 0, ""},
@@ -18029,6 +18285,7 @@ var PackageSymbols = map[string][]Symbol{
{"Extender", Var, 0, ""},
{"FoldCategory", Var, 0, ""},
{"FoldScript", Var, 0, ""},
+ {"Garay", Var, 27, ""},
{"Georgian", Var, 0, ""},
{"Glagolitic", Var, 0, ""},
{"Gothic", Var, 0, ""},
@@ -18038,6 +18295,7 @@ var PackageSymbols = map[string][]Symbol{
{"Gujarati", Var, 0, ""},
{"Gunjala_Gondi", Var, 13, ""},
{"Gurmukhi", Var, 0, ""},
+ {"Gurung_Khema", Var, 27, ""},
{"Han", Var, 0, ""},
{"Hangul", Var, 0, ""},
{"Hanifi_Rohingya", Var, 13, ""},
@@ -18049,6 +18307,9 @@ var PackageSymbols = map[string][]Symbol{
{"Hyphen", Var, 0, ""},
{"IDS_Binary_Operator", Var, 0, ""},
{"IDS_Trinary_Operator", Var, 0, ""},
+ {"IDS_Unary_Operator", Var, 27, ""},
+ {"ID_Compat_Math_Continue", Var, 27, ""},
+ {"ID_Compat_Math_Start", Var, 27, ""},
{"Ideographic", Var, 0, ""},
{"Imperial_Aramaic", Var, 0, ""},
{"In", Func, 2, "func(r rune, ranges ...*RangeTable) bool"},
@@ -18082,6 +18343,7 @@ var PackageSymbols = map[string][]Symbol{
{"Khmer", Var, 0, ""},
{"Khojki", Var, 4, ""},
{"Khudawadi", Var, 4, ""},
+ {"Kirat_Rai", Var, 27, ""},
{"L", Var, 0, ""},
{"LC", Var, 25, ""},
{"Lao", Var, 0, ""},
@@ -18125,6 +18387,7 @@ var PackageSymbols = map[string][]Symbol{
{"Miao", Var, 1, ""},
{"Mn", Var, 0, ""},
{"Modi", Var, 4, ""},
+ {"Modifier_Combining_Mark", Var, 27, ""},
{"Mongolian", Var, 0, ""},
{"Mro", Var, 4, ""},
{"Multani", Var, 5, ""},
@@ -18145,6 +18408,7 @@ var PackageSymbols = map[string][]Symbol{
{"Nyiakeng_Puachue_Hmong", Var, 14, ""},
{"Ogham", Var, 0, ""},
{"Ol_Chiki", Var, 0, ""},
+ {"Ol_Onal", Var, 27, ""},
{"Old_Hungarian", Var, 5, ""},
{"Old_Italic", Var, 0, ""},
{"Old_North_Arabian", Var, 4, ""},
@@ -18214,6 +18478,7 @@ var PackageSymbols = map[string][]Symbol{
{"Sharada", Var, 1, ""},
{"Shavian", Var, 0, ""},
{"Siddham", Var, 4, ""},
+ {"Sidetic", Var, 27, ""},
{"SignWriting", Var, 5, ""},
{"SimpleFold", Func, 0, "func(r rune) rune"},
{"Sinhala", Var, 0, ""},
@@ -18227,6 +18492,7 @@ var PackageSymbols = map[string][]Symbol{
{"Space", Var, 0, ""},
{"SpecialCase", Type, 0, ""},
{"Sundanese", Var, 0, ""},
+ {"Sunuwar", Var, 27, ""},
{"Syloti_Nagri", Var, 0, ""},
{"Symbol", Var, 0, ""},
{"Syriac", Var, 0, ""},
@@ -18235,6 +18501,7 @@ var PackageSymbols = map[string][]Symbol{
{"Tai_Le", Var, 0, ""},
{"Tai_Tham", Var, 0, ""},
{"Tai_Viet", Var, 0, ""},
+ {"Tai_Yo", Var, 27, ""},
{"Takri", Var, 1, ""},
{"Tamil", Var, 0, ""},
{"Tangsa", Var, 21, ""},
@@ -18252,7 +18519,10 @@ var PackageSymbols = map[string][]Symbol{
{"ToLower", Func, 0, "func(r rune) rune"},
{"ToTitle", Func, 0, "func(r rune) rune"},
{"ToUpper", Func, 0, "func(r rune) rune"},
+ {"Todhri", Var, 27, ""},
+ {"Tolong_Siki", Var, 27, ""},
{"Toto", Var, 21, ""},
+ {"Tulu_Tigalari", Var, 27, ""},
{"TurkishCase", Var, 0, ""},
{"Ugaritic", Var, 0, ""},
{"Unified_Ideograph", Var, 0, ""},
@@ -18320,6 +18590,21 @@ var PackageSymbols = map[string][]Symbol{
{"String", Func, 0, ""},
{"StringData", Func, 0, ""},
},
+ "uuid": {
+ {"(*UUID).UnmarshalText", Method, 27, ""},
+ {"(UUID).AppendText", Method, 27, ""},
+ {"(UUID).Compare", Method, 27, ""},
+ {"(UUID).MarshalText", Method, 27, ""},
+ {"(UUID).String", Method, 27, ""},
+ {"Max", Func, 27, "func() UUID"},
+ {"MustParse", Func, 27, "func(s string) UUID"},
+ {"New", Func, 27, "func() UUID"},
+ {"NewV4", Func, 27, "func() UUID"},
+ {"NewV7", Func, 27, "func() UUID"},
+ {"Nil", Func, 27, "func() UUID"},
+ {"Parse", Func, 27, "func(s string) (UUID, error)"},
+ {"UUID", Type, 27, ""},
+ },
"weak": {
{"(Pointer).Value", Method, 24, ""},
{"Make", Func, 24, "func[T any](ptr *T) Pointer[T]"},
diff --git a/vendor/golang.org/x/tools/internal/typesinternal/element.go b/vendor/golang.org/x/tools/internal/typesinternal/element.go
index 5fe4d8abc..89eeea165 100644
--- a/vendor/golang.org/x/tools/internal/typesinternal/element.go
+++ b/vendor/golang.org/x/tools/internal/typesinternal/element.go
@@ -37,6 +37,10 @@ func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T type
tmset := msets.MethodSet(T)
for method := range tmset.Methods() {
sig := method.Type().(*types.Signature)
+ if sig.TypeParams() != nil {
+ continue // skip type-parameterized methods
+ }
+
// It is tempting to call visit(sig, false)
// but, as noted in golang.org/cl/65450043,
// the Signature.Recv field is ignored by
@@ -123,10 +127,10 @@ func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T type
case *types.TypeParam, *types.Union:
// forEachReachable must not be called on parameterized types.
- panic(T)
+ panic(fmt.Sprintf("ForEachElement called on type containing %T", T))
default:
- panic(T)
+ panic(fmt.Sprintf("ForEachElement called on unexpected type %T", T))
}
}
visit(T, false)
diff --git a/vendor/golang.org/x/tools/internal/typesinternal/types.go b/vendor/golang.org/x/tools/internal/typesinternal/types.go
index 6582cc81f..d2c0b4c5f 100644
--- a/vendor/golang.org/x/tools/internal/typesinternal/types.go
+++ b/vendor/golang.org/x/tools/internal/typesinternal/types.go
@@ -22,6 +22,7 @@ import (
"go/ast"
"go/token"
"go/types"
+ "iter"
"reflect"
"golang.org/x/tools/go/ast/inspector"
@@ -242,3 +243,30 @@ func ObjectKind(obj types.Object) string {
}
return "unknown symbol"
}
+
+// ImplicitFieldSelections returns the sequence of implicit embedded fields
+// traversed by the given selection. It skips the final leaf field or method.
+// The boolean component indicates whether the traversal traversed a pointer.
+func ImplicitFieldSelections(seln types.Selection) iter.Seq2[*types.Var, bool] {
+ return func(yield func(*types.Var, bool) bool) {
+ var (
+ t = seln.Recv()
+ indices = seln.Index()
+ )
+ for _, idx := range indices[:len(indices)-1] {
+ ptr, isPtr := t.Underlying().(*types.Pointer)
+ if isPtr {
+ t = ptr.Elem()
+ }
+ structType, ok := t.Underlying().(*types.Struct)
+ if !ok {
+ break
+ }
+ field := structType.Field(idx)
+ if !yield(field, isPtr) {
+ break
+ }
+ t = field.Type()
+ }
+ }
+}
diff --git a/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go b/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go
index d612a7102..706ad33ef 100644
--- a/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go
+++ b/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go
@@ -259,13 +259,13 @@ func TypeExpr(t types.Type, qual types.Qualifier) ast.Expr {
case *types.Signature:
var params []*ast.Field
for v := range t.Params().Variables() {
+ var names []*ast.Ident
+ if v.Name() != "" {
+ names = []*ast.Ident{ast.NewIdent(v.Name())}
+ }
params = append(params, &ast.Field{
- Type: TypeExpr(v.Type(), qual),
- Names: []*ast.Ident{
- {
- Name: v.Name(),
- },
- },
+ Type: TypeExpr(v.Type(), qual),
+ Names: names,
})
}
if t.Variadic() {
@@ -328,10 +328,10 @@ func TypeExpr(t types.Type, qual types.Qualifier) ast.Expr {
return expr
case *types.Struct:
- return ast.NewIdent(t.String())
+ return ast.NewIdent(types.TypeString(t, qual))
case *types.Interface:
- return ast.NewIdent(t.String())
+ return ast.NewIdent(types.TypeString(t, qual))
case *types.Union:
if t.Len() == 0 {
diff --git a/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go b/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go
index 06a3f7106..f25a7bcc7 100644
--- a/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go
+++ b/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go
@@ -1,4 +1,4 @@
-// Copyright 2025 Google LLC
+// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -127,14 +127,13 @@ var file_google_rpc_status_proto_rawDesc = []byte{
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61,
0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52,
- 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x61, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e,
+ 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x5e, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x42, 0x0b, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73,
0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3b, 0x73, 0x74, 0x61, 0x74,
- 0x75, 0x73, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, 0x52, 0x50, 0x43, 0x62, 0x06, 0x70, 0x72, 0x6f,
- 0x74, 0x6f, 0x33,
+ 0x75, 0x73, 0xa2, 0x02, 0x03, 0x52, 0x50, 0x43, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/vendor/google.golang.org/grpc/balancer/balancer.go b/vendor/google.golang.org/grpc/balancer/balancer.go
index 326888ae3..7e3dbaad2 100644
--- a/vendor/google.golang.org/grpc/balancer/balancer.go
+++ b/vendor/google.golang.org/grpc/balancer/balancer.go
@@ -60,7 +60,7 @@ func Register(b Builder) {
if !envconfig.CaseSensitiveBalancerRegistries {
name = strings.ToLower(name)
if name != b.Name() {
- logger.Warningf("Balancer registered with name %q. grpc-go will be switching to case sensitive balancer registries soon. After 2 releases, we will enable the env var by default.", b.Name())
+ logger.Warningf("Balancer registered with name %q. grpc-go has switched to case sensitive balancer registries. GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES env variable will be removed in release v1.82.0", b.Name())
}
}
m[name] = b
@@ -85,7 +85,7 @@ func Get(name string) Builder {
if !envconfig.CaseSensitiveBalancerRegistries {
lowerName := strings.ToLower(name)
if lowerName != name {
- logger.Warningf("Balancer retrieved for name %q. grpc-go will be switching to case sensitive balancer registries soon. After 2 releases, we will enable the env var by default.", name)
+ logger.Warningf("Balancer retrieved for name %q. grpc-go has switched to case sensitive balancer registries. GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES env variable will be removed in release v1.82.0", name)
}
name = lowerName
}
diff --git a/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go
index 518a69d57..d48bc304c 100644
--- a/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go
+++ b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go
@@ -35,9 +35,9 @@ import (
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/pickfirst/internal"
"google.golang.org/grpc/connectivity"
+ "google.golang.org/grpc/experimental/balancer/weight"
expstats "google.golang.org/grpc/experimental/stats"
"google.golang.org/grpc/grpclog"
- "google.golang.org/grpc/internal/balancer/weight"
"google.golang.org/grpc/internal/envconfig"
internalgrpclog "google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/internal/pretty"
diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go
index 5dec2dacc..c4bca5203 100644
--- a/vendor/google.golang.org/grpc/clientconn.go
+++ b/vendor/google.golang.org/grpc/clientconn.go
@@ -24,10 +24,12 @@ import (
"fmt"
"math"
"net/url"
+ "os"
"slices"
"strings"
"sync"
"sync/atomic"
+ "syscall"
"time"
"google.golang.org/grpc/balancer"
@@ -1268,8 +1270,9 @@ type addrConn struct {
channelz *channelz.SubChannel
- localityLabel string
- backendServiceLabel string
+ localityLabel string
+ backendServiceLabel string
+ disconnectErrorLabel string
}
// Note: this requires a lock on ac.mu.
@@ -1286,9 +1289,14 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error)
// TODO: https://github.com/grpc/grpc-go/issues/7862 - Remove the second
// part of the if condition below once the issue is fixed.
if ac.state == connectivity.Ready || (ac.state == connectivity.Connecting && s == connectivity.Idle) {
- disconnectionsMetric.Record(ac.cc.metricsRecorderList, 1, ac.cc.target, ac.backendServiceLabel, ac.localityLabel, "unknown")
+ disconnectError := ac.disconnectErrorLabel
+ if disconnectError == "" {
+ disconnectError = "unknown"
+ }
+ disconnectionsMetric.Record(ac.cc.metricsRecorderList, 1, ac.cc.target, ac.backendServiceLabel, ac.localityLabel, disconnectError)
openConnectionsMetric.Record(ac.cc.metricsRecorderList, -1, ac.cc.target, ac.backendServiceLabel, ac.securityLevelLocked(), ac.localityLabel)
}
+ ac.disconnectErrorLabel = "" // Reset for next time
ac.state = s
ac.channelz.ChannelMetrics.State.Store(&s)
if lastErr == nil {
@@ -1483,11 +1491,11 @@ func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address,
addr.ServerName = ac.cc.getServerName(addr)
hctx, hcancel := context.WithCancel(ctx)
- onClose := func(r transport.GoAwayReason) {
+ onClose := func(info transport.GoAwayInfo) {
ac.mu.Lock()
defer ac.mu.Unlock()
// adjust params based on GoAwayReason
- ac.adjustParams(r)
+ ac.adjustParams(info.Reason)
if ctx.Err() != nil {
// Already shut down or connection attempt canceled. tearDown() or
// updateAddrs() already cleared the transport and canceled hctx
@@ -1504,6 +1512,7 @@ func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address,
return
}
ac.transport = nil
+ ac.disconnectErrorLabel = disconnectErrorString(info)
// Refresh the name resolver on any connection loss.
ac.cc.resolveNow(resolver.ResolveNowOptions{})
// Always go idle and wait for the LB policy to initiate a new
@@ -1560,6 +1569,32 @@ func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address,
return nil
}
+// disconnectErrorString returns the grpc.disconnect_error metric label corresponding
+// to the provided transport.GoAwayInfo, as specified by gRFC A94:
+// https://github.com/grpc/proposal/blob/master/A94-grpc-subchannel-disconnections-metrics.md
+func disconnectErrorString(info transport.GoAwayInfo) string {
+ err := info.Err
+ var sysErr syscall.Errno
+ switch {
+ case info.Reason != transport.GoAwayInvalid:
+ return fmt.Sprintf("GOAWAY %s", info.GoAwayCode.String())
+ case err == nil:
+ return "unknown"
+ case errors.Is(err, context.Canceled):
+ return "subchannel shutdown"
+ case errors.Is(err, syscall.ECONNRESET):
+ return "connection reset"
+ case errors.Is(err, syscall.ETIMEDOUT), errors.Is(err, context.DeadlineExceeded), errors.Is(err, os.ErrDeadlineExceeded):
+ return "connection timed out"
+ case errors.Is(err, syscall.ECONNABORTED):
+ return "connection aborted"
+ case errors.As(err, &sysErr):
+ return "socket error"
+ default:
+ return "unknown"
+ }
+}
+
// startHealthCheck starts the health checking stream (RPC) to watch the health
// stats of this connection if health checking is requested and configured.
//
@@ -1663,6 +1698,9 @@ func (ac *addrConn) tearDown(err error) {
}
curTr := ac.transport
ac.transport = nil
+ if ac.disconnectErrorLabel == "" {
+ ac.disconnectErrorLabel = "subchannel shutdown"
+ }
// We have to set the state to Shutdown before anything else to prevent races
// between setting the state and logic that waits on context cancellation / etc.
ac.updateConnectivityState(connectivity.Shutdown, nil)
diff --git a/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/grpc.go b/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/grpc.go
index cff016d66..31344f175 100644
--- a/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/grpc.go
+++ b/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/grpc.go
@@ -178,13 +178,8 @@ func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.
g.P("// This is a compile-time assertion to ensure that this generated file")
g.P("// is compatible with the grpc package it is being compiled against.")
- if *useGenericStreams {
- g.P("// Requires gRPC-Go v1.64.0 or later.")
- g.P("const _ = ", grpcPackage.Ident("SupportPackageIsVersion9"))
- } else {
- g.P("// Requires gRPC-Go v1.62.0 or later.")
- g.P("const _ = ", grpcPackage.Ident("SupportPackageIsVersion8")) // When changing, update version number above.
- }
+ g.P("// Requires gRPC-Go v1.64.0 or later.")
+ g.P("const _ = ", grpcPackage.Ident("SupportPackageIsVersion9"))
g.P()
for _, service := range file.Services {
genService(gen, file, g, service)
@@ -333,11 +328,7 @@ func clientSignature(g *protogen.GeneratedFile, method *protogen.Method) string
if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
s += "*" + g.QualifiedGoIdent(method.Output.GoIdent)
} else {
- if *useGenericStreams {
- s += clientStreamInterface(g, method)
- } else {
- s += method.Parent.GoName + "_" + method.GoName + "Client"
- }
+ s += clientStreamInterface(g, method)
}
s += ", error)"
return s
@@ -373,12 +364,8 @@ func genClientMethod(_ *protogen.Plugin, _ *protogen.File, g *protogen.Generated
return
}
- streamImpl := unexport(service.GoName) + method.GoName + "Client"
- if *useGenericStreams {
- typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent)
- streamImpl = g.QualifiedGoIdent(grpcPackage.Ident("GenericClientStream")) + "[" + typeParam + "]"
- }
-
+ typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent)
+ streamImpl := g.QualifiedGoIdent(grpcPackage.Ident("GenericClientStream")) + "[" + typeParam + "]"
serviceDescVar := service.GoName + "_ServiceDesc"
g.P("stream, err := c.cc.NewStream(ctx, &", serviceDescVar, ".Streams[", index, `], `, fmSymbol, `, cOpts...)`)
g.P("if err != nil { return nil, err }")
@@ -392,61 +379,9 @@ func genClientMethod(_ *protogen.Plugin, _ *protogen.File, g *protogen.Generated
g.P()
// Auxiliary types aliases, for backwards compatibility.
- if *useGenericStreams {
- g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.")
- g.P("type ", service.GoName, "_", method.GoName, "Client = ", clientStreamInterface(g, method))
- g.P()
- return
- }
-
- // Stream auxiliary types and methods, if we're not taking advantage of the
- // pre-implemented generic types and their methods.
- genSend := method.Desc.IsStreamingClient()
- genRecv := method.Desc.IsStreamingServer()
- genCloseAndRecv := !method.Desc.IsStreamingServer()
-
- g.P("type ", service.GoName, "_", method.GoName, "Client interface {")
- if genSend {
- g.P("Send(*", method.Input.GoIdent, ") error")
- }
- if genRecv {
- g.P("Recv() (*", method.Output.GoIdent, ", error)")
- }
- if genCloseAndRecv {
- g.P("CloseAndRecv() (*", method.Output.GoIdent, ", error)")
- }
- g.P(grpcPackage.Ident("ClientStream"))
- g.P("}")
- g.P()
-
- g.P("type ", streamImpl, " struct {")
- g.P(grpcPackage.Ident("ClientStream"))
- g.P("}")
+ g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.")
+ g.P("type ", service.GoName, "_", method.GoName, "Client = ", clientStreamInterface(g, method))
g.P()
-
- if genSend {
- g.P("func (x *", streamImpl, ") Send(m *", method.Input.GoIdent, ") error {")
- g.P("return x.ClientStream.SendMsg(m)")
- g.P("}")
- g.P()
- }
- if genRecv {
- g.P("func (x *", streamImpl, ") Recv() (*", method.Output.GoIdent, ", error) {")
- g.P("m := new(", method.Output.GoIdent, ")")
- g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
- g.P("return m, nil")
- g.P("}")
- g.P()
- }
- if genCloseAndRecv {
- g.P("func (x *", streamImpl, ") CloseAndRecv() (*", method.Output.GoIdent, ", error) {")
- g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
- g.P("m := new(", method.Output.GoIdent, ")")
- g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
- g.P("return m, nil")
- g.P("}")
- g.P()
- }
}
func serverSignature(g *protogen.GeneratedFile, method *protogen.Method) string {
@@ -460,11 +395,7 @@ func serverSignature(g *protogen.GeneratedFile, method *protogen.Method) string
reqArgs = append(reqArgs, "*"+g.QualifiedGoIdent(method.Input.GoIdent))
}
if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {
- if *useGenericStreams {
- reqArgs = append(reqArgs, serverStreamInterface(g, method))
- } else {
- reqArgs = append(reqArgs, method.Parent.GoName+"_"+method.GoName+"Server")
- }
+ reqArgs = append(reqArgs, serverStreamInterface(g, method))
}
return method.GoName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
}
@@ -545,11 +476,8 @@ func genServerMethod(_ *protogen.Plugin, _ *protogen.File, g *protogen.Generated
return hname
}
- streamImpl := unexport(service.GoName) + method.GoName + "Server"
- if *useGenericStreams {
- typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent)
- streamImpl = g.QualifiedGoIdent(grpcPackage.Ident("GenericServerStream")) + "[" + typeParam + "]"
- }
+ typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent)
+ streamImpl := g.QualifiedGoIdent(grpcPackage.Ident("GenericServerStream")) + "[" + typeParam + "]"
g.P("func ", hnameFuncNameFormatter(hname), "(srv interface{}, stream ", grpcPackage.Ident("ServerStream"), ") error {")
if !method.Desc.IsStreamingClient() {
@@ -563,59 +491,9 @@ func genServerMethod(_ *protogen.Plugin, _ *protogen.File, g *protogen.Generated
g.P()
// Auxiliary types aliases, for backwards compatibility.
- if *useGenericStreams {
- g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.")
- g.P("type ", service.GoName, "_", method.GoName, "Server = ", serverStreamInterface(g, method))
- g.P()
- return hname
- }
-
- // Stream auxiliary types and methods, if we're not taking advantage of the
- // pre-implemented generic types and their methods.
- genSend := method.Desc.IsStreamingServer()
- genSendAndClose := !method.Desc.IsStreamingServer()
- genRecv := method.Desc.IsStreamingClient()
-
- g.P("type ", service.GoName, "_", method.GoName, "Server interface {")
- if genSend {
- g.P("Send(*", method.Output.GoIdent, ") error")
- }
- if genSendAndClose {
- g.P("SendAndClose(*", method.Output.GoIdent, ") error")
- }
- if genRecv {
- g.P("Recv() (*", method.Input.GoIdent, ", error)")
- }
- g.P(grpcPackage.Ident("ServerStream"))
- g.P("}")
+ g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.")
+ g.P("type ", service.GoName, "_", method.GoName, "Server = ", serverStreamInterface(g, method))
g.P()
-
- g.P("type ", streamImpl, " struct {")
- g.P(grpcPackage.Ident("ServerStream"))
- g.P("}")
- g.P()
-
- if genSend {
- g.P("func (x *", streamImpl, ") Send(m *", method.Output.GoIdent, ") error {")
- g.P("return x.ServerStream.SendMsg(m)")
- g.P("}")
- g.P()
- }
- if genSendAndClose {
- g.P("func (x *", streamImpl, ") SendAndClose(m *", method.Output.GoIdent, ") error {")
- g.P("return x.ServerStream.SendMsg(m)")
- g.P("}")
- g.P()
- }
- if genRecv {
- g.P("func (x *", streamImpl, ") Recv() (*", method.Input.GoIdent, ", error) {")
- g.P("m := new(", method.Input.GoIdent, ")")
- g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }")
- g.P("return m, nil")
- g.P("}")
- g.P()
- }
-
return hname
}
diff --git a/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/main.go b/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/main.go
index 83bff58e0..94e7ffcf5 100644
--- a/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/main.go
+++ b/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/main.go
@@ -42,10 +42,9 @@ import (
"google.golang.org/protobuf/types/pluginpb"
)
-const version = "1.6.1"
+const version = "1.6.2"
var requireUnimplemented *bool
-var useGenericStreams *bool
func main() {
showVersion := flag.Bool("version", false, "print the version and exit")
@@ -57,7 +56,6 @@ func main() {
var flags flag.FlagSet
requireUnimplemented = flags.Bool("require_unimplemented_servers", true, "set to false to match legacy behavior")
- useGenericStreams = flags.Bool("use_generic_streams_experimental", true, "set to true to use generic types for streaming client and server objects; this flag is EXPERIMENTAL and may be changed or removed in a future release")
protogen.Options{
ParamFunc: flags.Set,
diff --git a/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/protoc-gen-go-grpc_test.sh b/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/protoc-gen-go-grpc_test.sh
index c70fd0fa6..fb67b1f41 100644
--- a/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/protoc-gen-go-grpc_test.sh
+++ b/vendor/google.golang.org/grpc/cmd/protoc-gen-go-grpc/protoc-gen-go-grpc_test.sh
@@ -30,7 +30,7 @@ popd
protoc \
--go-grpc_out="${TEMPDIR}" \
- --go-grpc_opt=paths=source_relative,use_generic_streams_experimental=true \
+ --go-grpc_opt=paths=source_relative \
"examples/route_guide/routeguide/route_guide.proto"
GOLDENFILE="examples/route_guide/routeguide/route_guide_grpc.pb.go"
diff --git a/vendor/google.golang.org/grpc/dialoptions.go b/vendor/google.golang.org/grpc/dialoptions.go
index 4ec5f9cd0..3af08e1ab 100644
--- a/vendor/google.golang.org/grpc/dialoptions.go
+++ b/vendor/google.golang.org/grpc/dialoptions.go
@@ -173,10 +173,8 @@ func newJoinDialOption(opts ...DialOption) DialOption {
// If this option is set to true every connection will release the buffer after
// flushing the data on the wire.
//
-// # Experimental
-//
-// Notice: This API is EXPERIMENTAL and may be changed or removed in a
-// later release.
+// Deprecated: shared write buffer is enabled by default. WithSharedWriteBuffer
+// will be removed in a future release.
func WithSharedWriteBuffer(val bool) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.SharedWriteBuffer = val
@@ -229,6 +227,14 @@ func WithInitialConnWindowSize(s int32) DialOption {
// WithStaticStreamWindowSize returns a DialOption which sets the initial
// stream window size to the value provided and disables dynamic flow control.
+//
+// Note that this also disables dynamic flow control for the connection,
+// falling back to a default static connection-level window of 64KB. To
+// use a larger connection-level window, you must also use the
+// [WithStaticConnWindowSize] DialOption.
+//
+// Most users should not configure static flow control windows unless
+// operating in a memory-constrained environment.
func WithStaticStreamWindowSize(s int32) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.InitialWindowSize = s
@@ -239,6 +245,14 @@ func WithStaticStreamWindowSize(s int32) DialOption {
// WithStaticConnWindowSize returns a DialOption which sets the initial
// connection window size to the value provided and disables dynamic flow
// control.
+//
+// Note that this also disables dynamic flow control for individual streams,
+// falling back to a default static connection-level window of 64KB. To
+// explicitly configure the stream-level window size, you must also use the
+// [WithStaticStreamWindowSize] DialOption.
+//
+// Most users should not configure static flow control windows unless
+// operating in a memory-constrained environment.
func WithStaticConnWindowSize(s int32) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.InitialConnWindowSize = s
diff --git a/vendor/google.golang.org/grpc/encoding/encoding.go b/vendor/google.golang.org/grpc/encoding/encoding.go
index 296f38c3a..bfa8b268f 100644
--- a/vendor/google.golang.org/grpc/encoding/encoding.go
+++ b/vendor/google.golang.org/grpc/encoding/encoding.go
@@ -66,6 +66,9 @@ type Compressor interface {
// Decompress reads data from r, decompresses it, and provides the
// uncompressed data via the returned io.Reader. If an error occurs while
// initializing the decompressor, that error is returned instead.
+ //
+ // The returned io.Reader may optionally implement io.ReadCloser, and if it
+ // does, gRPC will call Close() exactly once.
Decompress(r io.Reader) (io.Reader, error)
// Name is the name of the compression codec and is used to set the content
// coding header. The result must be static; the result cannot change
diff --git a/vendor/google.golang.org/grpc/internal/balancer/weight/weight.go b/vendor/google.golang.org/grpc/experimental/balancer/weight/weight.go
similarity index 71%
rename from vendor/google.golang.org/grpc/internal/balancer/weight/weight.go
rename to vendor/google.golang.org/grpc/experimental/balancer/weight/weight.go
index 11beb07d1..beab9e07c 100644
--- a/vendor/google.golang.org/grpc/internal/balancer/weight/weight.go
+++ b/vendor/google.golang.org/grpc/experimental/balancer/weight/weight.go
@@ -16,23 +16,23 @@
*
*/
-// Package weight contains utilities to manage endpoint weights. Weights are
-// used by LB policies such as ringhash to distribute load across multiple
-// endpoints.
+// Package weight contains utilities to manage endpoint weights.
+// Weights may be used by LB policies to distribute load across
+// multiple endpoints.
+//
+// # Experimental
+//
+// Notice: All APIs in this package are EXPERIMENTAL and may be changed
+// or removed in a later release.
package weight
-import (
- "fmt"
-
- "google.golang.org/grpc/resolver"
-)
+import "google.golang.org/grpc/resolver"
// attributeKey is the type used as the key to store EndpointInfo in the
// Attributes field of resolver.Endpoint.
type attributeKey struct{}
-// EndpointInfo will be stored in the Attributes field of Endpoints in order to
-// use the ringhash balancer.
+// EndpointInfo will be stored in the Attributes field of Endpoints.
type EndpointInfo struct {
Weight uint32
}
@@ -43,22 +43,16 @@ func (a EndpointInfo) Equal(o any) bool {
return ok && oa.Weight == a.Weight
}
-// Set returns a copy of endpoint in which the Attributes field is updated with
-// EndpointInfo.
+// Set returns a copy of endpoint in which the Attributes field is
+// updated with EndpointInfo.
func Set(endpoint resolver.Endpoint, epInfo EndpointInfo) resolver.Endpoint {
endpoint.Attributes = endpoint.Attributes.WithValue(attributeKey{}, epInfo)
return endpoint
}
-// String returns a human-readable representation of EndpointInfo.
-// This method is intended for logging, testing, and debugging purposes only.
-// Do not rely on the output format, as it is not guaranteed to remain stable.
-func (a EndpointInfo) String() string {
- return fmt.Sprintf("Weight: %d", a.Weight)
-}
-
-// FromEndpoint returns the EndpointInfo stored in the Attributes field of an
-// endpoint. It returns an empty EndpointInfo if attribute is not found.
+// FromEndpoint returns the EndpointInfo stored in the Attributes
+// field of an endpoint. It returns an empty EndpointInfo if attribute
+// is not found.
func FromEndpoint(endpoint resolver.Endpoint) EndpointInfo {
v := endpoint.Attributes.Value(attributeKey{})
ei, _ := v.(EndpointInfo)
diff --git a/vendor/google.golang.org/grpc/experimental/stats/metrics.go b/vendor/google.golang.org/grpc/experimental/stats/metrics.go
index 88742724a..8732e53bd 100644
--- a/vendor/google.golang.org/grpc/experimental/stats/metrics.go
+++ b/vendor/google.golang.org/grpc/experimental/stats/metrics.go
@@ -20,10 +20,27 @@
package stats
import (
+ "context"
+
"google.golang.org/grpc/internal"
"google.golang.org/grpc/stats"
)
+type customLabelKey struct{}
+
+// NewContextWithCustomLabel returns a new context with the provided custom label
+// attached. The label will be propagated to all metric instruments specified in gRFC A108.
+func NewContextWithCustomLabel(ctx context.Context, label string) context.Context {
+ return context.WithValue(ctx, customLabelKey{}, label)
+}
+
+// CustomLabelFromContext returns the custom label from the context if it exists.
+// If the custom label is not present, it returns an empty string.
+func CustomLabelFromContext(ctx context.Context) string {
+ label, _ := ctx.Value(customLabelKey{}).(string)
+ return label
+}
+
// MetricsRecorder records on metrics derived from metric registry.
// Implementors must embed UnimplementedMetricsRecorder.
type MetricsRecorder interface {
diff --git a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go
index 3ae45faa4..ba05b65d5 100644
--- a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go
+++ b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go
@@ -59,6 +59,15 @@ var (
// unconditionally.
XDSEndpointHashKeyBackwardCompat = boolFromEnv("GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT", false)
+ // LabelServerGoroutines controls setting [runtime/pprof.Labels] on the
+ // goroutines spawned by [grpc.Server] type.
+ // For now, this is limited to the goroutines spawned to handle incoming
+ // requests on the server.
+ // Set "GRPC_GO_SERVER_GOROUTINE_LABELS" to "grpc.method=true" to
+ // enable this grpc.method label, or "all" to enable all valid labels.
+ // This variable is a bit-field.
+ LabelServerGoroutines = goroutineLabelsFromEnv("GRPC_GO_SERVER_GOROUTINE_LABELS", 0)
+
// RingHashSetRequestHashKey is set if the ring hash balancer can get the
// request hash header by setting the "requestHashHeader" field, according
// to gRFC A76. It can be disabled by setting the environment variable
@@ -78,12 +87,12 @@ var (
EnableDefaultPortForProxyTarget = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_DEFAULT_PORT_FOR_PROXY_TARGET", true)
// CaseSensitiveBalancerRegistries is set if the balancer registry should be
- // case-sensitive. This is disabled by default, but can be enabled by setting
+ // case-sensitive. This is enabled by default, but can be disabled by setting
// the env variable "GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES"
- // to "true".
+ // to "false".
//
- // TODO: After 2 releases, we will enable the env var by default.
- CaseSensitiveBalancerRegistries = boolFromEnv("GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES", false)
+ // This env varible will be removed in release v1.82.0.
+ CaseSensitiveBalancerRegistries = boolFromEnv("GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES", true)
// XDSAuthorityRewrite indicates whether xDS authority rewriting is enabled.
// This feature is defined in gRFC A81 and is enabled by setting the
@@ -104,28 +113,34 @@ var (
// to "false".
XDSRecoverPanicInResourceParsing = boolFromEnv("GRPC_GO_EXPERIMENTAL_XDS_RESOURCE_PANIC_RECOVERY", true)
- // DisableStrictPathChecking indicates whether strict path checking is
- // disabled. This feature can be disabled by setting the environment
- // variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to "true".
- //
- // When strict path checking is enabled, gRPC will reject requests with
- // paths that do not conform to the gRPC over HTTP/2 specification found at
- // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md.
- //
- // When disabled, gRPC will allow paths that do not contain a leading slash.
- // Enabling strict path checking is recommended for security reasons, as it
- // prevents potential path traversal vulnerabilities.
- //
- // A future release will remove this environment variable, enabling strict
- // path checking behavior unconditionally.
- DisableStrictPathChecking = boolFromEnv("GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING", false)
-
// EnablePriorityLBChildPolicyCache controls whether the priority balancer
// should cache child balancers that are removed from the LB policy config,
// for a period of 15 minutes. This is disabled by default, but can be
// enabled by setting the env variable
// GRPC_EXPERIMENTAL_ENABLE_PRIORITY_LB_CHILD_POLICY_CACHE to true.
EnablePriorityLBChildPolicyCache = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_PRIORITY_LB_CHILD_POLICY_CACHE", false)
+
+ // Enable8KBDefaultHeaderListSize indicates that default maximum header list
+ // size is restricted to 8KB. This is disabled by default, but can be enabled
+ // by setting the environment variable
+ // "GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE" to "true".
+ // When disabled, the default maximum header list size of 16MB is used.
+ //
+ // When enabled, RPCs with a total size of headers exceeding 8KB will fail
+ // unless explicitly configured otherwise by the user.
+ //
+ // TODO: In release v1.82.0, env var will be enabled by default.
+ Enable8KBDefaultHeaderListSize = boolFromEnv("GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE", false)
+
+ // EnableHTTPFramerReadBufferPooling enables the use of the
+ // readyreader.Reader interface to perform non-memory-pinning reads,
+ // provided the underlying net.Conn supports it. This reduces memory usage
+ // when subchannels are idle.
+ //
+ // This environment variable serves as an escape hatch to disable the
+ // feature if unforeseen issues arise, and it will be removed in a future
+ // release.
+ EnableHTTPFramerReadBufferPooling = boolFromEnv("GRPC_GO_EXPERIMENTAL_HTTP_FRAMER_READ_BUFFER_POOLING", true)
)
func boolFromEnv(envVar string, def bool) bool {
@@ -150,3 +165,52 @@ func uint64FromEnv(envVar string, def, min, max uint64) uint64 {
}
return v
}
+
+// GoroutineLabels is a bitfield indicating which goroutine labels are enabled.
+type GoroutineLabels uint16
+
+func goroutineLabelsFromEnv(envVar string, def GoroutineLabels) GoroutineLabels {
+ val := def
+ v := os.Getenv(envVar)
+ if strings.EqualFold(v, "all") {
+ return AllGoroutineLabels
+ } else if strings.EqualFold(v, "none") {
+ return 0
+ }
+ for s := range strings.SplitSeq(v, ",") {
+ s = strings.TrimSpace(s)
+ if len(s) == 0 {
+ continue
+ }
+ pre, post, ok := strings.Cut(s, "=")
+ if !ok {
+ // no equals sign
+ continue
+ }
+ post = strings.TrimSpace(post)
+ pre = strings.TrimSpace(pre)
+ bitDesignator := GoroutineLabels(0)
+ switch {
+ case strings.EqualFold(pre, "grpc.method"):
+ bitDesignator = GoroutineLabelServerMethod
+ default:
+ continue
+ }
+ if strings.EqualFold(post, "true") {
+ val |= bitDesignator
+ } else if strings.EqualFold(post, "false") {
+ val &^= bitDesignator
+ }
+ }
+ return val
+}
+
+const (
+ // GoroutineLabelServerMethod sets the grpc.method label on new
+ // server-side gRPC streams.
+ GoroutineLabelServerMethod GoroutineLabels = 1 << iota
+)
+
+// AllGoroutineLabels is an or'd together bitfield of all valid GoroutineLabels
+// constant values (above).
+const AllGoroutineLabels = GoroutineLabelServerMethod
diff --git a/vendor/google.golang.org/grpc/internal/envconfig/xds.go b/vendor/google.golang.org/grpc/internal/envconfig/xds.go
index 7685d08b5..a2312f8ea 100644
--- a/vendor/google.golang.org/grpc/internal/envconfig/xds.go
+++ b/vendor/google.golang.org/grpc/internal/envconfig/xds.go
@@ -79,4 +79,24 @@ var (
// xDS bootstrap configuration via the `call_creds` field. For more details,
// see: https://github.com/grpc/proposal/blob/master/A97-xds-jwt-call-creds.md
XDSBootstrapCallCredsEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_BOOTSTRAP_CALL_CREDS", false)
+
+ // XDSSNIEnabled controls if gRPC should send SNI information in xDS
+ // configured TLS handshakes. For more details, see:
+ // https://github.com/grpc/proposal/blob/master/A101-SNI-setting-and-SNI-SAN-validation.md
+ XDSSNIEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_SNI", false)
+
+ // XDSORCAToLRSPropEnabled controls whether ORCA metrics are explicitly
+ // filtered and prefix-propagated to the LRS server. For more details, see:
+ // https://github.com/grpc/proposal/blob/master/A85-lrs-custom-metrics-changes.md
+ XDSORCAToLRSPropEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_ORCA_LRS_PROPAGATION", false)
+
+ // XDSClientExtProcEnabled indicates whether ExtProc filter is enabled on
+ // the client side. For more details, see:
+ // https://github.com/grpc/proposal/blob/master/A93-xds-ext-proc.md
+ XDSClientExtProcEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_EXT_PROC_ON_CLIENT", false)
+
+ // GCPAuthenticationFilterEnabled enables the xDS GCP Authentication
+ // filter. For more details, see:
+ // https://github.com/grpc/proposal/blob/master/A83-xds-gcp-authn-filter.md
+ GCPAuthenticationFilterEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER", false)
)
diff --git a/vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go b/vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go
index b25b0baec..1cc43fc6b 100644
--- a/vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go
+++ b/vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go
@@ -39,7 +39,6 @@ func div(d, r time.Duration) int64 {
//
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
func EncodeDuration(t time.Duration) string {
- // TODO: This is simplistic and not bandwidth efficient. Improve it.
if t <= 0 {
return "0n"
}
diff --git a/vendor/google.golang.org/grpc/internal/mem/buffer_pool.go b/vendor/google.golang.org/grpc/internal/mem/buffer_pool.go
index c2348a82e..2d83b2ece 100644
--- a/vendor/google.golang.org/grpc/internal/mem/buffer_pool.go
+++ b/vendor/google.golang.org/grpc/internal/mem/buffer_pool.go
@@ -73,7 +73,7 @@ type BinaryTieredBufferPool struct {
func NewBinaryTieredBufferPool(powerOfTwoExponents ...uint8) (*BinaryTieredBufferPool, error) {
return newBinaryTiered(func(size int) bufferPool {
return newSizedBufferPool(size, true)
- }, &simpleBufferPool{shouldZero: true}, powerOfTwoExponents...)
+ }, &SimpleBufferPool{shouldZero: true}, powerOfTwoExponents...)
}
// NewDirtyBinaryTieredBufferPool returns a BufferPool backed by multiple
@@ -82,7 +82,7 @@ func NewBinaryTieredBufferPool(powerOfTwoExponents ...uint8) (*BinaryTieredBuffe
func NewDirtyBinaryTieredBufferPool(powerOfTwoExponents ...uint8) (*BinaryTieredBufferPool, error) {
return newBinaryTiered(func(size int) bufferPool {
return newSizedBufferPool(size, false)
- }, &simpleBufferPool{shouldZero: false}, powerOfTwoExponents...)
+ }, NewDirtySimplePool(), powerOfTwoExponents...)
}
func newBinaryTiered(sizedPoolFactory func(int) bufferPool, fallbackPool bufferPool, powerOfTwoExponents ...uint8) (*BinaryTieredBufferPool, error) {
@@ -258,7 +258,7 @@ func newSizedBufferPool(size int, zero bool) *sizedBufferPool {
// buffer pools for different sizes of buffers.
type TieredBufferPool struct {
sizedPools []*sizedBufferPool
- fallbackPool simpleBufferPool
+ fallbackPool SimpleBufferPool
}
// NewTieredBufferPool returns a BufferPool implementation that uses multiple
@@ -271,7 +271,7 @@ func NewTieredBufferPool(poolSizes ...int) *TieredBufferPool {
}
return &TieredBufferPool{
sizedPools: pools,
- fallbackPool: simpleBufferPool{shouldZero: true},
+ fallbackPool: SimpleBufferPool{shouldZero: true},
}
}
@@ -297,16 +297,26 @@ func (p *TieredBufferPool) getPool(size int) bufferPool {
return p.sizedPools[poolIdx]
}
-// simpleBufferPool is an implementation of the BufferPool interface that
+// SimpleBufferPool is an implementation of the mem.BufferPool interface that
// attempts to pool buffers with a sync.Pool. When Get is invoked, it tries to
// acquire a buffer from the pool but if that buffer is too small, it returns it
// to the pool and creates a new one.
-type simpleBufferPool struct {
+type SimpleBufferPool struct {
pool sync.Pool
shouldZero bool
}
-func (p *simpleBufferPool) Get(size int) *[]byte {
+// NewDirtySimplePool constructs a [SimpleBufferPool]. It does not initialize
+// the buffers before returning them. Callers must ensure they don't read the
+// buffers before writing data to them.
+func NewDirtySimplePool() *SimpleBufferPool {
+ return &SimpleBufferPool{
+ shouldZero: false,
+ }
+}
+
+// Get returns a buffer with specified length from the pool.
+func (p *SimpleBufferPool) Get(size int) *[]byte {
bs, ok := p.pool.Get().(*[]byte)
if ok && cap(*bs) >= size {
if p.shouldZero {
@@ -333,6 +343,7 @@ func (p *simpleBufferPool) Get(size int) *[]byte {
return &b
}
-func (p *simpleBufferPool) Put(buf *[]byte) {
+// Put returns a buffer to the pool.
+func (p *SimpleBufferPool) Put(buf *[]byte) {
p.pool.Put(buf)
}
diff --git a/vendor/google.golang.org/grpc/internal/resolver/config_selector.go b/vendor/google.golang.org/grpc/internal/resolver/config_selector.go
index f0603871c..6320e9b57 100644
--- a/vendor/google.golang.org/grpc/internal/resolver/config_selector.go
+++ b/vendor/google.golang.org/grpc/internal/resolver/config_selector.go
@@ -106,15 +106,28 @@ type ClientStream interface {
// ClientInterceptor is an interceptor for gRPC client streams.
type ClientInterceptor interface {
- // NewStream produces a ClientStream for an RPC which may optionally use
- // the provided function to produce a stream for delegation. Note:
- // RPCInfo.Context should not be used (will be nil).
+ // NewStream creates a ClientStream for an RPC.
//
- // done is invoked when the RPC is finished using its connection, or could
- // not be assigned a connection. RPC operations may still occur on
- // ClientStream after done is called, since the interceptor is invoked by
- // application-layer operations. done must never be nil when called.
+ // Implementations must delegate stream creation to the provided newStream
+ // function. To intercept or override stream behavior, implementations
+ // may wrap the ClientStream returned by the delegate.
+ //
+ // Note: RPCInfo.Context is currently unused and will be nil.
+ //
+ // The done function is invoked when the RPC has finished using its
+ // underlying connection or if a connection could not be assigned. Because
+ // interceptors operate at the application layer, RPC operations may
+ // continue on the ClientStream even after done has been called. The
+ // caller must ensure done is non-nil.
+ //
+ // To ensure RPC completion notifications propagate through the entire
+ // interceptor chain, implementations must ensure that the done function
+ // passed to the delegate newStream invokes the done function passed to
+ // NewStream.
NewStream(ctx context.Context, ri RPCInfo, done func(), newStream func(ctx context.Context, done func()) (ClientStream, error)) (ClientStream, error)
+ // Close closes the interceptor. Once called, no new calls to NewStream are
+ // accepted. Ongoing calls to NewStream are allowed to complete.
+ Close()
}
// ServerInterceptor is an interceptor for incoming RPC's on gRPC server side.
@@ -123,6 +136,9 @@ type ServerInterceptor interface {
// information about connection RPC was received on, and HTTP Headers. This
// information will be piped into context.
AllowRPC(ctx context.Context) error // TODO: Make this a real interceptor for filters such as rate limiting.
+ // Close closes the interceptor. Once called, no new calls to NewStream are
+ // accepted. Ongoing calls to NewStream are allowed to complete.
+ Close()
}
type csKeyType string
diff --git a/vendor/google.golang.org/grpc/internal/stats/labels.go b/vendor/google.golang.org/grpc/internal/stats/labels.go
index fd33af51a..5ea898cb5 100644
--- a/vendor/google.golang.org/grpc/internal/stats/labels.go
+++ b/vendor/google.golang.org/grpc/internal/stats/labels.go
@@ -19,24 +19,56 @@
// Package stats provides internal stats related functionality.
package stats
-import "context"
+import (
+ "context"
+ "maps"
+)
-// Labels are the labels for metrics.
-type Labels struct {
- // TelemetryLabels are the telemetry labels to record.
- TelemetryLabels map[string]string
+// LabelCallback is a function that is executed when telemetry
+// label keys are updated.
+type LabelCallback func(map[string]string)
+type telemetryLabelCallbackKey struct{}
+
+// UpdateLabels executes registered telemetry callbacks with the update labels. Labels
+// are copied before being processed by any callbacks to ensure mutations are not
+// shared among derived contexts.
+//
+// It is the responsibility of the registrant to handle conflicts or label resets.
+func UpdateLabels(ctx context.Context, update map[string]string) {
+ executeTelemetryLabelCallbacks(ctx, update)
}
-type labelsKey struct{}
+// RegisterTelemetryLabelCallback registers a callback function that is executed whenever
+// telemetry labels are updated.
+func RegisterTelemetryLabelCallback(ctx context.Context, callback LabelCallback) context.Context {
+ if callback == nil {
+ return ctx
+ }
+
+ callbacks, ok := ctx.Value(telemetryLabelCallbackKey{}).([]LabelCallback)
+ if !ok {
+ return context.WithValue(ctx, telemetryLabelCallbackKey{}, []LabelCallback{callback})
+ }
+ return context.WithValue(ctx, telemetryLabelCallbackKey{}, append(append([]LabelCallback(nil), callbacks...), callback))
-// GetLabels returns the Labels stored in the context, or nil if there is one.
-func GetLabels(ctx context.Context) *Labels {
- labels, _ := ctx.Value(labelsKey{}).(*Labels)
- return labels
}
-// SetLabels sets the Labels in the context.
-func SetLabels(ctx context.Context, labels *Labels) context.Context {
- // could also append
- return context.WithValue(ctx, labelsKey{}, labels)
+// executeTelemetryLabelCallback runs the registered callbacks in the order they were
+// registered on the context with the provided labels. If no callbacks are registered
+// it does nothing.
+//
+// To ensure callbacks do not mutate the state of the provided label map it is copied
+// before execution.
+func executeTelemetryLabelCallbacks(ctx context.Context, labels map[string]string) {
+ callbacks, ok := ctx.Value(telemetryLabelCallbackKey{}).([]LabelCallback)
+ if !ok {
+ return
+ }
+
+ labelsCopy := map[string]string{}
+ maps.Copy(labelsCopy, labels)
+ for _, callback := range callbacks {
+ callback(labelsCopy)
+ }
+
}
diff --git a/vendor/google.golang.org/grpc/internal/transport/client_stream.go b/vendor/google.golang.org/grpc/internal/transport/client_stream.go
index cd8152ef1..ad382b0fd 100644
--- a/vendor/google.golang.org/grpc/internal/transport/client_stream.go
+++ b/vendor/google.golang.org/grpc/internal/transport/client_stream.go
@@ -19,6 +19,7 @@
package transport
import (
+ "fmt"
"sync/atomic"
"golang.org/x/net/http2"
@@ -28,6 +29,12 @@ import (
"google.golang.org/grpc/status"
)
+// nonGRPCDataMaxLen is the maximum length of nonGRPCDataBuf.
+//
+// NOTE: If changed this value, you MUST update the corresponding test in:
+// - /test/end2end_test.go:TestHTTPServerSendsNonGRPCHeaderSurfaceFurtherData
+const nonGRPCDataMaxLen = 1024
+
// ClientStream implements streaming functionality for a gRPC client.
type ClientStream struct {
Stream // Embed for common stream functionality.
@@ -46,7 +53,11 @@ type ClientStream struct {
// headerValid indicates whether a valid header was received. Only
// meaningful after headerChan is closed (always call waitOnHeader() before
// reading its value).
- headerValid bool
+ headerValid bool
+
+ nonGRPCStatus *status.Status // the initial status from the non-gRPC response header, finalized with collected data before closing.
+ nonGRPCDataBuf []byte // stores the data of a non-gRPC response.
+
noHeaders bool // set if the client never received headers (set only after the stream is done).
headerChanClosed uint32 // set when headerChan is closed. Used to avoid closing headerChan multiple times.
bytesReceived atomic.Bool // indicates whether any bytes have been received on this stream
@@ -54,6 +65,29 @@ type ClientStream struct {
statsHandler stats.Handler // nil for internal streams (e.g., health check, ORCA) where telemetry is not supported.
}
+func (s *ClientStream) startNonGRPCDataCollection(st *status.Status) {
+ s.nonGRPCStatus = st
+ s.nonGRPCDataBuf = make([]byte, 0, nonGRPCDataMaxLen)
+}
+
+// finalizeNonGRPCStatus builds the terminal status by appending the collected
+// response body to the original non-gRPC status message.
+func (s *ClientStream) finalizeNonGRPCStatus() *status.Status {
+ msg := fmt.Sprintf("%s\ndata: %q", s.nonGRPCStatus.Message(), s.nonGRPCDataBuf)
+ return status.New(s.nonGRPCStatus.Code(), msg)
+}
+
+// handleNonGRPCData collects non-gRPC body from the given data frame.
+// It returns non-nil value when the stream should be closed with it.
+func (s *ClientStream) handleNonGRPCData(f *parsedDataFrame) *status.Status {
+ n := min(f.data.Len(), nonGRPCDataMaxLen-len(s.nonGRPCDataBuf))
+ s.nonGRPCDataBuf = append(s.nonGRPCDataBuf, f.data.ReadOnlyData()[0:n]...)
+ if len(s.nonGRPCDataBuf) >= nonGRPCDataMaxLen || f.StreamEnded() {
+ return s.finalizeNonGRPCStatus()
+ }
+ return nil
+}
+
// Read reads an n byte message from the input stream.
func (s *ClientStream) Read(n int) (mem.BufferSlice, error) {
b, err := s.Stream.read(n)
diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
index 7efa52478..c5a76b70a 100644
--- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
+++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
@@ -956,39 +956,16 @@ func (l *loopyWriter) processData() (bool, error) {
// from data is copied to h to make as big as the maximum possible HTTP2 frame
// size.
- if len(dataItem.h) == 0 && reader.Remaining() == 0 { // Empty data frame
- // Client sends out empty data frame with endStream = true
- if err := l.framer.writeData(dataItem.streamID, dataItem.endStream, nil); err != nil {
- return false, err
- }
- str.itl.dequeue() // remove the empty data item from stream
- reader.Close()
- if str.itl.isEmpty() {
- str.state = empty
- } else if trailer, ok := str.itl.peek().(*headerFrame); ok { // the next item is trailers.
- if err := l.writeHeader(trailer.streamID, trailer.endStream, trailer.hf, trailer.onWrite); err != nil {
- return false, err
- }
- if err := l.cleanupStreamHandler(trailer.cleanup); err != nil {
- return false, err
- }
- } else {
- l.activeStreams.enqueue(str)
- }
- return false, nil
- }
-
+ isEmpty := len(dataItem.h) == 0 && reader.Remaining() == 0
// Figure out the maximum size we can send
maxSize := http2MaxFrameLen
- if strQuota := int(l.oiws) - str.bytesOutStanding; strQuota <= 0 { // stream-level flow control.
+ strQuota := int(l.oiws) - str.bytesOutStanding
+ if strQuota <= 0 && !isEmpty { // stream-level flow control.
str.state = waitingOnStreamQuota
return false, nil
- } else if maxSize > strQuota {
- maxSize = strQuota
- }
- if maxSize > int(l.sendQuota) { // connection-level flow control.
- maxSize = int(l.sendQuota)
}
+ maxSize = min(maxSize, max(strQuota, 0))
+ maxSize = min(maxSize, int(l.sendQuota)) // connection-level flow control.
// Compute how much of the header and data we can send within quota and max frame length
hSize := min(maxSize, len(dataItem.h))
dSize := min(maxSize-hSize, reader.Remaining())
@@ -1039,19 +1016,23 @@ func (l *loopyWriter) processData() (bool, error) {
reader.Close()
str.itl.dequeue()
}
+ return false, l.updateStreamAfterWrite(str)
+}
+
+func (l *loopyWriter) updateStreamAfterWrite(str *outStream) error {
if str.itl.isEmpty() {
str.state = empty
- } else if trailer, ok := str.itl.peek().(*headerFrame); ok { // The next item is trailers.
+ } else if trailer, ok := str.itl.peek().(*headerFrame); ok { // the next item is trailers.
if err := l.writeHeader(trailer.streamID, trailer.endStream, trailer.hf, trailer.onWrite); err != nil {
- return false, err
+ return err
}
if err := l.cleanupStreamHandler(trailer.cleanup); err != nil {
- return false, err
+ return err
}
} else if int(l.oiws)-str.bytesOutStanding <= 0 { // Ran out of stream quota.
str.state = waitingOnStreamQuota
} else { // Otherwise add it back to the list of active streams.
l.activeStreams.enqueue(str)
}
- return false, nil
+ return nil
}
diff --git a/vendor/google.golang.org/grpc/internal/transport/flowcontrol.go b/vendor/google.golang.org/grpc/internal/transport/flowcontrol.go
index 7cfbc9637..98cef9ec2 100644
--- a/vendor/google.golang.org/grpc/internal/transport/flowcontrol.go
+++ b/vendor/google.golang.org/grpc/internal/transport/flowcontrol.go
@@ -115,7 +115,6 @@ func (f *trInFlow) getSize() uint32 {
return atomic.LoadUint32(&f.effectiveWindowSize)
}
-// TODO(mmukhi): Simplify this code.
// inFlow deals with inbound flow control
type inFlow struct {
mu sync.Mutex
@@ -174,14 +173,14 @@ func (f *inFlow) maybeAdjust(n uint32) uint32 {
// onData is invoked when some data frame is received. It updates pendingData.
func (f *inFlow) onData(n uint32) error {
f.mu.Lock()
+ defer f.mu.Unlock()
+
f.pendingData += n
if f.pendingData+f.pendingUpdate > f.limit+f.delta {
limit := f.limit
rcvd := f.pendingData + f.pendingUpdate
- f.mu.Unlock()
return fmt.Errorf("received %d-bytes data exceeding the limit %d bytes", rcvd, limit)
}
- f.mu.Unlock()
return nil
}
@@ -189,8 +188,9 @@ func (f *inFlow) onData(n uint32) error {
// to be sent to the peer.
func (f *inFlow) onRead(n uint32) uint32 {
f.mu.Lock()
+ defer f.mu.Unlock()
+
if f.pendingData == 0 {
- f.mu.Unlock()
return 0
}
f.pendingData -= n
@@ -205,9 +205,7 @@ func (f *inFlow) onRead(n uint32) uint32 {
if f.pendingUpdate >= f.limit/4 {
wu := f.pendingUpdate
f.pendingUpdate = 0
- f.mu.Unlock()
return wu
}
- f.mu.Unlock()
return 0
}
diff --git a/vendor/google.golang.org/grpc/internal/transport/handler_server.go b/vendor/google.golang.org/grpc/internal/transport/handler_server.go
index 7ab3422b8..a8356c9ad 100644
--- a/vendor/google.golang.org/grpc/internal/transport/handler_server.go
+++ b/vendor/google.golang.org/grpc/internal/transport/handler_server.go
@@ -479,8 +479,8 @@ func (ht *serverHandlerTransport) runStream() {
func (ht *serverHandlerTransport) incrMsgRecv() {}
-func (ht *serverHandlerTransport) Drain(string) {
- panic("Drain() is not implemented")
+func (ht *serverHandlerTransport) Drain(s string) {
+ ht.Close(errors.New(s))
}
// mapRecvMsgError returns the non-nil err into the appropriate
diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go
index c943503f3..133f5d706 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go
@@ -39,6 +39,7 @@ import (
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/channelz"
icredentials "google.golang.org/grpc/internal/credentials"
+ "google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/internal/grpcsync"
"google.golang.org/grpc/internal/grpcutil"
@@ -134,6 +135,8 @@ type http2Client struct {
// goAwayDebugMessage contains a detailed human readable string about a
// GoAway frame, useful for error messages.
goAwayDebugMessage string
+ // goAwayCode records the http2.ErrCode received with the GoAway frame.
+ goAwayCode http2.ErrCode
// A condition variable used to signal when the keepalive goroutine should
// go dormant. The condition for dormancy is based on the number of active
// streams and the `PermitWithoutStream` keepalive client parameter. And
@@ -147,7 +150,7 @@ type http2Client struct {
channelz *channelz.Socket
- onClose func(GoAwayReason)
+ onClose OnCloseFunc
bufferPool mem.BufferPool
@@ -204,7 +207,7 @@ func isTemporary(err error) bool {
// NewHTTP2Client constructs a connected ClientTransport to addr based on HTTP2
// and starts to receive messages on it. Non-nil error returns if construction
// fails.
-func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts ConnectOptions, onClose func(GoAwayReason)) (_ ClientTransport, err error) {
+func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts ConnectOptions, onClose OnCloseFunc) (_ ClientTransport, err error) {
scheme := "http"
ctx, cancel := context.WithCancel(ctx)
defer func() {
@@ -316,7 +319,13 @@ func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
}
writeBufSize := opts.WriteBufferSize
readBufSize := opts.ReadBufferSize
+ // The default header list size is moving from 16MB to 8KB. The 8KB limit
+ // is only used if Enable8KBDefaultHeaderListSize is true; otherwise, the
+ // old 16MB default is used. User-specified options always take precedence.
maxHeaderListSize := defaultClientMaxHeaderListSize
+ if envconfig.Enable8KBDefaultHeaderListSize {
+ maxHeaderListSize = upcomingDefaultHeaderListSize
+ }
if opts.MaxHeaderListSize != nil {
maxHeaderListSize = *opts.MaxHeaderListSize
}
@@ -877,8 +886,8 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr, handler s
return false
}
}
- if sz > int64(upcomingDefaultHeaderListSize) {
- t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In a future release, this will be restricted to %d bytes.", sz, upcomingDefaultHeaderListSize, upcomingDefaultHeaderListSize)
+ if !envconfig.Enable8KBDefaultHeaderListSize && sz > int64(upcomingDefaultHeaderListSize) {
+ t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In release v1.82.0, GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE will be enabled by default, enforcing this limit.", sz, upcomingDefaultHeaderListSize)
}
return true
}
@@ -1015,7 +1024,7 @@ func (t *http2Client) Close(err error) {
// Call t.onClose ASAP to prevent the client from attempting to create new
// streams.
if t.state != draining {
- t.onClose(GoAwayInvalid)
+ t.onClose(GoAwayInfo{Reason: GoAwayInvalid, GoAwayCode: http2.ErrCodeNo, Err: err})
}
t.state = closing
streams := t.activeStreams
@@ -1086,7 +1095,7 @@ func (t *http2Client) GracefulClose() {
if t.logger.V(logLevel) {
t.logger.Infof("GracefulClose called")
}
- t.onClose(GoAwayInvalid)
+ t.onClose(GoAwayInfo{Reason: GoAwayInvalid, GoAwayCode: http2.ErrCodeNo})
t.state = draining
active := len(t.activeStreams)
t.mu.Unlock()
@@ -1222,6 +1231,23 @@ func (t *http2Client) handleData(f *parsedDataFrame) {
t.closeStream(s, io.EOF, true, http2.ErrCodeFlowControl, status.New(codes.Internal, err.Error()), nil, false)
return
}
+
+ if s.nonGRPCStatus != nil {
+ // The frame should be handled as a non-gRPC response body
+ st := s.handleNonGRPCData(f)
+ if st != nil {
+ t.closeStream(s, st.Err(), true, http2.ErrCodeProtocol, st, nil, true)
+ return
+ }
+ if w := s.fc.onRead(size); w > 0 {
+ t.controlBuf.put(&outgoingWindowUpdate{
+ streamID: s.id,
+ increment: w,
+ })
+ }
+ return
+ }
+
dataLen := f.data.Len()
if f.Header().Flags.Has(http2.FlagDataPadded) {
if w := s.fc.onRead(size - uint32(dataLen)); w > 0 {
@@ -1236,7 +1262,10 @@ func (t *http2Client) handleData(f *parsedDataFrame) {
// The server has closed the stream without sending trailers. Record that
// the read direction is closed, and set the status appropriately.
if f.StreamEnded() {
- t.closeStream(s, io.EOF, false, http2.ErrCodeNo, status.New(codes.Internal, "server closed the stream without sending trailers"), nil, true)
+ // If client received END_STREAM from server while stream was still
+ // active, send RST_STREAM.
+ rstStream := s.getState() == streamActive
+ t.closeStream(s, io.EOF, rstStream, http2.ErrCodeNo, status.New(codes.Internal, "server closed the stream without sending trailers"), nil, true)
}
}
@@ -1372,7 +1401,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) error {
// draining, to allow the client to stop attempting to create streams
// before disallowing new streams on this connection.
if t.state != draining {
- t.onClose(t.goAwayReason)
+ t.onClose(GoAwayInfo{Reason: t.goAwayReason, GoAwayCode: t.goAwayCode})
t.state = draining
}
}
@@ -1422,6 +1451,7 @@ func (t *http2Client) setGoAwayReason(f *http2.GoAwayFrame) {
} else {
t.goAwayDebugMessage = fmt.Sprintf("code: %s, debug data: %q", f.ErrCode, string(f.DebugData()))
}
+ t.goAwayCode = f.ErrCode
}
func (t *http2Client) GetGoAwayReason() (GoAwayReason, string) {
@@ -1462,6 +1492,17 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
return
}
+ // If we are collecting non-gRPC response data and receive a trailing
+ // HEADERS frame with END_STREAM, finalize the buffered data and close
+ // the stream.
+ if s.nonGRPCStatus != nil {
+ if endStream {
+ st := s.finalizeNonGRPCStatus()
+ t.closeStream(s, st.Err(), true, http2.ErrCodeProtocol, st, nil, true)
+ }
+ return
+ }
+
var (
// If a gRPC Response-Headers has already been received, then it means
// that the peer is speaking gRPC and we are in gRPC mode.
@@ -1562,7 +1603,12 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
}
se := status.New(grpcErrorCode, strings.Join(errs, "; "))
- t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
+ if endStream {
+ t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, true)
+ return
+ }
+
+ s.startNonGRPCDataCollection(se)
return
}
diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go
index 3a8c36e4f..1acd44be4 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go
@@ -38,11 +38,13 @@ import (
"google.golang.org/protobuf/proto"
"google.golang.org/grpc/internal"
+ "google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/internal/grpcutil"
"google.golang.org/grpc/internal/pretty"
istatus "google.golang.org/grpc/internal/status"
"google.golang.org/grpc/internal/syscall"
+ transportinternal "google.golang.org/grpc/internal/transport/internal"
"google.golang.org/grpc/mem"
"google.golang.org/grpc/codes"
@@ -165,7 +167,13 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
}
writeBufSize := config.WriteBufferSize
readBufSize := config.ReadBufferSize
+ // The default header list size is moving from 16MB to 8KB. The 8KB limit
+ // is only used if Enable8KBDefaultHeaderListSize is true; otherwise, the
+ // old 16MB default is used. User-specified options always take precedence.
maxHeaderListSize := defaultServerMaxHeaderListSize
+ if envconfig.Enable8KBDefaultHeaderListSize {
+ maxHeaderListSize = upcomingDefaultHeaderListSize
+ }
if config.MaxHeaderListSize != nil {
maxHeaderListSize = *config.MaxHeaderListSize
}
@@ -948,8 +956,8 @@ func (t *http2Server) checkForHeaderListSize(hf []hpack.HeaderField) bool {
return false
}
}
- if sz > int64(upcomingDefaultHeaderListSize) {
- t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In a future release, this will be restricted to %d bytes.", sz, upcomingDefaultHeaderListSize, upcomingDefaultHeaderListSize)
+ if !envconfig.Enable8KBDefaultHeaderListSize && sz > int64(upcomingDefaultHeaderListSize) {
+ t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In release v1.82.0, GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE will be enabled by default, enforcing this limit.", sz, upcomingDefaultHeaderListSize)
}
return true
}
@@ -1441,14 +1449,14 @@ func (t *http2Server) socketMetrics() *channelz.EphemeralSocketMetrics {
func (t *http2Server) incrMsgSent() {
if channelz.IsOn() {
t.channelz.SocketMetrics.MessagesSent.Add(1)
- t.channelz.SocketMetrics.LastMessageSentTimestamp.Add(1)
+ t.channelz.SocketMetrics.LastMessageSentTimestamp.Store(transportinternal.TimeNowFunc())
}
}
func (t *http2Server) incrMsgRecv() {
if channelz.IsOn() {
t.channelz.SocketMetrics.MessagesReceived.Add(1)
- t.channelz.SocketMetrics.LastMessageReceivedTimestamp.Add(1)
+ t.channelz.SocketMetrics.LastMessageReceivedTimestamp.Store(transportinternal.TimeNowFunc())
}
}
diff --git a/vendor/google.golang.org/grpc/internal/transport/http_util.go b/vendor/google.golang.org/grpc/internal/transport/http_util.go
index 5bbb641ad..c34975ffe 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http_util.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http_util.go
@@ -36,6 +36,9 @@ import (
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"
"google.golang.org/grpc/codes"
+ "google.golang.org/grpc/internal/envconfig"
+ imem "google.golang.org/grpc/internal/mem"
+ "google.golang.org/grpc/internal/transport/readyreader"
"google.golang.org/grpc/mem"
)
@@ -296,7 +299,7 @@ func decodeGrpcMessageUnchecked(msg string) string {
}
type bufWriter struct {
- pool *sync.Pool
+ pool *imem.SimpleBufferPool
buf []byte
offset int
batchSize int
@@ -304,7 +307,7 @@ type bufWriter struct {
err error
}
-func newBufWriter(conn io.Writer, batchSize int, pool *sync.Pool) *bufWriter {
+func newBufWriter(conn io.Writer, batchSize int, pool *imem.SimpleBufferPool) *bufWriter {
w := &bufWriter{
batchSize: batchSize,
conn: conn,
@@ -326,7 +329,7 @@ func (w *bufWriter) Write(b []byte) (int, error) {
return n, toIOError(err)
}
if w.buf == nil {
- b := w.pool.Get().(*[]byte)
+ b := w.pool.Get(w.batchSize)
w.buf = *b
}
written := 0
@@ -407,22 +410,32 @@ type framer struct {
errDetail error
}
-var writeBufferPoolMap = make(map[int]*sync.Pool)
-var writeBufferMutex sync.Mutex
+var ioBufferPoolMap = make(map[int]*imem.SimpleBufferPool)
+var ioBufferMutex sync.Mutex
+
+func bufferedReader(r io.Reader, bufSize int) io.Reader {
+ if bufSize <= 0 {
+ return r
+ }
+ if envconfig.EnableHTTPFramerReadBufferPooling {
+ if rr := readyreader.NewNonBlocking(r); rr != nil {
+ readPool := ioBufferPool(bufSize)
+ return readyreader.NewBuffered(rr, bufSize, readPool)
+ }
+ }
+ return bufio.NewReaderSize(r, bufSize)
+}
func newFramer(conn io.ReadWriter, writeBufferSize, readBufferSize int, sharedWriteBuffer bool, maxHeaderListSize uint32, memPool mem.BufferPool) *framer {
if writeBufferSize < 0 {
writeBufferSize = 0
}
- var r io.Reader = conn
- if readBufferSize > 0 {
- r = bufio.NewReaderSize(r, readBufferSize)
- }
- var pool *sync.Pool
+ r := bufferedReader(conn, readBufferSize)
+ var writePool *imem.SimpleBufferPool
if sharedWriteBuffer {
- pool = getWriteBufferPool(writeBufferSize)
+ writePool = ioBufferPool(writeBufferSize)
}
- w := newBufWriter(conn, writeBufferSize, pool)
+ w := newBufWriter(conn, writeBufferSize, writePool)
f := &framer{
writer: w,
fr: http2.NewFramer(w, r),
@@ -578,20 +591,15 @@ func (df *parsedDataFrame) Header() http2.FrameHeader {
return df.FrameHeader
}
-func getWriteBufferPool(size int) *sync.Pool {
- writeBufferMutex.Lock()
- defer writeBufferMutex.Unlock()
- pool, ok := writeBufferPoolMap[size]
+func ioBufferPool(size int) *imem.SimpleBufferPool {
+ ioBufferMutex.Lock()
+ defer ioBufferMutex.Unlock()
+ pool, ok := ioBufferPoolMap[size]
if ok {
return pool
}
- pool = &sync.Pool{
- New: func() any {
- b := make([]byte, size)
- return &b
- },
- }
- writeBufferPoolMap[size] = pool
+ pool = imem.NewDirtySimplePool()
+ ioBufferPoolMap[size] = pool
return pool
}
diff --git a/vendor/google.golang.org/grpc/internal/grpcutil/regex.go b/vendor/google.golang.org/grpc/internal/transport/internal/internal.go
similarity index 59%
rename from vendor/google.golang.org/grpc/internal/grpcutil/regex.go
rename to vendor/google.golang.org/grpc/internal/transport/internal/internal.go
index 7a092b2b8..a7c7c7d5a 100644
--- a/vendor/google.golang.org/grpc/internal/grpcutil/regex.go
+++ b/vendor/google.golang.org/grpc/internal/transport/internal/internal.go
@@ -1,6 +1,6 @@
/*
*
- * Copyright 2021 gRPC authors.
+ * Copyright 2026 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,10 @@
*
*/
-package grpcutil
+// Package internal contains functionality internal to the transport package.
+package internal
-import "regexp"
-
-// FullMatchWithRegex returns whether the full text matches the regex provided.
-func FullMatchWithRegex(re *regexp.Regexp, text string) bool {
- if len(text) == 0 {
- return re.MatchString(text)
- }
- re.Longest()
- rem := re.FindString(text)
- return len(rem) == len(text)
-}
+// TimeNowFunc is a variable that can be set to override the default behavior of
+// getting the current time in nanoseconds. It is used in transport code to set
+// channelz timestamps, and is exposed here for testing purposes.
+var TimeNowFunc func() int64
diff --git a/vendor/google.golang.org/grpc/internal/transport/readyreader/raw_conn_linux.go b/vendor/google.golang.org/grpc/internal/transport/readyreader/raw_conn_linux.go
new file mode 100644
index 000000000..56906c35b
--- /dev/null
+++ b/vendor/google.golang.org/grpc/internal/transport/readyreader/raw_conn_linux.go
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright 2026 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package readyreader
+
+import "syscall"
+
+func isRawConnSupported() bool {
+ return true
+}
+
+// sysRead uses the standard syscall package rather than the modern unix package
+// to avoid triggering the race detector. Because both packages perform sync
+// operations on a local variable to satisfy the race detector, mixing them
+// for read and write syscalls causes data races. We use syscall here to remain
+// consistent with net.Conn implementations in standard library.
+func sysRead(fd uintptr, p []byte) (int, error) {
+ return syscall.Read(int(fd), p)
+}
+
+// wouldBlock checks standard Unix non-blocking errors.
+func wouldBlock(err error) bool {
+ return err == syscall.EAGAIN || err == syscall.EWOULDBLOCK
+}
diff --git a/vendor/google.golang.org/grpc/internal/transport/readyreader/raw_conn_nonlinux.go b/vendor/google.golang.org/grpc/internal/transport/readyreader/raw_conn_nonlinux.go
new file mode 100644
index 000000000..4d1f33006
--- /dev/null
+++ b/vendor/google.golang.org/grpc/internal/transport/readyreader/raw_conn_nonlinux.go
@@ -0,0 +1,35 @@
+//go:build !linux
+
+/*
+ *
+ * Copyright 2026 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package readyreader
+
+func isRawConnSupported() bool {
+ return false
+}
+
+// sysRead is not implemented. Support can be added in the future if necessary.
+func sysRead(uintptr, []byte) (int, error) {
+ panic("RawConn functionality is not implemented for non-unix platforms.")
+}
+
+// wouldBlock is not implemented. Support can be added in the future if necessary.
+func wouldBlock(error) bool {
+ panic("RawConn functionality is not implemented for non-unix platforms.")
+}
diff --git a/vendor/google.golang.org/grpc/internal/transport/readyreader/ready_reader.go b/vendor/google.golang.org/grpc/internal/transport/readyreader/ready_reader.go
new file mode 100644
index 000000000..250a300c7
--- /dev/null
+++ b/vendor/google.golang.org/grpc/internal/transport/readyreader/ready_reader.go
@@ -0,0 +1,253 @@
+/*
+ *
+ * Copyright 2026 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Package readyreader provides utilities to perform non-memory-pinning reads.
+package readyreader
+
+import (
+ "io"
+ "net"
+ "syscall"
+
+ "google.golang.org/grpc/mem"
+)
+
+// Reader is an optional interface that can be implemented by [net.Conn]
+// implementations to enable gRPC to perform non-memory-pinning reads.
+type Reader interface {
+ // ReadOnReady waits for data to arrive, fetches a buffer, and performs a
+ // read. When the underlying IO is readable, it allocates a buffer of size
+ // bufSize from the pool and reads up to bufSize bytes into the buffer.
+ //
+ // It returns a pointer to the buffer so it can be returned to the pool
+ // later, the number of bytes read, and an error.
+ //
+ // Callers should always process the n > 0 bytes returned before considering
+ // the error. Doing so correctly handles I/O errors that happen after
+ // reading some bytes, as well as both of the allowed EOF behaviors.
+ ReadOnReady(bufSize int, pool mem.BufferPool) (b *[]byte, n int, err error)
+}
+
+// nonBlockingReader is optimized for non-memory-pinning reads using the RawConn
+// interface.
+type nonBlockingReader struct {
+ raw syscall.RawConn
+ // The following fields are stored as field to avoid heap allocations.
+ state readState
+ doRead func(fd uintptr) bool
+}
+
+type readState struct {
+ // Request params.
+ bufSize int
+ pool mem.BufferPool
+
+ // Response params.
+ readError error
+ bytesRead int
+ buf *[]byte
+}
+
+// NewNonBlocking returns a ReadyReader if the passed reader supports
+// non-memory-pinning reads, else nil.
+func NewNonBlocking(r io.Reader) Reader {
+ if rr, ok := r.(Reader); ok {
+ return rr
+ }
+ if !isRawConnSupported() {
+ return nil
+ }
+ // We restrict the types before asserting syscall.Conn. The credentials
+ // package may return a wrapper that implements syscall.Conn by embedding
+ // both the raw connection and the encrypted connection. If the code
+ // attempts to read directly from the raw syscall.RawConn, it would read
+ // encrypted data.
+ switch r.(type) {
+ case *net.TCPConn, *net.UDPConn, *net.UnixConn, *net.IPConn:
+ default:
+ return nil
+ }
+ sysConn, ok := r.(syscall.Conn)
+ if !ok {
+ return nil
+ }
+ raw, err := sysConn.SyscallConn()
+ if err != nil {
+ return nil
+ }
+ rr := &nonBlockingReader{raw: raw}
+ rr.doRead = func(fd uintptr) bool {
+ s := &rr.state
+
+ s.buf = s.pool.Get(s.bufSize)
+ s.bytesRead, s.readError = sysRead(fd, *s.buf)
+
+ if s.readError != nil {
+ s.pool.Put(s.buf)
+ s.buf = nil
+ }
+ return !wouldBlock(s.readError)
+ }
+ return rr
+}
+
+func (c *nonBlockingReader) ReadOnReady(bufSize int, pool mem.BufferPool) (*[]byte, int, error) {
+ c.state = readState{
+ pool: pool,
+ bufSize: bufSize,
+ }
+ err := c.raw.Read(c.doRead)
+
+ buf := c.state.buf
+ n := c.state.bytesRead
+ readErr := c.state.readError
+ c.state = readState{}
+
+ if err != nil {
+ if buf != nil {
+ pool.Put(buf)
+ }
+ return nil, 0, err
+ }
+ if readErr != nil {
+ // buffer is already released in the callback.
+ return nil, 0, readErr
+ }
+ if n == 0 {
+ // syscall.Read doesn't consider a graceful socket closure to be an
+ // error condition, but Go's io.Reader expects an EOF error.
+ pool.Put(buf)
+ return nil, 0, io.EOF
+ }
+ return buf, n, nil
+}
+
+type blockingReader struct {
+ reader io.Reader
+}
+
+func (c *blockingReader) ReadOnReady(bufSize int, pool mem.BufferPool) (*[]byte, int, error) {
+ buf := pool.Get(bufSize)
+ n, err := c.reader.Read(*buf)
+ if err != nil {
+ pool.Put(buf)
+ return nil, 0, err
+ }
+ return buf, n, nil
+}
+
+// New detects if [syscall.RawConn] is available for non-memory-pinning reads.
+// If [syscall.RawConn] is unavailable, it falls back to using the simpler
+// [io.Reader] interface for reads.
+func New(r io.Reader) Reader {
+ if r := NewNonBlocking(r); r != nil {
+ return r
+ }
+ return &blockingReader{reader: r}
+}
+
+// bufReadyReader implements buffering for a ReadyReader object.
+// A new bufReadyReader is created by calling [NewBuffered].
+type bufReadyReader struct {
+ buf *[]byte
+ pool mem.BufferPool
+ bufSize int
+ rd Reader // reader provided by the caller
+ r, w int // buf read and write positions
+ err error
+ constPool constBufferPool // stored as a field to avoid heap allocations.
+}
+
+// NewBuffered returns a new [io.Reader] with a buffer of the specified size
+// which is allocated from the provided pool.
+func NewBuffered(rd Reader, size int, pool mem.BufferPool) io.Reader {
+ return &bufReadyReader{
+ rd: rd,
+ pool: pool,
+ bufSize: size,
+ }
+}
+
+func (b *bufReadyReader) readErr() error {
+ err := b.err
+ b.err = nil
+ return err
+}
+
+func (b *bufReadyReader) buffered() int { return b.w - b.r }
+
+// Read reads data into p. It returns the number of bytes read into p. The
+// bytes are taken from at most one Read on the underlying [ReadyReader],
+// hence n may be less than len(p). If the underlying [ReadyReader] can return
+// a non-zero count with io.EOF, then this Read method can do so as well; see
+// the [io.Reader] docs.
+func (b *bufReadyReader) Read(p []byte) (n int, err error) {
+ n = len(p)
+ if n == 0 {
+ if b.buffered() > 0 {
+ return 0, nil
+ }
+ return 0, b.readErr()
+ }
+ if b.r == b.w {
+ if b.err != nil {
+ return 0, b.readErr()
+ }
+ if len(p) >= b.bufSize {
+ // Large read, empty buffer.
+ // Read directly into p to avoid copy.
+ b.constPool.buffer = p
+ _, n, b.err = b.rd.ReadOnReady(len(p), &b.constPool)
+ return n, b.readErr()
+ }
+ // One read.
+ b.r = 0
+ b.w = 0
+ b.buf, n, b.err = b.rd.ReadOnReady(b.bufSize, b.pool)
+ if n == 0 {
+ if b.buf != nil {
+ b.pool.Put(b.buf)
+ b.buf = nil
+ }
+ return 0, b.readErr()
+ }
+ b.w += n
+ }
+
+ // copy as much as we can
+ // b.buf must be non-nil since b.r != b.w.
+ buf := *b.buf
+ n = copy(p, buf[b.r:b.w])
+ b.r += n
+ if b.r == b.w {
+ // Consumed entire buffer, release it.
+ b.pool.Put(b.buf)
+ b.buf = nil
+ }
+ return n, nil
+}
+
+type constBufferPool struct {
+ buffer []byte
+}
+
+func (p *constBufferPool) Get(int) *[]byte {
+ return &p.buffer
+}
+
+func (p *constBufferPool) Put(*[]byte) {}
diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go
index b86094da9..6dfae3984 100644
--- a/vendor/google.golang.org/grpc/internal/transport/transport.go
+++ b/vendor/google.golang.org/grpc/internal/transport/transport.go
@@ -31,9 +31,11 @@ import (
"sync/atomic"
"time"
+ "golang.org/x/net/http2"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/internal/channelz"
+ "google.golang.org/grpc/internal/transport/internal"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/mem"
"google.golang.org/grpc/metadata"
@@ -45,6 +47,10 @@ import (
const logLevel = 2
+func init() {
+ internal.TimeNowFunc = func() int64 { return time.Now().UnixNano() }
+}
+
// recvMsg represents the received msg from the transport. All transport
// protocol specific info has been removed.
type recvMsg struct {
@@ -742,6 +748,22 @@ const (
GoAwayTooManyPings GoAwayReason = 2
)
+// GoAwayInfo contains metadata about why a connection was closed.
+type GoAwayInfo struct {
+ // Reason is the parsed reason for an HTTP/2 GOAWAY frame.
+ Reason GoAwayReason
+ // GoAwayCode is the raw HTTP/2 error code received in a GOAWAY frame.
+ GoAwayCode http2.ErrCode
+ // Err is the underlying error that caused the connection to close. It is
+ // populated if the connection was closed due to a socket error or context
+ // cancellation without receiving a GOAWAY frame. If the connection was
+ // closed due to a GOAWAY frame, this field will be nil.
+ Err error
+}
+
+// OnCloseFunc is a callback invoked when a ClientTransport closes.
+type OnCloseFunc func(GoAwayInfo)
+
// ContextErr converts the error from context package into a status error.
func ContextErr(err error) error {
switch err {
diff --git a/vendor/google.golang.org/grpc/mem/buffer_slice.go b/vendor/google.golang.org/grpc/mem/buffer_slice.go
index 084fb19c6..086e9f95d 100644
--- a/vendor/google.golang.org/grpc/mem/buffer_slice.go
+++ b/vendor/google.golang.org/grpc/mem/buffer_slice.go
@@ -165,7 +165,7 @@ func (r *Reader) Close() error {
}
func (r *Reader) freeFirstBufferIfEmpty() bool {
- if len(r.data) == 0 || r.bufferIdx != len(r.data[0].ReadOnlyData()) {
+ if len(r.data) == 0 || r.bufferIdx != r.data[0].Len() {
return false
}
diff --git a/vendor/google.golang.org/grpc/mem/buffers.go b/vendor/google.golang.org/grpc/mem/buffers.go
index db1620e6a..2b410b16e 100644
--- a/vendor/google.golang.org/grpc/mem/buffers.go
+++ b/vendor/google.golang.org/grpc/mem/buffers.go
@@ -53,6 +53,10 @@ type Buffer interface {
Free()
// Len returns the Buffer's size.
Len() int
+ // Slice returns a new Buffer that is a view into this buffer's data
+ // from [start:end). The buffer is not modified. Panics if the buffer
+ // has been freed or if start/end are out of bounds.
+ Slice(start, end int) Buffer
split(n int) (left, right Buffer)
read(buf []byte) (int, Buffer)
@@ -180,6 +184,32 @@ func (b *buffer) Len() int {
return len(b.ReadOnlyData())
}
+func (b *buffer) Slice(start, end int) Buffer {
+ if b.rootBuf == nil {
+ panic("Cannot slice freed buffer")
+ }
+
+ data := b.data[start:end] // access the data to check slice bounds
+
+ if len(data) == 0 {
+ return emptyBuffer{}
+ }
+ if len(data) == len(b.data) {
+ b.Ref()
+ return b
+ }
+ // We are creating a new reference (view) to a portion of the root buffer's
+ // data. Therefore, we must increment the reference count of the root buffer
+ // to ensure the underlying data is not freed while this view is still in
+ // use.
+ b.rootBuf.Ref()
+ s := newBuffer()
+ s.data = data
+ s.rootBuf = b.rootBuf
+ s.refs.Store(1)
+ return s
+}
+
func (b *buffer) split(n int) (Buffer, Buffer) {
if b.rootBuf == nil || b.rootBuf.refs.Add(1) <= 1 {
panic("Cannot split freed buffer")
@@ -240,6 +270,13 @@ func (e emptyBuffer) Len() int {
return 0
}
+func (e emptyBuffer) Slice(start, end int) Buffer {
+ if start != 0 || end != 0 {
+ panic(fmt.Sprintf("slice bounds out of range [%d:%d] with length 0", start, end))
+ }
+ return e
+}
+
func (e emptyBuffer) split(int) (left, right Buffer) {
return e, e
}
@@ -264,6 +301,9 @@ func (s SliceBuffer) Free() {}
// Len is a noop implementation of Len.
func (s SliceBuffer) Len() int { return len(s) }
+// Slice returns a new SliceBuffer that is a view into the receiver from [start:end).
+func (s SliceBuffer) Slice(start, end int) Buffer { return s[start:end] }
+
func (s SliceBuffer) split(n int) (left, right Buffer) {
return s[:n], s[n:]
}
diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go
index 81ced2fbf..21142c64e 100644
--- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go
+++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go
@@ -21,7 +21,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
-// - protoc-gen-go-grpc v1.6.1
+// - protoc-gen-go-grpc v1.6.2
// - protoc v5.27.1
// source: grpc/reflection/v1/reflection.proto
diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
index 18061e8ba..2a3f45d1b 100644
--- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
+++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
@@ -18,7 +18,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
-// - protoc-gen-go-grpc v1.6.1
+// - protoc-gen-go-grpc v1.6.2
// - protoc v5.27.1
// grpc/reflection/v1alpha/reflection.proto is a deprecated file.
diff --git a/vendor/google.golang.org/grpc/rpc_util.go b/vendor/google.golang.org/grpc/rpc_util.go
index ee7f7dead..52f4ea513 100644
--- a/vendor/google.golang.org/grpc/rpc_util.go
+++ b/vendor/google.golang.org/grpc/rpc_util.go
@@ -128,6 +128,16 @@ func NewGZIPDecompressor() Decompressor {
}
func (d *gzipDecompressor) Do(r io.Reader) ([]byte, error) {
+ return d.doWithMaxSize(r, math.MaxInt64)
+}
+
+// doWithMaxSize behaves like Do but caps the size of the decompressed
+// payload at maxMessageSize+1 bytes. The Decompressor interface does not
+// allow extra parameters, so callers inside the package type-assert to
+// *gzipDecompressor to invoke this method directly. The +1 byte makes it
+// possible for the caller to detect that the limit was exceeded and
+// return ResourceExhausted instead of materializing an unbounded payload.
+func (d *gzipDecompressor) doWithMaxSize(r io.Reader, maxMessageSize int64) ([]byte, error) {
var z *gzip.Reader
switch maybeZ := d.pool.Get().(type) {
case nil:
@@ -148,7 +158,11 @@ func (d *gzipDecompressor) Do(r io.Reader) ([]byte, error) {
z.Close()
d.pool.Put(z)
}()
- return io.ReadAll(z)
+ var src io.Reader = z
+ if maxMessageSize < math.MaxInt64 {
+ src = io.LimitReader(z, maxMessageSize+1)
+ }
+ return io.ReadAll(src)
}
func (d *gzipDecompressor) Type() string {
@@ -830,15 +844,15 @@ func compress(in mem.BufferSlice, cp Compressor, compressor encoding.Compressor,
if compressor != nil {
z, err := compressor.Compress(w)
if err != nil {
- return nil, 0, wrapErr(err)
+ return nil, compressionNone, wrapErr(err)
}
for _, b := range in {
if _, err := z.Write(b.ReadOnlyData()); err != nil {
- return nil, 0, wrapErr(err)
+ return nil, compressionNone, wrapErr(err)
}
}
if err := z.Close(); err != nil {
- return nil, 0, wrapErr(err)
+ return nil, compressionNone, wrapErr(err)
}
} else {
// This is obviously really inefficient since it fully materializes the data, but
@@ -848,7 +862,7 @@ func compress(in mem.BufferSlice, cp Compressor, compressor encoding.Compressor,
buf := in.MaterializeToBuffer(pool)
defer buf.Free()
if err := cp.Do(w, buf.ReadOnlyData()); err != nil {
- return nil, 0, wrapErr(err)
+ return nil, compressionNone, wrapErr(err)
}
}
return out, compressionMade, nil
@@ -971,7 +985,20 @@ func recvAndDecompress(p *parser, s recvCompressor, dc Decompressor, maxReceiveM
func decompress(compressor encoding.Compressor, d mem.BufferSlice, dc Decompressor, maxReceiveMessageSize int, pool mem.BufferPool) (mem.BufferSlice, error) {
if dc != nil {
r := d.Reader()
- uncompressed, err := dc.Do(r)
+ // For the built-in gzip decompressor, bound the decompressed output
+ // at maxReceiveMessageSize+1 so that a small but highly compressed
+ // payload (a "zip bomb") cannot expand to gigabytes in memory before
+ // the post-decompression size check below has a chance to fire. The
+ // Decompressor interface does not accept an extra size parameter,
+ // so we type-assert to invoke a size-aware helper. Third-party
+ // Decompressor implementations keep the original Do behavior.
+ var uncompressed []byte
+ var err error
+ if gd, ok := dc.(*gzipDecompressor); ok {
+ uncompressed, err = gd.doWithMaxSize(r, int64(maxReceiveMessageSize))
+ } else {
+ uncompressed, err = dc.Do(r)
+ }
if err != nil {
r.Close() // ensure buffers are reused
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message: %v", err)
@@ -989,6 +1016,9 @@ func decompress(compressor encoding.Compressor, d mem.BufferSlice, dc Decompress
r.Close() // ensure buffers are reused
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the message: %v", err)
}
+ if closer, ok := dcReader.(io.Closer); ok {
+ defer closer.Close()
+ }
// Read at most one byte more than the limit from the decompressor.
// Unless the limit is MaxInt64, in which case, that's impossible, so
diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go
index 5229adf71..cf0a20671 100644
--- a/vendor/google.golang.org/grpc/server.go
+++ b/vendor/google.golang.org/grpc/server.go
@@ -28,6 +28,7 @@ import (
"net/http"
"reflect"
"runtime"
+ "runtime/pprof"
"strings"
"sync"
"sync/atomic"
@@ -150,8 +151,6 @@ type Server struct {
serverWorkerChannel chan func()
serverWorkerChannelClose func()
-
- strictPathCheckingLogEmitted atomic.Bool
}
type serverOptions struct {
@@ -250,10 +249,8 @@ func newJoinServerOption(opts ...ServerOption) ServerOption {
// If this option is set to true every connection will release the buffer after
// flushing the data on the wire.
//
-// # Experimental
-//
-// Notice: This API is EXPERIMENTAL and may be changed or removed in a
-// later release.
+// Deprecated: shared write buffer is enabled by default. SharedWriteBuffer
+// will be removed in a future release.
func SharedWriteBuffer(val bool) ServerOption {
return newFuncServerOption(func(o *serverOptions) {
o.sharedWriteBuffer = val
@@ -302,6 +299,14 @@ func InitialConnWindowSize(s int32) ServerOption {
// window size to the value provided and disables dynamic flow control.
// The lower bound for window size is 64K and any value smaller than that
// will be ignored.
+//
+// Note that this also disables dynamic flow control for the connection,
+// falling back to a default static connection-level window of 64KB. To
+// use a larger connection-level window, you must also use the
+// [StaticConnWindowSize] ServerOption.
+//
+// Most users should not configure static flow control windows unless
+// operating in a memory-constrained environment.
func StaticStreamWindowSize(s int32) ServerOption {
return newFuncServerOption(func(o *serverOptions) {
o.initialWindowSize = s
@@ -313,6 +318,14 @@ func StaticStreamWindowSize(s int32) ServerOption {
// window size to the value provided and disables dynamic flow control.
// The lower bound for window size is 64K and any value smaller than that
// will be ignored.
+//
+// Note that this also disables dynamic flow control for individual streams,
+// falling back to a default static connection-level window of 64KB. To
+// explicitly configure the stream-level window size, you must also use the
+// [StaticStreamWindowSize] ServerOption.
+//
+// Most users should not configure static flow control windows unless
+// operating in a memory-constrained environment.
func StaticConnWindowSize(s int32) ServerOption {
return newFuncServerOption(func(o *serverOptions) {
o.initialConnWindowSize = s
@@ -1787,6 +1800,12 @@ func (s *Server) handleMalformedMethodName(stream *transport.ServerStream, ti *t
func (s *Server) handleStream(t transport.ServerTransport, stream *transport.ServerStream) {
ctx := stream.Context()
ctx = contextWithServer(ctx, s)
+ if envconfig.LabelServerGoroutines&envconfig.GoroutineLabelServerMethod != 0 {
+ // This method always runs in its own goroutine, so we can set a
+ // goroutine label without needing to restore a previous context.
+ ctx = pprof.WithLabels(ctx, pprof.Labels("grpc.method", stream.Method()))
+ pprof.SetGoroutineLabels(ctx)
+ }
var ti *traceInfo
if EnableTracing {
tr := newTrace("grpc.Recv."+methodFamily(stream.Method()), stream.Method())
@@ -1803,28 +1822,11 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Ser
}
}
- sm := stream.Method()
- if sm == "" {
+ sm, found := strings.CutPrefix(stream.Method(), "/")
+ if !found {
s.handleMalformedMethodName(stream, ti)
return
}
- if sm[0] != '/' {
- // TODO(easwars): Add a link to the CVE in the below log messages once
- // published.
- if envconfig.DisableStrictPathChecking {
- if old := s.strictPathCheckingLogEmitted.Swap(true); !old {
- channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream received malformed method name %q. Allowing it because the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING is set to true, but this option will be removed in a future release.", sm)
- }
- } else {
- if old := s.strictPathCheckingLogEmitted.Swap(true); !old {
- channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream rejected malformed method name %q. To temporarily allow such requests, set the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to true. Note that this is not recommended as it may allow requests to bypass security policies.", sm)
- }
- s.handleMalformedMethodName(stream, ti)
- return
- }
- } else {
- sm = sm[1:]
- }
pos := strings.LastIndex(sm, "/")
if pos == -1 {
s.handleMalformedMethodName(stream, ti)
diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go
index eedb5f9b9..4aac644a8 100644
--- a/vendor/google.golang.org/grpc/stream.go
+++ b/vendor/google.golang.org/grpc/stream.go
@@ -21,6 +21,7 @@ package grpc
import (
"context"
"errors"
+ "fmt"
"io"
"math"
rand "math/rand/v2"
@@ -749,7 +750,7 @@ func (a *csAttempt) shouldRetry(err error) (bool, error) {
return false, err
}
if cs.numRetries+1 >= rp.MaxAttempts {
- return false, err
+ return false, fmt.Errorf("max retries exhausted: failed after %d attempts: %w", cs.numRetries+1, err)
}
var dur time.Duration
diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go
index 12f649dcb..cf114ef4b 100644
--- a/vendor/google.golang.org/grpc/version.go
+++ b/vendor/google.golang.org/grpc/version.go
@@ -19,4 +19,4 @@
package grpc
// Version is the current grpc version.
-const Version = "1.80.0"
+const Version = "1.82.0"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index db877791c..ce01166b1 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,14 +1,14 @@
-# charm.land/bubbles/v2 v2.1.0
+# charm.land/bubbles/v2 v2.1.1
## explicit; go 1.25.0
charm.land/bubbles/v2/cursor
charm.land/bubbles/v2/internal/runeutil
charm.land/bubbles/v2/key
charm.land/bubbles/v2/textinput
charm.land/bubbles/v2/viewport
-# charm.land/bubbletea/v2 v2.0.2
-## explicit; go 1.24.2
+# charm.land/bubbletea/v2 v2.0.7
+## explicit; go 1.25.0
charm.land/bubbletea/v2
-# charm.land/lipgloss/v2 v2.0.2
+# charm.land/lipgloss/v2 v2.0.4
## explicit; go 1.25.0
charm.land/lipgloss/v2
# charm.land/log/v2 v2.0.0
@@ -46,10 +46,10 @@ github.com/charmbracelet/keygen
# github.com/charmbracelet/ssh v0.0.0-20250826160808-ebfa259c7309
## explicit; go 1.23.0
github.com/charmbracelet/ssh
-# github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8
-## explicit; go 1.24.2
+# github.com/charmbracelet/ultraviolet v0.0.0-20260525132238-948f4557a654
+## explicit; go 1.25.0
github.com/charmbracelet/ultraviolet
-# github.com/charmbracelet/x/ansi v0.11.6
+# github.com/charmbracelet/x/ansi v0.11.7
## explicit; go 1.24.2
github.com/charmbracelet/x/ansi
github.com/charmbracelet/x/ansi/kitty
@@ -69,16 +69,18 @@ github.com/charmbracelet/x/termios
# github.com/charmbracelet/x/windows v0.2.2
## explicit; go 1.23.0
github.com/charmbracelet/x/windows
-# github.com/cilium/ebpf v0.19.0
-## explicit; go 1.23.0
+# github.com/cilium/ebpf v0.22.0
+## explicit; go 1.25.0
github.com/cilium/ebpf
github.com/cilium/ebpf/asm
github.com/cilium/ebpf/btf
+github.com/cilium/ebpf/features
github.com/cilium/ebpf/internal
github.com/cilium/ebpf/internal/efw
github.com/cilium/ebpf/internal/kallsyms
github.com/cilium/ebpf/internal/kconfig
github.com/cilium/ebpf/internal/linux
+github.com/cilium/ebpf/internal/mountinfo
github.com/cilium/ebpf/internal/platform
github.com/cilium/ebpf/internal/sys
github.com/cilium/ebpf/internal/sysenc
@@ -96,7 +98,7 @@ github.com/clipperhouse/uax29/v2/graphemes
# github.com/creack/pty v1.1.24
## explicit; go 1.18
github.com/creack/pty
-# github.com/davecgh/go-spew v1.1.1
+# github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
## explicit
github.com/davecgh/go-spew/spew
# github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0
@@ -109,7 +111,7 @@ github.com/fsnotify/fsnotify/internal
# github.com/ftrvxmtrx/fd v0.0.0-20150925145434-c6d800382fff
## explicit
github.com/ftrvxmtrx/fd
-# github.com/gaissmai/bart v0.26.1
+# github.com/gaissmai/bart v0.28.0
## explicit; go 1.24.0
github.com/gaissmai/bart
github.com/gaissmai/bart/internal/allot
@@ -193,7 +195,7 @@ github.com/insomniacslk/dhcp/rfc1035label
# github.com/josharian/native v1.1.0
## explicit; go 1.13
github.com/josharian/native
-# github.com/lucasb-eyer/go-colorful v1.3.0
+# github.com/lucasb-eyer/go-colorful v1.4.0
## explicit; go 1.12
github.com/lucasb-eyer/go-colorful
# github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe
@@ -202,22 +204,22 @@ github.com/lunixbochs/struc
# github.com/mattn/go-isatty v0.0.20
## explicit; go 1.15
github.com/mattn/go-isatty
-# github.com/mattn/go-runewidth v0.0.21
+# github.com/mattn/go-runewidth v0.0.24
## explicit; go 1.20
github.com/mattn/go-runewidth
-# github.com/mdlayher/genetlink v1.3.2
-## explicit; go 1.18
+# github.com/mdlayher/genetlink v1.4.0
+## explicit; go 1.25.0
github.com/mdlayher/genetlink
-# github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42
-## explicit; go 1.21
+# github.com/mdlayher/netlink v1.9.0
+## explicit; go 1.24.0
github.com/mdlayher/netlink
github.com/mdlayher/netlink/nlenc
github.com/mdlayher/netlink/nltest
# github.com/mdlayher/packet v1.1.2
## explicit; go 1.20
github.com/mdlayher/packet
-# github.com/mdlayher/socket v0.5.1
-## explicit; go 1.20
+# github.com/mdlayher/socket v0.6.0
+## explicit; go 1.25.0
github.com/mdlayher/socket
# github.com/miekg/dns v1.1.72
## explicit; go 1.24.0
@@ -249,7 +251,7 @@ github.com/pierrec/lz4/v4/internal/lz4block
github.com/pierrec/lz4/v4/internal/lz4errors
github.com/pierrec/lz4/v4/internal/lz4stream
github.com/pierrec/lz4/v4/internal/xxh32
-# github.com/pmezard/go-difflib v1.0.0
+# github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
## explicit
github.com/pmezard/go-difflib/difflib
# github.com/prometheus/client_golang v1.23.2
@@ -268,16 +270,16 @@ github.com/prometheus/client_model/go
## explicit; go 1.23.0
github.com/prometheus/common/expfmt
github.com/prometheus/common/model
-# github.com/prometheus/procfs v0.16.1
-## explicit; go 1.23.0
+# github.com/prometheus/procfs v0.21.1
+## explicit; go 1.25.0
github.com/prometheus/procfs
github.com/prometheus/procfs/internal/fs
github.com/prometheus/procfs/internal/util
# github.com/rivo/uniseg v0.4.7
## explicit; go 1.18
github.com/rivo/uniseg
-# github.com/sirupsen/logrus v1.9.3
-## explicit; go 1.13
+# github.com/sirupsen/logrus v1.9.4
+## explicit; go 1.17
github.com/sirupsen/logrus
# github.com/sivchari/gomu v0.2.1
## explicit; go 1.24
@@ -290,7 +292,7 @@ github.com/sivchari/gomu/internal/ignore
github.com/sivchari/gomu/internal/mutation
github.com/sivchari/gomu/internal/report
github.com/sivchari/gomu/pkg/gomu
-# github.com/spf13/cobra v1.10.1
+# github.com/spf13/cobra v1.10.2
## explicit; go 1.15
github.com/spf13/cobra
# github.com/spf13/pflag v1.0.10
@@ -355,7 +357,7 @@ go.fd.io/govpp/core/genericpool
# go.yaml.in/yaml/v2 v2.4.2
## explicit; go 1.15
go.yaml.in/yaml/v2
-# golang.org/x/crypto v0.53.0
+# golang.org/x/crypto v0.54.0
## explicit; go 1.25.0
golang.org/x/crypto/argon2
golang.org/x/crypto/bcrypt
@@ -380,13 +382,13 @@ golang.org/x/exp/slices
golang.org/x/exp/slog
golang.org/x/exp/slog/internal
golang.org/x/exp/slog/internal/buffer
-# golang.org/x/mod v0.36.0
+# golang.org/x/mod v0.37.0
## explicit; go 1.25.0
golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/modfile
golang.org/x/mod/module
golang.org/x/mod/semver
-# golang.org/x/net v0.56.0
+# golang.org/x/net v0.57.0
## explicit; go 1.25.0
golang.org/x/net/bpf
golang.org/x/net/http/httpguts
@@ -401,35 +403,35 @@ golang.org/x/net/internal/timeseries
golang.org/x/net/ipv4
golang.org/x/net/ipv6
golang.org/x/net/trace
-# golang.org/x/oauth2 v0.34.0
-## explicit; go 1.24.0
+# golang.org/x/oauth2 v0.36.0
+## explicit; go 1.25.0
golang.org/x/oauth2
golang.org/x/oauth2/internal
-# golang.org/x/sync v0.21.0
+# golang.org/x/sync v0.22.0
## explicit; go 1.25.0
golang.org/x/sync/errgroup
-# golang.org/x/sys v0.46.0
+# golang.org/x/sys v0.47.0
## explicit; go 1.25.0
golang.org/x/sys/cpu
golang.org/x/sys/plan9
golang.org/x/sys/unix
golang.org/x/sys/windows
-# golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6
+# golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57
## explicit; go 1.25.0
golang.org/x/telemetry/counter
golang.org/x/telemetry/internal/counter
golang.org/x/telemetry/internal/mmap
golang.org/x/telemetry/internal/telemetry
-# golang.org/x/term v0.44.0
+# golang.org/x/term v0.45.0
## explicit; go 1.25.0
golang.org/x/term
-# golang.org/x/text v0.38.0
+# golang.org/x/text v0.40.0
## explicit; go 1.25.0
golang.org/x/text/secure/bidirule
golang.org/x/text/transform
golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm
-# golang.org/x/tools v0.45.0
+# golang.org/x/tools v0.47.0
## explicit; go 1.25.0
golang.org/x/tools/cmd/goimports
golang.org/x/tools/go/ast/astutil
@@ -471,11 +473,11 @@ golang.zx2c4.com/wireguard/wgctrl/internal/wguser
golang.zx2c4.com/wireguard/wgctrl/internal/wgwindows
golang.zx2c4.com/wireguard/wgctrl/internal/wgwindows/internal/ioctl
golang.zx2c4.com/wireguard/wgctrl/wgtypes
-# google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516
-## explicit; go 1.24.0
+# google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478
+## explicit; go 1.25.0
google.golang.org/genproto/googleapis/rpc/status
-# google.golang.org/grpc v1.80.0
-## explicit; go 1.24.0
+# google.golang.org/grpc v1.82.0
+## explicit; go 1.25.0
google.golang.org/grpc
google.golang.org/grpc/attributes
google.golang.org/grpc/backoff
@@ -495,13 +497,13 @@ google.golang.org/grpc/credentials/insecure
google.golang.org/grpc/encoding
google.golang.org/grpc/encoding/internal
google.golang.org/grpc/encoding/proto
+google.golang.org/grpc/experimental/balancer/weight
google.golang.org/grpc/experimental/stats
google.golang.org/grpc/grpclog
google.golang.org/grpc/grpclog/internal
google.golang.org/grpc/internal
google.golang.org/grpc/internal/backoff
google.golang.org/grpc/internal/balancer/gracefulswitch
-google.golang.org/grpc/internal/balancer/weight
google.golang.org/grpc/internal/balancerload
google.golang.org/grpc/internal/binarylog
google.golang.org/grpc/internal/buffer
@@ -527,7 +529,9 @@ google.golang.org/grpc/internal/stats
google.golang.org/grpc/internal/status
google.golang.org/grpc/internal/syscall
google.golang.org/grpc/internal/transport
+google.golang.org/grpc/internal/transport/internal
google.golang.org/grpc/internal/transport/networktype
+google.golang.org/grpc/internal/transport/readyreader
google.golang.org/grpc/keepalive
google.golang.org/grpc/mem
google.golang.org/grpc/metadata
@@ -542,8 +546,8 @@ google.golang.org/grpc/serviceconfig
google.golang.org/grpc/stats
google.golang.org/grpc/status
google.golang.org/grpc/tap
-# google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1
-## explicit; go 1.24.0
+# google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2
+## explicit; go 1.25.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc
# google.golang.org/protobuf v1.36.11
## explicit; go 1.23