@@ -6,21 +6,21 @@ require ( | |||||
github.com/globocom/go-redis-prometheus v0.4.0 | github.com/globocom/go-redis-prometheus v0.4.0 | ||||
github.com/go-redis/redis/v8 v8.11.5 | github.com/go-redis/redis/v8 v8.11.5 | ||||
github.com/gorilla/mux v1.8.0 | github.com/gorilla/mux v1.8.0 | ||||
github.com/hashicorp/go-multierror v1.0.0 | |||||
github.com/prometheus/client_golang v1.13.0 | |||||
github.com/hashicorp/go-multierror v1.1.1 | |||||
github.com/prometheus/client_golang v1.15.1 | |||||
github.com/tevino/abool/v2 v2.1.0 | github.com/tevino/abool/v2 v2.1.0 | ||||
) | ) | ||||
require ( | require ( | ||||
github.com/beorn7/perks v1.0.1 // indirect | github.com/beorn7/perks v1.0.1 // indirect | ||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect | |||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect | |||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect | ||||
github.com/golang/protobuf v1.5.2 // indirect | |||||
github.com/hashicorp/errwrap v1.0.0 // indirect | |||||
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect | |||||
github.com/prometheus/client_model v0.2.0 // indirect | |||||
github.com/prometheus/common v0.37.0 // indirect | |||||
github.com/prometheus/procfs v0.8.0 // indirect | |||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect | |||||
google.golang.org/protobuf v1.28.1 // indirect | |||||
github.com/golang/protobuf v1.5.3 // indirect | |||||
github.com/hashicorp/errwrap v1.1.0 // indirect | |||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect | |||||
github.com/prometheus/client_model v0.4.0 // indirect | |||||
github.com/prometheus/common v0.43.0 // indirect | |||||
github.com/prometheus/procfs v0.9.0 // indirect | |||||
golang.org/x/sys v0.8.0 // indirect | |||||
google.golang.org/protobuf v1.30.0 // indirect | |||||
) | ) |
@@ -63,6 +63,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA | |||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= | ||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= | |||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | |||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | ||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | ||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= | ||||
@@ -151,6 +153,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw | |||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= | ||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= | |||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | |||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
@@ -165,6 +169,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ | |||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= | ||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | |||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | ||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= | ||||
@@ -193,11 +198,15 @@ github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoP | |||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= | github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= | ||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= | ||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | ||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= | |||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | |||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | ||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= | ||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= | ||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= | github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= | ||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= | ||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= | |||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= | |||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= | ||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= | ||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= | ||||
@@ -248,6 +257,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp | |||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= | ||||
github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= | github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= | ||||
github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= | github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= | ||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= | |||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= | |||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= | ||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= | ||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | ||||
@@ -317,6 +328,8 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr | |||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= | github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= | ||||
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= | github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= | ||||
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= | github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= | ||||
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= | |||||
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= | |||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | ||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | ||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||
@@ -324,6 +337,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: | |||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= | ||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= | |||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= | |||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= | ||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= | ||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= | github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= | ||||
@@ -333,6 +348,8 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 | |||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= | github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= | ||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= | github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= | ||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= | github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= | ||||
github.com/prometheus/common v0.43.0 h1:iq+BVjvYLei5f27wiuNiB1DN6DYQkp1c8Bx0Vykh5us= | |||||
github.com/prometheus/common v0.43.0/go.mod h1:NCvr5cQIh3Y/gy73/RdVtC9r8xxrxwJnB+2lB3BxrFc= | |||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= | ||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= | ||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= | ||||
@@ -343,6 +360,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 | |||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= | github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= | ||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= | github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= | ||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= | github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= | ||||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= | |||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= | |||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= | ||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= | ||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | ||||
@@ -543,6 +562,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc | |||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc= | golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc= | ||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= | |||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
@@ -692,6 +713,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 | |||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | ||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= | ||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= | ||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= | |||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= | |||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= | ||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
@@ -3,8 +3,7 @@ | |||||
[![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2) | [![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2) | ||||
[![Test](https://github.com/cespare/xxhash/actions/workflows/test.yml/badge.svg)](https://github.com/cespare/xxhash/actions/workflows/test.yml) | [![Test](https://github.com/cespare/xxhash/actions/workflows/test.yml/badge.svg)](https://github.com/cespare/xxhash/actions/workflows/test.yml) | ||||
xxhash is a Go implementation of the 64-bit | |||||
[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a | |||||
xxhash is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a | |||||
high-quality hashing algorithm that is much faster than anything in the Go | high-quality hashing algorithm that is much faster than anything in the Go | ||||
standard library. | standard library. | ||||
@@ -25,8 +24,11 @@ func (*Digest) WriteString(string) (int, error) | |||||
func (*Digest) Sum64() uint64 | func (*Digest) Sum64() uint64 | ||||
``` | ``` | ||||
This implementation provides a fast pure-Go implementation and an even faster | |||||
assembly implementation for amd64. | |||||
The package is written with optimized pure Go and also contains even faster | |||||
assembly implementations for amd64 and arm64. If desired, the `purego` build tag | |||||
opts into using the Go code even on those architectures. | |||||
[xxHash]: http://cyan4973.github.io/xxHash/ | |||||
## Compatibility | ## Compatibility | ||||
@@ -45,19 +47,20 @@ I recommend using the latest release of Go. | |||||
Here are some quick benchmarks comparing the pure-Go and assembly | Here are some quick benchmarks comparing the pure-Go and assembly | ||||
implementations of Sum64. | implementations of Sum64. | ||||
| input size | purego | asm | | |||||
| --- | --- | --- | | |||||
| 5 B | 979.66 MB/s | 1291.17 MB/s | | |||||
| 100 B | 7475.26 MB/s | 7973.40 MB/s | | |||||
| 4 KB | 17573.46 MB/s | 17602.65 MB/s | | |||||
| 10 MB | 17131.46 MB/s | 17142.16 MB/s | | |||||
| input size | purego | asm | | |||||
| ---------- | --------- | --------- | | |||||
| 4 B | 1.3 GB/s | 1.2 GB/s | | |||||
| 16 B | 2.9 GB/s | 3.5 GB/s | | |||||
| 100 B | 6.9 GB/s | 8.1 GB/s | | |||||
| 4 KB | 11.7 GB/s | 16.7 GB/s | | |||||
| 10 MB | 12.0 GB/s | 17.3 GB/s | | |||||
These numbers were generated on Ubuntu 18.04 with an Intel i7-8700K CPU using | |||||
the following commands under Go 1.11.2: | |||||
These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C | |||||
CPU using the following commands under Go 1.19.2: | |||||
``` | ``` | ||||
$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes' | |||||
$ go test -benchtime 10s -bench '/xxhash,direct,bytes' | |||||
benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$') | |||||
benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$') | |||||
``` | ``` | ||||
## Projects using this package | ## Projects using this package | ||||
@@ -0,0 +1,10 @@ | |||||
#!/bin/bash | |||||
set -eu -o pipefail | |||||
# Small convenience script for running the tests with various combinations of | |||||
# arch/tags. This assumes we're running on amd64 and have qemu available. | |||||
go test ./... | |||||
go test -tags purego ./... | |||||
GOARCH=arm64 go test | |||||
GOARCH=arm64 go test -tags purego |
@@ -16,19 +16,11 @@ const ( | |||||
prime5 uint64 = 2870177450012600261 | prime5 uint64 = 2870177450012600261 | ||||
) | ) | ||||
// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where | |||||
// possible in the Go code is worth a small (but measurable) performance boost | |||||
// by avoiding some MOVQs. Vars are needed for the asm and also are useful for | |||||
// convenience in the Go code in a few places where we need to intentionally | |||||
// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the | |||||
// result overflows a uint64). | |||||
var ( | |||||
prime1v = prime1 | |||||
prime2v = prime2 | |||||
prime3v = prime3 | |||||
prime4v = prime4 | |||||
prime5v = prime5 | |||||
) | |||||
// Store the primes in an array as well. | |||||
// | |||||
// The consts are used when possible in Go code to avoid MOVs but we need a | |||||
// contiguous array of the assembly code. | |||||
var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5} | |||||
// Digest implements hash.Hash64. | // Digest implements hash.Hash64. | ||||
type Digest struct { | type Digest struct { | ||||
@@ -50,10 +42,10 @@ func New() *Digest { | |||||
// Reset clears the Digest's state so that it can be reused. | // Reset clears the Digest's state so that it can be reused. | ||||
func (d *Digest) Reset() { | func (d *Digest) Reset() { | ||||
d.v1 = prime1v + prime2 | |||||
d.v1 = primes[0] + prime2 | |||||
d.v2 = prime2 | d.v2 = prime2 | ||||
d.v3 = 0 | d.v3 = 0 | ||||
d.v4 = -prime1v | |||||
d.v4 = -primes[0] | |||||
d.total = 0 | d.total = 0 | ||||
d.n = 0 | d.n = 0 | ||||
} | } | ||||
@@ -69,21 +61,23 @@ func (d *Digest) Write(b []byte) (n int, err error) { | |||||
n = len(b) | n = len(b) | ||||
d.total += uint64(n) | d.total += uint64(n) | ||||
memleft := d.mem[d.n&(len(d.mem)-1):] | |||||
if d.n+n < 32 { | if d.n+n < 32 { | ||||
// This new data doesn't even fill the current block. | // This new data doesn't even fill the current block. | ||||
copy(d.mem[d.n:], b) | |||||
copy(memleft, b) | |||||
d.n += n | d.n += n | ||||
return | return | ||||
} | } | ||||
if d.n > 0 { | if d.n > 0 { | ||||
// Finish off the partial block. | // Finish off the partial block. | ||||
copy(d.mem[d.n:], b) | |||||
c := copy(memleft, b) | |||||
d.v1 = round(d.v1, u64(d.mem[0:8])) | d.v1 = round(d.v1, u64(d.mem[0:8])) | ||||
d.v2 = round(d.v2, u64(d.mem[8:16])) | d.v2 = round(d.v2, u64(d.mem[8:16])) | ||||
d.v3 = round(d.v3, u64(d.mem[16:24])) | d.v3 = round(d.v3, u64(d.mem[16:24])) | ||||
d.v4 = round(d.v4, u64(d.mem[24:32])) | d.v4 = round(d.v4, u64(d.mem[24:32])) | ||||
b = b[32-d.n:] | |||||
b = b[c:] | |||||
d.n = 0 | d.n = 0 | ||||
} | } | ||||
@@ -133,21 +127,20 @@ func (d *Digest) Sum64() uint64 { | |||||
h += d.total | h += d.total | ||||
i, end := 0, d.n | |||||
for ; i+8 <= end; i += 8 { | |||||
k1 := round(0, u64(d.mem[i:i+8])) | |||||
b := d.mem[:d.n&(len(d.mem)-1)] | |||||
for ; len(b) >= 8; b = b[8:] { | |||||
k1 := round(0, u64(b[:8])) | |||||
h ^= k1 | h ^= k1 | ||||
h = rol27(h)*prime1 + prime4 | h = rol27(h)*prime1 + prime4 | ||||
} | } | ||||
if i+4 <= end { | |||||
h ^= uint64(u32(d.mem[i:i+4])) * prime1 | |||||
if len(b) >= 4 { | |||||
h ^= uint64(u32(b[:4])) * prime1 | |||||
h = rol23(h)*prime2 + prime3 | h = rol23(h)*prime2 + prime3 | ||||
i += 4 | |||||
b = b[4:] | |||||
} | } | ||||
for i < end { | |||||
h ^= uint64(d.mem[i]) * prime5 | |||||
for ; len(b) > 0; b = b[1:] { | |||||
h ^= uint64(b[0]) * prime5 | |||||
h = rol11(h) * prime1 | h = rol11(h) * prime1 | ||||
i++ | |||||
} | } | ||||
h ^= h >> 33 | h ^= h >> 33 | ||||
@@ -1,215 +1,209 @@ | |||||
//go:build !appengine && gc && !purego | |||||
// +build !appengine | // +build !appengine | ||||
// +build gc | // +build gc | ||||
// +build !purego | // +build !purego | ||||
#include "textflag.h" | #include "textflag.h" | ||||
// Register allocation: | |||||
// AX h | |||||
// SI pointer to advance through b | |||||
// DX n | |||||
// BX loop end | |||||
// R8 v1, k1 | |||||
// R9 v2 | |||||
// R10 v3 | |||||
// R11 v4 | |||||
// R12 tmp | |||||
// R13 prime1v | |||||
// R14 prime2v | |||||
// DI prime4v | |||||
// round reads from and advances the buffer pointer in SI. | |||||
// It assumes that R13 has prime1v and R14 has prime2v. | |||||
#define round(r) \ | |||||
MOVQ (SI), R12 \ | |||||
ADDQ $8, SI \ | |||||
IMULQ R14, R12 \ | |||||
ADDQ R12, r \ | |||||
ROLQ $31, r \ | |||||
IMULQ R13, r | |||||
// mergeRound applies a merge round on the two registers acc and val. | |||||
// It assumes that R13 has prime1v, R14 has prime2v, and DI has prime4v. | |||||
#define mergeRound(acc, val) \ | |||||
IMULQ R14, val \ | |||||
ROLQ $31, val \ | |||||
IMULQ R13, val \ | |||||
XORQ val, acc \ | |||||
IMULQ R13, acc \ | |||||
ADDQ DI, acc | |||||
// Registers: | |||||
#define h AX | |||||
#define d AX | |||||
#define p SI // pointer to advance through b | |||||
#define n DX | |||||
#define end BX // loop end | |||||
#define v1 R8 | |||||
#define v2 R9 | |||||
#define v3 R10 | |||||
#define v4 R11 | |||||
#define x R12 | |||||
#define prime1 R13 | |||||
#define prime2 R14 | |||||
#define prime4 DI | |||||
#define round(acc, x) \ | |||||
IMULQ prime2, x \ | |||||
ADDQ x, acc \ | |||||
ROLQ $31, acc \ | |||||
IMULQ prime1, acc | |||||
// round0 performs the operation x = round(0, x). | |||||
#define round0(x) \ | |||||
IMULQ prime2, x \ | |||||
ROLQ $31, x \ | |||||
IMULQ prime1, x | |||||
// mergeRound applies a merge round on the two registers acc and x. | |||||
// It assumes that prime1, prime2, and prime4 have been loaded. | |||||
#define mergeRound(acc, x) \ | |||||
round0(x) \ | |||||
XORQ x, acc \ | |||||
IMULQ prime1, acc \ | |||||
ADDQ prime4, acc | |||||
// blockLoop processes as many 32-byte blocks as possible, | |||||
// updating v1, v2, v3, and v4. It assumes that there is at least one block | |||||
// to process. | |||||
#define blockLoop() \ | |||||
loop: \ | |||||
MOVQ +0(p), x \ | |||||
round(v1, x) \ | |||||
MOVQ +8(p), x \ | |||||
round(v2, x) \ | |||||
MOVQ +16(p), x \ | |||||
round(v3, x) \ | |||||
MOVQ +24(p), x \ | |||||
round(v4, x) \ | |||||
ADDQ $32, p \ | |||||
CMPQ p, end \ | |||||
JLE loop | |||||
// func Sum64(b []byte) uint64 | // func Sum64(b []byte) uint64 | ||||
TEXT ·Sum64(SB), NOSPLIT, $0-32 | |||||
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 | |||||
// Load fixed primes. | // Load fixed primes. | ||||
MOVQ ·prime1v(SB), R13 | |||||
MOVQ ·prime2v(SB), R14 | |||||
MOVQ ·prime4v(SB), DI | |||||
MOVQ ·primes+0(SB), prime1 | |||||
MOVQ ·primes+8(SB), prime2 | |||||
MOVQ ·primes+24(SB), prime4 | |||||
// Load slice. | // Load slice. | ||||
MOVQ b_base+0(FP), SI | |||||
MOVQ b_len+8(FP), DX | |||||
LEAQ (SI)(DX*1), BX | |||||
MOVQ b_base+0(FP), p | |||||
MOVQ b_len+8(FP), n | |||||
LEAQ (p)(n*1), end | |||||
// The first loop limit will be len(b)-32. | // The first loop limit will be len(b)-32. | ||||
SUBQ $32, BX | |||||
SUBQ $32, end | |||||
// Check whether we have at least one block. | // Check whether we have at least one block. | ||||
CMPQ DX, $32 | |||||
CMPQ n, $32 | |||||
JLT noBlocks | JLT noBlocks | ||||
// Set up initial state (v1, v2, v3, v4). | // Set up initial state (v1, v2, v3, v4). | ||||
MOVQ R13, R8 | |||||
ADDQ R14, R8 | |||||
MOVQ R14, R9 | |||||
XORQ R10, R10 | |||||
XORQ R11, R11 | |||||
SUBQ R13, R11 | |||||
// Loop until SI > BX. | |||||
blockLoop: | |||||
round(R8) | |||||
round(R9) | |||||
round(R10) | |||||
round(R11) | |||||
CMPQ SI, BX | |||||
JLE blockLoop | |||||
MOVQ R8, AX | |||||
ROLQ $1, AX | |||||
MOVQ R9, R12 | |||||
ROLQ $7, R12 | |||||
ADDQ R12, AX | |||||
MOVQ R10, R12 | |||||
ROLQ $12, R12 | |||||
ADDQ R12, AX | |||||
MOVQ R11, R12 | |||||
ROLQ $18, R12 | |||||
ADDQ R12, AX | |||||
mergeRound(AX, R8) | |||||
mergeRound(AX, R9) | |||||
mergeRound(AX, R10) | |||||
mergeRound(AX, R11) | |||||
MOVQ prime1, v1 | |||||
ADDQ prime2, v1 | |||||
MOVQ prime2, v2 | |||||
XORQ v3, v3 | |||||
XORQ v4, v4 | |||||
SUBQ prime1, v4 | |||||
blockLoop() | |||||
MOVQ v1, h | |||||
ROLQ $1, h | |||||
MOVQ v2, x | |||||
ROLQ $7, x | |||||
ADDQ x, h | |||||
MOVQ v3, x | |||||
ROLQ $12, x | |||||
ADDQ x, h | |||||
MOVQ v4, x | |||||
ROLQ $18, x | |||||
ADDQ x, h | |||||
mergeRound(h, v1) | |||||
mergeRound(h, v2) | |||||
mergeRound(h, v3) | |||||
mergeRound(h, v4) | |||||
JMP afterBlocks | JMP afterBlocks | ||||
noBlocks: | noBlocks: | ||||
MOVQ ·prime5v(SB), AX | |||||
MOVQ ·primes+32(SB), h | |||||
afterBlocks: | afterBlocks: | ||||
ADDQ DX, AX | |||||
// Right now BX has len(b)-32, and we want to loop until SI > len(b)-8. | |||||
ADDQ $24, BX | |||||
CMPQ SI, BX | |||||
JG fourByte | |||||
wordLoop: | |||||
// Calculate k1. | |||||
MOVQ (SI), R8 | |||||
ADDQ $8, SI | |||||
IMULQ R14, R8 | |||||
ROLQ $31, R8 | |||||
IMULQ R13, R8 | |||||
XORQ R8, AX | |||||
ROLQ $27, AX | |||||
IMULQ R13, AX | |||||
ADDQ DI, AX | |||||
CMPQ SI, BX | |||||
JLE wordLoop | |||||
fourByte: | |||||
ADDQ $4, BX | |||||
CMPQ SI, BX | |||||
JG singles | |||||
MOVL (SI), R8 | |||||
ADDQ $4, SI | |||||
IMULQ R13, R8 | |||||
XORQ R8, AX | |||||
ROLQ $23, AX | |||||
IMULQ R14, AX | |||||
ADDQ ·prime3v(SB), AX | |||||
singles: | |||||
ADDQ $4, BX | |||||
CMPQ SI, BX | |||||
ADDQ n, h | |||||
ADDQ $24, end | |||||
CMPQ p, end | |||||
JG try4 | |||||
loop8: | |||||
MOVQ (p), x | |||||
ADDQ $8, p | |||||
round0(x) | |||||
XORQ x, h | |||||
ROLQ $27, h | |||||
IMULQ prime1, h | |||||
ADDQ prime4, h | |||||
CMPQ p, end | |||||
JLE loop8 | |||||
try4: | |||||
ADDQ $4, end | |||||
CMPQ p, end | |||||
JG try1 | |||||
MOVL (p), x | |||||
ADDQ $4, p | |||||
IMULQ prime1, x | |||||
XORQ x, h | |||||
ROLQ $23, h | |||||
IMULQ prime2, h | |||||
ADDQ ·primes+16(SB), h | |||||
try1: | |||||
ADDQ $4, end | |||||
CMPQ p, end | |||||
JGE finalize | JGE finalize | ||||
singlesLoop: | |||||
MOVBQZX (SI), R12 | |||||
ADDQ $1, SI | |||||
IMULQ ·prime5v(SB), R12 | |||||
XORQ R12, AX | |||||
loop1: | |||||
MOVBQZX (p), x | |||||
ADDQ $1, p | |||||
IMULQ ·primes+32(SB), x | |||||
XORQ x, h | |||||
ROLQ $11, h | |||||
IMULQ prime1, h | |||||
ROLQ $11, AX | |||||
IMULQ R13, AX | |||||
CMPQ SI, BX | |||||
JL singlesLoop | |||||
CMPQ p, end | |||||
JL loop1 | |||||
finalize: | finalize: | ||||
MOVQ AX, R12 | |||||
SHRQ $33, R12 | |||||
XORQ R12, AX | |||||
IMULQ R14, AX | |||||
MOVQ AX, R12 | |||||
SHRQ $29, R12 | |||||
XORQ R12, AX | |||||
IMULQ ·prime3v(SB), AX | |||||
MOVQ AX, R12 | |||||
SHRQ $32, R12 | |||||
XORQ R12, AX | |||||
MOVQ AX, ret+24(FP) | |||||
MOVQ h, x | |||||
SHRQ $33, x | |||||
XORQ x, h | |||||
IMULQ prime2, h | |||||
MOVQ h, x | |||||
SHRQ $29, x | |||||
XORQ x, h | |||||
IMULQ ·primes+16(SB), h | |||||
MOVQ h, x | |||||
SHRQ $32, x | |||||
XORQ x, h | |||||
MOVQ h, ret+24(FP) | |||||
RET | RET | ||||
// writeBlocks uses the same registers as above except that it uses AX to store | |||||
// the d pointer. | |||||
// func writeBlocks(d *Digest, b []byte) int | // func writeBlocks(d *Digest, b []byte) int | ||||
TEXT ·writeBlocks(SB), NOSPLIT, $0-40 | |||||
TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 | |||||
// Load fixed primes needed for round. | // Load fixed primes needed for round. | ||||
MOVQ ·prime1v(SB), R13 | |||||
MOVQ ·prime2v(SB), R14 | |||||
MOVQ ·primes+0(SB), prime1 | |||||
MOVQ ·primes+8(SB), prime2 | |||||
// Load slice. | // Load slice. | ||||
MOVQ b_base+8(FP), SI | |||||
MOVQ b_len+16(FP), DX | |||||
LEAQ (SI)(DX*1), BX | |||||
SUBQ $32, BX | |||||
MOVQ b_base+8(FP), p | |||||
MOVQ b_len+16(FP), n | |||||
LEAQ (p)(n*1), end | |||||
SUBQ $32, end | |||||
// Load vN from d. | // Load vN from d. | ||||
MOVQ d+0(FP), AX | |||||
MOVQ 0(AX), R8 // v1 | |||||
MOVQ 8(AX), R9 // v2 | |||||
MOVQ 16(AX), R10 // v3 | |||||
MOVQ 24(AX), R11 // v4 | |||||
MOVQ s+0(FP), d | |||||
MOVQ 0(d), v1 | |||||
MOVQ 8(d), v2 | |||||
MOVQ 16(d), v3 | |||||
MOVQ 24(d), v4 | |||||
// We don't need to check the loop condition here; this function is | // We don't need to check the loop condition here; this function is | ||||
// always called with at least one block of data to process. | // always called with at least one block of data to process. | ||||
blockLoop: | |||||
round(R8) | |||||
round(R9) | |||||
round(R10) | |||||
round(R11) | |||||
CMPQ SI, BX | |||||
JLE blockLoop | |||||
blockLoop() | |||||
// Copy vN back to d. | // Copy vN back to d. | ||||
MOVQ R8, 0(AX) | |||||
MOVQ R9, 8(AX) | |||||
MOVQ R10, 16(AX) | |||||
MOVQ R11, 24(AX) | |||||
// The number of bytes written is SI minus the old base pointer. | |||||
SUBQ b_base+8(FP), SI | |||||
MOVQ SI, ret+32(FP) | |||||
MOVQ v1, 0(d) | |||||
MOVQ v2, 8(d) | |||||
MOVQ v3, 16(d) | |||||
MOVQ v4, 24(d) | |||||
// The number of bytes written is p minus the old base pointer. | |||||
SUBQ b_base+8(FP), p | |||||
MOVQ p, ret+32(FP) | |||||
RET | RET |
@@ -0,0 +1,183 @@ | |||||
//go:build !appengine && gc && !purego | |||||
// +build !appengine | |||||
// +build gc | |||||
// +build !purego | |||||
#include "textflag.h" | |||||
// Registers: | |||||
#define digest R1 | |||||
#define h R2 // return value | |||||
#define p R3 // input pointer | |||||
#define n R4 // input length | |||||
#define nblocks R5 // n / 32 | |||||
#define prime1 R7 | |||||
#define prime2 R8 | |||||
#define prime3 R9 | |||||
#define prime4 R10 | |||||
#define prime5 R11 | |||||
#define v1 R12 | |||||
#define v2 R13 | |||||
#define v3 R14 | |||||
#define v4 R15 | |||||
#define x1 R20 | |||||
#define x2 R21 | |||||
#define x3 R22 | |||||
#define x4 R23 | |||||
#define round(acc, x) \ | |||||
MADD prime2, acc, x, acc \ | |||||
ROR $64-31, acc \ | |||||
MUL prime1, acc | |||||
// round0 performs the operation x = round(0, x). | |||||
#define round0(x) \ | |||||
MUL prime2, x \ | |||||
ROR $64-31, x \ | |||||
MUL prime1, x | |||||
#define mergeRound(acc, x) \ | |||||
round0(x) \ | |||||
EOR x, acc \ | |||||
MADD acc, prime4, prime1, acc | |||||
// blockLoop processes as many 32-byte blocks as possible, | |||||
// updating v1, v2, v3, and v4. It assumes that n >= 32. | |||||
#define blockLoop() \ | |||||
LSR $5, n, nblocks \ | |||||
PCALIGN $16 \ | |||||
loop: \ | |||||
LDP.P 16(p), (x1, x2) \ | |||||
LDP.P 16(p), (x3, x4) \ | |||||
round(v1, x1) \ | |||||
round(v2, x2) \ | |||||
round(v3, x3) \ | |||||
round(v4, x4) \ | |||||
SUB $1, nblocks \ | |||||
CBNZ nblocks, loop | |||||
// func Sum64(b []byte) uint64 | |||||
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 | |||||
LDP b_base+0(FP), (p, n) | |||||
LDP ·primes+0(SB), (prime1, prime2) | |||||
LDP ·primes+16(SB), (prime3, prime4) | |||||
MOVD ·primes+32(SB), prime5 | |||||
CMP $32, n | |||||
CSEL LT, prime5, ZR, h // if n < 32 { h = prime5 } else { h = 0 } | |||||
BLT afterLoop | |||||
ADD prime1, prime2, v1 | |||||
MOVD prime2, v2 | |||||
MOVD $0, v3 | |||||
NEG prime1, v4 | |||||
blockLoop() | |||||
ROR $64-1, v1, x1 | |||||
ROR $64-7, v2, x2 | |||||
ADD x1, x2 | |||||
ROR $64-12, v3, x3 | |||||
ROR $64-18, v4, x4 | |||||
ADD x3, x4 | |||||
ADD x2, x4, h | |||||
mergeRound(h, v1) | |||||
mergeRound(h, v2) | |||||
mergeRound(h, v3) | |||||
mergeRound(h, v4) | |||||
afterLoop: | |||||
ADD n, h | |||||
TBZ $4, n, try8 | |||||
LDP.P 16(p), (x1, x2) | |||||
round0(x1) | |||||
// NOTE: here and below, sequencing the EOR after the ROR (using a | |||||
// rotated register) is worth a small but measurable speedup for small | |||||
// inputs. | |||||
ROR $64-27, h | |||||
EOR x1 @> 64-27, h, h | |||||
MADD h, prime4, prime1, h | |||||
round0(x2) | |||||
ROR $64-27, h | |||||
EOR x2 @> 64-27, h, h | |||||
MADD h, prime4, prime1, h | |||||
try8: | |||||
TBZ $3, n, try4 | |||||
MOVD.P 8(p), x1 | |||||
round0(x1) | |||||
ROR $64-27, h | |||||
EOR x1 @> 64-27, h, h | |||||
MADD h, prime4, prime1, h | |||||
try4: | |||||
TBZ $2, n, try2 | |||||
MOVWU.P 4(p), x2 | |||||
MUL prime1, x2 | |||||
ROR $64-23, h | |||||
EOR x2 @> 64-23, h, h | |||||
MADD h, prime3, prime2, h | |||||
try2: | |||||
TBZ $1, n, try1 | |||||
MOVHU.P 2(p), x3 | |||||
AND $255, x3, x1 | |||||
LSR $8, x3, x2 | |||||
MUL prime5, x1 | |||||
ROR $64-11, h | |||||
EOR x1 @> 64-11, h, h | |||||
MUL prime1, h | |||||
MUL prime5, x2 | |||||
ROR $64-11, h | |||||
EOR x2 @> 64-11, h, h | |||||
MUL prime1, h | |||||
try1: | |||||
TBZ $0, n, finalize | |||||
MOVBU (p), x4 | |||||
MUL prime5, x4 | |||||
ROR $64-11, h | |||||
EOR x4 @> 64-11, h, h | |||||
MUL prime1, h | |||||
finalize: | |||||
EOR h >> 33, h | |||||
MUL prime2, h | |||||
EOR h >> 29, h | |||||
MUL prime3, h | |||||
EOR h >> 32, h | |||||
MOVD h, ret+24(FP) | |||||
RET | |||||
// func writeBlocks(d *Digest, b []byte) int | |||||
TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 | |||||
LDP ·primes+0(SB), (prime1, prime2) | |||||
// Load state. Assume v[1-4] are stored contiguously. | |||||
MOVD d+0(FP), digest | |||||
LDP 0(digest), (v1, v2) | |||||
LDP 16(digest), (v3, v4) | |||||
LDP b_base+8(FP), (p, n) | |||||
blockLoop() | |||||
// Store updated state. | |||||
STP (v1, v2), 0(digest) | |||||
STP (v3, v4), 16(digest) | |||||
BIC $31, n | |||||
MOVD n, ret+32(FP) | |||||
RET |
@@ -1,3 +1,5 @@ | |||||
//go:build (amd64 || arm64) && !appengine && gc && !purego | |||||
// +build amd64 arm64 | |||||
// +build !appengine | // +build !appengine | ||||
// +build gc | // +build gc | ||||
// +build !purego | // +build !purego |
@@ -1,4 +1,5 @@ | |||||
// +build !amd64 appengine !gc purego | |||||
//go:build (!amd64 && !arm64) || appengine || !gc || purego | |||||
// +build !amd64,!arm64 appengine !gc purego | |||||
package xxhash | package xxhash | ||||
@@ -14,10 +15,10 @@ func Sum64(b []byte) uint64 { | |||||
var h uint64 | var h uint64 | ||||
if n >= 32 { | if n >= 32 { | ||||
v1 := prime1v + prime2 | |||||
v1 := primes[0] + prime2 | |||||
v2 := prime2 | v2 := prime2 | ||||
v3 := uint64(0) | v3 := uint64(0) | ||||
v4 := -prime1v | |||||
v4 := -primes[0] | |||||
for len(b) >= 32 { | for len(b) >= 32 { | ||||
v1 = round(v1, u64(b[0:8:len(b)])) | v1 = round(v1, u64(b[0:8:len(b)])) | ||||
v2 = round(v2, u64(b[8:16:len(b)])) | v2 = round(v2, u64(b[8:16:len(b)])) | ||||
@@ -36,19 +37,18 @@ func Sum64(b []byte) uint64 { | |||||
h += uint64(n) | h += uint64(n) | ||||
i, end := 0, len(b) | |||||
for ; i+8 <= end; i += 8 { | |||||
k1 := round(0, u64(b[i:i+8:len(b)])) | |||||
for ; len(b) >= 8; b = b[8:] { | |||||
k1 := round(0, u64(b[:8])) | |||||
h ^= k1 | h ^= k1 | ||||
h = rol27(h)*prime1 + prime4 | h = rol27(h)*prime1 + prime4 | ||||
} | } | ||||
if i+4 <= end { | |||||
h ^= uint64(u32(b[i:i+4:len(b)])) * prime1 | |||||
if len(b) >= 4 { | |||||
h ^= uint64(u32(b[:4])) * prime1 | |||||
h = rol23(h)*prime2 + prime3 | h = rol23(h)*prime2 + prime3 | ||||
i += 4 | |||||
b = b[4:] | |||||
} | } | ||||
for ; i < end; i++ { | |||||
h ^= uint64(b[i]) * prime5 | |||||
for ; len(b) > 0; b = b[1:] { | |||||
h ^= uint64(b[0]) * prime5 | |||||
h = rol11(h) * prime1 | h = rol11(h) * prime1 | ||||
} | } | ||||
@@ -1,3 +1,4 @@ | |||||
//go:build appengine | |||||
// +build appengine | // +build appengine | ||||
// This file contains the safe implementations of otherwise unsafe-using code. | // This file contains the safe implementations of otherwise unsafe-using code. | ||||
@@ -1,3 +1,4 @@ | |||||
//go:build !appengine | |||||
// +build !appengine | // +build !appengine | ||||
// This file encapsulates usage of unsafe. | // This file encapsulates usage of unsafe. | ||||
@@ -11,7 +12,7 @@ import ( | |||||
// In the future it's possible that compiler optimizations will make these | // In the future it's possible that compiler optimizations will make these | ||||
// XxxString functions unnecessary by realizing that calls such as | // XxxString functions unnecessary by realizing that calls such as | ||||
// Sum64([]byte(s)) don't need to copy s. See https://golang.org/issue/2205. | |||||
// Sum64([]byte(s)) don't need to copy s. See https://go.dev/issue/2205. | |||||
// If that happens, even if we keep these functions they can be replaced with | // If that happens, even if we keep these functions they can be replaced with | ||||
// the trivial safe code. | // the trivial safe code. | ||||
@@ -1,64 +0,0 @@ | |||||
// Code generated by protoc-gen-go. DO NOT EDIT. | |||||
// source: github.com/golang/protobuf/ptypes/timestamp/timestamp.proto | |||||
package timestamp | |||||
import ( | |||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect" | |||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl" | |||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb" | |||||
reflect "reflect" | |||||
) | |||||
// Symbols defined in public import of google/protobuf/timestamp.proto. | |||||
type Timestamp = timestamppb.Timestamp | |||||
var File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto protoreflect.FileDescriptor | |||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = []byte{ | |||||
0x0a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, | |||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, | |||||
0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x74, 0x69, | |||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, | |||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, | |||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x37, | |||||
0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, | |||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, | |||||
0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3b, 0x74, 0x69, | |||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, | |||||
0x33, | |||||
} | |||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = []interface{}{} | |||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = []int32{ | |||||
0, // [0:0] is the sub-list for method output_type | |||||
0, // [0:0] is the sub-list for method input_type | |||||
0, // [0:0] is the sub-list for extension type_name | |||||
0, // [0:0] is the sub-list for extension extendee | |||||
0, // [0:0] is the sub-list for field type_name | |||||
} | |||||
func init() { file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() } | |||||
func file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() { | |||||
if File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto != nil { | |||||
return | |||||
} | |||||
type x struct{} | |||||
out := protoimpl.TypeBuilder{ | |||||
File: protoimpl.DescBuilder{ | |||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), | |||||
RawDescriptor: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc, | |||||
NumEnums: 0, | |||||
NumMessages: 0, | |||||
NumExtensions: 0, | |||||
NumServices: 0, | |||||
}, | |||||
GoTypes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes, | |||||
DependencyIndexes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs, | |||||
}.Build() | |||||
File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto = out.File | |||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = nil | |||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = nil | |||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = nil | |||||
} |
@@ -0,0 +1,354 @@ | |||||
Mozilla Public License, version 2.0 | |||||
1. Definitions | |||||
1.1. “Contributor” | |||||
means each individual or legal entity that creates, contributes to the | |||||
creation of, or owns Covered Software. | |||||
1.2. “Contributor Version” | |||||
means the combination of the Contributions of others (if any) used by a | |||||
Contributor and that particular Contributor’s Contribution. | |||||
1.3. “Contribution” | |||||
means Covered Software of a particular Contributor. | |||||
1.4. “Covered Software” | |||||
means Source Code Form to which the initial Contributor has attached the | |||||
notice in Exhibit A, the Executable Form of such Source Code Form, and | |||||
Modifications of such Source Code Form, in each case including portions | |||||
thereof. | |||||
1.5. “Incompatible With Secondary Licenses” | |||||
means | |||||
a. that the initial Contributor has attached the notice described in | |||||
Exhibit B to the Covered Software; or | |||||
b. that the Covered Software was made available under the terms of version | |||||
1.1 or earlier of the License, but not also under the terms of a | |||||
Secondary License. | |||||
1.6. “Executable Form” | |||||
means any form of the work other than Source Code Form. | |||||
1.7. “Larger Work” | |||||
means a work that combines Covered Software with other material, in a separate | |||||
file or files, that is not Covered Software. | |||||
1.8. “License” | |||||
means this document. | |||||
1.9. “Licensable” | |||||
means having the right to grant, to the maximum extent possible, whether at the | |||||
time of the initial grant or subsequently, any and all of the rights conveyed by | |||||
this License. | |||||
1.10. “Modifications” | |||||
means any of the following: | |||||
a. any file in Source Code Form that results from an addition to, deletion | |||||
from, or modification of the contents of Covered Software; or | |||||
b. any new file in Source Code Form that contains any Covered Software. | |||||
1.11. “Patent Claims” of a Contributor | |||||
means any patent claim(s), including without limitation, method, process, | |||||
and apparatus claims, in any patent Licensable by such Contributor that | |||||
would be infringed, but for the grant of the License, by the making, | |||||
using, selling, offering for sale, having made, import, or transfer of | |||||
either its Contributions or its Contributor Version. | |||||
1.12. “Secondary License” | |||||
means either the GNU General Public License, Version 2.0, the GNU Lesser | |||||
General Public License, Version 2.1, the GNU Affero General Public | |||||
License, Version 3.0, or any later versions of those licenses. | |||||
1.13. “Source Code Form” | |||||
means the form of the work preferred for making modifications. | |||||
1.14. “You” (or “Your”) | |||||
means an individual or a legal entity exercising rights under this | |||||
License. For legal entities, “You” includes any entity that controls, is | |||||
controlled by, or is under common control with You. For purposes of this | |||||
definition, “control” means (a) the power, direct or indirect, to cause | |||||
the direction or management of such entity, whether by contract or | |||||
otherwise, or (b) ownership of more than fifty percent (50%) of the | |||||
outstanding shares or beneficial ownership of such entity. | |||||
2. License Grants and Conditions | |||||
2.1. Grants | |||||
Each Contributor hereby grants You a world-wide, royalty-free, | |||||
non-exclusive license: | |||||
a. under intellectual property rights (other than patent or trademark) | |||||
Licensable by such Contributor to use, reproduce, make available, | |||||
modify, display, perform, distribute, and otherwise exploit its | |||||
Contributions, either on an unmodified basis, with Modifications, or as | |||||
part of a Larger Work; and | |||||
b. under Patent Claims of such Contributor to make, use, sell, offer for | |||||
sale, have made, import, and otherwise transfer either its Contributions | |||||
or its Contributor Version. | |||||
2.2. Effective Date | |||||
The licenses granted in Section 2.1 with respect to any Contribution become | |||||
effective for each Contribution on the date the Contributor first distributes | |||||
such Contribution. | |||||
2.3. Limitations on Grant Scope | |||||
The licenses granted in this Section 2 are the only rights granted under this | |||||
License. No additional rights or licenses will be implied from the distribution | |||||
or licensing of Covered Software under this License. Notwithstanding Section | |||||
2.1(b) above, no patent license is granted by a Contributor: | |||||
a. for any code that a Contributor has removed from Covered Software; or | |||||
b. for infringements caused by: (i) Your and any other third party’s | |||||
modifications of Covered Software, or (ii) the combination of its | |||||
Contributions with other software (except as part of its Contributor | |||||
Version); or | |||||
c. under Patent Claims infringed by Covered Software in the absence of its | |||||
Contributions. | |||||
This License does not grant any rights in the trademarks, service marks, or | |||||
logos of any Contributor (except as may be necessary to comply with the | |||||
notice requirements in Section 3.4). | |||||
2.4. Subsequent Licenses | |||||
No Contributor makes additional grants as a result of Your choice to | |||||
distribute the Covered Software under a subsequent version of this License | |||||
(see Section 10.2) or under the terms of a Secondary License (if permitted | |||||
under the terms of Section 3.3). | |||||
2.5. Representation | |||||
Each Contributor represents that the Contributor believes its Contributions | |||||
are its original creation(s) or it has sufficient rights to grant the | |||||
rights to its Contributions conveyed by this License. | |||||
2.6. Fair Use | |||||
This License is not intended to limit any rights You have under applicable | |||||
copyright doctrines of fair use, fair dealing, or other equivalents. | |||||
2.7. Conditions | |||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | |||||
Section 2.1. | |||||
3. Responsibilities | |||||
3.1. Distribution of Source Form | |||||
All distribution of Covered Software in Source Code Form, including any | |||||
Modifications that You create or to which You contribute, must be under the | |||||
terms of this License. You must inform recipients that the Source Code Form | |||||
of the Covered Software is governed by the terms of this License, and how | |||||
they can obtain a copy of this License. You may not attempt to alter or | |||||
restrict the recipients’ rights in the Source Code Form. | |||||
3.2. Distribution of Executable Form | |||||
If You distribute Covered Software in Executable Form then: | |||||
a. such Covered Software must also be made available in Source Code Form, | |||||
as described in Section 3.1, and You must inform recipients of the | |||||
Executable Form how they can obtain a copy of such Source Code Form by | |||||
reasonable means in a timely manner, at a charge no more than the cost | |||||
of distribution to the recipient; and | |||||
b. You may distribute such Executable Form under the terms of this License, | |||||
or sublicense it under different terms, provided that the license for | |||||
the Executable Form does not attempt to limit or alter the recipients’ | |||||
rights in the Source Code Form under this License. | |||||
3.3. Distribution of a Larger Work | |||||
You may create and distribute a Larger Work under terms of Your choice, | |||||
provided that You also comply with the requirements of this License for the | |||||
Covered Software. If the Larger Work is a combination of Covered Software | |||||
with a work governed by one or more Secondary Licenses, and the Covered | |||||
Software is not Incompatible With Secondary Licenses, this License permits | |||||
You to additionally distribute such Covered Software under the terms of | |||||
such Secondary License(s), so that the recipient of the Larger Work may, at | |||||
their option, further distribute the Covered Software under the terms of | |||||
either this License or such Secondary License(s). | |||||
3.4. Notices | |||||
You may not remove or alter the substance of any license notices (including | |||||
copyright notices, patent notices, disclaimers of warranty, or limitations | |||||
of liability) contained within the Source Code Form of the Covered | |||||
Software, except that You may alter any license notices to the extent | |||||
required to remedy known factual inaccuracies. | |||||
3.5. Application of Additional Terms | |||||
You may choose to offer, and to charge a fee for, warranty, support, | |||||
indemnity or liability obligations to one or more recipients of Covered | |||||
Software. However, You may do so only on Your own behalf, and not on behalf | |||||
of any Contributor. You must make it absolutely clear that any such | |||||
warranty, support, indemnity, or liability obligation is offered by You | |||||
alone, and You hereby agree to indemnify every Contributor for any | |||||
liability incurred by such Contributor as a result of warranty, support, | |||||
indemnity or liability terms You offer. You may include additional | |||||
disclaimers of warranty and limitations of liability specific to any | |||||
jurisdiction. | |||||
4. Inability to Comply Due to Statute or Regulation | |||||
If it is impossible for You to comply with any of the terms of this License | |||||
with respect to some or all of the Covered Software due to statute, judicial | |||||
order, or regulation then You must: (a) comply with the terms of this License | |||||
to the maximum extent possible; and (b) describe the limitations and the code | |||||
they affect. Such description must be placed in a text file included with all | |||||
distributions of the Covered Software under this License. Except to the | |||||
extent prohibited by statute or regulation, such description must be | |||||
sufficiently detailed for a recipient of ordinary skill to be able to | |||||
understand it. | |||||
5. Termination | |||||
5.1. The rights granted under this License will terminate automatically if You | |||||
fail to comply with any of its terms. However, if You become compliant, | |||||
then the rights granted under this License from a particular Contributor | |||||
are reinstated (a) provisionally, unless and until such Contributor | |||||
explicitly and finally terminates Your grants, and (b) on an ongoing basis, | |||||
if such Contributor fails to notify You of the non-compliance by some | |||||
reasonable means prior to 60 days after You have come back into compliance. | |||||
Moreover, Your grants from a particular Contributor are reinstated on an | |||||
ongoing basis if such Contributor notifies You of the non-compliance by | |||||
some reasonable means, this is the first time You have received notice of | |||||
non-compliance with this License from such Contributor, and You become | |||||
compliant prior to 30 days after Your receipt of the notice. | |||||
5.2. If You initiate litigation against any entity by asserting a patent | |||||
infringement claim (excluding declaratory judgment actions, counter-claims, | |||||
and cross-claims) alleging that a Contributor Version directly or | |||||
indirectly infringes any patent, then the rights granted to You by any and | |||||
all Contributors for the Covered Software under Section 2.1 of this License | |||||
shall terminate. | |||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | |||||
license agreements (excluding distributors and resellers) which have been | |||||
validly granted by You or Your distributors under this License prior to | |||||
termination shall survive termination. | |||||
6. Disclaimer of Warranty | |||||
Covered Software is provided under this License on an “as is” basis, without | |||||
warranty of any kind, either expressed, implied, or statutory, including, | |||||
without limitation, warranties that the Covered Software is free of defects, | |||||
merchantable, fit for a particular purpose or non-infringing. The entire | |||||
risk as to the quality and performance of the Covered Software is with You. | |||||
Should any Covered Software prove defective in any respect, You (not any | |||||
Contributor) assume the cost of any necessary servicing, repair, or | |||||
correction. This disclaimer of warranty constitutes an essential part of this | |||||
License. No use of any Covered Software is authorized under this License | |||||
except under this disclaimer. | |||||
7. Limitation of Liability | |||||
Under no circumstances and under no legal theory, whether tort (including | |||||
negligence), contract, or otherwise, shall any Contributor, or anyone who | |||||
distributes Covered Software as permitted above, be liable to You for any | |||||
direct, indirect, special, incidental, or consequential damages of any | |||||
character including, without limitation, damages for lost profits, loss of | |||||
goodwill, work stoppage, computer failure or malfunction, or any and all | |||||
other commercial damages or losses, even if such party shall have been | |||||
informed of the possibility of such damages. This limitation of liability | |||||
shall not apply to liability for death or personal injury resulting from such | |||||
party’s negligence to the extent applicable law prohibits such limitation. | |||||
Some jurisdictions do not allow the exclusion or limitation of incidental or | |||||
consequential damages, so this exclusion and limitation may not apply to You. | |||||
8. Litigation | |||||
Any litigation relating to this License may be brought only in the courts of | |||||
a jurisdiction where the defendant maintains its principal place of business | |||||
and such litigation shall be governed by laws of that jurisdiction, without | |||||
reference to its conflict-of-law provisions. Nothing in this Section shall | |||||
prevent a party’s ability to bring cross-claims or counter-claims. | |||||
9. Miscellaneous | |||||
This License represents the complete agreement concerning the subject matter | |||||
hereof. If any provision of this License is held to be unenforceable, such | |||||
provision shall be reformed only to the extent necessary to make it | |||||
enforceable. Any law or regulation which provides that the language of a | |||||
contract shall be construed against the drafter shall not be used to construe | |||||
this License against a Contributor. | |||||
10. Versions of the License | |||||
10.1. New Versions | |||||
Mozilla Foundation is the license steward. Except as provided in Section | |||||
10.3, no one other than the license steward has the right to modify or | |||||
publish new versions of this License. Each version will be given a | |||||
distinguishing version number. | |||||
10.2. Effect of New Versions | |||||
You may distribute the Covered Software under the terms of the version of | |||||
the License under which You originally received the Covered Software, or | |||||
under the terms of any subsequent version published by the license | |||||
steward. | |||||
10.3. Modified Versions | |||||
If you create software not governed by this License, and you want to | |||||
create a new license for such software, you may create and use a modified | |||||
version of this License if you rename the license and remove any | |||||
references to the name of the license steward (except to note that such | |||||
modified license differs from this License). | |||||
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses | |||||
If You choose to distribute Source Code Form that is Incompatible With | |||||
Secondary Licenses under the terms of this version of the License, the | |||||
notice described in Exhibit B of this License must be attached. | |||||
Exhibit A - Source Code Form License Notice | |||||
This Source Code Form is subject to the | |||||
terms of the Mozilla Public License, v. | |||||
2.0. If a copy of the MPL was not | |||||
distributed with this file, You can | |||||
obtain one at | |||||
http://mozilla.org/MPL/2.0/. | |||||
If it is not possible or desirable to put the notice in a particular file, then | |||||
You may include the notice in a location (such as a LICENSE file in a relevant | |||||
directory) where a recipient would be likely to look for such a notice. | |||||
You may add additional accurate notices of copyright ownership. | |||||
Exhibit B - “Incompatible With Secondary Licenses” Notice | |||||
This Source Code Form is “Incompatible | |||||
With Secondary Licenses”, as defined by | |||||
the Mozilla Public License, v. 2.0. | |||||
@@ -0,0 +1,89 @@ | |||||
# errwrap | |||||
`errwrap` is a package for Go that formalizes the pattern of wrapping errors | |||||
and checking if an error contains another error. | |||||
There is a common pattern in Go of taking a returned `error` value and | |||||
then wrapping it (such as with `fmt.Errorf`) before returning it. The problem | |||||
with this pattern is that you completely lose the original `error` structure. | |||||
Arguably the _correct_ approach is that you should make a custom structure | |||||
implementing the `error` interface, and have the original error as a field | |||||
on that structure, such [as this example](http://golang.org/pkg/os/#PathError). | |||||
This is a good approach, but you have to know the entire chain of possible | |||||
rewrapping that happens, when you might just care about one. | |||||
`errwrap` formalizes this pattern (it doesn't matter what approach you use | |||||
above) by giving a single interface for wrapping errors, checking if a specific | |||||
error is wrapped, and extracting that error. | |||||
## Installation and Docs | |||||
Install using `go get github.com/hashicorp/errwrap`. | |||||
Full documentation is available at | |||||
http://godoc.org/github.com/hashicorp/errwrap | |||||
## Usage | |||||
#### Basic Usage | |||||
Below is a very basic example of its usage: | |||||
```go | |||||
// A function that always returns an error, but wraps it, like a real | |||||
// function might. | |||||
func tryOpen() error { | |||||
_, err := os.Open("/i/dont/exist") | |||||
if err != nil { | |||||
return errwrap.Wrapf("Doesn't exist: {{err}}", err) | |||||
} | |||||
return nil | |||||
} | |||||
func main() { | |||||
err := tryOpen() | |||||
// We can use the Contains helpers to check if an error contains | |||||
// another error. It is safe to do this with a nil error, or with | |||||
// an error that doesn't even use the errwrap package. | |||||
if errwrap.Contains(err, "does not exist") { | |||||
// Do something | |||||
} | |||||
if errwrap.ContainsType(err, new(os.PathError)) { | |||||
// Do something | |||||
} | |||||
// Or we can use the associated `Get` functions to just extract | |||||
// a specific error. This would return nil if that specific error doesn't | |||||
// exist. | |||||
perr := errwrap.GetType(err, new(os.PathError)) | |||||
} | |||||
``` | |||||
#### Custom Types | |||||
If you're already making custom types that properly wrap errors, then | |||||
you can get all the functionality of `errwraps.Contains` and such by | |||||
implementing the `Wrapper` interface with just one function. Example: | |||||
```go | |||||
type AppError { | |||||
Code ErrorCode | |||||
Err error | |||||
} | |||||
func (e *AppError) WrappedErrors() []error { | |||||
return []error{e.Err} | |||||
} | |||||
``` | |||||
Now this works: | |||||
```go | |||||
err := &AppError{Err: fmt.Errorf("an error")} | |||||
if errwrap.ContainsType(err, fmt.Errorf("")) { | |||||
// This will work! | |||||
} | |||||
``` |
@@ -0,0 +1,178 @@ | |||||
// Package errwrap implements methods to formalize error wrapping in Go. | |||||
// | |||||
// All of the top-level functions that take an `error` are built to be able | |||||
// to take any error, not just wrapped errors. This allows you to use errwrap | |||||
// without having to type-check and type-cast everywhere. | |||||
package errwrap | |||||
import ( | |||||
"errors" | |||||
"reflect" | |||||
"strings" | |||||
) | |||||
// WalkFunc is the callback called for Walk. | |||||
type WalkFunc func(error) | |||||
// Wrapper is an interface that can be implemented by custom types to | |||||
// have all the Contains, Get, etc. functions in errwrap work. | |||||
// | |||||
// When Walk reaches a Wrapper, it will call the callback for every | |||||
// wrapped error in addition to the wrapper itself. Since all the top-level | |||||
// functions in errwrap use Walk, this means that all those functions work | |||||
// with your custom type. | |||||
type Wrapper interface { | |||||
WrappedErrors() []error | |||||
} | |||||
// Wrap defines that outer wraps inner, returning an error type that | |||||
// can be cleanly used with the other methods in this package, such as | |||||
// Contains, GetAll, etc. | |||||
// | |||||
// This function won't modify the error message at all (the outer message | |||||
// will be used). | |||||
func Wrap(outer, inner error) error { | |||||
return &wrappedError{ | |||||
Outer: outer, | |||||
Inner: inner, | |||||
} | |||||
} | |||||
// Wrapf wraps an error with a formatting message. This is similar to using | |||||
// `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap | |||||
// errors, you should replace it with this. | |||||
// | |||||
// format is the format of the error message. The string '{{err}}' will | |||||
// be replaced with the original error message. | |||||
// | |||||
// Deprecated: Use fmt.Errorf() | |||||
func Wrapf(format string, err error) error { | |||||
outerMsg := "<nil>" | |||||
if err != nil { | |||||
outerMsg = err.Error() | |||||
} | |||||
outer := errors.New(strings.Replace( | |||||
format, "{{err}}", outerMsg, -1)) | |||||
return Wrap(outer, err) | |||||
} | |||||
// Contains checks if the given error contains an error with the | |||||
// message msg. If err is not a wrapped error, this will always return | |||||
// false unless the error itself happens to match this msg. | |||||
func Contains(err error, msg string) bool { | |||||
return len(GetAll(err, msg)) > 0 | |||||
} | |||||
// ContainsType checks if the given error contains an error with | |||||
// the same concrete type as v. If err is not a wrapped error, this will | |||||
// check the err itself. | |||||
func ContainsType(err error, v interface{}) bool { | |||||
return len(GetAllType(err, v)) > 0 | |||||
} | |||||
// Get is the same as GetAll but returns the deepest matching error. | |||||
func Get(err error, msg string) error { | |||||
es := GetAll(err, msg) | |||||
if len(es) > 0 { | |||||
return es[len(es)-1] | |||||
} | |||||
return nil | |||||
} | |||||
// GetType is the same as GetAllType but returns the deepest matching error. | |||||
func GetType(err error, v interface{}) error { | |||||
es := GetAllType(err, v) | |||||
if len(es) > 0 { | |||||
return es[len(es)-1] | |||||
} | |||||
return nil | |||||
} | |||||
// GetAll gets all the errors that might be wrapped in err with the | |||||
// given message. The order of the errors is such that the outermost | |||||
// matching error (the most recent wrap) is index zero, and so on. | |||||
func GetAll(err error, msg string) []error { | |||||
var result []error | |||||
Walk(err, func(err error) { | |||||
if err.Error() == msg { | |||||
result = append(result, err) | |||||
} | |||||
}) | |||||
return result | |||||
} | |||||
// GetAllType gets all the errors that are the same type as v. | |||||
// | |||||
// The order of the return value is the same as described in GetAll. | |||||
func GetAllType(err error, v interface{}) []error { | |||||
var result []error | |||||
var search string | |||||
if v != nil { | |||||
search = reflect.TypeOf(v).String() | |||||
} | |||||
Walk(err, func(err error) { | |||||
var needle string | |||||
if err != nil { | |||||
needle = reflect.TypeOf(err).String() | |||||
} | |||||
if needle == search { | |||||
result = append(result, err) | |||||
} | |||||
}) | |||||
return result | |||||
} | |||||
// Walk walks all the wrapped errors in err and calls the callback. If | |||||
// err isn't a wrapped error, this will be called once for err. If err | |||||
// is a wrapped error, the callback will be called for both the wrapper | |||||
// that implements error as well as the wrapped error itself. | |||||
func Walk(err error, cb WalkFunc) { | |||||
if err == nil { | |||||
return | |||||
} | |||||
switch e := err.(type) { | |||||
case *wrappedError: | |||||
cb(e.Outer) | |||||
Walk(e.Inner, cb) | |||||
case Wrapper: | |||||
cb(err) | |||||
for _, err := range e.WrappedErrors() { | |||||
Walk(err, cb) | |||||
} | |||||
case interface{ Unwrap() error }: | |||||
cb(err) | |||||
Walk(e.Unwrap(), cb) | |||||
default: | |||||
cb(err) | |||||
} | |||||
} | |||||
// wrappedError is an implementation of error that has both the | |||||
// outer and inner errors. | |||||
type wrappedError struct { | |||||
Outer error | |||||
Inner error | |||||
} | |||||
func (w *wrappedError) Error() string { | |||||
return w.Outer.Error() | |||||
} | |||||
func (w *wrappedError) WrappedErrors() []error { | |||||
return []error{w.Outer, w.Inner} | |||||
} | |||||
func (w *wrappedError) Unwrap() error { | |||||
return w.Inner | |||||
} |
@@ -0,0 +1,353 @@ | |||||
Mozilla Public License, version 2.0 | |||||
1. Definitions | |||||
1.1. “Contributor” | |||||
means each individual or legal entity that creates, contributes to the | |||||
creation of, or owns Covered Software. | |||||
1.2. “Contributor Version” | |||||
means the combination of the Contributions of others (if any) used by a | |||||
Contributor and that particular Contributor’s Contribution. | |||||
1.3. “Contribution” | |||||
means Covered Software of a particular Contributor. | |||||
1.4. “Covered Software” | |||||
means Source Code Form to which the initial Contributor has attached the | |||||
notice in Exhibit A, the Executable Form of such Source Code Form, and | |||||
Modifications of such Source Code Form, in each case including portions | |||||
thereof. | |||||
1.5. “Incompatible With Secondary Licenses” | |||||
means | |||||
a. that the initial Contributor has attached the notice described in | |||||
Exhibit B to the Covered Software; or | |||||
b. that the Covered Software was made available under the terms of version | |||||
1.1 or earlier of the License, but not also under the terms of a | |||||
Secondary License. | |||||
1.6. “Executable Form” | |||||
means any form of the work other than Source Code Form. | |||||
1.7. “Larger Work” | |||||
means a work that combines Covered Software with other material, in a separate | |||||
file or files, that is not Covered Software. | |||||
1.8. “License” | |||||
means this document. | |||||
1.9. “Licensable” | |||||
means having the right to grant, to the maximum extent possible, whether at the | |||||
time of the initial grant or subsequently, any and all of the rights conveyed by | |||||
this License. | |||||
1.10. “Modifications” | |||||
means any of the following: | |||||
a. any file in Source Code Form that results from an addition to, deletion | |||||
from, or modification of the contents of Covered Software; or | |||||
b. any new file in Source Code Form that contains any Covered Software. | |||||
1.11. “Patent Claims” of a Contributor | |||||
means any patent claim(s), including without limitation, method, process, | |||||
and apparatus claims, in any patent Licensable by such Contributor that | |||||
would be infringed, but for the grant of the License, by the making, | |||||
using, selling, offering for sale, having made, import, or transfer of | |||||
either its Contributions or its Contributor Version. | |||||
1.12. “Secondary License” | |||||
means either the GNU General Public License, Version 2.0, the GNU Lesser | |||||
General Public License, Version 2.1, the GNU Affero General Public | |||||
License, Version 3.0, or any later versions of those licenses. | |||||
1.13. “Source Code Form” | |||||
means the form of the work preferred for making modifications. | |||||
1.14. “You” (or “Your”) | |||||
means an individual or a legal entity exercising rights under this | |||||
License. For legal entities, “You” includes any entity that controls, is | |||||
controlled by, or is under common control with You. For purposes of this | |||||
definition, “control” means (a) the power, direct or indirect, to cause | |||||
the direction or management of such entity, whether by contract or | |||||
otherwise, or (b) ownership of more than fifty percent (50%) of the | |||||
outstanding shares or beneficial ownership of such entity. | |||||
2. License Grants and Conditions | |||||
2.1. Grants | |||||
Each Contributor hereby grants You a world-wide, royalty-free, | |||||
non-exclusive license: | |||||
a. under intellectual property rights (other than patent or trademark) | |||||
Licensable by such Contributor to use, reproduce, make available, | |||||
modify, display, perform, distribute, and otherwise exploit its | |||||
Contributions, either on an unmodified basis, with Modifications, or as | |||||
part of a Larger Work; and | |||||
b. under Patent Claims of such Contributor to make, use, sell, offer for | |||||
sale, have made, import, and otherwise transfer either its Contributions | |||||
or its Contributor Version. | |||||
2.2. Effective Date | |||||
The licenses granted in Section 2.1 with respect to any Contribution become | |||||
effective for each Contribution on the date the Contributor first distributes | |||||
such Contribution. | |||||
2.3. Limitations on Grant Scope | |||||
The licenses granted in this Section 2 are the only rights granted under this | |||||
License. No additional rights or licenses will be implied from the distribution | |||||
or licensing of Covered Software under this License. Notwithstanding Section | |||||
2.1(b) above, no patent license is granted by a Contributor: | |||||
a. for any code that a Contributor has removed from Covered Software; or | |||||
b. for infringements caused by: (i) Your and any other third party’s | |||||
modifications of Covered Software, or (ii) the combination of its | |||||
Contributions with other software (except as part of its Contributor | |||||
Version); or | |||||
c. under Patent Claims infringed by Covered Software in the absence of its | |||||
Contributions. | |||||
This License does not grant any rights in the trademarks, service marks, or | |||||
logos of any Contributor (except as may be necessary to comply with the | |||||
notice requirements in Section 3.4). | |||||
2.4. Subsequent Licenses | |||||
No Contributor makes additional grants as a result of Your choice to | |||||
distribute the Covered Software under a subsequent version of this License | |||||
(see Section 10.2) or under the terms of a Secondary License (if permitted | |||||
under the terms of Section 3.3). | |||||
2.5. Representation | |||||
Each Contributor represents that the Contributor believes its Contributions | |||||
are its original creation(s) or it has sufficient rights to grant the | |||||
rights to its Contributions conveyed by this License. | |||||
2.6. Fair Use | |||||
This License is not intended to limit any rights You have under applicable | |||||
copyright doctrines of fair use, fair dealing, or other equivalents. | |||||
2.7. Conditions | |||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | |||||
Section 2.1. | |||||
3. Responsibilities | |||||
3.1. Distribution of Source Form | |||||
All distribution of Covered Software in Source Code Form, including any | |||||
Modifications that You create or to which You contribute, must be under the | |||||
terms of this License. You must inform recipients that the Source Code Form | |||||
of the Covered Software is governed by the terms of this License, and how | |||||
they can obtain a copy of this License. You may not attempt to alter or | |||||
restrict the recipients’ rights in the Source Code Form. | |||||
3.2. Distribution of Executable Form | |||||
If You distribute Covered Software in Executable Form then: | |||||
a. such Covered Software must also be made available in Source Code Form, | |||||
as described in Section 3.1, and You must inform recipients of the | |||||
Executable Form how they can obtain a copy of such Source Code Form by | |||||
reasonable means in a timely manner, at a charge no more than the cost | |||||
of distribution to the recipient; and | |||||
b. You may distribute such Executable Form under the terms of this License, | |||||
or sublicense it under different terms, provided that the license for | |||||
the Executable Form does not attempt to limit or alter the recipients’ | |||||
rights in the Source Code Form under this License. | |||||
3.3. Distribution of a Larger Work | |||||
You may create and distribute a Larger Work under terms of Your choice, | |||||
provided that You also comply with the requirements of this License for the | |||||
Covered Software. If the Larger Work is a combination of Covered Software | |||||
with a work governed by one or more Secondary Licenses, and the Covered | |||||
Software is not Incompatible With Secondary Licenses, this License permits | |||||
You to additionally distribute such Covered Software under the terms of | |||||
such Secondary License(s), so that the recipient of the Larger Work may, at | |||||
their option, further distribute the Covered Software under the terms of | |||||
either this License or such Secondary License(s). | |||||
3.4. Notices | |||||
You may not remove or alter the substance of any license notices (including | |||||
copyright notices, patent notices, disclaimers of warranty, or limitations | |||||
of liability) contained within the Source Code Form of the Covered | |||||
Software, except that You may alter any license notices to the extent | |||||
required to remedy known factual inaccuracies. | |||||
3.5. Application of Additional Terms | |||||
You may choose to offer, and to charge a fee for, warranty, support, | |||||
indemnity or liability obligations to one or more recipients of Covered | |||||
Software. However, You may do so only on Your own behalf, and not on behalf | |||||
of any Contributor. You must make it absolutely clear that any such | |||||
warranty, support, indemnity, or liability obligation is offered by You | |||||
alone, and You hereby agree to indemnify every Contributor for any | |||||
liability incurred by such Contributor as a result of warranty, support, | |||||
indemnity or liability terms You offer. You may include additional | |||||
disclaimers of warranty and limitations of liability specific to any | |||||
jurisdiction. | |||||
4. Inability to Comply Due to Statute or Regulation | |||||
If it is impossible for You to comply with any of the terms of this License | |||||
with respect to some or all of the Covered Software due to statute, judicial | |||||
order, or regulation then You must: (a) comply with the terms of this License | |||||
to the maximum extent possible; and (b) describe the limitations and the code | |||||
they affect. Such description must be placed in a text file included with all | |||||
distributions of the Covered Software under this License. Except to the | |||||
extent prohibited by statute or regulation, such description must be | |||||
sufficiently detailed for a recipient of ordinary skill to be able to | |||||
understand it. | |||||
5. Termination | |||||
5.1. The rights granted under this License will terminate automatically if You | |||||
fail to comply with any of its terms. However, if You become compliant, | |||||
then the rights granted under this License from a particular Contributor | |||||
are reinstated (a) provisionally, unless and until such Contributor | |||||
explicitly and finally terminates Your grants, and (b) on an ongoing basis, | |||||
if such Contributor fails to notify You of the non-compliance by some | |||||
reasonable means prior to 60 days after You have come back into compliance. | |||||
Moreover, Your grants from a particular Contributor are reinstated on an | |||||
ongoing basis if such Contributor notifies You of the non-compliance by | |||||
some reasonable means, this is the first time You have received notice of | |||||
non-compliance with this License from such Contributor, and You become | |||||
compliant prior to 30 days after Your receipt of the notice. | |||||
5.2. If You initiate litigation against any entity by asserting a patent | |||||
infringement claim (excluding declaratory judgment actions, counter-claims, | |||||
and cross-claims) alleging that a Contributor Version directly or | |||||
indirectly infringes any patent, then the rights granted to You by any and | |||||
all Contributors for the Covered Software under Section 2.1 of this License | |||||
shall terminate. | |||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | |||||
license agreements (excluding distributors and resellers) which have been | |||||
validly granted by You or Your distributors under this License prior to | |||||
termination shall survive termination. | |||||
6. Disclaimer of Warranty | |||||
Covered Software is provided under this License on an “as is” basis, without | |||||
warranty of any kind, either expressed, implied, or statutory, including, | |||||
without limitation, warranties that the Covered Software is free of defects, | |||||
merchantable, fit for a particular purpose or non-infringing. The entire | |||||
risk as to the quality and performance of the Covered Software is with You. | |||||
Should any Covered Software prove defective in any respect, You (not any | |||||
Contributor) assume the cost of any necessary servicing, repair, or | |||||
correction. This disclaimer of warranty constitutes an essential part of this | |||||
License. No use of any Covered Software is authorized under this License | |||||
except under this disclaimer. | |||||
7. Limitation of Liability | |||||
Under no circumstances and under no legal theory, whether tort (including | |||||
negligence), contract, or otherwise, shall any Contributor, or anyone who | |||||
distributes Covered Software as permitted above, be liable to You for any | |||||
direct, indirect, special, incidental, or consequential damages of any | |||||
character including, without limitation, damages for lost profits, loss of | |||||
goodwill, work stoppage, computer failure or malfunction, or any and all | |||||
other commercial damages or losses, even if such party shall have been | |||||
informed of the possibility of such damages. This limitation of liability | |||||
shall not apply to liability for death or personal injury resulting from such | |||||
party’s negligence to the extent applicable law prohibits such limitation. | |||||
Some jurisdictions do not allow the exclusion or limitation of incidental or | |||||
consequential damages, so this exclusion and limitation may not apply to You. | |||||
8. Litigation | |||||
Any litigation relating to this License may be brought only in the courts of | |||||
a jurisdiction where the defendant maintains its principal place of business | |||||
and such litigation shall be governed by laws of that jurisdiction, without | |||||
reference to its conflict-of-law provisions. Nothing in this Section shall | |||||
prevent a party’s ability to bring cross-claims or counter-claims. | |||||
9. Miscellaneous | |||||
This License represents the complete agreement concerning the subject matter | |||||
hereof. If any provision of this License is held to be unenforceable, such | |||||
provision shall be reformed only to the extent necessary to make it | |||||
enforceable. Any law or regulation which provides that the language of a | |||||
contract shall be construed against the drafter shall not be used to construe | |||||
this License against a Contributor. | |||||
10. Versions of the License | |||||
10.1. New Versions | |||||
Mozilla Foundation is the license steward. Except as provided in Section | |||||
10.3, no one other than the license steward has the right to modify or | |||||
publish new versions of this License. Each version will be given a | |||||
distinguishing version number. | |||||
10.2. Effect of New Versions | |||||
You may distribute the Covered Software under the terms of the version of | |||||
the License under which You originally received the Covered Software, or | |||||
under the terms of any subsequent version published by the license | |||||
steward. | |||||
10.3. Modified Versions | |||||
If you create software not governed by this License, and you want to | |||||
create a new license for such software, you may create and use a modified | |||||
version of this License if you rename the license and remove any | |||||
references to the name of the license steward (except to note that such | |||||
modified license differs from this License). | |||||
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses | |||||
If You choose to distribute Source Code Form that is Incompatible With | |||||
Secondary Licenses under the terms of this version of the License, the | |||||
notice described in Exhibit B of this License must be attached. | |||||
Exhibit A - Source Code Form License Notice | |||||
This Source Code Form is subject to the | |||||
terms of the Mozilla Public License, v. | |||||
2.0. If a copy of the MPL was not | |||||
distributed with this file, You can | |||||
obtain one at | |||||
http://mozilla.org/MPL/2.0/. | |||||
If it is not possible or desirable to put the notice in a particular file, then | |||||
You may include the notice in a location (such as a LICENSE file in a relevant | |||||
directory) where a recipient would be likely to look for such a notice. | |||||
You may add additional accurate notices of copyright ownership. | |||||
Exhibit B - “Incompatible With Secondary Licenses” Notice | |||||
This Source Code Form is “Incompatible | |||||
With Secondary Licenses”, as defined by | |||||
the Mozilla Public License, v. 2.0. |
@@ -0,0 +1,31 @@ | |||||
TEST?=./... | |||||
default: test | |||||
# test runs the test suite and vets the code. | |||||
test: generate | |||||
@echo "==> Running tests..." | |||||
@go list $(TEST) \ | |||||
| grep -v "/vendor/" \ | |||||
| xargs -n1 go test -timeout=60s -parallel=10 ${TESTARGS} | |||||
# testrace runs the race checker | |||||
testrace: generate | |||||
@echo "==> Running tests (race)..." | |||||
@go list $(TEST) \ | |||||
| grep -v "/vendor/" \ | |||||
| xargs -n1 go test -timeout=60s -race ${TESTARGS} | |||||
# updatedeps installs all the dependencies needed to run and build. | |||||
updatedeps: | |||||
@sh -c "'${CURDIR}/scripts/deps.sh' '${NAME}'" | |||||
# generate runs `go generate` to build the dynamically generated source files. | |||||
generate: | |||||
@echo "==> Generating..." | |||||
@find . -type f -name '.DS_Store' -delete | |||||
@go list ./... \ | |||||
| grep -v "/vendor/" \ | |||||
| xargs -n1 go generate | |||||
.PHONY: default test testrace updatedeps generate |
@@ -0,0 +1,150 @@ | |||||
# go-multierror | |||||
[![CircleCI](https://img.shields.io/circleci/build/github/hashicorp/go-multierror/master)](https://circleci.com/gh/hashicorp/go-multierror) | |||||
[![Go Reference](https://pkg.go.dev/badge/github.com/hashicorp/go-multierror.svg)](https://pkg.go.dev/github.com/hashicorp/go-multierror) | |||||
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/hashicorp/go-multierror) | |||||
[circleci]: https://app.circleci.com/pipelines/github/hashicorp/go-multierror | |||||
[godocs]: https://pkg.go.dev/github.com/hashicorp/go-multierror | |||||
`go-multierror` is a package for Go that provides a mechanism for | |||||
representing a list of `error` values as a single `error`. | |||||
This allows a function in Go to return an `error` that might actually | |||||
be a list of errors. If the caller knows this, they can unwrap the | |||||
list and access the errors. If the caller doesn't know, the error | |||||
formats to a nice human-readable format. | |||||
`go-multierror` is fully compatible with the Go standard library | |||||
[errors](https://golang.org/pkg/errors/) package, including the | |||||
functions `As`, `Is`, and `Unwrap`. This provides a standardized approach | |||||
for introspecting on error values. | |||||
## Installation and Docs | |||||
Install using `go get github.com/hashicorp/go-multierror`. | |||||
Full documentation is available at | |||||
https://pkg.go.dev/github.com/hashicorp/go-multierror | |||||
### Requires go version 1.13 or newer | |||||
`go-multierror` requires go version 1.13 or newer. Go 1.13 introduced | |||||
[error wrapping](https://golang.org/doc/go1.13#error_wrapping), which | |||||
this library takes advantage of. | |||||
If you need to use an earlier version of go, you can use the | |||||
[v1.0.0](https://github.com/hashicorp/go-multierror/tree/v1.0.0) | |||||
tag, which doesn't rely on features in go 1.13. | |||||
If you see compile errors that look like the below, it's likely that | |||||
you're on an older version of go: | |||||
``` | |||||
/go/src/github.com/hashicorp/go-multierror/multierror.go:112:9: undefined: errors.As | |||||
/go/src/github.com/hashicorp/go-multierror/multierror.go:117:9: undefined: errors.Is | |||||
``` | |||||
## Usage | |||||
go-multierror is easy to use and purposely built to be unobtrusive in | |||||
existing Go applications/libraries that may not be aware of it. | |||||
**Building a list of errors** | |||||
The `Append` function is used to create a list of errors. This function | |||||
behaves a lot like the Go built-in `append` function: it doesn't matter | |||||
if the first argument is nil, a `multierror.Error`, or any other `error`, | |||||
the function behaves as you would expect. | |||||
```go | |||||
var result error | |||||
if err := step1(); err != nil { | |||||
result = multierror.Append(result, err) | |||||
} | |||||
if err := step2(); err != nil { | |||||
result = multierror.Append(result, err) | |||||
} | |||||
return result | |||||
``` | |||||
**Customizing the formatting of the errors** | |||||
By specifying a custom `ErrorFormat`, you can customize the format | |||||
of the `Error() string` function: | |||||
```go | |||||
var result *multierror.Error | |||||
// ... accumulate errors here, maybe using Append | |||||
if result != nil { | |||||
result.ErrorFormat = func([]error) string { | |||||
return "errors!" | |||||
} | |||||
} | |||||
``` | |||||
**Accessing the list of errors** | |||||
`multierror.Error` implements `error` so if the caller doesn't know about | |||||
multierror, it will work just fine. But if you're aware a multierror might | |||||
be returned, you can use type switches to access the list of errors: | |||||
```go | |||||
if err := something(); err != nil { | |||||
if merr, ok := err.(*multierror.Error); ok { | |||||
// Use merr.Errors | |||||
} | |||||
} | |||||
``` | |||||
You can also use the standard [`errors.Unwrap`](https://golang.org/pkg/errors/#Unwrap) | |||||
function. This will continue to unwrap into subsequent errors until none exist. | |||||
**Extracting an error** | |||||
The standard library [`errors.As`](https://golang.org/pkg/errors/#As) | |||||
function can be used directly with a multierror to extract a specific error: | |||||
```go | |||||
// Assume err is a multierror value | |||||
err := somefunc() | |||||
// We want to know if "err" has a "RichErrorType" in it and extract it. | |||||
var errRich RichErrorType | |||||
if errors.As(err, &errRich) { | |||||
// It has it, and now errRich is populated. | |||||
} | |||||
``` | |||||
**Checking for an exact error value** | |||||
Some errors are returned as exact errors such as the [`ErrNotExist`](https://golang.org/pkg/os/#pkg-variables) | |||||
error in the `os` package. You can check if this error is present by using | |||||
the standard [`errors.Is`](https://golang.org/pkg/errors/#Is) function. | |||||
```go | |||||
// Assume err is a multierror value | |||||
err := somefunc() | |||||
if errors.Is(err, os.ErrNotExist) { | |||||
// err contains os.ErrNotExist | |||||
} | |||||
``` | |||||
**Returning a multierror only if there are errors** | |||||
If you build a `multierror.Error`, you can use the `ErrorOrNil` function | |||||
to return an `error` implementation only if there are errors to return: | |||||
```go | |||||
var result *multierror.Error | |||||
// ... accumulate errors here | |||||
// Return the `error` only if errors were added to the multierror, otherwise | |||||
// return nil since there are no errors. | |||||
return result.ErrorOrNil() | |||||
``` |
@@ -0,0 +1,43 @@ | |||||
package multierror | |||||
// Append is a helper function that will append more errors | |||||
// onto an Error in order to create a larger multi-error. | |||||
// | |||||
// If err is not a multierror.Error, then it will be turned into | |||||
// one. If any of the errs are multierr.Error, they will be flattened | |||||
// one level into err. | |||||
// Any nil errors within errs will be ignored. If err is nil, a new | |||||
// *Error will be returned. | |||||
func Append(err error, errs ...error) *Error { | |||||
switch err := err.(type) { | |||||
case *Error: | |||||
// Typed nils can reach here, so initialize if we are nil | |||||
if err == nil { | |||||
err = new(Error) | |||||
} | |||||
// Go through each error and flatten | |||||
for _, e := range errs { | |||||
switch e := e.(type) { | |||||
case *Error: | |||||
if e != nil { | |||||
err.Errors = append(err.Errors, e.Errors...) | |||||
} | |||||
default: | |||||
if e != nil { | |||||
err.Errors = append(err.Errors, e) | |||||
} | |||||
} | |||||
} | |||||
return err | |||||
default: | |||||
newErrs := make([]error, 0, len(errs)+1) | |||||
if err != nil { | |||||
newErrs = append(newErrs, err) | |||||
} | |||||
newErrs = append(newErrs, errs...) | |||||
return Append(&Error{}, newErrs...) | |||||
} | |||||
} |
@@ -0,0 +1,26 @@ | |||||
package multierror | |||||
// Flatten flattens the given error, merging any *Errors together into | |||||
// a single *Error. | |||||
func Flatten(err error) error { | |||||
// If it isn't an *Error, just return the error as-is | |||||
if _, ok := err.(*Error); !ok { | |||||
return err | |||||
} | |||||
// Otherwise, make the result and flatten away! | |||||
flatErr := new(Error) | |||||
flatten(err, flatErr) | |||||
return flatErr | |||||
} | |||||
func flatten(err error, flatErr *Error) { | |||||
switch err := err.(type) { | |||||
case *Error: | |||||
for _, e := range err.Errors { | |||||
flatten(e, flatErr) | |||||
} | |||||
default: | |||||
flatErr.Errors = append(flatErr.Errors, err) | |||||
} | |||||
} |
@@ -0,0 +1,27 @@ | |||||
package multierror | |||||
import ( | |||||
"fmt" | |||||
"strings" | |||||
) | |||||
// ErrorFormatFunc is a function callback that is called by Error to | |||||
// turn the list of errors into a string. | |||||
type ErrorFormatFunc func([]error) string | |||||
// ListFormatFunc is a basic formatter that outputs the number of errors | |||||
// that occurred along with a bullet point list of the errors. | |||||
func ListFormatFunc(es []error) string { | |||||
if len(es) == 1 { | |||||
return fmt.Sprintf("1 error occurred:\n\t* %s\n\n", es[0]) | |||||
} | |||||
points := make([]string, len(es)) | |||||
for i, err := range es { | |||||
points[i] = fmt.Sprintf("* %s", err) | |||||
} | |||||
return fmt.Sprintf( | |||||
"%d errors occurred:\n\t%s\n\n", | |||||
len(es), strings.Join(points, "\n\t")) | |||||
} |
@@ -0,0 +1,38 @@ | |||||
package multierror | |||||
import "sync" | |||||
// Group is a collection of goroutines which return errors that need to be | |||||
// coalesced. | |||||
type Group struct { | |||||
mutex sync.Mutex | |||||
err *Error | |||||
wg sync.WaitGroup | |||||
} | |||||
// Go calls the given function in a new goroutine. | |||||
// | |||||
// If the function returns an error it is added to the group multierror which | |||||
// is returned by Wait. | |||||
func (g *Group) Go(f func() error) { | |||||
g.wg.Add(1) | |||||
go func() { | |||||
defer g.wg.Done() | |||||
if err := f(); err != nil { | |||||
g.mutex.Lock() | |||||
g.err = Append(g.err, err) | |||||
g.mutex.Unlock() | |||||
} | |||||
}() | |||||
} | |||||
// Wait blocks until all function calls from the Go method have returned, then | |||||
// returns the multierror. | |||||
func (g *Group) Wait() *Error { | |||||
g.wg.Wait() | |||||
g.mutex.Lock() | |||||
defer g.mutex.Unlock() | |||||
return g.err | |||||
} |
@@ -0,0 +1,121 @@ | |||||
package multierror | |||||
import ( | |||||
"errors" | |||||
"fmt" | |||||
) | |||||
// Error is an error type to track multiple errors. This is used to | |||||
// accumulate errors in cases and return them as a single "error". | |||||
type Error struct { | |||||
Errors []error | |||||
ErrorFormat ErrorFormatFunc | |||||
} | |||||
func (e *Error) Error() string { | |||||
fn := e.ErrorFormat | |||||
if fn == nil { | |||||
fn = ListFormatFunc | |||||
} | |||||
return fn(e.Errors) | |||||
} | |||||
// ErrorOrNil returns an error interface if this Error represents | |||||
// a list of errors, or returns nil if the list of errors is empty. This | |||||
// function is useful at the end of accumulation to make sure that the value | |||||
// returned represents the existence of errors. | |||||
func (e *Error) ErrorOrNil() error { | |||||
if e == nil { | |||||
return nil | |||||
} | |||||
if len(e.Errors) == 0 { | |||||
return nil | |||||
} | |||||
return e | |||||
} | |||||
func (e *Error) GoString() string { | |||||
return fmt.Sprintf("*%#v", *e) | |||||
} | |||||
// WrappedErrors returns the list of errors that this Error is wrapping. It is | |||||
// an implementation of the errwrap.Wrapper interface so that multierror.Error | |||||
// can be used with that library. | |||||
// | |||||
// This method is not safe to be called concurrently. Unlike accessing the | |||||
// Errors field directly, this function also checks if the multierror is nil to | |||||
// prevent a null-pointer panic. It satisfies the errwrap.Wrapper interface. | |||||
func (e *Error) WrappedErrors() []error { | |||||
if e == nil { | |||||
return nil | |||||
} | |||||
return e.Errors | |||||
} | |||||
// Unwrap returns an error from Error (or nil if there are no errors). | |||||
// This error returned will further support Unwrap to get the next error, | |||||
// etc. The order will match the order of Errors in the multierror.Error | |||||
// at the time of calling. | |||||
// | |||||
// The resulting error supports errors.As/Is/Unwrap so you can continue | |||||
// to use the stdlib errors package to introspect further. | |||||
// | |||||
// This will perform a shallow copy of the errors slice. Any errors appended | |||||
// to this error after calling Unwrap will not be available until a new | |||||
// Unwrap is called on the multierror.Error. | |||||
func (e *Error) Unwrap() error { | |||||
// If we have no errors then we do nothing | |||||
if e == nil || len(e.Errors) == 0 { | |||||
return nil | |||||
} | |||||
// If we have exactly one error, we can just return that directly. | |||||
if len(e.Errors) == 1 { | |||||
return e.Errors[0] | |||||
} | |||||
// Shallow copy the slice | |||||
errs := make([]error, len(e.Errors)) | |||||
copy(errs, e.Errors) | |||||
return chain(errs) | |||||
} | |||||
// chain implements the interfaces necessary for errors.Is/As/Unwrap to | |||||
// work in a deterministic way with multierror. A chain tracks a list of | |||||
// errors while accounting for the current represented error. This lets | |||||
// Is/As be meaningful. | |||||
// | |||||
// Unwrap returns the next error. In the cleanest form, Unwrap would return | |||||
// the wrapped error here but we can't do that if we want to properly | |||||
// get access to all the errors. Instead, users are recommended to use | |||||
// Is/As to get the correct error type out. | |||||
// | |||||
// Precondition: []error is non-empty (len > 0) | |||||
type chain []error | |||||
// Error implements the error interface | |||||
func (e chain) Error() string { | |||||
return e[0].Error() | |||||
} | |||||
// Unwrap implements errors.Unwrap by returning the next error in the | |||||
// chain or nil if there are no more errors. | |||||
func (e chain) Unwrap() error { | |||||
if len(e) == 1 { | |||||
return nil | |||||
} | |||||
return e[1:] | |||||
} | |||||
// As implements errors.As by attempting to map to the current value. | |||||
func (e chain) As(target interface{}) bool { | |||||
return errors.As(e[0], target) | |||||
} | |||||
// Is implements errors.Is by comparing the current value directly. | |||||
func (e chain) Is(target error) bool { | |||||
return errors.Is(e[0], target) | |||||
} |
@@ -0,0 +1,37 @@ | |||||
package multierror | |||||
import ( | |||||
"fmt" | |||||
"github.com/hashicorp/errwrap" | |||||
) | |||||
// Prefix is a helper function that will prefix some text | |||||
// to the given error. If the error is a multierror.Error, then | |||||
// it will be prefixed to each wrapped error. | |||||
// | |||||
// This is useful to use when appending multiple multierrors | |||||
// together in order to give better scoping. | |||||
func Prefix(err error, prefix string) error { | |||||
if err == nil { | |||||
return nil | |||||
} | |||||
format := fmt.Sprintf("%s {{err}}", prefix) | |||||
switch err := err.(type) { | |||||
case *Error: | |||||
// Typed nils can reach here, so initialize if we are nil | |||||
if err == nil { | |||||
err = new(Error) | |||||
} | |||||
// Wrap each of the errors | |||||
for i, e := range err.Errors { | |||||
err.Errors[i] = errwrap.Wrapf(format, e) | |||||
} | |||||
return err | |||||
default: | |||||
return errwrap.Wrapf(format, err) | |||||
} | |||||
} |
@@ -0,0 +1,16 @@ | |||||
package multierror | |||||
// Len implements sort.Interface function for length | |||||
func (err Error) Len() int { | |||||
return len(err.Errors) | |||||
} | |||||
// Swap implements sort.Interface function for swapping elements | |||||
func (err Error) Swap(i, j int) { | |||||
err.Errors[i], err.Errors[j] = err.Errors[j], err.Errors[i] | |||||
} | |||||
// Less implements sort.Interface function for determining order | |||||
func (err Error) Less(i, j int) bool { | |||||
return err.Errors[i].Error() < err.Errors[j].Error() | |||||
} |
@@ -59,6 +59,18 @@ type ExemplarAdder interface { | |||||
// CounterOpts is an alias for Opts. See there for doc comments. | // CounterOpts is an alias for Opts. See there for doc comments. | ||||
type CounterOpts Opts | type CounterOpts Opts | ||||
// CounterVecOpts bundles the options to create a CounterVec metric. | |||||
// It is mandatory to set CounterOpts, see there for mandatory fields. VariableLabels | |||||
// is optional and can safely be left to its default value. | |||||
type CounterVecOpts struct { | |||||
CounterOpts | |||||
// VariableLabels are used to partition the metric vector by the given set | |||||
// of labels. Each label value will be constrained with the optional Contraint | |||||
// function, if provided. | |||||
VariableLabels ConstrainableLabels | |||||
} | |||||
// NewCounter creates a new Counter based on the provided CounterOpts. | // NewCounter creates a new Counter based on the provided CounterOpts. | ||||
// | // | ||||
// The returned implementation also implements ExemplarAdder. It is safe to | // The returned implementation also implements ExemplarAdder. It is safe to | ||||
@@ -140,12 +152,13 @@ func (c *counter) get() float64 { | |||||
} | } | ||||
func (c *counter) Write(out *dto.Metric) error { | func (c *counter) Write(out *dto.Metric) error { | ||||
val := c.get() | |||||
// Read the Exemplar first and the value second. This is to avoid a race condition | |||||
// where users see an exemplar for a not-yet-existing observation. | |||||
var exemplar *dto.Exemplar | var exemplar *dto.Exemplar | ||||
if e := c.exemplar.Load(); e != nil { | if e := c.exemplar.Load(); e != nil { | ||||
exemplar = e.(*dto.Exemplar) | exemplar = e.(*dto.Exemplar) | ||||
} | } | ||||
val := c.get() | |||||
return populateMetric(CounterValue, val, c.labelPairs, exemplar, out) | return populateMetric(CounterValue, val, c.labelPairs, exemplar, out) | ||||
} | } | ||||
@@ -173,16 +186,24 @@ type CounterVec struct { | |||||
// NewCounterVec creates a new CounterVec based on the provided CounterOpts and | // NewCounterVec creates a new CounterVec based on the provided CounterOpts and | ||||
// partitioned by the given label names. | // partitioned by the given label names. | ||||
func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { | func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { | ||||
desc := NewDesc( | |||||
return V2.NewCounterVec(CounterVecOpts{ | |||||
CounterOpts: opts, | |||||
VariableLabels: UnconstrainedLabels(labelNames), | |||||
}) | |||||
} | |||||
// NewCounterVec creates a new CounterVec based on the provided CounterVecOpts. | |||||
func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec { | |||||
desc := V2.NewDesc( | |||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||||
opts.Help, | opts.Help, | ||||
labelNames, | |||||
opts.VariableLabels, | |||||
opts.ConstLabels, | opts.ConstLabels, | ||||
) | ) | ||||
return &CounterVec{ | return &CounterVec{ | ||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { | MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { | ||||
if len(lvs) != len(desc.variableLabels) { | if len(lvs) != len(desc.variableLabels) { | ||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs)) | |||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), lvs)) | |||||
} | } | ||||
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now} | result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now} | ||||
result.init(result) // Init self-collection. | result.init(result) // Init self-collection. | ||||
@@ -245,7 +266,8 @@ func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) { | |||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where | // WithLabelValues works as GetMetricWithLabelValues, but panics where | ||||
// GetMetricWithLabelValues would have returned an error. Not returning an | // GetMetricWithLabelValues would have returned an error. Not returning an | ||||
// error allows shortcuts like | // error allows shortcuts like | ||||
// myVec.WithLabelValues("404", "GET").Add(42) | |||||
// | |||||
// myVec.WithLabelValues("404", "GET").Add(42) | |||||
func (v *CounterVec) WithLabelValues(lvs ...string) Counter { | func (v *CounterVec) WithLabelValues(lvs ...string) Counter { | ||||
c, err := v.GetMetricWithLabelValues(lvs...) | c, err := v.GetMetricWithLabelValues(lvs...) | ||||
if err != nil { | if err != nil { | ||||
@@ -256,7 +278,8 @@ func (v *CounterVec) WithLabelValues(lvs ...string) Counter { | |||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have | // With works as GetMetricWith, but panics where GetMetricWithLabels would have | ||||
// returned an error. Not returning an error allows shortcuts like | // returned an error. Not returning an error allows shortcuts like | ||||
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42) | |||||
// | |||||
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42) | |||||
func (v *CounterVec) With(labels Labels) Counter { | func (v *CounterVec) With(labels Labels) Counter { | ||||
c, err := v.GetMetricWith(labels) | c, err := v.GetMetricWith(labels) | ||||
if err != nil { | if err != nil { | ||||
@@ -14,20 +14,16 @@ | |||||
package prometheus | package prometheus | ||||
import ( | import ( | ||||
"errors" | |||||
"fmt" | "fmt" | ||||
"sort" | "sort" | ||||
"strings" | "strings" | ||||
"github.com/cespare/xxhash/v2" | |||||
"github.com/prometheus/client_golang/prometheus/internal" | "github.com/prometheus/client_golang/prometheus/internal" | ||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. | |||||
"github.com/golang/protobuf/proto" | |||||
"github.com/prometheus/common/model" | |||||
"github.com/cespare/xxhash/v2" | |||||
dto "github.com/prometheus/client_model/go" | dto "github.com/prometheus/client_model/go" | ||||
"github.com/prometheus/common/model" | |||||
"google.golang.org/protobuf/proto" | |||||
) | ) | ||||
// Desc is the descriptor used by every Prometheus Metric. It is essentially | // Desc is the descriptor used by every Prometheus Metric. It is essentially | ||||
@@ -54,9 +50,9 @@ type Desc struct { | |||||
// constLabelPairs contains precalculated DTO label pairs based on | // constLabelPairs contains precalculated DTO label pairs based on | ||||
// the constant labels. | // the constant labels. | ||||
constLabelPairs []*dto.LabelPair | constLabelPairs []*dto.LabelPair | ||||
// variableLabels contains names of labels for which the metric | |||||
// maintains variable values. | |||||
variableLabels []string | |||||
// variableLabels contains names of labels and normalization function for | |||||
// which the metric maintains variable values. | |||||
variableLabels ConstrainedLabels | |||||
// id is a hash of the values of the ConstLabels and fqName. This | // id is a hash of the values of the ConstLabels and fqName. This | ||||
// must be unique among all registered descriptors and can therefore be | // must be unique among all registered descriptors and can therefore be | ||||
// used as an identifier of the descriptor. | // used as an identifier of the descriptor. | ||||
@@ -80,10 +76,24 @@ type Desc struct { | |||||
// For constLabels, the label values are constant. Therefore, they are fully | // For constLabels, the label values are constant. Therefore, they are fully | ||||
// specified in the Desc. See the Collector example for a usage pattern. | // specified in the Desc. See the Collector example for a usage pattern. | ||||
func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc { | func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc { | ||||
return V2.NewDesc(fqName, help, UnconstrainedLabels(variableLabels), constLabels) | |||||
} | |||||
// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc | |||||
// and will be reported on registration time. variableLabels and constLabels can | |||||
// be nil if no such labels should be set. fqName must not be empty. | |||||
// | |||||
// variableLabels only contain the label names and normalization functions. Their | |||||
// label values are variable and therefore not part of the Desc. (They are managed | |||||
// within the Metric.) | |||||
// | |||||
// For constLabels, the label values are constant. Therefore, they are fully | |||||
// specified in the Desc. See the Collector example for a usage pattern. | |||||
func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels) *Desc { | |||||
d := &Desc{ | d := &Desc{ | ||||
fqName: fqName, | fqName: fqName, | ||||
help: help, | help: help, | ||||
variableLabels: variableLabels, | |||||
variableLabels: variableLabels.constrainedLabels(), | |||||
} | } | ||||
if !model.IsValidMetricName(model.LabelValue(fqName)) { | if !model.IsValidMetricName(model.LabelValue(fqName)) { | ||||
d.err = fmt.Errorf("%q is not a valid metric name", fqName) | d.err = fmt.Errorf("%q is not a valid metric name", fqName) | ||||
@@ -93,7 +103,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * | |||||
// their sorted label names) plus the fqName (at position 0). | // their sorted label names) plus the fqName (at position 0). | ||||
labelValues := make([]string, 1, len(constLabels)+1) | labelValues := make([]string, 1, len(constLabels)+1) | ||||
labelValues[0] = fqName | labelValues[0] = fqName | ||||
labelNames := make([]string, 0, len(constLabels)+len(variableLabels)) | |||||
labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels)) | |||||
labelNameSet := map[string]struct{}{} | labelNameSet := map[string]struct{}{} | ||||
// First add only the const label names and sort them... | // First add only the const label names and sort them... | ||||
for labelName := range constLabels { | for labelName := range constLabels { | ||||
@@ -118,16 +128,16 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * | |||||
// Now add the variable label names, but prefix them with something that | // Now add the variable label names, but prefix them with something that | ||||
// cannot be in a regular label name. That prevents matching the label | // cannot be in a regular label name. That prevents matching the label | ||||
// dimension with a different mix between preset and variable labels. | // dimension with a different mix between preset and variable labels. | ||||
for _, labelName := range variableLabels { | |||||
if !checkLabelName(labelName) { | |||||
d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName) | |||||
for _, label := range d.variableLabels { | |||||
if !checkLabelName(label.Name) { | |||||
d.err = fmt.Errorf("%q is not a valid label name for metric %q", label.Name, fqName) | |||||
return d | return d | ||||
} | } | ||||
labelNames = append(labelNames, "$"+labelName) | |||||
labelNameSet[labelName] = struct{}{} | |||||
labelNames = append(labelNames, "$"+label.Name) | |||||
labelNameSet[label.Name] = struct{}{} | |||||
} | } | ||||
if len(labelNames) != len(labelNameSet) { | if len(labelNames) != len(labelNameSet) { | ||||
d.err = errors.New("duplicate label names") | |||||
d.err = fmt.Errorf("duplicate label names in constant and variable labels for metric %q", fqName) | |||||
return d | return d | ||||
} | } | ||||
@@ -21,55 +21,66 @@ | |||||
// All exported functions and methods are safe to be used concurrently unless | // All exported functions and methods are safe to be used concurrently unless | ||||
// specified otherwise. | // specified otherwise. | ||||
// | // | ||||
// A Basic Example | |||||
// # A Basic Example | |||||
// | // | ||||
// As a starting point, a very basic usage example: | // As a starting point, a very basic usage example: | ||||
// | // | ||||
// package main | |||||
// | |||||
// import ( | |||||
// "log" | |||||
// "net/http" | |||||
// | |||||
// "github.com/prometheus/client_golang/prometheus" | |||||
// "github.com/prometheus/client_golang/prometheus/promhttp" | |||||
// ) | |||||
// | |||||
// var ( | |||||
// cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{ | |||||
// Name: "cpu_temperature_celsius", | |||||
// Help: "Current temperature of the CPU.", | |||||
// }) | |||||
// hdFailures = prometheus.NewCounterVec( | |||||
// prometheus.CounterOpts{ | |||||
// Name: "hd_errors_total", | |||||
// Help: "Number of hard-disk errors.", | |||||
// }, | |||||
// []string{"device"}, | |||||
// ) | |||||
// ) | |||||
// | |||||
// func init() { | |||||
// // Metrics have to be registered to be exposed: | |||||
// prometheus.MustRegister(cpuTemp) | |||||
// prometheus.MustRegister(hdFailures) | |||||
// } | |||||
// | |||||
// func main() { | |||||
// cpuTemp.Set(65.3) | |||||
// hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc() | |||||
// | |||||
// // The Handler function provides a default handler to expose metrics | |||||
// // via an HTTP server. "/metrics" is the usual endpoint for that. | |||||
// http.Handle("/metrics", promhttp.Handler()) | |||||
// log.Fatal(http.ListenAndServe(":8080", nil)) | |||||
// } | |||||
// | |||||
// package main | |||||
// | |||||
// import ( | |||||
// "log" | |||||
// "net/http" | |||||
// | |||||
// "github.com/prometheus/client_golang/prometheus" | |||||
// "github.com/prometheus/client_golang/prometheus/promhttp" | |||||
// ) | |||||
// | |||||
// type metrics struct { | |||||
// cpuTemp prometheus.Gauge | |||||
// hdFailures *prometheus.CounterVec | |||||
// } | |||||
// | |||||
// func NewMetrics(reg prometheus.Registerer) *metrics { | |||||
// m := &metrics{ | |||||
// cpuTemp: prometheus.NewGauge(prometheus.GaugeOpts{ | |||||
// Name: "cpu_temperature_celsius", | |||||
// Help: "Current temperature of the CPU.", | |||||
// }), | |||||
// hdFailures: prometheus.NewCounterVec( | |||||
// prometheus.CounterOpts{ | |||||
// Name: "hd_errors_total", | |||||
// Help: "Number of hard-disk errors.", | |||||
// }, | |||||
// []string{"device"}, | |||||
// ), | |||||
// } | |||||
// reg.MustRegister(m.cpuTemp) | |||||
// reg.MustRegister(m.hdFailures) | |||||
// return m | |||||
// } | |||||
// | |||||
// func main() { | |||||
// // Create a non-global registry. | |||||
// reg := prometheus.NewRegistry() | |||||
// | |||||
// // Create new metrics and register them using the custom registry. | |||||
// m := NewMetrics(reg) | |||||
// // Set values for the new created metrics. | |||||
// m.cpuTemp.Set(65.3) | |||||
// m.hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc() | |||||
// | |||||
// // Expose metrics and custom registry via an HTTP server | |||||
// // using the HandleFor function. "/metrics" is the usual endpoint for that. | |||||
// http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg})) | |||||
// log.Fatal(http.ListenAndServe(":8080", nil)) | |||||
// } | |||||
// | // | ||||
// This is a complete program that exports two metrics, a Gauge and a Counter, | // This is a complete program that exports two metrics, a Gauge and a Counter, | ||||
// the latter with a label attached to turn it into a (one-dimensional) vector. | // the latter with a label attached to turn it into a (one-dimensional) vector. | ||||
// It register the metrics using a custom registry and exposes them via an HTTP server | |||||
// on the /metrics endpoint. | |||||
// | // | ||||
// Metrics | |||||
// # Metrics | |||||
// | // | ||||
// The number of exported identifiers in this package might appear a bit | // The number of exported identifiers in this package might appear a bit | ||||
// overwhelming. However, in addition to the basic plumbing shown in the example | // overwhelming. However, in addition to the basic plumbing shown in the example | ||||
@@ -100,7 +111,7 @@ | |||||
// To create instances of Metrics and their vector versions, you need a suitable | // To create instances of Metrics and their vector versions, you need a suitable | ||||
// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, or HistogramOpts. | // …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, or HistogramOpts. | ||||
// | // | ||||
// Custom Collectors and constant Metrics | |||||
// # Custom Collectors and constant Metrics | |||||
// | // | ||||
// While you could create your own implementations of Metric, most likely you | // While you could create your own implementations of Metric, most likely you | ||||
// will only ever implement the Collector interface on your own. At a first | // will only ever implement the Collector interface on your own. At a first | ||||
@@ -141,7 +152,7 @@ | |||||
// a metric, GaugeFunc, CounterFunc, or UntypedFunc might be interesting | // a metric, GaugeFunc, CounterFunc, or UntypedFunc might be interesting | ||||
// shortcuts. | // shortcuts. | ||||
// | // | ||||
// Advanced Uses of the Registry | |||||
// # Advanced Uses of the Registry | |||||
// | // | ||||
// While MustRegister is the by far most common way of registering a Collector, | // While MustRegister is the by far most common way of registering a Collector, | ||||
// sometimes you might want to handle the errors the registration might cause. | // sometimes you might want to handle the errors the registration might cause. | ||||
@@ -176,23 +187,23 @@ | |||||
// NewProcessCollector). With a custom registry, you are in control and decide | // NewProcessCollector). With a custom registry, you are in control and decide | ||||
// yourself about the Collectors to register. | // yourself about the Collectors to register. | ||||
// | // | ||||
// HTTP Exposition | |||||
// # HTTP Exposition | |||||
// | // | ||||
// The Registry implements the Gatherer interface. The caller of the Gather | // The Registry implements the Gatherer interface. The caller of the Gather | ||||
// method can then expose the gathered metrics in some way. Usually, the metrics | // method can then expose the gathered metrics in some way. Usually, the metrics | ||||
// are served via HTTP on the /metrics endpoint. That's happening in the example | // are served via HTTP on the /metrics endpoint. That's happening in the example | ||||
// above. The tools to expose metrics via HTTP are in the promhttp sub-package. | // above. The tools to expose metrics via HTTP are in the promhttp sub-package. | ||||
// | // | ||||
// Pushing to the Pushgateway | |||||
// # Pushing to the Pushgateway | |||||
// | // | ||||
// Function for pushing to the Pushgateway can be found in the push sub-package. | // Function for pushing to the Pushgateway can be found in the push sub-package. | ||||
// | // | ||||
// Graphite Bridge | |||||
// # Graphite Bridge | |||||
// | // | ||||
// Functions and examples to push metrics from a Gatherer to Graphite can be | // Functions and examples to push metrics from a Gatherer to Graphite can be | ||||
// found in the graphite sub-package. | // found in the graphite sub-package. | ||||
// | // | ||||
// Other Means of Exposition | |||||
// # Other Means of Exposition | |||||
// | // | ||||
// More ways of exposing metrics can easily be added by following the approaches | // More ways of exposing metrics can easily be added by following the approaches | ||||
// of the existing implementations. | // of the existing implementations. | ||||
@@ -55,6 +55,18 @@ type Gauge interface { | |||||
// GaugeOpts is an alias for Opts. See there for doc comments. | // GaugeOpts is an alias for Opts. See there for doc comments. | ||||
type GaugeOpts Opts | type GaugeOpts Opts | ||||
// GaugeVecOpts bundles the options to create a GaugeVec metric. | |||||
// It is mandatory to set GaugeOpts, see there for mandatory fields. VariableLabels | |||||
// is optional and can safely be left to its default value. | |||||
type GaugeVecOpts struct { | |||||
GaugeOpts | |||||
// VariableLabels are used to partition the metric vector by the given set | |||||
// of labels. Each label value will be constrained with the optional Contraint | |||||
// function, if provided. | |||||
VariableLabels ConstrainableLabels | |||||
} | |||||
// NewGauge creates a new Gauge based on the provided GaugeOpts. | // NewGauge creates a new Gauge based on the provided GaugeOpts. | ||||
// | // | ||||
// The returned implementation is optimized for a fast Set method. If you have a | // The returned implementation is optimized for a fast Set method. If you have a | ||||
@@ -138,16 +150,24 @@ type GaugeVec struct { | |||||
// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and | // NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and | ||||
// partitioned by the given label names. | // partitioned by the given label names. | ||||
func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { | func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { | ||||
desc := NewDesc( | |||||
return V2.NewGaugeVec(GaugeVecOpts{ | |||||
GaugeOpts: opts, | |||||
VariableLabels: UnconstrainedLabels(labelNames), | |||||
}) | |||||
} | |||||
// NewGaugeVec creates a new GaugeVec based on the provided GaugeVecOpts. | |||||
func (v2) NewGaugeVec(opts GaugeVecOpts) *GaugeVec { | |||||
desc := V2.NewDesc( | |||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||||
opts.Help, | opts.Help, | ||||
labelNames, | |||||
opts.VariableLabels, | |||||
opts.ConstLabels, | opts.ConstLabels, | ||||
) | ) | ||||
return &GaugeVec{ | return &GaugeVec{ | ||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { | MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { | ||||
if len(lvs) != len(desc.variableLabels) { | if len(lvs) != len(desc.variableLabels) { | ||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs)) | |||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), lvs)) | |||||
} | } | ||||
result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)} | result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)} | ||||
result.init(result) // Init self-collection. | result.init(result) // Init self-collection. | ||||
@@ -210,7 +230,8 @@ func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) { | |||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where | // WithLabelValues works as GetMetricWithLabelValues, but panics where | ||||
// GetMetricWithLabelValues would have returned an error. Not returning an | // GetMetricWithLabelValues would have returned an error. Not returning an | ||||
// error allows shortcuts like | // error allows shortcuts like | ||||
// myVec.WithLabelValues("404", "GET").Add(42) | |||||
// | |||||
// myVec.WithLabelValues("404", "GET").Add(42) | |||||
func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge { | func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge { | ||||
g, err := v.GetMetricWithLabelValues(lvs...) | g, err := v.GetMetricWithLabelValues(lvs...) | ||||
if err != nil { | if err != nil { | ||||
@@ -221,7 +242,8 @@ func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge { | |||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have | // With works as GetMetricWith, but panics where GetMetricWithLabels would have | ||||
// returned an error. Not returning an error allows shortcuts like | // returned an error. Not returning an error allows shortcuts like | ||||
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42) | |||||
// | |||||
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42) | |||||
func (v *GaugeVec) With(labels Labels) Gauge { | func (v *GaugeVec) With(labels Labels) Gauge { | ||||
g, err := v.GetMetricWith(labels) | g, err := v.GetMetricWith(labels) | ||||
if err != nil { | if err != nil { | ||||
@@ -23,11 +23,10 @@ import ( | |||||
"strings" | "strings" | ||||
"sync" | "sync" | ||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. | |||||
"github.com/golang/protobuf/proto" | |||||
dto "github.com/prometheus/client_model/go" | |||||
"github.com/prometheus/client_golang/prometheus/internal" | "github.com/prometheus/client_golang/prometheus/internal" | ||||
dto "github.com/prometheus/client_model/go" | |||||
"google.golang.org/protobuf/proto" | |||||
) | ) | ||||
const ( | const ( | ||||
@@ -0,0 +1,60 @@ | |||||
// Copyright (c) 2015 Björn Rabenstein | |||||
// | |||||
// 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: | |||||
// | |||||
// The above copyright notice and this permission notice shall be included in all | |||||
// copies or substantial portions of the Software. | |||||
// | |||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
// SOFTWARE. | |||||
// | |||||
// The code in this package is copy/paste to avoid a dependency. Hence this file | |||||
// carries the copyright of the original repo. | |||||
// https://github.com/beorn7/floats | |||||
package internal | |||||
import ( | |||||
"math" | |||||
) | |||||
// minNormalFloat64 is the smallest positive normal value of type float64. | |||||
var minNormalFloat64 = math.Float64frombits(0x0010000000000000) | |||||
// AlmostEqualFloat64 returns true if a and b are equal within a relative error | |||||
// of epsilon. See http://floating-point-gui.de/errors/comparison/ for the | |||||
// details of the applied method. | |||||
func AlmostEqualFloat64(a, b, epsilon float64) bool { | |||||
if a == b { | |||||
return true | |||||
} | |||||
absA := math.Abs(a) | |||||
absB := math.Abs(b) | |||||
diff := math.Abs(a - b) | |||||
if a == 0 || b == 0 || absA+absB < minNormalFloat64 { | |||||
return diff < epsilon*minNormalFloat64 | |||||
} | |||||
return diff/math.Min(absA+absB, math.MaxFloat64) < epsilon | |||||
} | |||||
// AlmostEqualFloat64s is the slice form of AlmostEqualFloat64. | |||||
func AlmostEqualFloat64s(a, b []float64, epsilon float64) bool { | |||||
if len(a) != len(b) { | |||||
return false | |||||
} | |||||
for i := range a { | |||||
if !AlmostEqualFloat64(a[i], b[i], epsilon) { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} |
@@ -201,12 +201,15 @@ func (m *SequenceMatcher) isBJunk(s string) bool { | |||||
// If IsJunk is not defined: | // If IsJunk is not defined: | ||||
// | // | ||||
// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where | // Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where | ||||
// alo <= i <= i+k <= ahi | |||||
// blo <= j <= j+k <= bhi | |||||
// | |||||
// alo <= i <= i+k <= ahi | |||||
// blo <= j <= j+k <= bhi | |||||
// | |||||
// and for all (i',j',k') meeting those conditions, | // and for all (i',j',k') meeting those conditions, | ||||
// k >= k' | |||||
// i <= i' | |||||
// and if i == i', j <= j' | |||||
// | |||||
// k >= k' | |||||
// i <= i' | |||||
// and if i == i', j <= j' | |||||
// | // | ||||
// In other words, of all maximal matching blocks, return one that | // In other words, of all maximal matching blocks, return one that | ||||
// starts earliest in a, and of all those maximal matching blocks that | // starts earliest in a, and of all those maximal matching blocks that | ||||
@@ -25,12 +25,85 @@ import ( | |||||
// Labels represents a collection of label name -> value mappings. This type is | // Labels represents a collection of label name -> value mappings. This type is | ||||
// commonly used with the With(Labels) and GetMetricWith(Labels) methods of | // commonly used with the With(Labels) and GetMetricWith(Labels) methods of | ||||
// metric vector Collectors, e.g.: | // metric vector Collectors, e.g.: | ||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42) | |||||
// | |||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42) | |||||
// | // | ||||
// The other use-case is the specification of constant label pairs in Opts or to | // The other use-case is the specification of constant label pairs in Opts or to | ||||
// create a Desc. | // create a Desc. | ||||
type Labels map[string]string | type Labels map[string]string | ||||
// ConstrainedLabels represents a label name and its constrain function | |||||
// to normalize label values. This type is commonly used when constructing | |||||
// metric vector Collectors. | |||||
type ConstrainedLabel struct { | |||||
Name string | |||||
Constraint func(string) string | |||||
} | |||||
func (cl ConstrainedLabel) Constrain(v string) string { | |||||
if cl.Constraint == nil { | |||||
return v | |||||
} | |||||
return cl.Constraint(v) | |||||
} | |||||
// ConstrainableLabels is an interface that allows creating of labels that can | |||||
// be optionally constrained. | |||||
// | |||||
// prometheus.V2().NewCounterVec(CounterVecOpts{ | |||||
// CounterOpts: {...}, // Usual CounterOpts fields | |||||
// VariableLabels: []ConstrainedLabels{ | |||||
// {Name: "A"}, | |||||
// {Name: "B", Constraint: func(v string) string { ... }}, | |||||
// }, | |||||
// }) | |||||
type ConstrainableLabels interface { | |||||
constrainedLabels() ConstrainedLabels | |||||
labelNames() []string | |||||
} | |||||
// ConstrainedLabels represents a collection of label name -> constrain function | |||||
// to normalize label values. This type is commonly used when constructing | |||||
// metric vector Collectors. | |||||
type ConstrainedLabels []ConstrainedLabel | |||||
func (cls ConstrainedLabels) constrainedLabels() ConstrainedLabels { | |||||
return cls | |||||
} | |||||
func (cls ConstrainedLabels) labelNames() []string { | |||||
names := make([]string, len(cls)) | |||||
for i, label := range cls { | |||||
names[i] = label.Name | |||||
} | |||||
return names | |||||
} | |||||
// UnconstrainedLabels represents collection of label without any constraint on | |||||
// their value. Thus, it is simply a collection of label names. | |||||
// | |||||
// UnconstrainedLabels([]string{ "A", "B" }) | |||||
// | |||||
// is equivalent to | |||||
// | |||||
// ConstrainedLabels { | |||||
// { Name: "A" }, | |||||
// { Name: "B" }, | |||||
// } | |||||
type UnconstrainedLabels []string | |||||
func (uls UnconstrainedLabels) constrainedLabels() ConstrainedLabels { | |||||
constrainedLabels := make([]ConstrainedLabel, len(uls)) | |||||
for i, l := range uls { | |||||
constrainedLabels[i] = ConstrainedLabel{Name: l} | |||||
} | |||||
return constrainedLabels | |||||
} | |||||
func (uls UnconstrainedLabels) labelNames() []string { | |||||
return uls | |||||
} | |||||
// reservedLabelPrefix is a prefix which is not legal in user-supplied | // reservedLabelPrefix is a prefix which is not legal in user-supplied | ||||
// label names. | // label names. | ||||
const reservedLabelPrefix = "__" | const reservedLabelPrefix = "__" | ||||
@@ -20,11 +20,9 @@ import ( | |||||
"strings" | "strings" | ||||
"time" | "time" | ||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. | |||||
"github.com/golang/protobuf/proto" | |||||
"github.com/prometheus/common/model" | |||||
dto "github.com/prometheus/client_model/go" | dto "github.com/prometheus/client_model/go" | ||||
"github.com/prometheus/common/model" | |||||
"google.golang.org/protobuf/proto" | |||||
) | ) | ||||
var separatorByteSlice = []byte{model.SeparatorByte} // For convenient use with xxhash. | var separatorByteSlice = []byte{model.SeparatorByte} // For convenient use with xxhash. | ||||
@@ -187,7 +185,7 @@ func (m *withExemplarsMetric) Write(pb *dto.Metric) error { | |||||
} else { | } else { | ||||
// The +Inf bucket should be explicitly added if there is an exemplar for it, similar to non-const histogram logic in https://github.com/prometheus/client_golang/blob/main/prometheus/histogram.go#L357-L365. | // The +Inf bucket should be explicitly added if there is an exemplar for it, similar to non-const histogram logic in https://github.com/prometheus/client_golang/blob/main/prometheus/histogram.go#L357-L365. | ||||
b := &dto.Bucket{ | b := &dto.Bucket{ | ||||
CumulativeCount: proto.Uint64(pb.Histogram.Bucket[len(pb.Histogram.GetBucket())-1].GetCumulativeCount()), | |||||
CumulativeCount: proto.Uint64(pb.Histogram.GetSampleCount()), | |||||
UpperBound: proto.Float64(math.Inf(1)), | UpperBound: proto.Float64(math.Inf(1)), | ||||
Exemplar: e, | Exemplar: e, | ||||
} | } | ||||
@@ -68,17 +68,17 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou | |||||
o.apply(rtOpts) | o.apply(rtOpts) | ||||
} | } | ||||
code, method := checkLabels(counter) | |||||
// Curry the counter with dynamic labels before checking the remaining labels. | |||||
code, method := checkLabels(counter.MustCurryWith(rtOpts.emptyDynamicLabels())) | |||||
return func(r *http.Request) (*http.Response, error) { | return func(r *http.Request) (*http.Response, error) { | ||||
resp, err := next.RoundTrip(r) | resp, err := next.RoundTrip(r) | ||||
if err == nil { | if err == nil { | ||||
exemplarAdd( | |||||
counter.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)), | |||||
1, | |||||
rtOpts.getExemplarFn(r.Context()), | |||||
) | |||||
counter.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)).Inc() | |||||
l := labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...) | |||||
for label, resolve := range rtOpts.extraLabelsFromCtx { | |||||
l[label] = resolve(resp.Request.Context()) | |||||
} | |||||
addWithExemplar(counter.With(l), 1, rtOpts.getExemplarFn(r.Context())) | |||||
} | } | ||||
return resp, err | return resp, err | ||||
} | } | ||||
@@ -111,17 +111,18 @@ func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundT | |||||
o.apply(rtOpts) | o.apply(rtOpts) | ||||
} | } | ||||
code, method := checkLabels(obs) | |||||
// Curry the observer with dynamic labels before checking the remaining labels. | |||||
code, method := checkLabels(obs.MustCurryWith(rtOpts.emptyDynamicLabels())) | |||||
return func(r *http.Request) (*http.Response, error) { | return func(r *http.Request) (*http.Response, error) { | ||||
start := time.Now() | start := time.Now() | ||||
resp, err := next.RoundTrip(r) | resp, err := next.RoundTrip(r) | ||||
if err == nil { | if err == nil { | ||||
exemplarObserve( | |||||
obs.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)), | |||||
time.Since(start).Seconds(), | |||||
rtOpts.getExemplarFn(r.Context()), | |||||
) | |||||
l := labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...) | |||||
for label, resolve := range rtOpts.extraLabelsFromCtx { | |||||
l[label] = resolve(resp.Request.Context()) | |||||
} | |||||
observeWithExemplar(obs.With(l), time.Since(start).Seconds(), rtOpts.getExemplarFn(r.Context())) | |||||
} | } | ||||
return resp, err | return resp, err | ||||
} | } | ||||
@@ -28,7 +28,9 @@ import ( | |||||
// magicString is used for the hacky label test in checkLabels. Remove once fixed. | // magicString is used for the hacky label test in checkLabels. Remove once fixed. | ||||
const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa" | const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa" | ||||
func exemplarObserve(obs prometheus.Observer, val float64, labels map[string]string) { | |||||
// observeWithExemplar is a wrapper for [prometheus.ExemplarAdder.ExemplarObserver], | |||||
// which falls back to [prometheus.Observer.Observe] if no labels are provided. | |||||
func observeWithExemplar(obs prometheus.Observer, val float64, labels map[string]string) { | |||||
if labels == nil { | if labels == nil { | ||||
obs.Observe(val) | obs.Observe(val) | ||||
return | return | ||||
@@ -36,7 +38,9 @@ func exemplarObserve(obs prometheus.Observer, val float64, labels map[string]str | |||||
obs.(prometheus.ExemplarObserver).ObserveWithExemplar(val, labels) | obs.(prometheus.ExemplarObserver).ObserveWithExemplar(val, labels) | ||||
} | } | ||||
func exemplarAdd(obs prometheus.Counter, val float64, labels map[string]string) { | |||||
// addWithExemplar is a wrapper for [prometheus.ExemplarAdder.AddWithExemplar], | |||||
// which falls back to [prometheus.Counter.Add] if no labels are provided. | |||||
func addWithExemplar(obs prometheus.Counter, val float64, labels map[string]string) { | |||||
if labels == nil { | if labels == nil { | ||||
obs.Add(val) | obs.Add(val) | ||||
return | return | ||||
@@ -83,7 +87,8 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op | |||||
o.apply(hOpts) | o.apply(hOpts) | ||||
} | } | ||||
code, method := checkLabels(obs) | |||||
// Curry the observer with dynamic labels before checking the remaining labels. | |||||
code, method := checkLabels(obs.MustCurryWith(hOpts.emptyDynamicLabels())) | |||||
if code { | if code { | ||||
return func(w http.ResponseWriter, r *http.Request) { | return func(w http.ResponseWriter, r *http.Request) { | ||||
@@ -91,23 +96,22 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op | |||||
d := newDelegator(w, nil) | d := newDelegator(w, nil) | ||||
next.ServeHTTP(d, r) | next.ServeHTTP(d, r) | ||||
exemplarObserve( | |||||
obs.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)), | |||||
time.Since(now).Seconds(), | |||||
hOpts.getExemplarFn(r.Context()), | |||||
) | |||||
l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) | |||||
for label, resolve := range hOpts.extraLabelsFromCtx { | |||||
l[label] = resolve(r.Context()) | |||||
} | |||||
observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) | |||||
} | } | ||||
} | } | ||||
return func(w http.ResponseWriter, r *http.Request) { | return func(w http.ResponseWriter, r *http.Request) { | ||||
now := time.Now() | now := time.Now() | ||||
next.ServeHTTP(w, r) | next.ServeHTTP(w, r) | ||||
exemplarObserve( | |||||
obs.With(labels(code, method, r.Method, 0, hOpts.extraMethods...)), | |||||
time.Since(now).Seconds(), | |||||
hOpts.getExemplarFn(r.Context()), | |||||
) | |||||
l := labels(code, method, r.Method, 0, hOpts.extraMethods...) | |||||
for label, resolve := range hOpts.extraLabelsFromCtx { | |||||
l[label] = resolve(r.Context()) | |||||
} | |||||
observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) | |||||
} | } | ||||
} | } | ||||
@@ -134,28 +138,30 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler, | |||||
o.apply(hOpts) | o.apply(hOpts) | ||||
} | } | ||||
code, method := checkLabels(counter) | |||||
// Curry the counter with dynamic labels before checking the remaining labels. | |||||
code, method := checkLabels(counter.MustCurryWith(hOpts.emptyDynamicLabels())) | |||||
if code { | if code { | ||||
return func(w http.ResponseWriter, r *http.Request) { | return func(w http.ResponseWriter, r *http.Request) { | ||||
d := newDelegator(w, nil) | d := newDelegator(w, nil) | ||||
next.ServeHTTP(d, r) | next.ServeHTTP(d, r) | ||||
exemplarAdd( | |||||
counter.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)), | |||||
1, | |||||
hOpts.getExemplarFn(r.Context()), | |||||
) | |||||
l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) | |||||
for label, resolve := range hOpts.extraLabelsFromCtx { | |||||
l[label] = resolve(r.Context()) | |||||
} | |||||
addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r.Context())) | |||||
} | } | ||||
} | } | ||||
return func(w http.ResponseWriter, r *http.Request) { | return func(w http.ResponseWriter, r *http.Request) { | ||||
next.ServeHTTP(w, r) | next.ServeHTTP(w, r) | ||||
exemplarAdd( | |||||
counter.With(labels(code, method, r.Method, 0, hOpts.extraMethods...)), | |||||
1, | |||||
hOpts.getExemplarFn(r.Context()), | |||||
) | |||||
l := labels(code, method, r.Method, 0, hOpts.extraMethods...) | |||||
for label, resolve := range hOpts.extraLabelsFromCtx { | |||||
l[label] = resolve(r.Context()) | |||||
} | |||||
addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r.Context())) | |||||
} | } | ||||
} | } | ||||
@@ -187,16 +193,17 @@ func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Ha | |||||
o.apply(hOpts) | o.apply(hOpts) | ||||
} | } | ||||
code, method := checkLabels(obs) | |||||
// Curry the observer with dynamic labels before checking the remaining labels. | |||||
code, method := checkLabels(obs.MustCurryWith(hOpts.emptyDynamicLabels())) | |||||
return func(w http.ResponseWriter, r *http.Request) { | return func(w http.ResponseWriter, r *http.Request) { | ||||
now := time.Now() | now := time.Now() | ||||
d := newDelegator(w, func(status int) { | d := newDelegator(w, func(status int) { | ||||
exemplarObserve( | |||||
obs.With(labels(code, method, r.Method, status, hOpts.extraMethods...)), | |||||
time.Since(now).Seconds(), | |||||
hOpts.getExemplarFn(r.Context()), | |||||
) | |||||
l := labels(code, method, r.Method, status, hOpts.extraMethods...) | |||||
for label, resolve := range hOpts.extraLabelsFromCtx { | |||||
l[label] = resolve(r.Context()) | |||||
} | |||||
observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) | |||||
}) | }) | ||||
next.ServeHTTP(d, r) | next.ServeHTTP(d, r) | ||||
} | } | ||||
@@ -227,28 +234,32 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler, | |||||
o.apply(hOpts) | o.apply(hOpts) | ||||
} | } | ||||
code, method := checkLabels(obs) | |||||
// Curry the observer with dynamic labels before checking the remaining labels. | |||||
code, method := checkLabels(obs.MustCurryWith(hOpts.emptyDynamicLabels())) | |||||
if code { | if code { | ||||
return func(w http.ResponseWriter, r *http.Request) { | return func(w http.ResponseWriter, r *http.Request) { | ||||
d := newDelegator(w, nil) | d := newDelegator(w, nil) | ||||
next.ServeHTTP(d, r) | next.ServeHTTP(d, r) | ||||
size := computeApproximateRequestSize(r) | size := computeApproximateRequestSize(r) | ||||
exemplarObserve( | |||||
obs.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)), | |||||
float64(size), | |||||
hOpts.getExemplarFn(r.Context()), | |||||
) | |||||
l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) | |||||
for label, resolve := range hOpts.extraLabelsFromCtx { | |||||
l[label] = resolve(r.Context()) | |||||
} | |||||
observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r.Context())) | |||||
} | } | ||||
} | } | ||||
return func(w http.ResponseWriter, r *http.Request) { | return func(w http.ResponseWriter, r *http.Request) { | ||||
next.ServeHTTP(w, r) | next.ServeHTTP(w, r) | ||||
size := computeApproximateRequestSize(r) | size := computeApproximateRequestSize(r) | ||||
exemplarObserve( | |||||
obs.With(labels(code, method, r.Method, 0, hOpts.extraMethods...)), | |||||
float64(size), | |||||
hOpts.getExemplarFn(r.Context()), | |||||
) | |||||
l := labels(code, method, r.Method, 0, hOpts.extraMethods...) | |||||
for label, resolve := range hOpts.extraLabelsFromCtx { | |||||
l[label] = resolve(r.Context()) | |||||
} | |||||
observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r.Context())) | |||||
} | } | ||||
} | } | ||||
@@ -277,16 +288,18 @@ func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler | |||||
o.apply(hOpts) | o.apply(hOpts) | ||||
} | } | ||||
code, method := checkLabels(obs) | |||||
// Curry the observer with dynamic labels before checking the remaining labels. | |||||
code, method := checkLabels(obs.MustCurryWith(hOpts.emptyDynamicLabels())) | |||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
d := newDelegator(w, nil) | d := newDelegator(w, nil) | ||||
next.ServeHTTP(d, r) | next.ServeHTTP(d, r) | ||||
exemplarObserve( | |||||
obs.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)), | |||||
float64(d.Written()), | |||||
hOpts.getExemplarFn(r.Context()), | |||||
) | |||||
l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) | |||||
for label, resolve := range hOpts.extraLabelsFromCtx { | |||||
l[label] = resolve(r.Context()) | |||||
} | |||||
observeWithExemplar(obs.With(l), float64(d.Written()), hOpts.getExemplarFn(r.Context())) | |||||
}) | }) | ||||
} | } | ||||
@@ -24,14 +24,32 @@ type Option interface { | |||||
apply(*options) | apply(*options) | ||||
} | } | ||||
// LabelValueFromCtx are used to compute the label value from request context. | |||||
// Context can be filled with values from request through middleware. | |||||
type LabelValueFromCtx func(ctx context.Context) string | |||||
// options store options for both a handler or round tripper. | // options store options for both a handler or round tripper. | ||||
type options struct { | type options struct { | ||||
extraMethods []string | |||||
getExemplarFn func(requestCtx context.Context) prometheus.Labels | |||||
extraMethods []string | |||||
getExemplarFn func(requestCtx context.Context) prometheus.Labels | |||||
extraLabelsFromCtx map[string]LabelValueFromCtx | |||||
} | } | ||||
func defaultOptions() *options { | func defaultOptions() *options { | ||||
return &options{getExemplarFn: func(ctx context.Context) prometheus.Labels { return nil }} | |||||
return &options{ | |||||
getExemplarFn: func(ctx context.Context) prometheus.Labels { return nil }, | |||||
extraLabelsFromCtx: map[string]LabelValueFromCtx{}, | |||||
} | |||||
} | |||||
func (o *options) emptyDynamicLabels() prometheus.Labels { | |||||
labels := prometheus.Labels{} | |||||
for label := range o.extraLabelsFromCtx { | |||||
labels[label] = "" | |||||
} | |||||
return labels | |||||
} | } | ||||
type optionApplyFunc func(*options) | type optionApplyFunc func(*options) | ||||
@@ -48,11 +66,19 @@ func WithExtraMethods(methods ...string) Option { | |||||
}) | }) | ||||
} | } | ||||
// WithExemplarFromContext adds allows to put a hook to all counter and histogram metrics. | |||||
// If the hook function returns non-nil labels, exemplars will be added for that request, otherwise metric | |||||
// will get instrumented without exemplar. | |||||
// WithExemplarFromContext allows to inject function that will get exemplar from context that will be put to counter and histogram metrics. | |||||
// If the function returns nil labels or the metric does not support exemplars, no exemplar will be added (noop), but | |||||
// metric will continue to observe/increment. | |||||
func WithExemplarFromContext(getExemplarFn func(requestCtx context.Context) prometheus.Labels) Option { | func WithExemplarFromContext(getExemplarFn func(requestCtx context.Context) prometheus.Labels) Option { | ||||
return optionApplyFunc(func(o *options) { | return optionApplyFunc(func(o *options) { | ||||
o.getExemplarFn = getExemplarFn | o.getExemplarFn = getExemplarFn | ||||
}) | }) | ||||
} | } | ||||
// WithLabelFromCtx registers a label for dynamic resolution with access to context. | |||||
// See the example for ExampleInstrumentHandlerWithLabelResolver for example usage | |||||
func WithLabelFromCtx(name string, valueFn LabelValueFromCtx) Option { | |||||
return optionApplyFunc(func(o *options) { | |||||
o.extraLabelsFromCtx[name] = valueFn | |||||
}) | |||||
} |
@@ -21,18 +21,17 @@ import ( | |||||
"path/filepath" | "path/filepath" | ||||
"runtime" | "runtime" | ||||
"sort" | "sort" | ||||
"strconv" | |||||
"strings" | "strings" | ||||
"sync" | "sync" | ||||
"unicode/utf8" | "unicode/utf8" | ||||
"github.com/cespare/xxhash/v2" | |||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. | |||||
"github.com/golang/protobuf/proto" | |||||
"github.com/prometheus/common/expfmt" | |||||
"github.com/prometheus/client_golang/prometheus/internal" | |||||
"github.com/cespare/xxhash/v2" | |||||
dto "github.com/prometheus/client_model/go" | dto "github.com/prometheus/client_model/go" | ||||
"github.com/prometheus/client_golang/prometheus/internal" | |||||
"github.com/prometheus/common/expfmt" | |||||
"google.golang.org/protobuf/proto" | |||||
) | ) | ||||
const ( | const ( | ||||
@@ -252,9 +251,12 @@ func (errs MultiError) MaybeUnwrap() error { | |||||
} | } | ||||
// Registry registers Prometheus collectors, collects their metrics, and gathers | // Registry registers Prometheus collectors, collects their metrics, and gathers | ||||
// them into MetricFamilies for exposition. It implements both Registerer and | |||||
// Gatherer. The zero value is not usable. Create instances with NewRegistry or | |||||
// NewPedanticRegistry. | |||||
// them into MetricFamilies for exposition. It implements Registerer, Gatherer, | |||||
// and Collector. The zero value is not usable. Create instances with | |||||
// NewRegistry or NewPedanticRegistry. | |||||
// | |||||
// Registry implements Collector to allow it to be used for creating groups of | |||||
// metrics. See the Grouping example for how this can be done. | |||||
type Registry struct { | type Registry struct { | ||||
mtx sync.RWMutex | mtx sync.RWMutex | ||||
collectorsByID map[uint64]Collector // ID is a hash of the descIDs. | collectorsByID map[uint64]Collector // ID is a hash of the descIDs. | ||||
@@ -556,6 +558,31 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { | |||||
return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() | return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() | ||||
} | } | ||||
// Describe implements Collector. | |||||
func (r *Registry) Describe(ch chan<- *Desc) { | |||||
r.mtx.RLock() | |||||
defer r.mtx.RUnlock() | |||||
// Only report the checked Collectors; unchecked collectors don't report any | |||||
// Desc. | |||||
for _, c := range r.collectorsByID { | |||||
c.Describe(ch) | |||||
} | |||||
} | |||||
// Collect implements Collector. | |||||
func (r *Registry) Collect(ch chan<- Metric) { | |||||
r.mtx.RLock() | |||||
defer r.mtx.RUnlock() | |||||
for _, c := range r.collectorsByID { | |||||
c.Collect(ch) | |||||
} | |||||
for _, c := range r.uncheckedCollectors { | |||||
c.Collect(ch) | |||||
} | |||||
} | |||||
// WriteToTextfile calls Gather on the provided Gatherer, encodes the result in the | // WriteToTextfile calls Gather on the provided Gatherer, encodes the result in the | ||||
// Prometheus text format, and writes it to a temporary file. Upon success, the | // Prometheus text format, and writes it to a temporary file. Upon success, the | ||||
// temporary file is renamed to the provided filename. | // temporary file is renamed to the provided filename. | ||||
@@ -905,6 +932,10 @@ func checkMetricConsistency( | |||||
h.WriteString(lp.GetValue()) | h.WriteString(lp.GetValue()) | ||||
h.Write(separatorByteSlice) | h.Write(separatorByteSlice) | ||||
} | } | ||||
if dtoMetric.TimestampMs != nil { | |||||
h.WriteString(strconv.FormatInt(*(dtoMetric.TimestampMs), 10)) | |||||
h.Write(separatorByteSlice) | |||||
} | |||||
hSum := h.Sum64() | hSum := h.Sum64() | ||||
if _, exists := metricHashes[hSum]; exists { | if _, exists := metricHashes[hSum]; exists { | ||||
return fmt.Errorf( | return fmt.Errorf( | ||||
@@ -934,7 +965,7 @@ func checkDescConsistency( | |||||
copy(lpsFromDesc, desc.constLabelPairs) | copy(lpsFromDesc, desc.constLabelPairs) | ||||
for _, l := range desc.variableLabels { | for _, l := range desc.variableLabels { | ||||
lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{ | lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{ | ||||
Name: proto.String(l), | |||||
Name: proto.String(l.Name), | |||||
}) | }) | ||||
} | } | ||||
if len(lpsFromDesc) != len(dtoMetric.Label) { | if len(lpsFromDesc) != len(dtoMetric.Label) { | ||||
@@ -22,11 +22,10 @@ import ( | |||||
"sync/atomic" | "sync/atomic" | ||||
"time" | "time" | ||||
"github.com/beorn7/perks/quantile" | |||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. | |||||
"github.com/golang/protobuf/proto" | |||||
dto "github.com/prometheus/client_model/go" | dto "github.com/prometheus/client_model/go" | ||||
"github.com/beorn7/perks/quantile" | |||||
"google.golang.org/protobuf/proto" | |||||
) | ) | ||||
// quantileLabel is used for the label that defines the quantile in a | // quantileLabel is used for the label that defines the quantile in a | ||||
@@ -148,6 +147,18 @@ type SummaryOpts struct { | |||||
BufCap uint32 | BufCap uint32 | ||||
} | } | ||||
// SummaryVecOpts bundles the options to create a SummaryVec metric. | |||||
// It is mandatory to set SummaryOpts, see there for mandatory fields. VariableLabels | |||||
// is optional and can safely be left to its default value. | |||||
type SummaryVecOpts struct { | |||||
SummaryOpts | |||||
// VariableLabels are used to partition the metric vector by the given set | |||||
// of labels. Each label value will be constrained with the optional Contraint | |||||
// function, if provided. | |||||
VariableLabels ConstrainableLabels | |||||
} | |||||
// Problem with the sliding-window decay algorithm... The Merge method of | // Problem with the sliding-window decay algorithm... The Merge method of | ||||
// perk/quantile is actually not working as advertised - and it might be | // perk/quantile is actually not working as advertised - and it might be | ||||
// unfixable, as the underlying algorithm is apparently not capable of merging | // unfixable, as the underlying algorithm is apparently not capable of merging | ||||
@@ -178,11 +189,11 @@ func NewSummary(opts SummaryOpts) Summary { | |||||
func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { | func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { | ||||
if len(desc.variableLabels) != len(labelValues) { | if len(desc.variableLabels) != len(labelValues) { | ||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues)) | |||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), labelValues)) | |||||
} | } | ||||
for _, n := range desc.variableLabels { | for _, n := range desc.variableLabels { | ||||
if n == quantileLabel { | |||||
if n.Name == quantileLabel { | |||||
panic(errQuantileLabelNotAllowed) | panic(errQuantileLabelNotAllowed) | ||||
} | } | ||||
} | } | ||||
@@ -530,20 +541,28 @@ type SummaryVec struct { | |||||
// it is handled by the Prometheus server internally, “quantile” is an illegal | // it is handled by the Prometheus server internally, “quantile” is an illegal | ||||
// label name. NewSummaryVec will panic if this label name is used. | // label name. NewSummaryVec will panic if this label name is used. | ||||
func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec { | func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec { | ||||
for _, ln := range labelNames { | |||||
return V2.NewSummaryVec(SummaryVecOpts{ | |||||
SummaryOpts: opts, | |||||
VariableLabels: UnconstrainedLabels(labelNames), | |||||
}) | |||||
} | |||||
// NewSummaryVec creates a new SummaryVec based on the provided SummaryVecOpts. | |||||
func (v2) NewSummaryVec(opts SummaryVecOpts) *SummaryVec { | |||||
for _, ln := range opts.VariableLabels.labelNames() { | |||||
if ln == quantileLabel { | if ln == quantileLabel { | ||||
panic(errQuantileLabelNotAllowed) | panic(errQuantileLabelNotAllowed) | ||||
} | } | ||||
} | } | ||||
desc := NewDesc( | |||||
desc := V2.NewDesc( | |||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||||
opts.Help, | opts.Help, | ||||
labelNames, | |||||
opts.VariableLabels, | |||||
opts.ConstLabels, | opts.ConstLabels, | ||||
) | ) | ||||
return &SummaryVec{ | return &SummaryVec{ | ||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { | MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { | ||||
return newSummary(desc, opts, lvs...) | |||||
return newSummary(desc, opts.SummaryOpts, lvs...) | |||||
}), | }), | ||||
} | } | ||||
} | } | ||||
@@ -603,7 +622,8 @@ func (v *SummaryVec) GetMetricWith(labels Labels) (Observer, error) { | |||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where | // WithLabelValues works as GetMetricWithLabelValues, but panics where | ||||
// GetMetricWithLabelValues would have returned an error. Not returning an | // GetMetricWithLabelValues would have returned an error. Not returning an | ||||
// error allows shortcuts like | // error allows shortcuts like | ||||
// myVec.WithLabelValues("404", "GET").Observe(42.21) | |||||
// | |||||
// myVec.WithLabelValues("404", "GET").Observe(42.21) | |||||
func (v *SummaryVec) WithLabelValues(lvs ...string) Observer { | func (v *SummaryVec) WithLabelValues(lvs ...string) Observer { | ||||
s, err := v.GetMetricWithLabelValues(lvs...) | s, err := v.GetMetricWithLabelValues(lvs...) | ||||
if err != nil { | if err != nil { | ||||
@@ -614,7 +634,8 @@ func (v *SummaryVec) WithLabelValues(lvs ...string) Observer { | |||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have | // With works as GetMetricWith, but panics where GetMetricWithLabels would have | ||||
// returned an error. Not returning an error allows shortcuts like | // returned an error. Not returning an error allows shortcuts like | ||||
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21) | |||||
// | |||||
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21) | |||||
func (v *SummaryVec) With(labels Labels) Observer { | func (v *SummaryVec) With(labels Labels) Observer { | ||||
s, err := v.GetMetricWith(labels) | s, err := v.GetMetricWith(labels) | ||||
if err != nil { | if err != nil { | ||||
@@ -701,7 +722,8 @@ func (s *constSummary) Write(out *dto.Metric) error { | |||||
// | // | ||||
// quantiles maps ranks to quantile values. For example, a median latency of | // quantiles maps ranks to quantile values. For example, a median latency of | ||||
// 0.23s and a 99th percentile latency of 0.56s would be expressed as: | // 0.23s and a 99th percentile latency of 0.56s would be expressed as: | ||||
// map[float64]float64{0.5: 0.23, 0.99: 0.56} | |||||
// | |||||
// map[float64]float64{0.5: 0.23, 0.99: 0.56} | |||||
// | // | ||||
// NewConstSummary returns an error if the length of labelValues is not | // NewConstSummary returns an error if the length of labelValues is not | ||||
// consistent with the variable labels in Desc or if Desc is invalid. | // consistent with the variable labels in Desc or if Desc is invalid. | ||||
@@ -23,13 +23,24 @@ type Timer struct { | |||||
} | } | ||||
// NewTimer creates a new Timer. The provided Observer is used to observe a | // NewTimer creates a new Timer. The provided Observer is used to observe a | ||||
// duration in seconds. Timer is usually used to time a function call in the | |||||
// duration in seconds. If the Observer implements ExemplarObserver, passing exemplar | |||||
// later on will be also supported. | |||||
// Timer is usually used to time a function call in the | |||||
// following way: | // following way: | ||||
// func TimeMe() { | |||||
// timer := NewTimer(myHistogram) | |||||
// defer timer.ObserveDuration() | |||||
// // Do actual work. | |||||
// } | |||||
// | |||||
// func TimeMe() { | |||||
// timer := NewTimer(myHistogram) | |||||
// defer timer.ObserveDuration() | |||||
// // Do actual work. | |||||
// } | |||||
// | |||||
// or | |||||
// | |||||
// func TimeMeWithExemplar() { | |||||
// timer := NewTimer(myHistogram) | |||||
// defer timer.ObserveDurationWithExemplar(exemplar) | |||||
// // Do actual work. | |||||
// } | |||||
func NewTimer(o Observer) *Timer { | func NewTimer(o Observer) *Timer { | ||||
return &Timer{ | return &Timer{ | ||||
begin: time.Now(), | begin: time.Now(), | ||||
@@ -52,3 +63,19 @@ func (t *Timer) ObserveDuration() time.Duration { | |||||
} | } | ||||
return d | return d | ||||
} | } | ||||
// ObserveDurationWithExemplar is like ObserveDuration, but it will also | |||||
// observe exemplar with the duration unless exemplar is nil or provided Observer can't | |||||
// be casted to ExemplarObserver. | |||||
func (t *Timer) ObserveDurationWithExemplar(exemplar Labels) time.Duration { | |||||
d := time.Since(t.begin) | |||||
eo, ok := t.observer.(ExemplarObserver) | |||||
if ok && exemplar != nil { | |||||
eo.ObserveWithExemplar(d.Seconds(), exemplar) | |||||
return d | |||||
} | |||||
if t.observer != nil { | |||||
t.observer.Observe(d.Seconds()) | |||||
} | |||||
return d | |||||
} |
@@ -19,13 +19,11 @@ import ( | |||||
"time" | "time" | ||||
"unicode/utf8" | "unicode/utf8" | ||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. | |||||
"github.com/golang/protobuf/proto" | |||||
"google.golang.org/protobuf/types/known/timestamppb" | |||||
"github.com/prometheus/client_golang/prometheus/internal" | "github.com/prometheus/client_golang/prometheus/internal" | ||||
dto "github.com/prometheus/client_model/go" | dto "github.com/prometheus/client_model/go" | ||||
"google.golang.org/protobuf/proto" | |||||
"google.golang.org/protobuf/types/known/timestamppb" | |||||
) | ) | ||||
// ValueType is an enumeration of metric types that represent a simple value. | // ValueType is an enumeration of metric types that represent a simple value. | ||||
@@ -188,9 +186,9 @@ func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair { | |||||
return desc.constLabelPairs | return desc.constLabelPairs | ||||
} | } | ||||
labelPairs := make([]*dto.LabelPair, 0, totalLen) | labelPairs := make([]*dto.LabelPair, 0, totalLen) | ||||
for i, n := range desc.variableLabels { | |||||
for i, l := range desc.variableLabels { | |||||
labelPairs = append(labelPairs, &dto.LabelPair{ | labelPairs = append(labelPairs, &dto.LabelPair{ | ||||
Name: proto.String(n), | |||||
Name: proto.String(l.Name), | |||||
Value: proto.String(labelValues[i]), | Value: proto.String(labelValues[i]), | ||||
}) | }) | ||||
} | } | ||||
@@ -72,6 +72,7 @@ func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec { | |||||
// with a performance overhead (for creating and processing the Labels map). | // with a performance overhead (for creating and processing the Labels map). | ||||
// See also the CounterVec example. | // See also the CounterVec example. | ||||
func (m *MetricVec) DeleteLabelValues(lvs ...string) bool { | func (m *MetricVec) DeleteLabelValues(lvs ...string) bool { | ||||
lvs = constrainLabelValues(m.desc, lvs, m.curry) | |||||
h, err := m.hashLabelValues(lvs) | h, err := m.hashLabelValues(lvs) | ||||
if err != nil { | if err != nil { | ||||
return false | return false | ||||
@@ -91,6 +92,7 @@ func (m *MetricVec) DeleteLabelValues(lvs ...string) bool { | |||||
// This method is used for the same purpose as DeleteLabelValues(...string). See | // This method is used for the same purpose as DeleteLabelValues(...string). See | ||||
// there for pros and cons of the two methods. | // there for pros and cons of the two methods. | ||||
func (m *MetricVec) Delete(labels Labels) bool { | func (m *MetricVec) Delete(labels Labels) bool { | ||||
labels = constrainLabels(m.desc, labels) | |||||
h, err := m.hashLabels(labels) | h, err := m.hashLabels(labels) | ||||
if err != nil { | if err != nil { | ||||
return false | return false | ||||
@@ -106,6 +108,7 @@ func (m *MetricVec) Delete(labels Labels) bool { | |||||
// Note that curried labels will never be matched if deleting from the curried vector. | // Note that curried labels will never be matched if deleting from the curried vector. | ||||
// To match curried labels with DeletePartialMatch, it must be called on the base vector. | // To match curried labels with DeletePartialMatch, it must be called on the base vector. | ||||
func (m *MetricVec) DeletePartialMatch(labels Labels) int { | func (m *MetricVec) DeletePartialMatch(labels Labels) int { | ||||
labels = constrainLabels(m.desc, labels) | |||||
return m.metricMap.deleteByLabels(labels, m.curry) | return m.metricMap.deleteByLabels(labels, m.curry) | ||||
} | } | ||||
@@ -145,10 +148,10 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { | |||||
iCurry int | iCurry int | ||||
) | ) | ||||
for i, label := range m.desc.variableLabels { | for i, label := range m.desc.variableLabels { | ||||
val, ok := labels[label] | |||||
val, ok := labels[label.Name] | |||||
if iCurry < len(oldCurry) && oldCurry[iCurry].index == i { | if iCurry < len(oldCurry) && oldCurry[iCurry].index == i { | ||||
if ok { | if ok { | ||||
return nil, fmt.Errorf("label name %q is already curried", label) | |||||
return nil, fmt.Errorf("label name %q is already curried", label.Name) | |||||
} | } | ||||
newCurry = append(newCurry, oldCurry[iCurry]) | newCurry = append(newCurry, oldCurry[iCurry]) | ||||
iCurry++ | iCurry++ | ||||
@@ -156,7 +159,7 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { | |||||
if !ok { | if !ok { | ||||
continue // Label stays uncurried. | continue // Label stays uncurried. | ||||
} | } | ||||
newCurry = append(newCurry, curriedLabelValue{i, val}) | |||||
newCurry = append(newCurry, curriedLabelValue{i, label.Constrain(val)}) | |||||
} | } | ||||
} | } | ||||
if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 { | if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 { | ||||
@@ -199,6 +202,7 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { | |||||
// a wrapper around MetricVec, implementing a vector for a specific Metric | // a wrapper around MetricVec, implementing a vector for a specific Metric | ||||
// implementation, for example GaugeVec. | // implementation, for example GaugeVec. | ||||
func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { | func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { | ||||
lvs = constrainLabelValues(m.desc, lvs, m.curry) | |||||
h, err := m.hashLabelValues(lvs) | h, err := m.hashLabelValues(lvs) | ||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
@@ -224,6 +228,7 @@ func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { | |||||
// around MetricVec, implementing a vector for a specific Metric implementation, | // around MetricVec, implementing a vector for a specific Metric implementation, | ||||
// for example GaugeVec. | // for example GaugeVec. | ||||
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) { | func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) { | ||||
labels = constrainLabels(m.desc, labels) | |||||
h, err := m.hashLabels(labels) | h, err := m.hashLabels(labels) | ||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
@@ -266,16 +271,16 @@ func (m *MetricVec) hashLabels(labels Labels) (uint64, error) { | |||||
iCurry int | iCurry int | ||||
) | ) | ||||
for i, label := range m.desc.variableLabels { | for i, label := range m.desc.variableLabels { | ||||
val, ok := labels[label] | |||||
val, ok := labels[label.Name] | |||||
if iCurry < len(curry) && curry[iCurry].index == i { | if iCurry < len(curry) && curry[iCurry].index == i { | ||||
if ok { | if ok { | ||||
return 0, fmt.Errorf("label name %q is already curried", label) | |||||
return 0, fmt.Errorf("label name %q is already curried", label.Name) | |||||
} | } | ||||
h = m.hashAdd(h, curry[iCurry].value) | h = m.hashAdd(h, curry[iCurry].value) | ||||
iCurry++ | iCurry++ | ||||
} else { | } else { | ||||
if !ok { | if !ok { | ||||
return 0, fmt.Errorf("label name %q missing in label map", label) | |||||
return 0, fmt.Errorf("label name %q missing in label map", label.Name) | |||||
} | } | ||||
h = m.hashAdd(h, val) | h = m.hashAdd(h, val) | ||||
} | } | ||||
@@ -453,7 +458,7 @@ func valueMatchesVariableOrCurriedValue(targetValue string, index int, values [] | |||||
func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool { | func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool { | ||||
for l, v := range labels { | for l, v := range labels { | ||||
// Check if the target label exists in our metrics and get the index. | // Check if the target label exists in our metrics and get the index. | ||||
varLabelIndex, validLabel := indexOf(l, desc.variableLabels) | |||||
varLabelIndex, validLabel := indexOf(l, desc.variableLabels.labelNames()) | |||||
if validLabel { | if validLabel { | ||||
// Check the value of that label against the target value. | // Check the value of that label against the target value. | ||||
// We don't consider curried values in partial matches. | // We don't consider curried values in partial matches. | ||||
@@ -605,7 +610,7 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe | |||||
iCurry++ | iCurry++ | ||||
continue | continue | ||||
} | } | ||||
if values[i] != labels[k] { | |||||
if values[i] != labels[k.Name] { | |||||
return false | return false | ||||
} | } | ||||
} | } | ||||
@@ -621,7 +626,7 @@ func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) [] | |||||
iCurry++ | iCurry++ | ||||
continue | continue | ||||
} | } | ||||
labelValues[i] = labels[k] | |||||
labelValues[i] = labels[k.Name] | |||||
} | } | ||||
return labelValues | return labelValues | ||||
} | } | ||||
@@ -640,3 +645,34 @@ func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string { | |||||
} | } | ||||
return labelValues | return labelValues | ||||
} | } | ||||
func constrainLabels(desc *Desc, labels Labels) Labels { | |||||
constrainedValues := make(Labels, len(labels)) | |||||
for l, v := range labels { | |||||
if i, ok := indexOf(l, desc.variableLabels.labelNames()); ok { | |||||
constrainedValues[l] = desc.variableLabels[i].Constrain(v) | |||||
continue | |||||
} | |||||
constrainedValues[l] = v | |||||
} | |||||
return constrainedValues | |||||
} | |||||
func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) []string { | |||||
constrainedValues := make([]string, len(lvs)) | |||||
var iCurry, iLVs int | |||||
for i := 0; i < len(lvs)+len(curry); i++ { | |||||
if iCurry < len(curry) && curry[iCurry].index == i { | |||||
iCurry++ | |||||
continue | |||||
} | |||||
if i < len(desc.variableLabels) { | |||||
constrainedValues[iLVs] = desc.variableLabels[i].Constrain(lvs[iLVs]) | |||||
} else { | |||||
constrainedValues[iLVs] = lvs[iLVs] | |||||
} | |||||
iLVs++ | |||||
} | |||||
return constrainedValues | |||||
} |
@@ -0,0 +1,23 @@ | |||||
// Copyright 2022 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 prometheus | |||||
type v2 struct{} | |||||
// V2 is a struct that can be referenced to access experimental API that might | |||||
// be present in v2 of client golang someday. It offers extended functionality | |||||
// of v1 with slightly changed API. It is acceptable to use some pieces from v1 | |||||
// and e.g `prometheus.NewGauge` and some from v2 e.g. `prometheus.V2.NewDesc` | |||||
// in the same codebase. | |||||
var V2 = v2{} |
@@ -17,12 +17,10 @@ import ( | |||||
"fmt" | "fmt" | ||||
"sort" | "sort" | ||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. | |||||
"github.com/golang/protobuf/proto" | |||||
"github.com/prometheus/client_golang/prometheus/internal" | |||||
dto "github.com/prometheus/client_model/go" | dto "github.com/prometheus/client_model/go" | ||||
"github.com/prometheus/client_golang/prometheus/internal" | |||||
"google.golang.org/protobuf/proto" | |||||
) | ) | ||||
// WrapRegistererWith returns a Registerer wrapping the provided | // WrapRegistererWith returns a Registerer wrapping the provided | ||||
@@ -206,7 +204,7 @@ func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc { | |||||
constLabels[ln] = lv | constLabels[ln] = lv | ||||
} | } | ||||
// NewDesc will do remaining validations. | // NewDesc will do remaining validations. | ||||
newDesc := NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels) | |||||
newDesc := V2.NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels) | |||||
// Propagate errors if there was any. This will override any errer | // Propagate errors if there was any. This will override any errer | ||||
// created by NewDesc above, i.e. earlier errors get precedence. | // created by NewDesc above, i.e. earlier errors get precedence. | ||||
if desc.err != nil { | if desc.err != nil { | ||||
@@ -115,32 +115,31 @@ func (d *protoDecoder) Decode(v *dto.MetricFamily) error { | |||||
// textDecoder implements the Decoder interface for the text protocol. | // textDecoder implements the Decoder interface for the text protocol. | ||||
type textDecoder struct { | type textDecoder struct { | ||||
r io.Reader | r io.Reader | ||||
p TextParser | |||||
fams []*dto.MetricFamily | |||||
fams map[string]*dto.MetricFamily | |||||
err error | |||||
} | } | ||||
// Decode implements the Decoder interface. | // Decode implements the Decoder interface. | ||||
func (d *textDecoder) Decode(v *dto.MetricFamily) error { | func (d *textDecoder) Decode(v *dto.MetricFamily) error { | ||||
// TODO(fabxc): Wrap this as a line reader to make streaming safer. | |||||
if len(d.fams) == 0 { | |||||
// No cached metric families, read everything and parse metrics. | |||||
fams, err := d.p.TextToMetricFamilies(d.r) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
if len(fams) == 0 { | |||||
return io.EOF | |||||
} | |||||
d.fams = make([]*dto.MetricFamily, 0, len(fams)) | |||||
for _, f := range fams { | |||||
d.fams = append(d.fams, f) | |||||
if d.err == nil { | |||||
// Read all metrics in one shot. | |||||
var p TextParser | |||||
d.fams, d.err = p.TextToMetricFamilies(d.r) | |||||
// If we don't get an error, store io.EOF for the end. | |||||
if d.err == nil { | |||||
d.err = io.EOF | |||||
} | } | ||||
} | } | ||||
*v = *d.fams[0] | |||||
d.fams = d.fams[1:] | |||||
return nil | |||||
// Pick off one MetricFamily per Decode until there's nothing left. | |||||
for key, fam := range d.fams { | |||||
v.Name = fam.Name | |||||
v.Help = fam.Help | |||||
v.Type = fam.Type | |||||
v.Metric = fam.Metric | |||||
delete(d.fams, key) | |||||
return nil | |||||
} | |||||
return d.err | |||||
} | } | ||||
// SampleDecoder wraps a Decoder to extract samples from the metric families | // SampleDecoder wraps a Decoder to extract samples from the metric families | ||||
@@ -18,9 +18,9 @@ import ( | |||||
"io" | "io" | ||||
"net/http" | "net/http" | ||||
"github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. | |||||
"github.com/matttproud/golang_protobuf_extensions/pbutil" | "github.com/matttproud/golang_protobuf_extensions/pbutil" | ||||
"github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg" | "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg" | ||||
"google.golang.org/protobuf/encoding/prototext" | |||||
dto "github.com/prometheus/client_model/go" | dto "github.com/prometheus/client_model/go" | ||||
) | ) | ||||
@@ -99,8 +99,11 @@ func NegotiateIncludingOpenMetrics(h http.Header) Format { | |||||
if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") { | if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") { | ||||
return FmtText | return FmtText | ||||
} | } | ||||
if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion || ver == "") { | |||||
return FmtOpenMetrics | |||||
if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion_0_0_1 || ver == OpenMetricsVersion_1_0_0 || ver == "") { | |||||
if ver == OpenMetricsVersion_1_0_0 { | |||||
return FmtOpenMetrics_1_0_0 | |||||
} | |||||
return FmtOpenMetrics_0_0_1 | |||||
} | } | ||||
} | } | ||||
return FmtText | return FmtText | ||||
@@ -133,7 +136,7 @@ func NewEncoder(w io.Writer, format Format) Encoder { | |||||
case FmtProtoText: | case FmtProtoText: | ||||
return encoderCloser{ | return encoderCloser{ | ||||
encode: func(v *dto.MetricFamily) error { | encode: func(v *dto.MetricFamily) error { | ||||
_, err := fmt.Fprintln(w, proto.MarshalTextString(v)) | |||||
_, err := fmt.Fprintln(w, prototext.Format(v)) | |||||
return err | return err | ||||
}, | }, | ||||
close: func() error { return nil }, | close: func() error { return nil }, | ||||
@@ -146,7 +149,7 @@ func NewEncoder(w io.Writer, format Format) Encoder { | |||||
}, | }, | ||||
close: func() error { return nil }, | close: func() error { return nil }, | ||||
} | } | ||||
case FmtOpenMetrics: | |||||
case FmtOpenMetrics_0_0_1, FmtOpenMetrics_1_0_0: | |||||
return encoderCloser{ | return encoderCloser{ | ||||
encode: func(v *dto.MetricFamily) error { | encode: func(v *dto.MetricFamily) error { | ||||
_, err := MetricFamilyToOpenMetrics(w, v) | _, err := MetricFamilyToOpenMetrics(w, v) | ||||
@@ -19,20 +19,22 @@ type Format string | |||||
// Constants to assemble the Content-Type values for the different wire protocols. | // Constants to assemble the Content-Type values for the different wire protocols. | ||||
const ( | const ( | ||||
TextVersion = "0.0.4" | |||||
ProtoType = `application/vnd.google.protobuf` | |||||
ProtoProtocol = `io.prometheus.client.MetricFamily` | |||||
ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";" | |||||
OpenMetricsType = `application/openmetrics-text` | |||||
OpenMetricsVersion = "0.0.1" | |||||
TextVersion = "0.0.4" | |||||
ProtoType = `application/vnd.google.protobuf` | |||||
ProtoProtocol = `io.prometheus.client.MetricFamily` | |||||
ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";" | |||||
OpenMetricsType = `application/openmetrics-text` | |||||
OpenMetricsVersion_0_0_1 = "0.0.1" | |||||
OpenMetricsVersion_1_0_0 = "1.0.0" | |||||
// The Content-Type values for the different wire protocols. | // The Content-Type values for the different wire protocols. | ||||
FmtUnknown Format = `<unknown>` | |||||
FmtText Format = `text/plain; version=` + TextVersion + `; charset=utf-8` | |||||
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited` | |||||
FmtProtoText Format = ProtoFmt + ` encoding=text` | |||||
FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text` | |||||
FmtOpenMetrics Format = OpenMetricsType + `; version=` + OpenMetricsVersion + `; charset=utf-8` | |||||
FmtUnknown Format = `<unknown>` | |||||
FmtText Format = `text/plain; version=` + TextVersion + `; charset=utf-8` | |||||
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited` | |||||
FmtProtoText Format = ProtoFmt + ` encoding=text` | |||||
FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text` | |||||
FmtOpenMetrics_1_0_0 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_1_0_0 + `; charset=utf-8` | |||||
FmtOpenMetrics_0_0_1 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_0_0_1 + `; charset=utf-8` | |||||
) | ) | ||||
const ( | const ( | ||||
@@ -21,8 +21,8 @@ import "bytes" | |||||
// Fuzz text metric parser with with github.com/dvyukov/go-fuzz: | // Fuzz text metric parser with with github.com/dvyukov/go-fuzz: | ||||
// | // | ||||
// go-fuzz-build github.com/prometheus/common/expfmt | |||||
// go-fuzz -bin expfmt-fuzz.zip -workdir fuzz | |||||
// go-fuzz-build github.com/prometheus/common/expfmt | |||||
// go-fuzz -bin expfmt-fuzz.zip -workdir fuzz | |||||
// | // | ||||
// Further input samples should go in the folder fuzz/corpus. | // Further input samples should go in the folder fuzz/corpus. | ||||
func Fuzz(in []byte) int { | func Fuzz(in []byte) int { | ||||
@@ -46,20 +46,20 @@ import ( | |||||
// missing features and peculiarities to avoid complications when switching from | // missing features and peculiarities to avoid complications when switching from | ||||
// Prometheus to OpenMetrics or vice versa: | // Prometheus to OpenMetrics or vice versa: | ||||
// | // | ||||
// - Counters are expected to have the `_total` suffix in their metric name. In | |||||
// the output, the suffix will be truncated from the `# TYPE` and `# HELP` | |||||
// line. A counter with a missing `_total` suffix is not an error. However, | |||||
// its type will be set to `unknown` in that case to avoid invalid OpenMetrics | |||||
// output. | |||||
// - Counters are expected to have the `_total` suffix in their metric name. In | |||||
// the output, the suffix will be truncated from the `# TYPE` and `# HELP` | |||||
// line. A counter with a missing `_total` suffix is not an error. However, | |||||
// its type will be set to `unknown` in that case to avoid invalid OpenMetrics | |||||
// output. | |||||
// | // | ||||
// - No support for the following (optional) features: `# UNIT` line, `_created` | |||||
// line, info type, stateset type, gaugehistogram type. | |||||
// - No support for the following (optional) features: `# UNIT` line, `_created` | |||||
// line, info type, stateset type, gaugehistogram type. | |||||
// | // | ||||
// - The size of exemplar labels is not checked (i.e. it's possible to create | |||||
// exemplars that are larger than allowed by the OpenMetrics specification). | |||||
// - The size of exemplar labels is not checked (i.e. it's possible to create | |||||
// exemplars that are larger than allowed by the OpenMetrics specification). | |||||
// | // | ||||
// - The value of Counters is not checked. (OpenMetrics doesn't allow counters | |||||
// with a `NaN` value.) | |||||
// - The value of Counters is not checked. (OpenMetrics doesn't allow counters | |||||
// with a `NaN` value.) | |||||
func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int, err error) { | func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int, err error) { | ||||
name := in.GetName() | name := in.GetName() | ||||
if name == "" { | if name == "" { | ||||
@@ -17,7 +17,6 @@ import ( | |||||
"bufio" | "bufio" | ||||
"fmt" | "fmt" | ||||
"io" | "io" | ||||
"io/ioutil" | |||||
"math" | "math" | ||||
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
@@ -44,7 +43,7 @@ const ( | |||||
var ( | var ( | ||||
bufPool = sync.Pool{ | bufPool = sync.Pool{ | ||||
New: func() interface{} { | New: func() interface{} { | ||||
return bufio.NewWriter(ioutil.Discard) | |||||
return bufio.NewWriter(io.Discard) | |||||
}, | }, | ||||
} | } | ||||
numBufPool = sync.Pool{ | numBufPool = sync.Pool{ | ||||
@@ -24,8 +24,8 @@ import ( | |||||
dto "github.com/prometheus/client_model/go" | dto "github.com/prometheus/client_model/go" | ||||
"github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. | |||||
"github.com/prometheus/common/model" | "github.com/prometheus/common/model" | ||||
"google.golang.org/protobuf/proto" | |||||
) | ) | ||||
// A stateFn is a function that represents a state in a state machine. By | // A stateFn is a function that represents a state in a state machine. By | ||||
@@ -142,9 +142,13 @@ func (p *TextParser) reset(in io.Reader) { | |||||
func (p *TextParser) startOfLine() stateFn { | func (p *TextParser) startOfLine() stateFn { | ||||
p.lineCount++ | p.lineCount++ | ||||
if p.skipBlankTab(); p.err != nil { | if p.skipBlankTab(); p.err != nil { | ||||
// End of input reached. This is the only case where | |||||
// that is not an error but a signal that we are done. | |||||
p.err = nil | |||||
// This is the only place that we expect to see io.EOF, | |||||
// which is not an error but the signal that we are done. | |||||
// Any other error that happens to align with the start of | |||||
// a line is still an error. | |||||
if p.err == io.EOF { | |||||
p.err = nil | |||||
} | |||||
return nil | return nil | ||||
} | } | ||||
switch p.currentByte { | switch p.currentByte { | ||||
@@ -11,18 +11,18 @@ Redistribution and use in source and binary forms, with or without | |||||
modification, are permitted provided that the following conditions are | modification, are permitted provided that the following conditions are | ||||
met: | met: | ||||
Redistributions of source code must retain the above copyright | |||||
notice, this list of conditions and the following disclaimer. | |||||
Redistributions of source code must retain the above copyright | |||||
notice, this list of conditions and the following disclaimer. | |||||
Redistributions in binary form must reproduce the above copyright | |||||
notice, this list of conditions and the following disclaimer in | |||||
the documentation and/or other materials provided with the | |||||
distribution. | |||||
Redistributions in binary form must reproduce the above copyright | |||||
notice, this list of conditions and the following disclaimer in | |||||
the documentation and/or other materials provided with the | |||||
distribution. | |||||
Neither the name of the Open Knowledge Foundation Ltd. nor the | |||||
names of its contributors may be used to endorse or promote | |||||
products derived from this software without specific prior written | |||||
permission. | |||||
Neither the name of the Open Knowledge Foundation Ltd. nor the | |||||
names of its contributors may be used to endorse or promote | |||||
products derived from this software without specific prior written | |||||
permission. | |||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
@@ -35,8 +35,6 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
*/ | */ | ||||
package goautoneg | package goautoneg | ||||
@@ -18,7 +18,6 @@ import ( | |||||
"errors" | "errors" | ||||
"fmt" | "fmt" | ||||
"math" | "math" | ||||
"regexp" | |||||
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
"time" | "time" | ||||
@@ -183,54 +182,78 @@ func (d *Duration) Type() string { | |||||
return "duration" | return "duration" | ||||
} | } | ||||
var durationRE = regexp.MustCompile("^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$") | |||||
func isdigit(c byte) bool { return c >= '0' && c <= '9' } | |||||
// Units are required to go in order from biggest to smallest. | |||||
// This guards against confusion from "1m1d" being 1 minute + 1 day, not 1 month + 1 day. | |||||
var unitMap = map[string]struct { | |||||
pos int | |||||
mult uint64 | |||||
}{ | |||||
"ms": {7, uint64(time.Millisecond)}, | |||||
"s": {6, uint64(time.Second)}, | |||||
"m": {5, uint64(time.Minute)}, | |||||
"h": {4, uint64(time.Hour)}, | |||||
"d": {3, uint64(24 * time.Hour)}, | |||||
"w": {2, uint64(7 * 24 * time.Hour)}, | |||||
"y": {1, uint64(365 * 24 * time.Hour)}, | |||||
} | |||||
// ParseDuration parses a string into a time.Duration, assuming that a year | // ParseDuration parses a string into a time.Duration, assuming that a year | ||||
// always has 365d, a week always has 7d, and a day always has 24h. | // always has 365d, a week always has 7d, and a day always has 24h. | ||||
func ParseDuration(durationStr string) (Duration, error) { | |||||
switch durationStr { | |||||
func ParseDuration(s string) (Duration, error) { | |||||
switch s { | |||||
case "0": | case "0": | ||||
// Allow 0 without a unit. | // Allow 0 without a unit. | ||||
return 0, nil | return 0, nil | ||||
case "": | case "": | ||||
return 0, errors.New("empty duration string") | return 0, errors.New("empty duration string") | ||||
} | } | ||||
matches := durationRE.FindStringSubmatch(durationStr) | |||||
if matches == nil { | |||||
return 0, fmt.Errorf("not a valid duration string: %q", durationStr) | |||||
} | |||||
var dur time.Duration | |||||
// Parse the match at pos `pos` in the regex and use `mult` to turn that | |||||
// into ms, then add that value to the total parsed duration. | |||||
var overflowErr error | |||||
m := func(pos int, mult time.Duration) { | |||||
if matches[pos] == "" { | |||||
return | |||||
orig := s | |||||
var dur uint64 | |||||
lastUnitPos := 0 | |||||
for s != "" { | |||||
if !isdigit(s[0]) { | |||||
return 0, fmt.Errorf("not a valid duration string: %q", orig) | |||||
} | |||||
// Consume [0-9]* | |||||
i := 0 | |||||
for ; i < len(s) && isdigit(s[i]); i++ { | |||||
} | |||||
v, err := strconv.ParseUint(s[:i], 10, 0) | |||||
if err != nil { | |||||
return 0, fmt.Errorf("not a valid duration string: %q", orig) | |||||
} | } | ||||
n, _ := strconv.Atoi(matches[pos]) | |||||
s = s[i:] | |||||
// Consume unit. | |||||
for i = 0; i < len(s) && !isdigit(s[i]); i++ { | |||||
} | |||||
if i == 0 { | |||||
return 0, fmt.Errorf("not a valid duration string: %q", orig) | |||||
} | |||||
u := s[:i] | |||||
s = s[i:] | |||||
unit, ok := unitMap[u] | |||||
if !ok { | |||||
return 0, fmt.Errorf("unknown unit %q in duration %q", u, orig) | |||||
} | |||||
if unit.pos <= lastUnitPos { // Units must go in order from biggest to smallest. | |||||
return 0, fmt.Errorf("not a valid duration string: %q", orig) | |||||
} | |||||
lastUnitPos = unit.pos | |||||
// Check if the provided duration overflows time.Duration (> ~ 290years). | // Check if the provided duration overflows time.Duration (> ~ 290years). | ||||
if n > int((1<<63-1)/mult/time.Millisecond) { | |||||
overflowErr = errors.New("duration out of range") | |||||
if v > 1<<63/unit.mult { | |||||
return 0, errors.New("duration out of range") | |||||
} | } | ||||
d := time.Duration(n) * time.Millisecond | |||||
dur += d * mult | |||||
if dur < 0 { | |||||
overflowErr = errors.New("duration out of range") | |||||
dur += v * unit.mult | |||||
if dur > 1<<63-1 { | |||||
return 0, errors.New("duration out of range") | |||||
} | } | ||||
} | } | ||||
m(2, 1000*60*60*24*365) // y | |||||
m(4, 1000*60*60*24*7) // w | |||||
m(6, 1000*60*60*24) // d | |||||
m(8, 1000*60*60) // h | |||||
m(10, 1000*60) // m | |||||
m(12, 1000) // s | |||||
m(14, 1) // ms | |||||
return Duration(dur), overflowErr | |||||
return Duration(dur), nil | |||||
} | } | ||||
func (d Duration) String() string { | func (d Duration) String() string { | ||||
@@ -16,20 +16,12 @@ package model | |||||
import ( | import ( | ||||
"encoding/json" | "encoding/json" | ||||
"fmt" | "fmt" | ||||
"math" | |||||
"sort" | "sort" | ||||
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
) | ) | ||||
var ( | var ( | ||||
// ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a | |||||
// non-existing sample pair. It is a SamplePair with timestamp Earliest and | |||||
// value 0.0. Note that the natural zero value of SamplePair has a timestamp | |||||
// of 0, which is possible to appear in a real SamplePair and thus not | |||||
// suitable to signal a non-existing SamplePair. | |||||
ZeroSamplePair = SamplePair{Timestamp: Earliest} | |||||
// ZeroSample is the pseudo zero-value of Sample used to signal a | // ZeroSample is the pseudo zero-value of Sample used to signal a | ||||
// non-existing sample. It is a Sample with timestamp Earliest, value 0.0, | // non-existing sample. It is a Sample with timestamp Earliest, value 0.0, | ||||
// and metric nil. Note that the natural zero value of Sample has a timestamp | // and metric nil. Note that the natural zero value of Sample has a timestamp | ||||
@@ -38,82 +30,14 @@ var ( | |||||
ZeroSample = Sample{Timestamp: Earliest} | ZeroSample = Sample{Timestamp: Earliest} | ||||
) | ) | ||||
// A SampleValue is a representation of a value for a given sample at a given | |||||
// time. | |||||
type SampleValue float64 | |||||
// MarshalJSON implements json.Marshaler. | |||||
func (v SampleValue) MarshalJSON() ([]byte, error) { | |||||
return json.Marshal(v.String()) | |||||
} | |||||
// UnmarshalJSON implements json.Unmarshaler. | |||||
func (v *SampleValue) UnmarshalJSON(b []byte) error { | |||||
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { | |||||
return fmt.Errorf("sample value must be a quoted string") | |||||
} | |||||
f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
*v = SampleValue(f) | |||||
return nil | |||||
} | |||||
// Equal returns true if the value of v and o is equal or if both are NaN. Note | |||||
// that v==o is false if both are NaN. If you want the conventional float | |||||
// behavior, use == to compare two SampleValues. | |||||
func (v SampleValue) Equal(o SampleValue) bool { | |||||
if v == o { | |||||
return true | |||||
} | |||||
return math.IsNaN(float64(v)) && math.IsNaN(float64(o)) | |||||
} | |||||
func (v SampleValue) String() string { | |||||
return strconv.FormatFloat(float64(v), 'f', -1, 64) | |||||
} | |||||
// SamplePair pairs a SampleValue with a Timestamp. | |||||
type SamplePair struct { | |||||
Timestamp Time | |||||
Value SampleValue | |||||
} | |||||
// MarshalJSON implements json.Marshaler. | |||||
func (s SamplePair) MarshalJSON() ([]byte, error) { | |||||
t, err := json.Marshal(s.Timestamp) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
v, err := json.Marshal(s.Value) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil | |||||
} | |||||
// UnmarshalJSON implements json.Unmarshaler. | |||||
func (s *SamplePair) UnmarshalJSON(b []byte) error { | |||||
v := [...]json.Unmarshaler{&s.Timestamp, &s.Value} | |||||
return json.Unmarshal(b, &v) | |||||
} | |||||
// Equal returns true if this SamplePair and o have equal Values and equal | |||||
// Timestamps. The semantics of Value equality is defined by SampleValue.Equal. | |||||
func (s *SamplePair) Equal(o *SamplePair) bool { | |||||
return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp)) | |||||
} | |||||
func (s SamplePair) String() string { | |||||
return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp) | |||||
} | |||||
// Sample is a sample pair associated with a metric. | |||||
// Sample is a sample pair associated with a metric. A single sample must either | |||||
// define Value or Histogram but not both. Histogram == nil implies the Value | |||||
// field is used, otherwise it should be ignored. | |||||
type Sample struct { | type Sample struct { | ||||
Metric Metric `json:"metric"` | |||||
Value SampleValue `json:"value"` | |||||
Timestamp Time `json:"timestamp"` | |||||
Metric Metric `json:"metric"` | |||||
Value SampleValue `json:"value"` | |||||
Timestamp Time `json:"timestamp"` | |||||
Histogram *SampleHistogram `json:"histogram"` | |||||
} | } | ||||
// Equal compares first the metrics, then the timestamp, then the value. The | // Equal compares first the metrics, then the timestamp, then the value. The | ||||
@@ -129,11 +53,19 @@ func (s *Sample) Equal(o *Sample) bool { | |||||
if !s.Timestamp.Equal(o.Timestamp) { | if !s.Timestamp.Equal(o.Timestamp) { | ||||
return false | return false | ||||
} | } | ||||
if s.Histogram != nil { | |||||
return s.Histogram.Equal(o.Histogram) | |||||
} | |||||
return s.Value.Equal(o.Value) | return s.Value.Equal(o.Value) | ||||
} | } | ||||
func (s Sample) String() string { | func (s Sample) String() string { | ||||
if s.Histogram != nil { | |||||
return fmt.Sprintf("%s => %s", s.Metric, SampleHistogramPair{ | |||||
Timestamp: s.Timestamp, | |||||
Histogram: s.Histogram, | |||||
}) | |||||
} | |||||
return fmt.Sprintf("%s => %s", s.Metric, SamplePair{ | return fmt.Sprintf("%s => %s", s.Metric, SamplePair{ | ||||
Timestamp: s.Timestamp, | Timestamp: s.Timestamp, | ||||
Value: s.Value, | Value: s.Value, | ||||
@@ -142,6 +74,19 @@ func (s Sample) String() string { | |||||
// MarshalJSON implements json.Marshaler. | // MarshalJSON implements json.Marshaler. | ||||
func (s Sample) MarshalJSON() ([]byte, error) { | func (s Sample) MarshalJSON() ([]byte, error) { | ||||
if s.Histogram != nil { | |||||
v := struct { | |||||
Metric Metric `json:"metric"` | |||||
Histogram SampleHistogramPair `json:"histogram"` | |||||
}{ | |||||
Metric: s.Metric, | |||||
Histogram: SampleHistogramPair{ | |||||
Timestamp: s.Timestamp, | |||||
Histogram: s.Histogram, | |||||
}, | |||||
} | |||||
return json.Marshal(&v) | |||||
} | |||||
v := struct { | v := struct { | ||||
Metric Metric `json:"metric"` | Metric Metric `json:"metric"` | ||||
Value SamplePair `json:"value"` | Value SamplePair `json:"value"` | ||||
@@ -152,21 +97,25 @@ func (s Sample) MarshalJSON() ([]byte, error) { | |||||
Value: s.Value, | Value: s.Value, | ||||
}, | }, | ||||
} | } | ||||
return json.Marshal(&v) | return json.Marshal(&v) | ||||
} | } | ||||
// UnmarshalJSON implements json.Unmarshaler. | // UnmarshalJSON implements json.Unmarshaler. | ||||
func (s *Sample) UnmarshalJSON(b []byte) error { | func (s *Sample) UnmarshalJSON(b []byte) error { | ||||
v := struct { | v := struct { | ||||
Metric Metric `json:"metric"` | |||||
Value SamplePair `json:"value"` | |||||
Metric Metric `json:"metric"` | |||||
Value SamplePair `json:"value"` | |||||
Histogram SampleHistogramPair `json:"histogram"` | |||||
}{ | }{ | ||||
Metric: s.Metric, | Metric: s.Metric, | ||||
Value: SamplePair{ | Value: SamplePair{ | ||||
Timestamp: s.Timestamp, | Timestamp: s.Timestamp, | ||||
Value: s.Value, | Value: s.Value, | ||||
}, | }, | ||||
Histogram: SampleHistogramPair{ | |||||
Timestamp: s.Timestamp, | |||||
Histogram: s.Histogram, | |||||
}, | |||||
} | } | ||||
if err := json.Unmarshal(b, &v); err != nil { | if err := json.Unmarshal(b, &v); err != nil { | ||||
@@ -174,8 +123,13 @@ func (s *Sample) UnmarshalJSON(b []byte) error { | |||||
} | } | ||||
s.Metric = v.Metric | s.Metric = v.Metric | ||||
s.Timestamp = v.Value.Timestamp | |||||
s.Value = v.Value.Value | |||||
if v.Histogram.Histogram != nil { | |||||
s.Timestamp = v.Histogram.Timestamp | |||||
s.Histogram = v.Histogram.Histogram | |||||
} else { | |||||
s.Timestamp = v.Value.Timestamp | |||||
s.Value = v.Value.Value | |||||
} | |||||
return nil | return nil | ||||
} | } | ||||
@@ -221,80 +175,76 @@ func (s Samples) Equal(o Samples) bool { | |||||
// SampleStream is a stream of Values belonging to an attached COWMetric. | // SampleStream is a stream of Values belonging to an attached COWMetric. | ||||
type SampleStream struct { | type SampleStream struct { | ||||
Metric Metric `json:"metric"` | |||||
Values []SamplePair `json:"values"` | |||||
Metric Metric `json:"metric"` | |||||
Values []SamplePair `json:"values"` | |||||
Histograms []SampleHistogramPair `json:"histograms"` | |||||
} | } | ||||
func (ss SampleStream) String() string { | func (ss SampleStream) String() string { | ||||
vals := make([]string, len(ss.Values)) | |||||
valuesLength := len(ss.Values) | |||||
vals := make([]string, valuesLength+len(ss.Histograms)) | |||||
for i, v := range ss.Values { | for i, v := range ss.Values { | ||||
vals[i] = v.String() | vals[i] = v.String() | ||||
} | } | ||||
for i, v := range ss.Histograms { | |||||
vals[i+valuesLength] = v.String() | |||||
} | |||||
return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n")) | return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n")) | ||||
} | } | ||||
// Value is a generic interface for values resulting from a query evaluation. | |||||
type Value interface { | |||||
Type() ValueType | |||||
String() string | |||||
func (ss SampleStream) MarshalJSON() ([]byte, error) { | |||||
if len(ss.Histograms) > 0 && len(ss.Values) > 0 { | |||||
v := struct { | |||||
Metric Metric `json:"metric"` | |||||
Values []SamplePair `json:"values"` | |||||
Histograms []SampleHistogramPair `json:"histograms"` | |||||
}{ | |||||
Metric: ss.Metric, | |||||
Values: ss.Values, | |||||
Histograms: ss.Histograms, | |||||
} | |||||
return json.Marshal(&v) | |||||
} else if len(ss.Histograms) > 0 { | |||||
v := struct { | |||||
Metric Metric `json:"metric"` | |||||
Histograms []SampleHistogramPair `json:"histograms"` | |||||
}{ | |||||
Metric: ss.Metric, | |||||
Histograms: ss.Histograms, | |||||
} | |||||
return json.Marshal(&v) | |||||
} else { | |||||
v := struct { | |||||
Metric Metric `json:"metric"` | |||||
Values []SamplePair `json:"values"` | |||||
}{ | |||||
Metric: ss.Metric, | |||||
Values: ss.Values, | |||||
} | |||||
return json.Marshal(&v) | |||||
} | |||||
} | } | ||||
func (Matrix) Type() ValueType { return ValMatrix } | |||||
func (Vector) Type() ValueType { return ValVector } | |||||
func (*Scalar) Type() ValueType { return ValScalar } | |||||
func (*String) Type() ValueType { return ValString } | |||||
type ValueType int | |||||
const ( | |||||
ValNone ValueType = iota | |||||
ValScalar | |||||
ValVector | |||||
ValMatrix | |||||
ValString | |||||
) | |||||
// MarshalJSON implements json.Marshaler. | |||||
func (et ValueType) MarshalJSON() ([]byte, error) { | |||||
return json.Marshal(et.String()) | |||||
} | |||||
func (ss *SampleStream) UnmarshalJSON(b []byte) error { | |||||
v := struct { | |||||
Metric Metric `json:"metric"` | |||||
Values []SamplePair `json:"values"` | |||||
Histograms []SampleHistogramPair `json:"histograms"` | |||||
}{ | |||||
Metric: ss.Metric, | |||||
Values: ss.Values, | |||||
Histograms: ss.Histograms, | |||||
} | |||||
func (et *ValueType) UnmarshalJSON(b []byte) error { | |||||
var s string | |||||
if err := json.Unmarshal(b, &s); err != nil { | |||||
if err := json.Unmarshal(b, &v); err != nil { | |||||
return err | return err | ||||
} | } | ||||
switch s { | |||||
case "<ValNone>": | |||||
*et = ValNone | |||||
case "scalar": | |||||
*et = ValScalar | |||||
case "vector": | |||||
*et = ValVector | |||||
case "matrix": | |||||
*et = ValMatrix | |||||
case "string": | |||||
*et = ValString | |||||
default: | |||||
return fmt.Errorf("unknown value type %q", s) | |||||
} | |||||
return nil | |||||
} | |||||
func (e ValueType) String() string { | |||||
switch e { | |||||
case ValNone: | |||||
return "<ValNone>" | |||||
case ValScalar: | |||||
return "scalar" | |||||
case ValVector: | |||||
return "vector" | |||||
case ValMatrix: | |||||
return "matrix" | |||||
case ValString: | |||||
return "string" | |||||
} | |||||
panic("ValueType.String: unhandled value type") | |||||
ss.Metric = v.Metric | |||||
ss.Values = v.Values | |||||
ss.Histograms = v.Histograms | |||||
return nil | |||||
} | } | ||||
// Scalar is a scalar value evaluated at the set timestamp. | // Scalar is a scalar value evaluated at the set timestamp. | ||||
@@ -0,0 +1,100 @@ | |||||
// Copyright 2013 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 model | |||||
import ( | |||||
"encoding/json" | |||||
"fmt" | |||||
"math" | |||||
"strconv" | |||||
) | |||||
var ( | |||||
// ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a | |||||
// non-existing sample pair. It is a SamplePair with timestamp Earliest and | |||||
// value 0.0. Note that the natural zero value of SamplePair has a timestamp | |||||
// of 0, which is possible to appear in a real SamplePair and thus not | |||||
// suitable to signal a non-existing SamplePair. | |||||
ZeroSamplePair = SamplePair{Timestamp: Earliest} | |||||
) | |||||
// A SampleValue is a representation of a value for a given sample at a given | |||||
// time. | |||||
type SampleValue float64 | |||||
// MarshalJSON implements json.Marshaler. | |||||
func (v SampleValue) MarshalJSON() ([]byte, error) { | |||||
return json.Marshal(v.String()) | |||||
} | |||||
// UnmarshalJSON implements json.Unmarshaler. | |||||
func (v *SampleValue) UnmarshalJSON(b []byte) error { | |||||
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { | |||||
return fmt.Errorf("sample value must be a quoted string") | |||||
} | |||||
f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
*v = SampleValue(f) | |||||
return nil | |||||
} | |||||
// Equal returns true if the value of v and o is equal or if both are NaN. Note | |||||
// that v==o is false if both are NaN. If you want the conventional float | |||||
// behavior, use == to compare two SampleValues. | |||||
func (v SampleValue) Equal(o SampleValue) bool { | |||||
if v == o { | |||||
return true | |||||
} | |||||
return math.IsNaN(float64(v)) && math.IsNaN(float64(o)) | |||||
} | |||||
func (v SampleValue) String() string { | |||||
return strconv.FormatFloat(float64(v), 'f', -1, 64) | |||||
} | |||||
// SamplePair pairs a SampleValue with a Timestamp. | |||||
type SamplePair struct { | |||||
Timestamp Time | |||||
Value SampleValue | |||||
} | |||||
func (s SamplePair) MarshalJSON() ([]byte, error) { | |||||
t, err := json.Marshal(s.Timestamp) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
v, err := json.Marshal(s.Value) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil | |||||
} | |||||
// UnmarshalJSON implements json.Unmarshaler. | |||||
func (s *SamplePair) UnmarshalJSON(b []byte) error { | |||||
v := [...]json.Unmarshaler{&s.Timestamp, &s.Value} | |||||
return json.Unmarshal(b, &v) | |||||
} | |||||
// Equal returns true if this SamplePair and o have equal Values and equal | |||||
// Timestamps. The semantics of Value equality is defined by SampleValue.Equal. | |||||
func (s *SamplePair) Equal(o *SamplePair) bool { | |||||
return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp)) | |||||
} | |||||
func (s SamplePair) String() string { | |||||
return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp) | |||||
} |
@@ -0,0 +1,178 @@ | |||||
// Copyright 2013 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 model | |||||
import ( | |||||
"encoding/json" | |||||
"fmt" | |||||
"strconv" | |||||
"strings" | |||||
) | |||||
type FloatString float64 | |||||
func (v FloatString) String() string { | |||||
return strconv.FormatFloat(float64(v), 'f', -1, 64) | |||||
} | |||||
func (v FloatString) MarshalJSON() ([]byte, error) { | |||||
return json.Marshal(v.String()) | |||||
} | |||||
func (v *FloatString) UnmarshalJSON(b []byte) error { | |||||
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { | |||||
return fmt.Errorf("float value must be a quoted string") | |||||
} | |||||
f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
*v = FloatString(f) | |||||
return nil | |||||
} | |||||
type HistogramBucket struct { | |||||
Boundaries int32 | |||||
Lower FloatString | |||||
Upper FloatString | |||||
Count FloatString | |||||
} | |||||
func (s HistogramBucket) MarshalJSON() ([]byte, error) { | |||||
b, err := json.Marshal(s.Boundaries) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
l, err := json.Marshal(s.Lower) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
u, err := json.Marshal(s.Upper) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
c, err := json.Marshal(s.Count) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return []byte(fmt.Sprintf("[%s,%s,%s,%s]", b, l, u, c)), nil | |||||
} | |||||
func (s *HistogramBucket) UnmarshalJSON(buf []byte) error { | |||||
tmp := []interface{}{&s.Boundaries, &s.Lower, &s.Upper, &s.Count} | |||||
wantLen := len(tmp) | |||||
if err := json.Unmarshal(buf, &tmp); err != nil { | |||||
return err | |||||
} | |||||
if gotLen := len(tmp); gotLen != wantLen { | |||||
return fmt.Errorf("wrong number of fields: %d != %d", gotLen, wantLen) | |||||
} | |||||
return nil | |||||
} | |||||
func (s *HistogramBucket) Equal(o *HistogramBucket) bool { | |||||
return s == o || (s.Boundaries == o.Boundaries && s.Lower == o.Lower && s.Upper == o.Upper && s.Count == o.Count) | |||||
} | |||||
func (b HistogramBucket) String() string { | |||||
var sb strings.Builder | |||||
lowerInclusive := b.Boundaries == 1 || b.Boundaries == 3 | |||||
upperInclusive := b.Boundaries == 0 || b.Boundaries == 3 | |||||
if lowerInclusive { | |||||
sb.WriteRune('[') | |||||
} else { | |||||
sb.WriteRune('(') | |||||
} | |||||
fmt.Fprintf(&sb, "%g,%g", b.Lower, b.Upper) | |||||
if upperInclusive { | |||||
sb.WriteRune(']') | |||||
} else { | |||||
sb.WriteRune(')') | |||||
} | |||||
fmt.Fprintf(&sb, ":%v", b.Count) | |||||
return sb.String() | |||||
} | |||||
type HistogramBuckets []*HistogramBucket | |||||
func (s HistogramBuckets) Equal(o HistogramBuckets) bool { | |||||
if len(s) != len(o) { | |||||
return false | |||||
} | |||||
for i, bucket := range s { | |||||
if !bucket.Equal(o[i]) { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} | |||||
type SampleHistogram struct { | |||||
Count FloatString `json:"count"` | |||||
Sum FloatString `json:"sum"` | |||||
Buckets HistogramBuckets `json:"buckets"` | |||||
} | |||||
func (s SampleHistogram) String() string { | |||||
return fmt.Sprintf("Count: %f, Sum: %f, Buckets: %v", s.Count, s.Sum, s.Buckets) | |||||
} | |||||
func (s *SampleHistogram) Equal(o *SampleHistogram) bool { | |||||
return s == o || (s.Count == o.Count && s.Sum == o.Sum && s.Buckets.Equal(o.Buckets)) | |||||
} | |||||
type SampleHistogramPair struct { | |||||
Timestamp Time | |||||
// Histogram should never be nil, it's only stored as pointer for efficiency. | |||||
Histogram *SampleHistogram | |||||
} | |||||
func (s SampleHistogramPair) MarshalJSON() ([]byte, error) { | |||||
if s.Histogram == nil { | |||||
return nil, fmt.Errorf("histogram is nil") | |||||
} | |||||
t, err := json.Marshal(s.Timestamp) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
v, err := json.Marshal(s.Histogram) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil | |||||
} | |||||
func (s *SampleHistogramPair) UnmarshalJSON(buf []byte) error { | |||||
tmp := []interface{}{&s.Timestamp, &s.Histogram} | |||||
wantLen := len(tmp) | |||||
if err := json.Unmarshal(buf, &tmp); err != nil { | |||||
return err | |||||
} | |||||
if gotLen := len(tmp); gotLen != wantLen { | |||||
return fmt.Errorf("wrong number of fields: %d != %d", gotLen, wantLen) | |||||
} | |||||
if s.Histogram == nil { | |||||
return fmt.Errorf("histogram is null") | |||||
} | |||||
return nil | |||||
} | |||||
func (s SampleHistogramPair) String() string { | |||||
return fmt.Sprintf("%s @[%s]", s.Histogram, s.Timestamp) | |||||
} | |||||
func (s *SampleHistogramPair) Equal(o *SampleHistogramPair) bool { | |||||
return s == o || (s.Histogram.Equal(o.Histogram) && s.Timestamp.Equal(o.Timestamp)) | |||||
} |
@@ -0,0 +1,83 @@ | |||||
// Copyright 2013 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 model | |||||
import ( | |||||
"encoding/json" | |||||
"fmt" | |||||
) | |||||
// Value is a generic interface for values resulting from a query evaluation. | |||||
type Value interface { | |||||
Type() ValueType | |||||
String() string | |||||
} | |||||
func (Matrix) Type() ValueType { return ValMatrix } | |||||
func (Vector) Type() ValueType { return ValVector } | |||||
func (*Scalar) Type() ValueType { return ValScalar } | |||||
func (*String) Type() ValueType { return ValString } | |||||
type ValueType int | |||||
const ( | |||||
ValNone ValueType = iota | |||||
ValScalar | |||||
ValVector | |||||
ValMatrix | |||||
ValString | |||||
) | |||||
// MarshalJSON implements json.Marshaler. | |||||
func (et ValueType) MarshalJSON() ([]byte, error) { | |||||
return json.Marshal(et.String()) | |||||
} | |||||
func (et *ValueType) UnmarshalJSON(b []byte) error { | |||||
var s string | |||||
if err := json.Unmarshal(b, &s); err != nil { | |||||
return err | |||||
} | |||||
switch s { | |||||
case "<ValNone>": | |||||
*et = ValNone | |||||
case "scalar": | |||||
*et = ValScalar | |||||
case "vector": | |||||
*et = ValVector | |||||
case "matrix": | |||||
*et = ValMatrix | |||||
case "string": | |||||
*et = ValString | |||||
default: | |||||
return fmt.Errorf("unknown value type %q", s) | |||||
} | |||||
return nil | |||||
} | |||||
func (e ValueType) String() string { | |||||
switch e { | |||||
case ValNone: | |||||
return "<ValNone>" | |||||
case ValScalar: | |||||
return "scalar" | |||||
case ValVector: | |||||
return "vector" | |||||
case ValMatrix: | |||||
return "matrix" | |||||
case ValString: | |||||
return "string" | |||||
} | |||||
panic("ValueType.String: unhandled value type") | |||||
} |
@@ -55,19 +55,22 @@ ifneq ($(shell which gotestsum),) | |||||
endif | endif | ||||
endif | endif | ||||
PROMU_VERSION ?= 0.13.0 | |||||
PROMU_VERSION ?= 0.14.0 | |||||
PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz | 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 := | ||||
GOLANGCI_LINT_OPTS ?= | GOLANGCI_LINT_OPTS ?= | ||||
GOLANGCI_LINT_VERSION ?= v1.45.2 | |||||
GOLANGCI_LINT_VERSION ?= v1.49.0 | |||||
# golangci-lint only supports linux, darwin and windows platforms on i386/amd64. | # golangci-lint only supports linux, darwin and windows platforms on i386/amd64. | ||||
# windows isn't included here because of the path separator being different. | # windows isn't included here because of the path separator being different. | ||||
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) | ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) | ||||
ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386)) | ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386)) | ||||
# If we're in CI and there is an Actions file, that means the linter | # If we're in CI and there is an Actions file, that means the linter | ||||
# is being run in Actions, so we don't need to run it here. | # is being run in Actions, so we don't need to run it here. | ||||
ifeq (,$(CIRCLE_JOB)) | |||||
ifneq (,$(SKIP_GOLANGCI_LINT)) | |||||
GOLANGCI_LINT := | |||||
else ifeq (,$(CIRCLE_JOB)) | |||||
GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint | GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint | ||||
else ifeq (,$(wildcard .github/workflows/golangci-lint.yml)) | else ifeq (,$(wildcard .github/workflows/golangci-lint.yml)) | ||||
GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint | GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint | ||||
@@ -380,6 +380,42 @@ func parseCPUInfoMips(info []byte) ([]CPUInfo, error) { | |||||
return cpuinfo, nil | return cpuinfo, nil | ||||
} | } | ||||
func parseCPUInfoLoong(info []byte) ([]CPUInfo, error) { | |||||
scanner := bufio.NewScanner(bytes.NewReader(info)) | |||||
// find the first "processor" line | |||||
firstLine := firstNonEmptyLine(scanner) | |||||
if !strings.HasPrefix(firstLine, "system type") || !strings.Contains(firstLine, ":") { | |||||
return nil, errors.New("invalid cpuinfo file: " + firstLine) | |||||
} | |||||
field := strings.SplitN(firstLine, ": ", 2) | |||||
cpuinfo := []CPUInfo{} | |||||
systemType := field[1] | |||||
i := 0 | |||||
for scanner.Scan() { | |||||
line := scanner.Text() | |||||
if !strings.Contains(line, ":") { | |||||
continue | |||||
} | |||||
field := strings.SplitN(line, ": ", 2) | |||||
switch strings.TrimSpace(field[0]) { | |||||
case "processor": | |||||
v, err := strconv.ParseUint(field[1], 0, 32) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
i = int(v) | |||||
cpuinfo = append(cpuinfo, CPUInfo{}) // start of the next processor | |||||
cpuinfo[i].Processor = uint(v) | |||||
cpuinfo[i].VendorID = systemType | |||||
case "CPU Family": | |||||
cpuinfo[i].CPUFamily = field[1] | |||||
case "Model Name": | |||||
cpuinfo[i].ModelName = field[1] | |||||
} | |||||
} | |||||
return cpuinfo, nil | |||||
} | |||||
func parseCPUInfoPPC(info []byte) ([]CPUInfo, error) { | func parseCPUInfoPPC(info []byte) ([]CPUInfo, error) { | ||||
scanner := bufio.NewScanner(bytes.NewReader(info)) | scanner := bufio.NewScanner(bytes.NewReader(info)) | ||||
@@ -0,0 +1,19 @@ | |||||
// Copyright 2022 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 linux | |||||
// +build linux | |||||
package procfs | |||||
var parseCPUInfo = parseCPUInfoLoong |
@@ -11,8 +11,8 @@ | |||||
// See the License for the specific language governing permissions and | // See the License for the specific language governing permissions and | ||||
// limitations under the License. | // limitations under the License. | ||||
//go:build linux && !386 && !amd64 && !arm && !arm64 && !mips && !mips64 && !mips64le && !mipsle && !ppc64 && !ppc64le && !riscv64 && !s390x | |||||
// +build linux,!386,!amd64,!arm,!arm64,!mips,!mips64,!mips64le,!mipsle,!ppc64,!ppc64le,!riscv64,!s390x | |||||
//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 | package procfs | ||||
@@ -16,30 +16,29 @@ | |||||
// | // | ||||
// Example: | // Example: | ||||
// | // | ||||
// package main | |||||
// | |||||
// import ( | |||||
// "fmt" | |||||
// "log" | |||||
// | |||||
// "github.com/prometheus/procfs" | |||||
// ) | |||||
// | |||||
// func main() { | |||||
// p, err := procfs.Self() | |||||
// if err != nil { | |||||
// log.Fatalf("could not get process: %s", err) | |||||
// } | |||||
// | |||||
// stat, err := p.Stat() | |||||
// if err != nil { | |||||
// log.Fatalf("could not get process stat: %s", err) | |||||
// } | |||||
// | |||||
// fmt.Printf("command: %s\n", stat.Comm) | |||||
// fmt.Printf("cpu time: %fs\n", stat.CPUTime()) | |||||
// fmt.Printf("vsize: %dB\n", stat.VirtualMemory()) | |||||
// fmt.Printf("rss: %dB\n", stat.ResidentMemory()) | |||||
// } | |||||
// | |||||
// package main | |||||
// | |||||
// import ( | |||||
// "fmt" | |||||
// "log" | |||||
// | |||||
// "github.com/prometheus/procfs" | |||||
// ) | |||||
// | |||||
// func main() { | |||||
// p, err := procfs.Self() | |||||
// if err != nil { | |||||
// log.Fatalf("could not get process: %s", err) | |||||
// } | |||||
// | |||||
// stat, err := p.Stat() | |||||
// if err != nil { | |||||
// log.Fatalf("could not get process stat: %s", err) | |||||
// } | |||||
// | |||||
// fmt.Printf("command: %s\n", stat.Comm) | |||||
// fmt.Printf("cpu time: %fs\n", stat.CPUTime()) | |||||
// fmt.Printf("vsize: %dB\n", stat.VirtualMemory()) | |||||
// fmt.Printf("rss: %dB\n", stat.ResidentMemory()) | |||||
// } | |||||
package procfs | package procfs |
@@ -284,7 +284,8 @@ func parseMountStats(r io.Reader) ([]*Mount, error) { | |||||
} | } | ||||
// parseMount parses an entry in /proc/[pid]/mountstats in the format: | // parseMount parses an entry in /proc/[pid]/mountstats in the format: | ||||
// device [device] mounted on [mount] with fstype [type] | |||||
// | |||||
// device [device] mounted on [mount] with fstype [type] | |||||
func parseMount(ss []string) (*Mount, error) { | func parseMount(ss []string) (*Mount, error) { | ||||
if len(ss) < deviceEntryLen { | if len(ss) < deviceEntryLen { | ||||
return nil, fmt.Errorf("invalid device entry: %v", ss) | return nil, fmt.Errorf("invalid device entry: %v", ss) | ||||
@@ -27,8 +27,9 @@ import ( | |||||
// For the proc file format details, | // For the proc file format details, | ||||
// See: | // See: | ||||
// * Linux 2.6.23 https://elixir.bootlin.com/linux/v2.6.23/source/net/core/dev.c#L2343 | // * Linux 2.6.23 https://elixir.bootlin.com/linux/v2.6.23/source/net/core/dev.c#L2343 | ||||
// * Linux 4.17 https://elixir.bootlin.com/linux/v4.17/source/net/core/net-procfs.c#L162 | |||||
// and https://elixir.bootlin.com/linux/v4.17/source/include/linux/netdevice.h#L2810. | |||||
// * Linux 2.6.39 https://elixir.bootlin.com/linux/v2.6.39/source/net/core/dev.c#L4086 | |||||
// * Linux 4.18 https://elixir.bootlin.com/linux/v4.18/source/net/core/net-procfs.c#L162 | |||||
// * Linux 5.14 https://elixir.bootlin.com/linux/v5.14/source/net/core/net-procfs.c#L169 | |||||
// SoftnetStat contains a single row of data from /proc/net/softnet_stat. | // SoftnetStat contains a single row of data from /proc/net/softnet_stat. | ||||
type SoftnetStat struct { | type SoftnetStat struct { | ||||
@@ -38,6 +39,18 @@ type SoftnetStat struct { | |||||
Dropped uint32 | Dropped uint32 | ||||
// Number of times processing packets ran out of quota. | // Number of times processing packets ran out of quota. | ||||
TimeSqueezed uint32 | TimeSqueezed uint32 | ||||
// Number of collision occur while obtaining device lock while transmitting. | |||||
CPUCollision uint32 | |||||
// Number of times cpu woken up received_rps. | |||||
ReceivedRps uint32 | |||||
// number of times flow limit has been reached. | |||||
FlowLimitCount uint32 | |||||
// Softnet backlog status. | |||||
SoftnetBacklogLen uint32 | |||||
// CPU id owning this softnet_data. | |||||
Index uint32 | |||||
// softnet_data's Width. | |||||
Width int | |||||
} | } | ||||
var softNetProcFile = "net/softnet_stat" | var softNetProcFile = "net/softnet_stat" | ||||
@@ -66,22 +79,57 @@ func parseSoftnet(r io.Reader) ([]SoftnetStat, error) { | |||||
for s.Scan() { | for s.Scan() { | ||||
columns := strings.Fields(s.Text()) | columns := strings.Fields(s.Text()) | ||||
width := len(columns) | width := len(columns) | ||||
softnetStat := SoftnetStat{} | |||||
if width < minColumns { | if width < minColumns { | ||||
return nil, fmt.Errorf("%d columns were detected, but at least %d were expected", width, minColumns) | return nil, fmt.Errorf("%d columns were detected, but at least %d were expected", width, minColumns) | ||||
} | } | ||||
// We only parse the first three columns at the moment. | |||||
us, err := parseHexUint32s(columns[0:3]) | |||||
if err != nil { | |||||
return nil, err | |||||
// Linux 2.6.23 https://elixir.bootlin.com/linux/v2.6.23/source/net/core/dev.c#L2347 | |||||
if width >= minColumns { | |||||
us, err := parseHexUint32s(columns[0:9]) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
softnetStat.Processed = us[0] | |||||
softnetStat.Dropped = us[1] | |||||
softnetStat.TimeSqueezed = us[2] | |||||
softnetStat.CPUCollision = us[8] | |||||
} | |||||
// Linux 2.6.39 https://elixir.bootlin.com/linux/v2.6.39/source/net/core/dev.c#L4086 | |||||
if width >= 10 { | |||||
us, err := parseHexUint32s(columns[9:10]) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
softnetStat.ReceivedRps = us[0] | |||||
} | } | ||||
stats = append(stats, SoftnetStat{ | |||||
Processed: us[0], | |||||
Dropped: us[1], | |||||
TimeSqueezed: us[2], | |||||
}) | |||||
// Linux 4.18 https://elixir.bootlin.com/linux/v4.18/source/net/core/net-procfs.c#L162 | |||||
if width >= 11 { | |||||
us, err := parseHexUint32s(columns[10:11]) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
softnetStat.FlowLimitCount = us[0] | |||||
} | |||||
// Linux 5.14 https://elixir.bootlin.com/linux/v5.14/source/net/core/net-procfs.c#L169 | |||||
if width >= 13 { | |||||
us, err := parseHexUint32s(columns[11:13]) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
softnetStat.SoftnetBacklogLen = us[0] | |||||
softnetStat.Index = us[1] | |||||
} | |||||
softnetStat.Width = width | |||||
stats = append(stats, softnetStat) | |||||
} | } | ||||
return stats, nil | return stats, nil | ||||
@@ -15,6 +15,7 @@ package procfs | |||||
import ( | import ( | ||||
"bufio" | "bufio" | ||||
"io" | |||||
"os" | "os" | ||||
"path/filepath" | "path/filepath" | ||||
"strconv" | "strconv" | ||||
@@ -42,27 +43,43 @@ func (fs FS) NetStat() ([]NetStat, error) { | |||||
return nil, err | return nil, err | ||||
} | } | ||||
netStatFile := NetStat{ | |||||
Filename: filepath.Base(filePath), | |||||
Stats: make(map[string][]uint64), | |||||
procNetstat, err := parseNetstat(file) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
procNetstat.Filename = filepath.Base(filePath) | |||||
netStatsTotal = append(netStatsTotal, procNetstat) | |||||
} | |||||
return netStatsTotal, nil | |||||
} | |||||
// parseNetstat parses the metrics from `/proc/net/stat/` file | |||||
// and returns a NetStat structure. | |||||
func parseNetstat(r io.Reader) (NetStat, error) { | |||||
var ( | |||||
scanner = bufio.NewScanner(r) | |||||
netStat = NetStat{ | |||||
Stats: make(map[string][]uint64), | |||||
} | } | ||||
scanner := bufio.NewScanner(file) | |||||
scanner.Scan() | |||||
// First string is always a header for stats | |||||
var headers []string | |||||
headers = append(headers, strings.Fields(scanner.Text())...) | |||||
) | |||||
scanner.Scan() | |||||
// Other strings represent per-CPU counters | |||||
for scanner.Scan() { | |||||
for num, counter := range strings.Fields(scanner.Text()) { | |||||
value, err := strconv.ParseUint(counter, 16, 64) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
netStatFile.Stats[headers[num]] = append(netStatFile.Stats[headers[num]], value) | |||||
// First string is always a header for stats | |||||
var headers []string | |||||
headers = append(headers, strings.Fields(scanner.Text())...) | |||||
// Other strings represent per-CPU counters | |||||
for scanner.Scan() { | |||||
for num, counter := range strings.Fields(scanner.Text()) { | |||||
value, err := strconv.ParseUint(counter, 16, 64) | |||||
if err != nil { | |||||
return NetStat{}, err | |||||
} | } | ||||
netStat.Stats[headers[num]] = append(netStat.Stats[headers[num]], value) | |||||
} | } | ||||
netStatsTotal = append(netStatsTotal, netStatFile) | |||||
} | } | ||||
return netStatsTotal, nil | |||||
return netStat, nil | |||||
} | } |
@@ -23,7 +23,7 @@ import ( | |||||
"github.com/prometheus/procfs/internal/util" | "github.com/prometheus/procfs/internal/util" | ||||
) | ) | ||||
// Cgroup models one line from /proc/[pid]/cgroup. Each Cgroup struct describes the the placement of a PID inside a | |||||
// Cgroup models one line from /proc/[pid]/cgroup. Each Cgroup struct describes the placement of a PID inside a | |||||
// specific control hierarchy. The kernel has two cgroup APIs, v1 and v2. v1 has one hierarchy per available resource | // specific control hierarchy. The kernel has two cgroup APIs, v1 and v2. v1 has one hierarchy per available resource | ||||
// controller, while v2 has one unified hierarchy shared by all controllers. Regardless of v1 or v2, all hierarchies | // controller, while v2 has one unified hierarchy shared by all controllers. Regardless of v1 or v2, all hierarchies | ||||
// contain all running processes, so the question answerable with a Cgroup struct is 'where is this process in | // contain all running processes, so the question answerable with a Cgroup struct is 'where is this process in | ||||
@@ -0,0 +1,98 @@ | |||||
// Copyright 2022 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" | |||||
"errors" | |||||
"fmt" | |||||
"io" | |||||
"strconv" | |||||
"strings" | |||||
"github.com/prometheus/procfs/internal/util" | |||||
) | |||||
// Interrupt represents a single interrupt line. | |||||
type Interrupt struct { | |||||
// Info is the type of interrupt. | |||||
Info string | |||||
// Devices is the name of the device that is located at that IRQ | |||||
Devices string | |||||
// Values is the number of interrupts per CPU. | |||||
Values []string | |||||
} | |||||
// Interrupts models the content of /proc/interrupts. Key is the IRQ number. | |||||
// - https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/s2-proc-interrupts | |||||
// - https://raspberrypi.stackexchange.com/questions/105802/explanation-of-proc-interrupts-output | |||||
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")) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return parseInterrupts(bytes.NewReader(data)) | |||||
} | |||||
func parseInterrupts(r io.Reader) (Interrupts, error) { | |||||
var ( | |||||
interrupts = Interrupts{} | |||||
scanner = bufio.NewScanner(r) | |||||
) | |||||
if !scanner.Scan() { | |||||
return nil, errors.New("interrupts empty") | |||||
} | |||||
cpuNum := len(strings.Fields(scanner.Text())) // one header per cpu | |||||
for scanner.Scan() { | |||||
parts := strings.Fields(scanner.Text()) | |||||
if len(parts) == 0 { // skip empty lines | |||||
continue | |||||
} | |||||
if len(parts) < 2 { | |||||
return nil, fmt.Errorf("not enough fields in interrupts (expected at least 2 fields but got %d): %s", len(parts), parts) | |||||
} | |||||
intName := parts[0][:len(parts[0])-1] // remove trailing : | |||||
if len(parts) == 2 { | |||||
interrupts[intName] = Interrupt{ | |||||
Info: "", | |||||
Devices: "", | |||||
Values: []string{ | |||||
parts[1], | |||||
}, | |||||
} | |||||
continue | |||||
} | |||||
intr := Interrupt{ | |||||
Values: parts[1 : cpuNum+1], | |||||
} | |||||
if _, err := strconv.Atoi(intName); err == nil { // numeral interrupt | |||||
intr.Info = parts[cpuNum+1] | |||||
intr.Devices = strings.Join(parts[cpuNum+2:], " ") | |||||
} else { | |||||
intr.Info = strings.Join(parts[cpuNum+1:], " ") | |||||
} | |||||
interrupts[intName] = intr | |||||
} | |||||
return interrupts, scanner.Err() | |||||
} |
@@ -33,139 +33,140 @@ type ProcNetstat struct { | |||||
} | } | ||||
type TcpExt struct { // nolint:revive | type TcpExt struct { // nolint:revive | ||||
SyncookiesSent float64 | |||||
SyncookiesRecv float64 | |||||
SyncookiesFailed float64 | |||||
EmbryonicRsts float64 | |||||
PruneCalled float64 | |||||
RcvPruned float64 | |||||
OfoPruned float64 | |||||
OutOfWindowIcmps float64 | |||||
LockDroppedIcmps float64 | |||||
ArpFilter float64 | |||||
TW float64 | |||||
TWRecycled float64 | |||||
TWKilled float64 | |||||
PAWSActive float64 | |||||
PAWSEstab float64 | |||||
DelayedACKs float64 | |||||
DelayedACKLocked float64 | |||||
DelayedACKLost float64 | |||||
ListenOverflows float64 | |||||
ListenDrops float64 | |||||
TCPHPHits float64 | |||||
TCPPureAcks float64 | |||||
TCPHPAcks float64 | |||||
TCPRenoRecovery float64 | |||||
TCPSackRecovery float64 | |||||
TCPSACKReneging float64 | |||||
TCPSACKReorder float64 | |||||
TCPRenoReorder float64 | |||||
TCPTSReorder float64 | |||||
TCPFullUndo float64 | |||||
TCPPartialUndo float64 | |||||
TCPDSACKUndo float64 | |||||
TCPLossUndo float64 | |||||
TCPLostRetransmit float64 | |||||
TCPRenoFailures float64 | |||||
TCPSackFailures float64 | |||||
TCPLossFailures float64 | |||||
TCPFastRetrans float64 | |||||
TCPSlowStartRetrans float64 | |||||
TCPTimeouts float64 | |||||
TCPLossProbes float64 | |||||
TCPLossProbeRecovery float64 | |||||
TCPRenoRecoveryFail float64 | |||||
TCPSackRecoveryFail float64 | |||||
TCPRcvCollapsed float64 | |||||
TCPDSACKOldSent float64 | |||||
TCPDSACKOfoSent float64 | |||||
TCPDSACKRecv float64 | |||||
TCPDSACKOfoRecv float64 | |||||
TCPAbortOnData float64 | |||||
TCPAbortOnClose float64 | |||||
TCPAbortOnMemory float64 | |||||
TCPAbortOnTimeout float64 | |||||
TCPAbortOnLinger float64 | |||||
TCPAbortFailed float64 | |||||
TCPMemoryPressures float64 | |||||
TCPMemoryPressuresChrono float64 | |||||
TCPSACKDiscard float64 | |||||
TCPDSACKIgnoredOld float64 | |||||
TCPDSACKIgnoredNoUndo float64 | |||||
TCPSpuriousRTOs float64 | |||||
TCPMD5NotFound float64 | |||||
TCPMD5Unexpected float64 | |||||
TCPMD5Failure float64 | |||||
TCPSackShifted float64 | |||||
TCPSackMerged float64 | |||||
TCPSackShiftFallback float64 | |||||
TCPBacklogDrop float64 | |||||
PFMemallocDrop float64 | |||||
TCPMinTTLDrop float64 | |||||
TCPDeferAcceptDrop float64 | |||||
IPReversePathFilter float64 | |||||
TCPTimeWaitOverflow float64 | |||||
TCPReqQFullDoCookies float64 | |||||
TCPReqQFullDrop float64 | |||||
TCPRetransFail float64 | |||||
TCPRcvCoalesce float64 | |||||
TCPOFOQueue float64 | |||||
TCPOFODrop float64 | |||||
TCPOFOMerge float64 | |||||
TCPChallengeACK float64 | |||||
TCPSYNChallenge float64 | |||||
TCPFastOpenActive float64 | |||||
TCPFastOpenActiveFail float64 | |||||
TCPFastOpenPassive float64 | |||||
TCPFastOpenPassiveFail float64 | |||||
TCPFastOpenListenOverflow float64 | |||||
TCPFastOpenCookieReqd float64 | |||||
TCPFastOpenBlackhole float64 | |||||
TCPSpuriousRtxHostQueues float64 | |||||
BusyPollRxPackets float64 | |||||
TCPAutoCorking float64 | |||||
TCPFromZeroWindowAdv float64 | |||||
TCPToZeroWindowAdv float64 | |||||
TCPWantZeroWindowAdv float64 | |||||
TCPSynRetrans float64 | |||||
TCPOrigDataSent float64 | |||||
TCPHystartTrainDetect float64 | |||||
TCPHystartTrainCwnd float64 | |||||
TCPHystartDelayDetect float64 | |||||
TCPHystartDelayCwnd float64 | |||||
TCPACKSkippedSynRecv float64 | |||||
TCPACKSkippedPAWS float64 | |||||
TCPACKSkippedSeq float64 | |||||
TCPACKSkippedFinWait2 float64 | |||||
TCPACKSkippedTimeWait float64 | |||||
TCPACKSkippedChallenge float64 | |||||
TCPWinProbe float64 | |||||
TCPKeepAlive float64 | |||||
TCPMTUPFail float64 | |||||
TCPMTUPSuccess float64 | |||||
TCPWqueueTooBig float64 | |||||
SyncookiesSent *float64 | |||||
SyncookiesRecv *float64 | |||||
SyncookiesFailed *float64 | |||||
EmbryonicRsts *float64 | |||||
PruneCalled *float64 | |||||
RcvPruned *float64 | |||||
OfoPruned *float64 | |||||
OutOfWindowIcmps *float64 | |||||
LockDroppedIcmps *float64 | |||||
ArpFilter *float64 | |||||
TW *float64 | |||||
TWRecycled *float64 | |||||
TWKilled *float64 | |||||
PAWSActive *float64 | |||||
PAWSEstab *float64 | |||||
DelayedACKs *float64 | |||||
DelayedACKLocked *float64 | |||||
DelayedACKLost *float64 | |||||
ListenOverflows *float64 | |||||
ListenDrops *float64 | |||||
TCPHPHits *float64 | |||||
TCPPureAcks *float64 | |||||
TCPHPAcks *float64 | |||||
TCPRenoRecovery *float64 | |||||
TCPSackRecovery *float64 | |||||
TCPSACKReneging *float64 | |||||
TCPSACKReorder *float64 | |||||
TCPRenoReorder *float64 | |||||
TCPTSReorder *float64 | |||||
TCPFullUndo *float64 | |||||
TCPPartialUndo *float64 | |||||
TCPDSACKUndo *float64 | |||||
TCPLossUndo *float64 | |||||
TCPLostRetransmit *float64 | |||||
TCPRenoFailures *float64 | |||||
TCPSackFailures *float64 | |||||
TCPLossFailures *float64 | |||||
TCPFastRetrans *float64 | |||||
TCPSlowStartRetrans *float64 | |||||
TCPTimeouts *float64 | |||||
TCPLossProbes *float64 | |||||
TCPLossProbeRecovery *float64 | |||||
TCPRenoRecoveryFail *float64 | |||||
TCPSackRecoveryFail *float64 | |||||
TCPRcvCollapsed *float64 | |||||
TCPDSACKOldSent *float64 | |||||
TCPDSACKOfoSent *float64 | |||||
TCPDSACKRecv *float64 | |||||
TCPDSACKOfoRecv *float64 | |||||
TCPAbortOnData *float64 | |||||
TCPAbortOnClose *float64 | |||||
TCPAbortOnMemory *float64 | |||||
TCPAbortOnTimeout *float64 | |||||
TCPAbortOnLinger *float64 | |||||
TCPAbortFailed *float64 | |||||
TCPMemoryPressures *float64 | |||||
TCPMemoryPressuresChrono *float64 | |||||
TCPSACKDiscard *float64 | |||||
TCPDSACKIgnoredOld *float64 | |||||
TCPDSACKIgnoredNoUndo *float64 | |||||
TCPSpuriousRTOs *float64 | |||||
TCPMD5NotFound *float64 | |||||
TCPMD5Unexpected *float64 | |||||
TCPMD5Failure *float64 | |||||
TCPSackShifted *float64 | |||||
TCPSackMerged *float64 | |||||
TCPSackShiftFallback *float64 | |||||
TCPBacklogDrop *float64 | |||||
PFMemallocDrop *float64 | |||||
TCPMinTTLDrop *float64 | |||||
TCPDeferAcceptDrop *float64 | |||||
IPReversePathFilter *float64 | |||||
TCPTimeWaitOverflow *float64 | |||||
TCPReqQFullDoCookies *float64 | |||||
TCPReqQFullDrop *float64 | |||||
TCPRetransFail *float64 | |||||
TCPRcvCoalesce *float64 | |||||
TCPRcvQDrop *float64 | |||||
TCPOFOQueue *float64 | |||||
TCPOFODrop *float64 | |||||
TCPOFOMerge *float64 | |||||
TCPChallengeACK *float64 | |||||
TCPSYNChallenge *float64 | |||||
TCPFastOpenActive *float64 | |||||
TCPFastOpenActiveFail *float64 | |||||
TCPFastOpenPassive *float64 | |||||
TCPFastOpenPassiveFail *float64 | |||||
TCPFastOpenListenOverflow *float64 | |||||
TCPFastOpenCookieReqd *float64 | |||||
TCPFastOpenBlackhole *float64 | |||||
TCPSpuriousRtxHostQueues *float64 | |||||
BusyPollRxPackets *float64 | |||||
TCPAutoCorking *float64 | |||||
TCPFromZeroWindowAdv *float64 | |||||
TCPToZeroWindowAdv *float64 | |||||
TCPWantZeroWindowAdv *float64 | |||||
TCPSynRetrans *float64 | |||||
TCPOrigDataSent *float64 | |||||
TCPHystartTrainDetect *float64 | |||||
TCPHystartTrainCwnd *float64 | |||||
TCPHystartDelayDetect *float64 | |||||
TCPHystartDelayCwnd *float64 | |||||
TCPACKSkippedSynRecv *float64 | |||||
TCPACKSkippedPAWS *float64 | |||||
TCPACKSkippedSeq *float64 | |||||
TCPACKSkippedFinWait2 *float64 | |||||
TCPACKSkippedTimeWait *float64 | |||||
TCPACKSkippedChallenge *float64 | |||||
TCPWinProbe *float64 | |||||
TCPKeepAlive *float64 | |||||
TCPMTUPFail *float64 | |||||
TCPMTUPSuccess *float64 | |||||
TCPWqueueTooBig *float64 | |||||
} | } | ||||
type IpExt struct { // nolint:revive | type IpExt struct { // nolint:revive | ||||
InNoRoutes float64 | |||||
InTruncatedPkts float64 | |||||
InMcastPkts float64 | |||||
OutMcastPkts float64 | |||||
InBcastPkts float64 | |||||
OutBcastPkts float64 | |||||
InOctets float64 | |||||
OutOctets float64 | |||||
InMcastOctets float64 | |||||
OutMcastOctets float64 | |||||
InBcastOctets float64 | |||||
OutBcastOctets float64 | |||||
InCsumErrors float64 | |||||
InNoECTPkts float64 | |||||
InECT1Pkts float64 | |||||
InECT0Pkts float64 | |||||
InCEPkts float64 | |||||
ReasmOverlaps float64 | |||||
InNoRoutes *float64 | |||||
InTruncatedPkts *float64 | |||||
InMcastPkts *float64 | |||||
OutMcastPkts *float64 | |||||
InBcastPkts *float64 | |||||
OutBcastPkts *float64 | |||||
InOctets *float64 | |||||
OutOctets *float64 | |||||
InMcastOctets *float64 | |||||
OutMcastOctets *float64 | |||||
InBcastOctets *float64 | |||||
OutBcastOctets *float64 | |||||
InCsumErrors *float64 | |||||
InNoECTPkts *float64 | |||||
InECT1Pkts *float64 | |||||
InECT0Pkts *float64 | |||||
InCEPkts *float64 | |||||
ReasmOverlaps *float64 | |||||
} | } | ||||
func (p Proc) Netstat() (ProcNetstat, error) { | func (p Proc) Netstat() (ProcNetstat, error) { | ||||
@@ -174,14 +175,14 @@ func (p Proc) Netstat() (ProcNetstat, error) { | |||||
if err != nil { | if err != nil { | ||||
return ProcNetstat{PID: p.PID}, err | return ProcNetstat{PID: p.PID}, err | ||||
} | } | ||||
procNetstat, err := parseNetstat(bytes.NewReader(data), filename) | |||||
procNetstat, err := parseProcNetstat(bytes.NewReader(data), filename) | |||||
procNetstat.PID = p.PID | procNetstat.PID = p.PID | ||||
return procNetstat, err | return procNetstat, err | ||||
} | } | ||||
// parseNetstat parses the metrics from proc/<pid>/net/netstat file | |||||
// parseProcNetstat parses the metrics from proc/<pid>/net/netstat file | |||||
// and returns a ProcNetstat structure. | // and returns a ProcNetstat structure. | ||||
func parseNetstat(r io.Reader, fileName string) (ProcNetstat, error) { | |||||
func parseProcNetstat(r io.Reader, fileName string) (ProcNetstat, error) { | |||||
var ( | var ( | ||||
scanner = bufio.NewScanner(r) | scanner = bufio.NewScanner(r) | ||||
procNetstat = ProcNetstat{} | procNetstat = ProcNetstat{} | ||||
@@ -208,230 +209,232 @@ func parseNetstat(r io.Reader, fileName string) (ProcNetstat, error) { | |||||
case "TcpExt": | case "TcpExt": | ||||
switch key { | switch key { | ||||
case "SyncookiesSent": | case "SyncookiesSent": | ||||
procNetstat.TcpExt.SyncookiesSent = value | |||||
procNetstat.TcpExt.SyncookiesSent = &value | |||||
case "SyncookiesRecv": | case "SyncookiesRecv": | ||||
procNetstat.TcpExt.SyncookiesRecv = value | |||||
procNetstat.TcpExt.SyncookiesRecv = &value | |||||
case "SyncookiesFailed": | case "SyncookiesFailed": | ||||
procNetstat.TcpExt.SyncookiesFailed = value | |||||
procNetstat.TcpExt.SyncookiesFailed = &value | |||||
case "EmbryonicRsts": | case "EmbryonicRsts": | ||||
procNetstat.TcpExt.EmbryonicRsts = value | |||||
procNetstat.TcpExt.EmbryonicRsts = &value | |||||
case "PruneCalled": | case "PruneCalled": | ||||
procNetstat.TcpExt.PruneCalled = value | |||||
procNetstat.TcpExt.PruneCalled = &value | |||||
case "RcvPruned": | case "RcvPruned": | ||||
procNetstat.TcpExt.RcvPruned = value | |||||
procNetstat.TcpExt.RcvPruned = &value | |||||
case "OfoPruned": | case "OfoPruned": | ||||
procNetstat.TcpExt.OfoPruned = value | |||||
procNetstat.TcpExt.OfoPruned = &value | |||||
case "OutOfWindowIcmps": | case "OutOfWindowIcmps": | ||||
procNetstat.TcpExt.OutOfWindowIcmps = value | |||||
procNetstat.TcpExt.OutOfWindowIcmps = &value | |||||
case "LockDroppedIcmps": | case "LockDroppedIcmps": | ||||
procNetstat.TcpExt.LockDroppedIcmps = value | |||||
procNetstat.TcpExt.LockDroppedIcmps = &value | |||||
case "ArpFilter": | case "ArpFilter": | ||||
procNetstat.TcpExt.ArpFilter = value | |||||
procNetstat.TcpExt.ArpFilter = &value | |||||
case "TW": | case "TW": | ||||
procNetstat.TcpExt.TW = value | |||||
procNetstat.TcpExt.TW = &value | |||||
case "TWRecycled": | case "TWRecycled": | ||||
procNetstat.TcpExt.TWRecycled = value | |||||
procNetstat.TcpExt.TWRecycled = &value | |||||
case "TWKilled": | case "TWKilled": | ||||
procNetstat.TcpExt.TWKilled = value | |||||
procNetstat.TcpExt.TWKilled = &value | |||||
case "PAWSActive": | case "PAWSActive": | ||||
procNetstat.TcpExt.PAWSActive = value | |||||
procNetstat.TcpExt.PAWSActive = &value | |||||
case "PAWSEstab": | case "PAWSEstab": | ||||
procNetstat.TcpExt.PAWSEstab = value | |||||
procNetstat.TcpExt.PAWSEstab = &value | |||||
case "DelayedACKs": | case "DelayedACKs": | ||||
procNetstat.TcpExt.DelayedACKs = value | |||||
procNetstat.TcpExt.DelayedACKs = &value | |||||
case "DelayedACKLocked": | case "DelayedACKLocked": | ||||
procNetstat.TcpExt.DelayedACKLocked = value | |||||
procNetstat.TcpExt.DelayedACKLocked = &value | |||||
case "DelayedACKLost": | case "DelayedACKLost": | ||||
procNetstat.TcpExt.DelayedACKLost = value | |||||
procNetstat.TcpExt.DelayedACKLost = &value | |||||
case "ListenOverflows": | case "ListenOverflows": | ||||
procNetstat.TcpExt.ListenOverflows = value | |||||
procNetstat.TcpExt.ListenOverflows = &value | |||||
case "ListenDrops": | case "ListenDrops": | ||||
procNetstat.TcpExt.ListenDrops = value | |||||
procNetstat.TcpExt.ListenDrops = &value | |||||
case "TCPHPHits": | case "TCPHPHits": | ||||
procNetstat.TcpExt.TCPHPHits = value | |||||
procNetstat.TcpExt.TCPHPHits = &value | |||||
case "TCPPureAcks": | case "TCPPureAcks": | ||||
procNetstat.TcpExt.TCPPureAcks = value | |||||
procNetstat.TcpExt.TCPPureAcks = &value | |||||
case "TCPHPAcks": | case "TCPHPAcks": | ||||
procNetstat.TcpExt.TCPHPAcks = value | |||||
procNetstat.TcpExt.TCPHPAcks = &value | |||||
case "TCPRenoRecovery": | case "TCPRenoRecovery": | ||||
procNetstat.TcpExt.TCPRenoRecovery = value | |||||
procNetstat.TcpExt.TCPRenoRecovery = &value | |||||
case "TCPSackRecovery": | case "TCPSackRecovery": | ||||
procNetstat.TcpExt.TCPSackRecovery = value | |||||
procNetstat.TcpExt.TCPSackRecovery = &value | |||||
case "TCPSACKReneging": | case "TCPSACKReneging": | ||||
procNetstat.TcpExt.TCPSACKReneging = value | |||||
procNetstat.TcpExt.TCPSACKReneging = &value | |||||
case "TCPSACKReorder": | case "TCPSACKReorder": | ||||
procNetstat.TcpExt.TCPSACKReorder = value | |||||
procNetstat.TcpExt.TCPSACKReorder = &value | |||||
case "TCPRenoReorder": | case "TCPRenoReorder": | ||||
procNetstat.TcpExt.TCPRenoReorder = value | |||||
procNetstat.TcpExt.TCPRenoReorder = &value | |||||
case "TCPTSReorder": | case "TCPTSReorder": | ||||
procNetstat.TcpExt.TCPTSReorder = value | |||||
procNetstat.TcpExt.TCPTSReorder = &value | |||||
case "TCPFullUndo": | case "TCPFullUndo": | ||||
procNetstat.TcpExt.TCPFullUndo = value | |||||
procNetstat.TcpExt.TCPFullUndo = &value | |||||
case "TCPPartialUndo": | case "TCPPartialUndo": | ||||
procNetstat.TcpExt.TCPPartialUndo = value | |||||
procNetstat.TcpExt.TCPPartialUndo = &value | |||||
case "TCPDSACKUndo": | case "TCPDSACKUndo": | ||||
procNetstat.TcpExt.TCPDSACKUndo = value | |||||
procNetstat.TcpExt.TCPDSACKUndo = &value | |||||
case "TCPLossUndo": | case "TCPLossUndo": | ||||
procNetstat.TcpExt.TCPLossUndo = value | |||||
procNetstat.TcpExt.TCPLossUndo = &value | |||||
case "TCPLostRetransmit": | case "TCPLostRetransmit": | ||||
procNetstat.TcpExt.TCPLostRetransmit = value | |||||
procNetstat.TcpExt.TCPLostRetransmit = &value | |||||
case "TCPRenoFailures": | case "TCPRenoFailures": | ||||
procNetstat.TcpExt.TCPRenoFailures = value | |||||
procNetstat.TcpExt.TCPRenoFailures = &value | |||||
case "TCPSackFailures": | case "TCPSackFailures": | ||||
procNetstat.TcpExt.TCPSackFailures = value | |||||
procNetstat.TcpExt.TCPSackFailures = &value | |||||
case "TCPLossFailures": | case "TCPLossFailures": | ||||
procNetstat.TcpExt.TCPLossFailures = value | |||||
procNetstat.TcpExt.TCPLossFailures = &value | |||||
case "TCPFastRetrans": | case "TCPFastRetrans": | ||||
procNetstat.TcpExt.TCPFastRetrans = value | |||||
procNetstat.TcpExt.TCPFastRetrans = &value | |||||
case "TCPSlowStartRetrans": | case "TCPSlowStartRetrans": | ||||
procNetstat.TcpExt.TCPSlowStartRetrans = value | |||||
procNetstat.TcpExt.TCPSlowStartRetrans = &value | |||||
case "TCPTimeouts": | case "TCPTimeouts": | ||||
procNetstat.TcpExt.TCPTimeouts = value | |||||
procNetstat.TcpExt.TCPTimeouts = &value | |||||
case "TCPLossProbes": | case "TCPLossProbes": | ||||
procNetstat.TcpExt.TCPLossProbes = value | |||||
procNetstat.TcpExt.TCPLossProbes = &value | |||||
case "TCPLossProbeRecovery": | case "TCPLossProbeRecovery": | ||||
procNetstat.TcpExt.TCPLossProbeRecovery = value | |||||
procNetstat.TcpExt.TCPLossProbeRecovery = &value | |||||
case "TCPRenoRecoveryFail": | case "TCPRenoRecoveryFail": | ||||
procNetstat.TcpExt.TCPRenoRecoveryFail = value | |||||
procNetstat.TcpExt.TCPRenoRecoveryFail = &value | |||||
case "TCPSackRecoveryFail": | case "TCPSackRecoveryFail": | ||||
procNetstat.TcpExt.TCPSackRecoveryFail = value | |||||
procNetstat.TcpExt.TCPSackRecoveryFail = &value | |||||
case "TCPRcvCollapsed": | case "TCPRcvCollapsed": | ||||
procNetstat.TcpExt.TCPRcvCollapsed = value | |||||
procNetstat.TcpExt.TCPRcvCollapsed = &value | |||||
case "TCPDSACKOldSent": | case "TCPDSACKOldSent": | ||||
procNetstat.TcpExt.TCPDSACKOldSent = value | |||||
procNetstat.TcpExt.TCPDSACKOldSent = &value | |||||
case "TCPDSACKOfoSent": | case "TCPDSACKOfoSent": | ||||
procNetstat.TcpExt.TCPDSACKOfoSent = value | |||||
procNetstat.TcpExt.TCPDSACKOfoSent = &value | |||||
case "TCPDSACKRecv": | case "TCPDSACKRecv": | ||||
procNetstat.TcpExt.TCPDSACKRecv = value | |||||
procNetstat.TcpExt.TCPDSACKRecv = &value | |||||
case "TCPDSACKOfoRecv": | case "TCPDSACKOfoRecv": | ||||
procNetstat.TcpExt.TCPDSACKOfoRecv = value | |||||
procNetstat.TcpExt.TCPDSACKOfoRecv = &value | |||||
case "TCPAbortOnData": | case "TCPAbortOnData": | ||||
procNetstat.TcpExt.TCPAbortOnData = value | |||||
procNetstat.TcpExt.TCPAbortOnData = &value | |||||
case "TCPAbortOnClose": | case "TCPAbortOnClose": | ||||
procNetstat.TcpExt.TCPAbortOnClose = value | |||||
procNetstat.TcpExt.TCPAbortOnClose = &value | |||||
case "TCPDeferAcceptDrop": | case "TCPDeferAcceptDrop": | ||||
procNetstat.TcpExt.TCPDeferAcceptDrop = value | |||||
procNetstat.TcpExt.TCPDeferAcceptDrop = &value | |||||
case "IPReversePathFilter": | case "IPReversePathFilter": | ||||
procNetstat.TcpExt.IPReversePathFilter = value | |||||
procNetstat.TcpExt.IPReversePathFilter = &value | |||||
case "TCPTimeWaitOverflow": | case "TCPTimeWaitOverflow": | ||||
procNetstat.TcpExt.TCPTimeWaitOverflow = value | |||||
procNetstat.TcpExt.TCPTimeWaitOverflow = &value | |||||
case "TCPReqQFullDoCookies": | case "TCPReqQFullDoCookies": | ||||
procNetstat.TcpExt.TCPReqQFullDoCookies = value | |||||
procNetstat.TcpExt.TCPReqQFullDoCookies = &value | |||||
case "TCPReqQFullDrop": | case "TCPReqQFullDrop": | ||||
procNetstat.TcpExt.TCPReqQFullDrop = value | |||||
procNetstat.TcpExt.TCPReqQFullDrop = &value | |||||
case "TCPRetransFail": | case "TCPRetransFail": | ||||
procNetstat.TcpExt.TCPRetransFail = value | |||||
procNetstat.TcpExt.TCPRetransFail = &value | |||||
case "TCPRcvCoalesce": | case "TCPRcvCoalesce": | ||||
procNetstat.TcpExt.TCPRcvCoalesce = value | |||||
procNetstat.TcpExt.TCPRcvCoalesce = &value | |||||
case "TCPRcvQDrop": | |||||
procNetstat.TcpExt.TCPRcvQDrop = &value | |||||
case "TCPOFOQueue": | case "TCPOFOQueue": | ||||
procNetstat.TcpExt.TCPOFOQueue = value | |||||
procNetstat.TcpExt.TCPOFOQueue = &value | |||||
case "TCPOFODrop": | case "TCPOFODrop": | ||||
procNetstat.TcpExt.TCPOFODrop = value | |||||
procNetstat.TcpExt.TCPOFODrop = &value | |||||
case "TCPOFOMerge": | case "TCPOFOMerge": | ||||
procNetstat.TcpExt.TCPOFOMerge = value | |||||
procNetstat.TcpExt.TCPOFOMerge = &value | |||||
case "TCPChallengeACK": | case "TCPChallengeACK": | ||||
procNetstat.TcpExt.TCPChallengeACK = value | |||||
procNetstat.TcpExt.TCPChallengeACK = &value | |||||
case "TCPSYNChallenge": | case "TCPSYNChallenge": | ||||
procNetstat.TcpExt.TCPSYNChallenge = value | |||||
procNetstat.TcpExt.TCPSYNChallenge = &value | |||||
case "TCPFastOpenActive": | case "TCPFastOpenActive": | ||||
procNetstat.TcpExt.TCPFastOpenActive = value | |||||
procNetstat.TcpExt.TCPFastOpenActive = &value | |||||
case "TCPFastOpenActiveFail": | case "TCPFastOpenActiveFail": | ||||
procNetstat.TcpExt.TCPFastOpenActiveFail = value | |||||
procNetstat.TcpExt.TCPFastOpenActiveFail = &value | |||||
case "TCPFastOpenPassive": | case "TCPFastOpenPassive": | ||||
procNetstat.TcpExt.TCPFastOpenPassive = value | |||||
procNetstat.TcpExt.TCPFastOpenPassive = &value | |||||
case "TCPFastOpenPassiveFail": | case "TCPFastOpenPassiveFail": | ||||
procNetstat.TcpExt.TCPFastOpenPassiveFail = value | |||||
procNetstat.TcpExt.TCPFastOpenPassiveFail = &value | |||||
case "TCPFastOpenListenOverflow": | case "TCPFastOpenListenOverflow": | ||||
procNetstat.TcpExt.TCPFastOpenListenOverflow = value | |||||
procNetstat.TcpExt.TCPFastOpenListenOverflow = &value | |||||
case "TCPFastOpenCookieReqd": | case "TCPFastOpenCookieReqd": | ||||
procNetstat.TcpExt.TCPFastOpenCookieReqd = value | |||||
procNetstat.TcpExt.TCPFastOpenCookieReqd = &value | |||||
case "TCPFastOpenBlackhole": | case "TCPFastOpenBlackhole": | ||||
procNetstat.TcpExt.TCPFastOpenBlackhole = value | |||||
procNetstat.TcpExt.TCPFastOpenBlackhole = &value | |||||
case "TCPSpuriousRtxHostQueues": | case "TCPSpuriousRtxHostQueues": | ||||
procNetstat.TcpExt.TCPSpuriousRtxHostQueues = value | |||||
procNetstat.TcpExt.TCPSpuriousRtxHostQueues = &value | |||||
case "BusyPollRxPackets": | case "BusyPollRxPackets": | ||||
procNetstat.TcpExt.BusyPollRxPackets = value | |||||
procNetstat.TcpExt.BusyPollRxPackets = &value | |||||
case "TCPAutoCorking": | case "TCPAutoCorking": | ||||
procNetstat.TcpExt.TCPAutoCorking = value | |||||
procNetstat.TcpExt.TCPAutoCorking = &value | |||||
case "TCPFromZeroWindowAdv": | case "TCPFromZeroWindowAdv": | ||||
procNetstat.TcpExt.TCPFromZeroWindowAdv = value | |||||
procNetstat.TcpExt.TCPFromZeroWindowAdv = &value | |||||
case "TCPToZeroWindowAdv": | case "TCPToZeroWindowAdv": | ||||
procNetstat.TcpExt.TCPToZeroWindowAdv = value | |||||
procNetstat.TcpExt.TCPToZeroWindowAdv = &value | |||||
case "TCPWantZeroWindowAdv": | case "TCPWantZeroWindowAdv": | ||||
procNetstat.TcpExt.TCPWantZeroWindowAdv = value | |||||
procNetstat.TcpExt.TCPWantZeroWindowAdv = &value | |||||
case "TCPSynRetrans": | case "TCPSynRetrans": | ||||
procNetstat.TcpExt.TCPSynRetrans = value | |||||
procNetstat.TcpExt.TCPSynRetrans = &value | |||||
case "TCPOrigDataSent": | case "TCPOrigDataSent": | ||||
procNetstat.TcpExt.TCPOrigDataSent = value | |||||
procNetstat.TcpExt.TCPOrigDataSent = &value | |||||
case "TCPHystartTrainDetect": | case "TCPHystartTrainDetect": | ||||
procNetstat.TcpExt.TCPHystartTrainDetect = value | |||||
procNetstat.TcpExt.TCPHystartTrainDetect = &value | |||||
case "TCPHystartTrainCwnd": | case "TCPHystartTrainCwnd": | ||||
procNetstat.TcpExt.TCPHystartTrainCwnd = value | |||||
procNetstat.TcpExt.TCPHystartTrainCwnd = &value | |||||
case "TCPHystartDelayDetect": | case "TCPHystartDelayDetect": | ||||
procNetstat.TcpExt.TCPHystartDelayDetect = value | |||||
procNetstat.TcpExt.TCPHystartDelayDetect = &value | |||||
case "TCPHystartDelayCwnd": | case "TCPHystartDelayCwnd": | ||||
procNetstat.TcpExt.TCPHystartDelayCwnd = value | |||||
procNetstat.TcpExt.TCPHystartDelayCwnd = &value | |||||
case "TCPACKSkippedSynRecv": | case "TCPACKSkippedSynRecv": | ||||
procNetstat.TcpExt.TCPACKSkippedSynRecv = value | |||||
procNetstat.TcpExt.TCPACKSkippedSynRecv = &value | |||||
case "TCPACKSkippedPAWS": | case "TCPACKSkippedPAWS": | ||||
procNetstat.TcpExt.TCPACKSkippedPAWS = value | |||||
procNetstat.TcpExt.TCPACKSkippedPAWS = &value | |||||
case "TCPACKSkippedSeq": | case "TCPACKSkippedSeq": | ||||
procNetstat.TcpExt.TCPACKSkippedSeq = value | |||||
procNetstat.TcpExt.TCPACKSkippedSeq = &value | |||||
case "TCPACKSkippedFinWait2": | case "TCPACKSkippedFinWait2": | ||||
procNetstat.TcpExt.TCPACKSkippedFinWait2 = value | |||||
procNetstat.TcpExt.TCPACKSkippedFinWait2 = &value | |||||
case "TCPACKSkippedTimeWait": | case "TCPACKSkippedTimeWait": | ||||
procNetstat.TcpExt.TCPACKSkippedTimeWait = value | |||||
procNetstat.TcpExt.TCPACKSkippedTimeWait = &value | |||||
case "TCPACKSkippedChallenge": | case "TCPACKSkippedChallenge": | ||||
procNetstat.TcpExt.TCPACKSkippedChallenge = value | |||||
procNetstat.TcpExt.TCPACKSkippedChallenge = &value | |||||
case "TCPWinProbe": | case "TCPWinProbe": | ||||
procNetstat.TcpExt.TCPWinProbe = value | |||||
procNetstat.TcpExt.TCPWinProbe = &value | |||||
case "TCPKeepAlive": | case "TCPKeepAlive": | ||||
procNetstat.TcpExt.TCPKeepAlive = value | |||||
procNetstat.TcpExt.TCPKeepAlive = &value | |||||
case "TCPMTUPFail": | case "TCPMTUPFail": | ||||
procNetstat.TcpExt.TCPMTUPFail = value | |||||
procNetstat.TcpExt.TCPMTUPFail = &value | |||||
case "TCPMTUPSuccess": | case "TCPMTUPSuccess": | ||||
procNetstat.TcpExt.TCPMTUPSuccess = value | |||||
procNetstat.TcpExt.TCPMTUPSuccess = &value | |||||
case "TCPWqueueTooBig": | case "TCPWqueueTooBig": | ||||
procNetstat.TcpExt.TCPWqueueTooBig = value | |||||
procNetstat.TcpExt.TCPWqueueTooBig = &value | |||||
} | } | ||||
case "IpExt": | case "IpExt": | ||||
switch key { | switch key { | ||||
case "InNoRoutes": | case "InNoRoutes": | ||||
procNetstat.IpExt.InNoRoutes = value | |||||
procNetstat.IpExt.InNoRoutes = &value | |||||
case "InTruncatedPkts": | case "InTruncatedPkts": | ||||
procNetstat.IpExt.InTruncatedPkts = value | |||||
procNetstat.IpExt.InTruncatedPkts = &value | |||||
case "InMcastPkts": | case "InMcastPkts": | ||||
procNetstat.IpExt.InMcastPkts = value | |||||
procNetstat.IpExt.InMcastPkts = &value | |||||
case "OutMcastPkts": | case "OutMcastPkts": | ||||
procNetstat.IpExt.OutMcastPkts = value | |||||
procNetstat.IpExt.OutMcastPkts = &value | |||||
case "InBcastPkts": | case "InBcastPkts": | ||||
procNetstat.IpExt.InBcastPkts = value | |||||
procNetstat.IpExt.InBcastPkts = &value | |||||
case "OutBcastPkts": | case "OutBcastPkts": | ||||
procNetstat.IpExt.OutBcastPkts = value | |||||
procNetstat.IpExt.OutBcastPkts = &value | |||||
case "InOctets": | case "InOctets": | ||||
procNetstat.IpExt.InOctets = value | |||||
procNetstat.IpExt.InOctets = &value | |||||
case "OutOctets": | case "OutOctets": | ||||
procNetstat.IpExt.OutOctets = value | |||||
procNetstat.IpExt.OutOctets = &value | |||||
case "InMcastOctets": | case "InMcastOctets": | ||||
procNetstat.IpExt.InMcastOctets = value | |||||
procNetstat.IpExt.InMcastOctets = &value | |||||
case "OutMcastOctets": | case "OutMcastOctets": | ||||
procNetstat.IpExt.OutMcastOctets = value | |||||
procNetstat.IpExt.OutMcastOctets = &value | |||||
case "InBcastOctets": | case "InBcastOctets": | ||||
procNetstat.IpExt.InBcastOctets = value | |||||
procNetstat.IpExt.InBcastOctets = &value | |||||
case "OutBcastOctets": | case "OutBcastOctets": | ||||
procNetstat.IpExt.OutBcastOctets = value | |||||
procNetstat.IpExt.OutBcastOctets = &value | |||||
case "InCsumErrors": | case "InCsumErrors": | ||||
procNetstat.IpExt.InCsumErrors = value | |||||
procNetstat.IpExt.InCsumErrors = &value | |||||
case "InNoECTPkts": | case "InNoECTPkts": | ||||
procNetstat.IpExt.InNoECTPkts = value | |||||
procNetstat.IpExt.InNoECTPkts = &value | |||||
case "InECT1Pkts": | case "InECT1Pkts": | ||||
procNetstat.IpExt.InECT1Pkts = value | |||||
procNetstat.IpExt.InECT1Pkts = &value | |||||
case "InECT0Pkts": | case "InECT0Pkts": | ||||
procNetstat.IpExt.InECT0Pkts = value | |||||
procNetstat.IpExt.InECT0Pkts = &value | |||||
case "InCEPkts": | case "InCEPkts": | ||||
procNetstat.IpExt.InCEPkts = value | |||||
procNetstat.IpExt.InCEPkts = &value | |||||
case "ReasmOverlaps": | case "ReasmOverlaps": | ||||
procNetstat.IpExt.ReasmOverlaps = value | |||||
procNetstat.IpExt.ReasmOverlaps = &value | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -37,100 +37,100 @@ type ProcSnmp struct { | |||||
} | } | ||||
type Ip struct { // nolint:revive | type Ip struct { // nolint:revive | ||||
Forwarding float64 | |||||
DefaultTTL float64 | |||||
InReceives float64 | |||||
InHdrErrors float64 | |||||
InAddrErrors float64 | |||||
ForwDatagrams float64 | |||||
InUnknownProtos float64 | |||||
InDiscards float64 | |||||
InDelivers float64 | |||||
OutRequests float64 | |||||
OutDiscards float64 | |||||
OutNoRoutes float64 | |||||
ReasmTimeout float64 | |||||
ReasmReqds float64 | |||||
ReasmOKs float64 | |||||
ReasmFails float64 | |||||
FragOKs float64 | |||||
FragFails float64 | |||||
FragCreates float64 | |||||
Forwarding *float64 | |||||
DefaultTTL *float64 | |||||
InReceives *float64 | |||||
InHdrErrors *float64 | |||||
InAddrErrors *float64 | |||||
ForwDatagrams *float64 | |||||
InUnknownProtos *float64 | |||||
InDiscards *float64 | |||||
InDelivers *float64 | |||||
OutRequests *float64 | |||||
OutDiscards *float64 | |||||
OutNoRoutes *float64 | |||||
ReasmTimeout *float64 | |||||
ReasmReqds *float64 | |||||
ReasmOKs *float64 | |||||
ReasmFails *float64 | |||||
FragOKs *float64 | |||||
FragFails *float64 | |||||
FragCreates *float64 | |||||
} | } | ||||
type Icmp struct { | |||||
InMsgs float64 | |||||
InErrors float64 | |||||
InCsumErrors float64 | |||||
InDestUnreachs float64 | |||||
InTimeExcds float64 | |||||
InParmProbs float64 | |||||
InSrcQuenchs float64 | |||||
InRedirects float64 | |||||
InEchos float64 | |||||
InEchoReps float64 | |||||
InTimestamps float64 | |||||
InTimestampReps float64 | |||||
InAddrMasks float64 | |||||
InAddrMaskReps float64 | |||||
OutMsgs float64 | |||||
OutErrors float64 | |||||
OutDestUnreachs float64 | |||||
OutTimeExcds float64 | |||||
OutParmProbs float64 | |||||
OutSrcQuenchs float64 | |||||
OutRedirects float64 | |||||
OutEchos float64 | |||||
OutEchoReps float64 | |||||
OutTimestamps float64 | |||||
OutTimestampReps float64 | |||||
OutAddrMasks float64 | |||||
OutAddrMaskReps float64 | |||||
type Icmp struct { // nolint:revive | |||||
InMsgs *float64 | |||||
InErrors *float64 | |||||
InCsumErrors *float64 | |||||
InDestUnreachs *float64 | |||||
InTimeExcds *float64 | |||||
InParmProbs *float64 | |||||
InSrcQuenchs *float64 | |||||
InRedirects *float64 | |||||
InEchos *float64 | |||||
InEchoReps *float64 | |||||
InTimestamps *float64 | |||||
InTimestampReps *float64 | |||||
InAddrMasks *float64 | |||||
InAddrMaskReps *float64 | |||||
OutMsgs *float64 | |||||
OutErrors *float64 | |||||
OutDestUnreachs *float64 | |||||
OutTimeExcds *float64 | |||||
OutParmProbs *float64 | |||||
OutSrcQuenchs *float64 | |||||
OutRedirects *float64 | |||||
OutEchos *float64 | |||||
OutEchoReps *float64 | |||||
OutTimestamps *float64 | |||||
OutTimestampReps *float64 | |||||
OutAddrMasks *float64 | |||||
OutAddrMaskReps *float64 | |||||
} | } | ||||
type IcmpMsg struct { | type IcmpMsg struct { | ||||
InType3 float64 | |||||
OutType3 float64 | |||||
InType3 *float64 | |||||
OutType3 *float64 | |||||
} | } | ||||
type Tcp struct { // nolint:revive | type Tcp struct { // nolint:revive | ||||
RtoAlgorithm float64 | |||||
RtoMin float64 | |||||
RtoMax float64 | |||||
MaxConn float64 | |||||
ActiveOpens float64 | |||||
PassiveOpens float64 | |||||
AttemptFails float64 | |||||
EstabResets float64 | |||||
CurrEstab float64 | |||||
InSegs float64 | |||||
OutSegs float64 | |||||
RetransSegs float64 | |||||
InErrs float64 | |||||
OutRsts float64 | |||||
InCsumErrors float64 | |||||
RtoAlgorithm *float64 | |||||
RtoMin *float64 | |||||
RtoMax *float64 | |||||
MaxConn *float64 | |||||
ActiveOpens *float64 | |||||
PassiveOpens *float64 | |||||
AttemptFails *float64 | |||||
EstabResets *float64 | |||||
CurrEstab *float64 | |||||
InSegs *float64 | |||||
OutSegs *float64 | |||||
RetransSegs *float64 | |||||
InErrs *float64 | |||||
OutRsts *float64 | |||||
InCsumErrors *float64 | |||||
} | } | ||||
type Udp struct { // nolint:revive | type Udp struct { // nolint:revive | ||||
InDatagrams float64 | |||||
NoPorts float64 | |||||
InErrors float64 | |||||
OutDatagrams float64 | |||||
RcvbufErrors float64 | |||||
SndbufErrors float64 | |||||
InCsumErrors float64 | |||||
IgnoredMulti float64 | |||||
InDatagrams *float64 | |||||
NoPorts *float64 | |||||
InErrors *float64 | |||||
OutDatagrams *float64 | |||||
RcvbufErrors *float64 | |||||
SndbufErrors *float64 | |||||
InCsumErrors *float64 | |||||
IgnoredMulti *float64 | |||||
} | } | ||||
type UdpLite struct { // nolint:revive | type UdpLite struct { // nolint:revive | ||||
InDatagrams float64 | |||||
NoPorts float64 | |||||
InErrors float64 | |||||
OutDatagrams float64 | |||||
RcvbufErrors float64 | |||||
SndbufErrors float64 | |||||
InCsumErrors float64 | |||||
IgnoredMulti float64 | |||||
InDatagrams *float64 | |||||
NoPorts *float64 | |||||
InErrors *float64 | |||||
OutDatagrams *float64 | |||||
RcvbufErrors *float64 | |||||
SndbufErrors *float64 | |||||
InCsumErrors *float64 | |||||
IgnoredMulti *float64 | |||||
} | } | ||||
func (p Proc) Snmp() (ProcSnmp, error) { | func (p Proc) Snmp() (ProcSnmp, error) { | ||||
@@ -173,178 +173,178 @@ func parseSnmp(r io.Reader, fileName string) (ProcSnmp, error) { | |||||
case "Ip": | case "Ip": | ||||
switch key { | switch key { | ||||
case "Forwarding": | case "Forwarding": | ||||
procSnmp.Ip.Forwarding = value | |||||
procSnmp.Ip.Forwarding = &value | |||||
case "DefaultTTL": | case "DefaultTTL": | ||||
procSnmp.Ip.DefaultTTL = value | |||||
procSnmp.Ip.DefaultTTL = &value | |||||
case "InReceives": | case "InReceives": | ||||
procSnmp.Ip.InReceives = value | |||||
procSnmp.Ip.InReceives = &value | |||||
case "InHdrErrors": | case "InHdrErrors": | ||||
procSnmp.Ip.InHdrErrors = value | |||||
procSnmp.Ip.InHdrErrors = &value | |||||
case "InAddrErrors": | case "InAddrErrors": | ||||
procSnmp.Ip.InAddrErrors = value | |||||
procSnmp.Ip.InAddrErrors = &value | |||||
case "ForwDatagrams": | case "ForwDatagrams": | ||||
procSnmp.Ip.ForwDatagrams = value | |||||
procSnmp.Ip.ForwDatagrams = &value | |||||
case "InUnknownProtos": | case "InUnknownProtos": | ||||
procSnmp.Ip.InUnknownProtos = value | |||||
procSnmp.Ip.InUnknownProtos = &value | |||||
case "InDiscards": | case "InDiscards": | ||||
procSnmp.Ip.InDiscards = value | |||||
procSnmp.Ip.InDiscards = &value | |||||
case "InDelivers": | case "InDelivers": | ||||
procSnmp.Ip.InDelivers = value | |||||
procSnmp.Ip.InDelivers = &value | |||||
case "OutRequests": | case "OutRequests": | ||||
procSnmp.Ip.OutRequests = value | |||||
procSnmp.Ip.OutRequests = &value | |||||
case "OutDiscards": | case "OutDiscards": | ||||
procSnmp.Ip.OutDiscards = value | |||||
procSnmp.Ip.OutDiscards = &value | |||||
case "OutNoRoutes": | case "OutNoRoutes": | ||||
procSnmp.Ip.OutNoRoutes = value | |||||
procSnmp.Ip.OutNoRoutes = &value | |||||
case "ReasmTimeout": | case "ReasmTimeout": | ||||
procSnmp.Ip.ReasmTimeout = value | |||||
procSnmp.Ip.ReasmTimeout = &value | |||||
case "ReasmReqds": | case "ReasmReqds": | ||||
procSnmp.Ip.ReasmReqds = value | |||||
procSnmp.Ip.ReasmReqds = &value | |||||
case "ReasmOKs": | case "ReasmOKs": | ||||
procSnmp.Ip.ReasmOKs = value | |||||
procSnmp.Ip.ReasmOKs = &value | |||||
case "ReasmFails": | case "ReasmFails": | ||||
procSnmp.Ip.ReasmFails = value | |||||
procSnmp.Ip.ReasmFails = &value | |||||
case "FragOKs": | case "FragOKs": | ||||
procSnmp.Ip.FragOKs = value | |||||
procSnmp.Ip.FragOKs = &value | |||||
case "FragFails": | case "FragFails": | ||||
procSnmp.Ip.FragFails = value | |||||
procSnmp.Ip.FragFails = &value | |||||
case "FragCreates": | case "FragCreates": | ||||
procSnmp.Ip.FragCreates = value | |||||
procSnmp.Ip.FragCreates = &value | |||||
} | } | ||||
case "Icmp": | case "Icmp": | ||||
switch key { | switch key { | ||||
case "InMsgs": | case "InMsgs": | ||||
procSnmp.Icmp.InMsgs = value | |||||
procSnmp.Icmp.InMsgs = &value | |||||
case "InErrors": | case "InErrors": | ||||
procSnmp.Icmp.InErrors = value | |||||
procSnmp.Icmp.InErrors = &value | |||||
case "InCsumErrors": | case "InCsumErrors": | ||||
procSnmp.Icmp.InCsumErrors = value | |||||
procSnmp.Icmp.InCsumErrors = &value | |||||
case "InDestUnreachs": | case "InDestUnreachs": | ||||
procSnmp.Icmp.InDestUnreachs = value | |||||
procSnmp.Icmp.InDestUnreachs = &value | |||||
case "InTimeExcds": | case "InTimeExcds": | ||||
procSnmp.Icmp.InTimeExcds = value | |||||
procSnmp.Icmp.InTimeExcds = &value | |||||
case "InParmProbs": | case "InParmProbs": | ||||
procSnmp.Icmp.InParmProbs = value | |||||
procSnmp.Icmp.InParmProbs = &value | |||||
case "InSrcQuenchs": | case "InSrcQuenchs": | ||||
procSnmp.Icmp.InSrcQuenchs = value | |||||
procSnmp.Icmp.InSrcQuenchs = &value | |||||
case "InRedirects": | case "InRedirects": | ||||
procSnmp.Icmp.InRedirects = value | |||||
procSnmp.Icmp.InRedirects = &value | |||||
case "InEchos": | case "InEchos": | ||||
procSnmp.Icmp.InEchos = value | |||||
procSnmp.Icmp.InEchos = &value | |||||
case "InEchoReps": | case "InEchoReps": | ||||
procSnmp.Icmp.InEchoReps = value | |||||
procSnmp.Icmp.InEchoReps = &value | |||||
case "InTimestamps": | case "InTimestamps": | ||||
procSnmp.Icmp.InTimestamps = value | |||||
procSnmp.Icmp.InTimestamps = &value | |||||
case "InTimestampReps": | case "InTimestampReps": | ||||
procSnmp.Icmp.InTimestampReps = value | |||||
procSnmp.Icmp.InTimestampReps = &value | |||||
case "InAddrMasks": | case "InAddrMasks": | ||||
procSnmp.Icmp.InAddrMasks = value | |||||
procSnmp.Icmp.InAddrMasks = &value | |||||
case "InAddrMaskReps": | case "InAddrMaskReps": | ||||
procSnmp.Icmp.InAddrMaskReps = value | |||||
procSnmp.Icmp.InAddrMaskReps = &value | |||||
case "OutMsgs": | case "OutMsgs": | ||||
procSnmp.Icmp.OutMsgs = value | |||||
procSnmp.Icmp.OutMsgs = &value | |||||
case "OutErrors": | case "OutErrors": | ||||
procSnmp.Icmp.OutErrors = value | |||||
procSnmp.Icmp.OutErrors = &value | |||||
case "OutDestUnreachs": | case "OutDestUnreachs": | ||||
procSnmp.Icmp.OutDestUnreachs = value | |||||
procSnmp.Icmp.OutDestUnreachs = &value | |||||
case "OutTimeExcds": | case "OutTimeExcds": | ||||
procSnmp.Icmp.OutTimeExcds = value | |||||
procSnmp.Icmp.OutTimeExcds = &value | |||||
case "OutParmProbs": | case "OutParmProbs": | ||||
procSnmp.Icmp.OutParmProbs = value | |||||
procSnmp.Icmp.OutParmProbs = &value | |||||
case "OutSrcQuenchs": | case "OutSrcQuenchs": | ||||
procSnmp.Icmp.OutSrcQuenchs = value | |||||
procSnmp.Icmp.OutSrcQuenchs = &value | |||||
case "OutRedirects": | case "OutRedirects": | ||||
procSnmp.Icmp.OutRedirects = value | |||||
procSnmp.Icmp.OutRedirects = &value | |||||
case "OutEchos": | case "OutEchos": | ||||
procSnmp.Icmp.OutEchos = value | |||||
procSnmp.Icmp.OutEchos = &value | |||||
case "OutEchoReps": | case "OutEchoReps": | ||||
procSnmp.Icmp.OutEchoReps = value | |||||
procSnmp.Icmp.OutEchoReps = &value | |||||
case "OutTimestamps": | case "OutTimestamps": | ||||
procSnmp.Icmp.OutTimestamps = value | |||||
procSnmp.Icmp.OutTimestamps = &value | |||||
case "OutTimestampReps": | case "OutTimestampReps": | ||||
procSnmp.Icmp.OutTimestampReps = value | |||||
procSnmp.Icmp.OutTimestampReps = &value | |||||
case "OutAddrMasks": | case "OutAddrMasks": | ||||
procSnmp.Icmp.OutAddrMasks = value | |||||
procSnmp.Icmp.OutAddrMasks = &value | |||||
case "OutAddrMaskReps": | case "OutAddrMaskReps": | ||||
procSnmp.Icmp.OutAddrMaskReps = value | |||||
procSnmp.Icmp.OutAddrMaskReps = &value | |||||
} | } | ||||
case "IcmpMsg": | case "IcmpMsg": | ||||
switch key { | switch key { | ||||
case "InType3": | case "InType3": | ||||
procSnmp.IcmpMsg.InType3 = value | |||||
procSnmp.IcmpMsg.InType3 = &value | |||||
case "OutType3": | case "OutType3": | ||||
procSnmp.IcmpMsg.OutType3 = value | |||||
procSnmp.IcmpMsg.OutType3 = &value | |||||
} | } | ||||
case "Tcp": | case "Tcp": | ||||
switch key { | switch key { | ||||
case "RtoAlgorithm": | case "RtoAlgorithm": | ||||
procSnmp.Tcp.RtoAlgorithm = value | |||||
procSnmp.Tcp.RtoAlgorithm = &value | |||||
case "RtoMin": | case "RtoMin": | ||||
procSnmp.Tcp.RtoMin = value | |||||
procSnmp.Tcp.RtoMin = &value | |||||
case "RtoMax": | case "RtoMax": | ||||
procSnmp.Tcp.RtoMax = value | |||||
procSnmp.Tcp.RtoMax = &value | |||||
case "MaxConn": | case "MaxConn": | ||||
procSnmp.Tcp.MaxConn = value | |||||
procSnmp.Tcp.MaxConn = &value | |||||
case "ActiveOpens": | case "ActiveOpens": | ||||
procSnmp.Tcp.ActiveOpens = value | |||||
procSnmp.Tcp.ActiveOpens = &value | |||||
case "PassiveOpens": | case "PassiveOpens": | ||||
procSnmp.Tcp.PassiveOpens = value | |||||
procSnmp.Tcp.PassiveOpens = &value | |||||
case "AttemptFails": | case "AttemptFails": | ||||
procSnmp.Tcp.AttemptFails = value | |||||
procSnmp.Tcp.AttemptFails = &value | |||||
case "EstabResets": | case "EstabResets": | ||||
procSnmp.Tcp.EstabResets = value | |||||
procSnmp.Tcp.EstabResets = &value | |||||
case "CurrEstab": | case "CurrEstab": | ||||
procSnmp.Tcp.CurrEstab = value | |||||
procSnmp.Tcp.CurrEstab = &value | |||||
case "InSegs": | case "InSegs": | ||||
procSnmp.Tcp.InSegs = value | |||||
procSnmp.Tcp.InSegs = &value | |||||
case "OutSegs": | case "OutSegs": | ||||
procSnmp.Tcp.OutSegs = value | |||||
procSnmp.Tcp.OutSegs = &value | |||||
case "RetransSegs": | case "RetransSegs": | ||||
procSnmp.Tcp.RetransSegs = value | |||||
procSnmp.Tcp.RetransSegs = &value | |||||
case "InErrs": | case "InErrs": | ||||
procSnmp.Tcp.InErrs = value | |||||
procSnmp.Tcp.InErrs = &value | |||||
case "OutRsts": | case "OutRsts": | ||||
procSnmp.Tcp.OutRsts = value | |||||
procSnmp.Tcp.OutRsts = &value | |||||
case "InCsumErrors": | case "InCsumErrors": | ||||
procSnmp.Tcp.InCsumErrors = value | |||||
procSnmp.Tcp.InCsumErrors = &value | |||||
} | } | ||||
case "Udp": | case "Udp": | ||||
switch key { | switch key { | ||||
case "InDatagrams": | case "InDatagrams": | ||||
procSnmp.Udp.InDatagrams = value | |||||
procSnmp.Udp.InDatagrams = &value | |||||
case "NoPorts": | case "NoPorts": | ||||
procSnmp.Udp.NoPorts = value | |||||
procSnmp.Udp.NoPorts = &value | |||||
case "InErrors": | case "InErrors": | ||||
procSnmp.Udp.InErrors = value | |||||
procSnmp.Udp.InErrors = &value | |||||
case "OutDatagrams": | case "OutDatagrams": | ||||
procSnmp.Udp.OutDatagrams = value | |||||
procSnmp.Udp.OutDatagrams = &value | |||||
case "RcvbufErrors": | case "RcvbufErrors": | ||||
procSnmp.Udp.RcvbufErrors = value | |||||
procSnmp.Udp.RcvbufErrors = &value | |||||
case "SndbufErrors": | case "SndbufErrors": | ||||
procSnmp.Udp.SndbufErrors = value | |||||
procSnmp.Udp.SndbufErrors = &value | |||||
case "InCsumErrors": | case "InCsumErrors": | ||||
procSnmp.Udp.InCsumErrors = value | |||||
procSnmp.Udp.InCsumErrors = &value | |||||
case "IgnoredMulti": | case "IgnoredMulti": | ||||
procSnmp.Udp.IgnoredMulti = value | |||||
procSnmp.Udp.IgnoredMulti = &value | |||||
} | } | ||||
case "UdpLite": | case "UdpLite": | ||||
switch key { | switch key { | ||||
case "InDatagrams": | case "InDatagrams": | ||||
procSnmp.UdpLite.InDatagrams = value | |||||
procSnmp.UdpLite.InDatagrams = &value | |||||
case "NoPorts": | case "NoPorts": | ||||
procSnmp.UdpLite.NoPorts = value | |||||
procSnmp.UdpLite.NoPorts = &value | |||||
case "InErrors": | case "InErrors": | ||||
procSnmp.UdpLite.InErrors = value | |||||
procSnmp.UdpLite.InErrors = &value | |||||
case "OutDatagrams": | case "OutDatagrams": | ||||
procSnmp.UdpLite.OutDatagrams = value | |||||
procSnmp.UdpLite.OutDatagrams = &value | |||||
case "RcvbufErrors": | case "RcvbufErrors": | ||||
procSnmp.UdpLite.RcvbufErrors = value | |||||
procSnmp.UdpLite.RcvbufErrors = &value | |||||
case "SndbufErrors": | case "SndbufErrors": | ||||
procSnmp.UdpLite.SndbufErrors = value | |||||
procSnmp.UdpLite.SndbufErrors = &value | |||||
case "InCsumErrors": | case "InCsumErrors": | ||||
procSnmp.UdpLite.InCsumErrors = value | |||||
procSnmp.UdpLite.InCsumErrors = &value | |||||
case "IgnoredMulti": | case "IgnoredMulti": | ||||
procSnmp.UdpLite.IgnoredMulti = value | |||||
procSnmp.UdpLite.IgnoredMulti = &value | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -36,106 +36,106 @@ type ProcSnmp6 struct { | |||||
} | } | ||||
type Ip6 struct { // nolint:revive | type Ip6 struct { // nolint:revive | ||||
InReceives float64 | |||||
InHdrErrors float64 | |||||
InTooBigErrors float64 | |||||
InNoRoutes float64 | |||||
InAddrErrors float64 | |||||
InUnknownProtos float64 | |||||
InTruncatedPkts float64 | |||||
InDiscards float64 | |||||
InDelivers float64 | |||||
OutForwDatagrams float64 | |||||
OutRequests float64 | |||||
OutDiscards float64 | |||||
OutNoRoutes float64 | |||||
ReasmTimeout float64 | |||||
ReasmReqds float64 | |||||
ReasmOKs float64 | |||||
ReasmFails float64 | |||||
FragOKs float64 | |||||
FragFails float64 | |||||
FragCreates float64 | |||||
InMcastPkts float64 | |||||
OutMcastPkts float64 | |||||
InOctets float64 | |||||
OutOctets float64 | |||||
InMcastOctets float64 | |||||
OutMcastOctets float64 | |||||
InBcastOctets float64 | |||||
OutBcastOctets float64 | |||||
InNoECTPkts float64 | |||||
InECT1Pkts float64 | |||||
InECT0Pkts float64 | |||||
InCEPkts float64 | |||||
InReceives *float64 | |||||
InHdrErrors *float64 | |||||
InTooBigErrors *float64 | |||||
InNoRoutes *float64 | |||||
InAddrErrors *float64 | |||||
InUnknownProtos *float64 | |||||
InTruncatedPkts *float64 | |||||
InDiscards *float64 | |||||
InDelivers *float64 | |||||
OutForwDatagrams *float64 | |||||
OutRequests *float64 | |||||
OutDiscards *float64 | |||||
OutNoRoutes *float64 | |||||
ReasmTimeout *float64 | |||||
ReasmReqds *float64 | |||||
ReasmOKs *float64 | |||||
ReasmFails *float64 | |||||
FragOKs *float64 | |||||
FragFails *float64 | |||||
FragCreates *float64 | |||||
InMcastPkts *float64 | |||||
OutMcastPkts *float64 | |||||
InOctets *float64 | |||||
OutOctets *float64 | |||||
InMcastOctets *float64 | |||||
OutMcastOctets *float64 | |||||
InBcastOctets *float64 | |||||
OutBcastOctets *float64 | |||||
InNoECTPkts *float64 | |||||
InECT1Pkts *float64 | |||||
InECT0Pkts *float64 | |||||
InCEPkts *float64 | |||||
} | } | ||||
type Icmp6 struct { | type Icmp6 struct { | ||||
InMsgs float64 | |||||
InErrors float64 | |||||
OutMsgs float64 | |||||
OutErrors float64 | |||||
InCsumErrors float64 | |||||
InDestUnreachs float64 | |||||
InPktTooBigs float64 | |||||
InTimeExcds float64 | |||||
InParmProblems float64 | |||||
InEchos float64 | |||||
InEchoReplies float64 | |||||
InGroupMembQueries float64 | |||||
InGroupMembResponses float64 | |||||
InGroupMembReductions float64 | |||||
InRouterSolicits float64 | |||||
InRouterAdvertisements float64 | |||||
InNeighborSolicits float64 | |||||
InNeighborAdvertisements float64 | |||||
InRedirects float64 | |||||
InMLDv2Reports float64 | |||||
OutDestUnreachs float64 | |||||
OutPktTooBigs float64 | |||||
OutTimeExcds float64 | |||||
OutParmProblems float64 | |||||
OutEchos float64 | |||||
OutEchoReplies float64 | |||||
OutGroupMembQueries float64 | |||||
OutGroupMembResponses float64 | |||||
OutGroupMembReductions float64 | |||||
OutRouterSolicits float64 | |||||
OutRouterAdvertisements float64 | |||||
OutNeighborSolicits float64 | |||||
OutNeighborAdvertisements float64 | |||||
OutRedirects float64 | |||||
OutMLDv2Reports float64 | |||||
InType1 float64 | |||||
InType134 float64 | |||||
InType135 float64 | |||||
InType136 float64 | |||||
InType143 float64 | |||||
OutType133 float64 | |||||
OutType135 float64 | |||||
OutType136 float64 | |||||
OutType143 float64 | |||||
InMsgs *float64 | |||||
InErrors *float64 | |||||
OutMsgs *float64 | |||||
OutErrors *float64 | |||||
InCsumErrors *float64 | |||||
InDestUnreachs *float64 | |||||
InPktTooBigs *float64 | |||||
InTimeExcds *float64 | |||||
InParmProblems *float64 | |||||
InEchos *float64 | |||||
InEchoReplies *float64 | |||||
InGroupMembQueries *float64 | |||||
InGroupMembResponses *float64 | |||||
InGroupMembReductions *float64 | |||||
InRouterSolicits *float64 | |||||
InRouterAdvertisements *float64 | |||||
InNeighborSolicits *float64 | |||||
InNeighborAdvertisements *float64 | |||||
InRedirects *float64 | |||||
InMLDv2Reports *float64 | |||||
OutDestUnreachs *float64 | |||||
OutPktTooBigs *float64 | |||||
OutTimeExcds *float64 | |||||
OutParmProblems *float64 | |||||
OutEchos *float64 | |||||
OutEchoReplies *float64 | |||||
OutGroupMembQueries *float64 | |||||
OutGroupMembResponses *float64 | |||||
OutGroupMembReductions *float64 | |||||
OutRouterSolicits *float64 | |||||
OutRouterAdvertisements *float64 | |||||
OutNeighborSolicits *float64 | |||||
OutNeighborAdvertisements *float64 | |||||
OutRedirects *float64 | |||||
OutMLDv2Reports *float64 | |||||
InType1 *float64 | |||||
InType134 *float64 | |||||
InType135 *float64 | |||||
InType136 *float64 | |||||
InType143 *float64 | |||||
OutType133 *float64 | |||||
OutType135 *float64 | |||||
OutType136 *float64 | |||||
OutType143 *float64 | |||||
} | } | ||||
type Udp6 struct { // nolint:revive | type Udp6 struct { // nolint:revive | ||||
InDatagrams float64 | |||||
NoPorts float64 | |||||
InErrors float64 | |||||
OutDatagrams float64 | |||||
RcvbufErrors float64 | |||||
SndbufErrors float64 | |||||
InCsumErrors float64 | |||||
IgnoredMulti float64 | |||||
InDatagrams *float64 | |||||
NoPorts *float64 | |||||
InErrors *float64 | |||||
OutDatagrams *float64 | |||||
RcvbufErrors *float64 | |||||
SndbufErrors *float64 | |||||
InCsumErrors *float64 | |||||
IgnoredMulti *float64 | |||||
} | } | ||||
type UdpLite6 struct { // nolint:revive | type UdpLite6 struct { // nolint:revive | ||||
InDatagrams float64 | |||||
NoPorts float64 | |||||
InErrors float64 | |||||
OutDatagrams float64 | |||||
RcvbufErrors float64 | |||||
SndbufErrors float64 | |||||
InCsumErrors float64 | |||||
InDatagrams *float64 | |||||
NoPorts *float64 | |||||
InErrors *float64 | |||||
OutDatagrams *float64 | |||||
RcvbufErrors *float64 | |||||
SndbufErrors *float64 | |||||
InCsumErrors *float64 | |||||
} | } | ||||
func (p Proc) Snmp6() (ProcSnmp6, error) { | func (p Proc) Snmp6() (ProcSnmp6, error) { | ||||
@@ -182,197 +182,197 @@ func parseSNMP6Stats(r io.Reader) (ProcSnmp6, error) { | |||||
case "Ip6": | case "Ip6": | ||||
switch key { | switch key { | ||||
case "InReceives": | case "InReceives": | ||||
procSnmp6.Ip6.InReceives = value | |||||
procSnmp6.Ip6.InReceives = &value | |||||
case "InHdrErrors": | case "InHdrErrors": | ||||
procSnmp6.Ip6.InHdrErrors = value | |||||
procSnmp6.Ip6.InHdrErrors = &value | |||||
case "InTooBigErrors": | case "InTooBigErrors": | ||||
procSnmp6.Ip6.InTooBigErrors = value | |||||
procSnmp6.Ip6.InTooBigErrors = &value | |||||
case "InNoRoutes": | case "InNoRoutes": | ||||
procSnmp6.Ip6.InNoRoutes = value | |||||
procSnmp6.Ip6.InNoRoutes = &value | |||||
case "InAddrErrors": | case "InAddrErrors": | ||||
procSnmp6.Ip6.InAddrErrors = value | |||||
procSnmp6.Ip6.InAddrErrors = &value | |||||
case "InUnknownProtos": | case "InUnknownProtos": | ||||
procSnmp6.Ip6.InUnknownProtos = value | |||||
procSnmp6.Ip6.InUnknownProtos = &value | |||||
case "InTruncatedPkts": | case "InTruncatedPkts": | ||||
procSnmp6.Ip6.InTruncatedPkts = value | |||||
procSnmp6.Ip6.InTruncatedPkts = &value | |||||
case "InDiscards": | case "InDiscards": | ||||
procSnmp6.Ip6.InDiscards = value | |||||
procSnmp6.Ip6.InDiscards = &value | |||||
case "InDelivers": | case "InDelivers": | ||||
procSnmp6.Ip6.InDelivers = value | |||||
procSnmp6.Ip6.InDelivers = &value | |||||
case "OutForwDatagrams": | case "OutForwDatagrams": | ||||
procSnmp6.Ip6.OutForwDatagrams = value | |||||
procSnmp6.Ip6.OutForwDatagrams = &value | |||||
case "OutRequests": | case "OutRequests": | ||||
procSnmp6.Ip6.OutRequests = value | |||||
procSnmp6.Ip6.OutRequests = &value | |||||
case "OutDiscards": | case "OutDiscards": | ||||
procSnmp6.Ip6.OutDiscards = value | |||||
procSnmp6.Ip6.OutDiscards = &value | |||||
case "OutNoRoutes": | case "OutNoRoutes": | ||||
procSnmp6.Ip6.OutNoRoutes = value | |||||
procSnmp6.Ip6.OutNoRoutes = &value | |||||
case "ReasmTimeout": | case "ReasmTimeout": | ||||
procSnmp6.Ip6.ReasmTimeout = value | |||||
procSnmp6.Ip6.ReasmTimeout = &value | |||||
case "ReasmReqds": | case "ReasmReqds": | ||||
procSnmp6.Ip6.ReasmReqds = value | |||||
procSnmp6.Ip6.ReasmReqds = &value | |||||
case "ReasmOKs": | case "ReasmOKs": | ||||
procSnmp6.Ip6.ReasmOKs = value | |||||
procSnmp6.Ip6.ReasmOKs = &value | |||||
case "ReasmFails": | case "ReasmFails": | ||||
procSnmp6.Ip6.ReasmFails = value | |||||
procSnmp6.Ip6.ReasmFails = &value | |||||
case "FragOKs": | case "FragOKs": | ||||
procSnmp6.Ip6.FragOKs = value | |||||
procSnmp6.Ip6.FragOKs = &value | |||||
case "FragFails": | case "FragFails": | ||||
procSnmp6.Ip6.FragFails = value | |||||
procSnmp6.Ip6.FragFails = &value | |||||
case "FragCreates": | case "FragCreates": | ||||
procSnmp6.Ip6.FragCreates = value | |||||
procSnmp6.Ip6.FragCreates = &value | |||||
case "InMcastPkts": | case "InMcastPkts": | ||||
procSnmp6.Ip6.InMcastPkts = value | |||||
procSnmp6.Ip6.InMcastPkts = &value | |||||
case "OutMcastPkts": | case "OutMcastPkts": | ||||
procSnmp6.Ip6.OutMcastPkts = value | |||||
procSnmp6.Ip6.OutMcastPkts = &value | |||||
case "InOctets": | case "InOctets": | ||||
procSnmp6.Ip6.InOctets = value | |||||
procSnmp6.Ip6.InOctets = &value | |||||
case "OutOctets": | case "OutOctets": | ||||
procSnmp6.Ip6.OutOctets = value | |||||
procSnmp6.Ip6.OutOctets = &value | |||||
case "InMcastOctets": | case "InMcastOctets": | ||||
procSnmp6.Ip6.InMcastOctets = value | |||||
procSnmp6.Ip6.InMcastOctets = &value | |||||
case "OutMcastOctets": | case "OutMcastOctets": | ||||
procSnmp6.Ip6.OutMcastOctets = value | |||||
procSnmp6.Ip6.OutMcastOctets = &value | |||||
case "InBcastOctets": | case "InBcastOctets": | ||||
procSnmp6.Ip6.InBcastOctets = value | |||||
procSnmp6.Ip6.InBcastOctets = &value | |||||
case "OutBcastOctets": | case "OutBcastOctets": | ||||
procSnmp6.Ip6.OutBcastOctets = value | |||||
procSnmp6.Ip6.OutBcastOctets = &value | |||||
case "InNoECTPkts": | case "InNoECTPkts": | ||||
procSnmp6.Ip6.InNoECTPkts = value | |||||
procSnmp6.Ip6.InNoECTPkts = &value | |||||
case "InECT1Pkts": | case "InECT1Pkts": | ||||
procSnmp6.Ip6.InECT1Pkts = value | |||||
procSnmp6.Ip6.InECT1Pkts = &value | |||||
case "InECT0Pkts": | case "InECT0Pkts": | ||||
procSnmp6.Ip6.InECT0Pkts = value | |||||
procSnmp6.Ip6.InECT0Pkts = &value | |||||
case "InCEPkts": | case "InCEPkts": | ||||
procSnmp6.Ip6.InCEPkts = value | |||||
procSnmp6.Ip6.InCEPkts = &value | |||||
} | } | ||||
case "Icmp6": | case "Icmp6": | ||||
switch key { | switch key { | ||||
case "InMsgs": | case "InMsgs": | ||||
procSnmp6.Icmp6.InMsgs = value | |||||
procSnmp6.Icmp6.InMsgs = &value | |||||
case "InErrors": | case "InErrors": | ||||
procSnmp6.Icmp6.InErrors = value | |||||
procSnmp6.Icmp6.InErrors = &value | |||||
case "OutMsgs": | case "OutMsgs": | ||||
procSnmp6.Icmp6.OutMsgs = value | |||||
procSnmp6.Icmp6.OutMsgs = &value | |||||
case "OutErrors": | case "OutErrors": | ||||
procSnmp6.Icmp6.OutErrors = value | |||||
procSnmp6.Icmp6.OutErrors = &value | |||||
case "InCsumErrors": | case "InCsumErrors": | ||||
procSnmp6.Icmp6.InCsumErrors = value | |||||
procSnmp6.Icmp6.InCsumErrors = &value | |||||
case "InDestUnreachs": | case "InDestUnreachs": | ||||
procSnmp6.Icmp6.InDestUnreachs = value | |||||
procSnmp6.Icmp6.InDestUnreachs = &value | |||||
case "InPktTooBigs": | case "InPktTooBigs": | ||||
procSnmp6.Icmp6.InPktTooBigs = value | |||||
procSnmp6.Icmp6.InPktTooBigs = &value | |||||
case "InTimeExcds": | case "InTimeExcds": | ||||
procSnmp6.Icmp6.InTimeExcds = value | |||||
procSnmp6.Icmp6.InTimeExcds = &value | |||||
case "InParmProblems": | case "InParmProblems": | ||||
procSnmp6.Icmp6.InParmProblems = value | |||||
procSnmp6.Icmp6.InParmProblems = &value | |||||
case "InEchos": | case "InEchos": | ||||
procSnmp6.Icmp6.InEchos = value | |||||
procSnmp6.Icmp6.InEchos = &value | |||||
case "InEchoReplies": | case "InEchoReplies": | ||||
procSnmp6.Icmp6.InEchoReplies = value | |||||
procSnmp6.Icmp6.InEchoReplies = &value | |||||
case "InGroupMembQueries": | case "InGroupMembQueries": | ||||
procSnmp6.Icmp6.InGroupMembQueries = value | |||||
procSnmp6.Icmp6.InGroupMembQueries = &value | |||||
case "InGroupMembResponses": | case "InGroupMembResponses": | ||||
procSnmp6.Icmp6.InGroupMembResponses = value | |||||
procSnmp6.Icmp6.InGroupMembResponses = &value | |||||
case "InGroupMembReductions": | case "InGroupMembReductions": | ||||
procSnmp6.Icmp6.InGroupMembReductions = value | |||||
procSnmp6.Icmp6.InGroupMembReductions = &value | |||||
case "InRouterSolicits": | case "InRouterSolicits": | ||||
procSnmp6.Icmp6.InRouterSolicits = value | |||||
procSnmp6.Icmp6.InRouterSolicits = &value | |||||
case "InRouterAdvertisements": | case "InRouterAdvertisements": | ||||
procSnmp6.Icmp6.InRouterAdvertisements = value | |||||
procSnmp6.Icmp6.InRouterAdvertisements = &value | |||||
case "InNeighborSolicits": | case "InNeighborSolicits": | ||||
procSnmp6.Icmp6.InNeighborSolicits = value | |||||
procSnmp6.Icmp6.InNeighborSolicits = &value | |||||
case "InNeighborAdvertisements": | case "InNeighborAdvertisements": | ||||
procSnmp6.Icmp6.InNeighborAdvertisements = value | |||||
procSnmp6.Icmp6.InNeighborAdvertisements = &value | |||||
case "InRedirects": | case "InRedirects": | ||||
procSnmp6.Icmp6.InRedirects = value | |||||
procSnmp6.Icmp6.InRedirects = &value | |||||
case "InMLDv2Reports": | case "InMLDv2Reports": | ||||
procSnmp6.Icmp6.InMLDv2Reports = value | |||||
procSnmp6.Icmp6.InMLDv2Reports = &value | |||||
case "OutDestUnreachs": | case "OutDestUnreachs": | ||||
procSnmp6.Icmp6.OutDestUnreachs = value | |||||
procSnmp6.Icmp6.OutDestUnreachs = &value | |||||
case "OutPktTooBigs": | case "OutPktTooBigs": | ||||
procSnmp6.Icmp6.OutPktTooBigs = value | |||||
procSnmp6.Icmp6.OutPktTooBigs = &value | |||||
case "OutTimeExcds": | case "OutTimeExcds": | ||||
procSnmp6.Icmp6.OutTimeExcds = value | |||||
procSnmp6.Icmp6.OutTimeExcds = &value | |||||
case "OutParmProblems": | case "OutParmProblems": | ||||
procSnmp6.Icmp6.OutParmProblems = value | |||||
procSnmp6.Icmp6.OutParmProblems = &value | |||||
case "OutEchos": | case "OutEchos": | ||||
procSnmp6.Icmp6.OutEchos = value | |||||
procSnmp6.Icmp6.OutEchos = &value | |||||
case "OutEchoReplies": | case "OutEchoReplies": | ||||
procSnmp6.Icmp6.OutEchoReplies = value | |||||
procSnmp6.Icmp6.OutEchoReplies = &value | |||||
case "OutGroupMembQueries": | case "OutGroupMembQueries": | ||||
procSnmp6.Icmp6.OutGroupMembQueries = value | |||||
procSnmp6.Icmp6.OutGroupMembQueries = &value | |||||
case "OutGroupMembResponses": | case "OutGroupMembResponses": | ||||
procSnmp6.Icmp6.OutGroupMembResponses = value | |||||
procSnmp6.Icmp6.OutGroupMembResponses = &value | |||||
case "OutGroupMembReductions": | case "OutGroupMembReductions": | ||||
procSnmp6.Icmp6.OutGroupMembReductions = value | |||||
procSnmp6.Icmp6.OutGroupMembReductions = &value | |||||
case "OutRouterSolicits": | case "OutRouterSolicits": | ||||
procSnmp6.Icmp6.OutRouterSolicits = value | |||||
procSnmp6.Icmp6.OutRouterSolicits = &value | |||||
case "OutRouterAdvertisements": | case "OutRouterAdvertisements": | ||||
procSnmp6.Icmp6.OutRouterAdvertisements = value | |||||
procSnmp6.Icmp6.OutRouterAdvertisements = &value | |||||
case "OutNeighborSolicits": | case "OutNeighborSolicits": | ||||
procSnmp6.Icmp6.OutNeighborSolicits = value | |||||
procSnmp6.Icmp6.OutNeighborSolicits = &value | |||||
case "OutNeighborAdvertisements": | case "OutNeighborAdvertisements": | ||||
procSnmp6.Icmp6.OutNeighborAdvertisements = value | |||||
procSnmp6.Icmp6.OutNeighborAdvertisements = &value | |||||
case "OutRedirects": | case "OutRedirects": | ||||
procSnmp6.Icmp6.OutRedirects = value | |||||
procSnmp6.Icmp6.OutRedirects = &value | |||||
case "OutMLDv2Reports": | case "OutMLDv2Reports": | ||||
procSnmp6.Icmp6.OutMLDv2Reports = value | |||||
procSnmp6.Icmp6.OutMLDv2Reports = &value | |||||
case "InType1": | case "InType1": | ||||
procSnmp6.Icmp6.InType1 = value | |||||
procSnmp6.Icmp6.InType1 = &value | |||||
case "InType134": | case "InType134": | ||||
procSnmp6.Icmp6.InType134 = value | |||||
procSnmp6.Icmp6.InType134 = &value | |||||
case "InType135": | case "InType135": | ||||
procSnmp6.Icmp6.InType135 = value | |||||
procSnmp6.Icmp6.InType135 = &value | |||||
case "InType136": | case "InType136": | ||||
procSnmp6.Icmp6.InType136 = value | |||||
procSnmp6.Icmp6.InType136 = &value | |||||
case "InType143": | case "InType143": | ||||
procSnmp6.Icmp6.InType143 = value | |||||
procSnmp6.Icmp6.InType143 = &value | |||||
case "OutType133": | case "OutType133": | ||||
procSnmp6.Icmp6.OutType133 = value | |||||
procSnmp6.Icmp6.OutType133 = &value | |||||
case "OutType135": | case "OutType135": | ||||
procSnmp6.Icmp6.OutType135 = value | |||||
procSnmp6.Icmp6.OutType135 = &value | |||||
case "OutType136": | case "OutType136": | ||||
procSnmp6.Icmp6.OutType136 = value | |||||
procSnmp6.Icmp6.OutType136 = &value | |||||
case "OutType143": | case "OutType143": | ||||
procSnmp6.Icmp6.OutType143 = value | |||||
procSnmp6.Icmp6.OutType143 = &value | |||||
} | } | ||||
case "Udp6": | case "Udp6": | ||||
switch key { | switch key { | ||||
case "InDatagrams": | case "InDatagrams": | ||||
procSnmp6.Udp6.InDatagrams = value | |||||
procSnmp6.Udp6.InDatagrams = &value | |||||
case "NoPorts": | case "NoPorts": | ||||
procSnmp6.Udp6.NoPorts = value | |||||
procSnmp6.Udp6.NoPorts = &value | |||||
case "InErrors": | case "InErrors": | ||||
procSnmp6.Udp6.InErrors = value | |||||
procSnmp6.Udp6.InErrors = &value | |||||
case "OutDatagrams": | case "OutDatagrams": | ||||
procSnmp6.Udp6.OutDatagrams = value | |||||
procSnmp6.Udp6.OutDatagrams = &value | |||||
case "RcvbufErrors": | case "RcvbufErrors": | ||||
procSnmp6.Udp6.RcvbufErrors = value | |||||
procSnmp6.Udp6.RcvbufErrors = &value | |||||
case "SndbufErrors": | case "SndbufErrors": | ||||
procSnmp6.Udp6.SndbufErrors = value | |||||
procSnmp6.Udp6.SndbufErrors = &value | |||||
case "InCsumErrors": | case "InCsumErrors": | ||||
procSnmp6.Udp6.InCsumErrors = value | |||||
procSnmp6.Udp6.InCsumErrors = &value | |||||
case "IgnoredMulti": | case "IgnoredMulti": | ||||
procSnmp6.Udp6.IgnoredMulti = value | |||||
procSnmp6.Udp6.IgnoredMulti = &value | |||||
} | } | ||||
case "UdpLite6": | case "UdpLite6": | ||||
switch key { | switch key { | ||||
case "InDatagrams": | case "InDatagrams": | ||||
procSnmp6.UdpLite6.InDatagrams = value | |||||
procSnmp6.UdpLite6.InDatagrams = &value | |||||
case "NoPorts": | case "NoPorts": | ||||
procSnmp6.UdpLite6.NoPorts = value | |||||
procSnmp6.UdpLite6.NoPorts = &value | |||||
case "InErrors": | case "InErrors": | ||||
procSnmp6.UdpLite6.InErrors = value | |||||
procSnmp6.UdpLite6.InErrors = &value | |||||
case "OutDatagrams": | case "OutDatagrams": | ||||
procSnmp6.UdpLite6.OutDatagrams = value | |||||
procSnmp6.UdpLite6.OutDatagrams = &value | |||||
case "RcvbufErrors": | case "RcvbufErrors": | ||||
procSnmp6.UdpLite6.RcvbufErrors = value | |||||
procSnmp6.UdpLite6.RcvbufErrors = &value | |||||
case "SndbufErrors": | case "SndbufErrors": | ||||
procSnmp6.UdpLite6.SndbufErrors = value | |||||
procSnmp6.UdpLite6.SndbufErrors = &value | |||||
case "InCsumErrors": | case "InCsumErrors": | ||||
procSnmp6.UdpLite6.InCsumErrors = value | |||||
procSnmp6.UdpLite6.InCsumErrors = &value | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -102,6 +102,8 @@ type ProcStat struct { | |||||
RSS int | RSS int | ||||
// Soft limit in bytes on the rss of the process. | // Soft limit in bytes on the rss of the process. | ||||
RSSLimit uint64 | RSSLimit uint64 | ||||
// CPU number last executed on. | |||||
Processor uint | |||||
// Real-time scheduling priority, a number in the range 1 to 99 for processes | // Real-time scheduling priority, a number in the range 1 to 99 for processes | ||||
// scheduled under a real-time policy, or 0, for non-real-time processes. | // scheduled under a real-time policy, or 0, for non-real-time processes. | ||||
RTPriority uint | RTPriority uint | ||||
@@ -184,7 +186,7 @@ func (p Proc) Stat() (ProcStat, error) { | |||||
&ignoreUint64, | &ignoreUint64, | ||||
&ignoreUint64, | &ignoreUint64, | ||||
&ignoreInt64, | &ignoreInt64, | ||||
&ignoreInt64, | |||||
&s.Processor, | |||||
&s.RTPriority, | &s.RTPriority, | ||||
&s.Policy, | &s.Policy, | ||||
&s.DelayAcctBlkIOTicks, | &s.DelayAcctBlkIOTicks, | ||||
@@ -96,10 +96,10 @@ func (p Proc) NewStatus() (ProcStatus, error) { | |||||
kv := strings.SplitN(line, ":", 2) | kv := strings.SplitN(line, ":", 2) | ||||
// removes spaces | // removes spaces | ||||
k := string(strings.TrimSpace(kv[0])) | |||||
v := string(strings.TrimSpace(kv[1])) | |||||
k := strings.TrimSpace(kv[0]) | |||||
v := strings.TrimSpace(kv[1]) | |||||
// removes "kB" | // removes "kB" | ||||
v = string(bytes.Trim([]byte(v), " kB")) | |||||
v = strings.TrimSuffix(v, " kB") | |||||
// value to int when possible | // value to int when possible | ||||
// we can skip error check here, 'cause vKBytes is not used when value is a string | // we can skip error check here, 'cause vKBytes is not used when value is a string | ||||
@@ -62,7 +62,7 @@ type Stat struct { | |||||
// Summed up cpu statistics. | // Summed up cpu statistics. | ||||
CPUTotal CPUStat | CPUTotal CPUStat | ||||
// Per-CPU statistics. | // Per-CPU statistics. | ||||
CPU []CPUStat | |||||
CPU map[int64]CPUStat | |||||
// Number of times interrupts were handled, which contains numbered and unnumbered IRQs. | // Number of times interrupts were handled, which contains numbered and unnumbered IRQs. | ||||
IRQTotal uint64 | IRQTotal uint64 | ||||
// Number of times a numbered IRQ was triggered. | // Number of times a numbered IRQ was triggered. | ||||
@@ -170,10 +170,23 @@ func (fs FS) Stat() (Stat, error) { | |||||
if err != nil { | if err != nil { | ||||
return Stat{}, err | return Stat{}, err | ||||
} | } | ||||
procStat, err := parseStat(bytes.NewReader(data), fileName) | |||||
if err != nil { | |||||
return Stat{}, err | |||||
} | |||||
return procStat, nil | |||||
} | |||||
stat := Stat{} | |||||
// parseStat parses the metrics from /proc/[pid]/stat. | |||||
func parseStat(r io.Reader, fileName string) (Stat, error) { | |||||
var ( | |||||
scanner = bufio.NewScanner(r) | |||||
stat = Stat{ | |||||
CPU: make(map[int64]CPUStat), | |||||
} | |||||
err error | |||||
) | |||||
scanner := bufio.NewScanner(bytes.NewReader(data)) | |||||
for scanner.Scan() { | for scanner.Scan() { | ||||
line := scanner.Text() | line := scanner.Text() | ||||
parts := strings.Fields(scanner.Text()) | parts := strings.Fields(scanner.Text()) | ||||
@@ -228,9 +241,6 @@ func (fs FS) Stat() (Stat, error) { | |||||
if cpuID == -1 { | if cpuID == -1 { | ||||
stat.CPUTotal = cpuStat | stat.CPUTotal = cpuStat | ||||
} else { | } else { | ||||
for int64(len(stat.CPU)) <= cpuID { | |||||
stat.CPU = append(stat.CPU, CPUStat{}) | |||||
} | |||||
stat.CPU[cpuID] = cpuStat | stat.CPU[cpuID] = cpuStat | ||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,79 @@ | |||||
// Copyright 2022 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 ( | |||||
"fmt" | |||||
"os" | |||||
"strconv" | |||||
fsi "github.com/prometheus/procfs/internal/fs" | |||||
) | |||||
// Provide access to /proc/PID/task/TID files, for thread specific values. Since | |||||
// such files have the same structure as /proc/PID/ ones, the data structures | |||||
// and the parsers for the latter may be reused. | |||||
// AllThreads returns a list of all currently available threads under /proc/PID. | |||||
func AllThreads(pid int) (Procs, error) { | |||||
fs, err := NewFS(DefaultMountPoint) | |||||
if err != nil { | |||||
return Procs{}, err | |||||
} | |||||
return fs.AllThreads(pid) | |||||
} | |||||
// AllThreads returns a list of all currently available threads for PID. | |||||
func (fs FS) AllThreads(pid int) (Procs, error) { | |||||
taskPath := fs.proc.Path(strconv.Itoa(pid), "task") | |||||
d, err := os.Open(taskPath) | |||||
if err != nil { | |||||
return Procs{}, err | |||||
} | |||||
defer d.Close() | |||||
names, err := d.Readdirnames(-1) | |||||
if err != nil { | |||||
return Procs{}, fmt.Errorf("could not read %q: %w", d.Name(), err) | |||||
} | |||||
t := Procs{} | |||||
for _, n := range names { | |||||
tid, err := strconv.ParseInt(n, 10, 64) | |||||
if err != nil { | |||||
continue | |||||
} | |||||
t = append(t, Proc{PID: int(tid), fs: fsi.FS(taskPath)}) | |||||
} | |||||
return t, nil | |||||
} | |||||
// Thread returns a process for a given PID, TID. | |||||
func (fs FS) Thread(pid, tid int) (Proc, error) { | |||||
taskPath := fs.proc.Path(strconv.Itoa(pid), "task") | |||||
if _, err := os.Stat(taskPath); err != nil { | |||||
return Proc{}, err | |||||
} | |||||
return Proc{PID: tid, fs: fsi.FS(taskPath)}, nil | |||||
} | |||||
// Thread returns a process for a given TID of Proc. | |||||
func (proc Proc) Thread(tid int) (Proc, error) { | |||||
tfs := fsi.FS(proc.path("task")) | |||||
if _, err := os.Stat(tfs.Path(strconv.Itoa(tid))); err != nil { | |||||
return Proc{}, err | |||||
} | |||||
return Proc{PID: tid, fs: tfs}, nil | |||||
} |
@@ -26,7 +26,9 @@ import ( | |||||
) | ) | ||||
// The VM interface is described at | // The VM interface is described at | ||||
// https://www.kernel.org/doc/Documentation/sysctl/vm.txt | |||||
// | |||||
// https://www.kernel.org/doc/Documentation/sysctl/vm.txt | |||||
// | |||||
// Each setting is exposed as a single file. | // Each setting is exposed as a single file. | ||||
// Each file contains one line with a single numerical value, except lowmem_reserve_ratio which holds an array | // Each file contains one line with a single numerical value, except lowmem_reserve_ratio which holds an array | ||||
// and numa_zonelist_order (deprecated) which is a string. | // and numa_zonelist_order (deprecated) which is a string. | ||||
@@ -2,8 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris | |||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | |||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos | |||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos | |||||
package unix | package unix | ||||
@@ -2,8 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build gccgo && !aix | |||||
// +build gccgo,!aix | |||||
//go:build gccgo && !aix && !hurd | |||||
// +build gccgo,!aix,!hurd | |||||
package unix | package unix | ||||
@@ -2,8 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build gccgo | |||||
// +build !aix | |||||
//go:build gccgo && !aix && !hurd | |||||
// +build gccgo,!aix,!hurd | |||||
#include <errno.h> | #include <errno.h> | ||||
#include <stdint.h> | #include <stdint.h> | ||||
@@ -0,0 +1,70 @@ | |||||
// Copyright 2018 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
//go:build aix || solaris | |||||
// +build aix solaris | |||||
package unix | |||||
import ( | |||||
"unsafe" | |||||
) | |||||
// ioctl itself should not be exposed directly, but additional get/set | |||||
// functions for specific types are permissible. | |||||
// IoctlSetInt performs an ioctl operation which sets an integer value | |||||
// on fd, using the specified request number. | |||||
func IoctlSetInt(fd int, req int, value int) error { | |||||
return ioctl(fd, req, uintptr(value)) | |||||
} | |||||
// IoctlSetPointerInt performs an ioctl operation which sets an | |||||
// integer value on fd, using the specified request number. The ioctl | |||||
// argument is called with a pointer to the integer value, rather than | |||||
// passing the integer value directly. | |||||
func IoctlSetPointerInt(fd int, req int, value int) error { | |||||
v := int32(value) | |||||
return ioctlPtr(fd, req, unsafe.Pointer(&v)) | |||||
} | |||||
// IoctlSetWinsize performs an ioctl on fd with a *Winsize argument. | |||||
// | |||||
// To change fd's window size, the req argument should be TIOCSWINSZ. | |||||
func IoctlSetWinsize(fd int, req int, value *Winsize) error { | |||||
// TODO: if we get the chance, remove the req parameter and | |||||
// hardcode TIOCSWINSZ. | |||||
return ioctlPtr(fd, req, unsafe.Pointer(value)) | |||||
} | |||||
// IoctlSetTermios performs an ioctl on fd with a *Termios. | |||||
// | |||||
// The req value will usually be TCSETA or TIOCSETA. | |||||
func IoctlSetTermios(fd int, req int, value *Termios) error { | |||||
// TODO: if we get the chance, remove the req parameter. | |||||
return ioctlPtr(fd, req, unsafe.Pointer(value)) | |||||
} | |||||
// IoctlGetInt performs an ioctl operation which gets an integer value | |||||
// from fd, using the specified request number. | |||||
// | |||||
// A few ioctl requests use the return value as an output parameter; | |||||
// for those, IoctlRetInt should be used instead of this function. | |||||
func IoctlGetInt(fd int, req int) (int, error) { | |||||
var value int | |||||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||||
return value, err | |||||
} | |||||
func IoctlGetWinsize(fd int, req int) (*Winsize, error) { | |||||
var value Winsize | |||||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||||
return &value, err | |||||
} | |||||
func IoctlGetTermios(fd int, req int) (*Termios, error) { | |||||
var value Termios | |||||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||||
return &value, err | |||||
} |
@@ -2,13 +2,12 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris | |||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | |||||
//go:build darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd | |||||
// +build darwin dragonfly freebsd hurd linux netbsd openbsd | |||||
package unix | package unix | ||||
import ( | import ( | ||||
"runtime" | |||||
"unsafe" | "unsafe" | ||||
) | ) | ||||
@@ -27,7 +26,7 @@ func IoctlSetInt(fd int, req uint, value int) error { | |||||
// passing the integer value directly. | // passing the integer value directly. | ||||
func IoctlSetPointerInt(fd int, req uint, value int) error { | func IoctlSetPointerInt(fd int, req uint, value int) error { | ||||
v := int32(value) | v := int32(value) | ||||
return ioctl(fd, req, uintptr(unsafe.Pointer(&v))) | |||||
return ioctlPtr(fd, req, unsafe.Pointer(&v)) | |||||
} | } | ||||
// IoctlSetWinsize performs an ioctl on fd with a *Winsize argument. | // IoctlSetWinsize performs an ioctl on fd with a *Winsize argument. | ||||
@@ -36,9 +35,7 @@ func IoctlSetPointerInt(fd int, req uint, value int) error { | |||||
func IoctlSetWinsize(fd int, req uint, value *Winsize) error { | func IoctlSetWinsize(fd int, req uint, value *Winsize) error { | ||||
// TODO: if we get the chance, remove the req parameter and | // TODO: if we get the chance, remove the req parameter and | ||||
// hardcode TIOCSWINSZ. | // hardcode TIOCSWINSZ. | ||||
err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) | |||||
runtime.KeepAlive(value) | |||||
return err | |||||
return ioctlPtr(fd, req, unsafe.Pointer(value)) | |||||
} | } | ||||
// IoctlSetTermios performs an ioctl on fd with a *Termios. | // IoctlSetTermios performs an ioctl on fd with a *Termios. | ||||
@@ -46,9 +43,7 @@ func IoctlSetWinsize(fd int, req uint, value *Winsize) error { | |||||
// The req value will usually be TCSETA or TIOCSETA. | // The req value will usually be TCSETA or TIOCSETA. | ||||
func IoctlSetTermios(fd int, req uint, value *Termios) error { | func IoctlSetTermios(fd int, req uint, value *Termios) error { | ||||
// TODO: if we get the chance, remove the req parameter. | // TODO: if we get the chance, remove the req parameter. | ||||
err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) | |||||
runtime.KeepAlive(value) | |||||
return err | |||||
return ioctlPtr(fd, req, unsafe.Pointer(value)) | |||||
} | } | ||||
// IoctlGetInt performs an ioctl operation which gets an integer value | // IoctlGetInt performs an ioctl operation which gets an integer value | ||||
@@ -58,18 +53,18 @@ func IoctlSetTermios(fd int, req uint, value *Termios) error { | |||||
// for those, IoctlRetInt should be used instead of this function. | // for those, IoctlRetInt should be used instead of this function. | ||||
func IoctlGetInt(fd int, req uint) (int, error) { | func IoctlGetInt(fd int, req uint) (int, error) { | ||||
var value int | var value int | ||||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||||
return value, err | return value, err | ||||
} | } | ||||
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) { | func IoctlGetWinsize(fd int, req uint) (*Winsize, error) { | ||||
var value Winsize | var value Winsize | ||||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||||
return &value, err | return &value, err | ||||
} | } | ||||
func IoctlGetTermios(fd int, req uint) (*Termios, error) { | func IoctlGetTermios(fd int, req uint) (*Termios, error) { | ||||
var value Termios | var value Termios | ||||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||||
return &value, err | return &value, err | ||||
} | } |
@@ -17,25 +17,23 @@ import ( | |||||
// IoctlSetInt performs an ioctl operation which sets an integer value | // IoctlSetInt performs an ioctl operation which sets an integer value | ||||
// on fd, using the specified request number. | // on fd, using the specified request number. | ||||
func IoctlSetInt(fd int, req uint, value int) error { | |||||
func IoctlSetInt(fd int, req int, value int) error { | |||||
return ioctl(fd, req, uintptr(value)) | return ioctl(fd, req, uintptr(value)) | ||||
} | } | ||||
// IoctlSetWinsize performs an ioctl on fd with a *Winsize argument. | // IoctlSetWinsize performs an ioctl on fd with a *Winsize argument. | ||||
// | // | ||||
// To change fd's window size, the req argument should be TIOCSWINSZ. | // To change fd's window size, the req argument should be TIOCSWINSZ. | ||||
func IoctlSetWinsize(fd int, req uint, value *Winsize) error { | |||||
func IoctlSetWinsize(fd int, req int, value *Winsize) error { | |||||
// TODO: if we get the chance, remove the req parameter and | // TODO: if we get the chance, remove the req parameter and | ||||
// hardcode TIOCSWINSZ. | // hardcode TIOCSWINSZ. | ||||
err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) | |||||
runtime.KeepAlive(value) | |||||
return err | |||||
return ioctlPtr(fd, req, unsafe.Pointer(value)) | |||||
} | } | ||||
// IoctlSetTermios performs an ioctl on fd with a *Termios. | // IoctlSetTermios performs an ioctl on fd with a *Termios. | ||||
// | // | ||||
// The req value is expected to be TCSETS, TCSETSW, or TCSETSF | // The req value is expected to be TCSETS, TCSETSW, or TCSETSF | ||||
func IoctlSetTermios(fd int, req uint, value *Termios) error { | |||||
func IoctlSetTermios(fd int, req int, value *Termios) error { | |||||
if (req != TCSETS) && (req != TCSETSW) && (req != TCSETSF) { | if (req != TCSETS) && (req != TCSETSW) && (req != TCSETSF) { | ||||
return ENOSYS | return ENOSYS | ||||
} | } | ||||
@@ -49,22 +47,22 @@ func IoctlSetTermios(fd int, req uint, value *Termios) error { | |||||
// | // | ||||
// A few ioctl requests use the return value as an output parameter; | // A few ioctl requests use the return value as an output parameter; | ||||
// for those, IoctlRetInt should be used instead of this function. | // for those, IoctlRetInt should be used instead of this function. | ||||
func IoctlGetInt(fd int, req uint) (int, error) { | |||||
func IoctlGetInt(fd int, req int) (int, error) { | |||||
var value int | var value int | ||||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||||
return value, err | return value, err | ||||
} | } | ||||
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) { | |||||
func IoctlGetWinsize(fd int, req int) (*Winsize, error) { | |||||
var value Winsize | var value Winsize | ||||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||||
return &value, err | return &value, err | ||||
} | } | ||||
// IoctlGetTermios performs an ioctl on fd with a *Termios. | // IoctlGetTermios performs an ioctl on fd with a *Termios. | ||||
// | // | ||||
// The req value is expected to be TCGETS | // The req value is expected to be TCGETS | ||||
func IoctlGetTermios(fd int, req uint) (*Termios, error) { | |||||
func IoctlGetTermios(fd int, req int) (*Termios, error) { | |||||
var value Termios | var value Termios | ||||
if req != TCGETS { | if req != TCGETS { | ||||
return &value, ENOSYS | return &value, ENOSYS | ||||
@@ -174,10 +174,10 @@ openbsd_arm64) | |||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | ||||
;; | ;; | ||||
openbsd_mips64) | openbsd_mips64) | ||||
mkasm="go run mkasm.go" | |||||
mkerrors="$mkerrors -m64" | mkerrors="$mkerrors -m64" | ||||
mksyscall="go run mksyscall.go -openbsd" | |||||
mksyscall="go run mksyscall.go -openbsd -libc" | |||||
mksysctl="go run mksysctl_openbsd.go" | mksysctl="go run mksysctl_openbsd.go" | ||||
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'" | |||||
# Let the type of C char be signed for making the bare syscall | # Let the type of C char be signed for making the bare syscall | ||||
# API consistent across platforms. | # API consistent across platforms. | ||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | ||||
@@ -66,6 +66,7 @@ includes_Darwin=' | |||||
#include <sys/ptrace.h> | #include <sys/ptrace.h> | ||||
#include <sys/select.h> | #include <sys/select.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/stat.h> | |||||
#include <sys/un.h> | #include <sys/un.h> | ||||
#include <sys/sockio.h> | #include <sys/sockio.h> | ||||
#include <sys/sys_domain.h> | #include <sys/sys_domain.h> | ||||
@@ -203,6 +204,7 @@ struct ltchars { | |||||
#include <sys/timerfd.h> | #include <sys/timerfd.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <sys/xattr.h> | #include <sys/xattr.h> | ||||
#include <netinet/udp.h> | |||||
#include <linux/audit.h> | #include <linux/audit.h> | ||||
#include <linux/bpf.h> | #include <linux/bpf.h> | ||||
#include <linux/can.h> | #include <linux/can.h> | ||||
@@ -517,10 +519,11 @@ ccflags="$@" | |||||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ || | $2 ~ /^LOCK_(SH|EX|NB|UN)$/ || | ||||
$2 ~ /^LO_(KEY|NAME)_SIZE$/ || | $2 ~ /^LO_(KEY|NAME)_SIZE$/ || | ||||
$2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || | $2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || | ||||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT)_/ || | |||||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ || | |||||
$2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ || | $2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ || | ||||
$2 ~ /^NFC_.*_(MAX)?SIZE$/ || | $2 ~ /^NFC_.*_(MAX)?SIZE$/ || | ||||
$2 ~ /^RAW_PAYLOAD_/ || | $2 ~ /^RAW_PAYLOAD_/ || | ||||
$2 ~ /^[US]F_/ || | |||||
$2 ~ /^TP_STATUS_/ || | $2 ~ /^TP_STATUS_/ || | ||||
$2 ~ /^FALLOC_/ || | $2 ~ /^FALLOC_/ || | ||||
$2 ~ /^ICMPV?6?_(FILTER|SEC)/ || | $2 ~ /^ICMPV?6?_(FILTER|SEC)/ || | ||||
@@ -7,6 +7,12 @@ | |||||
package unix | package unix | ||||
import "unsafe" | |||||
func ptrace(request int, pid int, addr uintptr, data uintptr) error { | func ptrace(request int, pid int, addr uintptr, data uintptr) error { | ||||
return ptrace1(request, pid, addr, data) | return ptrace1(request, pid, addr, data) | ||||
} | } | ||||
func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) error { | |||||
return ptrace1Ptr(request, pid, addr, data) | |||||
} |
@@ -7,6 +7,12 @@ | |||||
package unix | package unix | ||||
import "unsafe" | |||||
func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { | func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { | ||||
return ENOTSUP | return ENOTSUP | ||||
} | } | ||||
func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) (err error) { | |||||
return ENOTSUP | |||||
} |
@@ -52,6 +52,20 @@ func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) { | |||||
return msgs, nil | return msgs, nil | ||||
} | } | ||||
// ParseOneSocketControlMessage parses a single socket control message from b, returning the message header, | |||||
// message data (a slice of b), and the remainder of b after that single message. | |||||
// When there are no remaining messages, len(remainder) == 0. | |||||
func ParseOneSocketControlMessage(b []byte) (hdr Cmsghdr, data []byte, remainder []byte, err error) { | |||||
h, dbuf, err := socketControlMessageHeaderAndData(b) | |||||
if err != nil { | |||||
return Cmsghdr{}, nil, nil, err | |||||
} | |||||
if i := cmsgAlignOf(int(h.Len)); i < len(b) { | |||||
remainder = b[i:] | |||||
} | |||||
return *h, dbuf, remainder, nil | |||||
} | |||||
func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) { | func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) { | ||||
h := (*Cmsghdr)(unsafe.Pointer(&b[0])) | h := (*Cmsghdr)(unsafe.Pointer(&b[0])) | ||||
if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) { | if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) { | ||||
@@ -292,9 +292,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { | |||||
break | break | ||||
} | } | ||||
} | } | ||||
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] | |||||
sa.Name = string(bytes) | |||||
sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n)) | |||||
return sa, nil | return sa, nil | ||||
case AF_INET: | case AF_INET: | ||||
@@ -410,7 +408,8 @@ func (w WaitStatus) CoreDump() bool { return w&0x80 == 0x80 } | |||||
func (w WaitStatus) TrapCause() int { return -1 } | func (w WaitStatus) TrapCause() int { return -1 } | ||||
//sys ioctl(fd int, req uint, arg uintptr) (err error) | |||||
//sys ioctl(fd int, req int, arg uintptr) (err error) | |||||
//sys ioctlPtr(fd int, req int, arg unsafe.Pointer) (err error) = ioctl | |||||
// fcntl must never be called with cmd=F_DUP2FD because it doesn't work on AIX | // fcntl must never be called with cmd=F_DUP2FD because it doesn't work on AIX | ||||
// There is no way to create a custom fcntl and to keep //sys fcntl easily, | // There is no way to create a custom fcntl and to keep //sys fcntl easily, | ||||
@@ -8,7 +8,6 @@ | |||||
package unix | package unix | ||||
//sysnb Getrlimit(resource int, rlim *Rlimit) (err error) = getrlimit64 | //sysnb Getrlimit(resource int, rlim *Rlimit) (err error) = getrlimit64 | ||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error) = setrlimit64 | |||||
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = lseek64 | //sys Seek(fd int, offset int64, whence int) (off int64, err error) = lseek64 | ||||
//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) | //sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) | ||||
@@ -8,7 +8,6 @@ | |||||
package unix | package unix | ||||
//sysnb Getrlimit(resource int, rlim *Rlimit) (err error) | //sysnb Getrlimit(resource int, rlim *Rlimit) (err error) | ||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error) | |||||
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = lseek | //sys Seek(fd int, offset int64, whence int) (off int64, err error) = lseek | ||||
//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) = mmap64 | //sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) = mmap64 | ||||
@@ -245,8 +245,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { | |||||
break | break | ||||
} | } | ||||
} | } | ||||
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] | |||||
sa.Name = string(bytes) | |||||
sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n)) | |||||
return sa, nil | return sa, nil | ||||
case AF_INET: | case AF_INET: | ||||
@@ -14,7 +14,6 @@ package unix | |||||
import ( | import ( | ||||
"fmt" | "fmt" | ||||
"runtime" | |||||
"syscall" | "syscall" | ||||
"unsafe" | "unsafe" | ||||
) | ) | ||||
@@ -230,6 +229,7 @@ func direntNamlen(buf []byte) (uint64, bool) { | |||||
func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) } | func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) } | ||||
func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) } | func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) } | ||||
func PtraceDenyAttach() (err error) { return ptrace(PT_DENY_ATTACH, 0, 0, 0) } | |||||
//sysnb pipe(p *[2]int32) (err error) | //sysnb pipe(p *[2]int32) (err error) | ||||
@@ -375,11 +375,10 @@ func Flistxattr(fd int, dest []byte) (sz int, err error) { | |||||
func Kill(pid int, signum syscall.Signal) (err error) { return kill(pid, int(signum), 1) } | func Kill(pid int, signum syscall.Signal) (err error) { return kill(pid, int(signum), 1) } | ||||
//sys ioctl(fd int, req uint, arg uintptr) (err error) | //sys ioctl(fd int, req uint, arg uintptr) (err error) | ||||
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL | |||||
func IoctlCtlInfo(fd int, ctlInfo *CtlInfo) error { | func IoctlCtlInfo(fd int, ctlInfo *CtlInfo) error { | ||||
err := ioctl(fd, CTLIOCGINFO, uintptr(unsafe.Pointer(ctlInfo))) | |||||
runtime.KeepAlive(ctlInfo) | |||||
return err | |||||
return ioctlPtr(fd, CTLIOCGINFO, unsafe.Pointer(ctlInfo)) | |||||
} | } | ||||
// IfreqMTU is struct ifreq used to get or set a network device's MTU. | // IfreqMTU is struct ifreq used to get or set a network device's MTU. | ||||
@@ -393,16 +392,14 @@ type IfreqMTU struct { | |||||
func IoctlGetIfreqMTU(fd int, ifname string) (*IfreqMTU, error) { | func IoctlGetIfreqMTU(fd int, ifname string) (*IfreqMTU, error) { | ||||
var ifreq IfreqMTU | var ifreq IfreqMTU | ||||
copy(ifreq.Name[:], ifname) | copy(ifreq.Name[:], ifname) | ||||
err := ioctl(fd, SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq))) | |||||
err := ioctlPtr(fd, SIOCGIFMTU, unsafe.Pointer(&ifreq)) | |||||
return &ifreq, err | return &ifreq, err | ||||
} | } | ||||
// IoctlSetIfreqMTU performs the SIOCSIFMTU ioctl operation on fd to set the MTU | // IoctlSetIfreqMTU performs the SIOCSIFMTU ioctl operation on fd to set the MTU | ||||
// of the network device specified by ifreq.Name. | // of the network device specified by ifreq.Name. | ||||
func IoctlSetIfreqMTU(fd int, ifreq *IfreqMTU) error { | func IoctlSetIfreqMTU(fd int, ifreq *IfreqMTU) error { | ||||
err := ioctl(fd, SIOCSIFMTU, uintptr(unsafe.Pointer(ifreq))) | |||||
runtime.KeepAlive(ifreq) | |||||
return err | |||||
return ioctlPtr(fd, SIOCSIFMTU, unsafe.Pointer(ifreq)) | |||||
} | } | ||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS_SYSCTL | //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS_SYSCTL | ||||
@@ -616,6 +613,7 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) { | |||||
//sys Rmdir(path string) (err error) | //sys Rmdir(path string) (err error) | ||||
//sys Seek(fd int, offset int64, whence int) (newoffset int64, err error) = SYS_LSEEK | //sys Seek(fd int, offset int64, whence int) (newoffset int64, err error) = SYS_LSEEK | ||||
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) | //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) | ||||
//sys Setattrlist(path string, attrlist *Attrlist, attrBuf []byte, options int) (err error) | |||||
//sys Setegid(egid int) (err error) | //sys Setegid(egid int) (err error) | ||||
//sysnb Seteuid(euid int) (err error) | //sysnb Seteuid(euid int) (err error) | ||||
//sysnb Setgid(gid int) (err error) | //sysnb Setgid(gid int) (err error) | ||||
@@ -625,7 +623,6 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) { | |||||
//sys Setprivexec(flag int) (err error) | //sys Setprivexec(flag int) (err error) | ||||
//sysnb Setregid(rgid int, egid int) (err error) | //sysnb Setregid(rgid int, egid int) (err error) | ||||
//sysnb Setreuid(ruid int, euid int) (err error) | //sysnb Setreuid(ruid int, euid int) (err error) | ||||
//sysnb Setrlimit(which int, lim *Rlimit) (err error) | |||||
//sysnb Setsid() (pid int, err error) | //sysnb Setsid() (pid int, err error) | ||||
//sysnb Settimeofday(tp *Timeval) (err error) | //sysnb Settimeofday(tp *Timeval) (err error) | ||||
//sysnb Setuid(uid int) (err error) | //sysnb Setuid(uid int) (err error) | ||||
@@ -679,7 +676,6 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) { | |||||
// Kqueue_from_portset_np | // Kqueue_from_portset_np | ||||
// Kqueue_portset | // Kqueue_portset | ||||
// Getattrlist | // Getattrlist | ||||
// Setattrlist | |||||
// Getdirentriesattr | // Getdirentriesattr | ||||
// Searchfs | // Searchfs | ||||
// Delete | // Delete | ||||
@@ -47,5 +47,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, | |||||
//sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT64 | //sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT64 | ||||
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64 | //sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64 | ||||
//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace | //sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace | ||||
//sys ptrace1Ptr(request int, pid int, addr unsafe.Pointer, data uintptr) (err error) = SYS_ptrace | |||||
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64 | //sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64 | ||||
//sys Statfs(path string, stat *Statfs_t) (err error) = SYS_STATFS64 | //sys Statfs(path string, stat *Statfs_t) (err error) = SYS_STATFS64 |
@@ -47,5 +47,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, | |||||
//sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT | //sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT | ||||
//sys Lstat(path string, stat *Stat_t) (err error) | //sys Lstat(path string, stat *Stat_t) (err error) | ||||
//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace | //sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace | ||||
//sys ptrace1Ptr(request int, pid int, addr unsafe.Pointer, data uintptr) (err error) = SYS_ptrace | |||||
//sys Stat(path string, stat *Stat_t) (err error) | //sys Stat(path string, stat *Stat_t) (err error) | ||||
//sys Statfs(path string, stat *Statfs_t) (err error) | //sys Statfs(path string, stat *Statfs_t) (err error) |
@@ -172,6 +172,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { | |||||
} | } | ||||
//sys ioctl(fd int, req uint, arg uintptr) (err error) | //sys ioctl(fd int, req uint, arg uintptr) (err error) | ||||
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL | |||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL | //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL | ||||
@@ -255,6 +256,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e | |||||
//sys Chmod(path string, mode uint32) (err error) | //sys Chmod(path string, mode uint32) (err error) | ||||
//sys Chown(path string, uid int, gid int) (err error) | //sys Chown(path string, uid int, gid int) (err error) | ||||
//sys Chroot(path string) (err error) | //sys Chroot(path string) (err error) | ||||
//sys ClockGettime(clockid int32, time *Timespec) (err error) | |||||
//sys Close(fd int) (err error) | //sys Close(fd int) (err error) | ||||
//sys Dup(fd int) (nfd int, err error) | //sys Dup(fd int) (nfd int, err error) | ||||
//sys Dup2(from int, to int) (err error) | //sys Dup2(from int, to int) (err error) | ||||
@@ -324,7 +326,6 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e | |||||
//sysnb Setreuid(ruid int, euid int) (err error) | //sysnb Setreuid(ruid int, euid int) (err error) | ||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error) | //sysnb Setresgid(rgid int, egid int, sgid int) (err error) | ||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error) | //sysnb Setresuid(ruid int, euid int, suid int) (err error) | ||||
//sysnb Setrlimit(which int, lim *Rlimit) (err error) | |||||
//sysnb Setsid() (pid int, err error) | //sysnb Setsid() (pid int, err error) | ||||
//sysnb Settimeofday(tp *Timeval) (err error) | //sysnb Settimeofday(tp *Timeval) (err error) | ||||
//sysnb Setuid(uid int) (err error) | //sysnb Setuid(uid int) (err error) | ||||
@@ -161,7 +161,8 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { | |||||
return | return | ||||
} | } | ||||
//sys ioctl(fd int, req uint, arg uintptr) (err error) | |||||
//sys ioctl(fd int, req uint, arg uintptr) (err error) = SYS_IOCTL | |||||
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL | |||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL | //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL | ||||
@@ -253,6 +254,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e | |||||
} | } | ||||
//sys ptrace(request int, pid int, addr uintptr, data int) (err error) | //sys ptrace(request int, pid int, addr uintptr, data int) (err error) | ||||
//sys ptracePtr(request int, pid int, addr unsafe.Pointer, data int) (err error) = SYS_PTRACE | |||||
func PtraceAttach(pid int) (err error) { | func PtraceAttach(pid int) (err error) { | ||||
return ptrace(PT_ATTACH, pid, 0, 0) | return ptrace(PT_ATTACH, pid, 0, 0) | ||||
@@ -267,19 +269,36 @@ func PtraceDetach(pid int) (err error) { | |||||
} | } | ||||
func PtraceGetFpRegs(pid int, fpregsout *FpReg) (err error) { | func PtraceGetFpRegs(pid int, fpregsout *FpReg) (err error) { | ||||
return ptrace(PT_GETFPREGS, pid, uintptr(unsafe.Pointer(fpregsout)), 0) | |||||
return ptracePtr(PT_GETFPREGS, pid, unsafe.Pointer(fpregsout), 0) | |||||
} | } | ||||
func PtraceGetRegs(pid int, regsout *Reg) (err error) { | func PtraceGetRegs(pid int, regsout *Reg) (err error) { | ||||
return ptrace(PT_GETREGS, pid, uintptr(unsafe.Pointer(regsout)), 0) | |||||
return ptracePtr(PT_GETREGS, pid, unsafe.Pointer(regsout), 0) | |||||
} | |||||
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) { | |||||
ioDesc := PtraceIoDesc{ | |||||
Op: int32(req), | |||||
Offs: offs, | |||||
} | |||||
if countin > 0 { | |||||
_ = out[:countin] // check bounds | |||||
ioDesc.Addr = &out[0] | |||||
} else if out != nil { | |||||
ioDesc.Addr = (*byte)(unsafe.Pointer(&_zero)) | |||||
} | |||||
ioDesc.SetLen(countin) | |||||
err = ptracePtr(PT_IO, pid, unsafe.Pointer(&ioDesc), 0) | |||||
return int(ioDesc.Len), err | |||||
} | } | ||||
func PtraceLwpEvents(pid int, enable int) (err error) { | func PtraceLwpEvents(pid int, enable int) (err error) { | ||||
return ptrace(PT_LWP_EVENTS, pid, 0, enable) | return ptrace(PT_LWP_EVENTS, pid, 0, enable) | ||||
} | } | ||||
func PtraceLwpInfo(pid int, info uintptr) (err error) { | |||||
return ptrace(PT_LWPINFO, pid, info, int(unsafe.Sizeof(PtraceLwpInfoStruct{}))) | |||||
func PtraceLwpInfo(pid int, info *PtraceLwpInfoStruct) (err error) { | |||||
return ptracePtr(PT_LWPINFO, pid, unsafe.Pointer(info), int(unsafe.Sizeof(*info))) | |||||
} | } | ||||
func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) { | func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) { | ||||
@@ -299,13 +318,25 @@ func PtracePokeText(pid int, addr uintptr, data []byte) (count int, err error) { | |||||
} | } | ||||
func PtraceSetRegs(pid int, regs *Reg) (err error) { | func PtraceSetRegs(pid int, regs *Reg) (err error) { | ||||
return ptrace(PT_SETREGS, pid, uintptr(unsafe.Pointer(regs)), 0) | |||||
return ptracePtr(PT_SETREGS, pid, unsafe.Pointer(regs), 0) | |||||
} | } | ||||
func PtraceSingleStep(pid int) (err error) { | func PtraceSingleStep(pid int) (err error) { | ||||
return ptrace(PT_STEP, pid, 1, 0) | return ptrace(PT_STEP, pid, 1, 0) | ||||
} | } | ||||
func Dup3(oldfd, newfd, flags int) error { | |||||
if oldfd == newfd || flags&^O_CLOEXEC != 0 { | |||||
return EINVAL | |||||
} | |||||
how := F_DUP2FD | |||||
if flags&O_CLOEXEC != 0 { | |||||
how = F_DUP2FD_CLOEXEC | |||||
} | |||||
_, err := fcntl(oldfd, how, newfd) | |||||
return err | |||||
} | |||||
/* | /* | ||||
* Exposed directly | * Exposed directly | ||||
*/ | */ | ||||
@@ -319,6 +350,7 @@ func PtraceSingleStep(pid int) (err error) { | |||||
//sys Chmod(path string, mode uint32) (err error) | //sys Chmod(path string, mode uint32) (err error) | ||||
//sys Chown(path string, uid int, gid int) (err error) | //sys Chown(path string, uid int, gid int) (err error) | ||||
//sys Chroot(path string) (err error) | //sys Chroot(path string) (err error) | ||||
//sys ClockGettime(clockid int32, time *Timespec) (err error) | |||||
//sys Close(fd int) (err error) | //sys Close(fd int) (err error) | ||||
//sys Dup(fd int) (nfd int, err error) | //sys Dup(fd int) (nfd int, err error) | ||||
//sys Dup2(from int, to int) (err error) | //sys Dup2(from int, to int) (err error) | ||||
@@ -401,7 +433,6 @@ func PtraceSingleStep(pid int) (err error) { | |||||
//sysnb Setreuid(ruid int, euid int) (err error) | //sysnb Setreuid(ruid int, euid int) (err error) | ||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error) | //sysnb Setresgid(rgid int, egid int, sgid int) (err error) | ||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error) | //sysnb Setresuid(ruid int, euid int, suid int) (err error) | ||||
//sysnb Setrlimit(which int, lim *Rlimit) (err error) | |||||
//sysnb Setsid() (pid int, err error) | //sysnb Setsid() (pid int, err error) | ||||
//sysnb Settimeofday(tp *Timeval) (err error) | //sysnb Settimeofday(tp *Timeval) (err error) | ||||
//sysnb Setuid(uid int) (err error) | //sysnb Setuid(uid int) (err error) | ||||
@@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) { | |||||
cmsg.Len = uint32(length) | cmsg.Len = uint32(length) | ||||
} | } | ||||
func (d *PtraceIoDesc) SetLen(length int) { | |||||
d.Len = uint32(length) | |||||
} | |||||
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { | func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { | ||||
var writtenOut uint64 = 0 | var writtenOut uint64 = 0 | ||||
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0) | _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0) | ||||
@@ -57,11 +61,5 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e | |||||
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) | func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) | ||||
func PtraceGetFsBase(pid int, fsbase *int64) (err error) { | func PtraceGetFsBase(pid int, fsbase *int64) (err error) { | ||||
return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0) | |||||
} | |||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) { | |||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint32(countin)} | |||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) | |||||
return int(ioDesc.Len), err | |||||
return ptracePtr(PT_GETFSBASE, pid, unsafe.Pointer(fsbase), 0) | |||||
} | } |
@@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) { | |||||
cmsg.Len = uint32(length) | cmsg.Len = uint32(length) | ||||
} | } | ||||
func (d *PtraceIoDesc) SetLen(length int) { | |||||
d.Len = uint64(length) | |||||
} | |||||
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { | func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { | ||||
var writtenOut uint64 = 0 | var writtenOut uint64 = 0 | ||||
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0) | _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0) | ||||
@@ -57,11 +61,5 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e | |||||
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) | func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) | ||||
func PtraceGetFsBase(pid int, fsbase *int64) (err error) { | func PtraceGetFsBase(pid int, fsbase *int64) (err error) { | ||||
return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0) | |||||
} | |||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) { | |||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint64(countin)} | |||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) | |||||
return int(ioDesc.Len), err | |||||
return ptracePtr(PT_GETFSBASE, pid, unsafe.Pointer(fsbase), 0) | |||||
} | } |
@@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) { | |||||
cmsg.Len = uint32(length) | cmsg.Len = uint32(length) | ||||
} | } | ||||
func (d *PtraceIoDesc) SetLen(length int) { | |||||
d.Len = uint32(length) | |||||
} | |||||
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { | func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { | ||||
var writtenOut uint64 = 0 | var writtenOut uint64 = 0 | ||||
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0) | _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0) | ||||
@@ -55,9 +59,3 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e | |||||
} | } | ||||
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) | func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) | ||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) { | |||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint32(countin)} | |||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) | |||||
return int(ioDesc.Len), err | |||||
} |